././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1711734324.222456 borgbackup-1.2.8/0000755000076500000240000000000014601577064012230 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/.coveragerc0000644000076500000240000000074114601576577014363 0ustar00twstaff[run] branch = True disable_warnings = module-not-measured source = src/borg omit = */borg/__init__.py */borg/__main__.py */borg/_version.py */borg/fuse.py */borg/support/* */borg/testsuite/* */borg/hash_sizes.py [report] exclude_lines = pragma: no cover pragma: freebsd only pragma: unknown platform only def __repr__ raise AssertionError raise NotImplementedError if 0: if __name__ == .__main__.: ignore_errors = True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/.pre-commit-config.yaml0000644000076500000240000000021114601576577016513 0ustar00twstaffrepos: - repo: https://github.com/pycqa/flake8 rev: 6.1.0 hooks: - id: flake8 files: '(src|scripts|conftest.py)' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/.readthedocs.yaml0000644000076500000240000000125714601576577015474 0ustar00twstaff# .readthedocs.yaml - Read the Docs configuration file. # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details. version: 2 build: os: ubuntu-22.04 tools: python: "3.11" jobs: post_checkout: - git fetch --unshallow apt_packages: - build-essential - pkg-config - libacl1-dev - libssl-dev - liblz4-dev - libzstd-dev - libxxhash-dev python: install: - requirements: requirements.d/development.lock.txt - requirements: requirements.d/docs.txt - method: pip path: . sphinx: configuration: docs/conf.py formats: - htmlzip ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/AUTHORS0000644000076500000240000000353014601576577013311 0ustar00twstaffE-mail addresses listed here are not intended for support, please see the `support section`_ instead. .. _support section: https://borgbackup.readthedocs.io/en/stable/support.html Borg authors ("The Borg Collective") ------------------------------------ - Thomas Waldmann - Radek Podgorny - Yuri D'Elia - Michael Hanselmann - Teemu Toivanen - Marian Beermann - Martin Hostettler - Daniel Reichelt - Lauri Niskanen - Abdel-Rahman A. (Abogical) - Gu1nness - Andrey Andreyevich Bienkowski Retired ``````` - Antoine Beaupré Borg is a fork of Attic. Attic authors ------------- Attic is written and maintained by Jonas Borgström and various contributors: Attic Development Lead `````````````````````` - Jonas Borgström Attic Patches and Suggestions ````````````````````````````` - Brian Johnson - Cyril Roussillon - Dan Christensen - Jeremy Maitin-Shepard - Johann Klähn - Petros Moisiadis - Thomas Waldmann BLAKE2 ------ Borg includes BLAKE2: Copyright 2012, Samuel Neves , licensed under the terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0. Slicing CRC32 ------------- Borg includes a fast slice-by-8 implementation of CRC32, Copyright 2011-2015 Stephan Brumme, licensed under the terms of a zlib license. See http://create.stephan-brumme.com/crc32/ Folding CRC32 ------------- Borg includes an extremely fast folding implementation of CRC32, Copyright 2013 Intel Corporation, licensed under the terms of the zlib license. xxHash ------ XXH64, a fast non-cryptographic hash algorithm. Copyright 2012-2016 Yann Collet, licensed under a BSD 2-clause license. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/CHANGES.rst0000644000076500000240000067731014601576577014060 0ustar00twstaff.. _important_notes: Important notes =============== This section provides information about security and corruption issues. .. _archives_tam_vuln: Pre-1.2.5 archives spoofing vulnerability (CVE-2023-36811) ---------------------------------------------------------- A flaw in the cryptographic authentication scheme in Borg allowed an attacker to fake archives and potentially indirectly cause backup data loss in the repository. The attack requires an attacker to be able to 1. insert files (with no additional headers) into backups 2. gain write access to the repository This vulnerability does not disclose plaintext to the attacker, nor does it affect the authenticity of existing archives. Creating plausible fake archives may be feasible for empty or small archives, but is unlikely for large archives. The fix enforces checking the TAM authentication tag of archives at critical places. Borg now considers archives without TAM as garbage or an attack. We are not aware of others having discovered, disclosed or exploited this vulnerability. Below, if we speak of borg 1.2.8, we mean a borg version >= 1.2.8 **or** a borg version that has the relevant patches for this vulnerability applied (could be also an older version in that case). Steps you must take to upgrade a repository (this applies to all kinds of repos no matter what encryption mode they use, including "none"): 1. Upgrade all clients using this repository to borg 1.2.8. Note: it is not required to upgrade a server, except if the server-side borg is also used as a client (and not just for "borg serve"). Do **not** run ``borg check`` with borg > 1.2.4 before completing the upgrade steps: - ``borg check`` would complain about archives without a valid archive TAM. - ``borg check --repair`` would remove such archives! 2. Do this step on every client using this repo: ``borg upgrade --show-rc --check-tam `` This will check the manifest TAM authentication setup in the repo and on this client. The command will exit with rc=0 if all is OK, otherwise with rc=1. a) If you get "Manifest authentication setup OK for this client and this repository." and rc=0, continue with 3. b) If you get some warnings and rc=1, run: ``borg upgrade --tam --force `` 3. Run: ``borg upgrade --show-rc --check-archives-tam `` This will create a report about the TAM status for all archives. In the last line(s) of the report, it will also report the overall status. The command will exit with rc=0 if all archives are TAM authenticated or with rc=1 if there are some archives with TAM issues. If there are no issues and all archives are TAM authenticated, continue with 5. Archive TAM issues are expected for: - archives created by borg <1.0.9. - archives resulting from a borg rename or borg recreate operation (see #7791) But, important, archive TAM issues could also come from archives created by an attacker. You should verify that archives with TAM issues are authentic and not malicious (== have good content, have correct timestamp, can be extracted successfully). In case you find crappy/malicious archives, you must delete them before proceeding. In low-risk, trusted environments, you may decide on your own risk to skip step 3 and just trust in everything being OK. 4. If there are no archives with TAM issues left at this point, you can skip this step. Run ``borg upgrade --archives-tam ``. This will unconditionally add a correct archive TAM to all archives not having one. ``borg check`` would consider TAM-less or invalid-TAM archives as garbage or a potential attack. To see that all archives are OK now, you can optionally repeat the command from step 3. 5. Done. Manifest and archives are TAM authenticated now. Vulnerability time line: * 2023-06-13: Vulnerability discovered during code review by Thomas Waldmann * 2023-06-13...: Work on fixing the issue, upgrade procedure, docs. * 2023-06-30: CVE was assigned via Github CNA * 2023-06-30 .. 2023-08-29: Fixed issue, code review, docs, testing. * 2023-08-30: Released fixed version 1.2.5 (broken upgrade procedure for some repos) * 2023-08-31: Released fixed version 1.2.6 (fixes upgrade procedure) .. _hashindex_set_bug: Pre-1.1.11 potential index corruption / data loss issue ------------------------------------------------------- A bug was discovered in our hashtable code, see issue #4829. The code is used for the client-side chunks cache and the server-side repo index. Although borg uses the hashtables very heavily, the index corruption did not happen too frequently, because it needed specific conditions to happen. Data loss required even more specific conditions, so it should be rare (and also detectable via borg check). You might be affected if borg crashed with / complained about: - AssertionError: Corrupted segment reference count - corrupted index or hints - ObjectNotFound: Object with key ... not found in repository ... - Index mismatch for key b'...'. (..., ...) != (-1, -1) - ValueError: stats_against: key contained in self but not in master_index. Advised procedure to fix any related issue in your indexes/caches: - install fixed borg code (on client AND server) - for all of your clients and repos remove the cache by: borg delete --cache-only YOURREPO (later, the cache will be re-built automatically) - for all your repos, rebuild the repo index by: borg check --repair YOURREPO This will also check all archives and detect if there is any data-loss issue. Affected branches / releases: - fd06497 introduced the bug into 1.1-maint branch - it affects all borg 1.1.x since 1.1.0b4. - fd06497 introduced the bug into master branch - it affects all borg 1.2.0 alpha releases. - c5cd882 introduced the bug into 1.0-maint branch - it affects all borg 1.0.x since 1.0.11rc1. The bug was fixed by: - 701159a fixes the bug in 1.1-maint branch - will be released with borg 1.1.11. - fa63150 fixes the bug in master branch - will be released with borg 1.2.0a8. - 7bb90b6 fixes the bug in 1.0-maint branch. Branch is EOL, no new release is planned as of now. .. _broken_validator: Pre-1.1.4 potential data corruption issue ----------------------------------------- A data corruption bug was discovered in borg check --repair, see issue #3444. This is a 1.1.x regression, releases < 1.1 (e.g. 1.0.x) are not affected. To avoid data loss, you must not run borg check --repair using an unfixed version of borg 1.1.x. The first official release that has the fix is 1.1.4. Package maintainers may have applied the fix to updated packages of 1.1.x (x<4) though, see the package maintainer's package changelog to make sure. If you never had missing item metadata chunks, the bug has not affected you even if you did run borg check --repair with an unfixed version. When borg check --repair tried to repair corrupt archives that miss item metadata chunks, the resync to valid metadata in still present item metadata chunks malfunctioned. This was due to a broken validator that considered all (even valid) item metadata as invalid. As they were considered invalid, borg discarded them. Practically, that means the affected files, directories or other fs objects were discarded from the archive. Due to the malfunction, the process was extremely slow, but if you let it complete, borg would have created a "repaired" archive that has lost a lot of items. If you interrupted borg check --repair because it was so strangely slow (killing borg somehow, e.g. Ctrl-C) the transaction was rolled back and no corruption occurred. The log message indicating the precondition for the bug triggering looks like: item metadata chunk missing [chunk: 001056_bdee87d...a3e50d] If you never had that in your borg check --repair runs, you're not affected. But if you're unsure or you actually have seen that, better check your archives. By just using "borg list repo::archive" you can see if all expected filesystem items are listed. .. _tam_vuln: Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099) ---------------------------------------------------------- A flaw in the cryptographic authentication scheme in Borg allowed an attacker to spoof the manifest. The attack requires an attacker to be able to 1. insert files (with no additional headers) into backups 2. gain write access to the repository This vulnerability does not disclose plaintext to the attacker, nor does it affect the authenticity of existing archives. The vulnerability allows an attacker to create a spoofed manifest (the list of archives). Creating plausible fake archives may be feasible for small archives, but is unlikely for large archives. The fix adds a separate authentication tag to the manifest. For compatibility with prior versions this authentication tag is *not* required by default for existing repositories. Repositories created with 1.0.9 and later require it. Steps you should take: 1. Upgrade all clients to 1.0.9 or later. 2. Run ``borg upgrade --tam `` *on every client* for *each* repository. 3. This will list all archives, including archive IDs, for easy comparison with your logs. 4. Done. Prior versions can access and modify repositories with this measure enabled, however, to 1.0.9 or later their modifications are indiscernible from an attack and will raise an error until the below procedure is followed. We are aware that this can be annoying in some circumstances, but don't see a way to fix the vulnerability otherwise. In case a version prior to 1.0.9 is used to modify a repository where above procedure was completed, and now you get an error message from other clients: 1. ``borg upgrade --tam --force `` once with *any* client suffices. This attack is mitigated by: - Noting/logging ``borg list``, ``borg info``, or ``borg create --stats``, which contain the archive IDs. We are not aware of others having discovered, disclosed or exploited this vulnerability. Vulnerability time line: * 2016-11-14: Vulnerability and fix discovered during review of cryptography by Marian Beermann (@enkore) * 2016-11-20: First patch * 2016-12-20: Released fixed version 1.0.9 * 2017-01-02: CVE was assigned * 2017-01-15: Released fixed version 1.1.0b3 (fix was previously only available from source) .. _attic013_check_corruption: Pre-1.0.9 potential data loss ----------------------------- If you have archives in your repository that were made with attic <= 0.13 (and later migrated to borg), running borg check would report errors in these archives. See issue #1837. The reason for this is a invalid (and useless) metadata key that was always added due to a bug in these old attic versions. If you run borg check --repair, things escalate quickly: all archive items with invalid metadata will be killed. Due to that attic bug, that means all items in all archives made with these old attic versions. Pre-1.0.4 potential repo corruption ----------------------------------- Some external errors (like network or disk I/O errors) could lead to corruption of the backup repository due to issue #1138. A sign that this happened is if "E" status was reported for a file that can not be explained by problems with the source file. If you still have logs from "borg create -v --list", you can check for "E" status. Here is what could cause corruption and what you can do now: 1) I/O errors (e.g. repo disk errors) while writing data to repo. This could lead to corrupted segment files. Fix:: # check for corrupt chunks / segments: borg check -v --repository-only REPO # repair the repo: borg check -v --repository-only --repair REPO # make sure everything is fixed: borg check -v --repository-only REPO 2) Unreliable network / unreliable connection to the repo. This could lead to archive metadata corruption. Fix:: # check for corrupt archives: borg check -v --archives-only REPO # delete the corrupt archives: borg delete --force REPO::CORRUPT_ARCHIVE # make sure everything is fixed: borg check -v --archives-only REPO 3) In case you want to do more intensive checking. The best check that everything is ok is to run a dry-run extraction:: borg extract -v --dry-run REPO::ARCHIVE .. _upgradenotes: Upgrade Notes ============= borg 1.1.x to 1.2.x ------------------- Some things can be recommended for the upgrade process from borg 1.1.x (please also read the important compatibility notes below): - first upgrade to a recent 1.1.x release - especially if you run some older 1.1.* or even 1.0.* borg release. - using that, run at least one `borg create` (your normal backup), `prune` and especially a `check` to see everything is in a good state. - check the output of `borg check` - if there is anything special, consider a `borg check --repair` followed by another `borg check`. - if everything is fine so far (borg check reports no issues), you can consider upgrading to 1.2.x. if not, please first fix any already existing issue. - if you want to play safer, first **create a backup of your borg repository**. - upgrade to latest borg 1.2.x release (you could use the fat binary from github releases page) - borg 1.2.6 has a security fix for the pre-1.2.5 archives spoofing vulnerability (CVE-2023-36811), see details and necessary upgrade procedure described above. - run `borg compact --cleanup-commits` to clean up a ton of 17 bytes long files in your repo caused by a borg 1.1 bug - run `borg check` again (now with borg 1.2.x) and check if there is anything special. - run `borg info` (with borg 1.2.x) to build the local pre12-meta cache (can take significant time, but after that it will be fast) - for more details see below. - check the compatibility notes (see below) and adapt your scripts, if needed. - if you run into any issues, please check the github issue tracker before posting new issues there or elsewhere. If you follow this procedure, you can help avoiding that we get a lot of "borg 1.2" issue reports that are not really 1.2 issues, but existed before and maybe just were not noticed. Compatibility notes: - matching of path patterns has been aligned with borg storing relative paths. Borg archives file paths without leading slashes. Previously, include/exclude patterns could contain leading slashes. You should check your patterns and remove leading slashes. - dropped support / testing for older Pythons, minimum requirement is 3.8. In case your OS does not provide Python >= 3.8, consider using our binary, which does not need an external Python interpreter. Or continue using borg 1.1.x, which is still supported. - freeing repository space only happens when "borg compact" is invoked. - mount: the default for --numeric-ids is False now (same as borg extract) - borg create --noatime is deprecated. Not storing atime is the default behaviour now (use --atime if you want to store the atime). - --prefix is deprecated, use -a / --glob-archives, see #6806 - list: corrected mix-up of "isomtime" and "mtime" formats. Previously, "isomtime" was the default but produced a verbose human format, while "mtime" produced a ISO-8601-like format. The behaviours have been swapped (so "mtime" is human, "isomtime" is ISO-like), and the default is now "mtime". "isomtime" is now a real ISO-8601 format ("T" between date and time, not a space). - create/recreate --list: file status for all files used to get announced *AFTER* the file (with borg < 1.2). Now, file status is announced *BEFORE* the file contents are processed. If the file status changes later (e.g. due to an error or a content change), the updated/final file status will be printed again. - removed deprecated-since-long stuff (deprecated since): - command "borg change-passphrase" (2017-02), use "borg key ..." - option "--keep-tag-files" (2017-01), use "--keep-exclude-tags" - option "--list-format" (2017-10), use "--format" - option "--ignore-inode" (2017-09), use "--files-cache" w/o "inode" - option "--no-files-cache" (2017-09), use "--files-cache=disabled" - removed BORG_HOSTNAME_IS_UNIQUE env var. to use borg you must implement one of these 2 scenarios: - 1) the combination of FQDN and result of uuid.getnode() must be unique and stable (this should be the case for almost everybody, except when having duplicate FQDN *and* MAC address or all-zero MAC address) - 2) if you are aware that 1) is not the case for you, you must set BORG_HOST_ID env var to something unique. - exit with 128 + signal number, #5161. if you have scripts expecting rc == 2 for a signal exit, you need to update them to check for >= 128. .. _changelog: Change Log ========== Version 1.2.8 (2024-03-29) -------------------------- For upgrade and compatibility hints, please also read the section "Upgrade Notes" above. Fixes: - check: fix return code and log level for index entry value discrepancies - with-lock: catch FileNotFoundError exception, print error msg, #8022 - benchmark: inherit options --rsh --remote-path, #8099 - fix Ctrl-C / SIGINT behaviour for pyinstaller-made binaries, #8155 New features: - upgrade --check-tam: check manifest TAM auth, exit with rc=1 if there are issues. - upgrade --check-archives-tam: check archives TAM auth, exit with rc=1 if there are issues. Other changes: - allow msgpack 1.0.8 (this might fix memory leaks with Python 3.12), #8133 - use the latest Cython 0.29.x - vagrant: - use / build binaries with python 3.9.19 - use generic/openbsd7 box - docs: - simplify TAM-related upgrade docs using the new commands - improve docs for borg with-lock, #8022 - add more infos borg check --repair recreating the shadow index to change log, see #6687 Version 1.2.7 (2023-12-02) -------------------------- Fixes: - docs: CVE-2023-36811 upgrade steps: consider checkpoint archives, #7802 - check/compact: fix spurious reappearance of orphan chunks since borg 1.2, #6687 - this consists of 2 fixes: - for existing chunks: check --repair: recreate shadow index, #7897 #6687 - for newly created chunks: update shadow index when doing a double-put, #7896 #5661 If you have experienced issue #6687, you may want to run borg check --repair after upgrading to borg 1.2.7 to recreate the shadow index and get rid of the issue for existing chunks. - LockRoster.modify: no KeyError if element was already gone, #7937 - create --X-from-command: run subcommands with a clean environment, #7916 - list --sort-by: support "archive" as alias of "name", #7873 - fix rc and msg if arg parsing throws an exception, #7885 Other changes: - support and test on Python 3.12 - include unistd.h in _chunker.c (fix for Python 3.13) - allow msgpack 1.0.6 and 1.0.7 - TAM issues: show tracebacks, improve borg check logging, #7797 - replace "datetime.utcfromtimestamp" with custom helper to avoid deprecation warnings when using Python 3.12 - vagrant: - use generic/debian9 box, fixes #7579 - add VM with debian bookworm / test on OpenSSL 3.0.x. - docs: - not only attack/unsafe, can also be a fs issue, #7853 - point to CVE-2023-36811 upgrade steps from borg 1.1 to 1.2 upgrade steps, #7899 - upgrade steps needed for all kinds of repos (including "none" encryption mode), #7813 - upgrade steps: talk about consequences of borg check, #7816 - upgrade steps: remove period that could be interpreted as part of the command - automated-local.rst: use GPT UUID for consistent udev rule - create disk/partition sector backup by disk serial number, #7934 - update macOS hint about full disk access - clarify borg prune -a option description, #7871 - readthedocs: also build offline docs (HTMLzip), #7835 - frontends: add "check.rebuild_refcounts" message Version 1.2.6 (2023-08-31) -------------------------- Fixes: - The upgrade procedure docs as published with borg 1.2.5 did not work, if the repository had archives resulting from a borg rename or borg recreate operation. The updated docs now use BORG_WORKAROUNDS=ignore_invalid_archive_tam at some places to avoid that issue, #7791. See: fix pre-1.2.5 archives spoofing vulnerability (CVE-2023-36811), details and necessary upgrade procedure described above. Other changes: - updated 1.2.5 changelog entry: 1.2.5 already has the fix for rename/recreate. - remove cython restrictions. recommended is to build with cython 0.29.latest, because borg 1.2.x uses this since years and it is very stable. you can also try to build with cython 3.0.x, there is a good chance that it works. as a 3rd option, we also bundle the `*.c` files cython outputs in the release pypi package, so you can also just use these and not need cython at all. Version 1.2.5 (2023-08-30) -------------------------- Fixes: - Security: fix pre-1.2.5 archives spoofing vulnerability (CVE-2023-36811), see details and necessary upgrade procedure described above. - rename/recreate: correctly update resulting archive's TAM, see #7791 - create: do not try to read parent dir of recursion root, #7746 - extract: fix false warning about pattern never matching, #4110 - diff: remove surrogates before output, #7535 - compact: clear empty directories at end of compact process, #6823 - create --files-cache=size: fix crash, #7658 - keyfiles: improve key sanity check, #7561 - only warn about "invalid" chunker params, #7590 - ProgressIndicatorPercent: fix space computation for wide chars, #3027 - improve argparse validator error messages New features: - mount: make up volname if not given (macOS), #7690. macFUSE supports a volname mount option to give what finder displays on the desktop / in the directory view. if the user did not specify it, we make something up, because otherwise it would be "macFUSE Volume 0 (Python)" and hide the mountpoint directory name. - BORG_WORKAROUNDS=authenticated_no_key to extract from authenticated repos without key, #7700 Other changes: - add `utcnow()` helper function to avoid deprecated `datetime.utcnow()` - stay on latest Cython 0.29 (0.29.36) for borg 1.2.x (do not use Cython 3.0 yet) - docs: - move upgrade notes to own section, see #7546 - mount -olocal: how to show mount in finder's sidebar, #5321 - list: fix --pattern examples, #7611 - improve patterns help - incl./excl. options, path-from-stdin exclusiveness - obfuscation docs: markup fix, note about MAX_DATA_SIZE - --one-file-system: add macOS apfs notes, #4876 - improve --one-file-system help string, #5618 - rewrite borg check docs - improve the docs for --keep-within, #7687 - fix borg init command in environment.rst.inc - 1.1.x upgrade notes: more precise borg upgrade instructions, #3396 - tests: - fix repo reopen - avoid long ids in pytest output - check buzhash chunksize distribution, see #7586 Version 1.2.4 (2023-03-24) -------------------------- New features: - import-tar: add --ignore-zeros to process concatenated tars, #7432. - debug id-hash: computes file/chunk content id-hash, #7406 - diff: --content-only does not show mode/ctime/mtime changes, #7248 - diff: JSON strings in diff output are now sorted alphabetically Bug fixes: - xattrs: fix namespace processing on FreeBSD, #6997 - diff: fix path related bug seen when addressing deferred items. - debug get-obj/put-obj: always give chunkid as cli param, see #7290 (this is an incompatible change, see also borg debug id-hash) - extract: fix mtime when ResourceFork xattr is set (macOS specific), #7234 - recreate: without --chunker-params, do not re-chunk, #7337 - recreate: when --target is given, do not detect "nothing to do". use case: borg recreate -a src --target dst can be used to make a copy of an archive inside the same repository, #7254. - set .hardlink_master for ALL hardlinkable items, #7175 - locking: fix host, pid, tid order. tid (thread id) must be parsed as hex from lock file name. - update development.lock.txt, including a setuptools security fix, #7227 Other changes: - requirements: allow msgpack 1.0.5 also - upgrade Cython to 0.29.33 - hashindex minor fixes, refactor, tweaks, tests - use os.replace not os.rename - remove BORG_LIBB2_PREFIX (not used any more) - docs: - BORG_KEY_FILE: clarify docs, #7444 - update FAQ about locale/unicode issues, #6999 - improve mount options rendering, #7359 - make timestamps in manual pages reproducible - installation: update Fedora in distribution list, #7357 - tests: - fix test_size_on_disk_accurate for large st_blksize, #7250 - add same_ts_ns function and use it for relaxed timestamp comparisons - "auto" compressor tests: don't assume a specific size, do not assume zlib is better than lz4, #7363 - add test for extracted directory mtime - vagrant: - upgrade local freebsd 12.1 box -> generic/freebsd13 box (13.1) - use pythons > 3.8 which work on freebsd 13.1 - pyenv: also install python 3.11.1 for testing - pyenv: use python 3.10.1, 3.10.0 build is broken on freebsd Version 1.2.3 (2022-12-24) -------------------------- Fixes: - create: fix --list --dry-run output for directories, #7209 - diff/recreate: normalize chunker params before comparing them, #7079 - check: fix uninitialised variable if repo is completely empty, #7034 - xattrs: improve error handling, #6988 - fix args.paths related argparsing, #6994 - archive.save(): always use metadata from stats (e.g. nfiles, size, ...), #7072 - tar_filter: recognize .tar.zst as zstd, #7093 - get_chunker: fix missing sparse=False argument, #7056 - file_integrity.py: make sure file_fd is always closed on exit - repository: cleanup(): close segment before unlinking - repository: use os.replace instead of os.rename Other changes: - remove python < 3.7 compatibility code - do not use version_tuple placeholder in setuptools_scm template - CI: fix tox4 passenv issue, #7199 - vagrant: update to python 3.9.16, use the openbsd 7.1 box - misc. test suite and docs fixes / improvements - remove deprecated --prefix from docs, #7109 - Windows: use MSYS2 for Github CI, remove Appveyor CI Version 1.2.2 (2022-08-20) -------------------------- New features: - prune/delete --checkpoint-interval=1800 and ctrl-c/SIGINT support, #6284 Fixes: - SaveFile: use a custom mkstemp with mode support, #6933, #6400, #6786. This fixes umask/mode/ACL issues (and also "chmod not supported" exceptions seen in 1.2.1) of files updated using SaveFile, e.g. the repo config. - hashindex_compact: fix eval order (check idx before use), #5899 - create --paths-from-(stdin|command): normalize paths, #6778 - secure_erase: avoid collateral damage, #6768. If a hardlink copy of a repo was made and a new repo config shall be saved, do NOT fill in random garbage before deleting the previous repo config, because that would damage the hardlink copy. - list: fix {flags:} formatting, #6081 - check: try harder to create the key, #5719 - misc commands: ctrl-c must not kill other subprocesses, #6912 - borg create with a remote repo via ssh - borg create --content-from-command - borg create --paths-from-command - (de)compression filter process of import-tar / export-tar Other changes: - deprecate --prefix, use -a / --glob-archives, see #6806 - make setuptools happy ("package would be ignored"), #6874 - fix pyproject.toml to create a fixed _version.py file, compatible with both old and new setuptools_scm version, #6875 - automate asciinema screencasts - CI: test on macOS 12 without fuse / fuse tests (too troublesome on github CI due to kernel extensions needed by macFUSE) - tests: fix test_obfuscate byte accounting - repository: add debug logging for issue #6687 - _chunker.c: fix warnings on macOS - requirements.lock.txt: use the latest cython 0.29.32 - docs: - add info on man page installation, #6894 - update archive_progress json description about "finished", #6570 - json progress_percent: some values are optional, #4074 - FAQ: full quota / full disk, #5960 - correct shell syntax for installation using git Version 1.2.1 (2022-06-06) -------------------------- Fixes: - create: skip with warning if opening the parent dir of recursion root fails, #6374 - create: fix crash. metadata stream can produce all-zero chunks, #6587 - fix crash when computing stats, escape % chars in archive name, #6500 - fix transaction rollback: use files cache filename as found in txn.active/, #6353 - import-tar: kill filter process in case of borg exceptions, #6401 #6681 - import-tar: fix mtime type bug - ensure_dir: respect umask for created directory modes, #6400 - SaveFile: respect umask for final file mode, #6400 - check archive: improve error handling for corrupt archive metadata block, make robust_iterator more robust, #4777 - pre12-meta cache: do not use the cache if want_unique is True, #6612 - fix scp-style repo url parsing for ip v6 address, #6526 - mount -o versions: give clear error msg instead of crashing. it does not make sense to request versions view if you only look at 1 archive, but the code shall not crash in that case as it did, but give a clear error msg. - show_progress: add finished=true/false to archive_progress json, #6570 - delete/prune: fix --iec mode output (decimal vs. binary units), #6606 - info: fix authenticated mode repo to show "Encrypted: No", #6462 - diff: support presence change for blkdev, chrdev and fifo items, #6615 New features: - delete: add repository id and location to prompt, #6453 - borg debug dump-repo-objs --ghost: new --segment=S --offset=O options Other changes: - support python 3.11 - allow msgpack 1.0.4, #6716 - load_key: no key is same as empty key, #6441 - give a more helpful error msg for unsupported key formats, #6561 - better error msg for defect or unsupported repo configs, #6566 - docs: - document borg 1.2 pattern matching behavior change, #6407 Make clear that absolute paths always go into the matcher as if they are relative (without leading slash). Adapt all examples accordingly. - authentication primitives: improved security and performance infos - mention BORG_FILES_CACHE_SUFFIX as alternative to BORG_FILES_CACHE_TTL, #5602 - FAQ: add a hint about --debug-topic=files_cache - improve borg check --max-duration description - fix values of TAG bytes, #6515 - borg compact --cleanup-commits also runs a normal compaction, #6324 - virtualization speed tips - recommend umask for passphrase file perms - borg 1.2 is security supported - update link to ubuntu packages, #6485 - use --numeric-ids in pull mode docs - remove blake2 docs, blake2 code not bundled any more, #6371 - clarify on-disk order and size of segment file log entry fields, #6357 - docs building: do not transform --/--- to unicode dashes - tests: - check that borg does not require pytest for normal usage, fixes #6563 - fix OpenBSD symlink mode test failure, #2055 - vagrant: - darwin64: remove fakeroot, #6314 - update development.lock.txt - use pyinstaller 4.10 and python 3.9.13 for binary build - upgrade VMCPUS and xdistn from 4 to 16, maybe this speeds up the tests - crypto: - use hmac.compare_digest instead of ==, #6470 - hmac_sha256: replace own cython wrapper code by hmac.digest python stdlib (since py38) - hmac and blake2b minor optimizations and cleanups - removed some unused crypto related code, #6472 - avoid losing the key (potential use-after-free). this never could happen in 1.2 due to the way we use the code. The issue was discovered in master after other changes, so we also "fixed" it here before it bites us. - setup / build: - add pyproject.toml, fix sys.path, #6466 - setuptools_scm: also require it via pyproject.toml - allow extra compiler flags for every extension build - fix misc. C / Cython compiler warnings, deprecation warnings - fix zstd.h include for bundled zstd, #6369 - source using python 3.8 features: ``pyupgrade --py38-plus ./**/*.py`` Version 1.2.0 (2022-02-22 22:02:22 :-) -------------------------------------- Fixes: - diff: reduce memory consumption, fix is_hardlink_master, #6295 - compact: fix / improve freeable / freed space log output - derive really freed space from quota use before/after, #5679 - do not say "freeable", but "maybe freeable" (based on hint, unsure) - fix race conditions in internal SaveFile function, #6306 #6028 - implement internal safe_unlink (was: truncate_and_unlink) function more safely: usually it does not truncate any more, only under "disk full" circumstances and only if there is only one hardlink. see: https://github.com/borgbackup/borg/discussions/6286 Other changes: - info: use a pre12-meta cache to accelerate stats for borg < 1.2 archives. the first time borg info is invoked on a borg 1.1 repo, it can take a rather long time computing and caching some stats values for 1.1 archives, which borg 1.2 archives have in their archive metadata structure. be patient, esp. if you have lots of old archives. following invocations are much faster due to the cache. related change: add archive name to calc_stats progress display. - docs: - add borg 1.2 upgrade notes, #6217 - link to borg placeholders and borg patterns help - init: explain the encryption modes better - clarify usage of patternfile roots - put import-tar docs into same file as export-tar docs - explain the difference between a path that ends with or without a slash, #6297 Version 1.2.0rc1 (2022-02-05) ----------------------------- Fixes: - repo::archive location placeholder expansion fixes, #5826, #5998 - repository: fix intermediate commits, shall be at end of current segment - delete: don't commit if nothing was deleted, avoid cache sync, #6060 - argument parsing: accept some options only once, #6026 - disallow overwriting of existing keyfiles on init, #6036 - if ensure_dir() fails, give more informative error message, #5952 New features: - delete --force: do not ask when deleting a repo, #5941 Other changes: - requirements: exclude broken or incompatible-with-pyinstaller setuptools - add a requirements.d/development.lock.txt and use it for vagrant - tests: - added nonce-related tests - refactor: remove assert_true - vagrant: macos box tuning, netbsd box fixes, #5370, #5922 - docs: - update install docs / requirements docs, #6180 - borg mount / FUSE "versions" view is not experimental any more - --pattern* is not experimental any more, #6134 - impact of deleting path/to/repo/nonce, #5858 - key export: add examples, #6204 - ~/.config/borg/keys is not used for repokey keys, #6107 - excluded parent dir's metadata can't restore Version 1.2.0b4 (2022-01-23) ---------------------------- Fixes: - create: fix passing device nodes and symlinks to --paths-from-stdin, #6009 - create --dry-run: fix display of kept tagfile, #5834 - check --repair: fix missing parameter in "did not consistently fail" msg, #5822 - fix hardlinkable file type check, #6037 - list: remove placeholders for shake_* hashes, #6082 - prune: handle case of calling prune_split when there are no archives, #6015 - benchmark crud: make sure cleanup of borg-test-data files/dir happens, #5630 - do not show archive name in repository-related error msgs, #6014 - prettier error msg (no stacktrace) if exclude file is missing, #5734 - do not require BORG_CONFIG_DIR if BORG_{SECURITY,KEYS}_DIR are set, #5979 - fix pyinstaller detection for dir-mode, #5897 - atomically create the CACHE_TAG file, #6028 - deal with the SaveFile/SyncFile race, docs, see #6056 708a5853 - avoid expanding path into LHS of formatting operation + tests, #6064 #6063 - repository: quota / compactable computation fixes - info: emit repo info even if repo has 0 archives + test, #6120 New features: - check --repair: significantly speed up search for next valid object in segment, #6022 - check: add progress indicator for archive check, #5809 - create: add retry_erofs workaround for O_NOATIME issue on volume shadow copies in WSL1, #6024 - create: allow --files-cache=size (this is potentially dangerous, use on your own risk), #5686 - import-tar: implement import-tar to complement export-tar, #2233 - implement BORG_SELFTEST env variable (can be carefully used to speedup borg hosting), #5871 - key export: print key if path is '-' or not given, #6092 - list --format: Add command_line to format keys Other changes: - pypi metadata: alpha -> beta - require python 3.8+, #5975 - use pyinstaller 4.7 - allow msgpack 1.0.3 - upgrade to bundled xxhash to 0.8.1 - import-tar / export-tar: tar file related changes: - check for short tarfile extensions - add .lz4 and .zstd - fix docs about extensions and decompression commands - add github codeql analysis, #6148 - vagrant: - box updates / add new boxes / remove outdated and broken boxes - use Python 3.9.10 (incl. binary builds) and 3.10.0 - fix pyenv initialisation, #5798 - fix vagrant scp on macOS, #5921 - use macfuse instead of osxfuse - shell completions: - update shell completions to 1.1.17, #5923 - remove BORG_LIBC completion, since 9914968 borg no longer uses find_library(). - docs: - fixed readme.rst irc webchat link (we use libera chat now, not freenode) - fix exceptions thrown by `setup.py build_man` - check --repair: recommend checking hw before check --repair, #5855 - check --verify-data: clarify and document conflict with --repository-only, #5808 - serve: improve ssh forced commands docs, #6083 - list: improve docs for `borg list` --format, #6061 - list: remove --list-format from borg list - FAQ: fix manifest-timestamp path (inside security dir) - fix the broken link to .nix file - document behavior for filesystems with inconsistent inodes, #5770 - clarify user_id vs uid for fuse, #5723 - clarify pattern usage with commands, #5176 - clarify pp vs. pf pattern type, #5300 - update referenced freebsd/macOS versions used for binary build, #5942 - pull mode: add some warnings, #5827 - clarify "you will need key and passphrase" borg init warning, #4622 - add missing leading slashes in help patterns, #5857 - add info on renaming repositories, #5240 - check: add notice about defective hardware, #5753 - mention tar --compare (compare archive to fs files), #5880 - add note about grandfather-father-son backup retention policy / rotation scheme, #6006 - permissions note rewritten to make it less confusing - create github security policy - remove leftovers of BORG_HOSTNAME_IS_UNIQUE - excluded parent dir's metadata can't restore. (#6062) - if parent dir is not extracted, we do not have its metadata - clarify who starts the remote agent Version 1.2.0b3 (2021-05-12) ---------------------------- Fixes: - create: fix --progress --log-json, #4360#issuecomment-774580052 - do not load files cache for commands not using it, #5673 - fix repeated cache tag file writing bug New features: - create/recreate: print preliminary file status early, #5417 - create/extract: add --noxattrs and --noacls options, #3955 - create: verbose files cache logging via --debug-topic=files_cache, #5659 - mount: implement --numeric-ids (default: False!), #2377 - diff: add --json-lines option - info / create --stats: add --iec option to print sizes in powers of 1024. Other changes: - create: add --upload-(ratelimit|buffer), deprecate --remote-* options, #5611 - create/extract/mount: add --numeric-ids, deprecate --numeric-owner option, #5724 - config: accept non-int value for max_segment_size / storage_quota - use PyInstaller v4.3, #5671 - vagrant: use Python 3.9.5 to build binaries - tox.ini: modernize and enable execution without preinstalling deps - cleanup code style checks - get rid of distutils, use setuptools+packaging - github CI: test on Python 3.10-dev - check: missing / healed chunks: always tell chunk ID, #5704 - docs: - remove bad /var/cache exclusion in example commands, #5625 - misc. fixes and improvements, esp. for macOS - add unsafe workaround to use an old repo copy, #5722 Version 1.2.0b2 (2021-02-06) ---------------------------- Fixes: - create: do not recurse into duplicate roots, #5603 - create: only print stats if not ctrl-c'ed, fixes traceback, #5668 - extract: improve exception handling when setting xattrs, #5092. emit a warning message giving the path, xattr key and error message. continue trying to restore other xattrs and bsdflags of the same file after an exception with xattr-setting happened. - export-tar: fix memory leak with ssh: remote repository, #5568. fix potential memory leak with ssh: remote repository with partial extraction. - remove empty shadowed_segments lists, #5275 - fix bad default: manifest.archives.list(consider_checkpoints=False), fixes tracebacks / KeyErros for missing objects in ChunkIndex, #5668 New features: - create: improve sparse file support - create --sparse (detect sparse file holes) and file map support, only for the "fixed" chunker, #14 - detect all-zero chunks in read data in "buzhash" and "fixed" chunkers - cached_hash: use a small LRU cache to accelerate all-zero chunks hashing - use cached_hash also to generate all-zero replacement chunks - create --remote-buffer, add a upload buffer for remote repos, #5574 - prune: keep oldest archive when retention target not met Other changes: - use blake2 from python 3.6+ hashlib (this removes the requirement for libb2 and the bundled blake2 code) - also accept msgpack up to 1.0.2. exclude 1.0.1 though, which had some issues (not sure they affect borg). - create: add repository location to --stats output, #5491 - check: debug log the segment filename - delete: add a --list switch to borg delete, #5116 - borg debug dump-hints - implemented to e.g. to look at shadow_index - Tab completion support for additional archives for 'borg delete' - refactor: have one borg.constants.zero all-zero bytes object - refactor shadow_index updating repo.put/delete, #5661, #5636. - docs: - add another case of attempted hardlink usage - fix description of borg upgrade hardlink usage, #5518 - use HTTPS everywhere - add examples for --paths-from-stdin, --paths-from-command, --paths-separator, #5644 - fix typos/grammar - update docs for dev environment installation instructions - recommend running tests only on installed versions for setup - add badge with current status of package - vagrant: - use brew install --cask ..., #5557 - use Python 3.9.1 and PyInstaller 4.1 to build the borg binary Version 1.2.0b1 (2020-12-06) ---------------------------- Fixes: - BORG_CACHE_DIR crashing borg if empty, atomic handling of recursive directory creation, #5216 - fix --dry-run and --stats coexistence, #5415 - allow EIO with warning when trying to hardlink, #4336 - export-tar: set tar format to GNU_FORMAT explicitly, #5274 - use --timestamp for {utcnow} and {now} if given, #5189 - make timestamp helper timezone-aware New features: - create: implement --paths-from-stdin and --paths-from-command, see #5492. These switches read paths to archive from stdin. Delimiter can specified by --paths-delimiter=DELIM. Paths read will be added honoring every option but exclusion options and --one-file-system. borg won't recurse into directories. - 'obfuscate' pseudo compressor obfuscates compressed chunk size in repo - add pyfuse3 (successor of llfuse) as an alternative lowlevel fuse implementation to llfuse (deprecated), #5407. FUSE implementation can be switched via env var BORG_FUSE_IMPL. - allow appending to the files cache filename with BORG_FILES_CACHE_SUFFIX - create: implement --stdin-mode, --stdin-user and --stdin-group, #5333 Other changes: - split recursive directory walking/processing into directory walking and item processing. - fix warning by importing setuptools before distutils. - debug info: include infos about FUSE implementation, #5546 - testing: - add a test for the hashindex corruption bug, #5531 #4829 - move away from travis-ci, use github actions, #5528 #5467 - test both on fuse2 and fuse3 - upload coverage reports to codecov - fix spurious failure in test_cache_files, #5438 - add tests for Location.with_timestamp - tox: add a non-fuse env to the envlist - vagrant: - use python 3.7.latest and pyinstaller 4.0 for binary creation - pyinstaller: compute basepath from spec file location - vagrant: updates/fixes for archlinux box, #5543 - docs: - "filename with spaces" example added to exclude file, #5236 - add a hint about sleeping computer, #5301 - how to adjust macOS >= Catalina security settings, #5303 - process/policy for adding new compression algorithms - updated docs about hacked backup client, #5480 - improve ansible deployment docs, make it more generic - how to approach borg speed issues, give speed example, #5371 - fix mathematical inaccuracy about chunk size, #5336 - add example for excluding content using --pattern cli option - clarify borg create's '--one-file-system' option, #4009 - improve docs/FAQ about append-only remote repos, #5497 - fix reST markup issues, labels - add infos about contributor retirement status Version 1.2.0a9 (2020-10-05) ---------------------------- Fixes: - fix memory leak related to preloading, #5202 - check --repair: fix potential data loss, #5325 - persist shadow_index in between borg runs, #4830 - fix hardlinked CACHEDIR.TAG processing, #4911 - --read-special: .part files also should be regular files, #5217 - allow server side enforcing of umask, --umask is for the local borg process only (see docs), #4947 - exit with 128 + signal number, #5161 - borg config --list does not show last_segment_checked, #5159 - locking: - fix ExclusiveLock race condition bug, #4923 - fix race condition in lock migration, #4953 - fix locking on openindiana, #5271 New features: - --content-from-command: create archive using stdout of given command, #5174 - allow key-import + BORG_KEY_FILE to create key files - build directory-based binary for macOS to avoid Gatekeeper delays Other changes: - upgrade bundled zstd to 1.4.5 - upgrade bundled xxhash to 0.8.0, #5362 - if self test fails, also point to OS and hardware, #5334 - misc. shell completions fixes/updates, rewrite zsh completion - prettier error message when archive gets too big, #5307 - stop relying on `false` exiting with status code 1 - rephrase some warnings, #5164 - parseformat: unnecessary calls removed, #5169 - testing: - enable Python3.9 env for test suite and VMs, #5373 - drop python 3.5, #5344 - misc. vagrant fixes/updates - misc. testing fixes, #5196 - docs: - add ssh-agent pull backup method to doc, #5288 - mention double --force in prune docs - update Homebrew install instructions, #5185 - better description of how cache and rebuilds of it work and how the workaround applies to that - point to borg create --list item flags in recreate usage, #5165 - add a note to create from stdin regarding files cache, #5180 - add security faq explaining AES-CTR crypto issues, #5254 - clarify --exclude-if-present in recreate, #5193 - add socat pull mode, #5150, #900 - move content of resources doc page to community project, #2088 - explain hash collision, #4884 - clarify --recompress option, #5154 Version 1.2.0a8 (2020-04-22) ---------------------------- Fixes: - fixed potential index corruption / data loss issue due to bug in hashindex_set, #4829. Please read and follow the more detailed notes close to the top of this document. - fix crash when upgrading erroneous hints file, #4922 - commit-time free space calc: ignore bad compact map entries, #4796 - info: if the archive doesn't exist, print a pretty message, #4793 - --prefix / -P: fix processing, avoid argparse issue, #4769 - ignore EACCES (errno 13) when hardlinking, #4730 - add a try catch when formatting the info string, #4818 - check: do not stumble over invalid item key, #4845 - update prevalence of env vars to set config and cache paths - mount: fix FUSE low linear read speed on large files, #5032 - extract: fix confusing output of borg extract --list --strip-components, #4934 - recreate: support --timestamp option, #4745 - fix ProgressIndicator msgids (JSON output), #4935 - fuse: set f_namemax in statfs result, #2684 - accept absolute paths on windows - pyinstaller: work around issue with setuptools > 44 New features: - chunker speedup (plus regression test) - added --consider-checkpoints and related test, #4788 - added --noflags option, deprecate --nobsdflags option, #4489 - compact: add --threshold option, #4674 - mount: add birthtime to FUSE entries - support platforms with no os.link, #4901 - if we don't have os.link, we just extract another copy instead of making a hardlink. - move sync_file_range to its own extension for better platform compatibility. - new --bypass-lock option to bypass locking, e.g. for read-only repos - accept absolute paths by removing leading slashes in patterns of all sorts but re: style, #4029 - delete: new --keep-security-info option Other changes: - support msgpack 0.6.2 and 1.0.0, #5065 - upgrade bundled zstd to 1.4.4 - upgrade bundled lz4 to 1.9.2 - upgrade xxhash to 0.7.3 - require recent enough llfuse for birthtime support, #5064 - only store compressed data if the result actually is smaller, #4516 - check: improve error output for matching index size, see #4829 - ignore --stats when given with --dry-run, but continue, #4373 - replaced usage of os.statvfs with shutil.disk_usage (better cross-platform support). - fuse: remove unneeded version check and compat code, micro opts - docs: - improve description of path variables - document how to completely delete data, #2929 - add FAQ about Borg config dir, #4941 - add docs about errors not printed as JSON, #4073 - update usage_general.rst.inc - added "Will move with BORG_CONFIG_DIR variable unless specified." to BORG_SECURITY_DIR info. - put BORG_SECURITY_DIR immediately below BORG_CONFIG_DIR (and moved BORG_CACHE_DIR up before them). - add paragraph regarding cache security assumptions, #4900 - tell about borg cache security precautions - add FAQ describing difference between a local repo vs. repo on a server. - document how to test exclusion patterns without performing an actual backup - create: tell that "Calculating size" time and space needs are caused by --progress - fix/improve documentation for @api decorator, #4674 - add a pull backup / push restore how-to, #1552 - fix man pages creation, #4752 - more general FAQ for backup and retain original paths, #4532 - explain difference between --exclude and --pattern, #4118 - add FAQ for preventing SSH timeout in extract, #3866 - improve password FAQ (decrease pw length, add -w 0 option to base64 to prevent line wrap), #4591 - add note about patterns and stored paths, #4160 - add upgrade of tools to pip installation how-to, #5090 - document one cause of orphaned chunks in check command, #2295 - clean up the whole check usage paragraph - FAQ: linked recommended restrictions to ssh public keys on borg servers, #4946 - fixed "doc downplays severity of Nonce reuse issue", #4883 - borg repo restore instructions needed, #3428 - new FAQ: A repo is corrupt and must be replaced with an older repo. - clarify borg init's encryption modes - native windows port: - update README_WINDOWS.rst - updated pyinstaller spec file to support windows builds - testing / CI: - improved travis config / install script, improved macOS builds - allow osx builds to fail, #4955 - Windows 10 build on Appveyor CI - vagrant: - upgrade pyinstaller to v3.5 + patch - use py369 for binary build, add py380 for tests - fix issue in stretch VM hanging at grub installation - add a debian buster and a ubuntu focal VM - update darwin box to 10.12 - upgrade FreeBSD box to 12.1 - fix debianoid virtualenv packages - use pyenv in freebsd64 VM - remove the flake8 test - darwin: avoid error if pkg is already installed - debianoid: don't interactively ask questions Version 1.2.0a7 (2019-09-07) ---------------------------- Fixes: - slave hardlinks extraction issue, see #4350 - extract: fix KeyError for "partial" extraction, #4607 - preload chunks for hardlink slaves w/o preloaded master, #4350 - fix preloading for old remote servers, #4652 - fix partial extract for hardlinked contentless file types, #4725 - Repository.open: use stat() to check for repo dir, #4695 - Repository.check_can_create_repository: use stat() to check, ~ #4695. - SecurityManager.known(): check all files, #4614 - after double-force delete, warn about necessary repair, #4704 - cope with ANY error when importing pytest into borg.testsuite, #4652 - fix invalid archive error message - setup.py: fix detection of missing Cython - filter out selinux xattrs, #4574 - location arg - should it be optional? #4541 - enable placeholder usage in --comment, #4559 - use whitelist approach for borg serve, #4097 New features: - minimal native Windows support, see windows readme (work in progress) - create: first ctrl-c (SIGINT) triggers checkpoint and abort, #4606 - new BORG_WORKAROUNDS mechanism, basesyncfile, #4710 - remove WSL autodetection. if WSL still has this problem, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around it. - support xxh64 checksum in addition to the hashlib hashes in borg list - enable placeholder usage in all extra archive arguments - enable placeholder usage in --comment, #4559 - enable placeholder usage in --glob-archives, #4495 - ability to use a system-provided version of "xxhash" - create: - changed the default behaviour to not store the atime of fs items. atime is often rather not interesting and fragile - it easily changes even if nothing else has changed and, if stored into the archive, spoils deduplication of the archive metadata stream. - if you give the --noatime option, borg will output a deprecation warning because it is currently ignored / does nothing. Please remove the --noatime option when using borg 1.2. - added a --atime option for storing files' atime into an archive Other changes: - argparser: always use REPOSITORY in metavar - do not check python/libc for borg serve, #4483 - small borg compact improvements, #4522 - compact: log freed space at INFO level - tests: - tox / travis: add testing on py38-dev - fix broken test that relied on improper zlib assumptions - pure-py msgpack warning shall not make a lot of tests fail, #4558 - rename test_mount_hardlinks to test_fuse_mount_hardlinks (master) - vagrant: add up-to-date openindiana box (py35, openssl10) - get rid of confusing coverage warning, #2069 - docs: - reiterate that 'file cache names are absolute' in FAQ, mention bind mount solution, #4738 - add restore docs, #4670 - updated docs to cover use of temp directory on remote, #4545 - add a push-style example to borg-create(1), #4613 - timestamps in the files cache are now usually ctime, #4583 - benchmark crud: clarify that space is used until compact - update documentation of borg create, corrects a mention of borg 1.1 as a future version. - fix osxfuse github link in installation docs - how to supply a passphrase, use crypto devices, #4549 - extract: document limitation "needs empty destination", #4598 - update macOS Brew link - add note about software for automating backup - compact: improve docs, - README: new URL for funding options Version 1.2.0a6 (2019-04-22) ---------------------------- Fixes: - delete / prune: consider part files correctly for stats, #4507 - fix "all archives" stats considering part files, #4329 - create: only run stat_simple_attrs() once - create: --stats does not work with --dry-run, exit with error msg, #4373 - give "invalid repo" error msg if repo config not found, #4411 New features: - display msgpack version as part of sysinfo (e.g. in tracebacks) Other changes: - docs: - sdd "SSH Configuration" section, #4493, #3988, #636, #4485 - better document borg check --max-duration, #4473 - sorted commands help in multiple steps, #4471 - testing: - travis: use py 3.5.3 and 3.6.7 on macOS to get a pyenv-based python build with openssl 1.1 - vagrant: use py 3.5.3 and 3.6.8 on darwin64 VM to build python and borg with openssl 1.1 - pytest: -v and default XDISTN to 1, #4481 Version 1.2.0a5 (2019-03-21) ---------------------------- Fixes: - warn if a file has changed while being backed up, #1750 - lrucache: regularly remove old FDs, #4427 - borg command shall terminate with rc 2 for ImportErrors, #4424 - make freebsd xattr platform code api compatible with linux, #3952 Other changes: - major setup code refactoring (especially how libraries like openssl, liblz4, libzstd, libb2 are discovered and how it falls back to code bundled with borg), new: uses pkg-config now (and needs python "pkgconfig" package installed), #1925 if you are a borg package maintainer, please try packaging this (see comments in setup.py). - Vagrantfile: add zstd, reorder, build env vars, #4444 - travis: install script improvements - update shell completions - docs: - add a sample logging.conf in docs/misc, #4380 - fix spelling errors - update requirements / install docs, #4374 Version 1.2.0a4 (2019-03-11) ---------------------------- Fixes: - do not use O_NONBLOCK for special files, like FIFOs, block and char devices when using --read-special. fixes backing up FIFOs. fixes to test. #4394 - more LibreSSL build fixes: LibreSSL has HMAC_CTX_free and HMAC_CTX_new New features: - check: incremental repo check (only checks crc32 for segment entries), #1657 borg check --repository-only --max-duration SECONDS ... - delete: timestamp for borg delete --info added, #4359 Other changes: - redo stale lock handling, #3986 drop BORG_HOSTNAME_IS_UNIQUE (please use BORG_HOST_ID if needed). borg now always assumes it has a unique host id - either automatically from fqdn plus uuid.getnode() or overridden via BORG_HOST_ID. - docs: - added Alpine Linux to distribution list - elaborate on append-only mode docs - vagrant: - darwin: new 10.12 box - freebsd: new 12.0 box - openbsd: new 6.4 box - misc. updates / fixes Version 1.2.0a3 (2019-02-26) ---------------------------- Fixes: - LibreSSL build fixes, #4403 - dummy ACL/xattr code fixes (used by OpenBSD and others), #4403 - create: fix openat/statat issues for root directory, #4405 Version 1.2.0a2 and earlier (2019-02-24) ---------------------------------------- New features: - compact: "borg compact" needs to be used to free repository space by compacting the segments (reading sparse segments, rewriting still needed data to new segments, deleting the sparse segments). Borg < 1.2 invoked compaction automatically at the end of each repository writing command. Borg >= 1.2 does not do that any more to give better speed, more control, more segment file stability (== less stuff moving to newer segments) and more robustness. See the docs about "borg compact" for more details. - "borg compact --cleanup-commits" is to cleanup the tons of 17byte long commit-only segment files caused by borg 1.1.x issue #2850. Invoke this once after upgrading (the server side) borg to 1.2. Compaction now automatically removes unneeded commit-only segment files. - prune: Show which rule was applied to keep archive, #2886 - add fixed blocksize chunker (see --chunker-params docs), #1086 Fixes: - avoid stale filehandle issues, #3265 - use more FDs, avoid race conditions on active fs, #906, #908, #1038 - add O_NOFOLLOW to base flags, #908 - compact: - require >10% freeable space in a segment, #2985 - repository compaction now automatically removes unneeded 17byte commit-only segments, #2850 - make swidth available on all posix platforms, #2667 Other changes: - repository: better speed and less stuff moving around by using separate segment files for manifest DELETEs and PUTs, #3947 - use pyinstaller v3.3.1 to build binaries - update bundled zstd code to 1.3.8, #4210 - update bundled lz4 code to 1.8.3, #4209 - msgpack: - switch to recent "msgpack" pypi pkg name, #3890 - wrap msgpack to avoid future compat complications, #3632, #2738 - support msgpack 0.6.0 and 0.6.1, #4220, #4308 - llfuse: modernize / simplify llfuse version requirements - code refactorings / internal improvements: - include size/csize/nfiles[_parts] stats into archive, #3241 - calc_stats: use archive stats metadata, if available - crypto: refactored crypto to use an AEAD style API - crypto: new AES-OCB, CHACHA20-POLY1305 - create: use less syscalls by not using a python file obj, #906, #3962 - diff: refactor the diff functionality to new ItemDiff class, #2475 - archive: create FilesystemObjectProcessors class - helpers: make a package, split into smaller modules - xattrs: move to platform package, use cython instead ctypes, #2495 - xattrs/acls/bsdflags: misc. code/api optimizations - FUSE: separate creation of filesystem from implementation of llfuse funcs, #3042 - FUSE: use unpacker.tell() instead of deprecated write_bytes, #3899 - setup.py: move build_man / build_usage code to setup_docs.py - setup.py: update to use a newer Cython/setuptools API for compiling .pyx -> .c, #3788 - use python 3.5's os.scandir / os.set_blocking - multithreading preparations (not used yet): - item.to_optr(), Item.from_optr() - fix chunker holding the GIL during blocking I/O - C code portability / basic MSC compatibility, #4147, #2677 - testing: - vagrant: new VMs for linux/bsd/darwin, most with OpenSSL 1.1 and py36 Version 1.1.18 (2022-06-05) --------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. There is one exception though: If you upgrade from an unpatched borg < 1.0.9, please read that section above: "Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099)" - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - 1.1.11 removes WSL autodetection (Windows 10 Subsystem for Linux). If WSL still has a problem with sync_file_range, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around the WSL issue. - 1.1.14 changes return codes due to a bug fix: In case you have scripts expecting rc == 2 for a signal exit, you need to update them to check for >= 128 (as documented since long). - 1.1.15 drops python 3.4 support, minimum requirement is 3.5 now. - 1.1.17 install_requires the "packaging" pypi package now. New features: - check --repair: significantly speed up search for next valid object in segment, #6022 - create: add retry_erofs workaround for O_NOATIME issue on volume shadow copies in WSL1, #6024 - key export: display key if path is '-' or not given, #6092 - list --format: add command_line to format keys, #6108 Fixes: - check: improve error handling for corrupt archive metadata block, make robust_iterator more robust, #4777 - diff: support presence change for blkdev, chrdev and fifo items, #6483 - diff: reduce memory consumption, fix is_hardlink_master - init: disallow overwriting of existing keyfiles - info: fix authenticated mode repo to show "Encrypted: No", #6462 - info: emit repo info even if repo has 0 archives, #6120 - list: remove placeholders for shake_* hashes, #6082 - mount -o versions: give clear error msg instead of crashing - show_progress: add finished=true/false to archive_progress json, #6570 - fix hardlinkable file type check, #6037 - do not show archive name in error msgs referring to the repository, #6023 - prettier error msg (no stacktrace) if exclude file is missing, #5734 - do not require BORG_CONFIG_DIR if BORG_{SECURITY,KEYS}_DIR are set, #5979 - atomically create the CACHE_TAG file, #6028 - deal with the SaveFile/SyncFile race, docs, see #6176 5c5b59bc9 - avoid expanding path into LHS of formatting operation + tests, #6064 #6063 - repository: quota / compactable computation fixes, #6119. This is mainly to keep the repo code in sync with borg 1.2. As borg 1.1 compacts immediately, there was not really an issue with this in 1.1. - fix transaction rollback: use files cache filename as found in txn.active, #6353 - do not load files cache for commands not using it, fixes #5673 - fix scp repo url parsing for ip v6 addrs, #6526 - repo::archive location placeholder expansion fixes, #5826, #5998 - use expanded location for log output - support placeholder expansion for BORG_REPO env var - respect umask for created directory and file modes, #6400 - safer truncate_and_unlink implementation Other changes: - upgrade bundled xxhash code to 0.8.1 - fix xxh64 related build (setup.py and post-0.8.1 patch for static_assert). The patch was required to build the bundled xxhash code on FreeBSD, see https://github.com/Cyan4973/xxHash/pull/670 - msgpack build: remove endianness macro, #6105 - update and fix shell completions - fuse: remove unneeded version check and compat code - delete --force: do not ask when deleting a repo, #5941 - delete: don't commit if nothing was deleted, avoid cache sync, #6060 - delete: add repository id and location to prompt - compact segments: improve freeable / freed space log output, #5679 - if ensure_dir() fails, give more informative error message, #5952 - load_key: no key is same as empty key, #6441 - better error msg for defect or unsupported repo configs, #6566 - use hmac.compare_digest instead of ==, #6470 - implement more standard hashindex.setdefault behaviour - remove stray punctuation from secure-erase message - add development.lock.txt, use a real python 3.5 to generate frozen reqs - setuptools 60.7.0 breaks pyinstaller, #6246 - setup.py clean2 was added to work around some setuptools customizability limitation. - allow extra compiler flags for every extension build - C code: make switch fallthrough explicit - Cython code: fix "useless trailing comma" cython warnings - requirements.lock.txt: use the latest cython 0.29.30 - fix compilation warnings: ‘PyUnicode_AsUnicode’ is deprecated - docs: - ~/.config/borg/keys is not used for repokey keys, #6107 - excluded parent dir's metadata can't restore, #6062 - permissions note rewritten to make it less confusing, #5490 - add note about grandfather-father-son backup retention policy / rotation scheme - clarify who starts the remote agent (borg serve) - test/improve pull backup docs, #5903 - document the socat pull mode described in #900 #515ß - borg serve: improve ssh forced commands docs, #6083 - improve docs for borg list --format, #6080 - fix the broken link to .nix file - clarify pattern usage with commands, #5176 - clarify user_id vs uid for fuse, #5723 - fix binary build freebsd/macOS version, #5942 - FAQ: fix manifest-timestamp path, #6016 - remove duplicate faq entries, #5926 - fix sphinx warnings, #5919 - virtualisation speed tips - fix values of TAG bytes, #6515 - recommend umask for passphrase file perms - update link to ubuntu packages, #6485 - clarify on-disk order and size of log entry fields, #6357 - do not transform --/--- to unicode dashes - improve linking inside docs, link to borg_placeholders, link to borg_patterns - use same phrasing in misc. help texts - borg init: explain the encryption modes better - explain the difference between a path that ends with or without a slash, #6297 - clarify usage of patternfile roots, #6242 - borg key export: add examples - updates about features not experimental any more: FUSE "versions" view, --pattern*, #6134 - fix/update cygwin package requirements - impact of deleting path/to/repo/nonce, #5858 - warn about tampered server nonce - mention BORG_FILES_CACHE_SUFFIX as alternative to BORG_FILES_CACHE_TTL, #5602 - add a troubleshooting note about "is not a valid repository" to the FAQ - vagrant / CI / testing: - misc. fixes and updates, new python versions - macOS on github: re-enable fuse2 testing by downgrading to older macOS, #6099 - fix OpenBSD symlink mode test failure, #2055 - use the generic/openbsd6 box - strengthen the test: we can read data w/o nonces - add tests for path/to/repo/nonce deletion - darwin64: backport some tunings from master - darwin64: remove fakeroot, #6314 - darwin64: fix vagrant scp, #5921 - darwin64: use macfuse instead of osxfuse - add ubuntu "jammy" 22.04 LTS VM - adapt memory for openindiana64 and darwin64 Version 1.1.17 (2021-07-12) --------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. There is one exception though: If you upgrade from an unpatched borg < 1.0.9, please read that section above: "Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099)" - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - 1.1.11 removes WSL autodetection (Windows 10 Subsystem for Linux). If WSL still has a problem with sync_file_range, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around the WSL issue. - 1.1.14 changes return codes due to a bug fix: In case you have scripts expecting rc == 2 for a signal exit, you need to update them to check for >= 128 (as documented since long). - 1.1.15 drops python 3.4 support, minimum requirement is 3.5 now. - 1.1.17 install_requires the "packaging" pypi package now. Fixes: - pyinstaller dir-mode: fix pyi detection / LIBPATH treatment, #5897 - handle crash due to kill stale lock race, #5828 - fix BORG_CACHE_DIR crashing borg if empty, #5216 - create --dry-run: fix display of kept tagfile, #5834 - fix missing parameter in "did not consistently fail" msg, #5822 - missing / healed chunks: always tell chunk ID, #5704 - benchmark: make sure cleanup happens even on exceptions, #5630 New features: - implement BORG_SELFTEST env variable, #5871. this can be used to accelerate borg startup a bit. not recommended for normal usage, but borg mass hosters with a lot of borg invocations can save some resources with this. on my laptop, this saved ~100ms cpu time (sys+user) per borg command invocation. - implement BORG_LIBC env variable to give the libc filename, #5870. you can use this if a borg does not find your libc. - check: add progress indicator for archive check. - allow --files-cache=size (not recommended, make sure you know what you do) Other changes: - Python 3.10 now officially supported! we test on py310-dev on github CI since a while and now also on the vagrant machines, so it should work ok. - github CI: test on py310 (again) - get rid of distutils, use packaging and setuptools. distutils is deprecated and gives warnings on py 3.10. - setup.py: rename "clean" to "clean2" to avoid shadowing the "clean" command. - remove libc filename fallback for the BSDs (there is no "usual" name) - cleanup flake8 checks, fix some pep8 violations. - docs building: replace deprecated function ".add_stylesheet()" for Sphinx 4 compatibility - docs: - add a hint on sleeping computer and ssh connections, #5301 - update the documentation on hacked backup client, #5480 - improve docs/FAQ about append-only remote repos, #5497 - complement the documentation for pattern files and exclude files, #5520 - "filename with spaces" example added to exclude file, #5236 note: no whitespace escaping needed, processed by borg. - add info on renaming repositories, #5240 - clarify borg check --verify-data, #5808 - add notice about defective hardware to check documentation, #5753 - add paragraph added in #5855 to utility documentation source - add missing leading slashes in help patterns, #5857 - clarify "you will need key and passphrase" borg init warning, #4622 - pull mode: add some warnings, #5827 - mention tar --compare (compare archive to fs files), #5880 - fix typos, backport of #5597 - vagrant: - add py3.7.11 for binary build, also add 3.10-dev. - use latest Cython 0.29.23 for py310 compat fixes. - more RAM for openindiana upgrade plan resolver, it just hangs (swaps?) if there is too little RAM. - fix install_pyenv to adapt to recent changes in pyenv (same as in master now). - use generic/netbsd9 box, copied from master branch. Version 1.1.16 (2021-03-23) --------------------------- Fixes: - setup.py: add special openssl prefix for Apple M1 compatibility - do not recurse into duplicate roots, #5603 - remove empty shadowed_segments lists, #5275, #5614 - fix libpython load error when borg fat binary / dir-based binary is invoked via a symlink by upgrading pyinstaller to v4.2, #5688 - config: accept non-int value (like 500M or 100G) for max_segment_size or storage_quota, #5639. please note: when setting a non-int value for this in a repo config, using the repo will require borg >= 1.1.16. New features: - bundled msgpack: drop support for old buffer protocol to support Python 3.10 - verbose files cache logging via --debug-topic=files_cache, #5659. Use this if you suspect that borg does not detect unmodified files as expected. - create/extract: add --noxattrs and --noacls option, #3955. when given with borg create, borg will not get xattrs / ACLs from input files (and thus, it will not archive xattrs / ACLs). when given with borg extract, borg will not read xattrs / ACLs from archive and will not set xattrs / ACLs on extracted files. - diff: add --json-lines option, #3765 - check: debug log segment filename - borg debug dump-hints Other changes: - Tab completion support for additional archives for 'borg delete' - repository: deduplicate code of put and delete, no functional change - tests: fix result order issue (sporadic test failure on openindiana) - vagrant: - upgrade pyinstaller to v4.2, #5671 - avoid grub-install asking interactively for device - remove the xenial box - update freebsd box to 12.1 - docs: - update macOS install instructions, #5677 - use macFUSE (not osxfuse) for Apple M1 compatibility - update docs for dev environment installation instructions, #5643 - fix grammar in faq - recommend running tests only on installed versions for setup - add link back to git-installation - remove /var/cache exclusion in example commands, #5625. This is generally a poor idea and shouldn't be promoted through examples. - add repology.org badge with current packaging status - explain hash collision - add unsafe workaround to use an old repo copy, #5722 Version 1.1.15 (2020-12-25) --------------------------- Fixes: - extract: - improve exception handling when setting xattrs, #5092. - emit a warning message giving the path, xattr key and error message. - continue trying to restore other xattrs and bsdflags of the same file after an exception with xattr-setting happened. - export-tar: - set tar format to GNU_FORMAT explicitly, #5274 - fix memory leak with ssh: remote repository, #5568 - fix potential memory leak with ssh: remote repository with partial extraction - create: fix --dry-run and --stats coexistence, #5415 - use --timestamp for {utcnow} and {now} if given, #5189 New features: - create: implement --stdin-mode, --stdin-user and --stdin-group, #5333 - allow appending the files cache filename with BORG_FILES_CACHE_SUFFIX env var Other changes: - drop python 3.4 support, minimum requirement is 3.5 now. - enable using libxxhash instead of bundled xxh64 code - update llfuse requirements (1.3.8) - set cython language_level in some files to fix warnings - allow EIO with warning when trying to hardlink - PropDict: fail early if internal_dict is not a dict - update shell completions - tests / CI - add a test for the hashindex corruption bug, #5531 #4829 - fix spurious failure in test_cache_files, #5438 - added a github ci workflow - reduce testing on travis, no macOS, no py3x-dev, #5467 - travis: use newer dists, native py on dist - vagrant: - remove jessie and trusty boxes, #5348 #5383 - pyinstaller 4.0, build on py379 - binary build on stretch64, #5348 - remove easy_install based pip installation - docs: - clarify '--one-file-system' for btrfs, #5391 - add example for excluding content using the --pattern cmd line arg - complement the documentation for pattern files and exclude files, #5524 - made ansible playbook more generic, use package instead of pacman. also change state from "latest" to "present". - complete documentation on append-only remote repos, #5497 - internals: rather talk about target size than statistics, #5336 - new compression algorithm policy, #1633 #5505 - faq: add a hint on sleeping computer, #5301 - note requirements for full disk access on macOS Catalina, #5303 - fix/improve description of borg upgrade hardlink usage, #5518 - modernize 1.1 code: - drop code/workarounds only needed to support Python 3.4 - remove workaround for pre-release py37 argparse bug - removed some outdated comments/docstrings - requirements: remove some restrictions, lock on current versions Version 1.1.14 (2020-10-07) --------------------------- Fixes: - check --repair: fix potential data loss when interrupting it, #5325 - exit with 128 + signal number (as documented) when borg is killed by a signal, #5161 - fix hardlinked CACHEDIR.TAG processing, #4911 - create --read-special: .part files also should be regular files, #5217 - llfuse dependency: choose least broken 1.3.6/1.3.7. 1.3.6 is broken on python 3.9, 1.3.7 is broken on FreeBSD. Other changes: - upgrade bundled xxhash to 0.7.4 - self test: if it fails, also point to OS and hardware, #5334 - pyinstaller: compute basepath from spec file location - prettier error message when archive gets too big, #5307 - check/recreate are not "experimental" any more (but still potentially dangerous): - recreate: remove extra confirmation - rephrase some warnings, update docs, #5164 - shell completions: - misc. updates / fixes - support repositories in fish tab completion, #5256 - complete $BORG_RECREATE_I_KNOW_WHAT_I_AM_DOING - rewrite zsh completion: - completion for almost all optional and positional arguments - completion for Borg environment variables (parameters) - use "allow/deny list" instead of "white/black list" wording - declare "allow_cache_wipe" marker in setup.cfg to avoid pytest warning - vagrant / tests: - misc. fixes / updates - use python 3.5.10 for binary build - build directory-based binaries additionally to the single file binaries - add libffi-dev, required to build python - use cryptography<3.0, more recent versions break the jessie box - test on python 3.9 - do brew update with /dev/null redirect to avoid "too much log output" on travis-ci - docs: - add ssh-agent pull backup method docs, #5288 - how to approach borg speed issues, #5371 - mention double --force in prune docs - update Homebrew install instructions, #5185 - better description of how cache and rebuilds of it work - point to borg create --list item flags in recreate usage, #5165 - add security faq explaining AES-CTR crypto issues, #5254 - add a note to create from stdin regarding files cache, #5180 - fix borg.1 manpage generation regression, #5211 - clarify how exclude options work in recreate, #5193 - add section for retired contributors - hint about not misusing private email addresses of contributors for borg support Version 1.1.13 (2020-06-06) --------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. There is one exception though: If you upgrade from an unpatched borg < 1.0.9, please read that section above: "Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099)" - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - 1.1.11 removes WSL autodetection (Windows 10 Subsystem for Linux). If WSL still has a problem with sync_file_range, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around the WSL issue. Fixes: - rebuilt using a current Cython version, compatible with python 3.8, #5214 Version 1.1.12 (2020-06-06) --------------------------- Fixes: - fix preload-related memory leak, #5202. - mount / borgfs (FUSE filesystem): - fix FUSE low linear read speed on large files, #5067 - fix crash on old llfuse without birthtime attrs, #5064 - accidentally we required llfuse >= 1.3. Now also old llfuse works again. - set f_namemax in statfs result, #2684 - update precedence of env vars to set config and cache paths, #4894 - correctly calculate compression ratio, taking header size into account, too New features: - --bypass-lock option to bypass locking with read-only repositories Other changes: - upgrade bundled zstd to 1.4.5 - travis: adding comments and explanations to Travis config / install script, improve macOS builds. - tests: test_delete_force: avoid sporadic test setup issues, #5196 - misc. vagrant fixes - the binary for macOS is now built on macOS 10.12 - the binaries for Linux are now built on Debian 8 "Jessie", #3761 - docs: - PlaceholderError not printed as JSON, #4073 - "How important is Borg config?", #4941 - make Sphinx warnings break docs build, #4587 - some markup / warning fixes - add "updating borgbackup.org/releases" to release checklist, #4999 - add "rendering docs" to release checklist, #5000 - clarify borg init's encryption modes - add note about patterns and stored paths, #4160 - add upgrade of tools to pip installation how-to - document one cause of orphaned chunks in check command, #2295 - linked recommended restrictions to ssh public keys on borg servers in faq, #4946 Version 1.1.11 (2020-03-08) --------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. There is one exception though: If you upgrade from an unpatched borg < 1.0.9, please read that section above: "Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099)" - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - 1.1.11 removes WSL autodetection (Windows 10 Subsystem for Linux). If WSL still has a problem with sync_file_range, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around the WSL issue. Fixes: - fixed potential index corruption / data loss issue due to bug in hashindex_set, #4829. Please read and follow the more detailed notes close to the top of this document. - upgrade bundled xxhash to 0.7.3, #4891. 0.7.2 is the minimum requirement for correct operations on ARMv6 in non-fixup mode, where unaligned memory accesses cause bus errors. 0.7.3 adds some speedups and libxxhash 0.7.3 even has a pkg-config file now. - upgrade bundled lz4 to 1.9.2 - upgrade bundled zstd to 1.4.4 - fix crash when upgrading erroneous hints file, #4922 - extract: - fix KeyError for "partial" extraction, #4607 - fix "partial" extract for hardlinked contentless file types, #4725 - fix preloading for old (0.xx) remote servers, #4652 - fix confusing output of borg extract --list --strip-components, #4934 - delete: after double-force delete, warn about necessary repair, #4704 - create: give invalid repo error msg if repo config not found, #4411 - mount: fix FUSE mount missing st_birthtime, #4763 #4767 - check: do not stumble over invalid item key, #4845 - info: if the archive doesn't exist, print a pretty message, #4793 - SecurityManager.known(): check all files, #4614 - Repository.open: use stat() to check for repo dir, #4695 - Repository.check_can_create_repository: use stat() to check, #4695 - fix invalid archive error message - fix optional/non-optional location arg, #4541 - commit-time free space calc: ignore bad compact map entries, #4796 - ignore EACCES (errno 13) when hardlinking the old config, #4730 - --prefix / -P: fix processing, avoid argparse issue, #4769 New features: - enable placeholder usage in all extra archive arguments - new BORG_WORKAROUNDS mechanism, basesyncfile, #4710 - recreate: support --timestamp option, #4745 - support platforms without os.link (e.g. Android with Termux), #4901. if we don't have os.link, we just extract another copy instead of making a hardlink. - support linux platforms without sync_file_range (e.g. Android 7 with Termux), #4905 Other: - ignore --stats when given with --dry-run, but continue, #4373 - add some ProgressIndicator msgids to code / fix docs, #4935 - elaborate on "Calculating size" message - argparser: always use REPOSITORY in metavar, also use more consistent help phrasing. - check: improve error output for matching index size, see #4829 - docs: - changelog: add advisory about hashindex_set bug #4829 - better describe BORG_SECURITY_DIR, BORG_CACHE_DIR, #4919 - infos about cache security assumptions, #4900 - add FAQ describing difference between a local repo vs. repo on a server. - document how to test exclusion patterns without performing an actual backup - timestamps in the files cache are now usually ctime, #4583 - fix bad reference to borg compact (does not exist in 1.1), #4660 - create: borg 1.1 is not future any more - extract: document limitation "needs empty destination", #4598 - how to supply a passphrase, use crypto devices, #4549 - fix osxfuse github link in installation docs - add example of exclude-norecurse rule in help patterns - update macOS Brew link - add note about software for automating backups, #4581 - AUTHORS: mention copyright+license for bundled msgpack - fix various code blocks in the docs, #4708 - updated docs to cover use of temp directory on remote, #4545 - add restore docs, #4670 - add a pull backup / push restore how-to, #1552 - add FAQ how to retain original paths, #4532 - explain difference between --exclude and --pattern, #4118 - add FAQs for SSH connection issues, #3866 - improve password FAQ, #4591 - reiterate that 'file cache names are absolute' in FAQ - tests: - cope with ANY error when importing pytest into borg.testsuite, #4652 - fix broken test that relied on improper zlib assumptions - test_fuse: filter out selinux xattrs, #4574 - travis / vagrant: - misc python versions removed / changed (due to openssl 1.1 compatibility) or added (3.7 and 3.8, for better borg compatibility testing) - binary building is on python 3.5.9 now - vagrant: - add new boxes: ubuntu 18.04 and 20.04, debian 10 - update boxes: openindiana, darwin, netbsd - remove old boxes: centos 6 - darwin: updated osxfuse to 3.10.4 - use debian/ubuntu pip/virtualenv packages - rather use python 3.6.2 than 3.6.0, fixes coverage/sqlite3 issue - use requirements.d/development.lock.txt to avoid compat issues - travis: - darwin: backport some install code / order from master - remove deprecated keyword "sudo" from travis config - allow osx builds to fail, #4955 this is due to travis-ci frequently being so slow that the OS X builds just fail because they exceed 50 minutes and get killed by travis. Version 1.1.10 (2019-05-16) --------------------------- Fixes: - extract: hang on partial extraction with ssh: repo, when hardlink master is not matched/extracted and borg hangs on related slave hardlink, #4350 - lrucache: regularly remove old FDs, #4427 - avoid stale filehandle issues, #3265 - freebsd: make xattr platform code api compatible with linux, #3952 - use whitelist approach for borg serve, #4097 - borg command shall terminate with rc 2 for ImportErrors, #4424 - create: only run stat_simple_attrs() once, this increases backup with lots of unchanged files performance by ~ 5%. - prune: fix incorrect borg prune --stats output with --dry-run, #4373 - key export: emit user-friendly error if repo key is exported to a directory, #4348 New features: - bundle latest supported msgpack-python release (0.5.6), remove msgpack-python from setup.py install_requires - by default we use the bundled code now. optionally, we still support using an external msgpack (see hints in setup.py), but this requires solid requirements management within distributions and is not recommended. borgbackup will break if you upgrade msgpack to an unsupported version. - display msgpack version as part of sysinfo (e.g. in tracebacks) - timestamp for borg delete --info added, #4359 - enable placeholder usage in --comment and --glob-archives, #4559, #4495 Other: - serve: do not check python/libc for borg serve, #4483 - shell completions: borg diff second archive - release scripts: signing binaries with Qubes OS support - testing: - vagrant: upgrade openbsd box to 6.4 - travis-ci: lock test env to py 3.4 compatible versions, #4343 - get rid of confusing coverage warning, #2069 - rename test_mount_hardlinks to test_fuse_mount_hardlinks, so both can be excluded by "not test_fuse". - pure-py msgpack warning shall not make a lot of tests fail, #4558 - docs: - add "SSH Configuration" section to "borg serve", #3988, #636, #4485 - README: new URL for funding options - add a sample logging.conf in docs/misc, #4380 - elaborate on append-only mode docs, #3504 - installation: added Alpine Linux to distribution list, #4415 - usage.html: only modify window.location when redirecting, #4133 - add msgpack license to docs/3rd_party/msgpack - vagrant / binary builds: - use python 3.5.7 for builds - use osxfuse 3.8.3 Version 1.1.9 (2019-02-10) -------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. There is one exception though: If you upgrade from an unpatched borg < 1.0.9, please read that section above: "Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099)" - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. Fixes: - security fix: configure FUSE with "default_permissions", #3903 "default_permissions" is now enforced by borg by default to let the kernel check uid/gid/mode based permissions. "ignore_permissions" can be given to not enforce "default_permissions". - make "hostname" short, even on misconfigured systems, #4262 - fix free space calculation on macOS (and others?), #4289 - config: quit with error message when no key is provided, #4223 - recover_segment: handle too small segment files correctly, #4272 - correctly release memoryview, #4243 - avoid diaper pattern in configparser by opening files, #4263 - add "# cython: language_level=3" directive to .pyx files, #4214 - info: consider part files for "This archive" stats, #3522 - work around Microsoft WSL issue #645 (sync_file_range), #1961 New features: - add --rsh command line option to complement BORG_RSH env var, #1701 - init: --make-parent-dirs parent1/parent2/repo_dir, #4235 Other: - add archive name to check --repair output, #3447 - check for unsupported msgpack versions - shell completions: - new shell completions for borg 1.1.9 - more complete shell completions for borg mount -o - added shell completions for borg help - option arguments for zsh tab completion - docs: - add FAQ regarding free disk space check, #3905 - update BORG_PASSCOMMAND example and clarify variable expansion, #4249 - FAQ regarding change of compression settings, #4222 - add note about BSD flags to changelog, #4246 - improve logging in example automation script - add note about files changing during backup, #4081 - work around the backslash issue, #4280 - update release workflow using twine (docs, scripts), #4213 - add warnings on repository copies to avoid future problems, #4272 - tests: - fix the homebrew 1.9 issues on travis-ci, #4254 - fix duplicate test method name, #4311 Version 1.1.8 (2018-12-09) -------------------------- Fixes: - enforce storage quota if set by serve-command, #4093 - invalid locations: give err msg containing parsed location, #4179 - list repo: add placeholders for hostname and username, #4130 - on linux, symlinks can't have ACLs, so don't try to set any, #4044 New features: - create: added PATH::archive output on INFO log level - read a passphrase from a file descriptor specified in the BORG_PASSPHRASE_FD environment variable. Other: - docs: - option --format is required for some expensive-to-compute values for json borg list by default does not compute expensive values except when they are needed. whether they are needed is determined by the format, in standard mode as well as in --json mode. - tell that our binaries are x86/x64 amd/intel, bauerj has ARM - fixed wrong archive name pattern in CRUD benchmark help - fixed link to cachedir spec in docs, #4140 - tests: - stop using fakeroot on travis, avoids sporadic EISDIR errors, #2482 - xattr key names must start with "user." on linux - fix code so flake8 3.6 does not complain - explicitly convert environment variable to str, #4136 - fix DeprecationWarning: Flags not at the start of the expression, #4137 - support pytest4, #4172 - vagrant: - use python 3.5.6 for builds Version 1.1.7 (2018-08-11) -------------------------- Compatibility notes: - added support for Python 3.7 Fixes: - cache lock: use lock_wait everywhere to fix infinite wait, see #3968 - don't archive tagged dir when recursing an excluded dir, #3991 - py37 argparse: work around bad default in py 3.7.0a/b/rc, #3996 - py37 remove loggerDict.clear() from tearDown method, #3805 - some fixes for bugs which likely did not result in problems in practice: - fixed logic bug in platform module API version check - fixed xattr/acl function prototypes, added missing ones New features: - init: add warning to store both key and passphrase at safe place(s) - BORG_HOST_ID env var to work around all-zero MAC address issue, #3985 - borg debug dump-repo-objs --ghost (dump everything from segment files, including deleted or superseded objects or commit tags) - borg debug search-repo-objs (search in repo objects for hex bytes or strings) Other changes: - add Python 3.7 support - updated shell completions - call socket.gethostname only once - locking: better logging, add some asserts - borg debug dump-repo-objs: - filename layout improvements - use repository.scan() to get on-disk order - docs: - update installation instructions for macOS - added instructions to install fuse via homebrew - improve diff docs - added note that checkpoints inside files requires 1.1+ - add link to tempfile module - remove row/column-spanning from docs source, #4000 #3990 - tests: - fetch less data via os.urandom - add py37 env for tox - travis: add 3.7, remove 3.6-dev (we test with -dev in master) - vagrant / binary builds: - use osxfuse 3.8.2 - use own (uptodate) openindiana box Version 1.1.6 (2018-06-11) -------------------------- Compatibility notes: - 1.1.6 changes: - also allow msgpack-python 0.5.6. Fixes: - fix borg exception handling on ENOSPC error with xattrs, #3808 - prune: fix/improve overall progress display - borg config repo ... does not need cache/manifest/key, #3802 - debug dump-repo-objs should not depend on a manifest obj - pypi package: - include .coveragerc, needed by tox.ini - fix package long description, #3854 New features: - mount: add uid, gid, umask mount options - delete: - only commit once, #3823 - implement --dry-run, #3822 - check: - show progress while rebuilding missing manifest, #3787 - more --repair output - borg config --list , #3612 Other changes: - update msgpack requirement, #3753 - update bundled zstd to 1.3.4, #3745 - update bundled lz4 code to 1.8.2, #3870 - docs: - describe what BORG_LIBZSTD_PREFIX does - fix and deduplicate encryption quickstart docs, #3776 - vagrant: - FUSE for macOS: upgrade 3.7.1 to 3.8.0 - exclude macOS High Sierra upgrade on the darwin64 machine - remove borgbackup.egg-info dir in fs_init (after rsync) - use pyenv-based build/test on jessie32/62 - use local 32 and 64bit debian jessie boxes - use "vagrant" as username for new xenial box - travis OS X: use xcode 8.3 (not broken) Version 1.1.5 (2018-04-01) -------------------------- Compatibility notes: - 1.1.5 changes: - require msgpack-python >= 0.4.6 and < 0.5.0. 0.5.0+ dropped python 3.4 testing and also caused some other issues because the python package was renamed to msgpack and emitted some FutureWarning. Fixes: - create --list: fix that it was never showing M status, #3492 - create: fix timing for first checkpoint (read files cache early, init checkpoint timer after that), see #3394 - extract: set rc=1 when extracting damaged files with all-zero replacement chunks or with size inconsistencies, #3448 - diff: consider an empty file as different to a non-existing file, #3688 - files cache: improve exception handling, #3553 - ignore exceptions in scandir_inorder() caused by an implicit stat(), also remove unneeded sort, #3545 - fixed tab completion problem where a space is always added after path even when it shouldn't - build: do .h file content checks in binary mode, fixes build issue for non-ascii header files on pure-ascii locale platforms, #3544 #3639 - borgfs: fix patterns/paths processing, #3551 - config: add some validation, #3566 - repository config: add validation for max_segment_size, #3592 - set cache previous_location on load instead of save - remove platform.uname() call which caused library mismatch issues, #3732 - add exception handler around deprecated platform.linux_distribution() call - use same datetime object for {now} and {utcnow}, #3548 New features: - create: implement --stdin-name, #3533 - add chunker_params to borg archive info (--json) - BORG_SHOW_SYSINFO=no to hide system information from exceptions Other changes: - updated zsh completions for borg 1.1.4 - files cache related code cleanups - be more helpful when parsing invalid --pattern values, #3575 - be more clear in secure-erase warning message, #3591 - improve getpass user experience, #3689 - docs build: unicode problem fixed when using a py27-based sphinx - docs: - security: explicitly note what happens OUTSIDE the attack model - security: add note about combining compression and encryption - security: describe chunk size / proximity issue, #3687 - quickstart: add note about permissions, borg@localhost, #3452 - quickstart: add introduction to repositories & archives, #3620 - recreate --recompress: add missing metavar, clarify description, #3617 - improve logging docs, #3549 - add an example for --pattern usage, #3661 - clarify path semantics when matching, #3598 - link to offline documentation from README, #3502 - add docs on how to verify a signed release with GPG, #3634 - chunk seed is generated per repository (not: archive) - better formatting of CPU usage documentation, #3554 - extend append-only repo rollback docs, #3579 - tests: - fix erroneously skipped zstd compressor tests, #3606 - skip a test if argparse is broken, #3705 - vagrant: - xenial64 box now uses username 'vagrant', #3707 - move cleanup steps to fs_init, #3706 - the boxcutter wheezy boxes are 404, use local ones - update to Python 3.5.5 (for binary builds) Version 1.1.4 (2017-12-31) -------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. There is one exception though: If you upgrade from an unpatched borg < 1.0.9, please read that section above: "Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099)" - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - borg 1.1.4 changes: - zstd compression is new in borg 1.1.4, older borg can't handle it. - new minimum requirements for the compression libraries - if the required versions (header and lib) can't be found at build time, bundled code will be used: - added requirement: libzstd >= 1.3.0 (bundled: 1.3.2) - updated requirement: liblz4 >= 1.7.0 / r129 (bundled: 1.8.0) Fixes: - check: data corruption fix: fix for borg check --repair malfunction, #3444. See the more detailed notes close to the top of this document. - delete: also delete security dir when deleting a repo, #3427 - prune: fix building the "borg prune" man page, #3398 - init: use given --storage-quota for local repo, #3470 - init: properly quote repo path in output - fix startup delay with dns-only own fqdn resolving, #3471 New features: - added zstd compression. try it! - added placeholder {reverse-fqdn} for fqdn in reverse notation - added BORG_BASE_DIR environment variable, #3338 Other changes: - list help topics when invalid topic is requested - fix lz4 deprecation warning, requires lz4 >= 1.7.0 (r129) - add parens for C preprocessor macro argument usages (did not cause malfunction) - exclude broken pytest 3.3.0 release - updated fish/bash completions - init: more clear exception messages for borg create, #3465 - docs: - add auto-generated docs for borg config - don't generate HTML docs page for borgfs, #3404 - docs update for lz4 b2 zstd changes - add zstd to compression help, readme, docs - update requirements and install docs about bundled lz4 and zstd - refactored build of the compress and crypto.low_level extensions, #3415: - move some lib/build related code to setup_{zstd,lz4,b2}.py - bundle lz4 1.8.0 (requirement: >= 1.7.0 / r129) - bundle zstd 1.3.2 (requirement: >= 1.3.0) - blake2 was already bundled - rename BORG_LZ4_PREFIX env var to BORG_LIBLZ4_PREFIX for better consistency: we also have BORG_LIBB2_PREFIX and BORG_LIBZSTD_PREFIX now. - add prefer_system_lib* = True settings to setup.py - by default the build will prefer a shared library over the bundled code, if library and headers can be found and meet the minimum requirements. Version 1.1.3 (2017-11-27) -------------------------- Fixes: - Security Fix for CVE-2017-15914: Incorrect implementation of access controls allows remote users to override repository restrictions in Borg servers. A user able to access a remote Borg SSH server is able to circumvent access controls post-authentication. Affected releases: 1.1.0, 1.1.1, 1.1.2. Releases 1.0.x are NOT affected. - crc32: deal with unaligned buffer, add tests - this broke borg on older ARM CPUs that can not deal with unaligned 32bit memory accesses and raise a bus error in such cases. the fix might also improve performance on some CPUs as all 32bit memory accesses by the crc32 code are properly aligned now. #3317 - mount: fixed support of --consider-part-files and do not show .borg_part_N files by default in the mounted FUSE filesystem. #3347 - fixed cache/repo timestamp inconsistency message, highlight that information is obtained from security dir (deleting the cache will not bypass this error in case the user knows this is a legitimate repo). - borgfs: don't show sub-command in borgfs help, #3287 - create: show an error when --dry-run and --stats are used together, #3298 New features: - mount: added exclusion group options and paths, #2138 Reused some code to support similar options/paths as borg extract offers - making good use of these to only mount a smaller subset of dirs/files can speed up mounting a lot and also will consume way less memory. borg mount [options] repo_or_archive mountpoint path [paths...] paths: you can just give some "root paths" (like for borg extract) to only partially populate the FUSE filesystem. new options: --exclude[-from], --pattern[s-from], --strip-components - create/extract: support st_birthtime on platforms supporting it, #3272 - add "borg config" command for querying/setting/deleting config values, #3304 Other changes: - clean up and simplify packaging (only package committed files, do not install .c/.h/.pyx files) - docs: - point out tuning options for borg create, #3239 - add instructions for using ntfsclone, zerofree, #81 - move image backup-related FAQ entries to a new page - clarify key aliases for borg list --format, #3111 - mention break-lock in checkpointing FAQ entry, #3328 - document sshfs rename workaround, #3315 - add FAQ about removing files from existing archives - add FAQ about different prune policies - usage and man page for borgfs, #3216 - clarify create --stats duration vs. wall time, #3301 - clarify encrypted key format for borg key export, #3296 - update release checklist about security fixes - document good and problematic option placements, fix examples, #3356 - add note about using --nobsdflags to avoid speed penalty related to bsdflags, #3239 - move most of support section to www.borgbackup.org Version 1.1.2 (2017-11-05) -------------------------- Fixes: - fix KeyError crash when talking to borg server < 1.0.7, #3244 - extract: set bsdflags last (include immutable flag), #3263 - create: don't do stat() call on excluded-norecurse directory, fix exception handling for stat() call, #3209 - create --stats: do not count data volume twice when checkpointing, #3224 - recreate: move chunks_healthy when excluding hardlink master, #3228 - recreate: get rid of chunks_healthy when rechunking (does not match), #3218 - check: get rid of already existing not matching chunks_healthy metadata, #3218 - list: fix stdout broken pipe handling, #3245 - list/diff: remove tag-file options (not used), #3226 New features: - bash, zsh and fish shell auto-completions, see scripts/shell_completions/ - added BORG_CONFIG_DIR env var, #3083 Other changes: - docs: - clarify using a blank passphrase in keyfile mode - mention "!" (exclude-norecurse) type in "patterns" help - document to first heal before running borg recreate to re-chunk stuff, because that will have to get rid of chunks_healthy metadata. - more than 23 is not supported for CHUNK_MAX_EXP, #3115 - borg does not respect nodump flag by default any more - clarify same-filesystem requirement for borg upgrade, #2083 - update / rephrase cygwin / WSL status, #3174 - improve docs about --stats, #3260 - vagrant: openindiana new clang package Already contained in 1.1.1 (last minute fix): - arg parsing: fix fallback function, refactor, #3205. This is a fixup for #3155, which was broken on at least python <= 3.4.2. Version 1.1.1 (2017-10-22) -------------------------- Compatibility notes: - The deprecated --no-files-cache is not a global/common option any more, but only available for borg create (it is not needed for anything else). Use --files-cache=disabled instead of --no-files-cache. - The nodump flag ("do not backup this file") is not honoured any more by default because this functionality (esp. if it happened by error or unexpected) was rather confusing and unexplainable at first to users. If you want that "do not backup NODUMP-flagged files" behaviour, use: borg create --exclude-nodump ... - If you are on Linux and do not need bsdflags archived, consider using ``--nobsdflags`` with ``borg create`` to avoid additional syscalls and speed up backup creation. Fixes: - borg recreate: correctly compute part file sizes. fixes cosmetic, but annoying issue as borg check complains about size inconsistencies of part files in affected archives. you can solve that by running borg recreate on these archives, see also #3157. - bsdflags support: do not open BLK/CHR/LNK files, avoid crashes and slowness, #3130 - recreate: don't crash on attic archives w/o time_end, #3109 - don't crash on repository filesystems w/o hardlink support, #3107 - don't crash in first part of truncate_and_unlink, #3117 - fix server-side IndexError crash with clients < 1.0.7, #3192 - don't show traceback if only a global option is given, show help, #3142 - cache: use SaveFile for more safety, #3158 - init: fix wrong encryption choices in command line parser, fix missing "authenticated-blake2", #3103 - move --no-files-cache from common to borg create options, #3146 - fix detection of non-local path (failed on ..filename), #3108 - logging with fileConfig: set json attr on "borg" logger, #3114 - fix crash with relative BORG_KEY_FILE, #3197 - show excluded dir with "x" for tagged dirs / caches, #3189 New features: - create: --nobsdflags and --exclude-nodump options, #3160 - extract: --nobsdflags option, #3160 Other changes: - remove annoying hardlinked symlinks warning, #3175 - vagrant: use self-made FreeBSD 10.3 box, #3022 - travis: don't brew update, hopefully fixes #2532 - docs: - readme: -e option is required in borg 1.1 - add example showing --show-version --show-rc - use --format rather than --list-format (deprecated) in example - update docs about hardlinked symlinks limitation Version 1.1.0 (2017-10-07) -------------------------- Compatibility notes: - borg command line: do not put options in between positional arguments This sometimes works (e.g. it worked in borg 1.0.x), but can easily stop working if we make positional arguments optional (like it happened for borg create's "paths" argument in 1.1). There are also places in borg 1.0 where we do that, so it doesn't work there in general either. #3356 Good: borg create -v --stats repo::archive path Good: borg create repo::archive path -v --stats Bad: borg create repo::archive -v --stats path Fixes: - fix LD_LIBRARY_PATH restoration for subprocesses, #3077 - "auto" compression: make sure expensive compression is actually better, otherwise store lz4 compressed data we already computed. Other changes: - docs: - FAQ: we do not implement futile attempts of ETA / progress displays - manpage: fix typos, update homepage - implement simple "issue" role for manpage generation, #3075 Version 1.1.0rc4 (2017-10-01) ----------------------------- Compatibility notes: - A borg server >= 1.1.0rc4 does not support borg clients 1.1.0b3-b5. #3033 - The files cache is now controlled differently and has a new default mode: - the files cache now uses ctime by default for improved file change detection safety. You can still use mtime for more speed and less safety. - --ignore-inode is deprecated (use --files-cache=... without "inode") - --no-files-cache is deprecated (use --files-cache=disabled) New features: - --files-cache - implement files cache mode control, #911 You can now control the files cache mode using this option: --files-cache={ctime,mtime,size,inode,rechunk,disabled} (only some combinations are supported). See the docs for details. Fixes: - remote progress/logging: deal with partial lines, #2637 - remote progress: flush json mode output - fix subprocess environments, #3050 (and more) Other changes: - remove client_supports_log_v3 flag, #3033 - exclude broken Cython 0.27(.0) in requirements, #3066 - vagrant: - upgrade to FUSE for macOS 3.7.1 - use Python 3.5.4 to build the binaries - docs: - security: change-passphrase only changes the passphrase, #2990 - fixed/improved borg create --compression examples, #3034 - add note about metadata dedup and --no[ac]time, #2518 - twitter account @borgbackup now, better visible, #2948 - simplified rate limiting wrapper in FAQ Version 1.1.0rc3 (2017-09-10) ----------------------------- New features: - delete: support naming multiple archives, #2958 Fixes: - repo cleanup/write: invalidate cached FDs, #2982 - fix datetime.isoformat() microseconds issues, #2994 - recover_segment: use mmap(), lower memory needs, #2987 Other changes: - with-lock: close segment file before invoking subprocess - keymanager: don't depend on optional readline module, #2976 - docs: - fix macOS keychain integration command - show/link new screencasts in README, #2936 - document utf-8 locale requirement for json mode, #2273 - vagrant: clean up shell profile init, user name, #2977 - test_detect_attic_repo: don't test mount, #2975 - add debug logging for repository cleanup Version 1.1.0rc2 (2017-08-28) ----------------------------- Compatibility notes: - list: corrected mix-up of "isomtime" and "mtime" formats. Previously, "isomtime" was the default but produced a verbose human format, while "mtime" produced a ISO-8601-like format. The behaviours have been swapped (so "mtime" is human, "isomtime" is ISO-like), and the default is now "mtime". "isomtime" is now a real ISO-8601 format ("T" between date and time, not a space). New features: - None. Fixes: - list: fix weird mixup of mtime/isomtime - create --timestamp: set start time, #2957 - ignore corrupt files cache, #2939 - migrate locks to child PID when daemonize is used - fix exitcode of borg serve, #2910 - only compare contents when chunker params match, #2899 - umount: try fusermount, then try umount, #2863 Other changes: - JSON: use a more standard ISO 8601 datetime format, #2376 - cache: write_archive_index: truncate_and_unlink on error, #2628 - detect non-upgraded Attic repositories, #1933 - delete various nogil and threading related lines - coala / pylint related improvements - docs: - renew asciinema/screencasts, #669 - create: document exclusion through nodump, #2949 - minor formatting fixes - tar: tarpipe example - improve "with-lock" and "info" docs, #2869 - detail how to use macOS/GNOME/KDE keyrings for repo passwords, #392 - travis: only short-circuit docs-only changes for pull requests - vagrant: - netbsd: bash is already installed - fix netbsd version in PKG_PATH - add exe location to PATH when we build an exe Version 1.1.0rc1 (2017-07-24) ----------------------------- Compatibility notes: - delete: removed short option for --cache-only New features: - support borg list repo --format {comment} {bcomment} {end}, #2081 - key import: allow reading from stdin, #2760 Fixes: - with-lock: avoid creating segment files that might be overwritten later, #1867 - prune: fix checkpoints processing with --glob-archives - FUSE: versions view: keep original file extension at end, #2769 - fix --last, --first: do not accept values <= 0, fix reversed archive ordering with --last - include testsuite data (attic.tar.gz) when installing the package - use limited unpacker for outer key, for manifest (both security precautions), #2174 #2175 - fix bashism in shell scripts, #2820, #2816 - cleanup endianness detection, create _endian.h, fixes build on alpine linux, #2809 - fix crash with --no-cache-sync (give known chunk size to chunk_incref), #2853 Other changes: - FUSE: versions view: linear numbering by archive time - split up interval parsing from filtering for --keep-within, #2610 - add a basic .editorconfig, #2734 - use archive creation time as mtime for FUSE mount, #2834 - upgrade FUSE for macOS (osxfuse) from 3.5.8 to 3.6.3, #2706 - hashindex: speed up by replacing modulo with "if" to check for wraparound - coala checker / pylint: fixed requirements and .coafile, more ignores - borg upgrade: name backup directories as 'before-upgrade', #2811 - add .mailmap - some minor changes suggested by lgtm.com - docs: - better explanation of the --ignore-inode option relevance, #2800 - fix openSUSE command and add openSUSE section - simplify ssh authorized_keys file using "restrict", add legacy note, #2121 - mount: show usage of archive filters - mount: add repository example, #2462 - info: update and add examples, #2765 - prune: include example - improved style / formatting - improved/fixed segments_per_dir docs - recreate: fix wrong "remove unwanted files" example - reference list of status chars in borg recreate --filter description - update source-install docs about doc build dependencies, #2795 - cleanup installation docs - file system requirements, update segs per dir - fix checkpoints/parts reference in FAQ, #2859 - code: - hashindex: don't pass side effect into macro - crypto low_level: don't mutate local bytes() - use dash_open function to open file or "-" for stdin/stdout - archiver: argparse cleanup / refactoring - shellpattern: add match_end arg - tests: added some additional unit tests, some fixes, #2700 #2710 - vagrant: fix setup of cygwin, add Debian 9 "stretch" - travis: don't perform full travis build on docs-only changes, #2531 Version 1.1.0b6 (2017-06-18) ---------------------------- Compatibility notes: - Running "borg init" via a "borg serve --append-only" server will *not* create an append-only repository anymore. Use "borg init --append-only" to initialize an append-only repository. - Repositories in the "repokey" and "repokey-blake2" modes with an empty passphrase are now treated as unencrypted repositories for security checks (e.g. BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK). Previously there would be no prompts nor messages if an unknown repository in one of these modes with an empty passphrase was encountered. This would allow an attacker to swap a repository, if one assumed that the lack of password prompts was due to a set BORG_PASSPHRASE. Since the "trick" does not work if BORG_PASSPHRASE is set, this does generally not affect scripts. - Repositories in the "authenticated" mode are now treated as the unencrypted repositories they are. - The client-side temporary repository cache now holds unencrypted data for better speed. - borg init: removed the short form of --append-only (-a). - borg upgrade: removed the short form of --inplace (-i). New features: - reimplemented the RepositoryCache, size-limited caching of decrypted repo contents, integrity checked via xxh64. #2515 - reduced space usage of chunks.archive.d. Existing caches are migrated during a cache sync. #235 #2638 - integrity checking using xxh64 for important files used by borg, #1101: - repository: index and hints files - cache: chunks and files caches, chunks.archive.d - improve cache sync speed, #1729 - create: new --no-cache-sync option - add repository mandatory feature flags infrastructure, #1806 - Verify most operations against SecurityManager. Location, manifest timestamp and key types are now checked for almost all non-debug commands. #2487 - implement storage quotas, #2517 - serve: add --restrict-to-repository, #2589 - BORG_PASSCOMMAND: use external tool providing the key passphrase, #2573 - borg export-tar, #2519 - list: --json-lines instead of --json for archive contents, #2439 - add --debug-profile option (and also "borg debug convert-profile"), #2473 - implement --glob-archives/-a, #2448 - normalize authenticated key modes for better naming consistency: - rename "authenticated" to "authenticated-blake2" (uses blake2b) - implement "authenticated" mode (uses hmac-sha256) Fixes: - hashindex: read/write indices >2 GiB on 32bit systems, better error reporting, #2496 - repository URLs: implement IPv6 address support and also more informative error message when parsing fails. - mount: check whether llfuse is installed before asking for passphrase, #2540 - mount: do pre-mount checks before opening repository, #2541 - FUSE: - fix crash if empty (None) xattr is read, #2534 - fix read(2) caching data in metadata cache - fix negative uid/gid crash (fix crash when mounting archives of external drives made on cygwin), #2674 - redo ItemCache, on top of object cache - use decrypted cache - remove unnecessary normpaths - serve: ignore --append-only when initializing a repository (borg init), #2501 - serve: fix incorrect type of exception_short for Errors, #2513 - fix --exclude and --exclude-from recursing into directories, #2469 - init: don't allow creating nested repositories, #2563 - --json: fix encryption[mode] not being the cmdline name - remote: propagate Error.traceback correctly - fix remote logging and progress, #2241 - implement --debug-topic for remote servers - remote: restore "Remote:" prefix (as used in 1.0.x) - rpc negotiate: enable v3 log protocol only for supported clients - fix --progress and logging in general for remote - fix parse_version, add tests, #2556 - repository: truncate segments (and also some other files) before unlinking, #2557 - recreate: keep timestamps as in original archive, #2384 - recreate: if single archive is not processed, exit 2 - patterns: don't recurse with ! / --exclude for pf:, #2509 - cache sync: fix n^2 behaviour in lookup_name - extract: don't write to disk with --stdout (affected non-regular-file items), #2645 - hashindex: implement KeyError, more tests Other changes: - remote: show path in PathNotAllowed - consider repokey w/o passphrase == unencrypted, #2169 - consider authenticated mode == unencrypted, #2503 - restrict key file names, #2560 - document follow_symlinks requirements, check libc, use stat and chown with follow_symlinks=False, #2507 - support common options on the main command, #2508 - support common options on mid-level commands (e.g. borg *key* export) - make --progress a common option - increase DEFAULT_SEGMENTS_PER_DIR to 1000 - chunker: fix invalid use of types (function only used by tests) - chunker: don't do uint32_t >> 32 - FUSE: - add instrumentation (--debug and SIGUSR1/SIGINFO) - reduced memory usage for repository mounts by lazily instantiating archives - improved archive load times - info: use CacheSynchronizer & HashIndex.stats_against (better performance) - docs: - init: document --encryption as required - security: OpenSSL usage - security: used implementations; note python libraries - security: security track record of OpenSSL and msgpack - patterns: document denial of service (regex, wildcards) - init: note possible denial of service with "none" mode - init: document SHA extension is supported in OpenSSL and thus SHA is faster on AMD Ryzen than blake2b. - book: use A4 format, new builder option format. - book: create appendices - data structures: explain repository compaction - data structures: add chunk layout diagram - data structures: integrity checking - data structures: demingle cache and repo index - Attic FAQ: separate section for attic stuff - FAQ: I get an IntegrityError or similar - what now? - FAQ: Can I use Borg on SMR hard drives?, #2252 - FAQ: specify "using inline shell scripts" - add systemd warning regarding placeholders, #2543 - xattr: document API - add docs/misc/borg-data-flow data flow chart - debugging facilities - README: how to help the project, #2550 - README: add bountysource badge, #2558 - fresh new theme + tweaking - logo: vectorized (PDF and SVG) versions - frontends: use headlines - you can link to them - mark --pattern, --patterns-from as experimental - highlight experimental features in online docs - remove regex based pattern examples, #2458 - nanorst for "borg help TOPIC" and --help - split deployment - deployment: hosting repositories - deployment: automated backups to a local hard drive - development: vagrant, windows10 requirements - development: update docs remarks - split usage docs, #2627 - usage: avoid bash highlight, [options] instead of - usage: add benchmark page - helpers: truncate_and_unlink doc - don't suggest to leak BORG_PASSPHRASE - internals: columnize rather long ToC [webkit fixup] internals: manifest & feature flags - internals: more HashIndex details - internals: fix ASCII art equations - internals: edited obj graph related sections a bit - internals: layers image + description - fix way too small figures in pdf - index: disable syntax highlight (bash) - improve options formatting, fix accidental block quotes - testing / checking: - add support for using coala, #1366 - testsuite: add ArchiverCorruptionTestCase - do not test logger name, #2504 - call setup_logging after destroying logging config - testsuite.archiver: normalise pytest.raises vs. assert_raises - add test for preserved intermediate folder permissions, #2477 - key: add round-trip test - remove attic dependency of the tests, #2505 - enable remote tests on cygwin - tests: suppress tar's future timestamp warning - cache sync: add more refcount tests - repository: add tests, including corruption tests - vagrant: - control VM cpus and pytest workers via env vars VMCPUS and XDISTN - update cleaning workdir - fix openbsd shell - add OpenIndiana - packaging: - binaries: don't bundle libssl - setup.py clean to remove compiled files - fail in borg package if version metadata is very broken (setuptools_scm) - repo / code structure: - create borg.algorithms and borg.crypto packages - algorithms: rename crc32 to checksums - move patterns to module, #2469 - gitignore: complete paths for src/ excludes - cache: extract CacheConfig class - implement IntegrityCheckedFile + Detached variant, #2502 #1688 - introduce popen_with_error_handling to handle common user errors Version 1.1.0b5 (2017-04-30) ---------------------------- Compatibility notes: - BORG_HOSTNAME_IS_UNIQUE is now on by default. - removed --compression-from feature - recreate: add --recompress flag, unify --always-recompress and --recompress Fixes: - catch exception for os.link when hardlinks are not supported, #2405 - borg rename / recreate: expand placeholders, #2386 - generic support for hardlinks (files, devices, FIFOs), #2324 - extract: also create parent dir for device files, if needed, #2358 - extract: if a hardlink master is not in the to-be-extracted subset, the "x" status was not displayed for it, #2351 - embrace y2038 issue to support 32bit platforms: clamp timestamps to int32, #2347 - verify_data: fix IntegrityError handling for defect chunks, #2442 - allow excluding parent and including child, #2314 Other changes: - refactor compression decision stuff - change global compression default to lz4 as well, to be consistent with --compression defaults. - placeholders: deny access to internals and other unspecified stuff - clearer error message for unrecognized placeholder - more clear exception if borg check does not help, #2427 - vagrant: upgrade FUSE for macOS to 3.5.8, #2346 - linux binary builds: get rid of glibc 2.13 dependency, #2430 - docs: - placeholders: document escaping - serve: env vars in original commands are ignored - tell what kind of hardlinks we support - more docs about compression - LICENSE: use canonical formulation ("copyright holders and contributors" instead of "author") - document borg init behaviour via append-only borg serve, #2440 - be clear about what buzhash is used for, #2390 - add hint about chunker params, #2421 - clarify borg upgrade docs, #2436 - FAQ to explain warning when running borg check --repair, #2341 - repository file system requirements, #2080 - pre-install considerations - misc. formatting / crossref fixes - tests: - enhance travis setuptools_scm situation - add extra test for the hashindex - fix invalid param issue in benchmarks These belong to 1.1.0b4 release, but did not make it into changelog by then: - vagrant: increase memory for parallel testing - lz4 compress: lower max. buffer size, exception handling - add docstring to do_benchmark_crud - patterns help: mention path full-match in intro Version 1.1.0b4 (2017-03-27) ---------------------------- Compatibility notes: - init: the --encryption argument is mandatory now (there are several choices) - moved "borg migrate-to-repokey" to "borg key migrate-to-repokey". - "borg change-passphrase" is deprecated, use "borg key change-passphrase" instead. - the --exclude-if-present option now supports tagging a folder with any filesystem object type (file, folder, etc), instead of expecting only files as tags, #1999 - the --keep-tag-files option has been deprecated in favor of the new --keep-exclude-tags, to account for the change mentioned above. - use lz4 compression by default, #2179 New features: - JSON API to make developing frontends and automation easier (see :ref:`json_output`) - add JSON output to commands: `borg create/list/info --json ...`. - add --log-json option for structured logging output. - add JSON progress information, JSON support for confirmations (yes()). - add two new options --pattern and --patterns-from as discussed in #1406 - new path full match pattern style (pf:) for very fast matching, #2334 - add 'debug dump-manifest' and 'debug dump-archive' commands - add 'borg benchmark crud' command, #1788 - new 'borg delete --force --force' to delete severely corrupted archives, #1975 - info: show utilization of maximum archive size, #1452 - list: add dsize and dcsize keys, #2164 - paperkey.html: Add interactive html template for printing key backups. - key export: add qr html export mode - securely erase config file (which might have old encryption key), #2257 - archived file items: add size to metadata, 'borg extract' and 'borg check' do check the file size for consistency, FUSE uses precomputed size from Item. Fixes: - fix remote speed regression introduced in 1.1.0b3, #2185 - fix regression handling timestamps beyond 2262 (revert bigint removal), introduced in 1.1.0b3, #2321 - clamp (nano)second values to unproblematic range, #2304 - hashindex: rebuild hashtable if we have too little empty buckets (performance fix), #2246 - Location regex: fix bad parsing of wrong syntax - ignore posix_fadvise errors in repository.py, #2095 - borg rpc: use limited msgpack.Unpacker (security precaution), #2139 - Manifest: Make sure manifest timestamp is strictly monotonically increasing. - create: handle BackupOSError on a per-path level in one spot - create: clarify -x option / meaning of "same filesystem" - create: don't create hard link refs to failed files - archive check: detect and fix missing all-zero replacement chunks, #2180 - files cache: update inode number when --ignore-inode is used, #2226 - fix decompression exceptions crashing ``check --verify-data`` and others instead of reporting integrity error, #2224 #2221 - extract: warning for unextracted big extended attributes, #2258, #2161 - mount: umount on SIGINT/^C when in foreground - mount: handle invalid hard link refs - mount: fix huge RAM consumption when mounting a repository (saves number of archives * 8 MiB), #2308 - hashindex: detect mingw byte order #2073 - hashindex: fix wrong skip_hint on hashindex_set when encountering tombstones, the regression was introduced in #1748 - fix ChunkIndex.__contains__ assertion for big-endian archs - fix borg key/debug/benchmark crashing without subcommand, #2240 - Location: accept //servername/share/path - correct/refactor calculation of unique/non-unique chunks - extract: fix missing call to ProgressIndicator.finish - prune: fix error msg, it is --keep-within, not --within - fix "auto" compression mode bug (not compressing), #2331 - fix symlink item fs size computation, #2344 Other changes: - remote repository: improved async exception processing, #2255 #2225 - with --compression auto,C, only use C if lz4 achieves at least 3% compression - PatternMatcher: only normalize path once, #2338 - hashindex: separate endian-dependent defs from endian detection - migrate-to-repokey: ask using canonical_path() as we do everywhere else. - SyncFile: fix use of fd object after close - make LoggedIO.close_segment reentrant - creating a new segment: use "xb" mode, #2099 - redo key_creator, key_factory, centralise key knowledge, #2272 - add return code functions, #2199 - list: only load cache if needed - list: files->items, clarifications - list: add "name" key for consistency with info cmd - ArchiveFormatter: add "start" key for compatibility with "info" - RemoteRepository: account rx/tx bytes - setup.py build_usage/build_man/build_api fixes - Manifest.in: simplify, exclude .so, .dll and .orig, #2066 - FUSE: get rid of chunk accounting, st_blocks = ceil(size / blocksize). - tests: - help python development by testing 3.6-dev - test for borg delete --force - vagrant: - freebsd: some fixes, #2067 - darwin64: use osxfuse 3.5.4 for tests / to build binaries - darwin64: improve VM settings - use python 3.5.3 to build binaries, #2078 - upgrade pyinstaller from 3.1.1+ to 3.2.1 - pyinstaller: use fixed AND freshly compiled bootloader, #2002 - pyinstaller: automatically builds bootloader if missing - docs: - create really nice man pages - faq: mention --remote-ratelimit in bandwidth limit question - fix caskroom link, #2299 - docs/security: reiterate that RPC in Borg does no networking - docs/security: counter tracking, #2266 - docs/development: update merge remarks - address SSH batch mode in docs, #2202 #2270 - add warning about running build_usage on Python >3.4, #2123 - one link per distro in the installation page - improve --exclude-if-present and --keep-exclude-tags, #2268 - improve automated backup script in doc, #2214 - improve remote-path description - update docs for create -C default change (lz4) - document relative path usage, #1868 - document snapshot usage, #2178 - corrected some stuff in internals+security - internals: move toctree to after the introduction text - clarify metadata kind, manifest ops - key enc: correct / clarify some stuff, link to internals/security - datas: enc: 1.1.x mas different MACs - datas: enc: correct factual error -- no nonce involved there. - make internals.rst an index page and edit it a bit - add "Cryptography in Borg" and "Remote RPC protocol security" sections - document BORG_HOSTNAME_IS_UNIQUE, #2087 - FAQ by categories as proposed by @anarcat in #1802 - FAQ: update Which file types, attributes, etc. are *not* preserved? - development: new branching model for git repository - development: define "ours" merge strategy for auto-generated files - create: move --exclude note to main doc - create: move item flags to main doc - fix examples using borg init without -e/--encryption - list: don't print key listings in fat (html + man) - remove Python API docs (were very incomplete, build problems on RTFD) - added FAQ section about backing up root partition Version 1.1.0b3 (2017-01-15) ---------------------------- Compatibility notes: - borg init: removed the default of "--encryption/-e", #1979 This was done so users do a informed decision about -e mode. Bug fixes: - borg recreate: don't rechunkify unless explicitly told so - borg info: fixed bug when called without arguments, #1914 - borg init: fix free space check crashing if disk is full, #1821 - borg debug delete/get obj: fix wrong reference to exception - fix processing of remote ~/ and ~user/ paths (regressed since 1.1.0b1), #1759 - posix platform module: only build / import on non-win32 platforms, #2041 New features: - new CRC32 implementations that are much faster than the zlib one used previously, #1970 - add blake2b key modes (use blake2b as MAC). This links against system libb2, if possible, otherwise uses bundled code - automatically remove stale locks - set BORG_HOSTNAME_IS_UNIQUE env var to enable stale lock killing. If set, stale locks in both cache and repository are deleted. #562 #1253 - borg info : print general repo information, #1680 - borg check --first / --last / --sort / --prefix, #1663 - borg mount --first / --last / --sort / --prefix, #1542 - implement "health" item formatter key, #1749 - BORG_SECURITY_DIR to remember security related infos outside the cache. Key type, location and manifest timestamp checks now survive cache deletion. This also means that you can now delete your cache and avoid previous warnings, since Borg can still tell it's safe. - implement BORG_NEW_PASSPHRASE, #1768 Other changes: - borg recreate: - remove special-cased --dry-run - update --help - remove bloat: interruption blah, autocommit blah, resuming blah - re-use existing checkpoint functionality - archiver tests: add check_cache tool - lints refcounts - fixed cache sync performance regression from 1.1.0b1 onwards, #1940 - syncing the cache without chunks.archive.d (see :ref:`disable_archive_chunks`) now avoids any merges and is thus faster, #1940 - borg check --verify-data: faster due to linear on-disk-order scan - borg debug-xxx commands removed, we use "debug xxx" subcommands now, #1627 - improve metadata handling speed - shortcut hashindex_set by having hashindex_lookup hint about address - improve / add progress displays, #1721 - check for index vs. segment files object count mismatch - make RPC protocol more extensible: use named parameters. - RemoteRepository: misc. code cleanups / refactors - clarify cache/repository README file - docs: - quickstart: add a comment about other (remote) filesystems - quickstart: only give one possible ssh url syntax, all others are documented in usage chapter. - mention file:// - document repo URLs / archive location - clarify borg diff help, #980 - deployment: synthesize alternative --restrict-to-path example - improve cache / index docs, esp. files cache docs, #1825 - document using "git merge 1.0-maint -s recursive -X rename-threshold=20%" for avoiding troubles when merging the 1.0-maint branch into master. - tests: - FUSE tests: catch ENOTSUP on freebsd - FUSE tests: test troublesome xattrs last - fix byte range error in test, #1740 - use monkeypatch to set env vars, but only on pytest based tests. - point XDG_*_HOME to temp dirs for tests, #1714 - remove all BORG_* env vars from the outer environment Version 1.1.0b2 (2016-10-01) ---------------------------- Bug fixes: - fix incorrect preservation of delete tags, leading to "object count mismatch" on borg check, #1598. This only occurred with 1.1.0b1 (not with 1.0.x) and is normally fixed by running another borg create/delete/prune. - fix broken --progress for double-cell paths (e.g. CJK), #1624 - borg recreate: also catch SIGHUP - FUSE: - fix hardlinks in versions view, #1599 - add parameter check to ItemCache.get to make potential failures more clear New features: - Archiver, RemoteRepository: add --remote-ratelimit (send data) - borg help compression, #1582 - borg check: delete chunks with integrity errors, #1575, so they can be "repaired" immediately and maybe healed later. - archives filters concept (refactoring/unifying older code) - covers --first/--last/--prefix/--sort-by options - currently used for borg list/info/delete Other changes: - borg check --verify-data slightly tuned (use get_many()) - change {utcnow} and {now} to ISO-8601 format ("T" date/time separator) - repo check: log transaction IDs, improve object count mismatch diagnostic - Vagrantfile: use TW's fresh-bootloader pyinstaller branch - fix module names in api.rst - hashindex: bump api_version Version 1.1.0b1 (2016-08-28) ---------------------------- New features: - new commands: - borg recreate: re-create existing archives, #787 #686 #630 #70, also see #757, #770. - selectively remove files/dirs from old archives - re-compress data - re-chunkify data, e.g. to have upgraded Attic / Borg 0.xx archives deduplicate with Borg 1.x archives or to experiment with chunker-params. - borg diff: show differences between archives - borg with-lock: execute a command with the repository locked, #990 - borg create: - Flexible compression with pattern matching on path/filename, and LZ4 heuristic for deciding compressibility, #810, #1007 - visit files in inode order (better speed, esp. for large directories and rotating disks) - in-file checkpoints, #1217 - increased default checkpoint interval to 30 minutes (was 5 minutes), #896 - added uuid archive format tag, #1151 - save mountpoint directories with --one-file-system, makes system restore easier, #1033 - Linux: added support for some BSD flags, #1050 - add 'x' status for excluded paths, #814 - also means files excluded via UF_NODUMP, #1080 - borg check: - will not produce the "Checking segments" output unless new --progress option is passed, #824. - --verify-data to verify data cryptographically on the client, #975 - borg list, #751, #1179 - removed {formatkeys}, see "borg list --help" - --list-format is deprecated, use --format instead - --format now also applies to listing archives, not only archive contents, #1179 - now supports the usual [PATH [PATHS…]] syntax and excludes - new keys: csize, num_chunks, unique_chunks, NUL - supports guaranteed_available hashlib hashes (to avoid varying functionality depending on environment), which includes the SHA1 and SHA2 family as well as MD5 - borg prune: - to better visualize the "thinning out", we now list all archives in reverse time order. rephrase and reorder help text. - implement --keep-last N via --keep-secondly N, also --keep-minutely. assuming that there is not more than 1 backup archive made in 1s, --keep-last N and --keep-secondly N are equivalent, #537 - cleanup checkpoints except the latest, #1008 - borg extract: - added --progress, #1449 - Linux: limited support for BSD flags, #1050 - borg info: - output is now more similar to borg create --stats, #977 - borg mount: - provide "borgfs" wrapper for borg mount, enables usage via fstab, #743 - "versions" mount option - when used with a repository mount, this gives a merged, versioned view of the files in all archives, #729 - repository: - added progress information to commit/compaction phase (often takes some time when deleting/pruning), #1519 - automatic recovery for some forms of repository inconsistency, #858 - check free space before going forward with a commit, #1336 - improved write performance (esp. for rotating media), #985 - new IO code for Linux - raised default segment size to approx 512 MiB - improved compaction performance, #1041 - reduced client CPU load and improved performance for remote repositories, #940 - options that imply output (--show-rc, --show-version, --list, --stats, --progress) don't need -v/--info to have that output displayed, #865 - add archive comments (via borg (re)create --comment), #842 - borg list/prune/delete: also output archive id, #731 - --show-version: shows/logs the borg version, #725 - added --debug-topic for granular debug logging, #1447 - use atomic file writing/updating for configuration and key files, #1377 - BORG_KEY_FILE environment variable, #1001 - self-testing module, #970 Bug fixes: - list: fixed default output being produced if --format is given with empty parameter, #1489 - create: fixed overflowing progress line with CJK and similar characters, #1051 - prune: fixed crash if --prefix resulted in no matches, #1029 - init: clean up partial repo if passphrase input is aborted, #850 - info: quote cmdline arguments that have spaces in them - fix hardlinks failing in some cases for extracting subtrees, #761 Other changes: - replace stdlib hmac with OpenSSL, zero-copy decrypt (10-15% increase in performance of hash-lists and extract). - improved chunker performance, #1021 - open repository segment files in exclusive mode (fail-safe), #1134 - improved error logging, #1440 - Source: - pass meta-data around, #765 - move some constants to new constants module - better readability and fewer errors with namedtuples, #823 - moved source tree into src/ subdirectory, #1016 - made borg.platform a package, #1113 - removed dead crypto code, #1032 - improved and ported parts of the test suite to py.test, #912 - created data classes instead of passing dictionaries around, #981, #1158, #1161 - cleaned up imports, #1112 - Docs: - better help texts and sphinx reproduction of usage help: - Group options - Nicer list of options in Sphinx - Deduplicate 'Common options' (including --help) - chunker: added some insights by "Voltara", #903 - clarify what "deduplicated size" means - fix / update / add package list entries - added a SaltStack usage example, #956 - expanded FAQ - new contributors in AUTHORS! - Tests: - vagrant: add ubuntu/xenial 64bit - this box has still some issues - ChunkBuffer: add test for leaving partial chunk in buffer, fixes #945 Version 1.0.13 (2019-02-15) --------------------------- Please note: this is very likely the last 1.0.x release, please upgrade to 1.1.x. Bug fixes: - security fix: configure FUSE with "default_permissions", #3903. "default_permissions" is now enforced by borg by default to let the kernel check uid/gid/mode based permissions. "ignore_permissions" can be given to not enforce "default_permissions". - xattrs: fix borg exception handling on ENOSPC error, #3808. New features: - Read a passphrase from a file descriptor specified in the BORG_PASSPHRASE_FD environment variable. Other changes: - acl platform code: fix acl set return type - xattr: - add linux {list,get,set}xattr ctypes prototypes - fix darwin flistxattr ctypes prototype - testing / travis-ci: - fix the homebrew 1.9 issues on travis-ci, #4254 - travis OS X: use xcode 8.3 (not broken) - tox.ini: lock requirements - unbreak 1.0-maint on travis, fixes #4123 - vagrant: - misc. fixes - FUSE for macOS: upgrade 3.7.1 to 3.8.3 - Python: upgrade 3.5.5 to 3.5.6 - docs: - Update installation instructions for macOS - update release workflow using twine (docs, scripts), #4213 Version 1.0.12 (2018-04-08) --------------------------- Bug fixes: - repository: cleanup/write: invalidate cached FDs, tests - serve: fix exitcode, #2910 - extract: set bsdflags last (include immutable flag), #3263 - create --timestamp: set start time, #2957 - create: show excluded dir with "x" for tagged dirs / caches, #3189 - migrate locks to child PID when daemonize is used - Buffer: fix wrong thread-local storage use, #2951 - fix detection of non-local path, #3108 - fix LDLP restoration for subprocesses, #3077 - fix subprocess environments (xattr module's fakeroot version check, borg umount, BORG_PASSCOMMAND), #3050 - remote: deal with partial lines, #2637 - get rid of datetime.isoformat, use safe parse_timestamp to parse timestamps, #2994 - build: do .h file content checks in binary mode, fixes build issue for non-ascii header files on pure-ascii locale platforms, #3544 #3639 - remove platform.uname() call which caused library mismatch issues, #3732 - add exception handler around deprecated platform.linux_distribution() call Other changes: - require msgpack-python >= 0.4.6 and < 0.5.0, see #3753 - add parens for C preprocessor macro argument usages (did not cause malfunction) - ignore corrupt files cache, #2939 - replace "modulo" with "if" to check for wraparound in hashmap - keymanager: don't depend on optional readline module, #2980 - exclude broken pytest 3.3.0 release - exclude broken Cython 0.27(.0) release, #3066 - flake8: add some ignores - docs: - create: document exclusion through nodump - document good and problematic option placements, fix examples, #3356 - update docs about hardlinked symlinks limitation - faq: we do not implement futile attempts of ETA / progress displays - simplified rate limiting wrapper in FAQ - twitter account @borgbackup, #2948 - add note about metadata dedup and --no[ac]time, #2518 - change-passphrase only changes the passphrase, #2990 - clarify encrypted key format for borg key export, #3296 - document sshfs rename workaround, #3315 - update release checklist about security fixes - docs about how to verify a signed release, #3634 - chunk seed is generated per /repository/ - vagrant: - use FUSE for macOS 3.7.1 to build the macOS binary - use python 3.5.5 to build the binaries - add exe location to PATH when we build an exe - use https pypi url for wheezy - netbsd: bash is already installed - netbsd: fix netbsd version in PKG_PATH - use self-made FreeBSD 10.3 box, #3022 - backport fs_init (including related updates) from 1.1 - the boxcutter wheezy boxes are 404, use local ones - travis: - don't perform full Travis build on docs-only changes, #2531 - only short-circuit docs-only changes for pull requests Version 1.0.11 (2017-07-21) --------------------------- Bug fixes: - use limited unpacker for outer key (security precaution), #2174 - fix paperkey import bug Other changes: - change --checkpoint-interval default from 600s to 1800s, #2841. this improves efficiency for big repositories a lot. - docs: fix OpenSUSE command and add OpenSUSE section - tests: add tests for split_lstring and paperkey - vagrant: - fix openbsd shell - backport cpu/ram setup from master - add stretch64 VM Version 1.0.11rc1 (2017-06-27) ------------------------------ Bug fixes: - performance: rebuild hashtable if we have too few empty buckets, #2246. this fixes some sporadic, but severe performance breakdowns. - Archive: allocate zeros when needed, #2308 fixes huge memory usage of mount (8 MiB × number of archives) - IPv6 address support also: Location: more informative exception when parsing fails - borg single-file binary: use pyinstaller v3.2.1, #2396 this fixes that the prelink cronjob on some distros kills the borg binary by stripping away parts of it. - extract: - warning for unextracted big extended attributes, #2258 - also create parent dir for device files, if needed. - don't write to disk with --stdout, #2645 - archive check: detect and fix missing all-zero replacement chunks, #2180 - fix (de)compression exceptions, #2224 #2221 - files cache: update inode number, #2226 - borg rpc: use limited msgpack.Unpacker (security precaution), #2139 - Manifest: use limited msgpack.Unpacker (security precaution), #2175 - Location: accept //servername/share/path - fix ChunkIndex.__contains__ assertion for big-endian archs (harmless) - create: handle BackupOSError on a per-path level in one spot - fix error msg, there is no --keep-last in borg 1.0.x, #2282 - clamp (nano)second values to unproblematic range, #2304 - fuse / borg mount: - fix st_blocks to be an integer (not float) value - fix negative uid/gid crash (they could come into archives e.g. when backing up external drives under cygwin), #2674 - fix crash if empty (None) xattr is read - do pre-mount checks before opening repository - check llfuse is installed before asking for passphrase - borg rename: expand placeholders, #2386 - borg serve: fix forced command lines containing BORG_* env vars - fix error msg, it is --keep-within, not --within - fix borg key/debug/benchmark crashing without subcommand, #2240 - chunker: fix invalid use of types, don't do uint32_t >> 32 - document follow_symlinks requirements, check libc, #2507 New features: - added BORG_PASSCOMMAND environment variable, #2573 - add minimal version of in repository mandatory feature flags, #2134 This should allow us to make sure older borg versions can be cleanly prevented from doing operations that are no longer safe because of repository format evolution. This allows more fine grained control than just incrementing the manifest version. So for example a change that still allows new archives to be created but would corrupt the repository when an old version tries to delete an archive or check the repository would add the new feature to the check and delete set but leave it out of the write set. - borg delete --force --force to delete severely corrupted archives, #1975 Other changes: - embrace y2038 issue to support 32bit platforms - be more clear that this is a "beyond repair" case, #2427 - key file names: limit to 100 characters and remove colons from host name - upgrade FUSE for macOS to 3.5.8, #2346 - split up parsing and filtering for --keep-within, better error message, #2610 - docs: - fix caskroom link, #2299 - address SSH batch mode, #2202 #2270 - improve remote-path description - document snapshot usage, #2178 - document relative path usage, #1868 - one link per distro in the installation page - development: new branching model in git repository - kill api page - added FAQ section about backing up root partition - add bountysource badge, #2558 - create empty docs.txt reequirements, #2694 - README: how to help the project - note -v/--verbose requirement on affected options, #2542 - document borg init behaviour via append-only borg serve, #2440 - be clear about what buzhash is used for (chunking) and want it is not used for (deduplication)- also say already in the readme that we use a cryptohash for dedupe, so people don't worry, #2390 - add hint about chunker params to borg upgrade docs, #2421 - clarify borg upgrade docs, #2436 - quickstart: delete problematic BORG_PASSPHRASE use, #2623 - faq: specify "using inline shell scripts" - document pattern denial of service, #2624 - tests: - remove attic dependency of the tests, #2505 - travis: - enhance travis setuptools_scm situation - install fakeroot for Linux - add test for borg delete --force - enable remote tests on cygwin (the cygwin issue that caused these tests to break was fixed in cygwin at least since cygwin 2.8, maybe even since 2.7.0). - remove skipping the noatime tests on GNU/Hurd, #2710 - fix borg import issue, add comment, #2718 - include attic.tar.gz when installing the package also: add include_package_data=True Version 1.0.10 (2017-02-13) --------------------------- Bug fixes: - Manifest timestamps are now monotonically increasing, this fixes issues when the system clock jumps backwards or is set inconsistently across computers accessing the same repository, #2115 - Fixed testing regression in 1.0.10rc1 that lead to a hard dependency on py.test >= 3.0, #2112 New features: - "key export" can now generate a printable HTML page with both a QR code and a human-readable "paperkey" representation (and custom text) through the ``--qr-html`` option. The same functionality is also available through `paperkey.html `_, which is the same HTML page generated by ``--qr-html``. It works with existing "key export" files and key files. Other changes: - docs: - language clarification - "borg create --one-file-system" option does not respect mount points, but considers different file systems instead, #2141 - setup.py: build_api: sort file list for determinism Version 1.0.10rc1 (2017-01-29) ------------------------------ Bug fixes: - borg serve: fix transmission data loss of pipe writes, #1268 This affects only the cygwin platform (not Linux, BSD, OS X). - Avoid triggering an ObjectiveFS bug in xattr retrieval, #1992 - When running out of buffer memory when reading xattrs, only skip the current file, #1993 - Fixed "borg upgrade --tam" crashing with unencrypted repositories. Since :ref:`the issue ` is not relevant for unencrypted repositories, it now does nothing and prints an error, #1981. - Fixed change-passphrase crashing with unencrypted repositories, #1978 - Fixed "borg check repo::archive" indicating success if "archive" does not exist, #1997 - borg check: print non-exit-code warning if --last or --prefix aren't fulfilled - fix bad parsing of wrong repo location syntax - create: don't create hard link refs to failed files, mount: handle invalid hard link refs, #2092 - detect mingw byte order, #2073 - creating a new segment: use "xb" mode, #2099 - mount: umount on SIGINT/^C when in foreground, #2082 Other changes: - binary: use fixed AND freshly compiled pyinstaller bootloader, #2002 - xattr: ignore empty names returned by llistxattr(2) et al - Enable the fault handler: install handlers for the SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals to dump the Python traceback. - Also print a traceback on SIGUSR2. - borg change-passphrase: print key location (simplify making a backup of it) - officially support Python 3.6 (setup.py: add Python 3.6 qualifier) - tests: - vagrant / travis / tox: add Python 3.6 based testing - vagrant: fix openbsd repo, #2042 - vagrant: fix the freebsd64 machine, #2037 #2067 - vagrant: use python 3.5.3 to build binaries, #2078 - vagrant: use osxfuse 3.5.4 for tests / to build binaries vagrant: improve darwin64 VM settings - travis: fix osxfuse install (fixes OS X testing on Travis CI) - travis: require succeeding OS X tests, #2028 - travis: use latest pythons for OS X based testing - use pytest-xdist to parallelize testing - fix xattr test race condition, #2047 - setup.cfg: fix pytest deprecation warning, #2050 - docs: - language clarification - VM backup FAQ - borg create: document how to backup stdin, #2013 - borg upgrade: fix incorrect title levels - add CVE numbers for issues fixed in 1.0.9, #2106 - fix typos (taken from Debian package patch) - remote: include data hexdump in "unexpected RPC data" error message - remote: log SSH command line at debug level - API_VERSION: use numberspaces, #2023 - remove .github from pypi package, #2051 - add pip and setuptools to requirements file, #2030 - SyncFile: fix use of fd object after close (cosmetic) - Manifest.in: simplify, exclude \*.{so,dll,orig}, #2066 - ignore posix_fadvise errors in repository.py, #2095 (works around issues with docker on ARM) - make LoggedIO.close_segment reentrant, avoid reentrance Version 1.0.9 (2016-12-20) -------------------------- Security fixes: - A flaw in the cryptographic authentication scheme in Borg allowed an attacker to spoof the manifest. See :ref:`tam_vuln` above for the steps you should take. CVE-2016-10099 was assigned to this vulnerability. - borg check: When rebuilding the manifest (which should only be needed very rarely) duplicate archive names would be handled on a "first come first serve" basis, allowing an attacker to apparently replace archives. CVE-2016-10100 was assigned to this vulnerability. Bug fixes: - borg check: - rebuild manifest if it's corrupted - skip corrupted chunks during manifest rebuild - fix TypeError in integrity error handler, #1903, #1894 - fix location parser for archives with @ char (regression introduced in 1.0.8), #1930 - fix wrong duration/timestamps if system clock jumped during a create - fix progress display not updating if system clock jumps backwards - fix checkpoint interval being incorrect if system clock jumps Other changes: - docs: - add python3-devel as a dependency for cygwin-based installation - clarify extract is relative to current directory - FAQ: fix link to changelog - markup fixes - tests: - test_get\_(cache|keys)_dir: clean env state, #1897 - get back pytest's pretty assertion failures, #1938 - setup.py build_usage: - fixed build_usage not processing all commands - fixed build_usage not generating includes for debug commands Version 1.0.9rc1 (2016-11-27) ----------------------------- Bug fixes: - files cache: fix determination of newest mtime in backup set (which is used in cache cleanup and led to wrong "A" [added] status for unchanged files in next backup), #1860. - borg check: - fix incorrectly reporting attic 0.13 and earlier archives as corrupt - handle repo w/o objects gracefully and also bail out early if repo is *completely* empty, #1815. - fix tox/pybuild in 1.0-maint - at xattr module import time, loggers are not initialized yet New features: - borg umount exposed already existing umount code via the CLI api, so users can use it, which is more consistent than using borg to mount and fusermount -u (or umount) to un-mount, #1855. - implement borg create --noatime --noctime, fixes #1853 Other changes: - docs: - display README correctly on PyPI - improve cache / index docs, esp. files cache docs, fixes #1825 - different pattern matching for --exclude, #1779 - datetime formatting examples for {now} placeholder, #1822 - clarify passphrase mode attic repo upgrade, #1854 - clarify --umask usage, #1859 - clarify how to choose PR target branch - clarify prune behavior for different archive contents, #1824 - fix PDF issues, add logo, fix authors, headings, TOC - move security verification to support section - fix links in standalone README (:ref: tags) - add link to security contact in README - add FAQ about security - move fork differences to FAQ - add more details about resource usage - tests: skip remote tests on cygwin, #1268 - travis: - allow OS X failures until the brew cask osxfuse issue is fixed - caskroom osxfuse-beta gone, it's osxfuse now (3.5.3) - vagrant: - upgrade OSXfuse / FUSE for macOS to 3.5.3 - remove llfuse from tox.ini at a central place - do not try to install llfuse on centos6 - fix FUSE test for darwin, #1546 - add windows virtual machine with cygwin - Vagrantfile cleanup / code deduplication Version 1.0.8 (2016-10-29) -------------------------- Bug fixes: - RemoteRepository: Fix busy wait in call_many, #940 New features: - implement borgmajor/borgminor/borgpatch placeholders, #1694 {borgversion} was already there (full version string). With the new placeholders you can now also get e.g. 1 or 1.0 or 1.0.8. Other changes: - avoid previous_location mismatch, #1741 due to the changed canonicalization for relative paths in PR #1711 / #1655 (implement /./ relpath hack), there would be a changed repo location warning and the user would be asked if this is ok. this would break automation and require manual intervention, which is unwanted. thus, we automatically fix the previous_location config entry, if it only changed in the expected way, but still means the same location. - docs: - deployment.rst: do not use bare variables in ansible snippet - add clarification about append-only mode, #1689 - setup.py: add comment about requiring llfuse, #1726 - update usage.rst / api.rst - repo url / archive location docs + typo fix - quickstart: add a comment about other (remote) filesystems - vagrant / tests: - no chown when rsyncing (fixes boxes w/o vagrant group) - fix FUSE permission issues on linux/freebsd, #1544 - skip FUSE test for borg binary + fakeroot - ignore security.selinux xattrs, fixes tests on centos, #1735 Version 1.0.8rc1 (2016-10-17) ----------------------------- Bug fixes: - fix signal handling (SIGINT, SIGTERM, SIGHUP), #1620 #1593 Fixes e.g. leftover lock files for quickly repeated signals (e.g. Ctrl-C Ctrl-C) or lost connections or systemd sending SIGHUP. - progress display: adapt formatting to narrow screens, do not crash, #1628 - borg create --read-special - fix crash on broken symlink, #1584. also correctly processes broken symlinks. before this regressed to a crash (5b45385) a broken symlink would've been skipped. - process_symlink: fix missing backup_io() Fixes a chmod/chown/chgrp/unlink/rename/... crash race between getting dirents and dispatching to process_symlink. - yes(): abort on wrong answers, saying so, #1622 - fixed exception borg serve raised when connection was closed before repository was opened. Add an error message for this. - fix read-from-closed-FD issue, #1551 (this seems not to get triggered in 1.0.x, but was discovered in master) - hashindex: fix iterators (always raise StopIteration when exhausted) (this seems not to get triggered in 1.0.x, but was discovered in master) - enable relative paths in ssh:// repo URLs, via /./relpath hack, #1655 - allow repo paths with colons, #1705 - update changed repo location immediately after acceptance, #1524 - fix debug get-obj / delete-obj crash if object not found and remote repo, #1684 - pyinstaller: use a spec file to build borg.exe binary, exclude osxfuse dylib on Mac OS X (avoids mismatch lib <-> driver), #1619 New features: - add "borg key export" / "borg key import" commands, #1555, so users are able to backup / restore their encryption keys more easily. Supported formats are the keyfile format used by borg internally and a special "paper" format with by line checksums for printed backups. For the paper format, the import is an interactive process which checks each line as soon as it is input. - add "borg debug-refcount-obj" to determine a repo objects' referrer counts, #1352 Other changes: - add "borg debug ..." subcommands (borg debug-* still works, but will be removed in borg 1.1) - setup.py: Add subcommand support to build_usage. - remote: change exception message for unexpected RPC data format to indicate dataflow direction. - improved messages / error reporting: - IntegrityError: add placeholder for message, so that the message we give appears not only in the traceback, but also in the (short) error message, #1572 - borg.key: include chunk id in exception msgs, #1571 - better messages for cache newer than repo, #1700 - vagrant (testing/build VMs): - upgrade OSXfuse / FUSE for macOS to 3.5.2 - update Debian Wheezy boxes, #1686 - openbsd / netbsd: use own boxes, fixes misc rsync installation and FUSE/llfuse related testing issues, #1695 #1696 #1670 #1671 #1728 - docs: - add docs for "key export" and "key import" commands, #1641 - fix inconsistency in FAQ (pv-wrapper). - fix second block in "Easy to use" section not showing on GitHub, #1576 - add bestpractices badge - link reference docs and faq about BORG_FILES_CACHE_TTL, #1561 - improve borg info --help, explain size infos, #1532 - add release signing key / security contact to README, #1560 - add contribution guidelines for developers - development.rst: add sphinx_rtd_theme to the sphinx install command - adjust border color in borg.css - add debug-info usage help file - internals.rst: fix typos - setup.py: fix build_usage to always process all commands - added docs explaining multiple --restrict-to-path flags, #1602 - add more specific warning about write-access debug commands, #1587 - clarify FAQ regarding backup of virtual machines, #1672 - tests: - work around FUSE xattr test issue with recent fakeroot - simplify repo/hashindex tests - travis: test FUSE-enabled borg, use trusty to have a recent FUSE - re-enable FUSE tests for RemoteArchiver (no deadlocks any more) - clean env for pytest based tests, #1714 - fuse_mount contextmanager: accept any options Version 1.0.7 (2016-08-19) -------------------------- Security fixes: - borg serve: fix security issue with remote repository access, #1428 If you used e.g. --restrict-to-path /path/client1/ (with or without trailing slash does not make a difference), it acted like a path prefix match using /path/client1 (note the missing trailing slash) - the code then also allowed working in e.g. /path/client13 or /path/client1000. As this could accidentally lead to major security/privacy issues depending on the paths you use, the behaviour was changed to be a strict directory match. That means --restrict-to-path /path/client1 (with or without trailing slash does not make a difference) now uses /path/client1/ internally (note the trailing slash here!) for matching and allows precisely that path AND any path below it. So, /path/client1 is allowed, /path/client1/repo1 is allowed, but not /path/client13 or /path/client1000. If you willingly used the undocumented (dangerous) previous behaviour, you may need to rearrange your --restrict-to-path paths now. We are sorry if that causes work for you, but we did not want a potentially dangerous behaviour in the software (not even using a for-backwards-compat option). Bug fixes: - fixed repeated LockTimeout exceptions when borg serve tried to write into a already write-locked repo (e.g. by a borg mount), #502 part b) This was solved by the fix for #1220 in 1.0.7rc1 already. - fix cosmetics + file leftover for "not a valid borg repository", #1490 - Cache: release lock if cache is invalid, #1501 - borg extract --strip-components: fix leak of preloaded chunk contents - Repository, when a InvalidRepository exception happens: - fix spurious, empty lock.roster - fix repo not closed cleanly New features: - implement borg debug-info, fixes #1122 (just calls already existing code via cli, same output as below tracebacks) Other changes: - skip the O_NOATIME test on GNU Hurd, fixes #1315 (this is a very minor issue and the GNU Hurd project knows the bug) - document using a clean repo to test / build the release Version 1.0.7rc2 (2016-08-13) ----------------------------- Bug fixes: - do not write objects to repository that are bigger than the allowed size, borg will reject reading them, #1451. Important: if you created archives with many millions of files or directories, please verify if you can open them successfully, e.g. try a "borg list REPO::ARCHIVE". - lz4 compression: dynamically enlarge the (de)compression buffer, the static buffer was not big enough for archives with extremely many items, #1453 - larger item metadata stream chunks, raise archive item limit by 8x, #1452 - fix untracked segments made by moved DELETEs, #1442 Impact: Previously (metadata) segments could become untracked when deleting data, these would never be cleaned up. - extended attributes (xattrs) related fixes: - fixed a race condition in xattrs querying that led to the entire file not being backed up (while logging the error, exit code = 1), #1469 - fixed a race condition in xattrs querying that led to a crash, #1462 - raise OSError including the error message derived from errno, deal with path being a integer FD Other changes: - print active env var override by default, #1467 - xattr module: refactor code, deduplicate, clean up - repository: split object size check into too small and too big - add a transaction_id assertion, so borg init on a broken (inconsistent) filesystem does not look like a coding error in borg, but points to the real problem. - explain confusing TypeError caused by compat support for old servers, #1456 - add forgotten usage help file from build_usage - refactor/unify buffer code into helpers.Buffer class, add tests - docs: - document archive limitation, #1452 - improve prune examples Version 1.0.7rc1 (2016-08-05) ----------------------------- Bug fixes: - fix repo lock deadlocks (related to lock upgrade), #1220 - catch unpacker exceptions, resync, #1351 - fix borg break-lock ignoring BORG_REPO env var, #1324 - files cache performance fixes (fixes unnecessary re-reading/chunking/ hashing of unmodified files for some use cases): - fix unintended file cache eviction, #1430 - implement BORG_FILES_CACHE_TTL, update FAQ, raise default TTL from 10 to 20, #1338 - FUSE: - cache partially read data chunks (performance), #965, #966 - always create a root dir, #1125 - use an OrderedDict for helptext, making the build reproducible, #1346 - RemoteRepository init: always call close on exceptions, #1370 (cosmetic) - ignore stdout/stderr broken pipe errors (cosmetic), #1116 New features: - better borg versions management support (useful esp. for borg servers wanting to offer multiple borg versions and for clients wanting to choose a specific server borg version), #1392: - add BORG_VERSION environment variable before executing "borg serve" via ssh - add new placeholder {borgversion} - substitute placeholders in --remote-path - borg init --append-only option (makes using the more secure append-only mode more convenient. when used remotely, this requires 1.0.7+ also on the borg server), #1291. Other changes: - Vagrantfile: - darwin64: upgrade to FUSE for macOS 3.4.1 (aka osxfuse), #1378 - xenial64: use user "ubuntu", not "vagrant" (as usual), #1331 - tests: - fix FUSE tests on OS X, #1433 - docs: - FAQ: add backup using stable filesystem names recommendation - FAQ about glibc compatibility added, #491, glibc-check improved - FAQ: 'A' unchanged file; remove ambiguous entry age sentence. - OS X: install pkg-config to build with FUSE support, fixes #1400 - add notes about shell/sudo pitfalls with env. vars, #1380 - added platform feature matrix - implement borg debug-dump-repo-objs Version 1.0.6 (2016-07-12) -------------------------- Bug fixes: - Linux: handle multiple LD_PRELOAD entries correctly, #1314, #1111 - Fix crash with unclear message if the libc is not found, #1314, #1111 Other changes: - tests: - Fixed O_NOATIME tests for Solaris and GNU Hurd, #1315 - Fixed sparse file tests for (file) systems not supporting it, #1310 - docs: - Fixed syntax highlighting, #1313 - misc docs: added data processing overview picture Version 1.0.6rc1 (2016-07-10) ----------------------------- New features: - borg check --repair: heal damaged files if missing chunks re-appear (e.g. if the previously missing chunk was added again in a later backup archive), #148. (*) Also improved logging. Bug fixes: - sync_dir: silence fsync() failing with EINVAL, #1287 Some network filesystems (like smbfs) don't support this and we use this in repository code. - borg mount (FUSE): - fix directories being shadowed when contained paths were also specified, #1295 - raise I/O Error (EIO) on damaged files (unless -o allow_damaged_files is used), #1302. (*) - borg extract: warn if a damaged file is extracted, #1299. (*) - Added some missing return code checks (ChunkIndex._add, hashindex_resize). - borg check: fix/optimize initial hash table size, avoids resize of the table. Other changes: - tests: - add more FUSE tests, #1284 - deduplicate FUSE (u)mount code - fix borg binary test issues, #862 - docs: - changelog: added release dates to older borg releases - fix some sphinx (docs generator) warnings, #881 Notes: (*) Some features depend on information (chunks_healthy list) added to item metadata when a file with missing chunks was "repaired" using all-zero replacement chunks. The chunks_healthy list is generated since borg 1.0.4, thus borg can't recognize such "repaired" (but content-damaged) files if the repair was done with an older borg version. Version 1.0.5 (2016-07-07) -------------------------- Bug fixes: - borg mount: fix FUSE crash in xattr code on Linux introduced in 1.0.4, #1282 Other changes: - backport some FAQ entries from master branch - add release helper scripts - Vagrantfile: - centos6: no FUSE, don't build binary - add xz for redhat-like dists Version 1.0.4 (2016-07-07) -------------------------- New features: - borg serve --append-only, #1168 This was included because it was a simple change (append-only functionality was already present via repository config file) and makes better security now practically usable. - BORG_REMOTE_PATH environment variable, #1258 This was included because it was a simple change (--remote-path cli option was already present) and makes borg much easier to use if you need it. - Repository: cleanup incomplete transaction on "no space left" condition. In many cases, this can avoid a 100% full repo filesystem (which is very problematic as borg always needs free space - even to delete archives). Bug fixes: - Fix wrong handling and reporting of OSErrors in borg create, #1138. This was a serious issue: in the context of "borg create", errors like repository I/O errors (e.g. disk I/O errors, ssh repo connection errors) were handled badly and did not lead to a crash (which would be good for this case, because the repo transaction would be incomplete and trigger a transaction rollback to clean up). Now, error handling for source files is cleanly separated from every other error handling, so only problematic input files are logged and skipped. - Implement fail-safe error handling for borg extract. Note that this isn't nearly as critical as the borg create error handling bug, since nothing is written to the repo. So this was "merely" misleading error reporting. - Add missing error handler in directory attr restore loop. - repo: make sure write data hits disk before the commit tag (#1236) and also sync the containing directory. - FUSE: getxattr fail must use errno.ENOATTR, #1126 (fixes Mac OS X Finder malfunction: "zero bytes" file length, access denied) - borg check --repair: do not lose information about the good/original chunks. If we do not lose the original chunk IDs list when "repairing" a file (replacing missing chunks with all-zero chunks), we have a chance to "heal" the file back into its original state later, in case the chunks re-appear (e.g. in a fresh backup). Healing is not implemented yet, see #148. - fixes for --read-special mode: - ignore known files cache, #1241 - fake regular file mode, #1214 - improve symlinks handling, #1215 - remove passphrase from subprocess environment, #1105 - Ignore empty index file (will trigger index rebuild), #1195 - add missing placeholder support for --prefix, #1027 - improve exception handling for placeholder replacement - catch and format exceptions in arg parsing - helpers: fix "undefined name 'e'" in exception handler - better error handling for missing repo manifest, #1043 - borg delete: - make it possible to delete a repo without manifest - borg delete --forced allows one to delete corrupted archives, #1139 - borg check: - make borg check work for empty repo - fix resync and msgpacked item qualifier, #1135 - rebuild_manifest: fix crash if 'name' or 'time' key were missing. - better validation of item metadata dicts, #1130 - better validation of archive metadata dicts - close the repo on exit - even if rollback did not work, #1197. This is rather cosmetic, it avoids repo closing in the destructor. - tests: - fix sparse file test, #1170 - flake8: ignore new F405, #1185 - catch "invalid argument" on cygwin, #257 - fix sparseness assertion in test prep, #1264 Other changes: - make borg build/work on OpenSSL 1.0 and 1.1, #1187 - docs / help: - fix / clarify prune help, #1143 - fix "patterns" help formatting - add missing docs / help about placeholders - resources: rename atticmatic to borgmatic - document sshd settings, #545 - more details about checkpoints, add split trick, #1171 - support docs: add freenode web chat link, #1175 - add prune visualization / example, #723 - add note that Fnmatch is default, #1247 - make clear that lzma levels > 6 are a waste of cpu cycles - add a "do not edit" note to auto-generated files, #1250 - update cygwin installation docs - repository interoperability with borg master (1.1dev) branch: - borg check: read item metadata keys from manifest, #1147 - read v2 hints files, #1235 - fix hints file "unknown version" error handling bug - tests: add tests for format_line - llfuse: update version requirement for freebsd - Vagrantfile: - use openbsd 5.9, #716 - do not install llfuse on netbsd (broken) - update OSXfuse to version 3.3.3 - use Python 3.5.2 to build the binaries - glibc compatibility checker: scripts/glibc_check.py - add .eggs to .gitignore Version 1.0.3 (2016-05-20) -------------------------- Bug fixes: - prune: avoid that checkpoints are kept and completed archives are deleted in a prune run), #997 - prune: fix commandline argument validation - some valid command lines were considered invalid (annoying, but harmless), #942 - fix capabilities extraction on Linux (set xattrs last, after chown()), #1069 - repository: fix commit tags being seen in data - when probing key files, do binary reads. avoids crash when non-borg binary files are located in borg's key files directory. - handle SIGTERM and make a clean exit - avoids orphan lock files. - repository cache: don't cache large objects (avoid using lots of temp. disk space), #1063 Other changes: - Vagrantfile: OS X: update osxfuse / install lzma package, #933 - setup.py: add check for platform_darwin.c - setup.py: on freebsd, use a llfuse release that builds ok - docs / help: - update readthedocs URLs, #991 - add missing docs for "borg break-lock", #992 - borg create help: add some words to about the archive name - borg create help: document format tags, #894 Version 1.0.2 (2016-04-16) -------------------------- Bug fixes: - fix malfunction and potential corruption on (nowadays rather rare) big-endian architectures or bi-endian archs in (rare) BE mode. #886, #889 cache resync / index merge was malfunctioning due to this, potentially leading to data loss. borg info had cosmetic issues (displayed wrong values). note: all (widespread) little-endian archs (like x86/x64) or bi-endian archs in (widespread) LE mode (like ARMEL, MIPSEL, ...) were NOT affected. - add overflow and range checks for 1st (special) uint32 of the hashindex values, switch from int32 to uint32. - fix so that refcount will never overflow, but just stick to max. value after a overflow would have occurred. - borg delete: fix --cache-only for broken caches, #874 Makes --cache-only idempotent: it won't fail if the cache is already deleted. - fixed borg create --one-file-system erroneously traversing into other filesystems (if starting fs device number was 0), #873 - workaround a bug in Linux fadvise FADV_DONTNEED, #907 Other changes: - better test coverage for hashindex, incl. overflow testing, checking correct computations so endianness issues would be discovered. - reproducible doc for ProgressIndicator*, make the build reproducible. - use latest llfuse for vagrant machines - docs: - use /path/to/repo in examples, fixes #901 - fix confusing usage of "repo" as archive name (use "arch") Version 1.0.1 (2016-04-08) -------------------------- New features: Usually there are no new features in a bugfix release, but these were added due to their high impact on security/safety/speed or because they are fixes also: - append-only mode for repositories, #809, #36 (see docs) - borg create: add --ignore-inode option to make borg detect unmodified files even if your filesystem does not have stable inode numbers (like sshfs and possibly CIFS). - add options --warning, --error, --critical for missing log levels, #826. it's not recommended to suppress warnings or errors, but the user may decide this on his own. note: --warning is not given to borg serve so a <= 1.0.0 borg will still work as server (it is not needed as it is the default). do not use --error or --critical when using a <= 1.0.0 borg server. Bug fixes: - fix silently skipping EIO, #748 - add context manager for Repository (avoid orphan repository locks), #285 - do not sleep for >60s while waiting for lock, #773 - unpack file stats before passing to FUSE - fix build on illumos - don't try to backup doors or event ports (Solaris and derivatives) - remove useless/misleading libc version display, #738 - test suite: reset exit code of persistent archiver, #844 - RemoteRepository: clean up pipe if remote open() fails - Remote: don't print tracebacks for Error exceptions handled downstream, #792 - if BORG_PASSPHRASE is present but wrong, don't prompt for password, but fail instead, #791 - ArchiveChecker: move "orphaned objects check skipped" to INFO log level, #826 - fix capitalization, add ellipses, change log level to debug for 2 messages, #798 Other changes: - update llfuse requirement, llfuse 1.0 works - update OS / dist packages on build machines, #717 - prefer showing --info over -v in usage help, #859 - docs: - fix cygwin requirements (gcc-g++) - document how to debug / file filesystem issues, #664 - fix reproducible build of api docs - RTD theme: CSS !important overwrite, #727 - Document logo font. Recreate logo png. Remove GIMP logo file. Version 1.0.0 (2016-03-05) -------------------------- The major release number change (0.x -> 1.x) indicates bigger incompatible changes, please read the compatibility notes, adapt / test your scripts and check your backup logs. Compatibility notes: - drop support for python 3.2 and 3.3, require 3.4 or 3.5, #221 #65 #490 note: we provide binaries that include python 3.5.1 and everything else needed. they are an option in case you are stuck with < 3.4 otherwise. - change encryption to be on by default (using "repokey" mode) - moved keyfile keys from ~/.borg/keys to ~/.config/borg/keys, you can either move them manually or run "borg upgrade " - remove support for --encryption=passphrase, use borg migrate-to-repokey to switch to repokey mode, #97 - remove deprecated --compression , use --compression zlib, instead in case of 0, you could also use --compression none - remove deprecated --hourly/daily/weekly/monthly/yearly use --keep-hourly/daily/weekly/monthly/yearly instead - remove deprecated --do-not-cross-mountpoints, use --one-file-system instead - disambiguate -p option, #563: - -p now is same as --progress - -P now is same as --prefix - remove deprecated "borg verify", use "borg extract --dry-run" instead - cleanup environment variable semantics, #355 the environment variables used to be "yes sayers" when set, this was conceptually generalized to "automatic answerers" and they just give their value as answer (as if you typed in that value when being asked). See the "usage" / "Environment Variables" section of the docs for details. - change the builtin default for --chunker-params, create 2MiB chunks, #343 --chunker-params new default: 19,23,21,4095 - old default: 10,23,16,4095 one of the biggest issues with borg < 1.0 (and also attic) was that it had a default target chunk size of 64kiB, thus it created a lot of chunks and thus also a huge chunk management overhead (high RAM and disk usage). please note that the new default won't change the chunks that you already have in your repository. the new big chunks do not deduplicate with the old small chunks, so expect your repo to grow at least by the size of every changed file and in the worst case (e.g. if your files cache was lost / is not used) by the size of every file (minus any compression you might use). in case you want to immediately see a much lower resource usage (RAM / disk) for chunks management, it might be better to start with a new repo than continuing in the existing repo (with an existing repo, you'ld have to wait until all archives with small chunks got pruned to see a lower resource usage). if you used the old --chunker-params default value (or if you did not use --chunker-params option at all) and you'ld like to continue using small chunks (and you accept the huge resource usage that comes with that), just explicitly use borg create --chunker-params=10,23,16,4095. - archive timestamps: the 'time' timestamp now refers to archive creation start time (was: end time), the new 'time_end' timestamp refers to archive creation end time. This might affect prune if your backups take rather long. if you give a timestamp via cli this is stored into 'time', therefore it now needs to mean archive creation start time. New features: - implement password roundtrip, #695 Bug fixes: - remote end does not need cache nor keys directories, do not create them, #701 - added retry counter for passwords, #703 Other changes: - fix compiler warnings, #697 - docs: - update README.rst to new changelog location in docs/changes.rst - add Teemu to AUTHORS - changes.rst: fix old chunker params, #698 - FAQ: how to limit bandwidth Version 1.0.0rc2 (2016-02-28) ----------------------------- New features: - format options for location: user, pid, fqdn, hostname, now, utcnow, user - borg list --list-format - borg prune -v --list enables the keep/prune list output, #658 Bug fixes: - fix _open_rb noatime handling, #657 - add a simple archivename validator, #680 - borg create --stats: show timestamps in localtime, use same labels/formatting as borg info, #651 - llfuse compatibility fixes (now compatible with: 0.40, 0.41, 0.42) Other changes: - it is now possible to use "pip install borgbackup[fuse]" to automatically install the llfuse dependency using the correct version requirement for it. you still need to care about having installed the FUSE / build related OS package first, though, so that building llfuse can succeed. - Vagrant: drop Ubuntu Precise (12.04) - does not have Python >= 3.4 - Vagrant: use pyinstaller v3.1.1 to build binaries - docs: - borg upgrade: add to docs that only LOCAL repos are supported - borg upgrade also handles borg 0.xx -> 1.0 - use pip extras or requirements file to install llfuse - fix order in release process - updated usage docs and other minor / cosmetic fixes - verified borg examples in docs, #644 - freebsd dependency installation and FUSE configuration, #649 - add example how to restore a raw device, #671 - add a hint about the dev headers needed when installing from source - add examples for delete (and handle delete after list, before prune), #656 - update example for borg create -v --stats (use iso datetime format), #663 - added example to BORG_RSH docs - "connection closed by remote": add FAQ entry and point to issue #636 Version 1.0.0rc1 (2016-02-07) ----------------------------- New features: - borg migrate-to-repokey ("passphrase" -> "repokey" encryption key mode) - implement --short for borg list REPO, #611 - implement --list for borg extract (consistency with borg create) - borg serve: overwrite client's --restrict-to-path with ssh forced command's option value (but keep everything else from the client commandline), #544 - use $XDG_CONFIG_HOME/keys for keyfile keys (~/.config/borg/keys), #515 - "borg upgrade" moves the keyfile keys to the new location - display both archive creation start and end time in "borg info", #627 Bug fixes: - normalize trailing slashes for the repository path, #606 - Cache: fix exception handling in __init__, release lock, #610 Other changes: - suppress unneeded exception context (PEP 409), simpler tracebacks - removed special code needed to deal with imperfections / incompatibilities / missing stuff in py 3.2/3.3, simplify code that can be done simpler in 3.4 - removed some version requirements that were kept on old versions because newer did not support py 3.2 any more - use some py 3.4+ stdlib code instead of own/openssl/pypi code: - use os.urandom instead of own cython openssl RAND_bytes wrapper, #493 - use hashlib.pbkdf2_hmac from py stdlib instead of own openssl wrapper - use hmac.compare_digest instead of == operator (constant time comparison) - use stat.filemode instead of homegrown code - use "mock" library from stdlib, #145 - remove borg.support (with non-broken argparse copy), it is ok in 3.4+, #358 - Vagrant: copy CHANGES.rst as symlink, #592 - cosmetic code cleanups, add flake8 to tox/travis, #4 - docs / help: - make "borg -h" output prettier, #591 - slightly rephrase prune help - add missing example for --list option of borg create - quote exclude line that includes an asterisk to prevent shell expansion - fix dead link to license - delete Ubuntu Vivid, it is not supported anymore (EOL) - OS X binary does not work for older OS X releases, #629 - borg serve's special support for forced/original ssh commands, #544 - misc. updates and fixes Version 0.30.0 (2016-01-23) --------------------------- Compatibility notes: - you may need to use -v (or --info) more often to actually see output emitted at INFO log level (because it is suppressed at the default WARNING log level). See the "general" section in the usage docs. - for borg create, you need --list (additionally to -v) to see the long file list (was needed so you can have e.g. --stats alone without the long list) - see below about BORG_DELETE_I_KNOW_WHAT_I_AM_DOING (was: BORG_CHECK_I_KNOW_WHAT_I_AM_DOING) Bug fixes: - fix crash when using borg create --dry-run --keep-tag-files, #570 - make sure teardown with cleanup happens for Cache and RepositoryCache, avoiding leftover locks and TEMP dir contents, #285 (partially), #548 - fix locking KeyError, partial fix for #502 - log stats consistently, #526 - add abbreviated weekday to timestamp format, fixes #496 - strip whitespace when loading exclusions from file - unset LD_LIBRARY_PATH before invoking ssh, fixes strange OpenSSL library version warning when using the borg binary, #514 - add some error handling/fallback for C library loading, #494 - added BORG_DELETE_I_KNOW_WHAT_I_AM_DOING for check in "borg delete", #503 - remove unused "repair" rpc method name New features: - borg create: implement exclusions using regular expression patterns. - borg create: implement inclusions using patterns. - borg extract: support patterns, #361 - support different styles for patterns: - fnmatch (`fm:` prefix, default when omitted), like borg <= 0.29. - shell (`sh:` prefix) with `*` not matching directory separators and `**/` matching 0..n directories - path prefix (`pp:` prefix, for unifying borg create pp1 pp2 into the patterns system), semantics like in borg <= 0.29 - regular expression (`re:`), new! - --progress option for borg upgrade (#291) and borg delete - update progress indication more often (e.g. for borg create within big files or for borg check repo), #500 - finer chunker granularity for items metadata stream, #547, #487 - borg create --list now used (additionally to -v) to enable the verbose file list output - display borg version below tracebacks, #532 Other changes: - hashtable size (and thus: RAM and disk consumption) follows a growth policy: grows fast while small, grows slower when getting bigger, #527 - Vagrantfile: use pyinstaller 3.1 to build binaries, freebsd sqlite3 fix, fixes #569 - no separate binaries for centos6 any more because the generic linux binaries also work on centos6 (or in general: on systems with a slightly older glibc than debian7 - dev environment: require virtualenv<14.0 so we get a py32 compatible pip - docs: - add space-saving chunks.archive.d trick to FAQ - important: clarify -v and log levels in usage -> general, please read! - sphinx configuration: create a simple man page from usage docs - add a repo server setup example - disable unneeded SSH features in authorized_keys examples for security. - borg prune only knows "--keep-within" and not "--within" - add gource video to resources docs, #507 - add netbsd install instructions - authors: make it more clear what refers to borg and what to attic - document standalone binary requirements, #499 - rephrase the mailing list section - development docs: run build_api and build_usage before tagging release - internals docs: hash table max. load factor is 0.75 now - markup, typo, grammar, phrasing, clarifications and other fixes. - add gcc gcc-c++ to redhat/fedora/corora install docs, fixes #583 Version 0.29.0 (2015-12-13) --------------------------- Compatibility notes: - when upgrading to 0.29.0 you need to upgrade client as well as server installations due to the locking and commandline interface changes otherwise you'll get an error msg about a RPC protocol mismatch or a wrong commandline option. if you run a server that needs to support both old and new clients, it is suggested that you have a "borg-0.28.2" and a "borg-0.29.0" command. clients then can choose via e.g. "borg --remote-path=borg-0.29.0 ...". - the default waiting time for a lock changed from infinity to 1 second for a better interactive user experience. if the repo you want to access is currently locked, borg will now terminate after 1s with an error message. if you have scripts that shall wait for the lock for a longer time, use --lock-wait N (with N being the maximum wait time in seconds). Bug fixes: - hash table tuning (better chosen hashtable load factor 0.75 and prime initial size of 1031 gave ~1000x speedup in some scenarios) - avoid creation of an orphan lock for one case, #285 - --keep-tag-files: fix file mode and multiple tag files in one directory, #432 - fixes for "borg upgrade" (attic repo converter), #466 - remove --progress isatty magic (and also --no-progress option) again, #476 - borg init: display proper repo URL - fix format of umask in help pages, #463 New features: - implement --lock-wait, support timeout for UpgradableLock, #210 - implement borg break-lock command, #157 - include system info below traceback, #324 - sane remote logging, remote stderr, #461: - remote log output: intercept it and log it via local logging system, with "Remote: " prefixed to message. log remote tracebacks. - remote stderr: output it to local stderr with "Remote: " prefixed. - add --debug and --info (same as --verbose) to set the log level of the builtin logging configuration (which otherwise defaults to warning), #426 note: there are few messages emitted at DEBUG level currently. - optionally configure logging via env var BORG_LOGGING_CONF - add --filter option for status characters: e.g. to show only the added or modified files (and also errors), use "borg create -v --filter=AME ...". - more progress indicators, #394 - use ISO-8601 date and time format, #375 - "borg check --prefix" to restrict archive checking to that name prefix, #206 Other changes: - hashindex_add C implementation (speed up cache re-sync for new archives) - increase FUSE read_size to 1024 (speed up metadata operations) - check/delete/prune --save-space: free unused segments quickly, #239 - increase rpc protocol version to 2 (see also Compatibility notes), #458 - silence borg by default (via default log level WARNING) - get rid of C compiler warnings, #391 - upgrade OS X FUSE to 3.0.9 on the OS X binary build system - use python 3.5.1 to build binaries - docs: - new mailing list borgbackup@python.org, #468 - readthedocs: color and logo improvements - load coverage icons over SSL (avoids mixed content) - more precise binary installation steps - update release procedure docs about OS X FUSE - FAQ entry about unexpected 'A' status for unchanged file(s), #403 - add docs about 'E' file status - add "borg upgrade" docs, #464 - add developer docs about output and logging - clarify encryption, add note about client-side encryption - add resources section, with videos, talks, presentations, #149 - Borg moved to Arch Linux [community] - fix wrong installation instructions for archlinux Version 0.28.2 (2015-11-15) --------------------------- New features: - borg create --exclude-if-present TAGFILE - exclude directories that have the given file from the backup. You can additionally give --keep-tag-files to preserve just the directory roots and the tag-files (but not backup other directory contents), #395, attic #128, attic #142 Other changes: - do not create docs sources at build time (just have them in the repo), completely remove have_cython() hack, do not use the "mock" library at build time, #384 - avoid hidden import, make it easier for PyInstaller, easier fix for #218 - docs: - add description of item flags / status output, fixes #402 - explain how to regenerate usage and API files (build_api or build_usage) and when to commit usage files directly into git, #384 - minor install docs improvements Version 0.28.1 (2015-11-08) --------------------------- Bug fixes: - do not try to build api / usage docs for production install, fixes unexpected "mock" build dependency, #384 Other changes: - avoid using msgpack.packb at import time - fix formatting issue in changes.rst - fix build on readthedocs Version 0.28.0 (2015-11-08) --------------------------- Compatibility notes: - changed return codes (exit codes), see docs. in short: old: 0 = ok, 1 = error. now: 0 = ok, 1 = warning, 2 = error New features: - refactor return codes (exit codes), fixes #61 - add --show-rc option enable "terminating with X status, rc N" output, fixes 58, #351 - borg create backups atime and ctime additionally to mtime, fixes #317 - extract: support atime additionally to mtime - FUSE: support ctime and atime additionally to mtime - support borg --version - emit a warning if we have a slow msgpack installed - borg list --prefix=thishostname- REPO, fixes #205 - Debug commands (do not use except if you know what you do: debug-get-obj, debug-put-obj, debug-delete-obj, debug-dump-archive-items. Bug fixes: - setup.py: fix bug related to BORG_LZ4_PREFIX processing - fix "check" for repos that have incomplete chunks, fixes #364 - borg mount: fix unlocking of repository at umount time, fixes #331 - fix reading files without touching their atime, #334 - non-ascii ACL fixes for Linux, FreeBSD and OS X, #277 - fix acl_use_local_uid_gid() and add a test for it, attic #359 - borg upgrade: do not upgrade repositories in place by default, #299 - fix cascading failure with the index conversion code, #269 - borg check: implement 'cmdline' archive metadata value decoding, #311 - fix RobustUnpacker, it missed some metadata keys (new atime and ctime keys were missing, but also bsdflags). add check for unknown metadata keys. - create from stdin: also save atime, ctime (cosmetic) - use default_notty=False for confirmations, fixes #345 - vagrant: fix msgpack installation on centos, fixes #342 - deal with unicode errors for symlinks in same way as for regular files and have a helpful warning message about how to fix wrong locale setup, fixes #382 - add ACL keys the RobustUnpacker must know about Other changes: - improve file size displays, more flexible size formatters - explicitly commit to the units standard, #289 - archiver: add E status (means that an error occurred when processing this (single) item - do binary releases via "github releases", closes #214 - create: use -x and --one-file-system (was: --do-not-cross-mountpoints), #296 - a lot of changes related to using "logging" module and screen output, #233 - show progress display if on a tty, output more progress information, #303 - factor out status output so it is consistent, fix surrogates removal, maybe fixes #309 - move away from RawConfigParser to ConfigParser - archive checker: better error logging, give chunk_id and sequence numbers (can be used together with borg debug-dump-archive-items). - do not mention the deprecated passphrase mode - emit a deprecation warning for --compression N (giving a just a number) - misc .coverragerc fixes (and coverage measurement improvements), fixes #319 - refactor confirmation code, reduce code duplication, add tests - prettier error messages, fixes #307, #57 - tests: - add a test to find disk-full issues, #327 - travis: also run tests on Python 3.5 - travis: use tox -r so it rebuilds the tox environments - test the generated pyinstaller-based binary by archiver unit tests, #215 - vagrant: tests: announce whether fakeroot is used or not - vagrant: add vagrant user to fuse group for debianoid systems also - vagrant: llfuse install on darwin needs pkgconfig installed - vagrant: use pyinstaller from develop branch, fixes #336 - benchmarks: test create, extract, list, delete, info, check, help, fixes #146 - benchmarks: test with both the binary and the python code - archiver tests: test with both the binary and the python code, fixes #215 - make basic test more robust - docs: - moved docs to borgbackup.readthedocs.org, #155 - a lot of fixes and improvements, use mobile-friendly RTD standard theme - use zlib,6 compression in some examples, fixes #275 - add missing rename usage to docs, closes #279 - include the help offered by borg help in the usage docs, fixes #293 - include a list of major changes compared to attic into README, fixes #224 - add OS X install instructions, #197 - more details about the release process, #260 - fix linux glibc requirement (binaries built on debian7 now) - build: move usage and API generation to setup.py - update docs about return codes, #61 - remove api docs (too much breakage on rtd) - borgbackup install + basics presentation (asciinema) - describe the current style guide in documentation - add section about debug commands - warn about not running out of space - add example for rename - improve chunker params docs, fixes #362 - minor development docs update Version 0.27.0 (2015-10-07) --------------------------- New features: - "borg upgrade" command - attic -> borg one time converter / migration, #21 - temporary hack to avoid using lots of disk space for chunks.archive.d, #235: To use it: rm -rf chunks.archive.d ; touch chunks.archive.d - respect XDG_CACHE_HOME, attic #181 - add support for arbitrary SSH commands, attic #99 - borg delete --cache-only REPO (only delete cache, not REPO), attic #123 Bug fixes: - use Debian 7 (wheezy) to build pyinstaller borgbackup binaries, fixes slow down observed when running the Centos6-built binary on Ubuntu, #222 - do not crash on empty lock.roster, fixes #232 - fix multiple issues with the cache config version check, #234 - fix segment entry header size check, attic #352 plus other error handling improvements / code deduplication there. - always give segment and offset in repo IntegrityErrors Other changes: - stop producing binary wheels, remove docs about it, #147 - docs: - add warning about prune - generate usage include files only as needed - development docs: add Vagrant section - update / improve / reformat FAQ - hint to single-file pyinstaller binaries from README Version 0.26.1 (2015-09-28) --------------------------- This is a minor update, just docs and new pyinstaller binaries. - docs update about python and binary requirements - better docs for --read-special, fix #220 - re-built the binaries, fix #218 and #213 (glibc version issue) - update web site about single-file pyinstaller binaries Note: if you did a python-based installation, there is no need to upgrade. Version 0.26.0 (2015-09-19) --------------------------- New features: - Faster cache sync (do all in one pass, remove tar/compression stuff), #163 - BORG_REPO env var to specify the default repo, #168 - read special files as if they were regular files, #79 - implement borg create --dry-run, attic issue #267 - Normalize paths before pattern matching on OS X, #143 - support OpenBSD and NetBSD (except xattrs/ACLs) - support / run tests on Python 3.5 Bug fixes: - borg mount repo: use absolute path, attic #200, attic #137 - chunker: use off_t to get 64bit on 32bit platform, #178 - initialize chunker fd to -1, so it's not equal to STDIN_FILENO (0) - fix reaction to "no" answer at delete repo prompt, #182 - setup.py: detect lz4.h header file location - to support python < 3.2.4, add less buggy argparse lib from 3.2.6 (#194) - fix for obtaining ``char *`` from temporary Python value (old code causes a compile error on Mint 17.2) - llfuse 0.41 install troubles on some platforms, require < 0.41 (UnicodeDecodeError exception due to non-ascii llfuse setup.py) - cython code: add some int types to get rid of unspecific python add / subtract operations (avoid ``undefined symbol FPE_``... error on some platforms) - fix verbose mode display of stdin backup - extract: warn if a include pattern never matched, fixes #209, implement counters for Include/ExcludePatterns - archive names with slashes are invalid, attic issue #180 - chunker: add a check whether the POSIX_FADV_DONTNEED constant is defined - fixes building on OpenBSD. Other changes: - detect inconsistency / corruption / hash collision, #170 - replace versioneer with setuptools_scm, #106 - docs: - pkg-config is needed for llfuse installation - be more clear about pruning, attic issue #132 - unit tests: - xattr: ignore security.selinux attribute showing up - ext3 seems to need a bit more space for a sparse file - do not test lzma level 9 compression (avoid MemoryError) - work around strange mtime granularity issue on netbsd, fixes #204 - ignore st_rdev if file is not a block/char device, fixes #203 - stay away from the setgid and sticky mode bits - use Vagrant to do easy cross-platform testing (#196), currently: - Debian 7 "wheezy" 32bit, Debian 8 "jessie" 64bit - Ubuntu 12.04 32bit, Ubuntu 14.04 64bit - Centos 7 64bit - FreeBSD 10.2 64bit - OpenBSD 5.7 64bit - NetBSD 6.1.5 64bit - Darwin (OS X Yosemite) Version 0.25.0 (2015-08-29) --------------------------- Compatibility notes: - lz4 compression library (liblz4) is a new requirement (#156) - the new compression code is very compatible: as long as you stay with zlib compression, older borg releases will still be able to read data from a repo/archive made with the new code (note: this is not the case for the default "none" compression, use "zlib,0" if you want a "no compression" mode that can be read by older borg). Also the new code is able to read repos and archives made with older borg versions (for all zlib levels 0..9). Deprecations: - --compression N (with N being a number, as in 0.24) is deprecated. We keep the --compression 0..9 for now to not break scripts, but it is deprecated and will be removed later, so better fix your scripts now: --compression 0 (as in 0.24) is the same as --compression zlib,0 (now). BUT: if you do not want compression, you rather want --compression none (which is the default). --compression 1 (in 0.24) is the same as --compression zlib,1 (now) --compression 9 (in 0.24) is the same as --compression zlib,9 (now) New features: - create --compression none (default, means: do not compress, just pass through data "as is". this is more efficient than zlib level 0 as used in borg 0.24) - create --compression lz4 (super-fast, but not very high compression) - create --compression zlib,N (slower, higher compression, default for N is 6) - create --compression lzma,N (slowest, highest compression, default N is 6) - honor the nodump flag (UF_NODUMP) and do not backup such items - list --short just outputs a simple list of the files/directories in an archive Bug fixes: - fixed --chunker-params parameter order confusion / malfunction, fixes #154 - close fds of segments we delete (during compaction) - close files which fell out the lrucache - fadvise DONTNEED now is only called for the byte range actually read, not for the whole file, fixes #158. - fix issue with negative "all archives" size, fixes #165 - restore_xattrs: ignore if setxattr fails with EACCES, fixes #162 Other changes: - remove fakeroot requirement for tests, tests run faster without fakeroot (test setup does not fail any more without fakeroot, so you can run with or without fakeroot), fixes #151 and #91. - more tests for archiver - recover_segment(): don't assume we have an fd for segment - lrucache refactoring / cleanup, add dispose function, py.test tests - generalize hashindex code for any key length (less hardcoding) - lock roster: catch file not found in remove() method and ignore it - travis CI: use requirements file - improved docs: - replace hack for llfuse with proper solution (install libfuse-dev) - update docs about compression - update development docs about fakeroot - internals: add some words about lock files / locking system - support: mention BountySource and for what it can be used - theme: use a lighter green - add pypi, wheel, dist package based install docs - split install docs into system-specific preparations and generic instructions Version 0.24.0 (2015-08-09) --------------------------- Incompatible changes (compared to 0.23): - borg now always issues --umask NNN option when invoking another borg via ssh on the repository server. By that, it's making sure it uses the same umask for remote repos as for local ones. Because of this, you must upgrade both server and client(s) to 0.24. - the default umask is 077 now (if you do not specify via --umask) which might be a different one as you used previously. The default umask avoids that you accidentally give access permissions for group and/or others to files created by borg (e.g. the repository). Deprecations: - "--encryption passphrase" mode is deprecated, see #85 and #97. See the new "--encryption repokey" mode for a replacement. New features: - borg create --chunker-params ... to configure the chunker, fixes #16 (attic #302, attic #300, and somehow also #41). This can be used to reduce memory usage caused by chunk management overhead, so borg does not create a huge chunks index/repo index and eats all your RAM if you back up lots of data in huge files (like VM disk images). See docs/misc/create_chunker-params.txt for more information. - borg info now reports chunk counts in the chunk index. - borg create --compression 0..9 to select zlib compression level, fixes #66 (attic #295). - borg init --encryption repokey (to store the encryption key into the repo), fixes #85 - improve at-end error logging, always log exceptions and set exit_code=1 - LoggedIO: better error checks / exceptions / exception handling - implement --remote-path to allow non-default-path borg locations, #125 - implement --umask M and use 077 as default umask for better security, #117 - borg check: give a named single archive to it, fixes #139 - cache sync: show progress indication - cache sync: reimplement the chunk index merging in C Bug fixes: - fix segfault that happened for unreadable files (chunker: n needs to be a signed size_t), #116 - fix the repair mode, #144 - repo delete: add destroy to allowed rpc methods, fixes issue #114 - more compatible repository locking code (based on mkdir), maybe fixes #92 (attic #317, attic #201). - better Exception msg if no Borg is installed on the remote repo server, #56 - create a RepositoryCache implementation that can cope with >2GiB, fixes attic #326. - fix Traceback when running check --repair, attic #232 - clarify help text, fixes #73. - add help string for --no-files-cache, fixes #140 Other changes: - improved docs: - added docs/misc directory for misc. writeups that won't be included "as is" into the html docs. - document environment variables and return codes (attic #324, attic #52) - web site: add related projects, fix web site url, IRC #borgbackup - Fedora/Fedora-based install instructions added to docs - Cygwin-based install instructions added to docs - updated AUTHORS - add FAQ entries about redundancy / integrity - clarify that borg extract uses the cwd as extraction target - update internals doc about chunker params, memory usage and compression - added docs about development - add some words about resource usage in general - document how to backup a raw disk - add note about how to run borg from virtual env - add solutions for (ll)fuse installation problems - document what borg check does, fixes #138 - reorganize borgbackup.github.io sidebar, prev/next at top - deduplicate and refactor the docs / README.rst - use borg-tmp as prefix for temporary files / directories - short prune options without "keep-" are deprecated, do not suggest them - improved tox configuration - remove usage of unittest.mock, always use mock from pypi - use entrypoints instead of scripts, for better use of the wheel format and modern installs - add requirements.d/development.txt and modify tox.ini - use travis-ci for testing based on Linux and (new) OS X - use coverage.py, pytest-cov and codecov.io for test coverage support I forgot to list some stuff already implemented in 0.23.0, here they are: New features: - efficient archive list from manifest, meaning a big speedup for slow repo connections and "list ", "delete ", "prune" (attic #242, attic #167) - big speedup for chunks cache sync (esp. for slow repo connections), fixes #18 - hashindex: improve error messages Other changes: - explicitly specify binary mode to open binary files - some easy micro optimizations Version 0.23.0 (2015-06-11) --------------------------- Incompatible changes (compared to attic, fork related): - changed sw name and cli command to "borg", updated docs - package name (and name in urls) uses "borgbackup" to have fewer collisions - changed repo / cache internal magic strings from ATTIC* to BORG*, changed cache location to .cache/borg/ - this means that it currently won't accept attic repos (see issue #21 about improving that) Bug fixes: - avoid defect python-msgpack releases, fixes attic #171, fixes attic #185 - fix traceback when trying to do unsupported passphrase change, fixes attic #189 - datetime does not like the year 10.000, fixes attic #139 - fix "info" all archives stats, fixes attic #183 - fix parsing with missing microseconds, fixes attic #282 - fix misleading hint the fuse ImportError handler gave, fixes attic #237 - check unpacked data from RPC for tuple type and correct length, fixes attic #127 - fix Repository._active_txn state when lock upgrade fails - give specific path to xattr.is_enabled(), disable symlink setattr call that always fails - fix test setup for 32bit platforms, partial fix for attic #196 - upgraded versioneer, PEP440 compliance, fixes attic #257 New features: - less memory usage: add global option --no-cache-files - check --last N (only check the last N archives) - check: sort archives in reverse time order - rename repo::oldname newname (rename repository) - create -v output more informative - create --progress (backup progress indicator) - create --timestamp (utc string or reference file/dir) - create: if "-" is given as path, read binary from stdin - extract: if --stdout is given, write all extracted binary data to stdout - extract --sparse (simple sparse file support) - extra debug information for 'fread failed' - delete (deletes whole repo + local cache) - FUSE: reflect deduplication in allocated blocks - only allow whitelisted RPC calls in server mode - normalize source/exclude paths before matching - use posix_fadvise to not spoil the OS cache, fixes attic #252 - toplevel error handler: show tracebacks for better error analysis - sigusr1 / sigint handler to print current file infos - attic PR #286 - RPCError: include the exception args we get from remote Other changes: - source: misc. cleanups, pep8, style - docs and faq improvements, fixes, updates - cleanup crypto.pyx, make it easier to adapt to other AES modes - do os.fsync like recommended in the python docs - source: Let chunker optionally work with os-level file descriptor. - source: Linux: remove duplicate os.fsencode calls - source: refactor _open_rb code a bit, so it is more consistent / regular - source: refactor indicator (status) and item processing - source: use py.test for better testing, flake8 for code style checks - source: fix tox >=2.0 compatibility (test runner) - pypi package: add python version classifiers, add FreeBSD to platforms Attic Changelog --------------- Here you can see the full list of changes between each Attic release until Borg forked from Attic: Version 0.17 ~~~~~~~~~~~~ (bugfix release, released on X) - Fix hashindex ARM memory alignment issue (#309) - Improve hashindex error messages (#298) Version 0.16 ~~~~~~~~~~~~ (bugfix release, released on May 16, 2015) - Fix typo preventing the security confirmation prompt from working (#303) - Improve handling of systems with improperly configured file system encoding (#289) - Fix "All archives" output for attic info. (#183) - More user friendly error message when repository key file is not found (#236) - Fix parsing of iso 8601 timestamps with zero microseconds (#282) Version 0.15 ~~~~~~~~~~~~ (bugfix release, released on Apr 15, 2015) - xattr: Be less strict about unknown/unsupported platforms (#239) - Reduce repository listing memory usage (#163). - Fix BrokenPipeError for remote repositories (#233) - Fix incorrect behavior with two character directory names (#265, #268) - Require approval before accessing relocated/moved repository (#271) - Require approval before accessing previously unknown unencrypted repositories (#271) - Fix issue with hash index files larger than 2GB. - Fix Python 3.2 compatibility issue with noatime open() (#164) - Include missing pyx files in dist files (#168) Version 0.14 ~~~~~~~~~~~~ (feature release, released on Dec 17, 2014) - Added support for stripping leading path segments (#95) "attic extract --strip-segments X" - Add workaround for old Linux systems without acl_extended_file_no_follow (#96) - Add MacPorts' path to the default openssl search path (#101) - HashIndex improvements, eliminates unnecessary IO on low memory systems. - Fix "Number of files" output for attic info. (#124) - limit create file permissions so files aren't read while restoring - Fix issue with empty xattr values (#106) Version 0.13 ~~~~~~~~~~~~ (feature release, released on Jun 29, 2014) - Fix sporadic "Resource temporarily unavailable" when using remote repositories - Reduce file cache memory usage (#90) - Faster AES encryption (utilizing AES-NI when available) - Experimental Linux, OS X and FreeBSD ACL support (#66) - Added support for backup and restore of BSDFlags (OSX, FreeBSD) (#56) - Fix bug where xattrs on symlinks were not correctly restored - Added cachedir support. CACHEDIR.TAG compatible cache directories can now be excluded using ``--exclude-caches`` (#74) - Fix crash on extreme mtime timestamps (year 2400+) (#81) - Fix Python 3.2 specific lockf issue (EDEADLK) Version 0.12 ~~~~~~~~~~~~ (feature release, released on April 7, 2014) - Python 3.4 support (#62) - Various documentation improvements a new style - ``attic mount`` now supports mounting an entire repository not only individual archives (#59) - Added option to restrict remote repository access to specific path(s): ``attic serve --restrict-to-path X`` (#51) - Include "all archives" size information in "--stats" output. (#54) - Added ``--stats`` option to ``attic delete`` and ``attic prune`` - Fixed bug where ``attic prune`` used UTC instead of the local time zone when determining which archives to keep. - Switch to SI units (Power of 1000 instead 1024) when printing file sizes Version 0.11 ~~~~~~~~~~~~ (feature release, released on March 7, 2014) - New "check" command for repository consistency checking (#24) - Documentation improvements - Fix exception during "attic create" with repeated files (#39) - New "--exclude-from" option for attic create/extract/verify. - Improved archive metadata deduplication. - "attic verify" has been deprecated. Use "attic extract --dry-run" instead. - "attic prune --hourly|daily|..." has been deprecated. Use "attic prune --keep-hourly|daily|..." instead. - Ignore xattr errors during "extract" if not supported by the filesystem. (#46) Version 0.10 ~~~~~~~~~~~~ (bugfix release, released on Jan 30, 2014) - Fix deadlock when extracting 0 sized files from remote repositories - "--exclude" wildcard patterns are now properly applied to the full path not just the file name part (#5). - Make source code endianness agnostic (#1) Version 0.9 ~~~~~~~~~~~ (feature release, released on Jan 23, 2014) - Remote repository speed and reliability improvements. - Fix sorting of segment names to ignore NFS left over files. (#17) - Fix incorrect display of time (#13) - Improved error handling / reporting. (#12) - Use fcntl() instead of flock() when locking repository/cache. (#15) - Let ssh figure out port/user if not specified so we don't override .ssh/config (#9) - Improved libcrypto path detection (#23). Version 0.8.1 ~~~~~~~~~~~~~ (bugfix release, released on Oct 4, 2013) - Fix segmentation fault issue. Version 0.8 ~~~~~~~~~~~ (feature release, released on Oct 3, 2013) - Fix xattr issue when backing up sshfs filesystems (#4) - Fix issue with excessive index file size (#6) - Support access of read only repositories. - New syntax to enable repository encryption: attic init --encryption="none|passphrase|keyfile". - Detect and abort if repository is older than the cache. Version 0.7 ~~~~~~~~~~~ (feature release, released on Aug 5, 2013) - Ported to FreeBSD - Improved documentation - Experimental: Archives mountable as FUSE filesystems. - The "user." prefix is no longer stripped from xattrs on Linux Version 0.6.1 ~~~~~~~~~~~~~ (bugfix release, released on July 19, 2013) - Fixed an issue where mtime was not always correctly restored. Version 0.6 ~~~~~~~~~~~ First public release on July 9, 2013 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/LICENSE0000644000076500000240000000301714601576577013246 0ustar00twstaffCopyright (C) 2015-2023 The Borg Collective (see AUTHORS file) Copyright (C) 2010-2014 Jonas Borgström All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/MANIFEST.in0000644000076500000240000000073614601576577014004 0ustar00twstaff# stuff we need to include into the sdist is handled automatically by # setuptools_scm - it includes all git-committed files. # but we want to exclude some committed files/dirs not needed in the sdist: exclude .editorconfig .gitattributes .gitignore .mailmap Vagrantfile prune .github include src/borg/platform/darwin.c src/borg/platform/freebsd.c src/borg/platform/linux.c src/borg/platform/posix.c include src/borg/platform/syncfilerange.c include src/borg/platform/windows.c ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.2223382 borgbackup-1.2.8/PKG-INFO0000644000076500000240000002005114601577064013323 0ustar00twstaffMetadata-Version: 2.1 Name: borgbackup Version: 1.2.8 Summary: Deduplicated, encrypted, authenticated and compressed backups Home-page: https://borgbackup.readthedocs.io/ Author: The Borg Collective (see AUTHORS file) Author-email: borgbackup@python.org License: BSD Platform: Linux Platform: MacOS X Platform: FreeBSD Platform: OpenBSD Platform: NetBSD Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: POSIX :: BSD :: FreeBSD Classifier: Operating System :: POSIX :: BSD :: OpenBSD Classifier: Operating System :: POSIX :: BSD :: NetBSD Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Topic :: Security :: Cryptography Classifier: Topic :: System :: Archiving :: Backup Requires-Python: >=3.8 License-File: LICENSE License-File: AUTHORS Requires-Dist: msgpack!=1.0.1,<=1.0.8,>=0.5.6 Requires-Dist: packaging Provides-Extra: llfuse Requires-Dist: llfuse>=1.3.8; extra == "llfuse" Provides-Extra: pyfuse3 Requires-Dist: pyfuse3>=3.1.1; extra == "pyfuse3" Provides-Extra: nofuse What is BorgBackup? ------------------- BorgBackup (short: Borg) is a deduplicating backup program. Optionally, it supports compression and authenticated encryption. The main goal of Borg is to provide an efficient and secure way to backup data. The data deduplication technique used makes Borg suitable for daily backups since only changes are stored. The authenticated encryption technique makes it suitable for backups to not fully trusted targets. See the `installation manual`_ or, if you have already downloaded Borg, ``docs/installation.rst`` to get started with Borg. There is also an `offline documentation`_ available, in multiple formats. .. _installation manual: https://borgbackup.readthedocs.org/en/stable/installation.html .. _offline documentation: https://readthedocs.org/projects/borgbackup/downloads Main features ~~~~~~~~~~~~~ **Space efficient storage** Deduplication based on content-defined chunking is used to reduce the number of bytes stored: each file is split into a number of variable length chunks and only chunks that have never been seen before are added to the repository. A chunk is considered duplicate if its id_hash value is identical. A cryptographically strong hash or MAC function is used as id_hash, e.g. (hmac-)sha256. To deduplicate, all the chunks in the same repository are considered, no matter whether they come from different machines, from previous backups, from the same backup or even from the same single file. Compared to other deduplication approaches, this method does NOT depend on: * file/directory names staying the same: So you can move your stuff around without killing the deduplication, even between machines sharing a repo. * complete files or time stamps staying the same: If a big file changes a little, only a few new chunks need to be stored - this is great for VMs or raw disks. * The absolute position of a data chunk inside a file: Stuff may get shifted and will still be found by the deduplication algorithm. **Speed** * performance-critical code (chunking, compression, encryption) is implemented in C/Cython * local caching of files/chunks index data * quick detection of unmodified files **Data encryption** All data can be protected using 256-bit AES encryption, data integrity and authenticity is verified using HMAC-SHA256. Data is encrypted clientside. **Obfuscation** Optionally, borg can actively obfuscate e.g. the size of files / chunks to make fingerprinting attacks more difficult. **Compression** All data can be optionally compressed: * lz4 (super fast, low compression) * zstd (wide range from high speed and low compression to high compression and lower speed) * zlib (medium speed and compression) * lzma (low speed, high compression) **Off-site backups** Borg can store data on any remote host accessible over SSH. If Borg is installed on the remote host, big performance gains can be achieved compared to using a network filesystem (sshfs, nfs, ...). **Backups mountable as filesystems** Backup archives are mountable as userspace filesystems for easy interactive backup examination and restores (e.g. by using a regular file manager). **Easy installation on multiple platforms** We offer single-file binaries that do not require installing anything - you can just run them on these platforms: * Linux * Mac OS X * FreeBSD * OpenBSD and NetBSD (no xattrs/ACLs support or binaries yet) * Cygwin (experimental, no binaries yet) * Linux Subsystem of Windows 10 (experimental) **Free and Open Source Software** * security and functionality can be audited independently * licensed under the BSD (3-clause) license, see `License`_ for the complete license Easy to use ~~~~~~~~~~~ Initialize a new backup repository (see ``borg init --help`` for encryption options):: $ borg init -e repokey /path/to/repo Create a backup archive:: $ borg create /path/to/repo::Saturday1 ~/Documents Now doing another backup, just to show off the great deduplication:: $ borg create -v --stats /path/to/repo::Saturday2 ~/Documents ----------------------------------------------------------------------------- Archive name: Saturday2 Archive fingerprint: 622b7c53c... Time (start): Sat, 2016-02-27 14:48:13 Time (end): Sat, 2016-02-27 14:48:14 Duration: 0.88 seconds Number of files: 163 ----------------------------------------------------------------------------- Original size Compressed size Deduplicated size This archive: 6.85 MB 6.85 MB 30.79 kB <-- ! All archives: 13.69 MB 13.71 MB 6.88 MB Unique chunks Total chunks Chunk index: 167 330 ----------------------------------------------------------------------------- For a graphical frontend refer to our complementary project `BorgWeb `_. Helping, Donations and Bounties, becoming a Patron -------------------------------------------------- Your help is always welcome! Spread the word, give feedback, help with documentation, testing or development. You can also give monetary support to the project, see there for details: https://www.borgbackup.org/support/fund.html Links ----- * `Main Web Site `_ * `Releases `_, `PyPI packages `_ and `ChangeLog `_ * `Offline Documentation `_ * `GitHub `_ and `Issue Tracker `_. * `Web-Chat (IRC) `_ and `Mailing List `_ * `License `_ * `Security contact `_ Compatibility notes ------------------- EXPECT THAT WE WILL BREAK COMPATIBILITY REPEATEDLY WHEN MAJOR RELEASE NUMBER CHANGES (like when going from 0.x.y to 1.0.0 or from 1.x.y to 2.0.0). NOT RELEASED DEVELOPMENT VERSIONS HAVE UNKNOWN COMPATIBILITY PROPERTIES. THIS IS SOFTWARE IN DEVELOPMENT, DECIDE YOURSELF WHETHER IT FITS YOUR NEEDS. Security issues should be reported to the `Security contact`_ (or see ``docs/support.rst`` in the source distribution). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/README.rst0000644000076500000240000001735614601576577013743 0ustar00twstaff|screencast_basic| More screencasts: `installation`_, `advanced usage`_ What is BorgBackup? ------------------- BorgBackup (short: Borg) is a deduplicating backup program. Optionally, it supports compression and authenticated encryption. The main goal of Borg is to provide an efficient and secure way to backup data. The data deduplication technique used makes Borg suitable for daily backups since only changes are stored. The authenticated encryption technique makes it suitable for backups to not fully trusted targets. See the `installation manual`_ or, if you have already downloaded Borg, ``docs/installation.rst`` to get started with Borg. There is also an `offline documentation`_ available, in multiple formats. .. _installation manual: https://borgbackup.readthedocs.org/en/stable/installation.html .. _offline documentation: https://readthedocs.org/projects/borgbackup/downloads Main features ~~~~~~~~~~~~~ **Space efficient storage** Deduplication based on content-defined chunking is used to reduce the number of bytes stored: each file is split into a number of variable length chunks and only chunks that have never been seen before are added to the repository. A chunk is considered duplicate if its id_hash value is identical. A cryptographically strong hash or MAC function is used as id_hash, e.g. (hmac-)sha256. To deduplicate, all the chunks in the same repository are considered, no matter whether they come from different machines, from previous backups, from the same backup or even from the same single file. Compared to other deduplication approaches, this method does NOT depend on: * file/directory names staying the same: So you can move your stuff around without killing the deduplication, even between machines sharing a repo. * complete files or time stamps staying the same: If a big file changes a little, only a few new chunks need to be stored - this is great for VMs or raw disks. * The absolute position of a data chunk inside a file: Stuff may get shifted and will still be found by the deduplication algorithm. **Speed** * performance-critical code (chunking, compression, encryption) is implemented in C/Cython * local caching of files/chunks index data * quick detection of unmodified files **Data encryption** All data can be protected using 256-bit AES encryption, data integrity and authenticity is verified using HMAC-SHA256. Data is encrypted clientside. **Obfuscation** Optionally, borg can actively obfuscate e.g. the size of files / chunks to make fingerprinting attacks more difficult. **Compression** All data can be optionally compressed: * lz4 (super fast, low compression) * zstd (wide range from high speed and low compression to high compression and lower speed) * zlib (medium speed and compression) * lzma (low speed, high compression) **Off-site backups** Borg can store data on any remote host accessible over SSH. If Borg is installed on the remote host, big performance gains can be achieved compared to using a network filesystem (sshfs, nfs, ...). **Backups mountable as filesystems** Backup archives are mountable as userspace filesystems for easy interactive backup examination and restores (e.g. by using a regular file manager). **Easy installation on multiple platforms** We offer single-file binaries that do not require installing anything - you can just run them on these platforms: * Linux * Mac OS X * FreeBSD * OpenBSD and NetBSD (no xattrs/ACLs support or binaries yet) * Cygwin (experimental, no binaries yet) * Linux Subsystem of Windows 10 (experimental) **Free and Open Source Software** * security and functionality can be audited independently * licensed under the BSD (3-clause) license, see `License`_ for the complete license Easy to use ~~~~~~~~~~~ Initialize a new backup repository (see ``borg init --help`` for encryption options):: $ borg init -e repokey /path/to/repo Create a backup archive:: $ borg create /path/to/repo::Saturday1 ~/Documents Now doing another backup, just to show off the great deduplication:: $ borg create -v --stats /path/to/repo::Saturday2 ~/Documents ----------------------------------------------------------------------------- Archive name: Saturday2 Archive fingerprint: 622b7c53c... Time (start): Sat, 2016-02-27 14:48:13 Time (end): Sat, 2016-02-27 14:48:14 Duration: 0.88 seconds Number of files: 163 ----------------------------------------------------------------------------- Original size Compressed size Deduplicated size This archive: 6.85 MB 6.85 MB 30.79 kB <-- ! All archives: 13.69 MB 13.71 MB 6.88 MB Unique chunks Total chunks Chunk index: 167 330 ----------------------------------------------------------------------------- For a graphical frontend refer to our complementary project `BorgWeb `_. Helping, Donations and Bounties, becoming a Patron -------------------------------------------------- Your help is always welcome! Spread the word, give feedback, help with documentation, testing or development. You can also give monetary support to the project, see there for details: https://www.borgbackup.org/support/fund.html Links ----- * `Main Web Site `_ * `Releases `_, `PyPI packages `_ and `ChangeLog `_ * `Offline Documentation `_ * `GitHub `_ and `Issue Tracker `_. * `Web-Chat (IRC) `_ and `Mailing List `_ * `License `_ * `Security contact `_ Compatibility notes ------------------- EXPECT THAT WE WILL BREAK COMPATIBILITY REPEATEDLY WHEN MAJOR RELEASE NUMBER CHANGES (like when going from 0.x.y to 1.0.0 or from 1.x.y to 2.0.0). NOT RELEASED DEVELOPMENT VERSIONS HAVE UNKNOWN COMPATIBILITY PROPERTIES. THIS IS SOFTWARE IN DEVELOPMENT, DECIDE YOURSELF WHETHER IT FITS YOUR NEEDS. Security issues should be reported to the `Security contact`_ (or see ``docs/support.rst`` in the source distribution). .. start-badges |doc| |build| |coverage| |bestpractices| .. |doc| image:: https://readthedocs.org/projects/borgbackup/badge/?version=stable :alt: Documentation :target: https://borgbackup.readthedocs.org/en/stable/ .. |build| image:: https://github.com/borgbackup/borg/workflows/CI/badge.svg?branch=master :alt: Build Status (master) :target: https://github.com/borgbackup/borg/actions .. |coverage| image:: https://codecov.io/github/borgbackup/borg/coverage.svg?branch=master :alt: Test Coverage :target: https://codecov.io/github/borgbackup/borg?branch=master .. |screencast_basic| image:: https://asciinema.org/a/133292.png :alt: BorgBackup Basic Usage :target: https://asciinema.org/a/133292?autoplay=1&speed=1 :width: 100% .. _installation: https://asciinema.org/a/133291?autoplay=1&speed=1 .. _advanced usage: https://asciinema.org/a/133293?autoplay=1&speed=1 .. |bestpractices| image:: https://bestpractices.coreinfrastructure.org/projects/271/badge :alt: Best Practices Score :target: https://bestpractices.coreinfrastructure.org/projects/271 .. end-badges ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/README_WINDOWS.rst0000644000076500000240000000407314601576577015145 0ustar00twstaffBorg Native on Windows ====================== Running borg natively on windows is in a early alpha stage. Expect many things to fail. Do not use the native windows build on any data which you do not want to lose! Build Requirements ------------------ - VC 14.0 Compiler - OpenSSL Library v1.1.1c, 64bit (available at https://github.com/python/cpython-bin-deps) Please use the `win-download-openssl.ps1` script to download and extract the library to the correct location. See also the OpenSSL section below. - Patience and a lot of coffee / beer What's working -------------- .. note:: The following examples assume that the `BORG_REPO` and `BORG_PASSPHRASE` environment variables are set if the repo or passphrase is not explicitly given. - Borg does not crash if called with ``borg`` - ``borg init --encryption repokey-blake2 ./demoRepo`` runs without an error/warning. Note that absolute paths only work if the protocol is explicitly set to file:// - ``borg create ::backup-{now} D:\DemoData`` works as expected. - ``borg list`` works as expected. - ``borg extract --strip-components 1 ::backup-XXXX`` works. If absolute paths are extracted, it's important to pass ``--strip-components 1`` as otherwise the data is restored to the original location! What's NOT working ------------------ - Extracting a backup which was created on windows machine on a non windows machine will fail. - And many things more. OpenSSL, Windows and Python --------------------------- Windows does not ship OpenSSL by default, so we need to get the library from somewhere else. However, a default python installation does include `libcrypto` which is required by borg. The only things which are missing to build borg are the header and `*.lib` files. Luckily the python developers provide all required files in a separate repository. The `win-download-openssl.ps1` script can be used to download the package from https://github.com/python/cpython-bin-deps and extract the files to the correct location. For Anaconda, the required libraries can be installed with `conda install -c anaconda openssl`. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/SECURITY.md0000644000076500000240000000061014601576577014026 0ustar00twstaff# Security Policy ## Supported Versions These borg releases are currently supported with security updates. | Version | Supported | |---------|--------------------| | 1.2.x | :white_check_mark: | | 1.1.x | :x: | | < 1.1 | :x: | ## Reporting a Vulnerability See there: https://borgbackup.readthedocs.io/en/latest/support.html#security-contact ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/conftest.py0000644000076500000240000000552314601576577014444 0ustar00twstaffimport os import pytest # IMPORTANT keep this above all other borg imports to avoid inconsistent values # for `from borg.constants import PBKDF2_ITERATIONS` (or star import) usages before # this is executed from borg import constants # no fixture-based monkey-patching since star-imports are used for the constants module constants.PBKDF2_ITERATIONS = 1 # needed to get pretty assertion failures in unit tests: if hasattr(pytest, 'register_assert_rewrite'): pytest.register_assert_rewrite('borg.testsuite') import borg.cache # noqa: E402 from borg.logger import setup_logging # noqa: E402 # Ensure that the loggers exist for all tests setup_logging() from borg.testsuite import has_lchflags, has_llfuse, has_pyfuse3 # noqa: E402 from borg.testsuite import are_symlinks_supported, are_hardlinks_supported, is_utime_fully_supported # noqa: E402 from borg.testsuite.platform import fakeroot_detected # noqa: E402 @pytest.fixture(autouse=True) def clean_env(tmpdir_factory, monkeypatch): # avoid that we access / modify the user's normal .config / .cache directory: monkeypatch.setenv('XDG_CONFIG_HOME', str(tmpdir_factory.mktemp('xdg-config-home'))) monkeypatch.setenv('XDG_CACHE_HOME', str(tmpdir_factory.mktemp('xdg-cache-home'))) # also avoid to use anything from the outside environment: keys = [key for key in os.environ if key.startswith('BORG_') and key not in ('BORG_FUSE_IMPL', )] for key in keys: monkeypatch.delenv(key, raising=False) def pytest_report_header(config, startdir): tests = { "BSD flags": has_lchflags, "fuse2": has_llfuse, "fuse3": has_pyfuse3, "root": not fakeroot_detected(), "symlinks": are_symlinks_supported(), "hardlinks": are_hardlinks_supported(), "atime/mtime": is_utime_fully_supported(), "modes": "BORG_TESTS_IGNORE_MODES" not in os.environ } enabled = [] disabled = [] for test in tests: if tests[test]: enabled.append(test) else: disabled.append(test) output = "Tests enabled: " + ", ".join(enabled) + "\n" output += "Tests disabled: " + ", ".join(disabled) return output class DefaultPatches: def __init__(self, request): self.org_cache_wipe_cache = borg.cache.LocalCache.wipe_cache def wipe_should_not_be_called(*a, **kw): raise AssertionError("Cache wipe was triggered, if this is part of the test add " "@pytest.mark.allow_cache_wipe") if 'allow_cache_wipe' not in request.keywords: borg.cache.LocalCache.wipe_cache = wipe_should_not_be_called request.addfinalizer(self.undo) def undo(self): borg.cache.LocalCache.wipe_cache = self.org_cache_wipe_cache @pytest.fixture(autouse=True) def default_patches(request): return DefaultPatches(request) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1711734324.102513 borgbackup-1.2.8/docs/0000755000076500000240000000000014601577064013160 5ustar00twstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1026266 borgbackup-1.2.8/docs/3rd_party/0000755000076500000240000000000014601577064015067 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/3rd_party/README0000644000076500000240000000032714601576577015761 0ustar00twstaffHere we store 3rd party documentation, licenses, etc. Please note that all files inside the "borg" package directory (except the stuff excluded in setup.py) will be INSTALLED, so don't keep docs or licenses there. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1028583 borgbackup-1.2.8/docs/3rd_party/lz4/0000755000076500000240000000000014601577064015600 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/3rd_party/lz4/LICENSE0000644000076500000240000000243714601576577016623 0ustar00twstaffLZ4 Library Copyright (c) 2011-2016, Yann Collet All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1029766 borgbackup-1.2.8/docs/3rd_party/zstd/0000755000076500000240000000000014601577064016053 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/3rd_party/zstd/LICENSE0000644000076500000240000000277214601576577017100 0ustar00twstaffBSD License For Zstandard software Copyright (c) 2016-present, Facebook, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name Facebook nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/Makefile0000644000076500000240000001074614601576577014640 0ustar00twstaff# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/borg.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/borg.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/borg" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/borg" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1045506 borgbackup-1.2.8/docs/_static/0000755000076500000240000000000014601577064014606 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/_static/Makefile0000644000076500000240000000030614601576577016255 0ustar00twstaff all: logo.pdf logo.png logo.pdf: logo.svg inkscape logo.svg --export-pdf=logo.pdf logo.png: logo.svg inkscape logo.svg --export-png=logo.png --export-dpi=72,72 clean: rm -f logo.pdf logo.png ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/_static/favicon.ico0000644000076500000240000011401614601576577016742 0ustar00twstaffRR m6RRFmRR"&u(R PP aaaa PPKL<M>>>>>>?BuL0i9iuL0i9iuL0i9iCuL%|cs] cvu"uL4uL4uL4uL<>>>4B\\\\\3uL0 4uL064uL0-4uL`* 3e`Q=Lf9CflFLg9 glHLh9 hlJL%|L0L0L0PP aaaa PP?@@@@@@?(R(R "%*-03469<=>?BCFHJKLMQ\]`cefghilsuv|S\\\\\\\\' R\\\\\\\\\\(\\\\\\\\\\\\) !Q\\\\\"G\\\\\6(\\\\\U\\\\\\\;3\\\\N\\\Q9\\\\\D3\\\UG\\\\\6(\\\\\U\\\\\\\\;3\\\\\N\\\\P 9\\\\\D3\\\\\UG\\\\\6(\\\\\U\\\\\\\\\;3\\\\\\N\\\\\P 9\\\\\D3\\\\\\\#G\\\\\6(\\\\\UEGGGW\\\\\8.\\\\\T5,>\\\\\N 9\\\\\D.\\\\\L76G\\\\\6(\\\\\U<\\\\\PD\\\\\@\\\\\\9\\\\\DD\\\\\<G\\\\\6(\\\\\U<\\\\\PD\\\\\@\\\\\\9\\\\\DD\\\\\<G\\\\\6(\\\\\U<\\\\\PD\\\\\@\\\\\\9\\\\\DD\\\\\<G\\\\\6(\\\\\U  A\\\\\?D\\\\\@\\\\\\9\\\\\D"+++++D\\\\\<G\\\\\6(\\\\\U\\\\\\\\\AD\\\\\@\\\\\\9\\\\\DF\\\\\<D\\\\\<G\\\\\6(\\\\\U\\\\\\\\\D\\\\\@\\\\\\9\\\\\DF\\\\\<D\\\\\<G\\\\\6(\\\\\U\\\\\\\\\XD\\\\\@\\\\\\9\\\\\DF\\\\\<D\\\\\<G\\\\\6(\\\\\U<\\\\\M-\\\\\YH=R\\\\\L 9\\\\\DIJZ\\\\\/-\\\\\<*JJ[\\\\\(\\\\\U<\\\\\P0\\\\\\N\\\\\N 9\\\\\D#\\\\\\\@0\\\\<4\\\\\\\$(\\\\\U<\\\\\P1\\\\\N\\\\O 9\\\\\DB\\\\\K 1\\\<4\\\\\\%(\\\\\U<\\\\\P2\\\\N\\\Q 9\\\\\DC\\\V2\\<4\\\\\&(\\\\\UEGGGW\\\\\8(\\\\\U\\\\\\\\\:(\\\\\U\\\\\\\\:(\\\\\U\\\\\\\:././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/_static/logo.pdf0000644000076500000240000000244414601576577016255 0ustar00twstaff%PDF-1.4 % 3 0 obj << /Length 4 0 R /Filter /FlateDecode >> stream x}TAr! _@PyB!{H5ʞi  '?ƏvI0&t=ގZ=N*Q,J|DP ? ÜA-\ $k@ֽdхY3ko6T_,30^'w4=BJ5~AƑ`3WhF%$ acܒļRx|k]MX)`xLpRZ:>4V'}8Y( G||⥾ hs?$ J}( VR%Že3鯵"4 5)`OĹ){-֗Y<˹uPm؎mmV)jVtn y"9C$.}_T~ԫwXx/QXj^}5Rx'b.n5}S endstream endobj 4 0 obj 430 endobj 2 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> >> endobj 5 0 obj << /Type /Page /Parent 1 0 R /MediaBox [ 0 0 240 100 ] /Contents 3 0 R /Group << /Type /Group /S /Transparency /I true /CS /DeviceRGB >> /Resources 2 0 R >> endobj 1 0 obj << /Type /Pages /Kids [ 5 0 R ] /Count 1 >> endobj 6 0 obj << /Creator (cairo 1.14.8 (http://cairographics.org)) /Producer (cairo 1.14.8 (http://cairographics.org)) >> endobj 7 0 obj << /Type /Catalog /Pages 1 0 R >> endobj xref 0 8 0000000000 65535 f 0000000830 00000 n 0000000544 00000 n 0000000015 00000 n 0000000522 00000 n 0000000616 00000 n 0000000895 00000 n 0000001022 00000 n trailer << /Size 8 /Root 7 0 R /Info 6 0 R >> startxref 1074 %%EOF ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/_static/logo.png0000644000076500000240000000337514601576577016274 0ustar00twstaffPNG  IHDRdsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<zIDATx]e񯛽iFeaFPPHIeIKA^EQAAwEEb/FQkJkn=E9Ywg8z؇S Hq X0,b1LE S")`a X0,b1LE S")`a X0,b1LE S"Zz.u^ǀcD/Áik3x+B V&"QF"~ E,Nq G|z+"/AKc01(b1/݀Ay~'`ee)* oq.%o7XwoT16 8zOb`v%pJ-(ιL/~x"~_x0xvb7Sr1=m1LO,a 1ś)19La pdoc~(B&F<x81Ǜ)!n/8_{bE#Gr.KE2tHwԽ͓.Hp61FE S")`a X0,b1,ObsgӎzE\ xν#86'ULo?O\7Jrܹ}2Ώ$ xN pfjl.\c n};IvNw"Ni獂,t7|UwTnÝ4=!i; zZhL@ -R,R,M`/Vbq&p_k,)^Pt;pSy7|4_?nKMEmƽUTr%-y͸%3LKZ&elnC]ha X0,b1LE S"S gGIENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/_static/logo.svg0000644000076500000240000000337614601576577016310 0ustar00twstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/_static/logo_font.txt0000644000076500000240000000015714601576577017350 0ustar00twstaffBlack Ops One James Grieshaber SIL Open Font License, 1.1 https://www.google.com/fonts/specimen/Black+Ops+One ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1711734324.104772 borgbackup-1.2.8/docs/_templates/0000755000076500000240000000000014601577064015315 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/_templates/globaltoc.html0000644000076500000240000000117114601576577020161 0ustar00twstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/_templates/logo-text.html0000644000076500000240000000024014601576577020131 0ustar00twstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/authors.rst0000644000076500000240000000021314601576577015403 0ustar00twstaff.. include:: global.rst.inc Authors ======= .. include:: ../AUTHORS License ======= .. _license: .. include:: ../LICENSE :literal: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/book.rst0000644000076500000240000000062114601576577014653 0ustar00twstaff:orphan: .. include:: global.rst.inc Borg documentation ================== .. when you add an element here, do not forget to add it to index.rst .. Note: Some things are in appendices (see latex_appendices in conf.py) .. toctree:: :maxdepth: 2 introduction installation quickstart usage deployment faq support changes internals development authors ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1711734324.091994 borgbackup-1.2.8/docs/borg_theme/0000755000076500000240000000000014601577064015273 5ustar00twstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1048882 borgbackup-1.2.8/docs/borg_theme/css/0000755000076500000240000000000014601577064016063 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/borg_theme/css/borg.css0000644000076500000240000000764114601576577017546 0ustar00twstaff@import url("theme.css"); dt code { font-weight: normal; } #internals .toctree-wrapper > ul { column-count: 3; -webkit-column-count: 3; } #internals .toctree-wrapper > ul > li { display: inline-block; font-weight: bold; } #internals .toctree-wrapper > ul > li > ul { font-weight: normal; } /* bootstrap has a .container class which clashes with docutils' container class. */ .docutils.container { width: auto; margin: 0; padding: 0; } /* the default (38px) produces a jumpy baseline in Firefox on Linux. */ h1 { font-size: 36px; } .text-logo { background-color: #000200; color: #00dd00; } .text-logo:hover, .text-logo:active, .text-logo:focus { color: #5afe57; } /* by default the top and bottom margins are unequal which looks a bit unbalanced. */ .sidebar-block { padding: 0; margin: 14px 0 24px 0; } #borg-documentation h1 + p .external img { width: 100%; } .container.experimental, #debugging-facilities, #borg-recreate { /* don't change text dimensions */ margin: 0 -30px; /* padding below + border width */ padding: 0 10px; /* 10 px visual margin between edge of text and the border */ /* fallback for browsers that don't have repeating-linear-gradient: thick, red lines */ border-left: 20px solid red; border-right: 20px solid red; /* fancy red stripes */ border-image: repeating-linear-gradient( -45deg,rgba(255,0,0,0.1) 0,rgba(255,0,0,0.75) 10px,rgba(0,0,0,0) 10px,rgba(0,0,0,0) 20px,rgba(255,0,0,0.75) 20px) 0 20 repeat; } .topic { margin: 0 1em; padding: 0 1em; /* #4e4a4a = background of the ToC sidebar */ border-left: 2px solid #4e4a4a;; border-right: 2px solid #4e4a4a;; } table.docutils:not(.footnote) td, table.docutils:not(.footnote) th { padding: .2em; } table.docutils:not(.footnote) { border-collapse: collapse; border: none; } table.docutils:not(.footnote) td, table.docutils:not(.footnote) th { border: 1px solid #ddd; } table.docutils:not(.footnote) tr:first-child th, table.docutils:not(.footnote) tr:first-child td { border-top: 0; } table.docutils:not(.footnote) tr:last-child td { border-bottom: 0; } table.docutils:not(.footnote) tr td:first-child, table.docutils:not(.footnote) tr th:first-child { border-left: 0; } table.docutils:not(.footnote) tr td:last-child, table.docutils:not(.footnote) tr th:last-child, table.docutils.borg-options-table tr td { border-right: 0; } table.docutils.option-list tr td, table.docutils.borg-options-table tr td { border-left: 0; border-right: 0; } table.docutils.borg-options-table tr td:first-child:not([colspan="3"]) { border-top: 0; border-bottom: 0; } .borg-options-table td[colspan="3"] p { margin: 0; } .borg-options-table { width: 100%; } kbd, /* used in usage pages for options */ code, .rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal, .rst-content tt, .rst-content code, p .literal, p .literal span { border: none; padding: 0; color: black; /* slight contrast with #404040 of regular text */ background: none; } kbd { box-shadow: none; line-height: 23px; word-wrap: normal; font-size: 15px; font-family: Consolas, monospace; } .borg-options-table tr td:nth-child(2) .pre { white-space: nowrap; } .borg-options-table tr td:first-child { width: 2em; } cite { white-space: nowrap; color: black; /* slight contrast with #404040 of regular text */ font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; font-style: normal; text-decoration: underline; } .borg-common-opt-ref { font-weight: bold; } .sidebar-toc ul li.toctree-l2 a, .sidebar-toc ul li.toctree-l3 a { padding-right: 25px; } #common-options .option { white-space: nowrap; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/changes.rst0000644000076500000240000067731014601576577015350 0ustar00twstaff.. _important_notes: Important notes =============== This section provides information about security and corruption issues. .. _archives_tam_vuln: Pre-1.2.5 archives spoofing vulnerability (CVE-2023-36811) ---------------------------------------------------------- A flaw in the cryptographic authentication scheme in Borg allowed an attacker to fake archives and potentially indirectly cause backup data loss in the repository. The attack requires an attacker to be able to 1. insert files (with no additional headers) into backups 2. gain write access to the repository This vulnerability does not disclose plaintext to the attacker, nor does it affect the authenticity of existing archives. Creating plausible fake archives may be feasible for empty or small archives, but is unlikely for large archives. The fix enforces checking the TAM authentication tag of archives at critical places. Borg now considers archives without TAM as garbage or an attack. We are not aware of others having discovered, disclosed or exploited this vulnerability. Below, if we speak of borg 1.2.8, we mean a borg version >= 1.2.8 **or** a borg version that has the relevant patches for this vulnerability applied (could be also an older version in that case). Steps you must take to upgrade a repository (this applies to all kinds of repos no matter what encryption mode they use, including "none"): 1. Upgrade all clients using this repository to borg 1.2.8. Note: it is not required to upgrade a server, except if the server-side borg is also used as a client (and not just for "borg serve"). Do **not** run ``borg check`` with borg > 1.2.4 before completing the upgrade steps: - ``borg check`` would complain about archives without a valid archive TAM. - ``borg check --repair`` would remove such archives! 2. Do this step on every client using this repo: ``borg upgrade --show-rc --check-tam `` This will check the manifest TAM authentication setup in the repo and on this client. The command will exit with rc=0 if all is OK, otherwise with rc=1. a) If you get "Manifest authentication setup OK for this client and this repository." and rc=0, continue with 3. b) If you get some warnings and rc=1, run: ``borg upgrade --tam --force `` 3. Run: ``borg upgrade --show-rc --check-archives-tam `` This will create a report about the TAM status for all archives. In the last line(s) of the report, it will also report the overall status. The command will exit with rc=0 if all archives are TAM authenticated or with rc=1 if there are some archives with TAM issues. If there are no issues and all archives are TAM authenticated, continue with 5. Archive TAM issues are expected for: - archives created by borg <1.0.9. - archives resulting from a borg rename or borg recreate operation (see #7791) But, important, archive TAM issues could also come from archives created by an attacker. You should verify that archives with TAM issues are authentic and not malicious (== have good content, have correct timestamp, can be extracted successfully). In case you find crappy/malicious archives, you must delete them before proceeding. In low-risk, trusted environments, you may decide on your own risk to skip step 3 and just trust in everything being OK. 4. If there are no archives with TAM issues left at this point, you can skip this step. Run ``borg upgrade --archives-tam ``. This will unconditionally add a correct archive TAM to all archives not having one. ``borg check`` would consider TAM-less or invalid-TAM archives as garbage or a potential attack. To see that all archives are OK now, you can optionally repeat the command from step 3. 5. Done. Manifest and archives are TAM authenticated now. Vulnerability time line: * 2023-06-13: Vulnerability discovered during code review by Thomas Waldmann * 2023-06-13...: Work on fixing the issue, upgrade procedure, docs. * 2023-06-30: CVE was assigned via Github CNA * 2023-06-30 .. 2023-08-29: Fixed issue, code review, docs, testing. * 2023-08-30: Released fixed version 1.2.5 (broken upgrade procedure for some repos) * 2023-08-31: Released fixed version 1.2.6 (fixes upgrade procedure) .. _hashindex_set_bug: Pre-1.1.11 potential index corruption / data loss issue ------------------------------------------------------- A bug was discovered in our hashtable code, see issue #4829. The code is used for the client-side chunks cache and the server-side repo index. Although borg uses the hashtables very heavily, the index corruption did not happen too frequently, because it needed specific conditions to happen. Data loss required even more specific conditions, so it should be rare (and also detectable via borg check). You might be affected if borg crashed with / complained about: - AssertionError: Corrupted segment reference count - corrupted index or hints - ObjectNotFound: Object with key ... not found in repository ... - Index mismatch for key b'...'. (..., ...) != (-1, -1) - ValueError: stats_against: key contained in self but not in master_index. Advised procedure to fix any related issue in your indexes/caches: - install fixed borg code (on client AND server) - for all of your clients and repos remove the cache by: borg delete --cache-only YOURREPO (later, the cache will be re-built automatically) - for all your repos, rebuild the repo index by: borg check --repair YOURREPO This will also check all archives and detect if there is any data-loss issue. Affected branches / releases: - fd06497 introduced the bug into 1.1-maint branch - it affects all borg 1.1.x since 1.1.0b4. - fd06497 introduced the bug into master branch - it affects all borg 1.2.0 alpha releases. - c5cd882 introduced the bug into 1.0-maint branch - it affects all borg 1.0.x since 1.0.11rc1. The bug was fixed by: - 701159a fixes the bug in 1.1-maint branch - will be released with borg 1.1.11. - fa63150 fixes the bug in master branch - will be released with borg 1.2.0a8. - 7bb90b6 fixes the bug in 1.0-maint branch. Branch is EOL, no new release is planned as of now. .. _broken_validator: Pre-1.1.4 potential data corruption issue ----------------------------------------- A data corruption bug was discovered in borg check --repair, see issue #3444. This is a 1.1.x regression, releases < 1.1 (e.g. 1.0.x) are not affected. To avoid data loss, you must not run borg check --repair using an unfixed version of borg 1.1.x. The first official release that has the fix is 1.1.4. Package maintainers may have applied the fix to updated packages of 1.1.x (x<4) though, see the package maintainer's package changelog to make sure. If you never had missing item metadata chunks, the bug has not affected you even if you did run borg check --repair with an unfixed version. When borg check --repair tried to repair corrupt archives that miss item metadata chunks, the resync to valid metadata in still present item metadata chunks malfunctioned. This was due to a broken validator that considered all (even valid) item metadata as invalid. As they were considered invalid, borg discarded them. Practically, that means the affected files, directories or other fs objects were discarded from the archive. Due to the malfunction, the process was extremely slow, but if you let it complete, borg would have created a "repaired" archive that has lost a lot of items. If you interrupted borg check --repair because it was so strangely slow (killing borg somehow, e.g. Ctrl-C) the transaction was rolled back and no corruption occurred. The log message indicating the precondition for the bug triggering looks like: item metadata chunk missing [chunk: 001056_bdee87d...a3e50d] If you never had that in your borg check --repair runs, you're not affected. But if you're unsure or you actually have seen that, better check your archives. By just using "borg list repo::archive" you can see if all expected filesystem items are listed. .. _tam_vuln: Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099) ---------------------------------------------------------- A flaw in the cryptographic authentication scheme in Borg allowed an attacker to spoof the manifest. The attack requires an attacker to be able to 1. insert files (with no additional headers) into backups 2. gain write access to the repository This vulnerability does not disclose plaintext to the attacker, nor does it affect the authenticity of existing archives. The vulnerability allows an attacker to create a spoofed manifest (the list of archives). Creating plausible fake archives may be feasible for small archives, but is unlikely for large archives. The fix adds a separate authentication tag to the manifest. For compatibility with prior versions this authentication tag is *not* required by default for existing repositories. Repositories created with 1.0.9 and later require it. Steps you should take: 1. Upgrade all clients to 1.0.9 or later. 2. Run ``borg upgrade --tam `` *on every client* for *each* repository. 3. This will list all archives, including archive IDs, for easy comparison with your logs. 4. Done. Prior versions can access and modify repositories with this measure enabled, however, to 1.0.9 or later their modifications are indiscernible from an attack and will raise an error until the below procedure is followed. We are aware that this can be annoying in some circumstances, but don't see a way to fix the vulnerability otherwise. In case a version prior to 1.0.9 is used to modify a repository where above procedure was completed, and now you get an error message from other clients: 1. ``borg upgrade --tam --force `` once with *any* client suffices. This attack is mitigated by: - Noting/logging ``borg list``, ``borg info``, or ``borg create --stats``, which contain the archive IDs. We are not aware of others having discovered, disclosed or exploited this vulnerability. Vulnerability time line: * 2016-11-14: Vulnerability and fix discovered during review of cryptography by Marian Beermann (@enkore) * 2016-11-20: First patch * 2016-12-20: Released fixed version 1.0.9 * 2017-01-02: CVE was assigned * 2017-01-15: Released fixed version 1.1.0b3 (fix was previously only available from source) .. _attic013_check_corruption: Pre-1.0.9 potential data loss ----------------------------- If you have archives in your repository that were made with attic <= 0.13 (and later migrated to borg), running borg check would report errors in these archives. See issue #1837. The reason for this is a invalid (and useless) metadata key that was always added due to a bug in these old attic versions. If you run borg check --repair, things escalate quickly: all archive items with invalid metadata will be killed. Due to that attic bug, that means all items in all archives made with these old attic versions. Pre-1.0.4 potential repo corruption ----------------------------------- Some external errors (like network or disk I/O errors) could lead to corruption of the backup repository due to issue #1138. A sign that this happened is if "E" status was reported for a file that can not be explained by problems with the source file. If you still have logs from "borg create -v --list", you can check for "E" status. Here is what could cause corruption and what you can do now: 1) I/O errors (e.g. repo disk errors) while writing data to repo. This could lead to corrupted segment files. Fix:: # check for corrupt chunks / segments: borg check -v --repository-only REPO # repair the repo: borg check -v --repository-only --repair REPO # make sure everything is fixed: borg check -v --repository-only REPO 2) Unreliable network / unreliable connection to the repo. This could lead to archive metadata corruption. Fix:: # check for corrupt archives: borg check -v --archives-only REPO # delete the corrupt archives: borg delete --force REPO::CORRUPT_ARCHIVE # make sure everything is fixed: borg check -v --archives-only REPO 3) In case you want to do more intensive checking. The best check that everything is ok is to run a dry-run extraction:: borg extract -v --dry-run REPO::ARCHIVE .. _upgradenotes: Upgrade Notes ============= borg 1.1.x to 1.2.x ------------------- Some things can be recommended for the upgrade process from borg 1.1.x (please also read the important compatibility notes below): - first upgrade to a recent 1.1.x release - especially if you run some older 1.1.* or even 1.0.* borg release. - using that, run at least one `borg create` (your normal backup), `prune` and especially a `check` to see everything is in a good state. - check the output of `borg check` - if there is anything special, consider a `borg check --repair` followed by another `borg check`. - if everything is fine so far (borg check reports no issues), you can consider upgrading to 1.2.x. if not, please first fix any already existing issue. - if you want to play safer, first **create a backup of your borg repository**. - upgrade to latest borg 1.2.x release (you could use the fat binary from github releases page) - borg 1.2.6 has a security fix for the pre-1.2.5 archives spoofing vulnerability (CVE-2023-36811), see details and necessary upgrade procedure described above. - run `borg compact --cleanup-commits` to clean up a ton of 17 bytes long files in your repo caused by a borg 1.1 bug - run `borg check` again (now with borg 1.2.x) and check if there is anything special. - run `borg info` (with borg 1.2.x) to build the local pre12-meta cache (can take significant time, but after that it will be fast) - for more details see below. - check the compatibility notes (see below) and adapt your scripts, if needed. - if you run into any issues, please check the github issue tracker before posting new issues there or elsewhere. If you follow this procedure, you can help avoiding that we get a lot of "borg 1.2" issue reports that are not really 1.2 issues, but existed before and maybe just were not noticed. Compatibility notes: - matching of path patterns has been aligned with borg storing relative paths. Borg archives file paths without leading slashes. Previously, include/exclude patterns could contain leading slashes. You should check your patterns and remove leading slashes. - dropped support / testing for older Pythons, minimum requirement is 3.8. In case your OS does not provide Python >= 3.8, consider using our binary, which does not need an external Python interpreter. Or continue using borg 1.1.x, which is still supported. - freeing repository space only happens when "borg compact" is invoked. - mount: the default for --numeric-ids is False now (same as borg extract) - borg create --noatime is deprecated. Not storing atime is the default behaviour now (use --atime if you want to store the atime). - --prefix is deprecated, use -a / --glob-archives, see #6806 - list: corrected mix-up of "isomtime" and "mtime" formats. Previously, "isomtime" was the default but produced a verbose human format, while "mtime" produced a ISO-8601-like format. The behaviours have been swapped (so "mtime" is human, "isomtime" is ISO-like), and the default is now "mtime". "isomtime" is now a real ISO-8601 format ("T" between date and time, not a space). - create/recreate --list: file status for all files used to get announced *AFTER* the file (with borg < 1.2). Now, file status is announced *BEFORE* the file contents are processed. If the file status changes later (e.g. due to an error or a content change), the updated/final file status will be printed again. - removed deprecated-since-long stuff (deprecated since): - command "borg change-passphrase" (2017-02), use "borg key ..." - option "--keep-tag-files" (2017-01), use "--keep-exclude-tags" - option "--list-format" (2017-10), use "--format" - option "--ignore-inode" (2017-09), use "--files-cache" w/o "inode" - option "--no-files-cache" (2017-09), use "--files-cache=disabled" - removed BORG_HOSTNAME_IS_UNIQUE env var. to use borg you must implement one of these 2 scenarios: - 1) the combination of FQDN and result of uuid.getnode() must be unique and stable (this should be the case for almost everybody, except when having duplicate FQDN *and* MAC address or all-zero MAC address) - 2) if you are aware that 1) is not the case for you, you must set BORG_HOST_ID env var to something unique. - exit with 128 + signal number, #5161. if you have scripts expecting rc == 2 for a signal exit, you need to update them to check for >= 128. .. _changelog: Change Log ========== Version 1.2.8 (2024-03-29) -------------------------- For upgrade and compatibility hints, please also read the section "Upgrade Notes" above. Fixes: - check: fix return code and log level for index entry value discrepancies - with-lock: catch FileNotFoundError exception, print error msg, #8022 - benchmark: inherit options --rsh --remote-path, #8099 - fix Ctrl-C / SIGINT behaviour for pyinstaller-made binaries, #8155 New features: - upgrade --check-tam: check manifest TAM auth, exit with rc=1 if there are issues. - upgrade --check-archives-tam: check archives TAM auth, exit with rc=1 if there are issues. Other changes: - allow msgpack 1.0.8 (this might fix memory leaks with Python 3.12), #8133 - use the latest Cython 0.29.x - vagrant: - use / build binaries with python 3.9.19 - use generic/openbsd7 box - docs: - simplify TAM-related upgrade docs using the new commands - improve docs for borg with-lock, #8022 - add more infos borg check --repair recreating the shadow index to change log, see #6687 Version 1.2.7 (2023-12-02) -------------------------- Fixes: - docs: CVE-2023-36811 upgrade steps: consider checkpoint archives, #7802 - check/compact: fix spurious reappearance of orphan chunks since borg 1.2, #6687 - this consists of 2 fixes: - for existing chunks: check --repair: recreate shadow index, #7897 #6687 - for newly created chunks: update shadow index when doing a double-put, #7896 #5661 If you have experienced issue #6687, you may want to run borg check --repair after upgrading to borg 1.2.7 to recreate the shadow index and get rid of the issue for existing chunks. - LockRoster.modify: no KeyError if element was already gone, #7937 - create --X-from-command: run subcommands with a clean environment, #7916 - list --sort-by: support "archive" as alias of "name", #7873 - fix rc and msg if arg parsing throws an exception, #7885 Other changes: - support and test on Python 3.12 - include unistd.h in _chunker.c (fix for Python 3.13) - allow msgpack 1.0.6 and 1.0.7 - TAM issues: show tracebacks, improve borg check logging, #7797 - replace "datetime.utcfromtimestamp" with custom helper to avoid deprecation warnings when using Python 3.12 - vagrant: - use generic/debian9 box, fixes #7579 - add VM with debian bookworm / test on OpenSSL 3.0.x. - docs: - not only attack/unsafe, can also be a fs issue, #7853 - point to CVE-2023-36811 upgrade steps from borg 1.1 to 1.2 upgrade steps, #7899 - upgrade steps needed for all kinds of repos (including "none" encryption mode), #7813 - upgrade steps: talk about consequences of borg check, #7816 - upgrade steps: remove period that could be interpreted as part of the command - automated-local.rst: use GPT UUID for consistent udev rule - create disk/partition sector backup by disk serial number, #7934 - update macOS hint about full disk access - clarify borg prune -a option description, #7871 - readthedocs: also build offline docs (HTMLzip), #7835 - frontends: add "check.rebuild_refcounts" message Version 1.2.6 (2023-08-31) -------------------------- Fixes: - The upgrade procedure docs as published with borg 1.2.5 did not work, if the repository had archives resulting from a borg rename or borg recreate operation. The updated docs now use BORG_WORKAROUNDS=ignore_invalid_archive_tam at some places to avoid that issue, #7791. See: fix pre-1.2.5 archives spoofing vulnerability (CVE-2023-36811), details and necessary upgrade procedure described above. Other changes: - updated 1.2.5 changelog entry: 1.2.5 already has the fix for rename/recreate. - remove cython restrictions. recommended is to build with cython 0.29.latest, because borg 1.2.x uses this since years and it is very stable. you can also try to build with cython 3.0.x, there is a good chance that it works. as a 3rd option, we also bundle the `*.c` files cython outputs in the release pypi package, so you can also just use these and not need cython at all. Version 1.2.5 (2023-08-30) -------------------------- Fixes: - Security: fix pre-1.2.5 archives spoofing vulnerability (CVE-2023-36811), see details and necessary upgrade procedure described above. - rename/recreate: correctly update resulting archive's TAM, see #7791 - create: do not try to read parent dir of recursion root, #7746 - extract: fix false warning about pattern never matching, #4110 - diff: remove surrogates before output, #7535 - compact: clear empty directories at end of compact process, #6823 - create --files-cache=size: fix crash, #7658 - keyfiles: improve key sanity check, #7561 - only warn about "invalid" chunker params, #7590 - ProgressIndicatorPercent: fix space computation for wide chars, #3027 - improve argparse validator error messages New features: - mount: make up volname if not given (macOS), #7690. macFUSE supports a volname mount option to give what finder displays on the desktop / in the directory view. if the user did not specify it, we make something up, because otherwise it would be "macFUSE Volume 0 (Python)" and hide the mountpoint directory name. - BORG_WORKAROUNDS=authenticated_no_key to extract from authenticated repos without key, #7700 Other changes: - add `utcnow()` helper function to avoid deprecated `datetime.utcnow()` - stay on latest Cython 0.29 (0.29.36) for borg 1.2.x (do not use Cython 3.0 yet) - docs: - move upgrade notes to own section, see #7546 - mount -olocal: how to show mount in finder's sidebar, #5321 - list: fix --pattern examples, #7611 - improve patterns help - incl./excl. options, path-from-stdin exclusiveness - obfuscation docs: markup fix, note about MAX_DATA_SIZE - --one-file-system: add macOS apfs notes, #4876 - improve --one-file-system help string, #5618 - rewrite borg check docs - improve the docs for --keep-within, #7687 - fix borg init command in environment.rst.inc - 1.1.x upgrade notes: more precise borg upgrade instructions, #3396 - tests: - fix repo reopen - avoid long ids in pytest output - check buzhash chunksize distribution, see #7586 Version 1.2.4 (2023-03-24) -------------------------- New features: - import-tar: add --ignore-zeros to process concatenated tars, #7432. - debug id-hash: computes file/chunk content id-hash, #7406 - diff: --content-only does not show mode/ctime/mtime changes, #7248 - diff: JSON strings in diff output are now sorted alphabetically Bug fixes: - xattrs: fix namespace processing on FreeBSD, #6997 - diff: fix path related bug seen when addressing deferred items. - debug get-obj/put-obj: always give chunkid as cli param, see #7290 (this is an incompatible change, see also borg debug id-hash) - extract: fix mtime when ResourceFork xattr is set (macOS specific), #7234 - recreate: without --chunker-params, do not re-chunk, #7337 - recreate: when --target is given, do not detect "nothing to do". use case: borg recreate -a src --target dst can be used to make a copy of an archive inside the same repository, #7254. - set .hardlink_master for ALL hardlinkable items, #7175 - locking: fix host, pid, tid order. tid (thread id) must be parsed as hex from lock file name. - update development.lock.txt, including a setuptools security fix, #7227 Other changes: - requirements: allow msgpack 1.0.5 also - upgrade Cython to 0.29.33 - hashindex minor fixes, refactor, tweaks, tests - use os.replace not os.rename - remove BORG_LIBB2_PREFIX (not used any more) - docs: - BORG_KEY_FILE: clarify docs, #7444 - update FAQ about locale/unicode issues, #6999 - improve mount options rendering, #7359 - make timestamps in manual pages reproducible - installation: update Fedora in distribution list, #7357 - tests: - fix test_size_on_disk_accurate for large st_blksize, #7250 - add same_ts_ns function and use it for relaxed timestamp comparisons - "auto" compressor tests: don't assume a specific size, do not assume zlib is better than lz4, #7363 - add test for extracted directory mtime - vagrant: - upgrade local freebsd 12.1 box -> generic/freebsd13 box (13.1) - use pythons > 3.8 which work on freebsd 13.1 - pyenv: also install python 3.11.1 for testing - pyenv: use python 3.10.1, 3.10.0 build is broken on freebsd Version 1.2.3 (2022-12-24) -------------------------- Fixes: - create: fix --list --dry-run output for directories, #7209 - diff/recreate: normalize chunker params before comparing them, #7079 - check: fix uninitialised variable if repo is completely empty, #7034 - xattrs: improve error handling, #6988 - fix args.paths related argparsing, #6994 - archive.save(): always use metadata from stats (e.g. nfiles, size, ...), #7072 - tar_filter: recognize .tar.zst as zstd, #7093 - get_chunker: fix missing sparse=False argument, #7056 - file_integrity.py: make sure file_fd is always closed on exit - repository: cleanup(): close segment before unlinking - repository: use os.replace instead of os.rename Other changes: - remove python < 3.7 compatibility code - do not use version_tuple placeholder in setuptools_scm template - CI: fix tox4 passenv issue, #7199 - vagrant: update to python 3.9.16, use the openbsd 7.1 box - misc. test suite and docs fixes / improvements - remove deprecated --prefix from docs, #7109 - Windows: use MSYS2 for Github CI, remove Appveyor CI Version 1.2.2 (2022-08-20) -------------------------- New features: - prune/delete --checkpoint-interval=1800 and ctrl-c/SIGINT support, #6284 Fixes: - SaveFile: use a custom mkstemp with mode support, #6933, #6400, #6786. This fixes umask/mode/ACL issues (and also "chmod not supported" exceptions seen in 1.2.1) of files updated using SaveFile, e.g. the repo config. - hashindex_compact: fix eval order (check idx before use), #5899 - create --paths-from-(stdin|command): normalize paths, #6778 - secure_erase: avoid collateral damage, #6768. If a hardlink copy of a repo was made and a new repo config shall be saved, do NOT fill in random garbage before deleting the previous repo config, because that would damage the hardlink copy. - list: fix {flags:} formatting, #6081 - check: try harder to create the key, #5719 - misc commands: ctrl-c must not kill other subprocesses, #6912 - borg create with a remote repo via ssh - borg create --content-from-command - borg create --paths-from-command - (de)compression filter process of import-tar / export-tar Other changes: - deprecate --prefix, use -a / --glob-archives, see #6806 - make setuptools happy ("package would be ignored"), #6874 - fix pyproject.toml to create a fixed _version.py file, compatible with both old and new setuptools_scm version, #6875 - automate asciinema screencasts - CI: test on macOS 12 without fuse / fuse tests (too troublesome on github CI due to kernel extensions needed by macFUSE) - tests: fix test_obfuscate byte accounting - repository: add debug logging for issue #6687 - _chunker.c: fix warnings on macOS - requirements.lock.txt: use the latest cython 0.29.32 - docs: - add info on man page installation, #6894 - update archive_progress json description about "finished", #6570 - json progress_percent: some values are optional, #4074 - FAQ: full quota / full disk, #5960 - correct shell syntax for installation using git Version 1.2.1 (2022-06-06) -------------------------- Fixes: - create: skip with warning if opening the parent dir of recursion root fails, #6374 - create: fix crash. metadata stream can produce all-zero chunks, #6587 - fix crash when computing stats, escape % chars in archive name, #6500 - fix transaction rollback: use files cache filename as found in txn.active/, #6353 - import-tar: kill filter process in case of borg exceptions, #6401 #6681 - import-tar: fix mtime type bug - ensure_dir: respect umask for created directory modes, #6400 - SaveFile: respect umask for final file mode, #6400 - check archive: improve error handling for corrupt archive metadata block, make robust_iterator more robust, #4777 - pre12-meta cache: do not use the cache if want_unique is True, #6612 - fix scp-style repo url parsing for ip v6 address, #6526 - mount -o versions: give clear error msg instead of crashing. it does not make sense to request versions view if you only look at 1 archive, but the code shall not crash in that case as it did, but give a clear error msg. - show_progress: add finished=true/false to archive_progress json, #6570 - delete/prune: fix --iec mode output (decimal vs. binary units), #6606 - info: fix authenticated mode repo to show "Encrypted: No", #6462 - diff: support presence change for blkdev, chrdev and fifo items, #6615 New features: - delete: add repository id and location to prompt, #6453 - borg debug dump-repo-objs --ghost: new --segment=S --offset=O options Other changes: - support python 3.11 - allow msgpack 1.0.4, #6716 - load_key: no key is same as empty key, #6441 - give a more helpful error msg for unsupported key formats, #6561 - better error msg for defect or unsupported repo configs, #6566 - docs: - document borg 1.2 pattern matching behavior change, #6407 Make clear that absolute paths always go into the matcher as if they are relative (without leading slash). Adapt all examples accordingly. - authentication primitives: improved security and performance infos - mention BORG_FILES_CACHE_SUFFIX as alternative to BORG_FILES_CACHE_TTL, #5602 - FAQ: add a hint about --debug-topic=files_cache - improve borg check --max-duration description - fix values of TAG bytes, #6515 - borg compact --cleanup-commits also runs a normal compaction, #6324 - virtualization speed tips - recommend umask for passphrase file perms - borg 1.2 is security supported - update link to ubuntu packages, #6485 - use --numeric-ids in pull mode docs - remove blake2 docs, blake2 code not bundled any more, #6371 - clarify on-disk order and size of segment file log entry fields, #6357 - docs building: do not transform --/--- to unicode dashes - tests: - check that borg does not require pytest for normal usage, fixes #6563 - fix OpenBSD symlink mode test failure, #2055 - vagrant: - darwin64: remove fakeroot, #6314 - update development.lock.txt - use pyinstaller 4.10 and python 3.9.13 for binary build - upgrade VMCPUS and xdistn from 4 to 16, maybe this speeds up the tests - crypto: - use hmac.compare_digest instead of ==, #6470 - hmac_sha256: replace own cython wrapper code by hmac.digest python stdlib (since py38) - hmac and blake2b minor optimizations and cleanups - removed some unused crypto related code, #6472 - avoid losing the key (potential use-after-free). this never could happen in 1.2 due to the way we use the code. The issue was discovered in master after other changes, so we also "fixed" it here before it bites us. - setup / build: - add pyproject.toml, fix sys.path, #6466 - setuptools_scm: also require it via pyproject.toml - allow extra compiler flags for every extension build - fix misc. C / Cython compiler warnings, deprecation warnings - fix zstd.h include for bundled zstd, #6369 - source using python 3.8 features: ``pyupgrade --py38-plus ./**/*.py`` Version 1.2.0 (2022-02-22 22:02:22 :-) -------------------------------------- Fixes: - diff: reduce memory consumption, fix is_hardlink_master, #6295 - compact: fix / improve freeable / freed space log output - derive really freed space from quota use before/after, #5679 - do not say "freeable", but "maybe freeable" (based on hint, unsure) - fix race conditions in internal SaveFile function, #6306 #6028 - implement internal safe_unlink (was: truncate_and_unlink) function more safely: usually it does not truncate any more, only under "disk full" circumstances and only if there is only one hardlink. see: https://github.com/borgbackup/borg/discussions/6286 Other changes: - info: use a pre12-meta cache to accelerate stats for borg < 1.2 archives. the first time borg info is invoked on a borg 1.1 repo, it can take a rather long time computing and caching some stats values for 1.1 archives, which borg 1.2 archives have in their archive metadata structure. be patient, esp. if you have lots of old archives. following invocations are much faster due to the cache. related change: add archive name to calc_stats progress display. - docs: - add borg 1.2 upgrade notes, #6217 - link to borg placeholders and borg patterns help - init: explain the encryption modes better - clarify usage of patternfile roots - put import-tar docs into same file as export-tar docs - explain the difference between a path that ends with or without a slash, #6297 Version 1.2.0rc1 (2022-02-05) ----------------------------- Fixes: - repo::archive location placeholder expansion fixes, #5826, #5998 - repository: fix intermediate commits, shall be at end of current segment - delete: don't commit if nothing was deleted, avoid cache sync, #6060 - argument parsing: accept some options only once, #6026 - disallow overwriting of existing keyfiles on init, #6036 - if ensure_dir() fails, give more informative error message, #5952 New features: - delete --force: do not ask when deleting a repo, #5941 Other changes: - requirements: exclude broken or incompatible-with-pyinstaller setuptools - add a requirements.d/development.lock.txt and use it for vagrant - tests: - added nonce-related tests - refactor: remove assert_true - vagrant: macos box tuning, netbsd box fixes, #5370, #5922 - docs: - update install docs / requirements docs, #6180 - borg mount / FUSE "versions" view is not experimental any more - --pattern* is not experimental any more, #6134 - impact of deleting path/to/repo/nonce, #5858 - key export: add examples, #6204 - ~/.config/borg/keys is not used for repokey keys, #6107 - excluded parent dir's metadata can't restore Version 1.2.0b4 (2022-01-23) ---------------------------- Fixes: - create: fix passing device nodes and symlinks to --paths-from-stdin, #6009 - create --dry-run: fix display of kept tagfile, #5834 - check --repair: fix missing parameter in "did not consistently fail" msg, #5822 - fix hardlinkable file type check, #6037 - list: remove placeholders for shake_* hashes, #6082 - prune: handle case of calling prune_split when there are no archives, #6015 - benchmark crud: make sure cleanup of borg-test-data files/dir happens, #5630 - do not show archive name in repository-related error msgs, #6014 - prettier error msg (no stacktrace) if exclude file is missing, #5734 - do not require BORG_CONFIG_DIR if BORG_{SECURITY,KEYS}_DIR are set, #5979 - fix pyinstaller detection for dir-mode, #5897 - atomically create the CACHE_TAG file, #6028 - deal with the SaveFile/SyncFile race, docs, see #6056 708a5853 - avoid expanding path into LHS of formatting operation + tests, #6064 #6063 - repository: quota / compactable computation fixes - info: emit repo info even if repo has 0 archives + test, #6120 New features: - check --repair: significantly speed up search for next valid object in segment, #6022 - check: add progress indicator for archive check, #5809 - create: add retry_erofs workaround for O_NOATIME issue on volume shadow copies in WSL1, #6024 - create: allow --files-cache=size (this is potentially dangerous, use on your own risk), #5686 - import-tar: implement import-tar to complement export-tar, #2233 - implement BORG_SELFTEST env variable (can be carefully used to speedup borg hosting), #5871 - key export: print key if path is '-' or not given, #6092 - list --format: Add command_line to format keys Other changes: - pypi metadata: alpha -> beta - require python 3.8+, #5975 - use pyinstaller 4.7 - allow msgpack 1.0.3 - upgrade to bundled xxhash to 0.8.1 - import-tar / export-tar: tar file related changes: - check for short tarfile extensions - add .lz4 and .zstd - fix docs about extensions and decompression commands - add github codeql analysis, #6148 - vagrant: - box updates / add new boxes / remove outdated and broken boxes - use Python 3.9.10 (incl. binary builds) and 3.10.0 - fix pyenv initialisation, #5798 - fix vagrant scp on macOS, #5921 - use macfuse instead of osxfuse - shell completions: - update shell completions to 1.1.17, #5923 - remove BORG_LIBC completion, since 9914968 borg no longer uses find_library(). - docs: - fixed readme.rst irc webchat link (we use libera chat now, not freenode) - fix exceptions thrown by `setup.py build_man` - check --repair: recommend checking hw before check --repair, #5855 - check --verify-data: clarify and document conflict with --repository-only, #5808 - serve: improve ssh forced commands docs, #6083 - list: improve docs for `borg list` --format, #6061 - list: remove --list-format from borg list - FAQ: fix manifest-timestamp path (inside security dir) - fix the broken link to .nix file - document behavior for filesystems with inconsistent inodes, #5770 - clarify user_id vs uid for fuse, #5723 - clarify pattern usage with commands, #5176 - clarify pp vs. pf pattern type, #5300 - update referenced freebsd/macOS versions used for binary build, #5942 - pull mode: add some warnings, #5827 - clarify "you will need key and passphrase" borg init warning, #4622 - add missing leading slashes in help patterns, #5857 - add info on renaming repositories, #5240 - check: add notice about defective hardware, #5753 - mention tar --compare (compare archive to fs files), #5880 - add note about grandfather-father-son backup retention policy / rotation scheme, #6006 - permissions note rewritten to make it less confusing - create github security policy - remove leftovers of BORG_HOSTNAME_IS_UNIQUE - excluded parent dir's metadata can't restore. (#6062) - if parent dir is not extracted, we do not have its metadata - clarify who starts the remote agent Version 1.2.0b3 (2021-05-12) ---------------------------- Fixes: - create: fix --progress --log-json, #4360#issuecomment-774580052 - do not load files cache for commands not using it, #5673 - fix repeated cache tag file writing bug New features: - create/recreate: print preliminary file status early, #5417 - create/extract: add --noxattrs and --noacls options, #3955 - create: verbose files cache logging via --debug-topic=files_cache, #5659 - mount: implement --numeric-ids (default: False!), #2377 - diff: add --json-lines option - info / create --stats: add --iec option to print sizes in powers of 1024. Other changes: - create: add --upload-(ratelimit|buffer), deprecate --remote-* options, #5611 - create/extract/mount: add --numeric-ids, deprecate --numeric-owner option, #5724 - config: accept non-int value for max_segment_size / storage_quota - use PyInstaller v4.3, #5671 - vagrant: use Python 3.9.5 to build binaries - tox.ini: modernize and enable execution without preinstalling deps - cleanup code style checks - get rid of distutils, use setuptools+packaging - github CI: test on Python 3.10-dev - check: missing / healed chunks: always tell chunk ID, #5704 - docs: - remove bad /var/cache exclusion in example commands, #5625 - misc. fixes and improvements, esp. for macOS - add unsafe workaround to use an old repo copy, #5722 Version 1.2.0b2 (2021-02-06) ---------------------------- Fixes: - create: do not recurse into duplicate roots, #5603 - create: only print stats if not ctrl-c'ed, fixes traceback, #5668 - extract: improve exception handling when setting xattrs, #5092. emit a warning message giving the path, xattr key and error message. continue trying to restore other xattrs and bsdflags of the same file after an exception with xattr-setting happened. - export-tar: fix memory leak with ssh: remote repository, #5568. fix potential memory leak with ssh: remote repository with partial extraction. - remove empty shadowed_segments lists, #5275 - fix bad default: manifest.archives.list(consider_checkpoints=False), fixes tracebacks / KeyErros for missing objects in ChunkIndex, #5668 New features: - create: improve sparse file support - create --sparse (detect sparse file holes) and file map support, only for the "fixed" chunker, #14 - detect all-zero chunks in read data in "buzhash" and "fixed" chunkers - cached_hash: use a small LRU cache to accelerate all-zero chunks hashing - use cached_hash also to generate all-zero replacement chunks - create --remote-buffer, add a upload buffer for remote repos, #5574 - prune: keep oldest archive when retention target not met Other changes: - use blake2 from python 3.6+ hashlib (this removes the requirement for libb2 and the bundled blake2 code) - also accept msgpack up to 1.0.2. exclude 1.0.1 though, which had some issues (not sure they affect borg). - create: add repository location to --stats output, #5491 - check: debug log the segment filename - delete: add a --list switch to borg delete, #5116 - borg debug dump-hints - implemented to e.g. to look at shadow_index - Tab completion support for additional archives for 'borg delete' - refactor: have one borg.constants.zero all-zero bytes object - refactor shadow_index updating repo.put/delete, #5661, #5636. - docs: - add another case of attempted hardlink usage - fix description of borg upgrade hardlink usage, #5518 - use HTTPS everywhere - add examples for --paths-from-stdin, --paths-from-command, --paths-separator, #5644 - fix typos/grammar - update docs for dev environment installation instructions - recommend running tests only on installed versions for setup - add badge with current status of package - vagrant: - use brew install --cask ..., #5557 - use Python 3.9.1 and PyInstaller 4.1 to build the borg binary Version 1.2.0b1 (2020-12-06) ---------------------------- Fixes: - BORG_CACHE_DIR crashing borg if empty, atomic handling of recursive directory creation, #5216 - fix --dry-run and --stats coexistence, #5415 - allow EIO with warning when trying to hardlink, #4336 - export-tar: set tar format to GNU_FORMAT explicitly, #5274 - use --timestamp for {utcnow} and {now} if given, #5189 - make timestamp helper timezone-aware New features: - create: implement --paths-from-stdin and --paths-from-command, see #5492. These switches read paths to archive from stdin. Delimiter can specified by --paths-delimiter=DELIM. Paths read will be added honoring every option but exclusion options and --one-file-system. borg won't recurse into directories. - 'obfuscate' pseudo compressor obfuscates compressed chunk size in repo - add pyfuse3 (successor of llfuse) as an alternative lowlevel fuse implementation to llfuse (deprecated), #5407. FUSE implementation can be switched via env var BORG_FUSE_IMPL. - allow appending to the files cache filename with BORG_FILES_CACHE_SUFFIX - create: implement --stdin-mode, --stdin-user and --stdin-group, #5333 Other changes: - split recursive directory walking/processing into directory walking and item processing. - fix warning by importing setuptools before distutils. - debug info: include infos about FUSE implementation, #5546 - testing: - add a test for the hashindex corruption bug, #5531 #4829 - move away from travis-ci, use github actions, #5528 #5467 - test both on fuse2 and fuse3 - upload coverage reports to codecov - fix spurious failure in test_cache_files, #5438 - add tests for Location.with_timestamp - tox: add a non-fuse env to the envlist - vagrant: - use python 3.7.latest and pyinstaller 4.0 for binary creation - pyinstaller: compute basepath from spec file location - vagrant: updates/fixes for archlinux box, #5543 - docs: - "filename with spaces" example added to exclude file, #5236 - add a hint about sleeping computer, #5301 - how to adjust macOS >= Catalina security settings, #5303 - process/policy for adding new compression algorithms - updated docs about hacked backup client, #5480 - improve ansible deployment docs, make it more generic - how to approach borg speed issues, give speed example, #5371 - fix mathematical inaccuracy about chunk size, #5336 - add example for excluding content using --pattern cli option - clarify borg create's '--one-file-system' option, #4009 - improve docs/FAQ about append-only remote repos, #5497 - fix reST markup issues, labels - add infos about contributor retirement status Version 1.2.0a9 (2020-10-05) ---------------------------- Fixes: - fix memory leak related to preloading, #5202 - check --repair: fix potential data loss, #5325 - persist shadow_index in between borg runs, #4830 - fix hardlinked CACHEDIR.TAG processing, #4911 - --read-special: .part files also should be regular files, #5217 - allow server side enforcing of umask, --umask is for the local borg process only (see docs), #4947 - exit with 128 + signal number, #5161 - borg config --list does not show last_segment_checked, #5159 - locking: - fix ExclusiveLock race condition bug, #4923 - fix race condition in lock migration, #4953 - fix locking on openindiana, #5271 New features: - --content-from-command: create archive using stdout of given command, #5174 - allow key-import + BORG_KEY_FILE to create key files - build directory-based binary for macOS to avoid Gatekeeper delays Other changes: - upgrade bundled zstd to 1.4.5 - upgrade bundled xxhash to 0.8.0, #5362 - if self test fails, also point to OS and hardware, #5334 - misc. shell completions fixes/updates, rewrite zsh completion - prettier error message when archive gets too big, #5307 - stop relying on `false` exiting with status code 1 - rephrase some warnings, #5164 - parseformat: unnecessary calls removed, #5169 - testing: - enable Python3.9 env for test suite and VMs, #5373 - drop python 3.5, #5344 - misc. vagrant fixes/updates - misc. testing fixes, #5196 - docs: - add ssh-agent pull backup method to doc, #5288 - mention double --force in prune docs - update Homebrew install instructions, #5185 - better description of how cache and rebuilds of it work and how the workaround applies to that - point to borg create --list item flags in recreate usage, #5165 - add a note to create from stdin regarding files cache, #5180 - add security faq explaining AES-CTR crypto issues, #5254 - clarify --exclude-if-present in recreate, #5193 - add socat pull mode, #5150, #900 - move content of resources doc page to community project, #2088 - explain hash collision, #4884 - clarify --recompress option, #5154 Version 1.2.0a8 (2020-04-22) ---------------------------- Fixes: - fixed potential index corruption / data loss issue due to bug in hashindex_set, #4829. Please read and follow the more detailed notes close to the top of this document. - fix crash when upgrading erroneous hints file, #4922 - commit-time free space calc: ignore bad compact map entries, #4796 - info: if the archive doesn't exist, print a pretty message, #4793 - --prefix / -P: fix processing, avoid argparse issue, #4769 - ignore EACCES (errno 13) when hardlinking, #4730 - add a try catch when formatting the info string, #4818 - check: do not stumble over invalid item key, #4845 - update prevalence of env vars to set config and cache paths - mount: fix FUSE low linear read speed on large files, #5032 - extract: fix confusing output of borg extract --list --strip-components, #4934 - recreate: support --timestamp option, #4745 - fix ProgressIndicator msgids (JSON output), #4935 - fuse: set f_namemax in statfs result, #2684 - accept absolute paths on windows - pyinstaller: work around issue with setuptools > 44 New features: - chunker speedup (plus regression test) - added --consider-checkpoints and related test, #4788 - added --noflags option, deprecate --nobsdflags option, #4489 - compact: add --threshold option, #4674 - mount: add birthtime to FUSE entries - support platforms with no os.link, #4901 - if we don't have os.link, we just extract another copy instead of making a hardlink. - move sync_file_range to its own extension for better platform compatibility. - new --bypass-lock option to bypass locking, e.g. for read-only repos - accept absolute paths by removing leading slashes in patterns of all sorts but re: style, #4029 - delete: new --keep-security-info option Other changes: - support msgpack 0.6.2 and 1.0.0, #5065 - upgrade bundled zstd to 1.4.4 - upgrade bundled lz4 to 1.9.2 - upgrade xxhash to 0.7.3 - require recent enough llfuse for birthtime support, #5064 - only store compressed data if the result actually is smaller, #4516 - check: improve error output for matching index size, see #4829 - ignore --stats when given with --dry-run, but continue, #4373 - replaced usage of os.statvfs with shutil.disk_usage (better cross-platform support). - fuse: remove unneeded version check and compat code, micro opts - docs: - improve description of path variables - document how to completely delete data, #2929 - add FAQ about Borg config dir, #4941 - add docs about errors not printed as JSON, #4073 - update usage_general.rst.inc - added "Will move with BORG_CONFIG_DIR variable unless specified." to BORG_SECURITY_DIR info. - put BORG_SECURITY_DIR immediately below BORG_CONFIG_DIR (and moved BORG_CACHE_DIR up before them). - add paragraph regarding cache security assumptions, #4900 - tell about borg cache security precautions - add FAQ describing difference between a local repo vs. repo on a server. - document how to test exclusion patterns without performing an actual backup - create: tell that "Calculating size" time and space needs are caused by --progress - fix/improve documentation for @api decorator, #4674 - add a pull backup / push restore how-to, #1552 - fix man pages creation, #4752 - more general FAQ for backup and retain original paths, #4532 - explain difference between --exclude and --pattern, #4118 - add FAQ for preventing SSH timeout in extract, #3866 - improve password FAQ (decrease pw length, add -w 0 option to base64 to prevent line wrap), #4591 - add note about patterns and stored paths, #4160 - add upgrade of tools to pip installation how-to, #5090 - document one cause of orphaned chunks in check command, #2295 - clean up the whole check usage paragraph - FAQ: linked recommended restrictions to ssh public keys on borg servers, #4946 - fixed "doc downplays severity of Nonce reuse issue", #4883 - borg repo restore instructions needed, #3428 - new FAQ: A repo is corrupt and must be replaced with an older repo. - clarify borg init's encryption modes - native windows port: - update README_WINDOWS.rst - updated pyinstaller spec file to support windows builds - testing / CI: - improved travis config / install script, improved macOS builds - allow osx builds to fail, #4955 - Windows 10 build on Appveyor CI - vagrant: - upgrade pyinstaller to v3.5 + patch - use py369 for binary build, add py380 for tests - fix issue in stretch VM hanging at grub installation - add a debian buster and a ubuntu focal VM - update darwin box to 10.12 - upgrade FreeBSD box to 12.1 - fix debianoid virtualenv packages - use pyenv in freebsd64 VM - remove the flake8 test - darwin: avoid error if pkg is already installed - debianoid: don't interactively ask questions Version 1.2.0a7 (2019-09-07) ---------------------------- Fixes: - slave hardlinks extraction issue, see #4350 - extract: fix KeyError for "partial" extraction, #4607 - preload chunks for hardlink slaves w/o preloaded master, #4350 - fix preloading for old remote servers, #4652 - fix partial extract for hardlinked contentless file types, #4725 - Repository.open: use stat() to check for repo dir, #4695 - Repository.check_can_create_repository: use stat() to check, ~ #4695. - SecurityManager.known(): check all files, #4614 - after double-force delete, warn about necessary repair, #4704 - cope with ANY error when importing pytest into borg.testsuite, #4652 - fix invalid archive error message - setup.py: fix detection of missing Cython - filter out selinux xattrs, #4574 - location arg - should it be optional? #4541 - enable placeholder usage in --comment, #4559 - use whitelist approach for borg serve, #4097 New features: - minimal native Windows support, see windows readme (work in progress) - create: first ctrl-c (SIGINT) triggers checkpoint and abort, #4606 - new BORG_WORKAROUNDS mechanism, basesyncfile, #4710 - remove WSL autodetection. if WSL still has this problem, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around it. - support xxh64 checksum in addition to the hashlib hashes in borg list - enable placeholder usage in all extra archive arguments - enable placeholder usage in --comment, #4559 - enable placeholder usage in --glob-archives, #4495 - ability to use a system-provided version of "xxhash" - create: - changed the default behaviour to not store the atime of fs items. atime is often rather not interesting and fragile - it easily changes even if nothing else has changed and, if stored into the archive, spoils deduplication of the archive metadata stream. - if you give the --noatime option, borg will output a deprecation warning because it is currently ignored / does nothing. Please remove the --noatime option when using borg 1.2. - added a --atime option for storing files' atime into an archive Other changes: - argparser: always use REPOSITORY in metavar - do not check python/libc for borg serve, #4483 - small borg compact improvements, #4522 - compact: log freed space at INFO level - tests: - tox / travis: add testing on py38-dev - fix broken test that relied on improper zlib assumptions - pure-py msgpack warning shall not make a lot of tests fail, #4558 - rename test_mount_hardlinks to test_fuse_mount_hardlinks (master) - vagrant: add up-to-date openindiana box (py35, openssl10) - get rid of confusing coverage warning, #2069 - docs: - reiterate that 'file cache names are absolute' in FAQ, mention bind mount solution, #4738 - add restore docs, #4670 - updated docs to cover use of temp directory on remote, #4545 - add a push-style example to borg-create(1), #4613 - timestamps in the files cache are now usually ctime, #4583 - benchmark crud: clarify that space is used until compact - update documentation of borg create, corrects a mention of borg 1.1 as a future version. - fix osxfuse github link in installation docs - how to supply a passphrase, use crypto devices, #4549 - extract: document limitation "needs empty destination", #4598 - update macOS Brew link - add note about software for automating backup - compact: improve docs, - README: new URL for funding options Version 1.2.0a6 (2019-04-22) ---------------------------- Fixes: - delete / prune: consider part files correctly for stats, #4507 - fix "all archives" stats considering part files, #4329 - create: only run stat_simple_attrs() once - create: --stats does not work with --dry-run, exit with error msg, #4373 - give "invalid repo" error msg if repo config not found, #4411 New features: - display msgpack version as part of sysinfo (e.g. in tracebacks) Other changes: - docs: - sdd "SSH Configuration" section, #4493, #3988, #636, #4485 - better document borg check --max-duration, #4473 - sorted commands help in multiple steps, #4471 - testing: - travis: use py 3.5.3 and 3.6.7 on macOS to get a pyenv-based python build with openssl 1.1 - vagrant: use py 3.5.3 and 3.6.8 on darwin64 VM to build python and borg with openssl 1.1 - pytest: -v and default XDISTN to 1, #4481 Version 1.2.0a5 (2019-03-21) ---------------------------- Fixes: - warn if a file has changed while being backed up, #1750 - lrucache: regularly remove old FDs, #4427 - borg command shall terminate with rc 2 for ImportErrors, #4424 - make freebsd xattr platform code api compatible with linux, #3952 Other changes: - major setup code refactoring (especially how libraries like openssl, liblz4, libzstd, libb2 are discovered and how it falls back to code bundled with borg), new: uses pkg-config now (and needs python "pkgconfig" package installed), #1925 if you are a borg package maintainer, please try packaging this (see comments in setup.py). - Vagrantfile: add zstd, reorder, build env vars, #4444 - travis: install script improvements - update shell completions - docs: - add a sample logging.conf in docs/misc, #4380 - fix spelling errors - update requirements / install docs, #4374 Version 1.2.0a4 (2019-03-11) ---------------------------- Fixes: - do not use O_NONBLOCK for special files, like FIFOs, block and char devices when using --read-special. fixes backing up FIFOs. fixes to test. #4394 - more LibreSSL build fixes: LibreSSL has HMAC_CTX_free and HMAC_CTX_new New features: - check: incremental repo check (only checks crc32 for segment entries), #1657 borg check --repository-only --max-duration SECONDS ... - delete: timestamp for borg delete --info added, #4359 Other changes: - redo stale lock handling, #3986 drop BORG_HOSTNAME_IS_UNIQUE (please use BORG_HOST_ID if needed). borg now always assumes it has a unique host id - either automatically from fqdn plus uuid.getnode() or overridden via BORG_HOST_ID. - docs: - added Alpine Linux to distribution list - elaborate on append-only mode docs - vagrant: - darwin: new 10.12 box - freebsd: new 12.0 box - openbsd: new 6.4 box - misc. updates / fixes Version 1.2.0a3 (2019-02-26) ---------------------------- Fixes: - LibreSSL build fixes, #4403 - dummy ACL/xattr code fixes (used by OpenBSD and others), #4403 - create: fix openat/statat issues for root directory, #4405 Version 1.2.0a2 and earlier (2019-02-24) ---------------------------------------- New features: - compact: "borg compact" needs to be used to free repository space by compacting the segments (reading sparse segments, rewriting still needed data to new segments, deleting the sparse segments). Borg < 1.2 invoked compaction automatically at the end of each repository writing command. Borg >= 1.2 does not do that any more to give better speed, more control, more segment file stability (== less stuff moving to newer segments) and more robustness. See the docs about "borg compact" for more details. - "borg compact --cleanup-commits" is to cleanup the tons of 17byte long commit-only segment files caused by borg 1.1.x issue #2850. Invoke this once after upgrading (the server side) borg to 1.2. Compaction now automatically removes unneeded commit-only segment files. - prune: Show which rule was applied to keep archive, #2886 - add fixed blocksize chunker (see --chunker-params docs), #1086 Fixes: - avoid stale filehandle issues, #3265 - use more FDs, avoid race conditions on active fs, #906, #908, #1038 - add O_NOFOLLOW to base flags, #908 - compact: - require >10% freeable space in a segment, #2985 - repository compaction now automatically removes unneeded 17byte commit-only segments, #2850 - make swidth available on all posix platforms, #2667 Other changes: - repository: better speed and less stuff moving around by using separate segment files for manifest DELETEs and PUTs, #3947 - use pyinstaller v3.3.1 to build binaries - update bundled zstd code to 1.3.8, #4210 - update bundled lz4 code to 1.8.3, #4209 - msgpack: - switch to recent "msgpack" pypi pkg name, #3890 - wrap msgpack to avoid future compat complications, #3632, #2738 - support msgpack 0.6.0 and 0.6.1, #4220, #4308 - llfuse: modernize / simplify llfuse version requirements - code refactorings / internal improvements: - include size/csize/nfiles[_parts] stats into archive, #3241 - calc_stats: use archive stats metadata, if available - crypto: refactored crypto to use an AEAD style API - crypto: new AES-OCB, CHACHA20-POLY1305 - create: use less syscalls by not using a python file obj, #906, #3962 - diff: refactor the diff functionality to new ItemDiff class, #2475 - archive: create FilesystemObjectProcessors class - helpers: make a package, split into smaller modules - xattrs: move to platform package, use cython instead ctypes, #2495 - xattrs/acls/bsdflags: misc. code/api optimizations - FUSE: separate creation of filesystem from implementation of llfuse funcs, #3042 - FUSE: use unpacker.tell() instead of deprecated write_bytes, #3899 - setup.py: move build_man / build_usage code to setup_docs.py - setup.py: update to use a newer Cython/setuptools API for compiling .pyx -> .c, #3788 - use python 3.5's os.scandir / os.set_blocking - multithreading preparations (not used yet): - item.to_optr(), Item.from_optr() - fix chunker holding the GIL during blocking I/O - C code portability / basic MSC compatibility, #4147, #2677 - testing: - vagrant: new VMs for linux/bsd/darwin, most with OpenSSL 1.1 and py36 Version 1.1.18 (2022-06-05) --------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. There is one exception though: If you upgrade from an unpatched borg < 1.0.9, please read that section above: "Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099)" - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - 1.1.11 removes WSL autodetection (Windows 10 Subsystem for Linux). If WSL still has a problem with sync_file_range, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around the WSL issue. - 1.1.14 changes return codes due to a bug fix: In case you have scripts expecting rc == 2 for a signal exit, you need to update them to check for >= 128 (as documented since long). - 1.1.15 drops python 3.4 support, minimum requirement is 3.5 now. - 1.1.17 install_requires the "packaging" pypi package now. New features: - check --repair: significantly speed up search for next valid object in segment, #6022 - create: add retry_erofs workaround for O_NOATIME issue on volume shadow copies in WSL1, #6024 - key export: display key if path is '-' or not given, #6092 - list --format: add command_line to format keys, #6108 Fixes: - check: improve error handling for corrupt archive metadata block, make robust_iterator more robust, #4777 - diff: support presence change for blkdev, chrdev and fifo items, #6483 - diff: reduce memory consumption, fix is_hardlink_master - init: disallow overwriting of existing keyfiles - info: fix authenticated mode repo to show "Encrypted: No", #6462 - info: emit repo info even if repo has 0 archives, #6120 - list: remove placeholders for shake_* hashes, #6082 - mount -o versions: give clear error msg instead of crashing - show_progress: add finished=true/false to archive_progress json, #6570 - fix hardlinkable file type check, #6037 - do not show archive name in error msgs referring to the repository, #6023 - prettier error msg (no stacktrace) if exclude file is missing, #5734 - do not require BORG_CONFIG_DIR if BORG_{SECURITY,KEYS}_DIR are set, #5979 - atomically create the CACHE_TAG file, #6028 - deal with the SaveFile/SyncFile race, docs, see #6176 5c5b59bc9 - avoid expanding path into LHS of formatting operation + tests, #6064 #6063 - repository: quota / compactable computation fixes, #6119. This is mainly to keep the repo code in sync with borg 1.2. As borg 1.1 compacts immediately, there was not really an issue with this in 1.1. - fix transaction rollback: use files cache filename as found in txn.active, #6353 - do not load files cache for commands not using it, fixes #5673 - fix scp repo url parsing for ip v6 addrs, #6526 - repo::archive location placeholder expansion fixes, #5826, #5998 - use expanded location for log output - support placeholder expansion for BORG_REPO env var - respect umask for created directory and file modes, #6400 - safer truncate_and_unlink implementation Other changes: - upgrade bundled xxhash code to 0.8.1 - fix xxh64 related build (setup.py and post-0.8.1 patch for static_assert). The patch was required to build the bundled xxhash code on FreeBSD, see https://github.com/Cyan4973/xxHash/pull/670 - msgpack build: remove endianness macro, #6105 - update and fix shell completions - fuse: remove unneeded version check and compat code - delete --force: do not ask when deleting a repo, #5941 - delete: don't commit if nothing was deleted, avoid cache sync, #6060 - delete: add repository id and location to prompt - compact segments: improve freeable / freed space log output, #5679 - if ensure_dir() fails, give more informative error message, #5952 - load_key: no key is same as empty key, #6441 - better error msg for defect or unsupported repo configs, #6566 - use hmac.compare_digest instead of ==, #6470 - implement more standard hashindex.setdefault behaviour - remove stray punctuation from secure-erase message - add development.lock.txt, use a real python 3.5 to generate frozen reqs - setuptools 60.7.0 breaks pyinstaller, #6246 - setup.py clean2 was added to work around some setuptools customizability limitation. - allow extra compiler flags for every extension build - C code: make switch fallthrough explicit - Cython code: fix "useless trailing comma" cython warnings - requirements.lock.txt: use the latest cython 0.29.30 - fix compilation warnings: ‘PyUnicode_AsUnicode’ is deprecated - docs: - ~/.config/borg/keys is not used for repokey keys, #6107 - excluded parent dir's metadata can't restore, #6062 - permissions note rewritten to make it less confusing, #5490 - add note about grandfather-father-son backup retention policy / rotation scheme - clarify who starts the remote agent (borg serve) - test/improve pull backup docs, #5903 - document the socat pull mode described in #900 #515ß - borg serve: improve ssh forced commands docs, #6083 - improve docs for borg list --format, #6080 - fix the broken link to .nix file - clarify pattern usage with commands, #5176 - clarify user_id vs uid for fuse, #5723 - fix binary build freebsd/macOS version, #5942 - FAQ: fix manifest-timestamp path, #6016 - remove duplicate faq entries, #5926 - fix sphinx warnings, #5919 - virtualisation speed tips - fix values of TAG bytes, #6515 - recommend umask for passphrase file perms - update link to ubuntu packages, #6485 - clarify on-disk order and size of log entry fields, #6357 - do not transform --/--- to unicode dashes - improve linking inside docs, link to borg_placeholders, link to borg_patterns - use same phrasing in misc. help texts - borg init: explain the encryption modes better - explain the difference between a path that ends with or without a slash, #6297 - clarify usage of patternfile roots, #6242 - borg key export: add examples - updates about features not experimental any more: FUSE "versions" view, --pattern*, #6134 - fix/update cygwin package requirements - impact of deleting path/to/repo/nonce, #5858 - warn about tampered server nonce - mention BORG_FILES_CACHE_SUFFIX as alternative to BORG_FILES_CACHE_TTL, #5602 - add a troubleshooting note about "is not a valid repository" to the FAQ - vagrant / CI / testing: - misc. fixes and updates, new python versions - macOS on github: re-enable fuse2 testing by downgrading to older macOS, #6099 - fix OpenBSD symlink mode test failure, #2055 - use the generic/openbsd6 box - strengthen the test: we can read data w/o nonces - add tests for path/to/repo/nonce deletion - darwin64: backport some tunings from master - darwin64: remove fakeroot, #6314 - darwin64: fix vagrant scp, #5921 - darwin64: use macfuse instead of osxfuse - add ubuntu "jammy" 22.04 LTS VM - adapt memory for openindiana64 and darwin64 Version 1.1.17 (2021-07-12) --------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. There is one exception though: If you upgrade from an unpatched borg < 1.0.9, please read that section above: "Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099)" - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - 1.1.11 removes WSL autodetection (Windows 10 Subsystem for Linux). If WSL still has a problem with sync_file_range, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around the WSL issue. - 1.1.14 changes return codes due to a bug fix: In case you have scripts expecting rc == 2 for a signal exit, you need to update them to check for >= 128 (as documented since long). - 1.1.15 drops python 3.4 support, minimum requirement is 3.5 now. - 1.1.17 install_requires the "packaging" pypi package now. Fixes: - pyinstaller dir-mode: fix pyi detection / LIBPATH treatment, #5897 - handle crash due to kill stale lock race, #5828 - fix BORG_CACHE_DIR crashing borg if empty, #5216 - create --dry-run: fix display of kept tagfile, #5834 - fix missing parameter in "did not consistently fail" msg, #5822 - missing / healed chunks: always tell chunk ID, #5704 - benchmark: make sure cleanup happens even on exceptions, #5630 New features: - implement BORG_SELFTEST env variable, #5871. this can be used to accelerate borg startup a bit. not recommended for normal usage, but borg mass hosters with a lot of borg invocations can save some resources with this. on my laptop, this saved ~100ms cpu time (sys+user) per borg command invocation. - implement BORG_LIBC env variable to give the libc filename, #5870. you can use this if a borg does not find your libc. - check: add progress indicator for archive check. - allow --files-cache=size (not recommended, make sure you know what you do) Other changes: - Python 3.10 now officially supported! we test on py310-dev on github CI since a while and now also on the vagrant machines, so it should work ok. - github CI: test on py310 (again) - get rid of distutils, use packaging and setuptools. distutils is deprecated and gives warnings on py 3.10. - setup.py: rename "clean" to "clean2" to avoid shadowing the "clean" command. - remove libc filename fallback for the BSDs (there is no "usual" name) - cleanup flake8 checks, fix some pep8 violations. - docs building: replace deprecated function ".add_stylesheet()" for Sphinx 4 compatibility - docs: - add a hint on sleeping computer and ssh connections, #5301 - update the documentation on hacked backup client, #5480 - improve docs/FAQ about append-only remote repos, #5497 - complement the documentation for pattern files and exclude files, #5520 - "filename with spaces" example added to exclude file, #5236 note: no whitespace escaping needed, processed by borg. - add info on renaming repositories, #5240 - clarify borg check --verify-data, #5808 - add notice about defective hardware to check documentation, #5753 - add paragraph added in #5855 to utility documentation source - add missing leading slashes in help patterns, #5857 - clarify "you will need key and passphrase" borg init warning, #4622 - pull mode: add some warnings, #5827 - mention tar --compare (compare archive to fs files), #5880 - fix typos, backport of #5597 - vagrant: - add py3.7.11 for binary build, also add 3.10-dev. - use latest Cython 0.29.23 for py310 compat fixes. - more RAM for openindiana upgrade plan resolver, it just hangs (swaps?) if there is too little RAM. - fix install_pyenv to adapt to recent changes in pyenv (same as in master now). - use generic/netbsd9 box, copied from master branch. Version 1.1.16 (2021-03-23) --------------------------- Fixes: - setup.py: add special openssl prefix for Apple M1 compatibility - do not recurse into duplicate roots, #5603 - remove empty shadowed_segments lists, #5275, #5614 - fix libpython load error when borg fat binary / dir-based binary is invoked via a symlink by upgrading pyinstaller to v4.2, #5688 - config: accept non-int value (like 500M or 100G) for max_segment_size or storage_quota, #5639. please note: when setting a non-int value for this in a repo config, using the repo will require borg >= 1.1.16. New features: - bundled msgpack: drop support for old buffer protocol to support Python 3.10 - verbose files cache logging via --debug-topic=files_cache, #5659. Use this if you suspect that borg does not detect unmodified files as expected. - create/extract: add --noxattrs and --noacls option, #3955. when given with borg create, borg will not get xattrs / ACLs from input files (and thus, it will not archive xattrs / ACLs). when given with borg extract, borg will not read xattrs / ACLs from archive and will not set xattrs / ACLs on extracted files. - diff: add --json-lines option, #3765 - check: debug log segment filename - borg debug dump-hints Other changes: - Tab completion support for additional archives for 'borg delete' - repository: deduplicate code of put and delete, no functional change - tests: fix result order issue (sporadic test failure on openindiana) - vagrant: - upgrade pyinstaller to v4.2, #5671 - avoid grub-install asking interactively for device - remove the xenial box - update freebsd box to 12.1 - docs: - update macOS install instructions, #5677 - use macFUSE (not osxfuse) for Apple M1 compatibility - update docs for dev environment installation instructions, #5643 - fix grammar in faq - recommend running tests only on installed versions for setup - add link back to git-installation - remove /var/cache exclusion in example commands, #5625. This is generally a poor idea and shouldn't be promoted through examples. - add repology.org badge with current packaging status - explain hash collision - add unsafe workaround to use an old repo copy, #5722 Version 1.1.15 (2020-12-25) --------------------------- Fixes: - extract: - improve exception handling when setting xattrs, #5092. - emit a warning message giving the path, xattr key and error message. - continue trying to restore other xattrs and bsdflags of the same file after an exception with xattr-setting happened. - export-tar: - set tar format to GNU_FORMAT explicitly, #5274 - fix memory leak with ssh: remote repository, #5568 - fix potential memory leak with ssh: remote repository with partial extraction - create: fix --dry-run and --stats coexistence, #5415 - use --timestamp for {utcnow} and {now} if given, #5189 New features: - create: implement --stdin-mode, --stdin-user and --stdin-group, #5333 - allow appending the files cache filename with BORG_FILES_CACHE_SUFFIX env var Other changes: - drop python 3.4 support, minimum requirement is 3.5 now. - enable using libxxhash instead of bundled xxh64 code - update llfuse requirements (1.3.8) - set cython language_level in some files to fix warnings - allow EIO with warning when trying to hardlink - PropDict: fail early if internal_dict is not a dict - update shell completions - tests / CI - add a test for the hashindex corruption bug, #5531 #4829 - fix spurious failure in test_cache_files, #5438 - added a github ci workflow - reduce testing on travis, no macOS, no py3x-dev, #5467 - travis: use newer dists, native py on dist - vagrant: - remove jessie and trusty boxes, #5348 #5383 - pyinstaller 4.0, build on py379 - binary build on stretch64, #5348 - remove easy_install based pip installation - docs: - clarify '--one-file-system' for btrfs, #5391 - add example for excluding content using the --pattern cmd line arg - complement the documentation for pattern files and exclude files, #5524 - made ansible playbook more generic, use package instead of pacman. also change state from "latest" to "present". - complete documentation on append-only remote repos, #5497 - internals: rather talk about target size than statistics, #5336 - new compression algorithm policy, #1633 #5505 - faq: add a hint on sleeping computer, #5301 - note requirements for full disk access on macOS Catalina, #5303 - fix/improve description of borg upgrade hardlink usage, #5518 - modernize 1.1 code: - drop code/workarounds only needed to support Python 3.4 - remove workaround for pre-release py37 argparse bug - removed some outdated comments/docstrings - requirements: remove some restrictions, lock on current versions Version 1.1.14 (2020-10-07) --------------------------- Fixes: - check --repair: fix potential data loss when interrupting it, #5325 - exit with 128 + signal number (as documented) when borg is killed by a signal, #5161 - fix hardlinked CACHEDIR.TAG processing, #4911 - create --read-special: .part files also should be regular files, #5217 - llfuse dependency: choose least broken 1.3.6/1.3.7. 1.3.6 is broken on python 3.9, 1.3.7 is broken on FreeBSD. Other changes: - upgrade bundled xxhash to 0.7.4 - self test: if it fails, also point to OS and hardware, #5334 - pyinstaller: compute basepath from spec file location - prettier error message when archive gets too big, #5307 - check/recreate are not "experimental" any more (but still potentially dangerous): - recreate: remove extra confirmation - rephrase some warnings, update docs, #5164 - shell completions: - misc. updates / fixes - support repositories in fish tab completion, #5256 - complete $BORG_RECREATE_I_KNOW_WHAT_I_AM_DOING - rewrite zsh completion: - completion for almost all optional and positional arguments - completion for Borg environment variables (parameters) - use "allow/deny list" instead of "white/black list" wording - declare "allow_cache_wipe" marker in setup.cfg to avoid pytest warning - vagrant / tests: - misc. fixes / updates - use python 3.5.10 for binary build - build directory-based binaries additionally to the single file binaries - add libffi-dev, required to build python - use cryptography<3.0, more recent versions break the jessie box - test on python 3.9 - do brew update with /dev/null redirect to avoid "too much log output" on travis-ci - docs: - add ssh-agent pull backup method docs, #5288 - how to approach borg speed issues, #5371 - mention double --force in prune docs - update Homebrew install instructions, #5185 - better description of how cache and rebuilds of it work - point to borg create --list item flags in recreate usage, #5165 - add security faq explaining AES-CTR crypto issues, #5254 - add a note to create from stdin regarding files cache, #5180 - fix borg.1 manpage generation regression, #5211 - clarify how exclude options work in recreate, #5193 - add section for retired contributors - hint about not misusing private email addresses of contributors for borg support Version 1.1.13 (2020-06-06) --------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. There is one exception though: If you upgrade from an unpatched borg < 1.0.9, please read that section above: "Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099)" - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - 1.1.11 removes WSL autodetection (Windows 10 Subsystem for Linux). If WSL still has a problem with sync_file_range, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around the WSL issue. Fixes: - rebuilt using a current Cython version, compatible with python 3.8, #5214 Version 1.1.12 (2020-06-06) --------------------------- Fixes: - fix preload-related memory leak, #5202. - mount / borgfs (FUSE filesystem): - fix FUSE low linear read speed on large files, #5067 - fix crash on old llfuse without birthtime attrs, #5064 - accidentally we required llfuse >= 1.3. Now also old llfuse works again. - set f_namemax in statfs result, #2684 - update precedence of env vars to set config and cache paths, #4894 - correctly calculate compression ratio, taking header size into account, too New features: - --bypass-lock option to bypass locking with read-only repositories Other changes: - upgrade bundled zstd to 1.4.5 - travis: adding comments and explanations to Travis config / install script, improve macOS builds. - tests: test_delete_force: avoid sporadic test setup issues, #5196 - misc. vagrant fixes - the binary for macOS is now built on macOS 10.12 - the binaries for Linux are now built on Debian 8 "Jessie", #3761 - docs: - PlaceholderError not printed as JSON, #4073 - "How important is Borg config?", #4941 - make Sphinx warnings break docs build, #4587 - some markup / warning fixes - add "updating borgbackup.org/releases" to release checklist, #4999 - add "rendering docs" to release checklist, #5000 - clarify borg init's encryption modes - add note about patterns and stored paths, #4160 - add upgrade of tools to pip installation how-to - document one cause of orphaned chunks in check command, #2295 - linked recommended restrictions to ssh public keys on borg servers in faq, #4946 Version 1.1.11 (2020-03-08) --------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. There is one exception though: If you upgrade from an unpatched borg < 1.0.9, please read that section above: "Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099)" - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - 1.1.11 removes WSL autodetection (Windows 10 Subsystem for Linux). If WSL still has a problem with sync_file_range, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around the WSL issue. Fixes: - fixed potential index corruption / data loss issue due to bug in hashindex_set, #4829. Please read and follow the more detailed notes close to the top of this document. - upgrade bundled xxhash to 0.7.3, #4891. 0.7.2 is the minimum requirement for correct operations on ARMv6 in non-fixup mode, where unaligned memory accesses cause bus errors. 0.7.3 adds some speedups and libxxhash 0.7.3 even has a pkg-config file now. - upgrade bundled lz4 to 1.9.2 - upgrade bundled zstd to 1.4.4 - fix crash when upgrading erroneous hints file, #4922 - extract: - fix KeyError for "partial" extraction, #4607 - fix "partial" extract for hardlinked contentless file types, #4725 - fix preloading for old (0.xx) remote servers, #4652 - fix confusing output of borg extract --list --strip-components, #4934 - delete: after double-force delete, warn about necessary repair, #4704 - create: give invalid repo error msg if repo config not found, #4411 - mount: fix FUSE mount missing st_birthtime, #4763 #4767 - check: do not stumble over invalid item key, #4845 - info: if the archive doesn't exist, print a pretty message, #4793 - SecurityManager.known(): check all files, #4614 - Repository.open: use stat() to check for repo dir, #4695 - Repository.check_can_create_repository: use stat() to check, #4695 - fix invalid archive error message - fix optional/non-optional location arg, #4541 - commit-time free space calc: ignore bad compact map entries, #4796 - ignore EACCES (errno 13) when hardlinking the old config, #4730 - --prefix / -P: fix processing, avoid argparse issue, #4769 New features: - enable placeholder usage in all extra archive arguments - new BORG_WORKAROUNDS mechanism, basesyncfile, #4710 - recreate: support --timestamp option, #4745 - support platforms without os.link (e.g. Android with Termux), #4901. if we don't have os.link, we just extract another copy instead of making a hardlink. - support linux platforms without sync_file_range (e.g. Android 7 with Termux), #4905 Other: - ignore --stats when given with --dry-run, but continue, #4373 - add some ProgressIndicator msgids to code / fix docs, #4935 - elaborate on "Calculating size" message - argparser: always use REPOSITORY in metavar, also use more consistent help phrasing. - check: improve error output for matching index size, see #4829 - docs: - changelog: add advisory about hashindex_set bug #4829 - better describe BORG_SECURITY_DIR, BORG_CACHE_DIR, #4919 - infos about cache security assumptions, #4900 - add FAQ describing difference between a local repo vs. repo on a server. - document how to test exclusion patterns without performing an actual backup - timestamps in the files cache are now usually ctime, #4583 - fix bad reference to borg compact (does not exist in 1.1), #4660 - create: borg 1.1 is not future any more - extract: document limitation "needs empty destination", #4598 - how to supply a passphrase, use crypto devices, #4549 - fix osxfuse github link in installation docs - add example of exclude-norecurse rule in help patterns - update macOS Brew link - add note about software for automating backups, #4581 - AUTHORS: mention copyright+license for bundled msgpack - fix various code blocks in the docs, #4708 - updated docs to cover use of temp directory on remote, #4545 - add restore docs, #4670 - add a pull backup / push restore how-to, #1552 - add FAQ how to retain original paths, #4532 - explain difference between --exclude and --pattern, #4118 - add FAQs for SSH connection issues, #3866 - improve password FAQ, #4591 - reiterate that 'file cache names are absolute' in FAQ - tests: - cope with ANY error when importing pytest into borg.testsuite, #4652 - fix broken test that relied on improper zlib assumptions - test_fuse: filter out selinux xattrs, #4574 - travis / vagrant: - misc python versions removed / changed (due to openssl 1.1 compatibility) or added (3.7 and 3.8, for better borg compatibility testing) - binary building is on python 3.5.9 now - vagrant: - add new boxes: ubuntu 18.04 and 20.04, debian 10 - update boxes: openindiana, darwin, netbsd - remove old boxes: centos 6 - darwin: updated osxfuse to 3.10.4 - use debian/ubuntu pip/virtualenv packages - rather use python 3.6.2 than 3.6.0, fixes coverage/sqlite3 issue - use requirements.d/development.lock.txt to avoid compat issues - travis: - darwin: backport some install code / order from master - remove deprecated keyword "sudo" from travis config - allow osx builds to fail, #4955 this is due to travis-ci frequently being so slow that the OS X builds just fail because they exceed 50 minutes and get killed by travis. Version 1.1.10 (2019-05-16) --------------------------- Fixes: - extract: hang on partial extraction with ssh: repo, when hardlink master is not matched/extracted and borg hangs on related slave hardlink, #4350 - lrucache: regularly remove old FDs, #4427 - avoid stale filehandle issues, #3265 - freebsd: make xattr platform code api compatible with linux, #3952 - use whitelist approach for borg serve, #4097 - borg command shall terminate with rc 2 for ImportErrors, #4424 - create: only run stat_simple_attrs() once, this increases backup with lots of unchanged files performance by ~ 5%. - prune: fix incorrect borg prune --stats output with --dry-run, #4373 - key export: emit user-friendly error if repo key is exported to a directory, #4348 New features: - bundle latest supported msgpack-python release (0.5.6), remove msgpack-python from setup.py install_requires - by default we use the bundled code now. optionally, we still support using an external msgpack (see hints in setup.py), but this requires solid requirements management within distributions and is not recommended. borgbackup will break if you upgrade msgpack to an unsupported version. - display msgpack version as part of sysinfo (e.g. in tracebacks) - timestamp for borg delete --info added, #4359 - enable placeholder usage in --comment and --glob-archives, #4559, #4495 Other: - serve: do not check python/libc for borg serve, #4483 - shell completions: borg diff second archive - release scripts: signing binaries with Qubes OS support - testing: - vagrant: upgrade openbsd box to 6.4 - travis-ci: lock test env to py 3.4 compatible versions, #4343 - get rid of confusing coverage warning, #2069 - rename test_mount_hardlinks to test_fuse_mount_hardlinks, so both can be excluded by "not test_fuse". - pure-py msgpack warning shall not make a lot of tests fail, #4558 - docs: - add "SSH Configuration" section to "borg serve", #3988, #636, #4485 - README: new URL for funding options - add a sample logging.conf in docs/misc, #4380 - elaborate on append-only mode docs, #3504 - installation: added Alpine Linux to distribution list, #4415 - usage.html: only modify window.location when redirecting, #4133 - add msgpack license to docs/3rd_party/msgpack - vagrant / binary builds: - use python 3.5.7 for builds - use osxfuse 3.8.3 Version 1.1.9 (2019-02-10) -------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. There is one exception though: If you upgrade from an unpatched borg < 1.0.9, please read that section above: "Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099)" - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. Fixes: - security fix: configure FUSE with "default_permissions", #3903 "default_permissions" is now enforced by borg by default to let the kernel check uid/gid/mode based permissions. "ignore_permissions" can be given to not enforce "default_permissions". - make "hostname" short, even on misconfigured systems, #4262 - fix free space calculation on macOS (and others?), #4289 - config: quit with error message when no key is provided, #4223 - recover_segment: handle too small segment files correctly, #4272 - correctly release memoryview, #4243 - avoid diaper pattern in configparser by opening files, #4263 - add "# cython: language_level=3" directive to .pyx files, #4214 - info: consider part files for "This archive" stats, #3522 - work around Microsoft WSL issue #645 (sync_file_range), #1961 New features: - add --rsh command line option to complement BORG_RSH env var, #1701 - init: --make-parent-dirs parent1/parent2/repo_dir, #4235 Other: - add archive name to check --repair output, #3447 - check for unsupported msgpack versions - shell completions: - new shell completions for borg 1.1.9 - more complete shell completions for borg mount -o - added shell completions for borg help - option arguments for zsh tab completion - docs: - add FAQ regarding free disk space check, #3905 - update BORG_PASSCOMMAND example and clarify variable expansion, #4249 - FAQ regarding change of compression settings, #4222 - add note about BSD flags to changelog, #4246 - improve logging in example automation script - add note about files changing during backup, #4081 - work around the backslash issue, #4280 - update release workflow using twine (docs, scripts), #4213 - add warnings on repository copies to avoid future problems, #4272 - tests: - fix the homebrew 1.9 issues on travis-ci, #4254 - fix duplicate test method name, #4311 Version 1.1.8 (2018-12-09) -------------------------- Fixes: - enforce storage quota if set by serve-command, #4093 - invalid locations: give err msg containing parsed location, #4179 - list repo: add placeholders for hostname and username, #4130 - on linux, symlinks can't have ACLs, so don't try to set any, #4044 New features: - create: added PATH::archive output on INFO log level - read a passphrase from a file descriptor specified in the BORG_PASSPHRASE_FD environment variable. Other: - docs: - option --format is required for some expensive-to-compute values for json borg list by default does not compute expensive values except when they are needed. whether they are needed is determined by the format, in standard mode as well as in --json mode. - tell that our binaries are x86/x64 amd/intel, bauerj has ARM - fixed wrong archive name pattern in CRUD benchmark help - fixed link to cachedir spec in docs, #4140 - tests: - stop using fakeroot on travis, avoids sporadic EISDIR errors, #2482 - xattr key names must start with "user." on linux - fix code so flake8 3.6 does not complain - explicitly convert environment variable to str, #4136 - fix DeprecationWarning: Flags not at the start of the expression, #4137 - support pytest4, #4172 - vagrant: - use python 3.5.6 for builds Version 1.1.7 (2018-08-11) -------------------------- Compatibility notes: - added support for Python 3.7 Fixes: - cache lock: use lock_wait everywhere to fix infinite wait, see #3968 - don't archive tagged dir when recursing an excluded dir, #3991 - py37 argparse: work around bad default in py 3.7.0a/b/rc, #3996 - py37 remove loggerDict.clear() from tearDown method, #3805 - some fixes for bugs which likely did not result in problems in practice: - fixed logic bug in platform module API version check - fixed xattr/acl function prototypes, added missing ones New features: - init: add warning to store both key and passphrase at safe place(s) - BORG_HOST_ID env var to work around all-zero MAC address issue, #3985 - borg debug dump-repo-objs --ghost (dump everything from segment files, including deleted or superseded objects or commit tags) - borg debug search-repo-objs (search in repo objects for hex bytes or strings) Other changes: - add Python 3.7 support - updated shell completions - call socket.gethostname only once - locking: better logging, add some asserts - borg debug dump-repo-objs: - filename layout improvements - use repository.scan() to get on-disk order - docs: - update installation instructions for macOS - added instructions to install fuse via homebrew - improve diff docs - added note that checkpoints inside files requires 1.1+ - add link to tempfile module - remove row/column-spanning from docs source, #4000 #3990 - tests: - fetch less data via os.urandom - add py37 env for tox - travis: add 3.7, remove 3.6-dev (we test with -dev in master) - vagrant / binary builds: - use osxfuse 3.8.2 - use own (uptodate) openindiana box Version 1.1.6 (2018-06-11) -------------------------- Compatibility notes: - 1.1.6 changes: - also allow msgpack-python 0.5.6. Fixes: - fix borg exception handling on ENOSPC error with xattrs, #3808 - prune: fix/improve overall progress display - borg config repo ... does not need cache/manifest/key, #3802 - debug dump-repo-objs should not depend on a manifest obj - pypi package: - include .coveragerc, needed by tox.ini - fix package long description, #3854 New features: - mount: add uid, gid, umask mount options - delete: - only commit once, #3823 - implement --dry-run, #3822 - check: - show progress while rebuilding missing manifest, #3787 - more --repair output - borg config --list , #3612 Other changes: - update msgpack requirement, #3753 - update bundled zstd to 1.3.4, #3745 - update bundled lz4 code to 1.8.2, #3870 - docs: - describe what BORG_LIBZSTD_PREFIX does - fix and deduplicate encryption quickstart docs, #3776 - vagrant: - FUSE for macOS: upgrade 3.7.1 to 3.8.0 - exclude macOS High Sierra upgrade on the darwin64 machine - remove borgbackup.egg-info dir in fs_init (after rsync) - use pyenv-based build/test on jessie32/62 - use local 32 and 64bit debian jessie boxes - use "vagrant" as username for new xenial box - travis OS X: use xcode 8.3 (not broken) Version 1.1.5 (2018-04-01) -------------------------- Compatibility notes: - 1.1.5 changes: - require msgpack-python >= 0.4.6 and < 0.5.0. 0.5.0+ dropped python 3.4 testing and also caused some other issues because the python package was renamed to msgpack and emitted some FutureWarning. Fixes: - create --list: fix that it was never showing M status, #3492 - create: fix timing for first checkpoint (read files cache early, init checkpoint timer after that), see #3394 - extract: set rc=1 when extracting damaged files with all-zero replacement chunks or with size inconsistencies, #3448 - diff: consider an empty file as different to a non-existing file, #3688 - files cache: improve exception handling, #3553 - ignore exceptions in scandir_inorder() caused by an implicit stat(), also remove unneeded sort, #3545 - fixed tab completion problem where a space is always added after path even when it shouldn't - build: do .h file content checks in binary mode, fixes build issue for non-ascii header files on pure-ascii locale platforms, #3544 #3639 - borgfs: fix patterns/paths processing, #3551 - config: add some validation, #3566 - repository config: add validation for max_segment_size, #3592 - set cache previous_location on load instead of save - remove platform.uname() call which caused library mismatch issues, #3732 - add exception handler around deprecated platform.linux_distribution() call - use same datetime object for {now} and {utcnow}, #3548 New features: - create: implement --stdin-name, #3533 - add chunker_params to borg archive info (--json) - BORG_SHOW_SYSINFO=no to hide system information from exceptions Other changes: - updated zsh completions for borg 1.1.4 - files cache related code cleanups - be more helpful when parsing invalid --pattern values, #3575 - be more clear in secure-erase warning message, #3591 - improve getpass user experience, #3689 - docs build: unicode problem fixed when using a py27-based sphinx - docs: - security: explicitly note what happens OUTSIDE the attack model - security: add note about combining compression and encryption - security: describe chunk size / proximity issue, #3687 - quickstart: add note about permissions, borg@localhost, #3452 - quickstart: add introduction to repositories & archives, #3620 - recreate --recompress: add missing metavar, clarify description, #3617 - improve logging docs, #3549 - add an example for --pattern usage, #3661 - clarify path semantics when matching, #3598 - link to offline documentation from README, #3502 - add docs on how to verify a signed release with GPG, #3634 - chunk seed is generated per repository (not: archive) - better formatting of CPU usage documentation, #3554 - extend append-only repo rollback docs, #3579 - tests: - fix erroneously skipped zstd compressor tests, #3606 - skip a test if argparse is broken, #3705 - vagrant: - xenial64 box now uses username 'vagrant', #3707 - move cleanup steps to fs_init, #3706 - the boxcutter wheezy boxes are 404, use local ones - update to Python 3.5.5 (for binary builds) Version 1.1.4 (2017-12-31) -------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. There is one exception though: If you upgrade from an unpatched borg < 1.0.9, please read that section above: "Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099)" - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - borg 1.1.4 changes: - zstd compression is new in borg 1.1.4, older borg can't handle it. - new minimum requirements for the compression libraries - if the required versions (header and lib) can't be found at build time, bundled code will be used: - added requirement: libzstd >= 1.3.0 (bundled: 1.3.2) - updated requirement: liblz4 >= 1.7.0 / r129 (bundled: 1.8.0) Fixes: - check: data corruption fix: fix for borg check --repair malfunction, #3444. See the more detailed notes close to the top of this document. - delete: also delete security dir when deleting a repo, #3427 - prune: fix building the "borg prune" man page, #3398 - init: use given --storage-quota for local repo, #3470 - init: properly quote repo path in output - fix startup delay with dns-only own fqdn resolving, #3471 New features: - added zstd compression. try it! - added placeholder {reverse-fqdn} for fqdn in reverse notation - added BORG_BASE_DIR environment variable, #3338 Other changes: - list help topics when invalid topic is requested - fix lz4 deprecation warning, requires lz4 >= 1.7.0 (r129) - add parens for C preprocessor macro argument usages (did not cause malfunction) - exclude broken pytest 3.3.0 release - updated fish/bash completions - init: more clear exception messages for borg create, #3465 - docs: - add auto-generated docs for borg config - don't generate HTML docs page for borgfs, #3404 - docs update for lz4 b2 zstd changes - add zstd to compression help, readme, docs - update requirements and install docs about bundled lz4 and zstd - refactored build of the compress and crypto.low_level extensions, #3415: - move some lib/build related code to setup_{zstd,lz4,b2}.py - bundle lz4 1.8.0 (requirement: >= 1.7.0 / r129) - bundle zstd 1.3.2 (requirement: >= 1.3.0) - blake2 was already bundled - rename BORG_LZ4_PREFIX env var to BORG_LIBLZ4_PREFIX for better consistency: we also have BORG_LIBB2_PREFIX and BORG_LIBZSTD_PREFIX now. - add prefer_system_lib* = True settings to setup.py - by default the build will prefer a shared library over the bundled code, if library and headers can be found and meet the minimum requirements. Version 1.1.3 (2017-11-27) -------------------------- Fixes: - Security Fix for CVE-2017-15914: Incorrect implementation of access controls allows remote users to override repository restrictions in Borg servers. A user able to access a remote Borg SSH server is able to circumvent access controls post-authentication. Affected releases: 1.1.0, 1.1.1, 1.1.2. Releases 1.0.x are NOT affected. - crc32: deal with unaligned buffer, add tests - this broke borg on older ARM CPUs that can not deal with unaligned 32bit memory accesses and raise a bus error in such cases. the fix might also improve performance on some CPUs as all 32bit memory accesses by the crc32 code are properly aligned now. #3317 - mount: fixed support of --consider-part-files and do not show .borg_part_N files by default in the mounted FUSE filesystem. #3347 - fixed cache/repo timestamp inconsistency message, highlight that information is obtained from security dir (deleting the cache will not bypass this error in case the user knows this is a legitimate repo). - borgfs: don't show sub-command in borgfs help, #3287 - create: show an error when --dry-run and --stats are used together, #3298 New features: - mount: added exclusion group options and paths, #2138 Reused some code to support similar options/paths as borg extract offers - making good use of these to only mount a smaller subset of dirs/files can speed up mounting a lot and also will consume way less memory. borg mount [options] repo_or_archive mountpoint path [paths...] paths: you can just give some "root paths" (like for borg extract) to only partially populate the FUSE filesystem. new options: --exclude[-from], --pattern[s-from], --strip-components - create/extract: support st_birthtime on platforms supporting it, #3272 - add "borg config" command for querying/setting/deleting config values, #3304 Other changes: - clean up and simplify packaging (only package committed files, do not install .c/.h/.pyx files) - docs: - point out tuning options for borg create, #3239 - add instructions for using ntfsclone, zerofree, #81 - move image backup-related FAQ entries to a new page - clarify key aliases for borg list --format, #3111 - mention break-lock in checkpointing FAQ entry, #3328 - document sshfs rename workaround, #3315 - add FAQ about removing files from existing archives - add FAQ about different prune policies - usage and man page for borgfs, #3216 - clarify create --stats duration vs. wall time, #3301 - clarify encrypted key format for borg key export, #3296 - update release checklist about security fixes - document good and problematic option placements, fix examples, #3356 - add note about using --nobsdflags to avoid speed penalty related to bsdflags, #3239 - move most of support section to www.borgbackup.org Version 1.1.2 (2017-11-05) -------------------------- Fixes: - fix KeyError crash when talking to borg server < 1.0.7, #3244 - extract: set bsdflags last (include immutable flag), #3263 - create: don't do stat() call on excluded-norecurse directory, fix exception handling for stat() call, #3209 - create --stats: do not count data volume twice when checkpointing, #3224 - recreate: move chunks_healthy when excluding hardlink master, #3228 - recreate: get rid of chunks_healthy when rechunking (does not match), #3218 - check: get rid of already existing not matching chunks_healthy metadata, #3218 - list: fix stdout broken pipe handling, #3245 - list/diff: remove tag-file options (not used), #3226 New features: - bash, zsh and fish shell auto-completions, see scripts/shell_completions/ - added BORG_CONFIG_DIR env var, #3083 Other changes: - docs: - clarify using a blank passphrase in keyfile mode - mention "!" (exclude-norecurse) type in "patterns" help - document to first heal before running borg recreate to re-chunk stuff, because that will have to get rid of chunks_healthy metadata. - more than 23 is not supported for CHUNK_MAX_EXP, #3115 - borg does not respect nodump flag by default any more - clarify same-filesystem requirement for borg upgrade, #2083 - update / rephrase cygwin / WSL status, #3174 - improve docs about --stats, #3260 - vagrant: openindiana new clang package Already contained in 1.1.1 (last minute fix): - arg parsing: fix fallback function, refactor, #3205. This is a fixup for #3155, which was broken on at least python <= 3.4.2. Version 1.1.1 (2017-10-22) -------------------------- Compatibility notes: - The deprecated --no-files-cache is not a global/common option any more, but only available for borg create (it is not needed for anything else). Use --files-cache=disabled instead of --no-files-cache. - The nodump flag ("do not backup this file") is not honoured any more by default because this functionality (esp. if it happened by error or unexpected) was rather confusing and unexplainable at first to users. If you want that "do not backup NODUMP-flagged files" behaviour, use: borg create --exclude-nodump ... - If you are on Linux and do not need bsdflags archived, consider using ``--nobsdflags`` with ``borg create`` to avoid additional syscalls and speed up backup creation. Fixes: - borg recreate: correctly compute part file sizes. fixes cosmetic, but annoying issue as borg check complains about size inconsistencies of part files in affected archives. you can solve that by running borg recreate on these archives, see also #3157. - bsdflags support: do not open BLK/CHR/LNK files, avoid crashes and slowness, #3130 - recreate: don't crash on attic archives w/o time_end, #3109 - don't crash on repository filesystems w/o hardlink support, #3107 - don't crash in first part of truncate_and_unlink, #3117 - fix server-side IndexError crash with clients < 1.0.7, #3192 - don't show traceback if only a global option is given, show help, #3142 - cache: use SaveFile for more safety, #3158 - init: fix wrong encryption choices in command line parser, fix missing "authenticated-blake2", #3103 - move --no-files-cache from common to borg create options, #3146 - fix detection of non-local path (failed on ..filename), #3108 - logging with fileConfig: set json attr on "borg" logger, #3114 - fix crash with relative BORG_KEY_FILE, #3197 - show excluded dir with "x" for tagged dirs / caches, #3189 New features: - create: --nobsdflags and --exclude-nodump options, #3160 - extract: --nobsdflags option, #3160 Other changes: - remove annoying hardlinked symlinks warning, #3175 - vagrant: use self-made FreeBSD 10.3 box, #3022 - travis: don't brew update, hopefully fixes #2532 - docs: - readme: -e option is required in borg 1.1 - add example showing --show-version --show-rc - use --format rather than --list-format (deprecated) in example - update docs about hardlinked symlinks limitation Version 1.1.0 (2017-10-07) -------------------------- Compatibility notes: - borg command line: do not put options in between positional arguments This sometimes works (e.g. it worked in borg 1.0.x), but can easily stop working if we make positional arguments optional (like it happened for borg create's "paths" argument in 1.1). There are also places in borg 1.0 where we do that, so it doesn't work there in general either. #3356 Good: borg create -v --stats repo::archive path Good: borg create repo::archive path -v --stats Bad: borg create repo::archive -v --stats path Fixes: - fix LD_LIBRARY_PATH restoration for subprocesses, #3077 - "auto" compression: make sure expensive compression is actually better, otherwise store lz4 compressed data we already computed. Other changes: - docs: - FAQ: we do not implement futile attempts of ETA / progress displays - manpage: fix typos, update homepage - implement simple "issue" role for manpage generation, #3075 Version 1.1.0rc4 (2017-10-01) ----------------------------- Compatibility notes: - A borg server >= 1.1.0rc4 does not support borg clients 1.1.0b3-b5. #3033 - The files cache is now controlled differently and has a new default mode: - the files cache now uses ctime by default for improved file change detection safety. You can still use mtime for more speed and less safety. - --ignore-inode is deprecated (use --files-cache=... without "inode") - --no-files-cache is deprecated (use --files-cache=disabled) New features: - --files-cache - implement files cache mode control, #911 You can now control the files cache mode using this option: --files-cache={ctime,mtime,size,inode,rechunk,disabled} (only some combinations are supported). See the docs for details. Fixes: - remote progress/logging: deal with partial lines, #2637 - remote progress: flush json mode output - fix subprocess environments, #3050 (and more) Other changes: - remove client_supports_log_v3 flag, #3033 - exclude broken Cython 0.27(.0) in requirements, #3066 - vagrant: - upgrade to FUSE for macOS 3.7.1 - use Python 3.5.4 to build the binaries - docs: - security: change-passphrase only changes the passphrase, #2990 - fixed/improved borg create --compression examples, #3034 - add note about metadata dedup and --no[ac]time, #2518 - twitter account @borgbackup now, better visible, #2948 - simplified rate limiting wrapper in FAQ Version 1.1.0rc3 (2017-09-10) ----------------------------- New features: - delete: support naming multiple archives, #2958 Fixes: - repo cleanup/write: invalidate cached FDs, #2982 - fix datetime.isoformat() microseconds issues, #2994 - recover_segment: use mmap(), lower memory needs, #2987 Other changes: - with-lock: close segment file before invoking subprocess - keymanager: don't depend on optional readline module, #2976 - docs: - fix macOS keychain integration command - show/link new screencasts in README, #2936 - document utf-8 locale requirement for json mode, #2273 - vagrant: clean up shell profile init, user name, #2977 - test_detect_attic_repo: don't test mount, #2975 - add debug logging for repository cleanup Version 1.1.0rc2 (2017-08-28) ----------------------------- Compatibility notes: - list: corrected mix-up of "isomtime" and "mtime" formats. Previously, "isomtime" was the default but produced a verbose human format, while "mtime" produced a ISO-8601-like format. The behaviours have been swapped (so "mtime" is human, "isomtime" is ISO-like), and the default is now "mtime". "isomtime" is now a real ISO-8601 format ("T" between date and time, not a space). New features: - None. Fixes: - list: fix weird mixup of mtime/isomtime - create --timestamp: set start time, #2957 - ignore corrupt files cache, #2939 - migrate locks to child PID when daemonize is used - fix exitcode of borg serve, #2910 - only compare contents when chunker params match, #2899 - umount: try fusermount, then try umount, #2863 Other changes: - JSON: use a more standard ISO 8601 datetime format, #2376 - cache: write_archive_index: truncate_and_unlink on error, #2628 - detect non-upgraded Attic repositories, #1933 - delete various nogil and threading related lines - coala / pylint related improvements - docs: - renew asciinema/screencasts, #669 - create: document exclusion through nodump, #2949 - minor formatting fixes - tar: tarpipe example - improve "with-lock" and "info" docs, #2869 - detail how to use macOS/GNOME/KDE keyrings for repo passwords, #392 - travis: only short-circuit docs-only changes for pull requests - vagrant: - netbsd: bash is already installed - fix netbsd version in PKG_PATH - add exe location to PATH when we build an exe Version 1.1.0rc1 (2017-07-24) ----------------------------- Compatibility notes: - delete: removed short option for --cache-only New features: - support borg list repo --format {comment} {bcomment} {end}, #2081 - key import: allow reading from stdin, #2760 Fixes: - with-lock: avoid creating segment files that might be overwritten later, #1867 - prune: fix checkpoints processing with --glob-archives - FUSE: versions view: keep original file extension at end, #2769 - fix --last, --first: do not accept values <= 0, fix reversed archive ordering with --last - include testsuite data (attic.tar.gz) when installing the package - use limited unpacker for outer key, for manifest (both security precautions), #2174 #2175 - fix bashism in shell scripts, #2820, #2816 - cleanup endianness detection, create _endian.h, fixes build on alpine linux, #2809 - fix crash with --no-cache-sync (give known chunk size to chunk_incref), #2853 Other changes: - FUSE: versions view: linear numbering by archive time - split up interval parsing from filtering for --keep-within, #2610 - add a basic .editorconfig, #2734 - use archive creation time as mtime for FUSE mount, #2834 - upgrade FUSE for macOS (osxfuse) from 3.5.8 to 3.6.3, #2706 - hashindex: speed up by replacing modulo with "if" to check for wraparound - coala checker / pylint: fixed requirements and .coafile, more ignores - borg upgrade: name backup directories as 'before-upgrade', #2811 - add .mailmap - some minor changes suggested by lgtm.com - docs: - better explanation of the --ignore-inode option relevance, #2800 - fix openSUSE command and add openSUSE section - simplify ssh authorized_keys file using "restrict", add legacy note, #2121 - mount: show usage of archive filters - mount: add repository example, #2462 - info: update and add examples, #2765 - prune: include example - improved style / formatting - improved/fixed segments_per_dir docs - recreate: fix wrong "remove unwanted files" example - reference list of status chars in borg recreate --filter description - update source-install docs about doc build dependencies, #2795 - cleanup installation docs - file system requirements, update segs per dir - fix checkpoints/parts reference in FAQ, #2859 - code: - hashindex: don't pass side effect into macro - crypto low_level: don't mutate local bytes() - use dash_open function to open file or "-" for stdin/stdout - archiver: argparse cleanup / refactoring - shellpattern: add match_end arg - tests: added some additional unit tests, some fixes, #2700 #2710 - vagrant: fix setup of cygwin, add Debian 9 "stretch" - travis: don't perform full travis build on docs-only changes, #2531 Version 1.1.0b6 (2017-06-18) ---------------------------- Compatibility notes: - Running "borg init" via a "borg serve --append-only" server will *not* create an append-only repository anymore. Use "borg init --append-only" to initialize an append-only repository. - Repositories in the "repokey" and "repokey-blake2" modes with an empty passphrase are now treated as unencrypted repositories for security checks (e.g. BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK). Previously there would be no prompts nor messages if an unknown repository in one of these modes with an empty passphrase was encountered. This would allow an attacker to swap a repository, if one assumed that the lack of password prompts was due to a set BORG_PASSPHRASE. Since the "trick" does not work if BORG_PASSPHRASE is set, this does generally not affect scripts. - Repositories in the "authenticated" mode are now treated as the unencrypted repositories they are. - The client-side temporary repository cache now holds unencrypted data for better speed. - borg init: removed the short form of --append-only (-a). - borg upgrade: removed the short form of --inplace (-i). New features: - reimplemented the RepositoryCache, size-limited caching of decrypted repo contents, integrity checked via xxh64. #2515 - reduced space usage of chunks.archive.d. Existing caches are migrated during a cache sync. #235 #2638 - integrity checking using xxh64 for important files used by borg, #1101: - repository: index and hints files - cache: chunks and files caches, chunks.archive.d - improve cache sync speed, #1729 - create: new --no-cache-sync option - add repository mandatory feature flags infrastructure, #1806 - Verify most operations against SecurityManager. Location, manifest timestamp and key types are now checked for almost all non-debug commands. #2487 - implement storage quotas, #2517 - serve: add --restrict-to-repository, #2589 - BORG_PASSCOMMAND: use external tool providing the key passphrase, #2573 - borg export-tar, #2519 - list: --json-lines instead of --json for archive contents, #2439 - add --debug-profile option (and also "borg debug convert-profile"), #2473 - implement --glob-archives/-a, #2448 - normalize authenticated key modes for better naming consistency: - rename "authenticated" to "authenticated-blake2" (uses blake2b) - implement "authenticated" mode (uses hmac-sha256) Fixes: - hashindex: read/write indices >2 GiB on 32bit systems, better error reporting, #2496 - repository URLs: implement IPv6 address support and also more informative error message when parsing fails. - mount: check whether llfuse is installed before asking for passphrase, #2540 - mount: do pre-mount checks before opening repository, #2541 - FUSE: - fix crash if empty (None) xattr is read, #2534 - fix read(2) caching data in metadata cache - fix negative uid/gid crash (fix crash when mounting archives of external drives made on cygwin), #2674 - redo ItemCache, on top of object cache - use decrypted cache - remove unnecessary normpaths - serve: ignore --append-only when initializing a repository (borg init), #2501 - serve: fix incorrect type of exception_short for Errors, #2513 - fix --exclude and --exclude-from recursing into directories, #2469 - init: don't allow creating nested repositories, #2563 - --json: fix encryption[mode] not being the cmdline name - remote: propagate Error.traceback correctly - fix remote logging and progress, #2241 - implement --debug-topic for remote servers - remote: restore "Remote:" prefix (as used in 1.0.x) - rpc negotiate: enable v3 log protocol only for supported clients - fix --progress and logging in general for remote - fix parse_version, add tests, #2556 - repository: truncate segments (and also some other files) before unlinking, #2557 - recreate: keep timestamps as in original archive, #2384 - recreate: if single archive is not processed, exit 2 - patterns: don't recurse with ! / --exclude for pf:, #2509 - cache sync: fix n^2 behaviour in lookup_name - extract: don't write to disk with --stdout (affected non-regular-file items), #2645 - hashindex: implement KeyError, more tests Other changes: - remote: show path in PathNotAllowed - consider repokey w/o passphrase == unencrypted, #2169 - consider authenticated mode == unencrypted, #2503 - restrict key file names, #2560 - document follow_symlinks requirements, check libc, use stat and chown with follow_symlinks=False, #2507 - support common options on the main command, #2508 - support common options on mid-level commands (e.g. borg *key* export) - make --progress a common option - increase DEFAULT_SEGMENTS_PER_DIR to 1000 - chunker: fix invalid use of types (function only used by tests) - chunker: don't do uint32_t >> 32 - FUSE: - add instrumentation (--debug and SIGUSR1/SIGINFO) - reduced memory usage for repository mounts by lazily instantiating archives - improved archive load times - info: use CacheSynchronizer & HashIndex.stats_against (better performance) - docs: - init: document --encryption as required - security: OpenSSL usage - security: used implementations; note python libraries - security: security track record of OpenSSL and msgpack - patterns: document denial of service (regex, wildcards) - init: note possible denial of service with "none" mode - init: document SHA extension is supported in OpenSSL and thus SHA is faster on AMD Ryzen than blake2b. - book: use A4 format, new builder option format. - book: create appendices - data structures: explain repository compaction - data structures: add chunk layout diagram - data structures: integrity checking - data structures: demingle cache and repo index - Attic FAQ: separate section for attic stuff - FAQ: I get an IntegrityError or similar - what now? - FAQ: Can I use Borg on SMR hard drives?, #2252 - FAQ: specify "using inline shell scripts" - add systemd warning regarding placeholders, #2543 - xattr: document API - add docs/misc/borg-data-flow data flow chart - debugging facilities - README: how to help the project, #2550 - README: add bountysource badge, #2558 - fresh new theme + tweaking - logo: vectorized (PDF and SVG) versions - frontends: use headlines - you can link to them - mark --pattern, --patterns-from as experimental - highlight experimental features in online docs - remove regex based pattern examples, #2458 - nanorst for "borg help TOPIC" and --help - split deployment - deployment: hosting repositories - deployment: automated backups to a local hard drive - development: vagrant, windows10 requirements - development: update docs remarks - split usage docs, #2627 - usage: avoid bash highlight, [options] instead of - usage: add benchmark page - helpers: truncate_and_unlink doc - don't suggest to leak BORG_PASSPHRASE - internals: columnize rather long ToC [webkit fixup] internals: manifest & feature flags - internals: more HashIndex details - internals: fix ASCII art equations - internals: edited obj graph related sections a bit - internals: layers image + description - fix way too small figures in pdf - index: disable syntax highlight (bash) - improve options formatting, fix accidental block quotes - testing / checking: - add support for using coala, #1366 - testsuite: add ArchiverCorruptionTestCase - do not test logger name, #2504 - call setup_logging after destroying logging config - testsuite.archiver: normalise pytest.raises vs. assert_raises - add test for preserved intermediate folder permissions, #2477 - key: add round-trip test - remove attic dependency of the tests, #2505 - enable remote tests on cygwin - tests: suppress tar's future timestamp warning - cache sync: add more refcount tests - repository: add tests, including corruption tests - vagrant: - control VM cpus and pytest workers via env vars VMCPUS and XDISTN - update cleaning workdir - fix openbsd shell - add OpenIndiana - packaging: - binaries: don't bundle libssl - setup.py clean to remove compiled files - fail in borg package if version metadata is very broken (setuptools_scm) - repo / code structure: - create borg.algorithms and borg.crypto packages - algorithms: rename crc32 to checksums - move patterns to module, #2469 - gitignore: complete paths for src/ excludes - cache: extract CacheConfig class - implement IntegrityCheckedFile + Detached variant, #2502 #1688 - introduce popen_with_error_handling to handle common user errors Version 1.1.0b5 (2017-04-30) ---------------------------- Compatibility notes: - BORG_HOSTNAME_IS_UNIQUE is now on by default. - removed --compression-from feature - recreate: add --recompress flag, unify --always-recompress and --recompress Fixes: - catch exception for os.link when hardlinks are not supported, #2405 - borg rename / recreate: expand placeholders, #2386 - generic support for hardlinks (files, devices, FIFOs), #2324 - extract: also create parent dir for device files, if needed, #2358 - extract: if a hardlink master is not in the to-be-extracted subset, the "x" status was not displayed for it, #2351 - embrace y2038 issue to support 32bit platforms: clamp timestamps to int32, #2347 - verify_data: fix IntegrityError handling for defect chunks, #2442 - allow excluding parent and including child, #2314 Other changes: - refactor compression decision stuff - change global compression default to lz4 as well, to be consistent with --compression defaults. - placeholders: deny access to internals and other unspecified stuff - clearer error message for unrecognized placeholder - more clear exception if borg check does not help, #2427 - vagrant: upgrade FUSE for macOS to 3.5.8, #2346 - linux binary builds: get rid of glibc 2.13 dependency, #2430 - docs: - placeholders: document escaping - serve: env vars in original commands are ignored - tell what kind of hardlinks we support - more docs about compression - LICENSE: use canonical formulation ("copyright holders and contributors" instead of "author") - document borg init behaviour via append-only borg serve, #2440 - be clear about what buzhash is used for, #2390 - add hint about chunker params, #2421 - clarify borg upgrade docs, #2436 - FAQ to explain warning when running borg check --repair, #2341 - repository file system requirements, #2080 - pre-install considerations - misc. formatting / crossref fixes - tests: - enhance travis setuptools_scm situation - add extra test for the hashindex - fix invalid param issue in benchmarks These belong to 1.1.0b4 release, but did not make it into changelog by then: - vagrant: increase memory for parallel testing - lz4 compress: lower max. buffer size, exception handling - add docstring to do_benchmark_crud - patterns help: mention path full-match in intro Version 1.1.0b4 (2017-03-27) ---------------------------- Compatibility notes: - init: the --encryption argument is mandatory now (there are several choices) - moved "borg migrate-to-repokey" to "borg key migrate-to-repokey". - "borg change-passphrase" is deprecated, use "borg key change-passphrase" instead. - the --exclude-if-present option now supports tagging a folder with any filesystem object type (file, folder, etc), instead of expecting only files as tags, #1999 - the --keep-tag-files option has been deprecated in favor of the new --keep-exclude-tags, to account for the change mentioned above. - use lz4 compression by default, #2179 New features: - JSON API to make developing frontends and automation easier (see :ref:`json_output`) - add JSON output to commands: `borg create/list/info --json ...`. - add --log-json option for structured logging output. - add JSON progress information, JSON support for confirmations (yes()). - add two new options --pattern and --patterns-from as discussed in #1406 - new path full match pattern style (pf:) for very fast matching, #2334 - add 'debug dump-manifest' and 'debug dump-archive' commands - add 'borg benchmark crud' command, #1788 - new 'borg delete --force --force' to delete severely corrupted archives, #1975 - info: show utilization of maximum archive size, #1452 - list: add dsize and dcsize keys, #2164 - paperkey.html: Add interactive html template for printing key backups. - key export: add qr html export mode - securely erase config file (which might have old encryption key), #2257 - archived file items: add size to metadata, 'borg extract' and 'borg check' do check the file size for consistency, FUSE uses precomputed size from Item. Fixes: - fix remote speed regression introduced in 1.1.0b3, #2185 - fix regression handling timestamps beyond 2262 (revert bigint removal), introduced in 1.1.0b3, #2321 - clamp (nano)second values to unproblematic range, #2304 - hashindex: rebuild hashtable if we have too little empty buckets (performance fix), #2246 - Location regex: fix bad parsing of wrong syntax - ignore posix_fadvise errors in repository.py, #2095 - borg rpc: use limited msgpack.Unpacker (security precaution), #2139 - Manifest: Make sure manifest timestamp is strictly monotonically increasing. - create: handle BackupOSError on a per-path level in one spot - create: clarify -x option / meaning of "same filesystem" - create: don't create hard link refs to failed files - archive check: detect and fix missing all-zero replacement chunks, #2180 - files cache: update inode number when --ignore-inode is used, #2226 - fix decompression exceptions crashing ``check --verify-data`` and others instead of reporting integrity error, #2224 #2221 - extract: warning for unextracted big extended attributes, #2258, #2161 - mount: umount on SIGINT/^C when in foreground - mount: handle invalid hard link refs - mount: fix huge RAM consumption when mounting a repository (saves number of archives * 8 MiB), #2308 - hashindex: detect mingw byte order #2073 - hashindex: fix wrong skip_hint on hashindex_set when encountering tombstones, the regression was introduced in #1748 - fix ChunkIndex.__contains__ assertion for big-endian archs - fix borg key/debug/benchmark crashing without subcommand, #2240 - Location: accept //servername/share/path - correct/refactor calculation of unique/non-unique chunks - extract: fix missing call to ProgressIndicator.finish - prune: fix error msg, it is --keep-within, not --within - fix "auto" compression mode bug (not compressing), #2331 - fix symlink item fs size computation, #2344 Other changes: - remote repository: improved async exception processing, #2255 #2225 - with --compression auto,C, only use C if lz4 achieves at least 3% compression - PatternMatcher: only normalize path once, #2338 - hashindex: separate endian-dependent defs from endian detection - migrate-to-repokey: ask using canonical_path() as we do everywhere else. - SyncFile: fix use of fd object after close - make LoggedIO.close_segment reentrant - creating a new segment: use "xb" mode, #2099 - redo key_creator, key_factory, centralise key knowledge, #2272 - add return code functions, #2199 - list: only load cache if needed - list: files->items, clarifications - list: add "name" key for consistency with info cmd - ArchiveFormatter: add "start" key for compatibility with "info" - RemoteRepository: account rx/tx bytes - setup.py build_usage/build_man/build_api fixes - Manifest.in: simplify, exclude .so, .dll and .orig, #2066 - FUSE: get rid of chunk accounting, st_blocks = ceil(size / blocksize). - tests: - help python development by testing 3.6-dev - test for borg delete --force - vagrant: - freebsd: some fixes, #2067 - darwin64: use osxfuse 3.5.4 for tests / to build binaries - darwin64: improve VM settings - use python 3.5.3 to build binaries, #2078 - upgrade pyinstaller from 3.1.1+ to 3.2.1 - pyinstaller: use fixed AND freshly compiled bootloader, #2002 - pyinstaller: automatically builds bootloader if missing - docs: - create really nice man pages - faq: mention --remote-ratelimit in bandwidth limit question - fix caskroom link, #2299 - docs/security: reiterate that RPC in Borg does no networking - docs/security: counter tracking, #2266 - docs/development: update merge remarks - address SSH batch mode in docs, #2202 #2270 - add warning about running build_usage on Python >3.4, #2123 - one link per distro in the installation page - improve --exclude-if-present and --keep-exclude-tags, #2268 - improve automated backup script in doc, #2214 - improve remote-path description - update docs for create -C default change (lz4) - document relative path usage, #1868 - document snapshot usage, #2178 - corrected some stuff in internals+security - internals: move toctree to after the introduction text - clarify metadata kind, manifest ops - key enc: correct / clarify some stuff, link to internals/security - datas: enc: 1.1.x mas different MACs - datas: enc: correct factual error -- no nonce involved there. - make internals.rst an index page and edit it a bit - add "Cryptography in Borg" and "Remote RPC protocol security" sections - document BORG_HOSTNAME_IS_UNIQUE, #2087 - FAQ by categories as proposed by @anarcat in #1802 - FAQ: update Which file types, attributes, etc. are *not* preserved? - development: new branching model for git repository - development: define "ours" merge strategy for auto-generated files - create: move --exclude note to main doc - create: move item flags to main doc - fix examples using borg init without -e/--encryption - list: don't print key listings in fat (html + man) - remove Python API docs (were very incomplete, build problems on RTFD) - added FAQ section about backing up root partition Version 1.1.0b3 (2017-01-15) ---------------------------- Compatibility notes: - borg init: removed the default of "--encryption/-e", #1979 This was done so users do a informed decision about -e mode. Bug fixes: - borg recreate: don't rechunkify unless explicitly told so - borg info: fixed bug when called without arguments, #1914 - borg init: fix free space check crashing if disk is full, #1821 - borg debug delete/get obj: fix wrong reference to exception - fix processing of remote ~/ and ~user/ paths (regressed since 1.1.0b1), #1759 - posix platform module: only build / import on non-win32 platforms, #2041 New features: - new CRC32 implementations that are much faster than the zlib one used previously, #1970 - add blake2b key modes (use blake2b as MAC). This links against system libb2, if possible, otherwise uses bundled code - automatically remove stale locks - set BORG_HOSTNAME_IS_UNIQUE env var to enable stale lock killing. If set, stale locks in both cache and repository are deleted. #562 #1253 - borg info : print general repo information, #1680 - borg check --first / --last / --sort / --prefix, #1663 - borg mount --first / --last / --sort / --prefix, #1542 - implement "health" item formatter key, #1749 - BORG_SECURITY_DIR to remember security related infos outside the cache. Key type, location and manifest timestamp checks now survive cache deletion. This also means that you can now delete your cache and avoid previous warnings, since Borg can still tell it's safe. - implement BORG_NEW_PASSPHRASE, #1768 Other changes: - borg recreate: - remove special-cased --dry-run - update --help - remove bloat: interruption blah, autocommit blah, resuming blah - re-use existing checkpoint functionality - archiver tests: add check_cache tool - lints refcounts - fixed cache sync performance regression from 1.1.0b1 onwards, #1940 - syncing the cache without chunks.archive.d (see :ref:`disable_archive_chunks`) now avoids any merges and is thus faster, #1940 - borg check --verify-data: faster due to linear on-disk-order scan - borg debug-xxx commands removed, we use "debug xxx" subcommands now, #1627 - improve metadata handling speed - shortcut hashindex_set by having hashindex_lookup hint about address - improve / add progress displays, #1721 - check for index vs. segment files object count mismatch - make RPC protocol more extensible: use named parameters. - RemoteRepository: misc. code cleanups / refactors - clarify cache/repository README file - docs: - quickstart: add a comment about other (remote) filesystems - quickstart: only give one possible ssh url syntax, all others are documented in usage chapter. - mention file:// - document repo URLs / archive location - clarify borg diff help, #980 - deployment: synthesize alternative --restrict-to-path example - improve cache / index docs, esp. files cache docs, #1825 - document using "git merge 1.0-maint -s recursive -X rename-threshold=20%" for avoiding troubles when merging the 1.0-maint branch into master. - tests: - FUSE tests: catch ENOTSUP on freebsd - FUSE tests: test troublesome xattrs last - fix byte range error in test, #1740 - use monkeypatch to set env vars, but only on pytest based tests. - point XDG_*_HOME to temp dirs for tests, #1714 - remove all BORG_* env vars from the outer environment Version 1.1.0b2 (2016-10-01) ---------------------------- Bug fixes: - fix incorrect preservation of delete tags, leading to "object count mismatch" on borg check, #1598. This only occurred with 1.1.0b1 (not with 1.0.x) and is normally fixed by running another borg create/delete/prune. - fix broken --progress for double-cell paths (e.g. CJK), #1624 - borg recreate: also catch SIGHUP - FUSE: - fix hardlinks in versions view, #1599 - add parameter check to ItemCache.get to make potential failures more clear New features: - Archiver, RemoteRepository: add --remote-ratelimit (send data) - borg help compression, #1582 - borg check: delete chunks with integrity errors, #1575, so they can be "repaired" immediately and maybe healed later. - archives filters concept (refactoring/unifying older code) - covers --first/--last/--prefix/--sort-by options - currently used for borg list/info/delete Other changes: - borg check --verify-data slightly tuned (use get_many()) - change {utcnow} and {now} to ISO-8601 format ("T" date/time separator) - repo check: log transaction IDs, improve object count mismatch diagnostic - Vagrantfile: use TW's fresh-bootloader pyinstaller branch - fix module names in api.rst - hashindex: bump api_version Version 1.1.0b1 (2016-08-28) ---------------------------- New features: - new commands: - borg recreate: re-create existing archives, #787 #686 #630 #70, also see #757, #770. - selectively remove files/dirs from old archives - re-compress data - re-chunkify data, e.g. to have upgraded Attic / Borg 0.xx archives deduplicate with Borg 1.x archives or to experiment with chunker-params. - borg diff: show differences between archives - borg with-lock: execute a command with the repository locked, #990 - borg create: - Flexible compression with pattern matching on path/filename, and LZ4 heuristic for deciding compressibility, #810, #1007 - visit files in inode order (better speed, esp. for large directories and rotating disks) - in-file checkpoints, #1217 - increased default checkpoint interval to 30 minutes (was 5 minutes), #896 - added uuid archive format tag, #1151 - save mountpoint directories with --one-file-system, makes system restore easier, #1033 - Linux: added support for some BSD flags, #1050 - add 'x' status for excluded paths, #814 - also means files excluded via UF_NODUMP, #1080 - borg check: - will not produce the "Checking segments" output unless new --progress option is passed, #824. - --verify-data to verify data cryptographically on the client, #975 - borg list, #751, #1179 - removed {formatkeys}, see "borg list --help" - --list-format is deprecated, use --format instead - --format now also applies to listing archives, not only archive contents, #1179 - now supports the usual [PATH [PATHS…]] syntax and excludes - new keys: csize, num_chunks, unique_chunks, NUL - supports guaranteed_available hashlib hashes (to avoid varying functionality depending on environment), which includes the SHA1 and SHA2 family as well as MD5 - borg prune: - to better visualize the "thinning out", we now list all archives in reverse time order. rephrase and reorder help text. - implement --keep-last N via --keep-secondly N, also --keep-minutely. assuming that there is not more than 1 backup archive made in 1s, --keep-last N and --keep-secondly N are equivalent, #537 - cleanup checkpoints except the latest, #1008 - borg extract: - added --progress, #1449 - Linux: limited support for BSD flags, #1050 - borg info: - output is now more similar to borg create --stats, #977 - borg mount: - provide "borgfs" wrapper for borg mount, enables usage via fstab, #743 - "versions" mount option - when used with a repository mount, this gives a merged, versioned view of the files in all archives, #729 - repository: - added progress information to commit/compaction phase (often takes some time when deleting/pruning), #1519 - automatic recovery for some forms of repository inconsistency, #858 - check free space before going forward with a commit, #1336 - improved write performance (esp. for rotating media), #985 - new IO code for Linux - raised default segment size to approx 512 MiB - improved compaction performance, #1041 - reduced client CPU load and improved performance for remote repositories, #940 - options that imply output (--show-rc, --show-version, --list, --stats, --progress) don't need -v/--info to have that output displayed, #865 - add archive comments (via borg (re)create --comment), #842 - borg list/prune/delete: also output archive id, #731 - --show-version: shows/logs the borg version, #725 - added --debug-topic for granular debug logging, #1447 - use atomic file writing/updating for configuration and key files, #1377 - BORG_KEY_FILE environment variable, #1001 - self-testing module, #970 Bug fixes: - list: fixed default output being produced if --format is given with empty parameter, #1489 - create: fixed overflowing progress line with CJK and similar characters, #1051 - prune: fixed crash if --prefix resulted in no matches, #1029 - init: clean up partial repo if passphrase input is aborted, #850 - info: quote cmdline arguments that have spaces in them - fix hardlinks failing in some cases for extracting subtrees, #761 Other changes: - replace stdlib hmac with OpenSSL, zero-copy decrypt (10-15% increase in performance of hash-lists and extract). - improved chunker performance, #1021 - open repository segment files in exclusive mode (fail-safe), #1134 - improved error logging, #1440 - Source: - pass meta-data around, #765 - move some constants to new constants module - better readability and fewer errors with namedtuples, #823 - moved source tree into src/ subdirectory, #1016 - made borg.platform a package, #1113 - removed dead crypto code, #1032 - improved and ported parts of the test suite to py.test, #912 - created data classes instead of passing dictionaries around, #981, #1158, #1161 - cleaned up imports, #1112 - Docs: - better help texts and sphinx reproduction of usage help: - Group options - Nicer list of options in Sphinx - Deduplicate 'Common options' (including --help) - chunker: added some insights by "Voltara", #903 - clarify what "deduplicated size" means - fix / update / add package list entries - added a SaltStack usage example, #956 - expanded FAQ - new contributors in AUTHORS! - Tests: - vagrant: add ubuntu/xenial 64bit - this box has still some issues - ChunkBuffer: add test for leaving partial chunk in buffer, fixes #945 Version 1.0.13 (2019-02-15) --------------------------- Please note: this is very likely the last 1.0.x release, please upgrade to 1.1.x. Bug fixes: - security fix: configure FUSE with "default_permissions", #3903. "default_permissions" is now enforced by borg by default to let the kernel check uid/gid/mode based permissions. "ignore_permissions" can be given to not enforce "default_permissions". - xattrs: fix borg exception handling on ENOSPC error, #3808. New features: - Read a passphrase from a file descriptor specified in the BORG_PASSPHRASE_FD environment variable. Other changes: - acl platform code: fix acl set return type - xattr: - add linux {list,get,set}xattr ctypes prototypes - fix darwin flistxattr ctypes prototype - testing / travis-ci: - fix the homebrew 1.9 issues on travis-ci, #4254 - travis OS X: use xcode 8.3 (not broken) - tox.ini: lock requirements - unbreak 1.0-maint on travis, fixes #4123 - vagrant: - misc. fixes - FUSE for macOS: upgrade 3.7.1 to 3.8.3 - Python: upgrade 3.5.5 to 3.5.6 - docs: - Update installation instructions for macOS - update release workflow using twine (docs, scripts), #4213 Version 1.0.12 (2018-04-08) --------------------------- Bug fixes: - repository: cleanup/write: invalidate cached FDs, tests - serve: fix exitcode, #2910 - extract: set bsdflags last (include immutable flag), #3263 - create --timestamp: set start time, #2957 - create: show excluded dir with "x" for tagged dirs / caches, #3189 - migrate locks to child PID when daemonize is used - Buffer: fix wrong thread-local storage use, #2951 - fix detection of non-local path, #3108 - fix LDLP restoration for subprocesses, #3077 - fix subprocess environments (xattr module's fakeroot version check, borg umount, BORG_PASSCOMMAND), #3050 - remote: deal with partial lines, #2637 - get rid of datetime.isoformat, use safe parse_timestamp to parse timestamps, #2994 - build: do .h file content checks in binary mode, fixes build issue for non-ascii header files on pure-ascii locale platforms, #3544 #3639 - remove platform.uname() call which caused library mismatch issues, #3732 - add exception handler around deprecated platform.linux_distribution() call Other changes: - require msgpack-python >= 0.4.6 and < 0.5.0, see #3753 - add parens for C preprocessor macro argument usages (did not cause malfunction) - ignore corrupt files cache, #2939 - replace "modulo" with "if" to check for wraparound in hashmap - keymanager: don't depend on optional readline module, #2980 - exclude broken pytest 3.3.0 release - exclude broken Cython 0.27(.0) release, #3066 - flake8: add some ignores - docs: - create: document exclusion through nodump - document good and problematic option placements, fix examples, #3356 - update docs about hardlinked symlinks limitation - faq: we do not implement futile attempts of ETA / progress displays - simplified rate limiting wrapper in FAQ - twitter account @borgbackup, #2948 - add note about metadata dedup and --no[ac]time, #2518 - change-passphrase only changes the passphrase, #2990 - clarify encrypted key format for borg key export, #3296 - document sshfs rename workaround, #3315 - update release checklist about security fixes - docs about how to verify a signed release, #3634 - chunk seed is generated per /repository/ - vagrant: - use FUSE for macOS 3.7.1 to build the macOS binary - use python 3.5.5 to build the binaries - add exe location to PATH when we build an exe - use https pypi url for wheezy - netbsd: bash is already installed - netbsd: fix netbsd version in PKG_PATH - use self-made FreeBSD 10.3 box, #3022 - backport fs_init (including related updates) from 1.1 - the boxcutter wheezy boxes are 404, use local ones - travis: - don't perform full Travis build on docs-only changes, #2531 - only short-circuit docs-only changes for pull requests Version 1.0.11 (2017-07-21) --------------------------- Bug fixes: - use limited unpacker for outer key (security precaution), #2174 - fix paperkey import bug Other changes: - change --checkpoint-interval default from 600s to 1800s, #2841. this improves efficiency for big repositories a lot. - docs: fix OpenSUSE command and add OpenSUSE section - tests: add tests for split_lstring and paperkey - vagrant: - fix openbsd shell - backport cpu/ram setup from master - add stretch64 VM Version 1.0.11rc1 (2017-06-27) ------------------------------ Bug fixes: - performance: rebuild hashtable if we have too few empty buckets, #2246. this fixes some sporadic, but severe performance breakdowns. - Archive: allocate zeros when needed, #2308 fixes huge memory usage of mount (8 MiB × number of archives) - IPv6 address support also: Location: more informative exception when parsing fails - borg single-file binary: use pyinstaller v3.2.1, #2396 this fixes that the prelink cronjob on some distros kills the borg binary by stripping away parts of it. - extract: - warning for unextracted big extended attributes, #2258 - also create parent dir for device files, if needed. - don't write to disk with --stdout, #2645 - archive check: detect and fix missing all-zero replacement chunks, #2180 - fix (de)compression exceptions, #2224 #2221 - files cache: update inode number, #2226 - borg rpc: use limited msgpack.Unpacker (security precaution), #2139 - Manifest: use limited msgpack.Unpacker (security precaution), #2175 - Location: accept //servername/share/path - fix ChunkIndex.__contains__ assertion for big-endian archs (harmless) - create: handle BackupOSError on a per-path level in one spot - fix error msg, there is no --keep-last in borg 1.0.x, #2282 - clamp (nano)second values to unproblematic range, #2304 - fuse / borg mount: - fix st_blocks to be an integer (not float) value - fix negative uid/gid crash (they could come into archives e.g. when backing up external drives under cygwin), #2674 - fix crash if empty (None) xattr is read - do pre-mount checks before opening repository - check llfuse is installed before asking for passphrase - borg rename: expand placeholders, #2386 - borg serve: fix forced command lines containing BORG_* env vars - fix error msg, it is --keep-within, not --within - fix borg key/debug/benchmark crashing without subcommand, #2240 - chunker: fix invalid use of types, don't do uint32_t >> 32 - document follow_symlinks requirements, check libc, #2507 New features: - added BORG_PASSCOMMAND environment variable, #2573 - add minimal version of in repository mandatory feature flags, #2134 This should allow us to make sure older borg versions can be cleanly prevented from doing operations that are no longer safe because of repository format evolution. This allows more fine grained control than just incrementing the manifest version. So for example a change that still allows new archives to be created but would corrupt the repository when an old version tries to delete an archive or check the repository would add the new feature to the check and delete set but leave it out of the write set. - borg delete --force --force to delete severely corrupted archives, #1975 Other changes: - embrace y2038 issue to support 32bit platforms - be more clear that this is a "beyond repair" case, #2427 - key file names: limit to 100 characters and remove colons from host name - upgrade FUSE for macOS to 3.5.8, #2346 - split up parsing and filtering for --keep-within, better error message, #2610 - docs: - fix caskroom link, #2299 - address SSH batch mode, #2202 #2270 - improve remote-path description - document snapshot usage, #2178 - document relative path usage, #1868 - one link per distro in the installation page - development: new branching model in git repository - kill api page - added FAQ section about backing up root partition - add bountysource badge, #2558 - create empty docs.txt reequirements, #2694 - README: how to help the project - note -v/--verbose requirement on affected options, #2542 - document borg init behaviour via append-only borg serve, #2440 - be clear about what buzhash is used for (chunking) and want it is not used for (deduplication)- also say already in the readme that we use a cryptohash for dedupe, so people don't worry, #2390 - add hint about chunker params to borg upgrade docs, #2421 - clarify borg upgrade docs, #2436 - quickstart: delete problematic BORG_PASSPHRASE use, #2623 - faq: specify "using inline shell scripts" - document pattern denial of service, #2624 - tests: - remove attic dependency of the tests, #2505 - travis: - enhance travis setuptools_scm situation - install fakeroot for Linux - add test for borg delete --force - enable remote tests on cygwin (the cygwin issue that caused these tests to break was fixed in cygwin at least since cygwin 2.8, maybe even since 2.7.0). - remove skipping the noatime tests on GNU/Hurd, #2710 - fix borg import issue, add comment, #2718 - include attic.tar.gz when installing the package also: add include_package_data=True Version 1.0.10 (2017-02-13) --------------------------- Bug fixes: - Manifest timestamps are now monotonically increasing, this fixes issues when the system clock jumps backwards or is set inconsistently across computers accessing the same repository, #2115 - Fixed testing regression in 1.0.10rc1 that lead to a hard dependency on py.test >= 3.0, #2112 New features: - "key export" can now generate a printable HTML page with both a QR code and a human-readable "paperkey" representation (and custom text) through the ``--qr-html`` option. The same functionality is also available through `paperkey.html `_, which is the same HTML page generated by ``--qr-html``. It works with existing "key export" files and key files. Other changes: - docs: - language clarification - "borg create --one-file-system" option does not respect mount points, but considers different file systems instead, #2141 - setup.py: build_api: sort file list for determinism Version 1.0.10rc1 (2017-01-29) ------------------------------ Bug fixes: - borg serve: fix transmission data loss of pipe writes, #1268 This affects only the cygwin platform (not Linux, BSD, OS X). - Avoid triggering an ObjectiveFS bug in xattr retrieval, #1992 - When running out of buffer memory when reading xattrs, only skip the current file, #1993 - Fixed "borg upgrade --tam" crashing with unencrypted repositories. Since :ref:`the issue ` is not relevant for unencrypted repositories, it now does nothing and prints an error, #1981. - Fixed change-passphrase crashing with unencrypted repositories, #1978 - Fixed "borg check repo::archive" indicating success if "archive" does not exist, #1997 - borg check: print non-exit-code warning if --last or --prefix aren't fulfilled - fix bad parsing of wrong repo location syntax - create: don't create hard link refs to failed files, mount: handle invalid hard link refs, #2092 - detect mingw byte order, #2073 - creating a new segment: use "xb" mode, #2099 - mount: umount on SIGINT/^C when in foreground, #2082 Other changes: - binary: use fixed AND freshly compiled pyinstaller bootloader, #2002 - xattr: ignore empty names returned by llistxattr(2) et al - Enable the fault handler: install handlers for the SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals to dump the Python traceback. - Also print a traceback on SIGUSR2. - borg change-passphrase: print key location (simplify making a backup of it) - officially support Python 3.6 (setup.py: add Python 3.6 qualifier) - tests: - vagrant / travis / tox: add Python 3.6 based testing - vagrant: fix openbsd repo, #2042 - vagrant: fix the freebsd64 machine, #2037 #2067 - vagrant: use python 3.5.3 to build binaries, #2078 - vagrant: use osxfuse 3.5.4 for tests / to build binaries vagrant: improve darwin64 VM settings - travis: fix osxfuse install (fixes OS X testing on Travis CI) - travis: require succeeding OS X tests, #2028 - travis: use latest pythons for OS X based testing - use pytest-xdist to parallelize testing - fix xattr test race condition, #2047 - setup.cfg: fix pytest deprecation warning, #2050 - docs: - language clarification - VM backup FAQ - borg create: document how to backup stdin, #2013 - borg upgrade: fix incorrect title levels - add CVE numbers for issues fixed in 1.0.9, #2106 - fix typos (taken from Debian package patch) - remote: include data hexdump in "unexpected RPC data" error message - remote: log SSH command line at debug level - API_VERSION: use numberspaces, #2023 - remove .github from pypi package, #2051 - add pip and setuptools to requirements file, #2030 - SyncFile: fix use of fd object after close (cosmetic) - Manifest.in: simplify, exclude \*.{so,dll,orig}, #2066 - ignore posix_fadvise errors in repository.py, #2095 (works around issues with docker on ARM) - make LoggedIO.close_segment reentrant, avoid reentrance Version 1.0.9 (2016-12-20) -------------------------- Security fixes: - A flaw in the cryptographic authentication scheme in Borg allowed an attacker to spoof the manifest. See :ref:`tam_vuln` above for the steps you should take. CVE-2016-10099 was assigned to this vulnerability. - borg check: When rebuilding the manifest (which should only be needed very rarely) duplicate archive names would be handled on a "first come first serve" basis, allowing an attacker to apparently replace archives. CVE-2016-10100 was assigned to this vulnerability. Bug fixes: - borg check: - rebuild manifest if it's corrupted - skip corrupted chunks during manifest rebuild - fix TypeError in integrity error handler, #1903, #1894 - fix location parser for archives with @ char (regression introduced in 1.0.8), #1930 - fix wrong duration/timestamps if system clock jumped during a create - fix progress display not updating if system clock jumps backwards - fix checkpoint interval being incorrect if system clock jumps Other changes: - docs: - add python3-devel as a dependency for cygwin-based installation - clarify extract is relative to current directory - FAQ: fix link to changelog - markup fixes - tests: - test_get\_(cache|keys)_dir: clean env state, #1897 - get back pytest's pretty assertion failures, #1938 - setup.py build_usage: - fixed build_usage not processing all commands - fixed build_usage not generating includes for debug commands Version 1.0.9rc1 (2016-11-27) ----------------------------- Bug fixes: - files cache: fix determination of newest mtime in backup set (which is used in cache cleanup and led to wrong "A" [added] status for unchanged files in next backup), #1860. - borg check: - fix incorrectly reporting attic 0.13 and earlier archives as corrupt - handle repo w/o objects gracefully and also bail out early if repo is *completely* empty, #1815. - fix tox/pybuild in 1.0-maint - at xattr module import time, loggers are not initialized yet New features: - borg umount exposed already existing umount code via the CLI api, so users can use it, which is more consistent than using borg to mount and fusermount -u (or umount) to un-mount, #1855. - implement borg create --noatime --noctime, fixes #1853 Other changes: - docs: - display README correctly on PyPI - improve cache / index docs, esp. files cache docs, fixes #1825 - different pattern matching for --exclude, #1779 - datetime formatting examples for {now} placeholder, #1822 - clarify passphrase mode attic repo upgrade, #1854 - clarify --umask usage, #1859 - clarify how to choose PR target branch - clarify prune behavior for different archive contents, #1824 - fix PDF issues, add logo, fix authors, headings, TOC - move security verification to support section - fix links in standalone README (:ref: tags) - add link to security contact in README - add FAQ about security - move fork differences to FAQ - add more details about resource usage - tests: skip remote tests on cygwin, #1268 - travis: - allow OS X failures until the brew cask osxfuse issue is fixed - caskroom osxfuse-beta gone, it's osxfuse now (3.5.3) - vagrant: - upgrade OSXfuse / FUSE for macOS to 3.5.3 - remove llfuse from tox.ini at a central place - do not try to install llfuse on centos6 - fix FUSE test for darwin, #1546 - add windows virtual machine with cygwin - Vagrantfile cleanup / code deduplication Version 1.0.8 (2016-10-29) -------------------------- Bug fixes: - RemoteRepository: Fix busy wait in call_many, #940 New features: - implement borgmajor/borgminor/borgpatch placeholders, #1694 {borgversion} was already there (full version string). With the new placeholders you can now also get e.g. 1 or 1.0 or 1.0.8. Other changes: - avoid previous_location mismatch, #1741 due to the changed canonicalization for relative paths in PR #1711 / #1655 (implement /./ relpath hack), there would be a changed repo location warning and the user would be asked if this is ok. this would break automation and require manual intervention, which is unwanted. thus, we automatically fix the previous_location config entry, if it only changed in the expected way, but still means the same location. - docs: - deployment.rst: do not use bare variables in ansible snippet - add clarification about append-only mode, #1689 - setup.py: add comment about requiring llfuse, #1726 - update usage.rst / api.rst - repo url / archive location docs + typo fix - quickstart: add a comment about other (remote) filesystems - vagrant / tests: - no chown when rsyncing (fixes boxes w/o vagrant group) - fix FUSE permission issues on linux/freebsd, #1544 - skip FUSE test for borg binary + fakeroot - ignore security.selinux xattrs, fixes tests on centos, #1735 Version 1.0.8rc1 (2016-10-17) ----------------------------- Bug fixes: - fix signal handling (SIGINT, SIGTERM, SIGHUP), #1620 #1593 Fixes e.g. leftover lock files for quickly repeated signals (e.g. Ctrl-C Ctrl-C) or lost connections or systemd sending SIGHUP. - progress display: adapt formatting to narrow screens, do not crash, #1628 - borg create --read-special - fix crash on broken symlink, #1584. also correctly processes broken symlinks. before this regressed to a crash (5b45385) a broken symlink would've been skipped. - process_symlink: fix missing backup_io() Fixes a chmod/chown/chgrp/unlink/rename/... crash race between getting dirents and dispatching to process_symlink. - yes(): abort on wrong answers, saying so, #1622 - fixed exception borg serve raised when connection was closed before repository was opened. Add an error message for this. - fix read-from-closed-FD issue, #1551 (this seems not to get triggered in 1.0.x, but was discovered in master) - hashindex: fix iterators (always raise StopIteration when exhausted) (this seems not to get triggered in 1.0.x, but was discovered in master) - enable relative paths in ssh:// repo URLs, via /./relpath hack, #1655 - allow repo paths with colons, #1705 - update changed repo location immediately after acceptance, #1524 - fix debug get-obj / delete-obj crash if object not found and remote repo, #1684 - pyinstaller: use a spec file to build borg.exe binary, exclude osxfuse dylib on Mac OS X (avoids mismatch lib <-> driver), #1619 New features: - add "borg key export" / "borg key import" commands, #1555, so users are able to backup / restore their encryption keys more easily. Supported formats are the keyfile format used by borg internally and a special "paper" format with by line checksums for printed backups. For the paper format, the import is an interactive process which checks each line as soon as it is input. - add "borg debug-refcount-obj" to determine a repo objects' referrer counts, #1352 Other changes: - add "borg debug ..." subcommands (borg debug-* still works, but will be removed in borg 1.1) - setup.py: Add subcommand support to build_usage. - remote: change exception message for unexpected RPC data format to indicate dataflow direction. - improved messages / error reporting: - IntegrityError: add placeholder for message, so that the message we give appears not only in the traceback, but also in the (short) error message, #1572 - borg.key: include chunk id in exception msgs, #1571 - better messages for cache newer than repo, #1700 - vagrant (testing/build VMs): - upgrade OSXfuse / FUSE for macOS to 3.5.2 - update Debian Wheezy boxes, #1686 - openbsd / netbsd: use own boxes, fixes misc rsync installation and FUSE/llfuse related testing issues, #1695 #1696 #1670 #1671 #1728 - docs: - add docs for "key export" and "key import" commands, #1641 - fix inconsistency in FAQ (pv-wrapper). - fix second block in "Easy to use" section not showing on GitHub, #1576 - add bestpractices badge - link reference docs and faq about BORG_FILES_CACHE_TTL, #1561 - improve borg info --help, explain size infos, #1532 - add release signing key / security contact to README, #1560 - add contribution guidelines for developers - development.rst: add sphinx_rtd_theme to the sphinx install command - adjust border color in borg.css - add debug-info usage help file - internals.rst: fix typos - setup.py: fix build_usage to always process all commands - added docs explaining multiple --restrict-to-path flags, #1602 - add more specific warning about write-access debug commands, #1587 - clarify FAQ regarding backup of virtual machines, #1672 - tests: - work around FUSE xattr test issue with recent fakeroot - simplify repo/hashindex tests - travis: test FUSE-enabled borg, use trusty to have a recent FUSE - re-enable FUSE tests for RemoteArchiver (no deadlocks any more) - clean env for pytest based tests, #1714 - fuse_mount contextmanager: accept any options Version 1.0.7 (2016-08-19) -------------------------- Security fixes: - borg serve: fix security issue with remote repository access, #1428 If you used e.g. --restrict-to-path /path/client1/ (with or without trailing slash does not make a difference), it acted like a path prefix match using /path/client1 (note the missing trailing slash) - the code then also allowed working in e.g. /path/client13 or /path/client1000. As this could accidentally lead to major security/privacy issues depending on the paths you use, the behaviour was changed to be a strict directory match. That means --restrict-to-path /path/client1 (with or without trailing slash does not make a difference) now uses /path/client1/ internally (note the trailing slash here!) for matching and allows precisely that path AND any path below it. So, /path/client1 is allowed, /path/client1/repo1 is allowed, but not /path/client13 or /path/client1000. If you willingly used the undocumented (dangerous) previous behaviour, you may need to rearrange your --restrict-to-path paths now. We are sorry if that causes work for you, but we did not want a potentially dangerous behaviour in the software (not even using a for-backwards-compat option). Bug fixes: - fixed repeated LockTimeout exceptions when borg serve tried to write into a already write-locked repo (e.g. by a borg mount), #502 part b) This was solved by the fix for #1220 in 1.0.7rc1 already. - fix cosmetics + file leftover for "not a valid borg repository", #1490 - Cache: release lock if cache is invalid, #1501 - borg extract --strip-components: fix leak of preloaded chunk contents - Repository, when a InvalidRepository exception happens: - fix spurious, empty lock.roster - fix repo not closed cleanly New features: - implement borg debug-info, fixes #1122 (just calls already existing code via cli, same output as below tracebacks) Other changes: - skip the O_NOATIME test on GNU Hurd, fixes #1315 (this is a very minor issue and the GNU Hurd project knows the bug) - document using a clean repo to test / build the release Version 1.0.7rc2 (2016-08-13) ----------------------------- Bug fixes: - do not write objects to repository that are bigger than the allowed size, borg will reject reading them, #1451. Important: if you created archives with many millions of files or directories, please verify if you can open them successfully, e.g. try a "borg list REPO::ARCHIVE". - lz4 compression: dynamically enlarge the (de)compression buffer, the static buffer was not big enough for archives with extremely many items, #1453 - larger item metadata stream chunks, raise archive item limit by 8x, #1452 - fix untracked segments made by moved DELETEs, #1442 Impact: Previously (metadata) segments could become untracked when deleting data, these would never be cleaned up. - extended attributes (xattrs) related fixes: - fixed a race condition in xattrs querying that led to the entire file not being backed up (while logging the error, exit code = 1), #1469 - fixed a race condition in xattrs querying that led to a crash, #1462 - raise OSError including the error message derived from errno, deal with path being a integer FD Other changes: - print active env var override by default, #1467 - xattr module: refactor code, deduplicate, clean up - repository: split object size check into too small and too big - add a transaction_id assertion, so borg init on a broken (inconsistent) filesystem does not look like a coding error in borg, but points to the real problem. - explain confusing TypeError caused by compat support for old servers, #1456 - add forgotten usage help file from build_usage - refactor/unify buffer code into helpers.Buffer class, add tests - docs: - document archive limitation, #1452 - improve prune examples Version 1.0.7rc1 (2016-08-05) ----------------------------- Bug fixes: - fix repo lock deadlocks (related to lock upgrade), #1220 - catch unpacker exceptions, resync, #1351 - fix borg break-lock ignoring BORG_REPO env var, #1324 - files cache performance fixes (fixes unnecessary re-reading/chunking/ hashing of unmodified files for some use cases): - fix unintended file cache eviction, #1430 - implement BORG_FILES_CACHE_TTL, update FAQ, raise default TTL from 10 to 20, #1338 - FUSE: - cache partially read data chunks (performance), #965, #966 - always create a root dir, #1125 - use an OrderedDict for helptext, making the build reproducible, #1346 - RemoteRepository init: always call close on exceptions, #1370 (cosmetic) - ignore stdout/stderr broken pipe errors (cosmetic), #1116 New features: - better borg versions management support (useful esp. for borg servers wanting to offer multiple borg versions and for clients wanting to choose a specific server borg version), #1392: - add BORG_VERSION environment variable before executing "borg serve" via ssh - add new placeholder {borgversion} - substitute placeholders in --remote-path - borg init --append-only option (makes using the more secure append-only mode more convenient. when used remotely, this requires 1.0.7+ also on the borg server), #1291. Other changes: - Vagrantfile: - darwin64: upgrade to FUSE for macOS 3.4.1 (aka osxfuse), #1378 - xenial64: use user "ubuntu", not "vagrant" (as usual), #1331 - tests: - fix FUSE tests on OS X, #1433 - docs: - FAQ: add backup using stable filesystem names recommendation - FAQ about glibc compatibility added, #491, glibc-check improved - FAQ: 'A' unchanged file; remove ambiguous entry age sentence. - OS X: install pkg-config to build with FUSE support, fixes #1400 - add notes about shell/sudo pitfalls with env. vars, #1380 - added platform feature matrix - implement borg debug-dump-repo-objs Version 1.0.6 (2016-07-12) -------------------------- Bug fixes: - Linux: handle multiple LD_PRELOAD entries correctly, #1314, #1111 - Fix crash with unclear message if the libc is not found, #1314, #1111 Other changes: - tests: - Fixed O_NOATIME tests for Solaris and GNU Hurd, #1315 - Fixed sparse file tests for (file) systems not supporting it, #1310 - docs: - Fixed syntax highlighting, #1313 - misc docs: added data processing overview picture Version 1.0.6rc1 (2016-07-10) ----------------------------- New features: - borg check --repair: heal damaged files if missing chunks re-appear (e.g. if the previously missing chunk was added again in a later backup archive), #148. (*) Also improved logging. Bug fixes: - sync_dir: silence fsync() failing with EINVAL, #1287 Some network filesystems (like smbfs) don't support this and we use this in repository code. - borg mount (FUSE): - fix directories being shadowed when contained paths were also specified, #1295 - raise I/O Error (EIO) on damaged files (unless -o allow_damaged_files is used), #1302. (*) - borg extract: warn if a damaged file is extracted, #1299. (*) - Added some missing return code checks (ChunkIndex._add, hashindex_resize). - borg check: fix/optimize initial hash table size, avoids resize of the table. Other changes: - tests: - add more FUSE tests, #1284 - deduplicate FUSE (u)mount code - fix borg binary test issues, #862 - docs: - changelog: added release dates to older borg releases - fix some sphinx (docs generator) warnings, #881 Notes: (*) Some features depend on information (chunks_healthy list) added to item metadata when a file with missing chunks was "repaired" using all-zero replacement chunks. The chunks_healthy list is generated since borg 1.0.4, thus borg can't recognize such "repaired" (but content-damaged) files if the repair was done with an older borg version. Version 1.0.5 (2016-07-07) -------------------------- Bug fixes: - borg mount: fix FUSE crash in xattr code on Linux introduced in 1.0.4, #1282 Other changes: - backport some FAQ entries from master branch - add release helper scripts - Vagrantfile: - centos6: no FUSE, don't build binary - add xz for redhat-like dists Version 1.0.4 (2016-07-07) -------------------------- New features: - borg serve --append-only, #1168 This was included because it was a simple change (append-only functionality was already present via repository config file) and makes better security now practically usable. - BORG_REMOTE_PATH environment variable, #1258 This was included because it was a simple change (--remote-path cli option was already present) and makes borg much easier to use if you need it. - Repository: cleanup incomplete transaction on "no space left" condition. In many cases, this can avoid a 100% full repo filesystem (which is very problematic as borg always needs free space - even to delete archives). Bug fixes: - Fix wrong handling and reporting of OSErrors in borg create, #1138. This was a serious issue: in the context of "borg create", errors like repository I/O errors (e.g. disk I/O errors, ssh repo connection errors) were handled badly and did not lead to a crash (which would be good for this case, because the repo transaction would be incomplete and trigger a transaction rollback to clean up). Now, error handling for source files is cleanly separated from every other error handling, so only problematic input files are logged and skipped. - Implement fail-safe error handling for borg extract. Note that this isn't nearly as critical as the borg create error handling bug, since nothing is written to the repo. So this was "merely" misleading error reporting. - Add missing error handler in directory attr restore loop. - repo: make sure write data hits disk before the commit tag (#1236) and also sync the containing directory. - FUSE: getxattr fail must use errno.ENOATTR, #1126 (fixes Mac OS X Finder malfunction: "zero bytes" file length, access denied) - borg check --repair: do not lose information about the good/original chunks. If we do not lose the original chunk IDs list when "repairing" a file (replacing missing chunks with all-zero chunks), we have a chance to "heal" the file back into its original state later, in case the chunks re-appear (e.g. in a fresh backup). Healing is not implemented yet, see #148. - fixes for --read-special mode: - ignore known files cache, #1241 - fake regular file mode, #1214 - improve symlinks handling, #1215 - remove passphrase from subprocess environment, #1105 - Ignore empty index file (will trigger index rebuild), #1195 - add missing placeholder support for --prefix, #1027 - improve exception handling for placeholder replacement - catch and format exceptions in arg parsing - helpers: fix "undefined name 'e'" in exception handler - better error handling for missing repo manifest, #1043 - borg delete: - make it possible to delete a repo without manifest - borg delete --forced allows one to delete corrupted archives, #1139 - borg check: - make borg check work for empty repo - fix resync and msgpacked item qualifier, #1135 - rebuild_manifest: fix crash if 'name' or 'time' key were missing. - better validation of item metadata dicts, #1130 - better validation of archive metadata dicts - close the repo on exit - even if rollback did not work, #1197. This is rather cosmetic, it avoids repo closing in the destructor. - tests: - fix sparse file test, #1170 - flake8: ignore new F405, #1185 - catch "invalid argument" on cygwin, #257 - fix sparseness assertion in test prep, #1264 Other changes: - make borg build/work on OpenSSL 1.0 and 1.1, #1187 - docs / help: - fix / clarify prune help, #1143 - fix "patterns" help formatting - add missing docs / help about placeholders - resources: rename atticmatic to borgmatic - document sshd settings, #545 - more details about checkpoints, add split trick, #1171 - support docs: add freenode web chat link, #1175 - add prune visualization / example, #723 - add note that Fnmatch is default, #1247 - make clear that lzma levels > 6 are a waste of cpu cycles - add a "do not edit" note to auto-generated files, #1250 - update cygwin installation docs - repository interoperability with borg master (1.1dev) branch: - borg check: read item metadata keys from manifest, #1147 - read v2 hints files, #1235 - fix hints file "unknown version" error handling bug - tests: add tests for format_line - llfuse: update version requirement for freebsd - Vagrantfile: - use openbsd 5.9, #716 - do not install llfuse on netbsd (broken) - update OSXfuse to version 3.3.3 - use Python 3.5.2 to build the binaries - glibc compatibility checker: scripts/glibc_check.py - add .eggs to .gitignore Version 1.0.3 (2016-05-20) -------------------------- Bug fixes: - prune: avoid that checkpoints are kept and completed archives are deleted in a prune run), #997 - prune: fix commandline argument validation - some valid command lines were considered invalid (annoying, but harmless), #942 - fix capabilities extraction on Linux (set xattrs last, after chown()), #1069 - repository: fix commit tags being seen in data - when probing key files, do binary reads. avoids crash when non-borg binary files are located in borg's key files directory. - handle SIGTERM and make a clean exit - avoids orphan lock files. - repository cache: don't cache large objects (avoid using lots of temp. disk space), #1063 Other changes: - Vagrantfile: OS X: update osxfuse / install lzma package, #933 - setup.py: add check for platform_darwin.c - setup.py: on freebsd, use a llfuse release that builds ok - docs / help: - update readthedocs URLs, #991 - add missing docs for "borg break-lock", #992 - borg create help: add some words to about the archive name - borg create help: document format tags, #894 Version 1.0.2 (2016-04-16) -------------------------- Bug fixes: - fix malfunction and potential corruption on (nowadays rather rare) big-endian architectures or bi-endian archs in (rare) BE mode. #886, #889 cache resync / index merge was malfunctioning due to this, potentially leading to data loss. borg info had cosmetic issues (displayed wrong values). note: all (widespread) little-endian archs (like x86/x64) or bi-endian archs in (widespread) LE mode (like ARMEL, MIPSEL, ...) were NOT affected. - add overflow and range checks for 1st (special) uint32 of the hashindex values, switch from int32 to uint32. - fix so that refcount will never overflow, but just stick to max. value after a overflow would have occurred. - borg delete: fix --cache-only for broken caches, #874 Makes --cache-only idempotent: it won't fail if the cache is already deleted. - fixed borg create --one-file-system erroneously traversing into other filesystems (if starting fs device number was 0), #873 - workaround a bug in Linux fadvise FADV_DONTNEED, #907 Other changes: - better test coverage for hashindex, incl. overflow testing, checking correct computations so endianness issues would be discovered. - reproducible doc for ProgressIndicator*, make the build reproducible. - use latest llfuse for vagrant machines - docs: - use /path/to/repo in examples, fixes #901 - fix confusing usage of "repo" as archive name (use "arch") Version 1.0.1 (2016-04-08) -------------------------- New features: Usually there are no new features in a bugfix release, but these were added due to their high impact on security/safety/speed or because they are fixes also: - append-only mode for repositories, #809, #36 (see docs) - borg create: add --ignore-inode option to make borg detect unmodified files even if your filesystem does not have stable inode numbers (like sshfs and possibly CIFS). - add options --warning, --error, --critical for missing log levels, #826. it's not recommended to suppress warnings or errors, but the user may decide this on his own. note: --warning is not given to borg serve so a <= 1.0.0 borg will still work as server (it is not needed as it is the default). do not use --error or --critical when using a <= 1.0.0 borg server. Bug fixes: - fix silently skipping EIO, #748 - add context manager for Repository (avoid orphan repository locks), #285 - do not sleep for >60s while waiting for lock, #773 - unpack file stats before passing to FUSE - fix build on illumos - don't try to backup doors or event ports (Solaris and derivatives) - remove useless/misleading libc version display, #738 - test suite: reset exit code of persistent archiver, #844 - RemoteRepository: clean up pipe if remote open() fails - Remote: don't print tracebacks for Error exceptions handled downstream, #792 - if BORG_PASSPHRASE is present but wrong, don't prompt for password, but fail instead, #791 - ArchiveChecker: move "orphaned objects check skipped" to INFO log level, #826 - fix capitalization, add ellipses, change log level to debug for 2 messages, #798 Other changes: - update llfuse requirement, llfuse 1.0 works - update OS / dist packages on build machines, #717 - prefer showing --info over -v in usage help, #859 - docs: - fix cygwin requirements (gcc-g++) - document how to debug / file filesystem issues, #664 - fix reproducible build of api docs - RTD theme: CSS !important overwrite, #727 - Document logo font. Recreate logo png. Remove GIMP logo file. Version 1.0.0 (2016-03-05) -------------------------- The major release number change (0.x -> 1.x) indicates bigger incompatible changes, please read the compatibility notes, adapt / test your scripts and check your backup logs. Compatibility notes: - drop support for python 3.2 and 3.3, require 3.4 or 3.5, #221 #65 #490 note: we provide binaries that include python 3.5.1 and everything else needed. they are an option in case you are stuck with < 3.4 otherwise. - change encryption to be on by default (using "repokey" mode) - moved keyfile keys from ~/.borg/keys to ~/.config/borg/keys, you can either move them manually or run "borg upgrade " - remove support for --encryption=passphrase, use borg migrate-to-repokey to switch to repokey mode, #97 - remove deprecated --compression , use --compression zlib, instead in case of 0, you could also use --compression none - remove deprecated --hourly/daily/weekly/monthly/yearly use --keep-hourly/daily/weekly/monthly/yearly instead - remove deprecated --do-not-cross-mountpoints, use --one-file-system instead - disambiguate -p option, #563: - -p now is same as --progress - -P now is same as --prefix - remove deprecated "borg verify", use "borg extract --dry-run" instead - cleanup environment variable semantics, #355 the environment variables used to be "yes sayers" when set, this was conceptually generalized to "automatic answerers" and they just give their value as answer (as if you typed in that value when being asked). See the "usage" / "Environment Variables" section of the docs for details. - change the builtin default for --chunker-params, create 2MiB chunks, #343 --chunker-params new default: 19,23,21,4095 - old default: 10,23,16,4095 one of the biggest issues with borg < 1.0 (and also attic) was that it had a default target chunk size of 64kiB, thus it created a lot of chunks and thus also a huge chunk management overhead (high RAM and disk usage). please note that the new default won't change the chunks that you already have in your repository. the new big chunks do not deduplicate with the old small chunks, so expect your repo to grow at least by the size of every changed file and in the worst case (e.g. if your files cache was lost / is not used) by the size of every file (minus any compression you might use). in case you want to immediately see a much lower resource usage (RAM / disk) for chunks management, it might be better to start with a new repo than continuing in the existing repo (with an existing repo, you'ld have to wait until all archives with small chunks got pruned to see a lower resource usage). if you used the old --chunker-params default value (or if you did not use --chunker-params option at all) and you'ld like to continue using small chunks (and you accept the huge resource usage that comes with that), just explicitly use borg create --chunker-params=10,23,16,4095. - archive timestamps: the 'time' timestamp now refers to archive creation start time (was: end time), the new 'time_end' timestamp refers to archive creation end time. This might affect prune if your backups take rather long. if you give a timestamp via cli this is stored into 'time', therefore it now needs to mean archive creation start time. New features: - implement password roundtrip, #695 Bug fixes: - remote end does not need cache nor keys directories, do not create them, #701 - added retry counter for passwords, #703 Other changes: - fix compiler warnings, #697 - docs: - update README.rst to new changelog location in docs/changes.rst - add Teemu to AUTHORS - changes.rst: fix old chunker params, #698 - FAQ: how to limit bandwidth Version 1.0.0rc2 (2016-02-28) ----------------------------- New features: - format options for location: user, pid, fqdn, hostname, now, utcnow, user - borg list --list-format - borg prune -v --list enables the keep/prune list output, #658 Bug fixes: - fix _open_rb noatime handling, #657 - add a simple archivename validator, #680 - borg create --stats: show timestamps in localtime, use same labels/formatting as borg info, #651 - llfuse compatibility fixes (now compatible with: 0.40, 0.41, 0.42) Other changes: - it is now possible to use "pip install borgbackup[fuse]" to automatically install the llfuse dependency using the correct version requirement for it. you still need to care about having installed the FUSE / build related OS package first, though, so that building llfuse can succeed. - Vagrant: drop Ubuntu Precise (12.04) - does not have Python >= 3.4 - Vagrant: use pyinstaller v3.1.1 to build binaries - docs: - borg upgrade: add to docs that only LOCAL repos are supported - borg upgrade also handles borg 0.xx -> 1.0 - use pip extras or requirements file to install llfuse - fix order in release process - updated usage docs and other minor / cosmetic fixes - verified borg examples in docs, #644 - freebsd dependency installation and FUSE configuration, #649 - add example how to restore a raw device, #671 - add a hint about the dev headers needed when installing from source - add examples for delete (and handle delete after list, before prune), #656 - update example for borg create -v --stats (use iso datetime format), #663 - added example to BORG_RSH docs - "connection closed by remote": add FAQ entry and point to issue #636 Version 1.0.0rc1 (2016-02-07) ----------------------------- New features: - borg migrate-to-repokey ("passphrase" -> "repokey" encryption key mode) - implement --short for borg list REPO, #611 - implement --list for borg extract (consistency with borg create) - borg serve: overwrite client's --restrict-to-path with ssh forced command's option value (but keep everything else from the client commandline), #544 - use $XDG_CONFIG_HOME/keys for keyfile keys (~/.config/borg/keys), #515 - "borg upgrade" moves the keyfile keys to the new location - display both archive creation start and end time in "borg info", #627 Bug fixes: - normalize trailing slashes for the repository path, #606 - Cache: fix exception handling in __init__, release lock, #610 Other changes: - suppress unneeded exception context (PEP 409), simpler tracebacks - removed special code needed to deal with imperfections / incompatibilities / missing stuff in py 3.2/3.3, simplify code that can be done simpler in 3.4 - removed some version requirements that were kept on old versions because newer did not support py 3.2 any more - use some py 3.4+ stdlib code instead of own/openssl/pypi code: - use os.urandom instead of own cython openssl RAND_bytes wrapper, #493 - use hashlib.pbkdf2_hmac from py stdlib instead of own openssl wrapper - use hmac.compare_digest instead of == operator (constant time comparison) - use stat.filemode instead of homegrown code - use "mock" library from stdlib, #145 - remove borg.support (with non-broken argparse copy), it is ok in 3.4+, #358 - Vagrant: copy CHANGES.rst as symlink, #592 - cosmetic code cleanups, add flake8 to tox/travis, #4 - docs / help: - make "borg -h" output prettier, #591 - slightly rephrase prune help - add missing example for --list option of borg create - quote exclude line that includes an asterisk to prevent shell expansion - fix dead link to license - delete Ubuntu Vivid, it is not supported anymore (EOL) - OS X binary does not work for older OS X releases, #629 - borg serve's special support for forced/original ssh commands, #544 - misc. updates and fixes Version 0.30.0 (2016-01-23) --------------------------- Compatibility notes: - you may need to use -v (or --info) more often to actually see output emitted at INFO log level (because it is suppressed at the default WARNING log level). See the "general" section in the usage docs. - for borg create, you need --list (additionally to -v) to see the long file list (was needed so you can have e.g. --stats alone without the long list) - see below about BORG_DELETE_I_KNOW_WHAT_I_AM_DOING (was: BORG_CHECK_I_KNOW_WHAT_I_AM_DOING) Bug fixes: - fix crash when using borg create --dry-run --keep-tag-files, #570 - make sure teardown with cleanup happens for Cache and RepositoryCache, avoiding leftover locks and TEMP dir contents, #285 (partially), #548 - fix locking KeyError, partial fix for #502 - log stats consistently, #526 - add abbreviated weekday to timestamp format, fixes #496 - strip whitespace when loading exclusions from file - unset LD_LIBRARY_PATH before invoking ssh, fixes strange OpenSSL library version warning when using the borg binary, #514 - add some error handling/fallback for C library loading, #494 - added BORG_DELETE_I_KNOW_WHAT_I_AM_DOING for check in "borg delete", #503 - remove unused "repair" rpc method name New features: - borg create: implement exclusions using regular expression patterns. - borg create: implement inclusions using patterns. - borg extract: support patterns, #361 - support different styles for patterns: - fnmatch (`fm:` prefix, default when omitted), like borg <= 0.29. - shell (`sh:` prefix) with `*` not matching directory separators and `**/` matching 0..n directories - path prefix (`pp:` prefix, for unifying borg create pp1 pp2 into the patterns system), semantics like in borg <= 0.29 - regular expression (`re:`), new! - --progress option for borg upgrade (#291) and borg delete - update progress indication more often (e.g. for borg create within big files or for borg check repo), #500 - finer chunker granularity for items metadata stream, #547, #487 - borg create --list now used (additionally to -v) to enable the verbose file list output - display borg version below tracebacks, #532 Other changes: - hashtable size (and thus: RAM and disk consumption) follows a growth policy: grows fast while small, grows slower when getting bigger, #527 - Vagrantfile: use pyinstaller 3.1 to build binaries, freebsd sqlite3 fix, fixes #569 - no separate binaries for centos6 any more because the generic linux binaries also work on centos6 (or in general: on systems with a slightly older glibc than debian7 - dev environment: require virtualenv<14.0 so we get a py32 compatible pip - docs: - add space-saving chunks.archive.d trick to FAQ - important: clarify -v and log levels in usage -> general, please read! - sphinx configuration: create a simple man page from usage docs - add a repo server setup example - disable unneeded SSH features in authorized_keys examples for security. - borg prune only knows "--keep-within" and not "--within" - add gource video to resources docs, #507 - add netbsd install instructions - authors: make it more clear what refers to borg and what to attic - document standalone binary requirements, #499 - rephrase the mailing list section - development docs: run build_api and build_usage before tagging release - internals docs: hash table max. load factor is 0.75 now - markup, typo, grammar, phrasing, clarifications and other fixes. - add gcc gcc-c++ to redhat/fedora/corora install docs, fixes #583 Version 0.29.0 (2015-12-13) --------------------------- Compatibility notes: - when upgrading to 0.29.0 you need to upgrade client as well as server installations due to the locking and commandline interface changes otherwise you'll get an error msg about a RPC protocol mismatch or a wrong commandline option. if you run a server that needs to support both old and new clients, it is suggested that you have a "borg-0.28.2" and a "borg-0.29.0" command. clients then can choose via e.g. "borg --remote-path=borg-0.29.0 ...". - the default waiting time for a lock changed from infinity to 1 second for a better interactive user experience. if the repo you want to access is currently locked, borg will now terminate after 1s with an error message. if you have scripts that shall wait for the lock for a longer time, use --lock-wait N (with N being the maximum wait time in seconds). Bug fixes: - hash table tuning (better chosen hashtable load factor 0.75 and prime initial size of 1031 gave ~1000x speedup in some scenarios) - avoid creation of an orphan lock for one case, #285 - --keep-tag-files: fix file mode and multiple tag files in one directory, #432 - fixes for "borg upgrade" (attic repo converter), #466 - remove --progress isatty magic (and also --no-progress option) again, #476 - borg init: display proper repo URL - fix format of umask in help pages, #463 New features: - implement --lock-wait, support timeout for UpgradableLock, #210 - implement borg break-lock command, #157 - include system info below traceback, #324 - sane remote logging, remote stderr, #461: - remote log output: intercept it and log it via local logging system, with "Remote: " prefixed to message. log remote tracebacks. - remote stderr: output it to local stderr with "Remote: " prefixed. - add --debug and --info (same as --verbose) to set the log level of the builtin logging configuration (which otherwise defaults to warning), #426 note: there are few messages emitted at DEBUG level currently. - optionally configure logging via env var BORG_LOGGING_CONF - add --filter option for status characters: e.g. to show only the added or modified files (and also errors), use "borg create -v --filter=AME ...". - more progress indicators, #394 - use ISO-8601 date and time format, #375 - "borg check --prefix" to restrict archive checking to that name prefix, #206 Other changes: - hashindex_add C implementation (speed up cache re-sync for new archives) - increase FUSE read_size to 1024 (speed up metadata operations) - check/delete/prune --save-space: free unused segments quickly, #239 - increase rpc protocol version to 2 (see also Compatibility notes), #458 - silence borg by default (via default log level WARNING) - get rid of C compiler warnings, #391 - upgrade OS X FUSE to 3.0.9 on the OS X binary build system - use python 3.5.1 to build binaries - docs: - new mailing list borgbackup@python.org, #468 - readthedocs: color and logo improvements - load coverage icons over SSL (avoids mixed content) - more precise binary installation steps - update release procedure docs about OS X FUSE - FAQ entry about unexpected 'A' status for unchanged file(s), #403 - add docs about 'E' file status - add "borg upgrade" docs, #464 - add developer docs about output and logging - clarify encryption, add note about client-side encryption - add resources section, with videos, talks, presentations, #149 - Borg moved to Arch Linux [community] - fix wrong installation instructions for archlinux Version 0.28.2 (2015-11-15) --------------------------- New features: - borg create --exclude-if-present TAGFILE - exclude directories that have the given file from the backup. You can additionally give --keep-tag-files to preserve just the directory roots and the tag-files (but not backup other directory contents), #395, attic #128, attic #142 Other changes: - do not create docs sources at build time (just have them in the repo), completely remove have_cython() hack, do not use the "mock" library at build time, #384 - avoid hidden import, make it easier for PyInstaller, easier fix for #218 - docs: - add description of item flags / status output, fixes #402 - explain how to regenerate usage and API files (build_api or build_usage) and when to commit usage files directly into git, #384 - minor install docs improvements Version 0.28.1 (2015-11-08) --------------------------- Bug fixes: - do not try to build api / usage docs for production install, fixes unexpected "mock" build dependency, #384 Other changes: - avoid using msgpack.packb at import time - fix formatting issue in changes.rst - fix build on readthedocs Version 0.28.0 (2015-11-08) --------------------------- Compatibility notes: - changed return codes (exit codes), see docs. in short: old: 0 = ok, 1 = error. now: 0 = ok, 1 = warning, 2 = error New features: - refactor return codes (exit codes), fixes #61 - add --show-rc option enable "terminating with X status, rc N" output, fixes 58, #351 - borg create backups atime and ctime additionally to mtime, fixes #317 - extract: support atime additionally to mtime - FUSE: support ctime and atime additionally to mtime - support borg --version - emit a warning if we have a slow msgpack installed - borg list --prefix=thishostname- REPO, fixes #205 - Debug commands (do not use except if you know what you do: debug-get-obj, debug-put-obj, debug-delete-obj, debug-dump-archive-items. Bug fixes: - setup.py: fix bug related to BORG_LZ4_PREFIX processing - fix "check" for repos that have incomplete chunks, fixes #364 - borg mount: fix unlocking of repository at umount time, fixes #331 - fix reading files without touching their atime, #334 - non-ascii ACL fixes for Linux, FreeBSD and OS X, #277 - fix acl_use_local_uid_gid() and add a test for it, attic #359 - borg upgrade: do not upgrade repositories in place by default, #299 - fix cascading failure with the index conversion code, #269 - borg check: implement 'cmdline' archive metadata value decoding, #311 - fix RobustUnpacker, it missed some metadata keys (new atime and ctime keys were missing, but also bsdflags). add check for unknown metadata keys. - create from stdin: also save atime, ctime (cosmetic) - use default_notty=False for confirmations, fixes #345 - vagrant: fix msgpack installation on centos, fixes #342 - deal with unicode errors for symlinks in same way as for regular files and have a helpful warning message about how to fix wrong locale setup, fixes #382 - add ACL keys the RobustUnpacker must know about Other changes: - improve file size displays, more flexible size formatters - explicitly commit to the units standard, #289 - archiver: add E status (means that an error occurred when processing this (single) item - do binary releases via "github releases", closes #214 - create: use -x and --one-file-system (was: --do-not-cross-mountpoints), #296 - a lot of changes related to using "logging" module and screen output, #233 - show progress display if on a tty, output more progress information, #303 - factor out status output so it is consistent, fix surrogates removal, maybe fixes #309 - move away from RawConfigParser to ConfigParser - archive checker: better error logging, give chunk_id and sequence numbers (can be used together with borg debug-dump-archive-items). - do not mention the deprecated passphrase mode - emit a deprecation warning for --compression N (giving a just a number) - misc .coverragerc fixes (and coverage measurement improvements), fixes #319 - refactor confirmation code, reduce code duplication, add tests - prettier error messages, fixes #307, #57 - tests: - add a test to find disk-full issues, #327 - travis: also run tests on Python 3.5 - travis: use tox -r so it rebuilds the tox environments - test the generated pyinstaller-based binary by archiver unit tests, #215 - vagrant: tests: announce whether fakeroot is used or not - vagrant: add vagrant user to fuse group for debianoid systems also - vagrant: llfuse install on darwin needs pkgconfig installed - vagrant: use pyinstaller from develop branch, fixes #336 - benchmarks: test create, extract, list, delete, info, check, help, fixes #146 - benchmarks: test with both the binary and the python code - archiver tests: test with both the binary and the python code, fixes #215 - make basic test more robust - docs: - moved docs to borgbackup.readthedocs.org, #155 - a lot of fixes and improvements, use mobile-friendly RTD standard theme - use zlib,6 compression in some examples, fixes #275 - add missing rename usage to docs, closes #279 - include the help offered by borg help in the usage docs, fixes #293 - include a list of major changes compared to attic into README, fixes #224 - add OS X install instructions, #197 - more details about the release process, #260 - fix linux glibc requirement (binaries built on debian7 now) - build: move usage and API generation to setup.py - update docs about return codes, #61 - remove api docs (too much breakage on rtd) - borgbackup install + basics presentation (asciinema) - describe the current style guide in documentation - add section about debug commands - warn about not running out of space - add example for rename - improve chunker params docs, fixes #362 - minor development docs update Version 0.27.0 (2015-10-07) --------------------------- New features: - "borg upgrade" command - attic -> borg one time converter / migration, #21 - temporary hack to avoid using lots of disk space for chunks.archive.d, #235: To use it: rm -rf chunks.archive.d ; touch chunks.archive.d - respect XDG_CACHE_HOME, attic #181 - add support for arbitrary SSH commands, attic #99 - borg delete --cache-only REPO (only delete cache, not REPO), attic #123 Bug fixes: - use Debian 7 (wheezy) to build pyinstaller borgbackup binaries, fixes slow down observed when running the Centos6-built binary on Ubuntu, #222 - do not crash on empty lock.roster, fixes #232 - fix multiple issues with the cache config version check, #234 - fix segment entry header size check, attic #352 plus other error handling improvements / code deduplication there. - always give segment and offset in repo IntegrityErrors Other changes: - stop producing binary wheels, remove docs about it, #147 - docs: - add warning about prune - generate usage include files only as needed - development docs: add Vagrant section - update / improve / reformat FAQ - hint to single-file pyinstaller binaries from README Version 0.26.1 (2015-09-28) --------------------------- This is a minor update, just docs and new pyinstaller binaries. - docs update about python and binary requirements - better docs for --read-special, fix #220 - re-built the binaries, fix #218 and #213 (glibc version issue) - update web site about single-file pyinstaller binaries Note: if you did a python-based installation, there is no need to upgrade. Version 0.26.0 (2015-09-19) --------------------------- New features: - Faster cache sync (do all in one pass, remove tar/compression stuff), #163 - BORG_REPO env var to specify the default repo, #168 - read special files as if they were regular files, #79 - implement borg create --dry-run, attic issue #267 - Normalize paths before pattern matching on OS X, #143 - support OpenBSD and NetBSD (except xattrs/ACLs) - support / run tests on Python 3.5 Bug fixes: - borg mount repo: use absolute path, attic #200, attic #137 - chunker: use off_t to get 64bit on 32bit platform, #178 - initialize chunker fd to -1, so it's not equal to STDIN_FILENO (0) - fix reaction to "no" answer at delete repo prompt, #182 - setup.py: detect lz4.h header file location - to support python < 3.2.4, add less buggy argparse lib from 3.2.6 (#194) - fix for obtaining ``char *`` from temporary Python value (old code causes a compile error on Mint 17.2) - llfuse 0.41 install troubles on some platforms, require < 0.41 (UnicodeDecodeError exception due to non-ascii llfuse setup.py) - cython code: add some int types to get rid of unspecific python add / subtract operations (avoid ``undefined symbol FPE_``... error on some platforms) - fix verbose mode display of stdin backup - extract: warn if a include pattern never matched, fixes #209, implement counters for Include/ExcludePatterns - archive names with slashes are invalid, attic issue #180 - chunker: add a check whether the POSIX_FADV_DONTNEED constant is defined - fixes building on OpenBSD. Other changes: - detect inconsistency / corruption / hash collision, #170 - replace versioneer with setuptools_scm, #106 - docs: - pkg-config is needed for llfuse installation - be more clear about pruning, attic issue #132 - unit tests: - xattr: ignore security.selinux attribute showing up - ext3 seems to need a bit more space for a sparse file - do not test lzma level 9 compression (avoid MemoryError) - work around strange mtime granularity issue on netbsd, fixes #204 - ignore st_rdev if file is not a block/char device, fixes #203 - stay away from the setgid and sticky mode bits - use Vagrant to do easy cross-platform testing (#196), currently: - Debian 7 "wheezy" 32bit, Debian 8 "jessie" 64bit - Ubuntu 12.04 32bit, Ubuntu 14.04 64bit - Centos 7 64bit - FreeBSD 10.2 64bit - OpenBSD 5.7 64bit - NetBSD 6.1.5 64bit - Darwin (OS X Yosemite) Version 0.25.0 (2015-08-29) --------------------------- Compatibility notes: - lz4 compression library (liblz4) is a new requirement (#156) - the new compression code is very compatible: as long as you stay with zlib compression, older borg releases will still be able to read data from a repo/archive made with the new code (note: this is not the case for the default "none" compression, use "zlib,0" if you want a "no compression" mode that can be read by older borg). Also the new code is able to read repos and archives made with older borg versions (for all zlib levels 0..9). Deprecations: - --compression N (with N being a number, as in 0.24) is deprecated. We keep the --compression 0..9 for now to not break scripts, but it is deprecated and will be removed later, so better fix your scripts now: --compression 0 (as in 0.24) is the same as --compression zlib,0 (now). BUT: if you do not want compression, you rather want --compression none (which is the default). --compression 1 (in 0.24) is the same as --compression zlib,1 (now) --compression 9 (in 0.24) is the same as --compression zlib,9 (now) New features: - create --compression none (default, means: do not compress, just pass through data "as is". this is more efficient than zlib level 0 as used in borg 0.24) - create --compression lz4 (super-fast, but not very high compression) - create --compression zlib,N (slower, higher compression, default for N is 6) - create --compression lzma,N (slowest, highest compression, default N is 6) - honor the nodump flag (UF_NODUMP) and do not backup such items - list --short just outputs a simple list of the files/directories in an archive Bug fixes: - fixed --chunker-params parameter order confusion / malfunction, fixes #154 - close fds of segments we delete (during compaction) - close files which fell out the lrucache - fadvise DONTNEED now is only called for the byte range actually read, not for the whole file, fixes #158. - fix issue with negative "all archives" size, fixes #165 - restore_xattrs: ignore if setxattr fails with EACCES, fixes #162 Other changes: - remove fakeroot requirement for tests, tests run faster without fakeroot (test setup does not fail any more without fakeroot, so you can run with or without fakeroot), fixes #151 and #91. - more tests for archiver - recover_segment(): don't assume we have an fd for segment - lrucache refactoring / cleanup, add dispose function, py.test tests - generalize hashindex code for any key length (less hardcoding) - lock roster: catch file not found in remove() method and ignore it - travis CI: use requirements file - improved docs: - replace hack for llfuse with proper solution (install libfuse-dev) - update docs about compression - update development docs about fakeroot - internals: add some words about lock files / locking system - support: mention BountySource and for what it can be used - theme: use a lighter green - add pypi, wheel, dist package based install docs - split install docs into system-specific preparations and generic instructions Version 0.24.0 (2015-08-09) --------------------------- Incompatible changes (compared to 0.23): - borg now always issues --umask NNN option when invoking another borg via ssh on the repository server. By that, it's making sure it uses the same umask for remote repos as for local ones. Because of this, you must upgrade both server and client(s) to 0.24. - the default umask is 077 now (if you do not specify via --umask) which might be a different one as you used previously. The default umask avoids that you accidentally give access permissions for group and/or others to files created by borg (e.g. the repository). Deprecations: - "--encryption passphrase" mode is deprecated, see #85 and #97. See the new "--encryption repokey" mode for a replacement. New features: - borg create --chunker-params ... to configure the chunker, fixes #16 (attic #302, attic #300, and somehow also #41). This can be used to reduce memory usage caused by chunk management overhead, so borg does not create a huge chunks index/repo index and eats all your RAM if you back up lots of data in huge files (like VM disk images). See docs/misc/create_chunker-params.txt for more information. - borg info now reports chunk counts in the chunk index. - borg create --compression 0..9 to select zlib compression level, fixes #66 (attic #295). - borg init --encryption repokey (to store the encryption key into the repo), fixes #85 - improve at-end error logging, always log exceptions and set exit_code=1 - LoggedIO: better error checks / exceptions / exception handling - implement --remote-path to allow non-default-path borg locations, #125 - implement --umask M and use 077 as default umask for better security, #117 - borg check: give a named single archive to it, fixes #139 - cache sync: show progress indication - cache sync: reimplement the chunk index merging in C Bug fixes: - fix segfault that happened for unreadable files (chunker: n needs to be a signed size_t), #116 - fix the repair mode, #144 - repo delete: add destroy to allowed rpc methods, fixes issue #114 - more compatible repository locking code (based on mkdir), maybe fixes #92 (attic #317, attic #201). - better Exception msg if no Borg is installed on the remote repo server, #56 - create a RepositoryCache implementation that can cope with >2GiB, fixes attic #326. - fix Traceback when running check --repair, attic #232 - clarify help text, fixes #73. - add help string for --no-files-cache, fixes #140 Other changes: - improved docs: - added docs/misc directory for misc. writeups that won't be included "as is" into the html docs. - document environment variables and return codes (attic #324, attic #52) - web site: add related projects, fix web site url, IRC #borgbackup - Fedora/Fedora-based install instructions added to docs - Cygwin-based install instructions added to docs - updated AUTHORS - add FAQ entries about redundancy / integrity - clarify that borg extract uses the cwd as extraction target - update internals doc about chunker params, memory usage and compression - added docs about development - add some words about resource usage in general - document how to backup a raw disk - add note about how to run borg from virtual env - add solutions for (ll)fuse installation problems - document what borg check does, fixes #138 - reorganize borgbackup.github.io sidebar, prev/next at top - deduplicate and refactor the docs / README.rst - use borg-tmp as prefix for temporary files / directories - short prune options without "keep-" are deprecated, do not suggest them - improved tox configuration - remove usage of unittest.mock, always use mock from pypi - use entrypoints instead of scripts, for better use of the wheel format and modern installs - add requirements.d/development.txt and modify tox.ini - use travis-ci for testing based on Linux and (new) OS X - use coverage.py, pytest-cov and codecov.io for test coverage support I forgot to list some stuff already implemented in 0.23.0, here they are: New features: - efficient archive list from manifest, meaning a big speedup for slow repo connections and "list ", "delete ", "prune" (attic #242, attic #167) - big speedup for chunks cache sync (esp. for slow repo connections), fixes #18 - hashindex: improve error messages Other changes: - explicitly specify binary mode to open binary files - some easy micro optimizations Version 0.23.0 (2015-06-11) --------------------------- Incompatible changes (compared to attic, fork related): - changed sw name and cli command to "borg", updated docs - package name (and name in urls) uses "borgbackup" to have fewer collisions - changed repo / cache internal magic strings from ATTIC* to BORG*, changed cache location to .cache/borg/ - this means that it currently won't accept attic repos (see issue #21 about improving that) Bug fixes: - avoid defect python-msgpack releases, fixes attic #171, fixes attic #185 - fix traceback when trying to do unsupported passphrase change, fixes attic #189 - datetime does not like the year 10.000, fixes attic #139 - fix "info" all archives stats, fixes attic #183 - fix parsing with missing microseconds, fixes attic #282 - fix misleading hint the fuse ImportError handler gave, fixes attic #237 - check unpacked data from RPC for tuple type and correct length, fixes attic #127 - fix Repository._active_txn state when lock upgrade fails - give specific path to xattr.is_enabled(), disable symlink setattr call that always fails - fix test setup for 32bit platforms, partial fix for attic #196 - upgraded versioneer, PEP440 compliance, fixes attic #257 New features: - less memory usage: add global option --no-cache-files - check --last N (only check the last N archives) - check: sort archives in reverse time order - rename repo::oldname newname (rename repository) - create -v output more informative - create --progress (backup progress indicator) - create --timestamp (utc string or reference file/dir) - create: if "-" is given as path, read binary from stdin - extract: if --stdout is given, write all extracted binary data to stdout - extract --sparse (simple sparse file support) - extra debug information for 'fread failed' - delete (deletes whole repo + local cache) - FUSE: reflect deduplication in allocated blocks - only allow whitelisted RPC calls in server mode - normalize source/exclude paths before matching - use posix_fadvise to not spoil the OS cache, fixes attic #252 - toplevel error handler: show tracebacks for better error analysis - sigusr1 / sigint handler to print current file infos - attic PR #286 - RPCError: include the exception args we get from remote Other changes: - source: misc. cleanups, pep8, style - docs and faq improvements, fixes, updates - cleanup crypto.pyx, make it easier to adapt to other AES modes - do os.fsync like recommended in the python docs - source: Let chunker optionally work with os-level file descriptor. - source: Linux: remove duplicate os.fsencode calls - source: refactor _open_rb code a bit, so it is more consistent / regular - source: refactor indicator (status) and item processing - source: use py.test for better testing, flake8 for code style checks - source: fix tox >=2.0 compatibility (test runner) - pypi package: add python version classifiers, add FreeBSD to platforms Attic Changelog --------------- Here you can see the full list of changes between each Attic release until Borg forked from Attic: Version 0.17 ~~~~~~~~~~~~ (bugfix release, released on X) - Fix hashindex ARM memory alignment issue (#309) - Improve hashindex error messages (#298) Version 0.16 ~~~~~~~~~~~~ (bugfix release, released on May 16, 2015) - Fix typo preventing the security confirmation prompt from working (#303) - Improve handling of systems with improperly configured file system encoding (#289) - Fix "All archives" output for attic info. (#183) - More user friendly error message when repository key file is not found (#236) - Fix parsing of iso 8601 timestamps with zero microseconds (#282) Version 0.15 ~~~~~~~~~~~~ (bugfix release, released on Apr 15, 2015) - xattr: Be less strict about unknown/unsupported platforms (#239) - Reduce repository listing memory usage (#163). - Fix BrokenPipeError for remote repositories (#233) - Fix incorrect behavior with two character directory names (#265, #268) - Require approval before accessing relocated/moved repository (#271) - Require approval before accessing previously unknown unencrypted repositories (#271) - Fix issue with hash index files larger than 2GB. - Fix Python 3.2 compatibility issue with noatime open() (#164) - Include missing pyx files in dist files (#168) Version 0.14 ~~~~~~~~~~~~ (feature release, released on Dec 17, 2014) - Added support for stripping leading path segments (#95) "attic extract --strip-segments X" - Add workaround for old Linux systems without acl_extended_file_no_follow (#96) - Add MacPorts' path to the default openssl search path (#101) - HashIndex improvements, eliminates unnecessary IO on low memory systems. - Fix "Number of files" output for attic info. (#124) - limit create file permissions so files aren't read while restoring - Fix issue with empty xattr values (#106) Version 0.13 ~~~~~~~~~~~~ (feature release, released on Jun 29, 2014) - Fix sporadic "Resource temporarily unavailable" when using remote repositories - Reduce file cache memory usage (#90) - Faster AES encryption (utilizing AES-NI when available) - Experimental Linux, OS X and FreeBSD ACL support (#66) - Added support for backup and restore of BSDFlags (OSX, FreeBSD) (#56) - Fix bug where xattrs on symlinks were not correctly restored - Added cachedir support. CACHEDIR.TAG compatible cache directories can now be excluded using ``--exclude-caches`` (#74) - Fix crash on extreme mtime timestamps (year 2400+) (#81) - Fix Python 3.2 specific lockf issue (EDEADLK) Version 0.12 ~~~~~~~~~~~~ (feature release, released on April 7, 2014) - Python 3.4 support (#62) - Various documentation improvements a new style - ``attic mount`` now supports mounting an entire repository not only individual archives (#59) - Added option to restrict remote repository access to specific path(s): ``attic serve --restrict-to-path X`` (#51) - Include "all archives" size information in "--stats" output. (#54) - Added ``--stats`` option to ``attic delete`` and ``attic prune`` - Fixed bug where ``attic prune`` used UTC instead of the local time zone when determining which archives to keep. - Switch to SI units (Power of 1000 instead 1024) when printing file sizes Version 0.11 ~~~~~~~~~~~~ (feature release, released on March 7, 2014) - New "check" command for repository consistency checking (#24) - Documentation improvements - Fix exception during "attic create" with repeated files (#39) - New "--exclude-from" option for attic create/extract/verify. - Improved archive metadata deduplication. - "attic verify" has been deprecated. Use "attic extract --dry-run" instead. - "attic prune --hourly|daily|..." has been deprecated. Use "attic prune --keep-hourly|daily|..." instead. - Ignore xattr errors during "extract" if not supported by the filesystem. (#46) Version 0.10 ~~~~~~~~~~~~ (bugfix release, released on Jan 30, 2014) - Fix deadlock when extracting 0 sized files from remote repositories - "--exclude" wildcard patterns are now properly applied to the full path not just the file name part (#5). - Make source code endianness agnostic (#1) Version 0.9 ~~~~~~~~~~~ (feature release, released on Jan 23, 2014) - Remote repository speed and reliability improvements. - Fix sorting of segment names to ignore NFS left over files. (#17) - Fix incorrect display of time (#13) - Improved error handling / reporting. (#12) - Use fcntl() instead of flock() when locking repository/cache. (#15) - Let ssh figure out port/user if not specified so we don't override .ssh/config (#9) - Improved libcrypto path detection (#23). Version 0.8.1 ~~~~~~~~~~~~~ (bugfix release, released on Oct 4, 2013) - Fix segmentation fault issue. Version 0.8 ~~~~~~~~~~~ (feature release, released on Oct 3, 2013) - Fix xattr issue when backing up sshfs filesystems (#4) - Fix issue with excessive index file size (#6) - Support access of read only repositories. - New syntax to enable repository encryption: attic init --encryption="none|passphrase|keyfile". - Detect and abort if repository is older than the cache. Version 0.7 ~~~~~~~~~~~ (feature release, released on Aug 5, 2013) - Ported to FreeBSD - Improved documentation - Experimental: Archives mountable as FUSE filesystems. - The "user." prefix is no longer stripped from xattrs on Linux Version 0.6.1 ~~~~~~~~~~~~~ (bugfix release, released on July 19, 2013) - Fixed an issue where mtime was not always correctly restored. Version 0.6 ~~~~~~~~~~~ First public release on July 9, 2013 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/conf.py0000644000076500000240000002032114601576577014465 0ustar00twstaff# documentation build configuration file, created by # sphinx-quickstart on Sat Sep 10 18:18:25 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. import sys, os sys.path.insert(0, os.path.abspath('../src')) from borg import __version__ as sw_version # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Borg - Deduplicating Archiver' copyright = '2010-2014 Jonas Borgström, 2015-2023 The Borg Collective (see AUTHORS file)' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. split_char = '+' if '+' in sw_version else '-' version = sw_version.split(split_char)[0] # The full version, including alpha/beta/rc tags. release = version suppress_warnings = ['image.nonlocal_uri'] # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%Y-%m-%d' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # The Borg docs contain no or very little Python docs. # Thus, the primary domain is rst. primary_domain = 'rst' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. import guzzle_sphinx_theme html_theme_path = guzzle_sphinx_theme.html_theme_path() html_theme = 'guzzle_sphinx_theme' def set_rst_settings(app): app.env.settings.update({ 'field_name_limit': 0, 'option_limit': 0, }) def setup(app): app.add_css_file('css/borg.css') app.connect('builder-inited', set_rst_settings) # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { 'project_nav_name': 'Borg %s' % version, } # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = ['_themes'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = '_static/logo.svg' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. html_favicon = '_static/favicon.ico' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['borg_theme'] html_extra_path = ['../src/borg/paperkey.html'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%Y-%m-%d' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. html_use_smartypants = True smartquotes_action = 'qe' # no D in there means "do not transform -- and ---" # Custom sidebar templates, maps document names to template names. html_sidebars = { '**': ['logo-text.html', 'searchbox.html', 'globaltoc.html'], } # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. html_use_index = False # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. html_show_sphinx = False # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. html_show_copyright = False # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'borgdoc' # -- Options for LaTeX output -------------------------------------------------- # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('book', 'Borg.tex', 'Borg Documentation', 'The Borg Collective', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. latex_logo = '_static/logo.pdf' latex_elements = { 'papersize': 'a4paper', 'pointsize': '10pt', 'figure_align': 'H', } # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. latex_show_urls = 'footnote' # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. latex_appendices = [ 'support', 'resources', 'changes', 'authors', ] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('usage', 'borg', 'BorgBackup is a deduplicating backup program with optional compression and authenticated encryption.', ['The Borg Collective (see AUTHORS file)'], 1), ] extensions = [ 'sphinx.ext.extlinks', 'sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', ] extlinks = { 'issue': ('https://github.com/borgbackup/borg/issues/%s', '#%s'), 'targz_url': ('https://pypi.python.org/packages/source/b/borgbackup/%%s-%s.tar.gz' % version, None), } ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1711734324.105678 borgbackup-1.2.8/docs/deployment/0000755000076500000240000000000014601577064015340 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/deployment/automated-local.rst0000644000076500000240000001715314601576577021164 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none Automated backups to a local hard drive ======================================= This guide shows how to automate backups to a hard drive directly connected to your computer. If a backup hard drive is connected, backups are automatically started, and the drive shut-down and disconnected when they are done. This guide is written for a Linux-based operating system and makes use of systemd and udev. Overview -------- An udev rule is created to trigger on the addition of block devices. The rule contains a tag that triggers systemd to start a oneshot service. The oneshot service executes a script in the standard systemd service environment, which automatically captures stdout/stderr and logs it to the journal. The script mounts the added block device, if it is a registered backup drive, and creates backups on it. When done, it optionally unmounts the file system and spins the drive down, so that it may be physically disconnected. Configuring the system ---------------------- First, create the ``/etc/backups`` directory (as root). All configuration goes into this directory. Find out the ID of the partition table of your backup disk (here assumed to be /dev/sdz): lsblk --fs -o +PTUUID /dev/sdz Then, create ``/etc/backups/40-backup.rules`` with the following content (all on one line):: ACTION=="add", SUBSYSTEM=="block", ENV{ID_PART_TABLE_UUID}=="", TAG+="systemd", ENV{SYSTEMD_WANTS}="automatic-backup.service" The "systemd" tag in conjunction with the SYSTEMD_WANTS environment variable has systemd launch the "automatic-backup" service, which we will create next, as the ``/etc/backups/automatic-backup.service`` file: .. code-block:: ini [Service] Type=oneshot ExecStart=/etc/backups/run.sh Now, create the main backup script, ``/etc/backups/run.sh``. Below is a template, modify it to suit your needs (e.g. more backup sets, dumping databases etc.). .. code-block:: bash #!/bin/bash -ue # The udev rule is not terribly accurate and may trigger our service before # the kernel has finished probing partitions. Sleep for a bit to ensure # the kernel is done. # # This can be avoided by using a more precise udev rule, e.g. matching # a specific hardware path and partition. sleep 5 # # Script configuration # # The backup partition is mounted there MOUNTPOINT=/mnt/backup # This is the location of the Borg repository TARGET=$MOUNTPOINT/borg-backups/backup.borg # Archive name schema DATE=$(date --iso-8601)-$(hostname) # This is the file that will later contain UUIDs of registered backup drives DISKS=/etc/backups/backup.disks # Find whether the connected block device is a backup drive for uuid in $(lsblk --noheadings --list --output uuid) do if grep --quiet --fixed-strings $uuid $DISKS; then break fi uuid= done if [ ! $uuid ]; then echo "No backup disk found, exiting" exit 0 fi echo "Disk $uuid is a backup disk" partition_path=/dev/disk/by-uuid/$uuid # Mount file system if not already done. This assumes that if something is already # mounted at $MOUNTPOINT, it is the backup drive. It won't find the drive if # it was mounted somewhere else. findmnt $MOUNTPOINT >/dev/null || mount $partition_path $MOUNTPOINT drive=$(lsblk --inverse --noheadings --list --paths --output name $partition_path | head --lines 1) echo "Drive path: $drive" # # Create backups # # Options for borg create BORG_OPTS="--stats --one-file-system --compression lz4 --checkpoint-interval 86400" # Set BORG_PASSPHRASE or BORG_PASSCOMMAND somewhere around here, using export, # if encryption is used. # No one can answer if Borg asks these questions, it is better to just fail quickly # instead of hanging. export BORG_RELOCATED_REPO_ACCESS_IS_OK=no export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no # Log Borg version borg --version echo "Starting backup for $DATE" # This is just an example, change it however you see fit borg create $BORG_OPTS \ --exclude root/.cache \ --exclude var/lib/docker/devicemapper \ $TARGET::$DATE-$$-system \ / /boot # /home is often a separate partition / file system. # Even if it isn't (add --exclude /home above), it probably makes sense # to have /home in a separate archive. borg create $BORG_OPTS \ --exclude 'sh:home/*/.cache' \ $TARGET::$DATE-$$-home \ /home/ echo "Completed backup for $DATE" # Just to be completely paranoid sync if [ -f /etc/backups/autoeject ]; then umount $MOUNTPOINT hdparm -Y $drive fi if [ -f /etc/backups/backup-suspend ]; then systemctl suspend fi Create the ``/etc/backups/autoeject`` file to have the script automatically eject the drive after creating the backup. Rename the file to something else (e.g. ``/etc/backup/autoeject-no``) when you want to do something with the drive after creating backups (e.g running check). Create the ``/etc/backups/backup-suspend`` file if the machine should suspend after completing the backup. Don't forget to physically disconnect the device before resuming, otherwise you'll enter a cycle. You can also add an option to power down instead. Create an empty ``/etc/backups/backup.disks`` file, you'll register your backup drives there. The last part is to actually enable the udev rules and services: .. code-block:: bash ln -s /etc/backups/40-backup.rules /etc/udev/rules.d/40-backup.rules ln -s /etc/backups/automatic-backup.service /etc/systemd/system/automatic-backup.service systemctl daemon-reload udevadm control --reload Adding backup hard drives ------------------------- Connect your backup hard drive. Format it, if not done already. Find the UUID of the file system that backups should be stored on:: lsblk -o+uuid,label Note the UUID into the ``/etc/backup/backup.disks`` file. Mount the drive to /mnt/backup. Initialize a Borg repository at the location indicated by ``TARGET``:: borg init --encryption ... /mnt/backup/borg-backups/backup.borg Unmount and reconnect the drive, or manually start the ``automatic-backup`` service to start the first backup:: systemctl start --no-block automatic-backup See backup logs using journalctl:: journalctl -fu automatic-backup [-n number-of-lines] Security considerations ----------------------- The script as shown above will mount any file system with an UUID listed in ``/etc/backup/backup.disks``. The UUID check is a safety / annoyance-reduction mechanism to keep the script from blowing up whenever a random USB thumb drive is connected. It is not meant as a security mechanism. Mounting file systems and reading repository data exposes additional attack surfaces (kernel file system drivers, possibly user space services and Borg itself). On the other hand, someone standing right next to your computer can attempt a lot of attacks, most of which are easier to do than e.g. exploiting file systems (installing a physical key logger, DMA attacks, stealing the machine, ...). Borg ensures that backups are not created on random drives that "just happen" to contain a Borg repository. If an unknown unencrypted repository is encountered, then the script aborts (BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no). Backups are only created on hard drives that contain a Borg repository that is either known (by ID) to your machine or you are using encryption and the passphrase of the repository has to match the passphrase supplied to Borg. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/deployment/central-backup-server.rst0000644000076500000240000001704314601576577022306 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none Central repository server with Ansible or Salt ============================================== This section will give an example how to setup a borg repository server for multiple clients. Machines -------- There are multiple machines used in this section and will further be named by their respective fully qualified domain name (fqdn). * The backup server: `backup01.srv.local` * The clients: - John Doe's desktop: `johndoe.clnt.local` - Webserver 01: `web01.srv.local` - Application server 01: `app01.srv.local` User and group -------------- The repository server needs to have only one UNIX user for all the clients. Recommended user and group with additional settings: * User: `backup` * Group: `backup` * Shell: `/bin/bash` (or other capable to run the `borg serve` command) * Home: `/home/backup` Most clients shall initiate a backup from the root user to catch all users, groups and permissions (e.g. when backing up `/home`). Folders ------- The following folder tree layout is suggested on the repository server: * User home directory, /home/backup * Repositories path (storage pool): /home/backup/repos * Clients restricted paths (`/home/backup/repos/`): - johndoe.clnt.local: `/home/backup/repos/johndoe.clnt.local` - web01.srv.local: `/home/backup/repos/web01.srv.local` - app01.srv.local: `/home/backup/repos/app01.srv.local` Restrictions ------------ Borg is instructed to restrict clients into their own paths: ``borg serve --restrict-to-path /home/backup/repos/`` The client will be able to access any file or subdirectory inside of ``/home/backup/repos/`` but no other directories. You can allow a client to access several separate directories by passing multiple ``--restrict-to-path`` flags, for instance: ``borg serve --restrict-to-path /home/backup/repos/ --restrict-to-path /home/backup/repos/``, which could make sense if multiple machines belong to one person which should then have access to all the backups of their machines. There is only one ssh key per client allowed. Keys are added for ``johndoe.clnt.local``, ``web01.srv.local`` and ``app01.srv.local``. But they will access the backup under only one UNIX user account as: ``backup@backup01.srv.local``. Every key in ``$HOME/.ssh/authorized_keys`` has a forced command and restrictions applied as shown below: :: command="cd /home/backup/repos/; borg serve --restrict-to-path /home/backup/repos/", restrict .. note:: The text shown above needs to be written on a single line! The options which are added to the key will perform the following: 1. Change working directory 2. Run ``borg serve`` restricted to the client base path 3. Restrict ssh and do not allow stuff which imposes a security risk Due to the ``cd`` command we use, the server automatically changes the current working directory. Then client doesn't need to have knowledge of the absolute or relative remote repository path and can directly access the repositories at ``@:``. .. note:: The setup above ignores all client given commandline parameters which are normally appended to the `borg serve` command. Client ------ The client needs to initialize the `pictures` repository like this: :: borg init backup@backup01.srv.local:pictures Or with the full path (should actually never be used, as only for demonstrational purposes). The server should automatically change the current working directory to the `` folder. :: borg init backup@backup01.srv.local:/home/backup/repos/johndoe.clnt.local/pictures When `johndoe.clnt.local` tries to access a not restricted path the following error is raised. John Doe tries to backup into the Web 01 path: :: borg init backup@backup01.srv.local:/home/backup/repos/web01.srv.local/pictures :: ~~~ SNIP ~~~ Remote: borg.remote.PathNotAllowed: /home/backup/repos/web01.srv.local/pictures ~~~ SNIP ~~~ Repository path not allowed Ansible ------- Ansible takes care of all the system-specific commands to add the user, create the folder, install and configure software. :: - hosts: backup01.srv.local vars: user: backup group: backup home: /home/backup pool: "{{ home }}/repos" auth_users: - host: johndoe.clnt.local key: "{{ lookup('file', '/path/to/keys/johndoe.clnt.local.pub') }}" - host: web01.clnt.local key: "{{ lookup('file', '/path/to/keys/web01.clnt.local.pub') }}" - host: app01.clnt.local key: "{{ lookup('file', '/path/to/keys/app01.clnt.local.pub') }}" tasks: - package: name=borg state=present - group: name="{{ group }}" state=present - user: name="{{ user }}" shell=/bin/bash home="{{ home }}" createhome=yes group="{{ group }}" groups= state=present - file: path="{{ home }}" owner="{{ user }}" group="{{ group }}" mode=0700 state=directory - file: path="{{ home }}/.ssh" owner="{{ user }}" group="{{ group }}" mode=0700 state=directory - file: path="{{ pool }}" owner="{{ user }}" group="{{ group }}" mode=0700 state=directory - authorized_key: user="{{ user }}" key="{{ item.key }}" key_options='command="cd {{ pool }}/{{ item.host }};borg serve --restrict-to-path {{ pool }}/{{ item.host }}",restrict' with_items: "{{ auth_users }}" - file: path="{{ home }}/.ssh/authorized_keys" owner="{{ user }}" group="{{ group }}" mode=0600 state=file - file: path="{{ pool }}/{{ item.host }}" owner="{{ user }}" group="{{ group }}" mode=0700 state=directory with_items: "{{ auth_users }}" Salt ---- This is a configuration similar to the one above, configured to be deployed with Salt running on a Debian system. :: Install borg backup from pip: pkg.installed: - pkgs: - python3 - python3-dev - python3-pip - python-virtualenv - libssl-dev - openssl - libacl1-dev - libacl1 - build-essential - libfuse-dev - fuse - pkg-config pip.installed: - pkgs: ["borgbackup"] - bin_env: /usr/bin/pip3 Setup backup user: user.present: - name: backup - fullname: Backup User - home: /home/backup - shell: /bin/bash # CAUTION! # If you change the ssh command= option below, it won't necessarily get pushed to the backup # server correctly unless you delete the ~/.ssh/authorized_keys file and re-create it! {% for host in backupclients %} Give backup access to {{host}}: ssh_auth.present: - user: backup - source: salt://conf/ssh-pubkeys/{{host}}-backup.id_ecdsa.pub - options: - command="cd /home/backup/repos/{{host}}; borg serve --restrict-to-path /home/backup/repos/{{host}}" - restrict {% endfor %} Enhancements ------------ As this section only describes a simple and effective setup it could be further enhanced when supporting (a limited set) of client supplied commands. A wrapper for starting `borg serve` could be written. Or borg itself could be enhanced to autodetect it runs under SSH by checking the `SSH_ORIGINAL_COMMAND` environment variable. This is left open for future improvements. When extending ssh autodetection in borg no external wrapper script is necessary and no other interpreter or application has to be deployed. See also -------- * `SSH Daemon manpage `_ * `Ansible `_ * `Salt `_ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/deployment/hosting-repositories.rst0000644000076500000240000000655514601576577022315 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none .. _hosting_repositories: Hosting repositories ==================== This sections shows how to securely provide repository storage for users. Optionally, each user can have a storage quota. Repositories are accessed through SSH. Each user of the service should have her own login which is only able to access the user's files. Technically it would be possible to have multiple users share one login, however, separating them is better. Separate logins increase isolation and are thus an additional layer of security and safety for both the provider and the users. For example, if a user manages to breach ``borg serve`` then she can only damage her own data (assuming that the system does not have further vulnerabilities). Use the standard directory structure of the operating system. Each user is assigned a home directory and repositories of the user reside in her home directory. The following ``~user/.ssh/authorized_keys`` file is the most important piece for a correct deployment. It allows the user to login via their public key (which must be provided by the user), and restricts SSH access to safe operations only. :: command="borg serve --restrict-to-repository /home//repository",restrict .. note:: The text shown above needs to be written on a **single** line! .. warning:: If this file should be automatically updated (e.g. by a web console), pay **utmost attention** to sanitizing user input. Strip all whitespace around the user-supplied key, ensure that it **only** contains ASCII with no control characters and that it consists of three parts separated by a single space. Ensure that no newlines are contained within the key. The ``restrict`` keyword enables all restrictions, i.e. disables port, agent and X11 forwarding, as well as disabling PTY allocation and execution of ~/.ssh/rc. If any future restriction capabilities are added to authorized_keys files they will be included in this set. The ``command`` keyword forces execution of the specified command line upon login. This must be ``borg serve``. The ``--restrict-to-repository`` option permits access to exactly **one** repository. It can be given multiple times to permit access to more than one repository. The repository may not exist yet; it can be initialized by the user, which allows for encryption. **Storage quotas** can be enabled by adding the ``--storage-quota`` option to the ``borg serve`` command line:: restrict,command="borg serve --storage-quota 20G ..." ... The storage quotas of repositories are completely independent. If a client is able to access multiple repositories, each repository can be filled to the specified quota. If storage quotas are used, ensure that all deployed Borg releases support storage quotas. Refer to :ref:`internals_storage_quota` for more details on storage quotas. **Specificities: Append-only repositories** Running ``borg init`` via a ``borg serve --append-only`` server will **not** create a repository that is configured to be append-only by its repository config. But, ``--append-only`` arguments in ``authorized_keys`` will override the repository config, therefore append-only mode can be enabled on a key by key basis. Refer to the `sshd(8) `_ man page for more details on SSH options. See also :ref:`borg_serve` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/deployment/image-backup.rst0000644000076500000240000001575214601576577020441 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none Backing up entire disk images ============================= Backing up disk images can still be efficient with Borg because its `deduplication`_ technique makes sure only the modified parts of the file are stored. Borg also has optional simple sparse file support for extract. It is of utmost importancy to pin down the disk you want to backup. You need to use the SERIAL for that. Use: .. code-block:: bash # You can find the short disk serial by: # udevadm info --query=property --name=nvme1n1 | grep ID_SERIAL_SHORT | cut -d '=' -f 2 DISK_SERIAL="7VS0224F" DISK_ID=$(readlink -f /dev/disk/by-id/*"${DISK_SERIAL}") # Returns /dev/nvme1n1 mapfile -t PARTITIONS < <(lsblk -o NAME,TYPE -p -n -l "$DISK_ID" | awk '$2 == "part" {print $1}') echo "Partitions of $DISK_ID:" echo "${PARTITIONS[@]}" echo "Disk Identifier: $DISK_ID" # Use the following line to perform a borg backup for the full disk: # borg create --read-special /path/to/repo::{now} "$DISK_ID" # Use the following to perform a borg backup for all partitions of the disk # borg create --read-special /path/to/repo::{now} "${PARTITIONS[@]}" # Example output: # Partitions of /dev/nvme1n1: # /dev/nvme1n1p1 # /dev/nvme1n1p2 # /dev/nvme1n1p3 # Disk Identifier: /dev/nvme1n1 # borg create --read-special /path/to/repo::{now} /dev/nvme1n1 # borg create --read-special /path/to/repo::{now} /dev/nvme1n1p1 /dev/nvme1n1p2 /dev/nvme1n1p3 Decreasing the size of image backups ------------------------------------ Disk images are as large as the full disk when uncompressed and might not get much smaller post-deduplication after heavy use because virtually all file systems don't actually delete file data on disk but instead delete the filesystem entries referencing the data. Therefore, if a disk nears capacity and files are deleted again, the change will barely decrease the space it takes up when compressed and deduplicated. Depending on the filesystem, there are several ways to decrease the size of a disk image: Using ntfsclone (NTFS, i.e. Windows VMs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``ntfsclone`` can only operate on filesystems with the journal cleared (i.e. turned-off machines), which somewhat limits its utility in the case of VM snapshots. However, when it can be used, its special image format is even more efficient than just zeroing and deduplicating. For backup, save the disk header and the contents of each partition:: HEADER_SIZE=$(sfdisk -lo Start $DISK | grep -A1 -P 'Start$' | tail -n1 | xargs echo) PARTITIONS=$(sfdisk -lo Device,Type $DISK | sed -e '1,/Device\s*Type/d') dd if=$DISK count=$HEADER_SIZE | borg create repo::hostname-partinfo - echo "$PARTITIONS" | grep NTFS | cut -d' ' -f1 | while read x; do PARTNUM=$(echo $x | grep -Eo "[0-9]+$") ntfsclone -so - $x | borg create repo::hostname-part$PARTNUM - done # to backup non-NTFS partitions as well: echo "$PARTITIONS" | grep -v NTFS | cut -d' ' -f1 | while read x; do PARTNUM=$(echo $x | grep -Eo "[0-9]+$") borg create --read-special repo::hostname-part$PARTNUM $x done Restoration is a similar process:: borg extract --stdout repo::hostname-partinfo | dd of=$DISK && partprobe PARTITIONS=$(sfdisk -lo Device,Type $DISK | sed -e '1,/Device\s*Type/d') borg list --format {archive}{NL} repo | grep 'part[0-9]*$' | while read x; do PARTNUM=$(echo $x | grep -Eo "[0-9]+$") PARTITION=$(echo "$PARTITIONS" | grep -E "$DISKp?$PARTNUM" | head -n1) if echo "$PARTITION" | cut -d' ' -f2- | grep -q NTFS; then borg extract --stdout repo::$x | ntfsclone -rO $(echo "$PARTITION" | cut -d' ' -f1) - else borg extract --stdout repo::$x | dd of=$(echo "$PARTITION" | cut -d' ' -f1) fi done .. note:: When backing up a disk image (as opposed to a real block device), mount it as a loopback image to use the above snippets:: DISK=$(losetup -Pf --show /path/to/disk/image) # do backup as shown above losetup -d $DISK Using zerofree (ext2, ext3, ext4) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``zerofree`` works similarly to ntfsclone in that it zeros out unused chunks of the FS, except it works in place, zeroing the original partition. This makes the backup process a bit simpler:: sfdisk -lo Device,Type $DISK | sed -e '1,/Device\s*Type/d' | grep Linux | cut -d' ' -f1 | xargs -n1 zerofree borg create --read-special repo::hostname-disk $DISK Because the partitions were zeroed in place, restoration is only one command:: borg extract --stdout repo::hostname-disk | dd of=$DISK .. note:: The "traditional" way to zero out space on a partition, especially one already mounted, is to simply ``dd`` from ``/dev/zero`` to a temporary file and delete it. This is ill-advised for the reasons mentioned in the ``zerofree`` man page: - it is slow - it makes the disk image (temporarily) grow to its maximal extent - it (temporarily) uses all free space on the disk, so other concurrent write actions may fail. Virtual machines ---------------- If you use non-snapshotting backup tools like Borg to back up virtual machines, then the VMs should be turned off for the duration of the backup. Backing up live VMs can (and will) result in corrupted or inconsistent backup contents: a VM image is just a regular file to Borg with the same issues as regular files when it comes to concurrent reading and writing from the same file. For backing up live VMs use filesystem snapshots on the VM host, which establishes crash-consistency for the VM images. This means that with most file systems (that are journaling) the FS will always be fine in the backup (but may need a journal replay to become accessible). Usually this does not mean that file *contents* on the VM are consistent, since file contents are normally not journaled. Notable exceptions are ext4 in data=journal mode, ZFS and btrfs (unless nodatacow is used). Applications designed with crash-consistency in mind (most relational databases like PostgreSQL, SQLite etc. but also for example Borg repositories) should always be able to recover to a consistent state from a backup created with crash-consistent snapshots (even on ext4 with data=writeback or XFS). Other applications may require a lot of work to reach application-consistency; it's a broad and complex issue that cannot be explained in entirety here. Hypervisor snapshots capturing most of the VM's state can also be used for backups and can be a better alternative to pure file system based snapshots of the VM's disk, since no state is lost. Depending on the application this can be the easiest and most reliable way to create application-consistent backups. Borg doesn't intend to address these issues due to their huge complexity and platform/software dependency. Combining Borg with the mechanisms provided by the platform (snapshots, hypervisor features) will be the best approach to start tackling them. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/deployment/pull-backup.rst0000644000076500000240000004217714601576577020334 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none .. _pull_backup: ======================= Backing up in pull mode ======================= Typically the borg client connects to a backup server using SSH as a transport when initiating a backup. This is referred to as push mode. If you however require the backup server to initiate the connection or prefer it to initiate the backup run, one of the following workarounds is required to allow such a pull mode setup. A common use case for pull mode is to backup a remote server to a local personal computer. SSHFS ===== Assuming you have a pull backup system set up with borg, where a backup server pulls the data from the target via SSHFS. In this mode, the backup client's file system is mounted remotely on the backup server. Pull mode is even possible if the SSH connection must be established by the client via a remote tunnel. Other network file systems like NFS or SMB could be used as well, but SSHFS is very simple to set up and probably the most secure one. There are some restrictions caused by SSHFS. For example, unless you define UID and GID mappings when mounting via ``sshfs``, owners and groups of the mounted file system will probably change, and you may not have access to those files if BorgBackup is not run with root privileges. SSHFS is a FUSE file system and uses the SFTP protocol, so there may be also other unsupported features that the actual implementations of sshfs, libfuse and sftp on the backup server do not support, like file name encodings, ACLs, xattrs or flags. So there is no guarantee that you are able to restore a system completely in every aspect from such a backup. .. warning:: To mount the client's root file system you will need root access to the client. This contradicts to the usual threat model of BorgBackup, where clients don't need to trust the backup server (data is encrypted). In pull mode the server (when logged in as root) could cause unlimited damage to the client. Therefore, pull mode should be used only from servers you do fully trust! .. warning:: Additionally, while being chrooted into the client's root file system, code from the client will be executed. Thus, you should only do that when fully trusting the client. .. warning:: The chroot method was chosen to get the right user and group name-id mappings, assuming they only come from files (/etc/passwd and group). This assumption might be wrong, e.g. if users/groups also come from ldap or other providers. Thus, it might be better to use ``--numeric-ids`` and not archive any user or group names (but just the numeric IDs) and not use chroot. Creating a backup ----------------- Generally, in a pull backup situation there is no direct way for borg to know the client's original UID:GID name mapping of files, because Borg would use ``/etc/passwd`` and ``/etc/group`` of the backup server to map the names. To derive the right names, Borg needs to have access to the client's passwd and group files and use them in the backup process. The solution to this problem is chrooting into an sshfs mounted directory. In this example the whole client root file system is mounted. We use the stand-alone BorgBackup executable and copy it into the mounted file system to make Borg available after entering chroot; this can be skipped if Borg is already installed on the client. :: # Mount client root file system. mkdir /tmp/sshfs sshfs root@host:/ /tmp/sshfs # Mount BorgBackup repository inside it. mkdir /tmp/sshfs/borgrepo mount --bind /path/to/repo /tmp/sshfs/borgrepo # Make borg executable available. cp /usr/local/bin/borg /tmp/sshfs/usr/local/bin/borg # Mount important system directories and enter chroot. cd /tmp/sshfs for i in dev proc sys; do mount --bind /$i $i; done chroot /tmp/sshfs Now we are on the backup system but inside a chroot with the client's root file system. We have a copy of Borg binary in ``/usr/local/bin`` and the repository in ``/borgrepo``. Borg will back up the client's user/group names, and we can create the backup, retaining the original paths, excluding the repository: :: borg create --exclude borgrepo --files-cache ctime,size /borgrepo::archive / For the sake of simplicity only ``borgrepo`` is excluded here. You may want to set up an exclude file with additional files and folders to be excluded. Also note that we have to modify Borg's file change detection behaviour – SSHFS cannot guarantee stable inode numbers, so we have to supply the ``--files-cache`` option. Finally, we need to exit chroot, unmount all the stuff and clean up: :: exit # exit chroot rm /tmp/sshfs/usr/local/bin/borg cd /tmp/sshfs for i in dev proc sys borgrepo; do umount ./$i; done rmdir borgrepo cd ~ umount /tmp/sshfs rmdir /tmp/sshfs Thanks to secuser on IRC for this how-to! Restore methods --------------- The counterpart of a pull backup is a push restore. Depending on the type of restore – full restore or partial restore – there are different methods to make sure the correct IDs are restored. Partial restore ~~~~~~~~~~~~~~~ In case of a partial restore, using the archived UIDs/GIDs might lead to wrong results if the name-to-ID mapping on the target system has changed compared to backup time (might be the case e.g. for a fresh OS install). The workaround again is chrooting into an sshfs mounted directory, so Borg is able to map the user/group names of the backup files to the actual IDs on the client. This example is similar to the backup above – only the Borg command is different: :: # Mount client root file system. mkdir /tmp/sshfs sshfs root@host:/ /tmp/sshfs # Mount BorgBackup repository inside it. mkdir /tmp/sshfs/borgrepo mount --bind /path/to/repo /tmp/sshfs/borgrepo # Make borg executable available. cp /usr/local/bin/borg /tmp/sshfs/usr/local/bin/borg # Mount important system directories and enter chroot. cd /tmp/sshfs for i in dev proc sys; do mount --bind /$i $i; done chroot /tmp/sshfs Now we can run :: borg extract /borgrepo::archive PATH to partially restore whatever we like. Finally, do the clean-up: :: exit # exit chroot rm /tmp/sshfs/usr/local/bin/borg cd /tmp/sshfs for i in dev proc sys borgrepo; do umount ./$i; done rmdir borgrepo cd ~ umount /tmp/sshfs rmdir /tmp/sshfs Full restore ~~~~~~~~~~~~ When doing a full restore, we restore all files (including the ones containing the ID-to-name mapping, ``/etc/passwd`` and ``/etc/group``). Everything will be consistent automatically if we restore the numeric IDs stored in the archive. So there is no need for a chroot environment; we just mount the client file system and extract a backup, utilizing the ``--numeric-ids`` option: :: sshfs root@host:/ /mnt/sshfs cd /mnt/sshfs borg extract --numeric-ids /path/to/repo::archive cd ~ umount /mnt/sshfs Simple (lossy) full restore ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Using ``borg export-tar`` it is possible to stream a backup to the client and directly extract it without the need of mounting with SSHFS: :: borg export-tar /path/to/repo::archive - | ssh root@host 'tar -C / -x' Note that in this scenario the tar format is the limiting factor – it cannot restore all the advanced features that BorgBackup supports. See :ref:`borg_export-tar` for limitations. socat ===== In this setup a SSH connection from the backup server to the client is established that uses SSH reverse port forwarding to transparently tunnel data between UNIX domain sockets on the client and server and the socat tool to connect these with the borg client and server processes, respectively. The program socat has to be available on the backup server and on the client to be backed up. When **pushing** a backup the borg client (holding the data to be backed up) connects to the backup server via ssh, starts ``borg serve`` on the backup server and communicates via standard input and output (transported via SSH) with the process on the backup server. With the help of socat this process can be reversed. The backup server will create a connection to the client (holding the data to be backed up) and will **pull** the data. In the following example *borg-server* connects to *borg-client* to pull a backup. To provide a secure setup sockets should be stored in ``/run/borg``, only accessible to the users that run the backup process. So on both systems, *borg-server* and *borg-client* the folder ``/run/borg`` has to be created:: sudo mkdir -m 0700 /run/borg On *borg-server* the socket file is opened by the user running the ``borg serve`` process writing to the repository so the user has to have read and write permissions on ``/run/borg``:: borg-server:~$ sudo chown borgs /run/borg On *borg-client* the socket file is created by ssh, so the user used to connect to *borg-client* has to have read and write permissions on ``/run/borg``:: borg-client:~$ sudo chown borgc /run/borg On *borg-server*, we have to start the command ``borg serve`` and make its standard input and output available to a unix socket:: borg-server:~$ socat UNIX-LISTEN:/run/borg/reponame.sock,fork EXEC:"borg serve --append-only --restrict-to-path /path/to/repo" Socat will wait until a connection is opened. Then socat will execute the command given, redirecting Standard Input and Output to the unix socket. The optional arguments for ``borg serve`` are not necessary but a sane default. .. note:: When used in production you may also use systemd socket-based activation instead of socat on the server side. You would wrap the ``borg serve`` command in a `service unit`_ and configure a matching `socket unit`_ to start the service whenever a client connects to the socket. .. _service unit: https://www.freedesktop.org/software/systemd/man/systemd.service.html .. _socket unit: https://www.freedesktop.org/software/systemd/man/systemd.socket.html Now we need a way to access the unix socket on *borg-client* (holding the data to be backed up), as we created the unix socket on *borg-server* Opening a SSH connection from the *borg-server* to the *borg-client* with reverse port forwarding can do this for us:: borg-server:~$ ssh -R /run/borg/reponame.sock:/run/borg/reponame.sock borgc@borg-client .. note:: As the default value of OpenSSH for ``StreamLocalBindUnlink`` is ``no``, the socket file created by sshd is not removed. Trying to connect a second time, will print a short warning, and the forwarding does **not** take place:: Warning: remote port forwarding failed for listen path /run/borg/reponame.sock When you are done, you have to manually remove the socket file, otherwise you may see an error like this when trying to execute borg commands:: Remote: YYYY/MM/DD HH:MM:SS socat[XXX] E connect(5, AF=1 "/run/borg/reponame.sock", 13): Connection refused Connection closed by remote host. Is borg working on the server? When a process opens the socket on *borg-client*, SSH will forward all data to the socket on *borg-server*. The next step is to tell borg on *borg-client* to use the unix socket to communicate with the ``borg serve`` command on *borg-server* via the socat socket instead of SSH:: borg-client:~$ export BORG_RSH="sh -c 'exec socat STDIO UNIX-CONNECT:/run/borg/reponame.sock'" The default value for ``BORG_RSH`` is ``ssh``. By default Borg uses SSH to create the connection to the backup server. Therefore Borg parses the repo URL and adds the server name (and other arguments) to the SSH command. Those arguments can not be handled by socat. We wrap the command with ``sh`` to ignore all arguments intended for the SSH command. All Borg commands can now be executed on *borg-client*. For example to create a backup execute the ``borg create`` command:: borg-client:~$ borg create ssh://borg-server/path/to/repo::archive /path_to_backup When automating backup creation, the interactive ssh session may seem inappropriate. An alternative way of creating a backup may be the following command:: borg-server:~$ ssh \ -R /run/borg/reponame.sock:/run/borg/reponame.sock \ borgc@borg-client \ borg create \ --rsh "sh -c 'exec socat STDIO UNIX-CONNECT:/run/borg/reponame.sock'" \ ssh://borg-server/path/to/repo::archive /path_to_backup \ ';' rm /run/borg/reponame.sock This command also automatically removes the socket file after the ``borg create`` command is done. ssh-agent ========= In this scenario *borg-server* initiates an SSH connection to *borg-client* and forwards the authentication agent connection. After that, it works similar to the push mode: *borg-client* initiates another SSH connection back to *borg-server* using the forwarded authentication agent connection to authenticate itself, starts ``borg serve`` and communicates with it. Using this method requires ssh access of user *borgs* to *borgc@borg-client*, where: * *borgs* is the user on the server side with read/write access to local borg repository. * *borgc* is the user on the client side with read access to files meant to be backed up. Applying this method for automated backup operations ---------------------------------------------------- Assume that the borg-client host is untrusted. Therefore we do some effort to prevent a hostile user on the borg-client side to do something harmful. In case of a fully trusted borg-client the method could be simplified. Preparing the server side ~~~~~~~~~~~~~~~~~~~~~~~~~ Do this once for each client on *borg-server* to allow *borgs* to connect itself on *borg-server* using a dedicated ssh key: :: borgs@borg-server$ install -m 700 -d ~/.ssh/ borgs@borg-server$ ssh-keygen -N '' -t rsa -f ~/.ssh/borg-client_key borgs@borg-server$ { echo -n 'command="borg serve --append-only --restrict-to-repo ~/repo",restrict '; cat ~/.ssh/borg-client_key.pub; } >> ~/.ssh/authorized_keys borgs@borg-server$ chmod 600 ~/.ssh/authorized_keys ``install -m 700 -d ~/.ssh/`` Create directory ~/.ssh with correct permissions if it does not exist yet. ``ssh-keygen -N '' -t rsa -f ~/.ssh/borg-client_key`` Create an ssh key dedicated to communication with borg-client. .. note:: Another more complex approach is using a unique ssh key for each pull operation. This is more secure as it guarantees that the key will not be used for other purposes. ``{ echo -n 'command="borg serve --append-only --restrict-to-repo ~/repo",restrict '; cat ~/.ssh/borg-client_key.pub; } >> ~/.ssh/authorized_keys`` Add borg-client's ssh public key to ~/.ssh/authorized_keys with forced command and restricted mode. The borg client is restricted to use one repo at the specified path and to append-only operation. Commands like *delete*, *prune* and *compact* have to be executed another way, for example directly on *borg-server* side or from a privileged, less restricted client (using another authorized_keys entry). ``chmod 600 ~/.ssh/authorized_keys`` Fix permissions of ~/.ssh/authorized_keys. Pull operation ~~~~~~~~~~~~~~ Initiating borg command execution from *borg-server* (e.g. init):: borgs@borg-server$ ( eval $(ssh-agent) > /dev/null ssh-add -q ~/.ssh/borg-client_key echo 'your secure borg key passphrase' | \ ssh -A -o StrictHostKeyChecking=no borgc@borg-client "BORG_PASSPHRASE=\$(cat) borg --rsh 'ssh -o StrictHostKeyChecking=no' init --encryption repokey ssh://borgs@borg-server/~/repo" kill "${SSH_AGENT_PID}" ) Parentheses around commands are needed to avoid interference with a possibly already running ssh-agent. Parentheses are not needed when using a dedicated bash process. ``eval $(ssh-agent) > /dev/null`` Run the SSH agent in the background and export related environment variables to the current bash session. ``ssh-add -q ~/.ssh/borg-client_key`` Load the SSH private key dedicated to communication with the borg-client into the SSH agent. Look at ``man 1 ssh-add`` for a more detailed explanation. .. note:: Care needs to be taken when loading keys into the SSH agent. Users on the *borg-client* having read/write permissions to the agent's UNIX-domain socket (at least borgc and root in our case) can access the agent on *borg-server* through the forwarded connection and can authenticate using any of the identities loaded into the agent (look at ``man 1 ssh`` for more detailed explanation). Therefore there are some security considerations: * Private keys loaded into the agent must not be used to enable access anywhere else. * The keys meant to be loaded into the agent must be specified explicitly, not from default locations. * The *borg-client*'s entry in *borgs@borg-server:~/.ssh/authorized_keys* must be as restrictive as possible. ``echo 'your secure borg key passphrase' | ssh -A -o StrictHostKeyChecking=no borgc@borg-client "BORG_PASSPHRASE=\$(cat) borg --rsh 'ssh -o StrictHostKeyChecking=no' init --encryption repokey ssh://borgs@borg-server/~/repo"`` Run the *borg init* command on *borg-client*. *ssh://borgs@borg-server/~/repo* refers to the repository *repo* within borgs's home directory on *borg-server*. *StrictHostKeyChecking=no* is used to automatically add host keys to *~/.ssh/known_hosts* without user intervention. ``kill "${SSH_AGENT_PID}"`` Kill ssh-agent with loaded keys when it is not needed anymore. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/deployment.rst0000644000076500000240000000051114601576577016077 0ustar00twstaff.. include:: global.rst.inc .. highlight:: none Deployment ========== This chapter details deployment strategies for the following scenarios. .. toctree:: :titlesonly: deployment/central-backup-server deployment/hosting-repositories deployment/automated-local deployment/image-backup deployment/pull-backup ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/development.rst0000644000076500000240000002746114601576577016256 0ustar00twstaff.. include:: global.rst.inc .. highlight:: bash .. _development: Development =========== This chapter will get you started with Borg development. Borg is written in Python (with a little bit of Cython and C for the performance critical parts). Contributions ------------- ... are welcome! Some guidance for contributors: - Discuss changes on the GitHub issue tracker, on IRC or on the mailing list. - Make your PRs on the ``master`` branch (see `Branching Model`_ for details). - Do clean changesets: - Focus on some topic, resist changing anything else. - Do not do style changes mixed with functional changes. - Try to avoid refactorings mixed with functional changes. - If you need to fix something after commit/push: - If there are ongoing reviews: do a fixup commit you can squash into the bad commit later. - If there are no ongoing reviews or you did not push the bad commit yet: amend the commit to include your fix or merge the fixup commit before pushing. - Have a nice, clear, typo-free commit comment. - If you fixed an issue, refer to it in your commit comment. - Follow the style guide (see below). - If you write new code, please add tests and docs for it. - Run the tests, fix any issues that come up. - Make a pull request on GitHub. - Wait for review by other developers. Branching model --------------- Borg development happens on the ``master`` branch and uses GitHub pull requests (if you don't have GitHub or don't want to use it you can send smaller patches via the borgbackup mailing list to the maintainers). Stable releases are maintained on maintenance branches named ``x.y-maint``, eg. the maintenance branch of the 1.0.x series is ``1.0-maint``. Most PRs should be filed against the ``master`` branch. Only if an issue affects **only** a particular maintenance branch a PR should be filed against it directly. While discussing / reviewing a PR it will be decided whether the change should be applied to maintenance branches. Each maintenance branch has a corresponding *backport/x.y-maint* label, which will then be applied. Changes that are typically considered for backporting: - Data loss, corruption and inaccessibility fixes. - Security fixes. - Forward-compatibility improvements. - Documentation corrections. .. rubric:: Maintainer part From time to time a maintainer will backport the changes for a maintenance branch, typically before a release or if enough changes were collected: 1. Notify others that you're doing this to avoid duplicate work. 2. Branch a backporting branch off the maintenance branch. 3. Cherry pick and backport the changes from each labelled PR, remove the label for each PR you've backported. To preserve authorship metadata, do not follow the ``git cherry-pick`` instructions to use ``git commit`` after resolving conflicts. Instead, stage conflict resolutions and run ``git cherry-pick --continue``, much like using ``git rebase``. To avoid merge issues (a cherry pick is a form of merge), use these options (similar to the ``git merge`` options used previously, the ``-x`` option adds a reference to the original commit):: git cherry-pick --strategy recursive -X rename-threshold=5% -x 4. Make a PR of the backporting branch against the maintenance branch for backport review. Mention the backported PRs in this PR, e.g.: Includes changes from #2055 #2057 #2381 This way GitHub will automatically show in these PRs where they were backported. .. rubric:: Historic model Previously (until release 1.0.10) Borg used a `"merge upwards" `_ model where most minor changes and fixes where committed to a maintenance branch (eg. 1.0-maint), and the maintenance branch(es) were regularly merged back into the main development branch. This became more and more troublesome due to merges growing more conflict-heavy and error-prone. Code and issues --------------- Code is stored on GitHub, in the `Borgbackup organization `_. `Issues `_ and `pull requests `_ should be sent there as well. See also the :ref:`support` section for more details. Style guide ----------- We generally follow `pep8 `_, with 120 columns instead of 79. We do *not* use form-feed (``^L``) characters to separate sections either. Compliance is tested automatically when you run the tests. Continuous Integration ---------------------- All pull requests go through `GitHub Actions`_, which runs the tests on misc. Python versions and on misc. platforms as well as some additional checks. .. _GitHub Actions: https://github.com/borgbackup/borg/actions Output and Logging ------------------ When writing logger calls, always use correct log level (debug only for debugging, info for informative messages, warning for warnings, error for errors, critical for critical errors/states). When directly talking to the user (e.g. Y/N questions), do not use logging, but directly output to stderr (not: stdout, it could be connected to a pipe). To control the amount and kinds of messages output emitted at info level, use flags like ``--stats`` or ``--list``, then create a topic logger for messages controlled by that flag. See ``_setup_implied_logging()`` in ``borg/archiver.py`` for the entry point to topic logging. Building a development environment ---------------------------------- First, just install borg into a virtual env :ref:`as described before `. To install some additional packages needed for running the tests, activate your virtual env and run:: pip install -r requirements.d/development.txt This project utilizes pre-commit to lint code before it is committed. Although pre-commit is installed when running the command above, the pre-commit hooks will have to be installed separately. Run this command to install the pre-commit hooks:: pre-commit install Running the tests ----------------- The tests are in the borg/testsuite package. To run all the tests, you need to have fakeroot installed. If you do not have fakeroot, you still will be able to run most tests, just leave away the `fakeroot -u` from the given command lines. To run the test suite use the following command:: fakeroot -u tox # run all tests Some more advanced examples:: # verify a changed tox.ini (run this after any change to tox.ini): fakeroot -u tox --recreate fakeroot -u tox -e py38 # run all tests, but only on python 3.8 fakeroot -u tox borg.testsuite.locking # only run 1 test module fakeroot -u tox borg.testsuite.locking -- -k '"not Timer"' # exclude some tests fakeroot -u tox borg.testsuite -- -v # verbose py.test Important notes: - When using ``--`` to give options to py.test, you MUST also give ``borg.testsuite[.module]``. Adding a compression algorithm ------------------------------ If you want to add a new compression algorithm, please refer to :issue:`1633` and leave a post there in order to discuss about the proposal. Documentation ------------- Generated files ~~~~~~~~~~~~~~~ Usage documentation (found in ``docs/usage/``) and man pages (``docs/man/``) are generated automatically from the command line parsers declared in the program and their documentation, which is embedded in the program (see archiver.py). These are committed to git for easier use by packagers downstream. When a command is added, a command line flag changed, added or removed, the usage docs need to be rebuilt as well:: python setup.py build_usage python setup.py build_man However, we prefer to do this as part of our :ref:`releasing` preparations, so it is generally not necessary to update these when submitting patches that change something about the command line. Building the docs with Sphinx ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The documentation (in reStructuredText format, .rst) is in docs/. To build the html version of it, you need to have Sphinx installed (in your Borg virtualenv with Python 3):: pip install -r requirements.d/docs.txt Now run:: cd docs/ make html Then point a web browser at docs/_build/html/index.html. The website is updated automatically by ReadTheDocs through GitHub web hooks on the main repository. Using Vagrant ------------- We use Vagrant for the automated creation of testing environments and borgbackup standalone binaries for various platforms. For better security, there is no automatic sync in the VM to host direction. The plugin `vagrant-scp` is useful to copy stuff from the VMs to the host. The "windows10" box requires the `reload` plugin (``vagrant plugin install vagrant-reload``). Usage:: # To create and provision the VM: vagrant up OS # same, but use 6 VM cpus and 12 workers for pytest: VMCPUS=6 XDISTN=12 vagrant up OS # To create an ssh session to the VM: vagrant ssh OS # To execute a command via ssh in the VM: vagrant ssh OS -c "command args" # To shut down the VM: vagrant halt OS # To shut down and destroy the VM: vagrant destroy OS # To copy files from the VM (in this case, the generated binary): vagrant scp OS:/vagrant/borg/borg.exe . Creating standalone binaries ---------------------------- Make sure you have everything built and installed (including fuse stuff). When using the Vagrant VMs, pyinstaller will already be installed. With virtual env activated:: pip install pyinstaller # or git checkout master pyinstaller -F -n borg-PLATFORM borg/__main__.py for file in dist/borg-*; do gpg --armor --detach-sign $file; done If you encounter issues, see also our `Vagrantfile` for details. .. note:: Standalone binaries built with pyinstaller are supposed to work on same OS, same architecture (x86 32bit, amd64 64bit) without external dependencies. .. _releasing: Creating a new release ---------------------- Checklist: - Make sure all issues for this milestone are closed or moved to the next milestone. - Check if there are any pending fixes for security issues. - Find and fix any low hanging fruit left on the issue tracker. - Check that GitHub Actions CI is happy. - Update ``CHANGES.rst``, based on ``git log $PREVIOUS_RELEASE..``. - Check version number of upcoming release in ``CHANGES.rst``. - Render ``CHANGES.rst`` via ``make html`` and check for markup errors. - Verify that ``MANIFEST.in`` and ``setup.py`` are complete. - ``python setup.py build_usage ; python setup.py build_man`` and commit. - Tag the release:: git tag -s -m "tagged/signed release X.Y.Z" X.Y.Z - Create a clean repo and use it for the following steps:: git clone borg borg-clean This makes sure no uncommitted files get into the release archive. It will also reveal uncommitted required files. Moreover, it makes sure the vagrant machines only get committed files and do a fresh start based on that. - Run tox and/or binary builds on all supported platforms via vagrant, check for test failures. - Create sdist, sign it, upload release to (test) PyPi: :: scripts/sdist-sign X.Y.Z scripts/upload-pypi X.Y.Z test scripts/upload-pypi X.Y.Z Note: the signature is not uploaded to PyPi any more, but we upload it to github releases. - Put binaries into dist/borg-OSNAME and sign them: :: scripts/sign-binaries 201912312359 - Close the release milestone on GitHub. - `Update borgbackup.org `_ with the new version number and release date. - Announce on: - Mailing list. - Twitter. - IRC channel (change ``/topic``). - Create a GitHub release, include: * pypi dist package and signature * Standalone binaries (see above for how to create them). + For macOS, document the macFUSE version in the README of the binaries. macFUSE uses a kernel extension that needs to be compatible with the code contained in the binary. * A link to ``CHANGES.rst``. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/faq.rst0000644000076500000240000017300114601576577014473 0ustar00twstaff.. include:: global.rst.inc .. highlight:: none .. _faq: Frequently asked questions ========================== Usage & Limitations ################### What is the difference between a repo on an external hard drive vs. repo on a server? ------------------------------------------------------------------------------------- If Borg is running in client/server mode, the client uses SSH as a transport to talk to the remote agent, which is another Borg process (Borg is installed on the server, too) started automatically by the client. The Borg server is doing storage-related low-level repo operations (get, put, commit, check, compact), while the Borg client does the high-level stuff: deduplication, encryption, compression, dealing with archives, backups, restores, etc., which reduces the amount of data that goes over the network. When Borg is writing to a repo on a locally mounted remote file system, e.g. SSHFS, the Borg client only can do file system operations and has no agent running on the remote side, so *every* operation needs to go over the network, which is slower. Can I backup from multiple servers into a single repository? ------------------------------------------------------------ Yes, this is *possible* from the technical standpoint, but it is *not recommended* from the security perspective. BorgBackup is built upon a defined :ref:`attack_model` that cannot provide its guarantees for multiple clients using the same repository. See :ref:`borg_security_critique` for a detailed explanation. Also, in order for the deduplication used by Borg to work, it needs to keep a local cache containing checksums of all file chunks already stored in the repository. This cache is stored in ``~/.cache/borg/``. If Borg detects that a repository has been modified since the local cache was updated it will need to rebuild the cache. This rebuild can be quite time consuming. So, yes it's possible. But it will be most efficient if a single repository is only modified from one place. Also keep in mind that Borg will keep an exclusive lock on the repository while creating or deleting archives, which may make *simultaneous* backups fail. Can I back up to multiple, swapped backup targets? -------------------------------------------------- It is possible to swap your backup disks if each backup medium is assigned its own repository by creating a new one with :ref:`borg_init`. Can I copy or synchronize my repo to another location? ------------------------------------------------------ If you want to have redundant backup repositories (preferably at separate locations), the recommended way to do that is like this: - ``borg init repo1`` - ``borg init repo2`` - client machine ---borg create---> repo1 - client machine ---borg create---> repo2 This will create distinct repositories (separate repo ID, separate keys) and nothing bad happening in repo1 will influence repo2. Some people decide against above recommendation and create identical copies of a repo (using some copy / sync / clone tool). While this might be better than having no redundancy at all, you have to be very careful about how you do that and what you may / must not do with the result (if you decide against our recommendation). What you would get with this is: - client machine ---borg create---> repo - repo ---copy/sync---> copy-of-repo There is no special borg command to do the copying, you could just use any reliable tool that creates an identical copy (cp, rsync, rclone might be options). But think about whether that is really what you want. If something goes wrong in repo, you will have the same issue in copy-of-repo. Make sure you do the copy/sync while no backup is running, see :ref:`borg_with-lock` about how to do that. Also, you must not run borg against multiple instances of the same repo (like repo and copy-of-repo) as that would create severe issues: - Data loss: they have the same repository ID, so the borg client will think they are identical and e.g. use the same local cache for them (which is an issue if they happen to be not the same). See :issue:`4272` for an example. - Encryption security issues if you would update repo and copy-of-repo independently, due to AES counter reuse. See also: :ref:`faq_corrupt_repo` "this is either an attack or unsafe" warning -------------------------------------------- About the warning: Cache, or information obtained from the security directory is newer than repository - this is either an attack or unsafe (multiple repos with same ID) "unsafe": If not following the advice from the previous section, you can easily run into this by yourself by restoring an older copy of your repository. "attack": maybe an attacker has replaced your repo by an older copy, trying to trick you into AES counter reuse, trying to break your repo encryption. Borg users have also reported that fs issues (like hw issues / I/O errors causing the fs to become read-only) can cause this warning, see :issue:`7853`. If you'ld decide to ignore this and accept unsafe operation for this repository, you could delete the manifest-timestamp and the local cache: :: borg config repo id # shows the REPO_ID rm ~/.config/borg/security/REPO_ID/manifest-timestamp borg delete --cache-only REPO This is an unsafe and unsupported way to use borg, you have been warned. Which file types, attributes, etc. are *not* preserved? ------------------------------------------------------- * UNIX domain sockets (because it does not make sense - they are meaningless without the running process that created them and the process needs to recreate them in any case). So, don't panic if your backup misses a UDS! * The precise on-disk (or rather: not-on-disk) representation of the holes in a sparse file. Archive creation has no special support for sparse files, holes are backed up as (deduplicated and compressed) runs of zero bytes. Archive extraction has optional support to extract all-zero chunks as holes in a sparse file. * Some filesystem specific attributes, like btrfs NOCOW, see :ref:`platforms`. * For hardlinked symlinks, the hardlinking can not be archived (and thus, the hardlinking will not be done at extraction time). The symlinks will be archived and extracted as non-hardlinked symlinks, see :issue:`2379`. Are there other known limitations? ---------------------------------- - A single archive can only reference a limited volume of file/dir metadata, usually corresponding to tens or hundreds of millions of files/dirs. When trying to go beyond that limit, you will get a fatal IntegrityError exception telling that the (archive) object is too big. An easy workaround is to create multiple archives with fewer items each. See also the :ref:`archive_limitation` and :issue:`1452`. :ref:`borg_info` shows how large (relative to the maximum size) existing archives are. - borg extract only supports restoring into an empty destination. After that, the destination will exactly have the contents of the extracted archive. If you extract into a non-empty destination, borg will (for example) not remove files which are in the destination, but not in the archive. See :issue:`4598` for a workaround and more details. .. _checkpoints_parts: If a backup stops mid-way, does the already-backed-up data stay there? ---------------------------------------------------------------------- Yes, Borg supports resuming backups. During a backup a special checkpoint archive named ``.checkpoint`` is saved every checkpoint interval (the default value for this is 30 minutes) containing all the data backed-up until that point. This checkpoint archive is a valid archive, but it is only a partial backup (not all files that you wanted to backup are contained in it). Having it in the repo until a successful, full backup is completed is useful because it references all the transmitted chunks up to the checkpoint. This means that in case of an interruption, you only need to retransfer the data since the last checkpoint. If a backup was interrupted, you normally do not need to do anything special, just invoke ``borg create`` as you always do. If the repository is still locked, you may need to run ``borg break-lock`` before the next backup. You may use the same archive name as in previous attempt or a different one (e.g. if you always include the current datetime), it does not matter. Borg always does full single-pass backups, so it will start again from the beginning - but it will be much faster, because some of the data was already stored into the repo (and is still referenced by the checkpoint archive), so it does not need to get transmitted and stored again. Once your backup has finished successfully, you can delete all ``.checkpoint`` archives. If you run ``borg prune``, it will also care for deleting unneeded checkpoints. Note: the checkpointing mechanism creates hidden, partial files in an archive, so that checkpoints even work while a big file is being processed. They are named ``.borg_part_`` and all operations usually ignore these files, but you can make them considered by giving the option ``--consider-part-files``. You usually only need that option if you are really desperate (e.g. if you have no completed backup of that file and you'ld rather get a partial file extracted than nothing). You do **not** want to give that option under any normal circumstances. Note that checkpoints inside files are created only since version 1.1, make sure you have an up-to-date version of borgbackup if you want to continue instead of retransferring a huge file. In some cases, there is only an outdated version shipped with your distribution (e.g. Debian). See :ref:`installation`. How can I backup huge file(s) over a unstable connection? --------------------------------------------------------- This is not a problem anymore. For more details, see :ref:`checkpoints_parts`. How can I switch append-only mode on and off? ----------------------------------------------------------------------------------------------------------------------------------- You could do that (via borg config REPO append_only 0/1), but using different ssh keys and different entries in ``authorized_keys`` is much easier and also maybe has less potential of things going wrong somehow. My machine goes to sleep causing `Broken pipe` ---------------------------------------------- When backing up your data over the network, your machine should not go to sleep. On macOS you can use `caffeinate` to avoid that. How can I restore huge file(s) over an unstable connection? ----------------------------------------------------------- If you cannot manage to extract the whole big file in one go, you can extract all the part files and manually concatenate them together. For more details, see :ref:`checkpoints_parts`. How can I compare contents of an archive to my local filesystem? ----------------------------------------------------------------- You can instruct ``export-tar`` to send a tar stream to the stdout, and then use ``tar`` to perform the comparison: :: borg export-tar /path/to/repo::archive-name - | tar --compare -f - -C /path/to/compare/to .. _faq_corrupt_repo: My repository is corrupt, how can I restore from an older copy of it? --------------------------------------------------------------------- If your repositories are encrypted and have the same ID, the recommended method is to delete the corrupted repository, but keep its security info, and then copy the working repository to the same location: :: borg delete --keep-security-info /path/to/repo rsync -aH /path/to/repo-working/ /path/to/repo # Note the trailing slash. A plain delete command would remove the security info in ``~/.config/borg/security``, including the nonce value. In BorgBackup :ref:`security_encryption` is AES-CTR, where the nonce is a counter. When the working repo was used later for creating new archives, Borg would re-use nonce values due to starting from a lower counter value given by the older copy of the repository. To prevent this, the ``keep-security-info`` option is applied so that the client-side nonce counter is kept. Can Borg add redundancy to the backup data to deal with hardware malfunction? ----------------------------------------------------------------------------- No, it can't. While that at first sounds like a good idea to defend against some defect HDD sectors or SSD flash blocks, dealing with this in a reliable way needs a lot of low-level storage layout information and control which we do not have (and also can't get, even if we wanted). So, if you need that, consider RAID or a filesystem that offers redundant storage or just make backups to different locations / different hardware. See also :issue:`225`. Can Borg verify data integrity of a backup archive? --------------------------------------------------- Yes, if you want to detect accidental data damage (like bit rot), use the ``check`` operation. It will notice corruption using CRCs and hashes. If you want to be able to detect malicious tampering also, use an encrypted repo. It will then be able to check using CRCs and HMACs. Can I use Borg on SMR hard drives? ---------------------------------- SMR (shingled magnetic recording) hard drives are very different from regular hard drives. Applications have to behave in certain ways or performance will be heavily degraded. Borg 1.1 ships with default settings suitable for SMR drives, and has been successfully tested on *Seagate Archive v2* drives using the ext4 file system. Some Linux kernel versions between 3.19 and 4.5 had various bugs handling device-managed SMR drives, leading to IO errors, unresponsive drives and unreliable operation in general. For more details, refer to :issue:`2252`. .. _faq-integrityerror: I get an IntegrityError or similar - what now? ---------------------------------------------- A single error does not necessarily indicate bad hardware or a Borg bug. All hardware exhibits a bit error rate (BER). Hard drives are typically specified as exhibiting fewer than one error every 12 to 120 TB (one bit error in 10e14 to 10e15 bits). The specification is often called *unrecoverable read error rate* (URE rate). Apart from these very rare errors there are two main causes of errors: (i) Defective hardware: described below. (ii) Bugs in software (Borg, operating system, libraries): Ensure software is up to date. Check whether the issue is caused by any fixed bugs described in :ref:`important_notes`. .. rubric:: Finding defective hardware .. note:: Hardware diagnostics are operating system dependent and do not apply universally. The commands shown apply for popular Unix-like systems. Refer to your operating system's manual. Checking hard drives Find the drive containing the repository and use *findmnt*, *mount* or *lsblk* to learn the device path (typically */dev/...*) of the drive. Then, smartmontools can retrieve self-diagnostics of the drive in question:: # smartctl -a /dev/sdSomething The *Offline_Uncorrectable*, *Current_Pending_Sector* and *Reported_Uncorrect* attributes indicate data corruption. A high *UDMA_CRC_Error_Count* usually indicates a bad cable. I/O errors logged by the system (refer to the system journal or dmesg) can point to issues as well. I/O errors only affecting the file system easily go unnoticed, since they are not reported to applications (e.g. Borg), while these errors can still corrupt data. Drives can corrupt some sectors in one event, while remaining reliable otherwise. Conversely, drives can fail completely with no advance warning. If in doubt, copy all data from the drive in question to another drive -- just in case it fails completely. If any of these are suspicious, a self-test is recommended:: # smartctl -t long /dev/sdSomething Running ``fsck`` if not done already might yield further insights. Checking memory Intermittent issues, such as ``borg check`` finding errors inconsistently between runs, are frequently caused by bad memory. Run memtest86+ (or an equivalent memory tester) to verify that the memory subsystem is operating correctly. Checking processors Processors rarely cause errors. If they do, they are usually overclocked or otherwise operated outside their specifications. We do not recommend to operate hardware outside its specifications for productive use. Tools to verify correct processor operation include Prime95 (mprime), linpack, and the `Intel Processor Diagnostic Tool `_ (applies only to Intel processors). .. rubric:: Repairing a damaged repository With any defective hardware found and replaced, the damage done to the repository needs to be ascertained and fixed. :ref:`borg_check` provides diagnostics and ``--repair`` options for repositories with issues. We recommend to first run without ``--repair`` to assess the situation. If the found issues and proposed repairs seem right, re-run "check" with ``--repair`` enabled. How probable is it to get a hash collision problem? --------------------------------------------------- If you noticed, there are some issues (:issue:`170` (**warning: hell**) and :issue:`4884`) about the probability of a chunk having the same hash as another chunk, making the file corrupted because it grabbed the wrong chunk. This is called the `Birthday Problem `_. There is a lot of probability in here so, I can give you my interpretation of such math but it's honestly better that you read it yourself and grab your own resolution from that. Assuming that all your chunks have a size of :math:`2^{21}` bytes (approximately 2.1 MB) and we have a "perfect" hash algorithm, we can think that the probability of collision would be of :math:`p^2/2^{n+1}` then, using SHA-256 (:math:`n=256`) and for example we have 1000 million chunks (:math:`p=10^9`) (1000 million chunks would be about 2100TB). The probability would be around to 0.0000000000000000000000000000000000000000000000000000000000043. A mass-murderer space rock happens about once every 30 million years on average. This leads to a probability of such an event occurring in the next second to about :math:`10^{-15}`. That's **45** orders of magnitude more probable than the SHA-256 collision. Briefly stated, if you find SHA-256 collisions scary then your priorities are wrong. This example was grabbed from `this SO answer `_, it's great honestly. Still, the real question is if Borg tries to not make this happen? Well... it used to not check anything but there was a feature added which saves the size of the chunks too, so the size of the chunks is compared to the size that you got with the hash and if the check says there is a mismatch it will raise an exception instead of corrupting the file. This doesn't save us from everything but reduces the chances of corruption. There are other ways of trying to escape this but it would affect performance so much that it wouldn't be worth it and it would contradict Borg's design, so if you don't want this to happen, simply don't use Borg. Why is the time elapsed in the archive stats different from wall clock time? ---------------------------------------------------------------------------- Borg needs to write the time elapsed into the archive metadata before finalizing the archive, compacting the segments, and committing the repo & cache. This means when Borg is run with e.g. the ``time`` command, the duration shown in the archive stats may be shorter than the full time the command runs for. How do I configure different prune policies for different directories? ---------------------------------------------------------------------- Say you want to prune ``/var/log`` faster than the rest of ``/``. How do we implement that? The answer is to backup to different archive *names* and then implement different prune policies for different --glob-archives matching patterns. For example, you could have a script that does:: borg create --exclude var/log $REPOSITORY:main-$(date +%Y-%m-%d) / borg create $REPOSITORY:logs-$(date +%Y-%m-%d) /var/log Then you would have two different prune calls with different policies:: borg prune --verbose --list -d 30 --glob-archives 'main-*' "$REPOSITORY" borg prune --verbose --list -d 7 --glob-archives 'logs-*' "$REPOSITORY" This will keep 7 days of logs and 30 days of everything else. How do I remove files from an existing backup? ---------------------------------------------- A file is only removed from a BorgBackup repository if all archives that contain the file are deleted and the corresponding data chunks are removed from the repository There are two ways how to remove files from a repository. 1. Use :ref:`borg_delete` to remove all archives that contain the files. This will of course delete everything in the archive, not only some files. 2. If you really want to remove only some specific files, you can run the :ref:`borg_recreate` command to rewrite all archives with a different ``--exclude`` pattern. See the examples in the manpage for more information. Finally, run :ref:`borg_compact` with the ``--threshold 0`` option to delete the data chunks from the repository. Can I safely change the compression level or algorithm? -------------------------------------------------------- The compression level and algorithm don't affect deduplication. Chunk ID hashes are calculated *before* compression. New compression settings will only be applied to new chunks, not existing chunks. So it's safe to change them. Security ######## .. _borg_security_critique: Isn't BorgBackup's AES-CTR crypto broken? ----------------------------------------- If a nonce (counter) value is reused, AES-CTR mode crypto is broken. To exploit the AES counter management issue, an attacker would need to have access to the borg repository. By tampering with the repo, the attacker could bring the repo into a state so that it reports a lower "highest used counter value" than the one that actually was used. The client would usually notice that, because it rather trusts the clientside stored "highest used counter value" than trusting the server. But there are situations, where this is simply not possible: - If clients A and B used the repo, the client A can only know its own highest CTR value, but not the one produced by B. That is only known to (B and) the server (the repo) and thus the client A needs to trust the server about the value produced by B in that situation. You can't do much about this except not having multiple clients per repo. - Even if there is only one client, if client-side information is completely lost (e.g. due to disk defect), the client also needs to trust the value from server side. You can avoid this by not continuing to write to the repository after you have lost clientside borg information. .. _home_config_borg: How important is the $HOME/.config/borg directory? -------------------------------------------------- The Borg config directory has content that you should take care of: ``security`` subdirectory Each directory here represents one Borg repository by its ID and contains the last known status. If a repository's status is different from this information at the beginning of BorgBackup operation, Borg outputs warning messages and asks for confirmation, so make sure you do not lose or manipulate these files. However, apart from those warnings, a loss of these files can be recovered. ``keys`` subdirectory All your borg keyfile keys are stored in this directory. Please note that borg repokey keys are stored inside the repository. You MUST make sure to have an independent backup of these keyfiles, otherwise you cannot access your backups anymore if you lose them. You also MUST keep these files secret; everyone who gains access to your repository and has the corresponding keyfile (and the key passphrase) can extract it. Make sure that only you have access to the Borg config directory. .. _cache_security: Do I need to take security precautions regarding the cache? ----------------------------------------------------------- The cache contains a lot of metadata information about the files in your repositories and it is not encrypted. However, the assumption is that the cache is being stored on the very same system which also contains the original files which are being backed up. So someone with access to the cache files would also have access the original files anyway. The Internals section contains more details about :ref:`cache`. If you ever need to move the cache to a different location, this can be achieved by using the appropriate :ref:`env_vars`. How can I specify the encryption passphrase programmatically? ------------------------------------------------------------- There are several ways to specify a passphrase without human intervention: Setting ``BORG_PASSPHRASE`` The passphrase can be specified using the ``BORG_PASSPHRASE`` environment variable. This is often the simplest option, but can be insecure if the script that sets it is world-readable. .. _password_env: .. note:: Be careful how you set the environment; using the ``env`` command, a ``system()`` call or using inline shell scripts (e.g. ``BORG_PASSPHRASE=hunter2 borg ...``) might expose the credentials in the process list directly and they will be readable to all users on a system. Using ``export`` in a shell script file should be safe, however, as the environment of a process is `accessible only to that user `_. Using ``BORG_PASSCOMMAND`` with a properly permissioned file Another option is to create a file with a password in it in your home directory and use permissions to keep anyone else from reading it. For example, first create a key:: (umask 0077; head -c 32 /dev/urandom | base64 -w 0 > ~/.borg-passphrase) Then in an automated script one can put:: export BORG_PASSCOMMAND="cat $HOME/.borg-passphrase" and Borg will automatically use that passphrase. Using keyfile-based encryption with a blank passphrase It is possible to encrypt your repository in ``keyfile`` mode instead of the default ``repokey`` mode and use a blank passphrase for the key file (simply press Enter twice when ``borg init`` asks for the password). See :ref:`encrypted_repos` for more details. Using ``BORG_PASSCOMMAND`` with macOS Keychain macOS has a native manager for secrets (such as passphrases) which is safer than just using a file as it is encrypted at rest and unlocked manually (fortunately, the login keyring automatically unlocks when you login). With the built-in ``security`` command, you can access it from the command line, making it useful for ``BORG_PASSCOMMAND``. First generate a passphrase and use ``security`` to save it to your login (default) keychain:: security add-generic-password -D secret -U -a $USER -s borg-passphrase -w $(head -c 32 /dev/urandom | base64 -w 0) In your backup script retrieve it in the ``BORG_PASSCOMMAND``:: export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w" Using ``BORG_PASSCOMMAND`` with GNOME Keyring GNOME also has a keyring daemon that can be used to store a Borg passphrase. First ensure ``libsecret-tools``, ``gnome-keyring`` and ``libpam-gnome-keyring`` are installed. If ``libpam-gnome-keyring`` wasn't already installed, ensure it runs on login:: sudo sh -c "echo session optional pam_gnome_keyring.so auto_start >> /etc/pam.d/login" sudo sh -c "echo password optional pam_gnome_keyring.so >> /etc/pam.d/passwd" # you may need to relogin afterwards to activate the login keyring Then add a secret to the login keyring:: head -c 32 /dev/urandom | base64 -w 0 | secret-tool store borg-repository repo-name --label="Borg Passphrase" If a dialog box pops up prompting you to pick a password for a new keychain, use your login password. If there is a checkbox for automatically unlocking on login, check it to allow backups without any user intervention whatsoever. Once the secret is saved, retrieve it in a backup script using ``BORG_PASSCOMMAND``:: export BORG_PASSCOMMAND="secret-tool lookup borg-repository repo-name" .. note:: For this to automatically unlock the keychain it must be run in the ``dbus`` session of an unlocked terminal; for example, running a backup script as a ``cron`` job might not work unless you also ``export DISPLAY=:0`` so ``secret-tool`` can pick up your open session. `It gets even more complicated`__ when you are running the tool as a different user (e.g. running a backup as root with the password stored in the user keyring). __ https://github.com/borgbackup/borg/pull/2837#discussion_r127641330 Using ``BORG_PASSCOMMAND`` with KWallet KDE also has a keychain feature in the form of KWallet. The command-line tool ``kwalletcli`` can be used to store and retrieve secrets. Ensure ``kwalletcli`` is installed, generate a passphrase, and store it in your "wallet":: head -c 32 /dev/urandom | base64 -w 0 | kwalletcli -Pe borg-passphrase -f Passwords Once the secret is saved, retrieve it in a backup script using ``BORG_PASSCOMMAND``:: export BORG_PASSCOMMAND="kwalletcli -e borg-passphrase -f Passwords" When backing up to remote encrypted repos, is encryption done locally? ---------------------------------------------------------------------- Yes, file and directory metadata and data is locally encrypted, before leaving the local machine. We do not mean the transport layer encryption by that, but the data/metadata itself. Transport layer encryption (e.g. when ssh is used as a transport) applies additionally. When backing up to remote servers, do I have to trust the remote server? ------------------------------------------------------------------------ Yes and No. No, as far as data confidentiality is concerned - if you use encryption, all your files/dirs data and metadata are stored in their encrypted form into the repository. Yes, as an attacker with access to the remote server could delete (or otherwise make unavailable) all your backups. How can I protect against a hacked backup client? ------------------------------------------------- Assume you backup your backup client machine C to the backup server S and C gets hacked. In a simple push setup, the attacker could then use borg on C to delete all backups residing on S. These are your options to protect against that: - Do not allow to permanently delete data from the repo, see :ref:`append_only_mode`. - Use a pull-mode setup using ``ssh -R``, see :ref:`pull_backup` for more information. - Mount C's filesystem on another machine and then create a backup of it. - Do not give C filesystem-level access to S. See :ref:`hosting_repositories` for a detailed protection guide. How can I protect against a hacked backup server? ------------------------------------------------- Just in case you got the impression that pull-mode backups are way more safe than push-mode, you also need to consider the case that your backup server S gets hacked. In case S has access to a lot of clients C, that might bring you into even bigger trouble than a hacked backup client in the previous FAQ entry. These are your options to protect against that: - Use the standard push-mode setup (see also previous FAQ entry). - Mount (the repo part of) S's filesystem on C. - Do not give S file-system level access to C. - Have your backup server at a well protected place (maybe not reachable from the internet), configure it safely, apply security updates, monitor it, ... How can I protect against theft, sabotage, lightning, fire, ...? ---------------------------------------------------------------- In general: if your only backup medium is nearby the backupped machine and always connected, you can easily get into trouble: they likely share the same fate if something goes really wrong. Thus: - have multiple backup media - have media disconnected from network, power, computer - have media at another place - have a relatively recent backup on your media How do I report a security issue with Borg? ------------------------------------------- Send a private email to the :ref:`security contact ` if you think you have discovered a security issue. Please disclose security issues responsibly. How important are the nonce files? ------------------------------------ Borg uses :ref:`AES-CTR encryption `. An essential part of AES-CTR is a sequential counter that must **never** repeat. If the same value of the counter is used twice in the same repository, an attacker can decrypt the data. The counter is stored in the home directory of each user ($HOME/.config/borg/security/$REPO_ID/nonce) as well as in the repository (/path/to/repo/nonce). When creating a new archive borg uses the highest of the two values. The value of the counter in the repository may be higher than your local value if another user has created an archive more recently than you did. Since the nonce is not necessary to read the data that is already encrypted, ``borg info``, ``borg list``, ``borg extract`` and ``borg mount`` should work just fine without it. If the nonce file stored in the repo is lost, but you still have your local copy, borg will recreate the repository nonce file the next time you run ``borg create``. This should be safe for repositories that are only used from one user account on one machine. For repositories that are used by multiple users and/or from multiple machines it is safest to avoid running *any* commands that modify the repository after the nonce is deleted or if you suspect it may have been tampered with. See :ref:`attack_model`. Common issues ############# /path/to/repo is not a valid repository. Check repo config. ----------------------------------------------------------- There can be many causes of this error. E.g. you have incorrectly specified the repository path. You will also get this error if you try to access a repository that uses the argon2 key algorithm using an old version of borg. We recommend upgrading to the latest stable version and trying again. We are sorry. We should have thought abount forward compatibility and implemented a more helpful error message. Why does Borg extract hang after some time? ------------------------------------------- When I do a ``borg extract``, after a while all activity stops, no cpu usage, no downloads. This may happen when the SSH connection is stuck on server side. You can configure SSH on client side to prevent this by sending keep-alive requests, for example in ~/.ssh/config: :: Host borg.example.com # Client kills connection after 3*30 seconds without server response: ServerAliveInterval 30 ServerAliveCountMax 3 You can also do the opposite and configure SSH on server side in /etc/ssh/sshd_config, to make the server send keep-alive requests to the client: :: # Server kills connection after 3*30 seconds without client response: ClientAliveInterval 30 ClientAliveCountMax 3 How can I deal with my very unstable SSH connection? ---------------------------------------------------- If you have issues with lost connections during long-running borg commands, you could try to work around: - Make partial extracts like ``borg extract REPO PATTERN`` to do multiple smaller extraction runs that complete before your connection has issues. - Try using ``borg mount REPO MOUNTPOINT`` and ``rsync -avH`` from ``MOUNTPOINT`` to your desired extraction directory. If the connection breaks down, just repeat that over and over again until rsync does not find anything to do any more. Due to the way borg mount works, this might be less efficient than borg extract for bigger volumes of data. Why do I get "connection closed by remote" after a while? --------------------------------------------------------- When doing a backup to a remote server (using a ssh: repo URL), it sometimes stops after a while (some minutes, hours, ... - not immediately) with "connection closed by remote" error message. Why? That's a good question and we are trying to find a good answer in :issue:`636`. Why am I seeing idle borg serve processes on the repo server? ------------------------------------------------------------- Maybe the ssh connection between client and server broke down and that was not yet noticed on the server. Try these settings: :: # /etc/ssh/sshd_config on borg repo server - kill connection to client # after ClientAliveCountMax * ClientAliveInterval seconds with no response ClientAliveInterval 20 ClientAliveCountMax 3 If you have multiple borg create ... ; borg create ... commands in a already serialized way in a single script, you need to give them ``--lock-wait N`` (with N being a bit more than the time the server needs to terminate broken down connections and release the lock). .. _disable_archive_chunks: The borg cache eats way too much disk space, what can I do? ----------------------------------------------------------- This may especially happen if borg needs to rebuild the local "chunks" index - either because it was removed, or because it was not coherent with the repository state any more (e.g. because another borg instance changed the repository). To optimize this rebuild process, borg caches per-archive information in the ``chunks.archive.d/`` directory. It won't help the first time it happens, but it will make the subsequent rebuilds faster (because it needs to transfer less data from the repository). While being faster, the cache needs quite some disk space, which might be unwanted. There is a temporary (but maybe long lived) hack to avoid using lots of disk space for chunks.archive.d (see :issue:`235` for details): :: # this assumes you are working with the same user as the backup. cd ~/.cache/borg/$(borg config /path/to/repo id) rm -rf chunks.archive.d ; touch chunks.archive.d This deletes all the cached archive chunk indexes and replaces the directory that kept them with a file, so borg won't be able to store anything "in" there in future. This has some pros and cons, though: - much less disk space needs for ~/.cache/borg. - chunk cache resyncs will be slower as it will have to transfer chunk usage metadata for all archives from the repository (which might be slow if your repo connection is slow) and it will also have to build the hashtables from that data. chunk cache resyncs happen e.g. if your repo was written to by another machine (if you share same backup repo between multiple machines) or if your local chunks cache was lost somehow. The long term plan to improve this is called "borgception", see :issue:`474`. Can I backup my root partition (/) with Borg? --------------------------------------------- Backing up your entire root partition works just fine, but remember to exclude directories that make no sense to backup, such as /dev, /proc, /sys, /tmp and /run, and to use ``--one-file-system`` if you only want to backup the root partition (and not any mounted devices e.g.). If it crashes with a UnicodeError, what can I do? ------------------------------------------------- Check if your encoding is set correctly. For most POSIX-like systems, try:: export LANG=en_US.UTF-8 # or similar, important is correct charset If that does not help: - check for typos, check if you really used ``export``. - check if you have set ``LC_ALL`` - if so, try not setting it. - check if you generated the respective locale via ``locale-gen``. I can't extract non-ascii filenames by giving them on the commandline!? ----------------------------------------------------------------------- This might be due to different ways to represent some characters in unicode or due to other non-ascii encoding issues. If you run into that, try this: - avoid the non-ascii characters on the commandline by e.g. extracting the parent directory (or even everything) - mount the repo using FUSE and use some file manager .. _expected_performance: What's the expected backup performance? --------------------------------------- A first backup will usually be somehow "slow" because there is a lot of data to process. Performance here depends on a lot of factors, so it is hard to give specific numbers. Subsequent backups are usually very fast if most files are unchanged and only a few are new or modified. The high performance on unchanged files primarily depends only on a few factors (like fs recursion + metadata reading performance and the files cache working as expected) and much less on other factors. E.g., for this setup: - server grade machine (4C/8T 2013 Xeon, 64GB RAM, 2x good 7200RPM disks) - local zfs filesystem (mirrored) containing the backup source data - repository is remote (does not matter much for unchanged files) - backup job runs while machine is otherwise idle The observed performance is that Borg can process about **1 million unchanged files (and a few small changed ones) in 4 minutes!** If you are seeing much less than that in similar circumstances, read the next few FAQ entries below. .. _slow_backup: Why is backup slow for me? -------------------------- So, if you feel your Borg backup is too slow somehow, you should find out why. The usual way to approach this is to add ``--list --filter=AME --stats`` to your ``borg create`` call to produce more log output, including a file list (with file status characters) and also some statistics at the end of the backup. Then you do the backup and look at the log output: - stats: Do you really have little changes or are there more changes than you thought? In the stats you can see the overall volume of changed data, which needed to be added to the repo. If that is a lot, that can be the reason why it is slow. - ``A`` status ("added") in the file list: If you see that often, you have a lot of new files (files that Borg did not find in the files cache). If you think there is something wrong with that (the file was there already in the previous backup), please read the FAQ entries below. - ``M`` status ("modified") in the file list: If you see that often, Borg thinks that a lot of your files might be modified (Borg found them in the files cache, but the metadata read from the filesystem did not match the metadata stored in the files cache). In such a case, Borg will need to process the files' contents completely, which is much slower than processing unmodified files (Borg does not read their contents!). The metadata values used in this comparison are determined by the ``--files-cache`` option and could be e.g. size, ctime and inode number (see the ``borg create`` docs for more details and potential issues). You can use the ``stat`` command on files to manually look at fs metadata to debug if there is any unexpected change triggering the ``M`` status. Also, the ``--debug-topic=files_cache`` option of ``borg create`` provides a lot of debug output helping to analyse why the files cache does not give its expected high performance. When borg runs inside a virtual machine, there are some more things to look at: Some hypervisors (e.g. kvm on proxmox) give some broadly compatible CPU type to the VM (usually to ease migration between VM hosts of potentially different hardware CPUs). It is broadly compatible because they leave away modern CPU features that could be not present in older or other CPUs, e.g. hardware acceleration for AES crypto, for sha2 hashes, for (P)CLMUL(QDQ) computations useful for crc32. So, basically you pay for compatibility with bad performance. If you prefer better performance, you should try to expose the host CPU's misc. hw acceleration features to the VM which runs borg. On Linux, check ``/proc/cpuinfo`` for the CPU flags inside the VM. For kvm check the docs about "Host model" and "Host passthrough". See also the next few FAQ entries for more details. .. _a_status_oddity: I am seeing 'A' (added) status for an unchanged file!? ------------------------------------------------------ The files cache is used to determine whether Borg already "knows" / has backed up a file and if so, to skip the file from chunking. It intentionally *excludes* files that have a timestamp which is the same as the newest timestamp in the created archive. So, if you see an 'A' status for unchanged file(s), they are likely the files with the most recent timestamp in that archive. This is expected: it is to avoid data loss with files that are backed up from a snapshot and that are immediately changed after the snapshot (but within timestamp granularity time, so the timestamp would not change). Without the code that removes these files from the files cache, the change that happened right after the snapshot would not be contained in the next backup as Borg would think the file is unchanged. This does not affect deduplication, the file will be chunked, but as the chunks will often be the same and already stored in the repo (except in the above mentioned rare condition), it will just re-use them as usual and not store new data chunks. If you want to avoid unnecessary chunking, just create or touch a small or empty file in your backup source file set (so that one has the latest timestamp, not your 50GB VM disk image) and, if you do snapshots, do the snapshot after that. Since only the files cache is used in the display of files status, those files are reported as being added when, really, chunks are already used. By default, ctime (change time) is used for the timestamps to have a rather safe change detection (see also the --files-cache option). Furthermore, pathnames recorded in files cache are always absolute, even if you specify source directories with relative pathname. If relative pathnames are stable, but absolute are not (for example if you mount a filesystem without stable mount points for each backup or if you are running the backup from a filesystem snapshot whose name is not stable), borg will assume that files are different and will report them as 'added', even though no new chunks will be actually recorded for them. To avoid this, you could bind mount your source directory in a directory with the stable path. .. _always_chunking: It always chunks all my files, even unchanged ones! --------------------------------------------------- Borg maintains a files cache where it remembers the timestamp, size and inode of files. When Borg does a new backup and starts processing a file, it first looks whether the file has changed (compared to the values stored in the files cache). If the values are the same, the file is assumed unchanged and thus its contents won't get chunked (again). Borg can't keep an infinite history of files of course, thus entries in the files cache have a "maximum time to live" which is set via the environment variable BORG_FILES_CACHE_TTL (and defaults to 20). Every time you do a backup (on the same machine, using the same user), the cache entries' ttl values of files that were not "seen" are incremented by 1 and if they reach BORG_FILES_CACHE_TTL, the entry is removed from the cache. So, for example, if you do daily backups of 26 different data sets A, B, C, ..., Z on one machine (using the default TTL), the files from A will be already forgotten when you repeat the same backups on the next day and it will be slow because it would chunk all the files each time. If you set BORG_FILES_CACHE_TTL to at least 26 (or maybe even a small multiple of that), it would be much faster. Besides using a higher BORG_FILES_CACHE_TTL (which also increases memory usage), there is also BORG_FILES_CACHE_SUFFIX which can be used to have separate (smaller) files caches for each backup set instead of the default one (big) unified files cache. Another possible reason is that files don't always have the same path, for example if you mount a filesystem without stable mount points for each backup or if you are running the backup from a filesystem snapshot whose name is not stable. If the directory where you mount a filesystem is different every time, Borg assumes they are different files. This is true even if you backup these files with relative pathnames - borg uses full pathnames in files cache regardless. It is possible for some filesystems, such as ``mergerfs`` or network filesystems, to return inconsistent inode numbers across runs, causing borg to consider them changed. A workaround is to set the option ``--files-cache=ctime,size`` to exclude the inode number comparison from the files cache check so that files with different inode numbers won't be treated as modified. Is there a way to limit bandwidth with Borg? -------------------------------------------- To limit upload (i.e. :ref:`borg_create`) bandwidth, use the ``--remote-ratelimit`` option. There is no built-in way to limit *download* (i.e. :ref:`borg_extract`) bandwidth, but limiting download bandwidth can be accomplished with pipeviewer_: Create a wrapper script: /usr/local/bin/pv-wrapper :: #!/bin/sh ## -q, --quiet do not output any transfer information at all ## -L, --rate-limit RATE limit transfer to RATE bytes per second RATE=307200 pv -q -L $RATE | "$@" Add BORG_RSH environment variable to use pipeviewer wrapper script with ssh. :: export BORG_RSH='/usr/local/bin/pv-wrapper ssh' Now Borg will be bandwidth limited. The nice thing about ``pv`` is that you can change rate-limit on the fly: :: pv -R $(pidof pv) -L 102400 .. _pipeviewer: http://www.ivarch.com/programs/pv.shtml How can I avoid unwanted base directories getting stored into archives? ----------------------------------------------------------------------- Possible use cases: - Another file system is mounted and you want to backup it with original paths. - You have created a BTRFS snapshot in a ``/.snapshots`` directory for backup. To achieve this, run ``borg create`` within the mountpoint/snapshot directory: :: # Example: Some file system mounted in /mnt/rootfs. cd /mnt/rootfs borg create /path/to/repo::rootfs_backup . I am having troubles with some network/FUSE/special filesystem, why? -------------------------------------------------------------------- Borg is doing nothing special in the filesystem, it only uses very common and compatible operations (even the locking is just "rename"). So, if you are encountering issues like slowness, corruption or malfunction when using a specific filesystem, please try if you can reproduce the issues with a local (non-network) and proven filesystem (like ext4 on Linux). If you can't reproduce the issue then, you maybe have found an issue within the filesystem code you used (not with Borg). For this case, it is recommended that you talk to the developers / support of the network fs and maybe open an issue in their issue tracker. Do not file an issue in the Borg issue tracker. If you can reproduce the issue with the proven filesystem, please file an issue in the Borg issue tracker about that. Why does running 'borg check --repair' warn about data loss? ------------------------------------------------------------ Repair usually works for recovering data in a corrupted archive. However, it's impossible to predict all modes of corruption. In some very rare instances, such as malfunctioning storage hardware, additional repo corruption may occur. If you can't afford to lose the repo, it's strongly recommended that you perform repair on a copy of the repo. In other words, the warning is there to emphasize that Borg: - Will perform automated routines that modify your backup repository - Might not actually fix the problem you are experiencing - Might, in very rare cases, further corrupt your repository In the case of malfunctioning hardware, such as a drive or USB hub corrupting data when read or written, it's best to diagnose and fix the cause of the initial corruption before attempting to repair the repo. If the corruption is caused by a one time event such as a power outage, running `borg check --repair` will fix most problems. Why isn't there more progress / ETA information displayed? ---------------------------------------------------------- Some borg runs take quite a bit, so it would be nice to see a progress display, maybe even including a ETA (expected time of "arrival" [here rather "completion"]). For some functionality, this can be done: if the total amount of work is more or less known, we can display progress. So check if there is a ``--progress`` option. But sometimes, the total amount is unknown (e.g. for ``borg create`` we just do a single pass over the filesystem, so we do not know the total file count or data volume before reaching the end). Adding another pass just to determine that would take additional time and could be incorrect, if the filesystem is changing. Even if the fs does not change and we knew count and size of all files, we still could not compute the ``borg create`` ETA as we do not know the amount of changed chunks, how the bandwidth of source and destination or system performance might fluctuate. You see, trying to display ETA would be futile. The borg developers prefer to rather not implement progress / ETA display than doing futile attempts. See also: https://xkcd.com/612/ Why am I getting 'Operation not permitted' errors when backing up on sshfs? --------------------------------------------------------------------------- By default, ``sshfs`` is not entirely POSIX-compliant when renaming files due to a technicality in the SFTP protocol. Fortunately, it also provides a workaround_ to make it behave correctly:: sshfs -o workaround=rename user@host:dir /mnt/dir .. _workaround: https://unix.stackexchange.com/a/123236 Can I disable checking for free disk space? ------------------------------------------- In some cases, the free disk space of the target volume is reported incorrectly. This can happen for CIFS- or FUSE shares. If you are sure that your target volume will always have enough disk space, you can use the following workaround to disable checking for free disk space:: borg config -- $REPO_LOCATION additional_free_space -2T How do I rename a repository? ----------------------------- There is nothing special that needs to be done, you can simply rename the directory that corresponds to the repository. However, the next time borg interacts with the repository (i.e, via ``borg list``), depending on the value of ``BORG_RELOCATED_REPO_ACCESS_IS_OK``, borg may warn you that the repository has been moved. You will be given a prompt to confirm you are OK with this. If ``BORG_RELOCATED_REPO_ACCESS_IS_OK`` is unset, borg will interactively ask for each repository whether it's OK. It may be useful to set ``BORG_RELOCATED_REPO_ACCESS_IS_OK=yes`` to avoid the prompts when renaming multiple repositories or in a non-interactive context such as a script. See :doc:`deployment` for an example. The repository quota size is reached, what can I do? ---------------------------------------------------- The simplest solution is to increase or disable the quota and resume the backup: :: borg config /path/to/repo storage_quota 0 If you are bound to the quota, you have to free repository space. The first to try is running :ref:`borg_compact` to free unused backup space (see also :ref:`separate_compaction`): :: borg compact /path/to/repo If your repository is already compacted, run :ref:`borg_prune` or :ref:`borg_delete` to delete archives that you do not need anymore, and then run ``borg compact`` again. My backup disk is full, what can I do? -------------------------------------- Borg cannot work if you really have zero free space on the backup disk, so the first thing you must do is deleting some files to regain free disk space. See :ref:`about_free_space` for further details. Some Borg commands that do not change the repository might work under disk-full conditions, but generally this should be avoided. If your backup disk is already full when Borg starts a write command like `borg create`, it will abort immediately and the repository will stay as-is. If you run a backup that stops due to a disk running full, Borg will roll back, delete the new new segment file and thus freeing disk space automatically. There may be a checkpoint archive left that has been saved before the disk got full. You can keep it to speed up the next backup or delete it to get back more disk space. Miscellaneous ############# macOS: borg mounts not shown in Finder's side bar ------------------------------------------------- https://github.com/osxfuse/osxfuse/wiki/Mount-options#local Read the above first and use this on your own risk:: borg mount -olocal REPO MOUNTPOINT Requirements for the borg single-file binary, esp. (g)libc? ----------------------------------------------------------- We try to build the binary on old, but still supported systems - to keep the minimum requirement for the (g)libc low. The (g)libc can't be bundled into the binary as it needs to fit your kernel and OS, but Python and all other required libraries will be bundled into the binary. If your system fulfills the minimum (g)libc requirement (see the README that is released with the binary), there should be no problem. If you are slightly below the required version, maybe just try. Due to the dynamic loading (or not loading) of some shared libraries, it might still work depending on what libraries are actually loaded and used. In the borg git repository, there is scripts/glibc_check.py that can determine (based on the symbols' versions they want to link to) whether a set of given (Linux) binaries works with a given glibc version. Why was Borg forked from Attic? ------------------------------- Borg was created in May 2015 in response to the difficulty of getting new code or larger changes incorporated into Attic and establishing a bigger developer community / more open development. More details can be found in `ticket 217 `_ that led to the fork. Borg intends to be: * simple: * as simple as possible, but no simpler * do the right thing by default, but offer options * open: * welcome feature requests * accept pull requests of good quality and coding style * give feedback on PRs that can't be accepted "as is" * discuss openly, don't work in the dark * changing: * Borg is not compatible with Attic * do not break compatibility accidentally, without a good reason or without warning. allow compatibility breaking for other cases. * if major version number changes, it may have incompatible changes Migrating from Attic #################### What are the differences between Attic and Borg? ------------------------------------------------ Borg is a fork of `Attic`_ and maintained by "`The Borg collective`_". .. _Attic: https://github.com/jborg/attic .. _The Borg collective: https://borgbackup.readthedocs.org/en/latest/authors.html Here's a (incomplete) list of some major changes: * lots of attic issues fixed (see `issue #5 `_), including critical data corruption bugs and security issues. * more open, faster paced development (see `issue #1 `_) * less chunk management overhead (less memory and disk usage for chunks index) * faster remote cache resync (useful when backing up multiple machines into same repo) * compression: no, lz4, zstd, zlib or lzma compression, adjustable compression levels * repokey replaces problematic passphrase mode (you can't change the passphrase nor the pbkdf2 iteration count in "passphrase" mode) * simple sparse file support, great for virtual machine disk files * can read special files (e.g. block devices) or from stdin, write to stdout * rename-based locking is more compatible than attic's posix locking * uses fadvise to not spoil / blow up the fs cache * better error messages / exception handling * better logging, screen output, progress indication * tested on misc. Linux systems, 32 and 64bit, FreeBSD, OpenBSD, NetBSD, macOS Please read the :ref:`changelog` (or ``docs/changes.rst`` in the source distribution) for more information. Borg is not compatible with original Attic (but there is a one-way conversion). How do I migrate from Attic to Borg? ------------------------------------ Use :ref:`borg_upgrade`. This is a one-way process that cannot be reversed. There are some caveats: - The upgrade can only be performed on local repositories. It cannot be performed on remote repositories. - If the repository is in "keyfile" encryption mode, the keyfile must exist locally or it must be manually moved after performing the upgrade: 1. Get the repository ID with ``borg config /path/to/repo id``. 2. Locate the attic key file at ``~/.attic/keys/``. The correct key for the repository starts with the line ``ATTIC_KEY ``. 3. Copy the attic key file to ``~/.config/borg/keys/`` 4. Change the first line from ``ATTIC_KEY ...`` to ``BORG_KEY ...``. 5. Verify that the repository is now accessible (e.g. ``borg list ``). - Attic and Borg use different :ref:`"chunker params" `. This means that data added by Borg won't deduplicate with the existing data stored by Attic. The effect is lessened if the files cache is used with Borg. - Repositories in "passphrase" mode *must* be migrated to "repokey" mode using :ref:`borg_key_migrate-to-repokey`. Borg does not support the "passphrase" mode any other way. Why is my backup bigger than with attic? ---------------------------------------- Attic was rather unflexible when it comes to compression, it always compressed using zlib level 6 (no way to switch compression off or adjust the level or algorithm). The default in Borg is lz4, which is fast enough to not use significant CPU time in most cases, but can only achieve modest compression. It still compresses easily compressed data fairly well. Borg also offers zstd, zlib and lzma compression, choose wisely. Which choice is the best option depends on a number of factors, like bandwidth to the repository, how well the data compresses, available CPU power and so on. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/global.rst.inc0000644000076500000240000000306414601576577015735 0ustar00twstaff.. highlight:: bash .. |package_dirname| replace:: borgbackup-|version| .. |package_filename| replace:: |package_dirname|.tar.gz .. |package_url| replace:: https://pypi.python.org/packages/source/b/borgbackup/|package_filename| .. |git_url| replace:: https://github.com/borgbackup/borg.git .. _github: https://github.com/borgbackup/borg .. _issue tracker: https://github.com/borgbackup/borg/issues .. _deduplication: https://en.wikipedia.org/wiki/Data_deduplication .. _AES: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard .. _HMAC-SHA256: https://en.wikipedia.org/wiki/HMAC .. _SHA256: https://en.wikipedia.org/wiki/SHA-256 .. _PBKDF2: https://en.wikipedia.org/wiki/PBKDF2 .. _ACL: https://en.wikipedia.org/wiki/Access_control_list .. _libacl: https://savannah.nongnu.org/projects/acl/ .. _libattr: https://savannah.nongnu.org/projects/attr/ .. _liblz4: https://github.com/Cyan4973/lz4 .. _libzstd: https://github.com/facebook/zstd .. _OpenSSL: https://www.openssl.org/ .. _`Python 3`: https://www.python.org/ .. _Buzhash: https://en.wikipedia.org/wiki/Buzhash .. _msgpack: https://msgpack.org/ .. _`msgpack-python`: https://pypi.python.org/pypi/msgpack-python/ .. _llfuse: https://pypi.python.org/pypi/llfuse/ .. _pyfuse3: https://pypi.python.org/pypi/pyfuse3/ .. _userspace filesystems: https://en.wikipedia.org/wiki/Filesystem_in_Userspace .. _Cython: http://cython.org/ .. _virtualenv: https://pypi.python.org/pypi/virtualenv/ .. _mailing list discussion about internals: http://librelist.com/browser/attic/2014/5/6/questions-and-suggestions-about-inner-working-of-attic> ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/index.rst0000644000076500000240000000052114601576577015027 0ustar00twstaff.. include:: global.rst.inc .. highlight:: none Borg Documentation ================== .. include:: ../README.rst .. when you add an element here, do not forget to add it to book.rst .. toctree:: :maxdepth: 2 installation quickstart usage deployment faq support changes internals development authors ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/installation.rst0000644000076500000240000004103214601576577016423 0ustar00twstaff.. include:: global.rst.inc .. highlight:: bash .. _installation: Installation ============ There are different ways to install Borg: - :ref:`distribution-package` - easy and fast if a package is available from your distribution. - :ref:`pyinstaller-binary` - easy and fast, we provide a ready-to-use binary file that comes bundled with all dependencies. - :ref:`source-install`, either: - :ref:`pip-installation` - installing a source package with pip needs more installation steps and requires all dependencies with development headers and a compiler. - :ref:`git-installation` - for developers and power users who want to have the latest code or use revision control (each release is tagged). .. _distribution-package: Distribution Package -------------------- Some distributions might offer a ready-to-use ``borgbackup`` package which can be installed with the package manager. .. important:: Those packages may not be up to date with the latest Borg releases. Before submitting a bug report, check the package version and compare that to our latest release then review :doc:`changes` to see if the bug has been fixed. Report bugs to the package maintainer rather than directly to Borg if the package is out of date in the distribution. .. keep this list in alphabetical order ============ ============================================= ======= Distribution Source Command ============ ============================================= ======= Alpine Linux `Alpine repository`_ ``apk add borgbackup`` Arch Linux `[community]`_ ``pacman -S borg`` Debian `Debian packages`_ ``apt install borgbackup`` Gentoo `ebuild`_ ``emerge borgbackup`` GNU Guix `GNU Guix`_ ``guix package --install borg`` Fedora/RHEL `Fedora official repository`_ ``dnf install borgbackup`` FreeBSD `FreeBSD ports`_ ``cd /usr/ports/archivers/py-borgbackup && make install clean`` macOS `Homebrew`_ | ``brew install borgbackup`` (official formula, **no** FUSE support) | **or** | ``brew install --cask macfuse`` (`private Tap`_, FUSE support) | ``brew install borgbackup/tap/borgbackup-fuse`` Mageia `cauldron`_ ``urpmi borgbackup`` NetBSD `pkgsrc`_ ``pkg_add py-borgbackup`` NixOS `.nix file`_ ``nix-env -i borgbackup`` OpenBSD `OpenBSD ports`_ ``pkg_add borgbackup`` OpenIndiana `OpenIndiana hipster repository`_ ``pkg install borg`` openSUSE `openSUSE official repository`_ ``zypper in borgbackup`` Raspbian `Raspbian testing`_ ``apt install borgbackup`` Ubuntu `Ubuntu packages`_, `Ubuntu PPA`_ ``apt install borgbackup`` ============ ============================================= ======= .. _Alpine repository: https://pkgs.alpinelinux.org/packages?name=borgbackup .. _[community]: https://www.archlinux.org/packages/?name=borg .. _Debian packages: https://packages.debian.org/search?keywords=borgbackup&searchon=names&exact=1&suite=all§ion=all .. _Fedora official repository: https://packages.fedoraproject.org/pkgs/borgbackup/borgbackup/ .. _FreeBSD ports: https://www.freshports.org/archivers/py-borgbackup/ .. _ebuild: https://packages.gentoo.org/packages/app-backup/borgbackup .. _GNU Guix: https://www.gnu.org/software/guix/package-list.html#borg .. _pkgsrc: http://pkgsrc.se/sysutils/py-borgbackup .. _cauldron: http://madb.mageia.org/package/show/application/0/release/cauldron/name/borgbackup .. _.nix file: https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/backup/borgbackup/default.nix .. _OpenBSD ports: https://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/sysutils/borgbackup/ .. _OpenIndiana hipster repository: https://pkg.openindiana.org/hipster/en/search.shtml?token=borg&action=Search .. _openSUSE official repository: https://software.opensuse.org/package/borgbackup .. _Homebrew: https://formulae.brew.sh/formula/borgbackup .. _private Tap: https://github.com/borgbackup/homebrew-tap .. _Raspbian testing: https://archive.raspbian.org/raspbian/pool/main/b/borgbackup/ .. _Ubuntu packages: https://launchpad.net/ubuntu/+source/borgbackup .. _Ubuntu PPA: https://launchpad.net/~costamagnagianfranco/+archive/ubuntu/borgbackup Please ask package maintainers to build a package or, if you can package / submit it yourself, please help us with that! See :issue:`105` on github to followup on packaging efforts. **Current status of package in the repositories** .. start-badges |Packaging status| .. |Packaging status| image:: https://repology.org/badge/vertical-allrepos/borgbackup.svg :alt: Packaging status :target: https://repology.org/project/borgbackup/versions .. end-badges .. _pyinstaller-binary: Standalone Binary ----------------- .. note:: Releases are signed with an OpenPGP key, see :ref:`security-contact` for more instructions. Borg x86/x64 amd/intel compatible binaries (generated with `pyinstaller`_) are available on the releases_ page for the following platforms: * **Linux**: glibc >= 2.28 (ok for most supported Linux releases). Older glibc releases are untested and may not work. * **MacOS**: 10.12 or newer (To avoid signing issues download the file via command line **or** remove the ``quarantine`` attribute after downloading: ``$ xattr -dr com.apple.quarantine borg-macosx64.tgz``) * **FreeBSD**: 12.1 (unknown whether it works for older releases) ARM binaries are built by Johann Bauer, see: https://borg.bauerj.eu/ To install such a binary, just drop it into a directory in your ``PATH``, make borg readable and executable for its users and then you can run ``borg``:: sudo cp borg-linux64 /usr/local/bin/borg sudo chown root:root /usr/local/bin/borg sudo chmod 755 /usr/local/bin/borg Optionally you can create a symlink to have ``borgfs`` available, which is an alias for ``borg mount``:: ln -s /usr/local/bin/borg /usr/local/bin/borgfs Note that the binary uses /tmp to unpack Borg with all dependencies. It will fail if /tmp has not enough free space or is mounted with the ``noexec`` option. You can change the temporary directory by setting the ``TEMP`` environment variable before running Borg. If a new version is released, you will have to manually download it and replace the old version using the same steps as shown above. .. _pyinstaller: http://www.pyinstaller.org .. _releases: https://github.com/borgbackup/borg/releases .. _source-install: From Source ----------- .. note:: Some older Linux systems (like RHEL/CentOS 5) and Python interpreter binaries compiled to be able to run on such systems (like Python installed via Anaconda) might miss functions required by Borg. This issue will be detected early and Borg will abort with a fatal error. Dependencies ~~~~~~~~~~~~ To install Borg from a source package (including pip), you have to install the following dependencies first: * `Python 3`_ >= 3.8.0, plus development headers. * OpenSSL_ >= 1.0.0, plus development headers. * libacl_ (which depends on libattr_), both plus development headers. * We have bundled code of the following packages, but borg by default (see setup.py if you want to change that) prefers a shared library if it can be found on the system (lib + dev headers) at build time: - liblz4_ >= 1.7.0 (r129) - libzstd_ >= 1.3.0 - libxxhash >= 0.8.1 (0.8.0 might work also) * pkg-config (cli tool) and pkgconfig python package (borg uses these to discover header and library location - if it can't import pkgconfig and is not pointed to header/library locations via env vars [see setup.py], it will fall back to using the bundled code, see above). **These must be present before invoking setup.py!** * some other Python dependencies, pip will automatically install them for you. * optionally, if you wish to mount an archive as a FUSE filesystem, you need a FUSE implementation for Python: - Either pyfuse3_ (preferably, newer and maintained) or llfuse_ (older, unmaintained now). See also the BORG_FUSE_IMPL env variable. - See setup.py about the version requirements. If you have troubles finding the right package names, have a look at the distribution specific sections below or the Vagrantfile in the git repository, which contains installation scripts for a number of operating systems. In the following, the steps needed to install the dependencies are listed for a selection of platforms. If your distribution is not covered by these instructions, try to use your package manager to install the dependencies. On FreeBSD, you may need to get a recent enough OpenSSL version from FreeBSD ports. After you have installed the dependencies, you can proceed with steps outlined under :ref:`pip-installation`. Debian / Ubuntu +++++++++++++++ Install the dependencies with development headers:: sudo apt-get install python3 python3-dev python3-pip python3-virtualenv \ libacl1-dev libacl1 \ libssl-dev \ liblz4-dev libzstd-dev libxxhash-dev \ build-essential \ pkg-config python3-pkgconfig sudo apt-get install libfuse-dev fuse # needed for llfuse sudo apt-get install libfuse3-dev fuse3 # needed for pyfuse3 In case you get complaints about permission denied on ``/etc/fuse.conf``: on Ubuntu this means your user is not in the ``fuse`` group. Add yourself to that group, log out and log in again. Fedora ++++++ Install the dependencies with development headers:: sudo dnf install python3 python3-devel python3-pip python3-virtualenv \ libacl-devel libacl \ openssl-devel \ lz4-devel libzstd-devel xxhash-devel \ pkgconf python3-pkgconfig sudo dnf install gcc gcc-c++ redhat-rpm-config sudo dnf install fuse-devel fuse # needed for llfuse sudo dnf install fuse3-devel fuse3 # needed for pyfuse3 openSUSE Tumbleweed / Leap ++++++++++++++++++++++++++ Install the dependencies automatically using zypper:: sudo zypper source-install --build-deps-only borgbackup Alternatively, you can enumerate all build dependencies in the command line:: sudo zypper install python3 python3-devel \ libacl-devel openssl-devel \ libxxhash-devel \ python3-Cython python3-Sphinx python3-msgpack-python python3-pkgconfig pkgconf \ python3-pytest python3-setuptools python3-setuptools_scm \ python3-sphinx_rtd_theme gcc gcc-c++ sudo zypper install python3-llfuse # llfuse macOS +++++ When installing via Homebrew_, dependencies are installed automatically. To install dependencies manually:: brew install python3 openssl zstd lz4 xxhash brew install pkg-config pip3 install virtualenv pkgconfig For FUSE support to mount the backup archives, you need at least version 3.0 of macFUSE, which is available via `github `__, or Homebrew:: brew install --cask macfuse When installing Borg via ``pip``, be sure to install the ``llfuse`` extra, since macFUSE only supports FUSE API v2. Also, since Homebrew won't link the installed ``openssl`` formula, point pkg-config to the correct path:: PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig" pip install borgbackup[llfuse] Be aware that for all recent macOS releases you must authorize full disk access. It is no longer sufficient to run borg backups as root. If you have not yet granted full disk access, and you run Borg backup from cron, you will see messages such as:: /Users/you/Pictures/Photos Library.photoslibrary: scandir: [Errno 1] Operation not permitted: To fix this problem, you should grant full disk access to cron, and to your Terminal application. More information `can be found here `__. FreeBSD ++++++++ Listed below are packages you will need to install Borg, its dependencies, and commands to make FUSE work for using the mount command. :: pkg install -y python3 pkgconf pkg install openssl pkg install liblz4 zstd xxhash pkg install fusefs-libs # needed for llfuse pkg install -y git python3 -m ensurepip # to install pip for Python3 To use the mount command: echo 'fuse_load="YES"' >> /boot/loader.conf echo 'vfs.usermount=1' >> /etc/sysctl.conf kldload fuse sysctl vfs.usermount=1 Windows 10's Linux Subsystem ++++++++++++++++++++++++++++ .. note:: Running under Windows 10's Linux Subsystem is experimental and has not been tested much yet. Just follow the Ubuntu Linux installation steps. You can omit the FUSE stuff, it won't work anyway. Cygwin ++++++ .. note:: Running under Cygwin is experimental and has not been tested much yet. Use the Cygwin installer to install the dependencies:: python38 python38-devel python38-pkgconfig python38-setuptools python38-pip python38-wheel python38-virtualenv libssl-devel libxxhash-devel liblz4-devel libzstd-devel binutils gcc-g++ git make openssh .. _pip-installation: Using pip ~~~~~~~~~ Ensure to install the dependencies as described within :ref:`source-install`. Virtualenv_ can be used to build and install Borg without affecting the system Python or requiring root access. Using a virtual environment is optional, but recommended except for the most simple use cases. .. note:: If you install into a virtual environment, you need to **activate** it first (``source borg-env/bin/activate``), before running ``borg``. Alternatively, symlink ``borg-env/bin/borg`` into some directory that is in your ``PATH`` so you can just run ``borg``. This will use ``pip`` to install the latest release from PyPi:: virtualenv --python=python3 borg-env source borg-env/bin/activate # might be required if your tools are outdated pip install -U pip setuptools wheel # pkgconfig MUST be available before borg is installed! pip install pkgconfig # install Borg + Python dependencies into virtualenv pip install borgbackup # or alternatively (if you want FUSE support): pip install borgbackup[llfuse] # to use llfuse pip install borgbackup[pyfuse3] # to use pyfuse3 To upgrade Borg to a new version later, run the following after activating your virtual environment:: pip install -U borgbackup # or ... borgbackup[llfuse/pyfuse3] When doing manual pip installation, man pages are not automatically installed. You can run these commands to install the man pages locally:: # get borg from github git clone https://github.com/borgbackup/borg.git borg # Install the files with proper permissions install -D -m 0644 borg/docs/man/borg*.1* $HOME/.local/share/man/man1/borg.1 # Update the man page cache mandb .. _git-installation: Using git ~~~~~~~~~ Ensure to install the dependencies as described within :ref:`source-install`. This uses latest, unreleased development code from git. While we try not to break master, there are no guarantees on anything. :: # get borg from github git clone https://github.com/borgbackup/borg.git # create a virtual environment virtualenv --python=$(which python3) borg-env source borg-env/bin/activate # always before using! # install borg + dependencies into virtualenv cd borg pip install -r requirements.d/development.txt pip install -r requirements.d/docs.txt # optional, to build the docs pip install -e . # in-place editable mode or pip install -e .[pyfuse3] # in-place editable mode, use pyfuse3 or pip install -e .[llfuse] # in-place editable mode, use llfuse # optional: run all the tests, on all installed Python versions # requires fakeroot, available through your package manager fakeroot -u tox --skip-missing-interpreters By default the system installation of python will be used. If you need to use a different version of Python you can install this using ``pyenv``: :: ... # create a virtual environment pyenv install 3.8.0 # minimum, preferably use something more recent! pyenv global 3.8.0 pyenv local 3.8.0 virtualenv --python=${pyenv which python} borg-env source borg-env/bin/activate # always before using! ... .. note:: As a developer or power user, you always want to use a virtual environment. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1125643 borgbackup-1.2.8/docs/internals/0000755000076500000240000000000014601577064015157 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/internals/compaction.png0000644000076500000240000275146214601576577020052 0ustar00twstaffPNG  IHDRp)OQgAMA a cHRMz&u0`:pQ< pHYs\F\FCAtIME #~wFbKGD !IDATx U%AfJ"J4ϥ$hBe*SIAeBfHI|}:nweg?swkkEd2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&S>˗G_~e4whٲeѼy={Fs̉sѷ~kd2L&d2L&d2J6l~Ǩvؾф |{'}F|I4}tv}]8d2L&d2L&dmYGo~ѫ[v^{Ϳ/x7ߌ6n;6ꫯL&d2L&d2L&Sno?xHۣGhݺuG >_ԿKXg}ݨI&;-ZGN8.d2L&d2L&d2冈:uj$`_]tEN)xq6f̘N۶m͚5+СC4yd~ɝk2L&d2L&d2L)/8p`ǚ5kUn9sJ*xA ,(z6lذ￷i2L&d2L&d2L9^z plꪫ\ʕg9WZ5wyyVZ0~R[*l2L&.3fvw}7… e˖1iҤRYF/S G@k;omAk;7.z79lG^L,YyGׅ&~-X ݾ2'kڴiޢ4 ۴inMW\q V Li{_0a5L&iV:E>>A?=þ1?T OQFN:EzG{lTlOsoV+xҷoX޽Yֳg?W=t{nݻ۵CZ^bOڿqǥ qqH?soꫯ4ǿZWM]v~֬e+oߠ?k;]ߠ_f-_x=i-L >#/EWkkw;>D/]FyQnݢf͚uff|v>Ow&MVc_?ξd2v=.] ,Yҿψ)⧱l޼9<~`F @%:QSޣi|,ӾbeHc(o2ǓG>lSOZ*b 3#޴Ç?_@;vb4 r,yGBt?i=ZMv'ע{m?0[u[m;^՟'8Ik;MnѢ{mZ[+kk~|˖-h־})|>LY.hJev=I;MmOWltzFяϟ?C/NGW<% S?-V7n֬Y~&d2{e8<`}OHꫯz U>? |Engh٣O7mp-d2v *UVd/~NQ/x3xwС2@u)Dc@Xv4/Sexf7ns=> ~Qݺuvk}.b{D 푃L?n>Ӕ#fV\\F'MLjy 0it P|z{|>nzypXuZ%JDGqDeZ7~k#O=a@Anհ֬I7apwϥJUvTR'Sr?wּ|5ky͍xXp֒ҲVɼO?="UŊ' ZyQ"E81jlKp@?)?h:+9qTq qZ .kʏ 4 )ǧYвN6|4/Q\5L&SQr*bSSTo6[#AbDr{[T, />߷_x!"so=RJ34]|e_={+T* J{ItJPC@pCZ{:AߕWmJ/1Lz`.J|ݶ=#YS%kr7Q1M?KOf`Z^ ֺ\oSk q"'܇5j8ݗ*Jwź%M&dʷx4 [P@uR5=ϊtGƽHz({8>x\ysHuu@8*M{ٳeڲNyuk^ TF=ܩ(~q@wV8F`⒣Wwh$IVXxک?+hE7g$. n&S^xrK.0rS|IoS\~vRhA(MM} .F"OQĩs"D}ʸ/rW, pѳwSJ$-M1/Rj4#p?Ӭ Vh=_rM&dwZpOK ]ve81U/ |*JzeޯJsN9lKs1,{91i)>jD5B 8׿調kR>ʳ{4R/"io8QNQ'I8 竕A]\rNNT7F~[|H*j 5kpMpY3kHSLwGE=i3e([4|(RY+^4bĈ))9܆gNQwHk2L&S"%q$|M? r8 )}|`t^WdO ymdך5&֬5Yf?4muҺ9##]BTlߚ8h6X}[\u#p?grZ d2K-X\=p&NRw7Nׯ_KoVSLi(/BUm_͚5+(JMEUE҃i92&%_ߤ*Pm8- cY"}j*GiQ-;~\OM֢8Pޠe;Rڅ$g߿y?/s#r %~ݷހ3fEp Zfdך5~M%3Twm{SLԴi*.zWqFQdVWA=W^ig r7xxn&dʗZ|y4gΜh˖-<|NiVQ)UBeAޢ~yE>DuOCr }D\;[6 .X`5DurT>f .-}LK496UN8ϔwNK$sUwF{j@jj ?.@=ezGJP_0xSo-U'bާP.ʯ@8`pԦ=(\. 5kpMpY3kJOQfŨ20O:9ZUXӯg7.W9RuR(֮]uC#d2LJL}yEU ߬䋍(F뮻R+ǫjTvm'`;@6l[ xtEF~t% Bb@UZ#弍q#H06RWAW~:uDSNeJ߾=D/RQ׈ ZZ(rFu5R%X?B^Xܷŋ`4 gώedך5&֬5eqI3Qu4cȡF4. o%7j n\efի*fg-yp/њL&)iʕ>B)bLPTw@hLS2Dt}~"(;;vB N[_E),Æ bb"xcJS2L@T@-gT,FDC>+eBL9v} k6FW e|㖴 ǯ_O?%On(t Te)7ʰ1hq(`6tP&֬5f)C;SA-jTJ?W_ttWtufoPn\(T&R)T Km5L&S0ѢE_~'[>ZU|C7eɓ}FIa@j붝mSI}*)Zhh]E{zت/F.e`/b?#ժ6 mۼys+q[R+W?3^8)ł/aߔ^`K,2pD Z3kdך\&3g>}~FC(YY  ,U?ܟ~)-\Aۍ7zk2L&SѸD.]4!UADCDR K':N\DzK,U>#f88̌J/*Z8^ qLq.^o7l\R3uL6!<Fd0yF/"njAI|+[ qw|H5jI3xAS$AǃpB.ξ uTgωt;V U)r2piGo\|d N6 &pArSJ{CGQoA E# .0h /k< Z3Ea_J)S(xgk"  u{Ȍ miHvY,eNAEZ|q癊 g1>_92Es8ҨQqT<g,Bp; A #jjUVn7cѣ#fqф Lda4F jAl"Iߚ;ޞ`),|52Ag6h{̬ף_qtG=קٯ=UP@'i,IcƟ\JP p,R&dڭ@|g=y @G-=:@mӦMKD.BQ>QGUsHd]HUUPN63t~D.aATi5>ӽn|H㐒"쏨f}yӬSV tHpv*3DCBxLyEZIhJ$ZN 7p1<xE 5{x=!:)Pcك~d;,pχ@:Ӊ\A_|qOEZ\MHUl:@GZR3(d`=3S>b vRa+|'<p}o h7ldܿܣT~8 v,^ΝXca`_?<ߚ;ŋmN=P3^t 2 6>0vȎ6g-d|yA^w apM&d]1OtvQ HD:]ԠmD]":%Swjt Ln_+N.)Pt\h8!jG58Ao`0TX;Sl#@ex7n.q`: 8㈥D^࠳=~#p\pP,#XF -o\_r;>ԍ;U ;Tp>,a&r>}A4/}\?RCo H|;%Y8gX~>!<ؖd@oo?_,˴+t)׭[. oCJ:QϠ0Ž31rG>g{ -y ̿/c )‰-^nX%^l< JIvʖ-T\!52-"–  Ǟ{@1eЖp?]8fY+F{2ŲZ(75hS w3۝H?O_2"g־ж,O)ڬctLtʹq=-~aq( -&!f `#a|(p5wojW;Ƞf b/>hsL#ày;RxykٱkP=|kթk߹{vL',psj*X:}kpkw 5Hta=}8_5os|p1?J`7sMj\\UQUukjs)x;.nI\to~k@pٰKET8vmo+{NvC)6ϻ_4W8q^@>?X6E?dggPBl`lU :fKrKm+;WO =,p) JNTG /Cmֵ݃K\&d2v 9ǀl*l p%JprҗjDYElsTȹ-#P^ۓTy$)* F19 tX,mp<3QS/RFG"р ŀ"[ҲtIQ]Np THڬ(0:l -RDC%K&{챇ӹ*h[e˖pz-{{M9\GubC:ޟ}t (N3\`HI_#2{7M 4a@Wi~ r_>GQ]n_L&jNQ{ ="rpޜP(6>R4 vxQ7a#XYYv`knMvGzHs%B{ VpǝxB{Eow1K7ԫs^s=wKm|0q/g#C41_+4f'agD*`"pX\Af?ձ7,/s=~ ?eAl4"wŀb6p Y3&2CC1_hq>?:G`3l,S_ir#^Hfd2L pI@S|MTDYraM8r֗(SM.a@%bʪÉxyhX9zM`"f':@XEv`}E^91")tGԭ[w瀜^q/;;Mw'~5˝^xܕtgV'GW@njq [MF$kwհHsz@9eJ꧜hX8 'O-@Ω2([Dtnn\g6=oN5}Cv"͚{|l΂pұgsdC"do=2![zIz b\0ﺗfJɖ-̝-08kPt/ua> O=ʝYr^DVtwPɔgLtT/⟟nuCJ,Q#vTõP$otg}~{8h?7st :kmxpqwglujgW;͍xKap~=kn .R^9gON#% }_I (PW;WJZ~u8AӗdOG![eWm'.P3*bF"3wޟ`w}5@JmM#.e#o*%Y0߅mvr3S\d2L;1'w"|ϩVK~8?#βF"!D{ r2UMkrD-0NE W@>YWu0n֭[$Hɘ ?A٣xim/ AI@g88 6U/\P4(EGɮ$ [F^S}|9&{!e**'[/PuzWod-`DӎE6& x &")cȍ4UqQ2}g~!M=W1Ew{(.f)t,02^Q XlgƁObe5q@ #+dn\h .s{WrWchϻ=@{Ws[yoh%WWMs{eIϛQgZhpuO1绿В6ᕉ+**{gjynA zA՚׷:ЈvG\ ֹ_[p=}!/p%cGZ V`fPbhD"`+4|]^ j{s$\yoO>l?믿u @Ia3+Hi&c%iOڋ/Ͳ˗/7kd2L&pVǍZ^lU$849y]+'/1G)t"m#"KiD> ZęR fLmc9b0!P/2[" 3qOt-S9b6d؛BD)h QD w>?/YttR\}ދ `947N·g x'ɉ^9 u[N*1SpȃdA>lXkݛ<@)^W-9bYgž8ާM-&wyH1CqÔv݋Ci\˳j+PI3AIsQIkbl/EZh^E)lYg; aG<61e6Dp&2PNƈB=}ǵДܒ䮥(#QJcQkLFYÉ3S{5vcد8)%Gm\rh~>?lXzS7= PyىՏ\Eg{>zw^n0 m)|%t>8wtE9 +0 r4,8+tG sUIIgYl#pyv}\ܴL.%!ۃ]]o}`czq*Px9vldf HVzW[cxF0{BσdI<6B4VYiTgy?0G^#Zq:e/[?Rގfc9&g;Gsx q< |\ܬ ;ݙ.`EM`0%K"X_rb#M^@.Qb9bVY_d?l`]>'իlX(@e0M64@`c̼b6pۑ0D;v\d2L;1Gz[w+R# :>g*DSz+cYMsFߡ;th-ߛ r*Ҫ4p1,;BP(Ru3 TXXx88 -s.Xy:V8LQHRs}tb"0T8f] @Q}=JP,>EU8o9sm>d9dWKî)invN"J7?RΣP<5Ɛ{rk_kzgkzVl6d&+iR p fp)@P'9QB54.Q_D!2)#`1G//}uN/Uz- V;2235j ˠ"Qd]He|)e~bqcNWW`;둮 !slӗ ЙiӵbpN򹧹9+_P-Ļ 3[CNp_*S_ \ReE\?T3 GԎ΂>WZχ)-ZGӲ]rISvOۚkd2L ʡBx汊&mq52) E'V,ٞ>}zbT 60LcH 'dtS\ApK 1ZR*`Bj0׉S>Z lqi&:QϞ= |ɎpݼyssCSp KDq}R;pt H#>S=Fa)2i" N "9>:IM/e^i"̃_H5.pS"ДG/H@X}C[wqǨ eS[طgߥ.)Zs_$RB,#M~Uֽ g if!0HaI ܝ&{ F[mc8$ 21F(W.3=;"caNLI.9nonxI؟)KEϒXϕ9hW3m,׬]E p˹ޝO E$ڊ\$2_U(^II pzVBm%G3\H /Su_@fVeyV0O%r O:ɟF,"~:*9}S>ӴЦ@u(YPWޜ7 <yP' p=;䙤E~+sq<xcʉe^j}[hpSK@9FST %V jx*5||2kp3 `Hr.a&@c RR,UMAKd_ޗ/{FyO@F4d`!\GD6t7̚Р%\7էa0밻\VO).vA피 Fſ.ҔT1O狚e=wJٲjT xEt+_a"R%]ZgO7YqyORX0(%ݖӀ_`>?fv F M[S@,hQRaͤA D<(rn?ld2L\Ɉk)6l8H:e!WTtSXcׅ--??5 ю>h F*a~yq$WPC7Ǭ d 4[LV ,TMQeKd/U SW2ώ(B9U8LЊ-Jdr=6pi܌ӭD(wJ,s4yC1:ݮ:ϕՖrᙃ9iʏ)H'z~?˧&J|~"T |Rm8ѭ{0V&>{*f8Gk-Q-Ͱmu{<p*"QWl0XEu EM~yӔ|Il'Gtx e-ehJu_M-N @0J( ]cG3"xd{Wώe(5a@w}ݚ/!p9 gIŋ 3Y]\o~3/Y{f{&rI>_m<>}`|wx1@93+\+ f b?-J|oDN\,m)6< 90`<BƜIu;tJPsfP K5bk5k2L&i'>Zu4AҟyHs|P48z0/Ot@A81u!_ 0 ,ο"ؐt L'`-ɁG~0%L4F(m (B”>@ Z)l Ȯ817Wx/ĭ͢xԲ @#j;:Y+(khZg߶R\M--p)w%-ΛV8`'r`8Yf;[R*>_߿RGdИ_.٥)Sx-yp̆Jwp!%S"N.urخb[ 5Ol-_^Eaa L$0".Th{焠Ư! \d'- *3F15 Y5q/xQiw) @ **˹+q{0B2/KFlN pЧWLKs p}!4XC:LK.ƾ{[Sz_=DcWщ{*\l`-A}& G)(DדF*?qS7ҵ/Kf~АUG E?`i/ǯ_B̓ ;#͖lkHR:3kd2L&Np}KDr+wCGx**'.W싉XZ*)*W7mZ ͞=*z"]b:8 G.J91r)kM~~5X0V9YȊ9?5@Ɏl< Ĵ4191^-*pZ p |ZAlP4i+K?a:eʑlL~)t:L֦B)?ܴZ ]/<_Owmr1ğ{h8ĵN~y@}g-m[RC޿/rY˧Eڒ%nxV`P\I&e 2PW~;d{G}ޥ+} jI٬> TvJGv c<;T$r'|Jرck:> s6kd2L&Np-JnYl}Jj( 02G`qF']l-"*B;Atq F9-T@& b#;"+a4w\8Ak58pS.}a3 K'Dqt`"D5ȡSm0A>d SǨ'V_?mJ Re& K?5Uy XHb:\7% 弪74C˧.O#%ydo:V+6w\+ƴn8.DvgDm")2?XC\ݻϻuIy͠q f vw fp>Cu:s4]PlOa@ +Knـ( p?j\@B2셽Wzl'i =BDZ6] (XYAٳTľZ|ZG,m3hQ<7 H>q_;7tmz nzM%4rǒ" -););C="f/ތ~l)YIog}ݸL;Zc**$z2q[t_9W>|š.[>FX PW۰qw}\nڴ)pes= Z ˃k ?E|_A\T,ؾ3e*2(6yd}Y-mTf 2XbMER@C>l㔍Q:p L&dI"mWੜX>/+iHy @Z#<d WZW@(%j4`N? \9R$"9xpEYT[*)Y]Q^LIVk5΅\c8v?Z(Q\,b іS1Ht08Ȝ+9p/_*}gXN 5䨣ց8D3mX۽"ClSQ+.%NW~ܡѩ 2mR;%7dQAOx4ܯ?䮨x_؅J]^UdOM -^ʸGYnܚU$ q/r*j>Z(9 8r/"@srkec ؗ@4/Ck*3D,t@FG@vE='`UH߫@:8r,gn"ƍ#A?{YWrW,̀U7Ͻ?c@rʗSpn1镉먴"f#-.QʠhB6 E̲!<ՇWvkˬWg~AYT2hΠ>1鵰Q MO1-t>v?p6UrSXX3ZR%}3 -k>#yuO9Wz~;Ѳe˸eѶ-x6)h"FQ[}~OH&d2v=FN35CHaȯҺ{2 G.pLaWn8QJ߀D -KNZ: d(uH@ENqݫc9C8/r| "K'9ȘFd jO "hQd9pm,;86ה(_A:w]ZTgt̻"pW#~قtCbo#^Ke@IG#-gMg9piya(V4~f\TO-uTҹ}^רD!ut}A߽tzI( `/Z6dO5c W_}0~m(HCxL =z1x> 0Z~20ԦW)pB+ 85fcec 6\*钞 7n4 dNp@zGk<Ƽd!#JsEӇ9Slh s~MIcв7ԫϭP3m1'Ys/?8[߸PPe5r0a'N[]{c}s/y-zC屓Nx icɂ! hi:6ߖ8ZnUt1-#Jnj,l"-Q  it/?6lق/5*Nz;Nz';6s+yKx}'`BN <z&@+@ Qn0,@爝:&Gfn!'nd_:sSX۬]̋tuӖEL^çC/9xJ/vh9fڮ prseCI-@PӵZ_Gw; b_ x=p衇rη !j_;a gǎ_3M`3T x~}-G@QJ YMC6f^5k2L&).y>?@?u'2No B~!`#3Sh(%@"3ZAwCߨg 2 W`3MsN4/9ytVċbc=%G'EsO"]x S545dGYQ&>'`g^HK5g]:\9`ׂo}$w4I V"+*R]\Eib:-t,k_3Up}. :fWa/vuCϩ5k~{mKK!t?)P.Eצ>_m4DaRBQ1prNJb{\F}D0EӮeG׾OnP4 ? 6$3ƽ4\p 4+CH" "2 BZ!->.^48@$ͬ'[͊0QDc4&/$~6]<m| c}MW 8YD1;.p 9ub ν04Aנɕn3J6M Et=u}uF3rWP O=ʍo_Tyr/|_dWρUs<Zνj*[ÜH֦#}a|Gx_ ʎ%ۇAȊ-j@pCn DE]}IPr>eS8KpM&d21p^d DfԀ Oq4)V>4BpC-MyAӉ6@3)#BN6TO~N&b( "};Pzea9MŻ0A cDs$SHߐ9Z ̙s23ܒe4λ"[R'y\r\$F-Q͜B̚8xn$N2_O>pGD_?\D~CZù{.78ny_t-n.WڶѭCk+\f\jfQp=Wq/3 LOD.^ Ro`["SSo;  .J 0E鼲"D :vÿS xbV!+H\q=s[F4zA燠\}71pid2L<B %qK7)7ִ*:ɉ{FPwΉТst| ,dڡrj&AMD)zj>} N.O")# R !SGsDaAL"h^u hﵯ XhaJׄB_efCw*kU$_a"t,cqoRVй}h-H ?ιH`MC1fM}lT˲,E3fcp>ɝ{I1S.iOvv\w\Dw~Jםy½YI@ʁ2殭\}Sٺ6:C x~2/\{ v*m,wbiO/jWH>Ԉ̝wѢEF O"Q7 `| RB'U+VT:^Uvw6$O*krkxL~STHK0w38N%8H@t?]QXkew6A*!Z45(sg+BHz.=Kv`Y:^҆ʮp/^|;igFئ(WپQ/פ Y݁4wܛ8+kܱ'#zYg{'H@Jw_ά|;qy{Ege 0geg5\_8p?w.JUOuޞ'Gng@_۶?#A5 1azZdk@ u_Yخh /g|Nt}~/"ELd;H:+dlP6HT)XۛS}򛔚aQl*->1 j.Qp L&dc#ƴV{M38|>D(r74i>@T@Ui"t/u(F,bJˈ: gh{Wۋ(VZyGPd9qK#hfΏH#3vZ(-*9Kx pf9O 9i94 SjF(*4- 'Ny+Ct?=~nE3hQ*4EyH&#|E9Ak׮e)ɾ=@xWzgr>>@9ww7O9dy} we@W!dza;Y]pqǝh7)gRr.Cx:^> g, H^@lp+偷!{[4o.vl;~cK ut T&0:Q"IML~ŖcW8# sZhRgnC\d%׫RxXHJ+*+}&pW@sN冝?Λ y|BoOڄ4kwrM^!vG\p{4{yn3Q5A,._ᾗZTaEpg ]-v3_eUI65X_{|~d` ֱ& Q'&CVpaݝ4!ػO5k2L&).SlCQ88LMn<8ZC@ЊHM([D üyfo8B h ib;Dz=n(hF!Pp?8&ryGm8v"@Xh9&R, rrV˲qނyB'rĵo"yM}n|g,xslH3DÄsgY )%uYn<- y-zo׶5cOmG\"Ա /`'Fׯ6oBcC]{ }~ "n "l \n>q10 `/y{ӲCt pl [Y.y1:_~%S{Zcؕn9ʶR"PSl;"S>yrs P4أpaw;4LbP{yl{ P_Oj;za ?cq yk~B$h qZLk@ҹS\d2 f 3:8Lu%ɓ'c9:@$ʁ}^>Ph,&hY <t:Dm1-  xqȁ4ޣ\'uj%1%la S*EDe*YlyZP:S\I88x~\"I\Ir㸉А3䐣sAgd>c99cQ8>\? gE;{pStkDc?ۖ% A:l6 )b?ߣU% &qj7U)[n0( ό-vef m"ЉsS;psa  _2 0O᷅[lu 𙰃b@@ߴ=f(qmw3e2# {qG=5ȏ"OݛCc\{E+'.>8>\&d2vEt%jјe D X T:AR o":pԁiAE:8ӀY3D@)dDM@NH:qqMy{)D:P~msth%/$[<ĩu&2 7w vt5k#jVve p)h"lĉ}l`6%+|mp9'D0H~&@tvlaf659+-4y؅ `% b,|& lǎ[hePr|u˸9Gw~5kd2L&NpM[Tr$xFe>ۙ."yZp52k)UM{g+O2\d2Lpw U|=(Fm[ExtT"L  pY3k2k͚\ӎ`Ժ@9Oo,-QaÆ \fQ\d2Lpw)eJc `vi6WhQ֬5fi"w;R*ۊ *UD'pBK'\&d2 "rQrȅvKnYr2k\kpMOVXbE['f8G(*wP=5L&dwWEۨ,O.U)EN4Q|j7֬5f)%tPv*wަNQ=-JЌe\&d2 Zfdך5\}.̷ի7S_&L 7nJ*91bDD4kd2L&\\k Zfה"Q7h`UW]Y{ѓO>y[j=a1cD+W4kd2L&\\k Zfהz嗣Aaq)ڬYM6N:Xu׮1.5k73Nhʔ)>MG-ڸqdVs'S3c&dd2kdך\.PgϞQnݢ~bǎnժEUV]ۨQԾ@[bڴiц gey*#u;Y]=-ҥKDG6(@]ٞD+ˡi@!+UTTB[jxҩcVXk-eEufV`iUXzNS{ieKkYcssX]ҬY3'Xs>J@$&[J^Jjժ4v:rʌ"^#x衇OgqR0,^qFW.lԩbwvRcdIΝlDPpa2AD<)$+2W- 6< AǨ׋5~9>m_;5/fm=:&{o^ȹSL'E\>9^Vc ׃zjo1;v,!!~lR7ˊRК2b%GKFj֪[f;l|đVhiv5jU_kuJN7)Bkmjhڮf&[cEv5[8f5}7a@EUVEgKBsLMoNhu=M /9O7ۑf Jn>)v MO} X>$*yrUJ`%K,鮽aԯh+Vr:^^-gG++ C08#q _пXQI $2ÚBinڵ$'`*\"E8)gG1 Y6* iWx ʕµ 彡Cz8 ^Uǐcclg`%*JΘ`tLc<`JpF.k}hѢu;0KXВ:˰m|yO@dLX>k`]阔 E2`کݴU` `+@=$fEXϞ=cM41)&hĤN)))ќ9suO\Ljy0`M?Xlpe%ҹJPs=ㄪ>W^5йq ͘α n~acjol2ȑ# Jq>FիW/QlW *qy9}P/%)*r-,,,|pS7Xwp{YE7>ЀQ~Dyň*oF ^)KrFd@++3U(ߊ dfFؚ];fD,b/\rub_Cv@-IRN`$POEA7 '@_ܠ,\ =.IxxZ+yB2>Hԏ[5mz-R*O .uqNM^?Qp7իVQ#DŊ}nK!nP'emQS?k֬OAo(bRv#/@YO/Jdl\K= x9l @oիrAsTb$6#[ Iз׃d3/Ń֡ b*L /{~_98oE\{z>m{; _P+V k9/p}yfq32M ?F)ZӅ"F)뷣xpc[M~zR>] &QoTL?[`pO ^%-.uN~-?Yh\yOsdg >-goa?H`/TlRti謓#azhcAֺx%OH>;4PV < |Tmz0{Le9O9>R-vYՃgNG}RhI/s[}8b=Ѝt|;cAOS7l&6x)+}6Ga \#Qcs<'eDZ7JU)x}{+Aஂ~꟧u,wAZ 0g? m}ٷ] )1ƲwRhQ]a.jss=5j=a4F=omVFЩsp`g='G @plaaaaaaaaaaaaaaaaE!L qI:-gSmE]Rc5{‚+zqs1?u#t_UY"q8`"YW@' "Ӹ*`ؼd\rWܻwCԩO+%UZ~L@vk/ u0NJ'#ܹs߲8vS0]{`"g!(6y8()[^>*y A2y:} Ԋ/5ueEӓ%[bQc]5,  DzX1T=/Pb'Ծ7gKT;' rm(|p N)|QIH\)au|ZK4 _]s!pneOfycjL{ϯ#|\8]Pe`!Abb e*fb:fJi;L Guxyȅ{:P2>d,ZXo;l 0ݜ)% FEjqIg끷X36bO0_P5;2(m`ZyEyj@Oέ,VT{$AQ'1~fS?GؾCkRTw'<Cg6R}3uIΦC|(+dy*UȮ*qBx6ޭ[uB!!ZLoʜ(Ug,49Fw C]_ xΓX'x:*0[؍j<P)!B?^׾gt \*q8 ][&ޟ.\د>M| Ziz/E}vuII}Y%rm:}}{ᯮXn`i=>LZ~WgJe`d $y'8:Z*ց4M(5.ohv%YU,XN, Oˢ&M 3=}wWP3pP_u Iط,0ujR>2D^h;[/dP?YS QId](R8߻˛p]%!޹u%(K!SP>\cXd~+uK0*W&]^-T^z '*O՜W: `GJI/Au>VBǰ 0}O_^JTJ)^񲥾xb%'[NmȺtRo pkY5 |sʳ*8m-ޏ/IP^l_8U*RMժUgP?n H%9p3 I"\ڧsV 4En貓= 6X*>(a5@pAw3z-#{*qTGx_]*.VĔ-F mɏG9 a  8l[ejlW^-~n(N \̵l P"yL5*lww/wa(j{-|y1<~\ k1>ԺxDRJ9w/[XXXXXXXXXXXXXXXXC%ӧ<Q)QS3fs)/oH1 vTRQl:޷@0`cHΖ^*)GQK$#Ɯ%.S*/ee, 2{B y!0 8pߖBf U ,>LX@X. uj,%@$8(MI!.WgFP W T  $,}V |y66 IJRI!\uy pyo ȗHOV~lj`;Z <>"@Ս1W0oW r $[>Lpu1~QBwPc=<\+uQ~spXY`窔ЭX `2,,,,,,,,,,,,,,,,,H p|2+\Fk>TـIA0 sG۶m$=F &޿AMLb\(e:Z)"7H/N.'Uqdy|Y=pvXdj?@NSُୀPMaŐj$޽uBPc_.ɫXw Zo?*{J[g̙~OQ}jT_}UbT fQPTU`宓د h%w(3 6x0 d>lU qR{_9&[>Oղ!P (mJ ~9UW$PQ 2C@j`8[ ٮc;7V {it\g)Q\c'Ad40b,ŧLK [= Z|KY X66nS<6͛7G5;)25\%Hĥ=PVTB_ Lc{Ç$R?pQ. ~''{cY?g$R_U z C5r]գ! EQ u($DS?U V D*UCّzjIu½H6 ~? ty'`S$=C*[\ `tcOT bVK{KHq W`TN_2g9vs DE]*ZIѱ#JUΊO9 \i]X;\|WI Xڕ±%7QޮE,ww \ T=|Yp:/%qE+1 BA^ȭws[guou:Fp-,,,,,,,,,,,,,,,( CL;>j/شiϨ(shnڵN~=7R>$hWGzN+O?|d+s@EC' `_OW$s"Xr +,SD{>)-x \m7F!L[;&unbwA]clv)3I:|/ #){Qer@$>@ܠV?PO'Lk~$ji`,!oth=λ]\kb_ L7c6x+Y^IEb3Y"<)( p\k_Rx"7[|^M@7c Nm DefX(ؒC$(*Ȇ@PTYMbPUj~˴\lDnp1WLO>EJ{z,rA3O&(MFJ:qsU;2ΠH|%wr}RxtTE8^Jm.]z o8u¦nO=RPGa'B[!v6kaaaaaaaaaaaaaaaJM` ąxqr2LѠMSR(\>W2?CJvWs/Zx Jջ,@- 6rs)X_[IiVnP8J('Pw 0$ZxU+ۈp \~b!/rCq-;T_1K{԰PQ;ʊb Sշ5Z U. # hBðb4=t׫T )iGYͱ1O|cձ^65k}T$2oC9,z9*܀  [o%hПkARW)b vbRߤR;,STondFC(`+W XZ L -3CM;&S˖- |(o)8%ChEj+ sŋk\ԭcJg!(WM|A+I{ebFaT6@+eg -L@"qX ЯR6 vR-o"?V ^JͪRJf&uǚ(϶UK -_}꯹; ,>'\b mq^dk%&LpN3)cx0˪6l/t8qhmreX^&^ԖѨy_Ps!82kaaaaaaaaaaaaaaaoP aT/&o)_x[Rvp6=`+mW$`{֭[mQ__vkL 5\`ᗼa= 0B@PCB2$uA Tа%x'_ خJ\)VЀ|RE^XeQa 2~14َ3uv_OJ +cu9u2\:GFIWU@'o-7XYFT/yUNjOq<5u̾ (ipoTp$S. >`[@UNa5B`1Q{rBS8|&:]16Pg 1Amgl^o:z)>Ob&='Qmج kM/:?|ɿw!:}~ld%Uh3]|kxt\=b/[<1 *'/[A|E؃S0(q] @ۖـA,K`m۶LGd dLW`.кk)Luy:UR.kp(ek)o},3Z/h^ M^ #1)giK幍((}?ŲlNS{, ZNe&P4xbQ7Kl$% T}`;BIRce `q-E-U}+)mu3z?&97MII~6kĉ> S`z l;ΩF@SҁWTL4Luuٷ0`R5wƖ@^'N/~~ iWRc2h kG9ux`B?6 /:h[9PrK|*xMӹ(;#-uo? STI뾥 Zg=Ǖ--0k9t.{9"E xǦL~Nе%K">IA98Yy ^*-%)qc\nCS!-M` AHUi|cY'8Em9">) PhC{A@`0jD@MuԆIn}@З^z'Jj`rG1V UwGjPv,+)QhL,#d-S~{kQ:@бM߀l &֬YQ@ ؎s%'jYIz-f;@/ZWnG=˲ˑ䋿Sz?jRAۋD+\eMu]reKG8o@m?We)zqR=_# ; hq\I8'HXƹ6QkEͲ(_Eǚje)]UJUo+ Pt Hlp*Q$}$ P/9sJIQ9;3aP5,}{=Դ7l6AE ,ִ|,ol:C>y󰊈>s.tW^P;#`:} <'ۡ{9䌺Џ#/WI2/C0T(V ڃܴIBNf[/q '@/>Jm*'k=y%P ~cYK_/,d9A=e{(g98˲?7YK_ufb/Y I)-/O36'nޟuR^N܌@۽@N`$A 5!JS`?R#uN~=De= Hc ?F 2V=@4^ X hf=ad=C |{1f Wc$ԹEb. R3xs6m<6ñm@! ha@c`OSe'@4cBaCI8Q(/,<POy81<PbnXù9Dz<[)t=pը/}Ts_;R'Bh;u%b/`,P2`l7 $qe|u5MNo)RY> \D1WԎ B d=e"իW{%=cC/^Q<>Se Pj&eb=@Kg;B|'=GdcY& c EMLd&f(oI~`+~ƨ=}@MPBp\gI}Xxq aFf%P6YM]xHU[XXX fHq`Z+VվVF+Vk %`apGRN>TE$*Mbkooaaaaa ` )}hC~<;Orx2S!@,y%dc+=ϑKoL“v|k/ԱmUo S-6^w=FFJpK{}b08H>YftE쓴6p;차s>ۥ) r+9U4rΓO:cF)ӧ[ɖ2c3gM@iS:1+Vrp͙d?}uʞ.3`3M+Vr<6m/b΋ 6-,߿?c%b m୔+X'ZXXXXp z~L֭եJl)CQ\pAԫWsTiZtiq]vi"; OYldZoO]610YL#+!C/@b-fޘ]M ,ҥիW`5\b-Zvޥ'7k@q7{ɒ%ic׫W5m&MaJNιzo-Wk0wa V&\CJ˻6G4q6b%]vM\]f;+{hVT<5hN+Vro5<6uy1Ytp5~Xc:xba'm'o7o։@P| SI菉u:uaÆzF7P P5- OF0}^rJ`Ao,O!C'b,Pגn&a@OVBTq /ğ  فjKynPbw.VH!rE)vX쥨[bl蔕NY~VF}p|'rSrzʢXɱGR.}8Y {5kܮr{:|nKRF]2jV2קFq#r $pk6\|7N:aoRznx5Hw[X1^juY=y%۠QFކN [bEjSAP7~_ +uij>˓4LW:5ߥ^ӯ_?Ek;뵜j/ _p+lW7-VGXIQNS>ڪnݺ }NOc@H&3n:rϞ=#y[Nj2D˔SRE'2+>4 }q9-t+9V-}rluѫIݽe]dwK]κ}K7jWVPY〸pG[o9⳰f~s:t W Y 8M;PgWY\2N:. >(, :e˖NPj``2)s <)zּysxw[hZjkyV3fU !܈ǿʖ-,\ (vܨ󙖿Cu pժU[xl1`{Mʕƍ.ZCb<|wp=m[ 6%E<Ӯ.7 ݿqKVOwo|+9V 7U۵{{]dgz޼+^\+Vrxp- ZXXXXXd5)=^*FV_m2d >`>} :AXF}KźUUzO3;^I!{V-?ڼI؎,|}O\@|WioTO?AXZ Px RN'>{T?[Ul߈u?7*aY<WV-J5"$d6lOF>3?-D&$ఠE}ڷoQGp Zb+p- ZXXXXX>}zFg *)qÂFNb;)Rą@A*YOd`J 6DSʗ/?{,vRF!/_h$/J1LⵣT'߉ldiӦMj\+V Zb\ RF@ 07n\Կ@f#'K8l{1znGlڴp'bAяz~]v }QƁj//x hѢ$<ۨdw . GrT_֮]FH]er--W?)hヒ>ppk?N ,+ Z5kŊ\ V0kaaaaawC>yގK t^=^-)rQ>H0|dWgϞYn07 ( $?M\%F)u=-TP$ˆRNA+YN[o5Ҳ-ߊK ?%So^{m${lx뭷zꫯ"FrJt׷[noɓ'ݗ^zg+6ka+p- Zb %WY\r"@Pz*>G3&lf[XN4*U8yζ zob n޾@jw'27bB>gj{H۔~m}#}Ν{nʅY^um%0lߏ?jy^uz WZe\+V Zb\ J:9r$P)zz*~tCr/Qo5loAՊ(Xu\,oR]%5-[7o^Or^_~7n … }ݮ _3fD~:eʔh< $Gu`B"3&D?_X6d._\V0kŊ\ {7P7ǎHt I-Z(z=(ǬW?>ڶm3N}SdlڶmuN8Ca`;eRz*O^_2 'O]f_'H+2ȷ?7ka+p- Zbž ,`*Mj>`JvW(U|e#H H as=[΃Y5-_맟~7 .eˢŋ' *]^W_}yԩ~df|^'u3? pިQ2qQ#vrnS_"'`MQǍ<H>|p=7K^{-Zb_ni#`۟}}qƂ7ǂsX1ݵ1+o}66/3Q(1k׊܋"ftk׮>+$@Z|_#%2r%'<1_|-uzm#~Pwmf23۸{ʕ+oY9:03p-,,,,,= :Ԭ ,VFdJ^`~X,ͫ"%gۡtEbYw~ydr(7CA,cN c:bĈYfnUx;Շt\rt2#>.F E-{ 1$:ǟ Q}oj6f5}  M]&bRqYXԦ<@ t/tIpL0@⛾ } 01A tv85knN\)iIѣN׽VZ{x2WLqmڵ9֫WRZ ZߔԿ2fҥѰa-u,%mq/v>uO z<إ^q/KQ{3zRx Z1kaajY(QvCJirx$EMLaD߾}pra MII}ddz3gByQ xyȢX &@(3C~pG?֍7opw=u&n 1l3ϮW= (S rϭ2ff~TB=vZOJhp- ZXXXXXXdSൖ7Q(]vłHzW`*l8@Qʩޠ3R UX[e;(pA+PƢYgL`QrTFR6zQ2=s<e` dd%zik\) ,7@fϞUULj0E 駟@\,(@^훡z>);[c6:9(|^)I5Cu3ڼp`;]t`^EA&RNq|J߹R>+M[9TWMCw/Cyn]w/˿CYcEcó pU?z{ }4[-ەN{e].͘,Щ^ulkUMJNs[p׽쒚ds}^>< 1mj͚5Ymb 6u7m_j󐌇f ۘCP qMކ5̈ƿ[} ff0 |wZľCYWuG?nw;̝ &>k!mM5qSJ2@eܝO3uOwS>tWyxbouta|;K \ ) l׮]XS8]B\ѢE]"EPr5 (XePV># ŋGC)PgN*ZN|i  v*Tp9c.Ͼ$5.j.m+J(bNujڴӀz2X_35q8A<ձ8t,'ڛ-?)ƀcUo*ӨQ?[w:Ŋs$>_ b@L?ܕƸnvҩ E';Xm]]5ܗ6z`zӴzP,H%uj{,IN&߶N-b/u?<_/[Ե{!~ɰW<4(ϿѾ|1 ;X% ا ʟ1e9J,c҅;ʇ{_<|e~ѮHB;m:չ{pM^g-*P0.v:b]b%Lm`-}3t p*(lk9k8!+hРA O|-l|:-W#zN縎}{/%?>!?ah Ь{ߓ Nky2ef %]ŃS`pxЉeggW-!=d3~"KCxLwzSuմmrI@~+ڝp2yˆruQVaie~qp>($FL<͌7>6\-sO7ck/Srmtg_;1kzޥ-KU4Jfp-,,,,,,Jڡ|HNa0zo)Yd6  Xa@ɴӠ:{czyu+V&hT)ժU> wd+Bjժ1HQ^Nۨ_jмFC ! V֑g)ɅЫ pLП'k'?~'hXQZǰ 5 p|bЕ}ʟE t$;ȍ\kw=ZivV(l@VR|ݮZ2~6kz aC]93J\w;֨]}+qAXbE9rtlN,3b^P^'WI [l /^Q) $Y )0Z`~ ojWUVU]&uk;畻Aŋ%ꝶ6}v_7T4û5{o[AVRi&Cg<Ь Lװ#QuիWt_-[v˳=*#'kkc9j-G}cQ$UTiLNgsNؿpo kFOaՄ>Xr!×}›@~Sk&#,/öb@;iym,|+'Xr-@jT#ۯE*jjv+S,Ľ]nٱInۂ:k\pm< qgǯۗ= |-3v'_|_7l?+sHM_?=G|Я %,"sR>cp-,,,,,,YxJGg˯㢘V@e7Lԭ|bTJJ` Ȕ}| NHy=-6;X>%E?jJi@x~l iоY*IW WKpQ Ķ`TZP)օmh볍'$+C4X!@&VaU'W΢MAUZ}ё$f%Y6I>MMz2_~൑zvg#%-ͪKM }qT'uۀ @^ &@r^%9PDZlDDcAmN`#TGw֢Wn9TMڇ&$nIA෴Gcok{@#6 ԏk+]+[.5(? K'.wnׅfzKOr/-]8]=lS1`NM/ٝvp/#Cy3oL,z{>o; ɶK,~}&˼tkٮ_ޝ|}0uL۶rHԫpQ6o[:u<뵮_uEtEEJZ2ܯLR=lH׼Y᤮%x`mՃȒE(Ac~xM3,a)!lZI)[ 4\mזf.{G?]Gh[{‰@\}X"`i&o}^q `=WA~/ns rpow^Wsl :r'`U`Ww1mh~׮l)L'E ėS+V a@IH@njZk5 +eJR)HN/Ea؇_⛋W, lBxWnP?V Ѭ>(Qۡ7.ĒA?|ftK|5bO_aFXKk>lqDA_=HԦMJg8q >\dmYo%A'<<8EWgp!)f|->w?ࢢůko寧≽xh+puĜ]Ӯ𞸩˗-WA 5^q]q?T([ g^nx́c˞?dJ,lmƧ4|w+Z}d 0TCr6S+]ڍ9W\m%2 pIE߷gwC\rk,\ W\ Dxd``\:`i]8+YO< )(<^TOx⃫NW Z$!C5AKQ2h0ےA`l Akrb@[,+˰M!YU:DlN1Y~Dgm^Lm$I=Ƥ 1} V?a] E1Ma9@[ BҜd=pG'+KXD7vhX߭Vr3>_b1#jwlO\ڇUB3IW6rEvvgo3R⁛B{hWI}O-RYmw, C\:[y(n [sm5k6ȵ8@l6fp.>i&+UfDq;=5`X 3}C>1?`n~,|xh{'VC-A2Px+H,Az|j} aP_'L v MZ$[/7'GwV~=rn\w pQPT~5ȸ~pǜ/Ƥ^KwԳb"7x}xW Z01%Aٍk1!wT튰bq\G+k& L=fpI>C0X׀sIRd̏Bԫoo)(MOE`h/V$b۔?>JLf?$ |n5_VuUC1[/@1@ 5ؽ5`$KnɄαDKs K$$ȡ>v'nP|5R}tvM>ؾ pQ_v\gݥXC38{|=A:^FU{ ྽bpiiEȗ/{|(hI~ƱLIM~6y k}{R̺#9= f7q㺗-k#ƨ X >XzY%(bE˺ܧ.P0= X!T_{+Lxx.pb6r!).wS.Wqe+{mfd&Į$`S*5@T;\԰7O cwOHS=p^=]#=Q~ O l}dtqpi>E,>74wӨ]>[hw]_S VfN d}zu p+Oݶ13}b_C[F*W >zI羪ks--֝ea4xO:.=$O\}w$A/`_;Kj((x{^bG$=\ԹUE}{GFP/O1.:?cSi'.4c{"Szw63}l2{[ؖ-["W}.8~6p} t^Y^[ۨ5S;Vu gd9ףG$_?q sj;3`ECM%g leG`ԇ'^"oШ݉ 1ԖT@]z=Y;|JZvVqLr49$Gp-, ZXXXXXX4 ~w>lrA@ *I$0hX|}oUV?b\!P*K2gTj2}O4QR_TQ# 6_+@UxI_Zʫd)ݢ-}~ Fס@H!]<iBx:whPj;UA P:mra ȟ:mnmZ BUvO\qFnC&ɾeG5aﲿm)<b_Ɖ+gK=o}YbW'K}}ı'IvU:wk\[#J 8D.??\oOJ2M4:a׬%Wg/{[h8ֿd\J=8?XijIJ?4#+7Lΰ@!g|qțY;w@+Wܠq/lxg0Yw̓\ {ত`2d_bE5l`jbp,>$K4}$9Lu5z}>dj$Y)"+7cDbA ԟ"Ex^ XTHvQ*i+>8%)H7q.6wWPomoGuN,X({ puOC{?Y6])A7( 7.m+3-jp'^> pv۞ oi P-{v pQŽGWDlD;\UofcIƶۼé3op-,,,,,,  JLN IXF N6 V4\2$'.H@0J5;/U?[ux\ \V=c2P6b$S}'2e?<#%O[%;Vۚ_L'U'5~\]G1fPM x x6nܸOW%=Z#yEWijW КkcRJ:1#O(pCjPH|Q {{BK^[nm |:ڿ ͨ};q~Iw +>i5spy^uvjZ pS2:t?~1A׮h֬Yє)S+pB$غ",vS.+!?{JXf۶m!'&|IܟO"E;- 5T\~R{n%qZCj:r,\iyam>.U+sy:`$g %\ { r~Šjd*hc~Ygʹ{c5eRN#uDىʴ]va~&v` exjN JSP) WSJGHFqc)ݱ~ >?~|tR0}n/Wyn**JJ1_6W3n;~ @%PVm(z77Rމ`# eJP=~վD F )1OZ~ H,e㽲5=\z\VQ Km(wv̈́zRr+ֵjv-RkФPפew|vp.});)Iw)珺|9] 5md'R~ wfcӘs (kנ>}Kkv@{qVt]zŻ L_g8{%L ޴dj>cG s<]9$!9? t5;()4 W 5 A1k\Wb,(**Q(H\3`HVb`jmf|9 ^;N^vd / LZvc̀߂. ER掆jpԲ%w m`{bwN8+[%y>Ź.W&n?kkYeYefHTve6L2f9Y+!Qb.:̉ƌCk @g ʐ=NMOFwa>LEl,MUl`5"Y1Xe񣥳:dȐ<՚CuJ9kK ѳ>(o rq ebj?')0.*Pfʔb"Τ6:@n:a@ķ չ/7!k p->#@Rwy kؑ x_.YWCb\bn2=N.{+ۤ. ĶھF|3,߱Y +KFoeW;&ܐ}ѧ]9N\2,˲,k A:u]4dK&;l^i$>X 4I&U!~ pձ'4 d}9٨dz(h&^ 0]w(WRc, cJG\=q>ɪn:,Lԓ큛 p,0lxչI;K^z@m˶8Y䬑5vJgٖ>ׂnUpn{_٣e6($dpRJ ^֣#2A xpt>~+$YJkJo2eJXO?41ǀOG*{Szoʒ_.`WS)2K-ꠏO:zL=@5಍ĝ/ uϕm;Bv6wůұ붹ݶ6@)5 m[ԌP,^#yopl>P8~^Lƾ WI^䔿fx@.* < EG EV_@i\n*-_sljOO@ؿp`hpuZn\plײ p-˲,ZL5Cǔ̧'ൔ:n ke5ݭ ٤IGF |iDN $[.١Qz/*BǕ#l Qq1o9V8V93Tk׮)ʭo}t;IJ!̫\Ef-Y_(abe:,b&!X<\ǚ_t2s\̱ecdbeAf[_WƧU,e26 da z7y >0pInrYf2oj*-Ex=옚QkwmWf|Qe/-Ngx}N'X>~7o|~5;QcI{4;?tS<'| X0tOwwl9'zwr'* <5ce;LֱOSe%s>.ٝXE0!ZQmjZ^Bdz\Ap @ĉٳgb1V4J(wdGF|ϨL+bN5 7G2tM.-AMxI=ހpWB?:;}R= Kr&QyyF/tcy׉Weyȭ!6^-e[N\lnT۴|ŸYח➂$aDsl?lr|) >`,0/dr۝%M>G3ͺgf Gdfݼ8;uu@SX&yeZeYn߾}N6QՄ&TZ^M2ZkȆS( 4yV+)B ?)8:o% &w p)@ 0Ru_d2y:㝙(L`t q{#e*cAk豇LX5e@8,mSғʔRFs ZJ.)cv~$;HAaG079~I4$(#;FPF[}v]Upv[:PG<}VG.xxC=͞{'~Kx&7/S}RiY1T\GoY!^Ӻr}m9oe\ˊ\e!-#rniVeO&wz787S?J&/lzjyMbߘ4 VX'g<{K<'x}j.@1`Ax% c+z݇6I&}<P2gMy@:{QId*+VJY\:je^S':`ે)"4qfgDLu--Ƚ#wrG @l7eYy _.OڣyY\ݴB|e+9+[n;_ޟx,7 /ά_iMٲ-{_f9#.p7vyq)kKxhRHnbpA]Cr\>95p6 p p-˲,7?E0MM%]BV_!ﳽCA҉÷3k YMe=uV('u\I FXH,$W 2v#l":-[LV(=i+7Ǖ q%ہ/[ҡsq nr- ٤\^!1 "U2ph w*؊s}c[9qݼyv 3lxtrx&IvC> ;o1Pum8lczD@ BՈ܁YW @_JeH^<ލ{V!~涛ق %`TVv$6-Cf,HeuKef{~d;K^|GS}JG][*CxMdV)w~ ŚT볁!Rʜӊ+U]խiƶ2]vhY[a @}W M+3!QQܺbfen y&7{/Fd߮J*u;>'o!p{#=)Hd2rOQ?}U{BAӥ^yu&Bɐ|SkZo eCQ.YL2petNr,lӽoUP"FP`1hР`~x%K"&=#)yO?^{o?LPPѻ.B|R>*m[mcY /o\cCJY]>">a6%w4&ŷrҳ똱Yhfh "c!8{DjU ;yǞyuZo,TMoktcy\v'.e:IC2 p-\˲,˲[;s$'t A,fizAUCSQFd)̖+uaUO`* % G&bdid 0!'hHk}:@b%dzN (eXg=Xv˶CbvtL++GWu[WCLGu' t}y38}(Kg h/p,s"]_d'7oa#G`:8>o8/OXt#ff08pWkx瘓Cou lyN~I\lVld fBg?`|%DbPPϺwʷ )K)X($m7/<=gᓨItmqsOU[kXgv S/w{=~{{'G;xގ-X Y?*w¯i[}3v 6%yϾ%s%rO(:STO(z2.'+IuYlO~܇2u&?Vcr4xĖ/[lQ<4(zQGYdD_q߿ju/^|L&s_'~E=.(ZzPuԞu;qOk߾:p3v @;_5=_he ,*}|sa]62ɤelC2sʇ seQadyCno42,]2Oob|GdM'(ˉc XIPMg+ cʁsfZeYe"-tvd.፛drX h#n W2}Ȋ8 V2aԩS#  2ۘ. .kkO꼆{xG}IݬsnSO=E|ɦ%ɷb`.FD}T~N~<#K[dr{iaJ+#4yqK,|HtÊQ7ܯu9˻$"!;wp͆@Q@n]ec$7[v}""l#IXK-I+8ovmr*06g9$+On#LsWyekYeYpIJ Oe"YNtV52V: P2`ҡLǙ f&#,2۠SL'^, DG[J0I,N:)t5}2kd۬հP&2l}΄ R.1'^2S H#ߓm|FG .M-BX2%F /Fp:q/uku.u- ԇC2/Zl%{ z>8X>#Rԑ|>syeH`lsy羔`8o=N=cqNJJ<%/< ޯs?Ywc} \.eYeY%pMg}:ft,f%36dr.OdwtjMd KF(Hp5Pf) :[f-C]t<.S&e27";IP&9rLMyy:O-: ){AaYRّW9ss XS21LV3825u۩+7K=k u7YmzvQ`$ܯxG xWTcˮ>ٱ3fz{e{[oM`Y999޳ p+)vv>K\AɺmkYeYֲb.*0AYFy&#)jgQ04 ĺ&\\e: p-+XiH*m3< ps4DDvZfw\˲,25-Do&ekℼ V]Ic/a0,\I06'N'k(:ͩW^>ſݮeYek[ov'ԩh}pؕ1ak:m~-<;z'X'}ɋcY \˲,25-!1h xj<M4I--X+0 p\k觟~O@#4H}kXsMOvc>yakYeYVq%SNaf0gnf /No E: p-ak"&Bf"NdŇzh ;cDӦM|2pO.^8}R-˲,ײ p p\0>Z`A4cƌ`&OF=a ڲb%O?=:tL,X{ 6 pcK) 7djYeYe:akQ={ ޓa }饗.]5o[e I''/>m۶#S_~-Zy⯿:Gϝ;sAܐyK, ,˲,\25u8 p-\*E^QΝ)SDzm&ѬY뮻[2pP\_+vgݐ+pK?̘:uj&+C'ݲ,˲ p-0 p\(=zt4rȨo߾Q="e[7PcR'x`͟1JuKo9䐸qkvUϟ?pd >2{oeYeZak:VꫯF>hxꪫ"eٞݢE)͛7_'pBIȚ, ܋.qݱx?G2!N2pySn…̲,˲ p-k%R m,\^詧'zOӃV6WW^ܬYywer2nIfoC|w>ѯoy,˲,k)kUh 7LW&n <|xd²;,[[&,[nܨQEج`mnݺu Zpd*я?7dಬeYeY}CO!FRusΙpߜ8F|eȠ:+Nʙ9 ˙ñ Bk~R|xs/>F쬕+^|Gf2ŒK/4ӄfg̘Q/zhɒ% nO?MolmMdd},˲uXx+W.ULH1^=hX#U*Tp\+ӯ u Nux=Ǎ9-* Tu+W2ZxujyhLnݳ90={ƽ{sNݻGC qgc=PU ]vѼy5m/mذa׭XN ѴiB&~nIX nPgxߒ-YeYjժUX1l Q c5͊M:\9VA4Xqe8k֬Z#G @nرlƒ `СCܗm]`Ynɺ% ؞)lŋd[eYֺ+":S# wܡeYeYeC[4hР~„ a~,YFG}t/R ʢѣG+him n V3gδeYe:S#8":]v%*]O<48J4R`TXgY>ñcR+СCjj4yd_VФIEĉ?8d.Z[A`69Y];BV63kYeYzO>9:묳=#*[OeYUb2,k}ԩu-{'W~+V+唁`ml%ʶ}^w_|t-OeYeYeYeYeYVI? Џ)>[}_,? % lYeYeYeYeYeȢ.Y$c̙.Ж>[j˲,˲,˲,˲,˲$-0lZ^_$ϟ"XK=|xb@˲,˲,˲,˲,˲) n7@YZ #pkhkYeYeYeYeYMkYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeY֪VΝ#GF]vYN;ET'Ų,˲,˲,ZoZVSNW_};>)eYw}7СCK/E^ziTzu\˲,˲,˲,ko>/ѣGG'O,˲/u5jݺu,˲,˲,˲5A5jԈ>eY^sOGgLwO}\p8p8UVs#4hPµ,˲J_~e fVoFp8p8j2e7x \r G}#oYe_:ujxW^Ԯ]Fʕ{J>p8p8{nrŭׅ ⎼eY~7ވxhΜ9Qƍ+z3˲,˲,˲,k6l}+˿0'1,˲;M4)#GF{nۦRp8p8ժUKtMرcCFǏwG޲,Z4`讻5j(A3p-˲,˲,˲5B5jԈZno3fL4}t˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲5*1bDoDZhhΜ9G}-Z(5kOeYeYeYeYeY֪; >(^&M}t-˲,˲,˲,˲,˲{/z_=ݹs^xċ/]e?`m-x[zrǎ^?_,˲,˲,˲,˲,*iuu޷O??;,>#?< .޽{:DSL}ѐۭ[eYeYeYeYeYV g Wp{GC=4G7G  7K/4BkeߖzݻGӧO7'Բ,˲w1Df͚&laYeY*J$X_щ'"Q˖-]wu<'[lE8Ž/[&'JAn晧~z|I'G}t&x{GƇrH,XY#YK3/"iYe裏9sDcƌ믏h„ 7xc8N54'#ԩStGp^zin2ecYeYA_lvcOOٳgGmڴviTP!UVK I23g cǎj+7&3WKflx`$oM[˲,ZgnƍG 6 p( 4XO!N81ܬM4Ix=cg/),ñ h{G>`yEv}w@h=eGXQVñEǪ}ucMn :@H!dĈ=)>ի!+h}:sX;vl졇 }$+w…,X+6V_/ֶWٹod9?_~,˲ ap 7}>qE7˗db.ߠA0sOf ~2dȹ{GU `aWd˪ ᩵D{Wtyn6~=as=7le̞s9x/pԏ<ʾm[y袋9GժUvmG*TLs8Va?7} 6_V,*6o(8פcUׁnRu-6_XJ+Upn/}Wƚ#OLVi' (I"YDm49tҿ)+8ڊrn{ףÇh"x+K7ꫯ>5kV UM|ui_dO?>eYejݺudwL8 %P l0HCS"8KL#yLWF[a=^Ԯ];Wn@]`.g}o˾s{'o}.ΦlliӦXd"1+x/׼2ARǑ?T|pPOrujv{t?X%bfe׍ݡmT/קc @) \[QC~2U/kժ;x}:J@F+?!KPһwv!i%iӭ'=87zQ,:Z+~cї& Llƫ8,Y2B}ڐysٖ[eYknMըQlWx)>Y{gH>V'Fm2g1N>=`nLUl=pp+_@\tWF*UVbw-ONYm%@wd^qOա*WHC9Ν?#[ya=IOicUT]wCcCJfkUk\5nO-ګ[SOgs]ps^:{X}5MfYH>Q?jF2~BERxsTm /ogȊW-;*~ayRfn, }K.Ϗȧz*d!ٖ!eYV 5.R{'ٱ \y+dbڶmCv;`kHlU%ʘݏnP6d= ƺ˶`@$eкh*3bq<}&[A޴5f`O/vկ_}1ܨ eR{et1|'kʨ ^ve!kK62tw>*k"W?ZbWMvq8VY|5W|2p١ڈ7UЄ$URbfw>S'Έ cs}ux.$0 .+F2#4iJN:åшHQKmbUXg|vE./Q)=X)0IHYv˵"z8ܹs#KLlfYeYk j*X)X6sdnѣGV ]V0-\d&6 xڒ++TcZJv 3mt7|+ &,o q-˲V_~e4thoMdv`m&zݛի7S tH{Re?p&~ϞbIO, "!'7S2lO籾DmlXyeXƲD}!܅*Kʾ?;5>3-«^} p-\2u8 pի<"NNI%F#/$V`tvK]YoI̙#$Jyx9faÆła3e=0[eYZ'A^ }1㧞XU4L_VJ|IjllX5eg}?I@X1@mHp nZN&+W  †A>T[7X($~repZ<1OZY)zq#Fc{k#% úl]7 5-er~;M1{~#\};&SN  p-˲[s'G48K0&.g̘\_2dn:FV Ge], be~%%W (b-.Uv/W R,#/jX"(<~ѓգx5*pnpdհx/c򝺏F2hxn:4pzRL]hak:VzgDƍoAI?Dvn)pO('PY14/h+'Eb΋/U4pյJĎ,˲J_$@KCE7B2l@_QcU:Uǖ.]:Zj-2pr[ +{\.a9$p: hp#e6X(lJ9d~ن(Gj4d&p5\[5槗講xf3K'7uKXKDmFG}O p-\2u8 p矇~ s.A$aMJĔ0 w4k,_Js,pCE[ApeЙ И I,˲,kGHJz2<EL*իW|ù9E{Yg*%dj3@h,b )6,aD!jK櫀53խ[ , *\{} M}+ Utb:5I%d"=]~+1[ 8ÛX_Oq>kPaՠmL[ZepZ K6%DJ`@}42ISj䓰>gVR%{ώN=Ԩ_~a=cW|ֵ^]yL6^5Y8N02u8 p-\ZHМϣMVZApn~Z&շow%p|8ٷ}>Df@\^-˲,kZfN^tE3ό0GCX )i`IHx*+vY=bVNF.lBݻw3?e;2 ÍMf͚՘qaw=BK_~eH:}2i?6 `LYn!HX?>3 p\0+FɊ- HT #S>^f.LA;s pKO ڞFjײ,Z{8 AMpg%\m8 KC %:mڴ9Cx. & @JF/"m3kرc3, F.C :4=zt7@ @,Z>ǖ [n .d5a„;t1A/PmԩS)6eh 5|T1e6Z4ļ.Olcb=&2eJA!hy#>@ >^OIFY:(I88y(,c9IhѢ0s;ӗJR!xSdCڅ$cfFaO,BeYe7PPbS͔gV@nǎוHn4iȤQAC-VANJ1 ]l0h\[66Y?@8R^iWK工th~s֡U Z/ԤͱUE>y>)7bA甑Y综ǟ_P(ι7|7ۧSN tĹd;0a[?V:̯zo{:<posFmpM&TӲZ%&X}PA)uVz=#77x@ԝ<ʯN.9GCp :> AGcC|J_ K򊈎IK%]pހt941e` lAsO(c93¾/ BG%kdMS{u+0 [~xZZSg;+GA1edERz:yn2L)^.uW2@_ Cz0E=uԑd0zm]#>ۃ0::jpºԣٰ=L,o=:lua$ >(Yzԧr1p/y]s\`ey i-2X*) ^)88VHF@{.:k2FJی6 m2GFq ӖJdQ싶Lv3ITzclQ73墬m?IX&}Ǩ>Fj3$2dؼ1a(?5}'pB; gkkYeY+]4 PE'! 0XCXovA3;[bРALca:XN^sذa5npuoJ`S~0ґ)ʸ{:44ꁎ@W~a4XGɻF5*c{}W _:lmWcI{j0sv긟ѹj27$C9IA^'o7}1snt. l>1N E5{i!l p'at7ٷGU^ N c6w/sGT-|1[ƣ|P=-?EAg=՝+ ^?ѹ;vC=7=c <آ^z^ɾ>F7ĂI9$;_z}Gms0B^;G:Nbiչ,ZooLpv,֨ 6RǔNt7.up+@.ñUos5P]2Hո?0/ yij 2-/|ƺgϜqg7),A./Bb֟ݧņ.g܏/f_O|WR@c+XV&@^.'m?LG}:avU<_SHHU}kzJ[d]w dܤ]ԃu0TS|uw>t}M1}[=-GMIF 2k,2F"Y+ >LZeYv\@' & F 5ëb}BC:4i,إ Bj X.S&VXŚ.Vg"V,-?H˖V"=VdMX}>?ֲɜVߴ5`Ǜoi\|J[VsG?uKӰ|6`ٯG|';7Hm?r?>?C)--6hww%u˕-t lBwAPRl2qA$9v׋nqAzWx| % X-]w}R)}Hudup `t} JG=uL] TJT#Hͧ 3:AejwI,TG2eI |ɪ=a/ysk^k֥b9U:_eMk?{-L+>)hLP*KF'~yjKOm3~-~G|];A{hY>qm+rt.] SפO{;'˹Y >eZq<}Ze]h^S[#:BXudm=tl{ c \Su&) TG'|F9sCHUT-/Sr{H}qP~>,4>h8k;'s~I7D+e )sḵ~e /"d:犯펫WE 9j)k@2.(ނQk-Kw_ʑOw*9Koie&;x, VVڇ*vq/W',]k~uv׫:xdxȣhM0|.5״@F덡To;W:!;.'7Ip_Ձ<6<daByU d;VݱI2cE8u?9_>V^pwYٓ)΄W6!2صu6qnmǭ&@{G<ɇ/aڵ{kp}th~e'6(CpM(Rjo_~Nw}q̝hwN`=mo H.77XSk:H ]nIoCI:'i+qu00طpa ˇB4 h̀ Y "VlxJ6^sRn{ag2jy~܇RuƧEZ:gSzu]ޡѷ{<6w{OJɂzO!> zlNP[i3=;Q8w[Rݿ*snyǃ 0htȈ.iX˯>ߝ8&~!öud>nscg@׌.}6fO=q^B3(7'gla`O~;dxӿ!.;bи#O`c>ZpWb`hSPuf*-qہa.O5f p7gp-*Pڒ[|Hqe$CUݟq6/ڨn%]G6z~SkvvZJz:MQϩg[my%`ǖjM8fX) \e{ҝwޙmР']t>Mf5BZeY\jVc#dEА%F%'.Y ,_d Bi2udTi2$Y:x d,pI6 t:%zK-ft'{=\?Ahj0D#rHxs]vtcy0EqU%27H9YmY _':7(q'߫ߋug'qtP(Kr# *ʾL~W_l&v?-q\MK_w촴kn.}ofn1s-nolxR˞>2Cn>B8 2߬L2oޱZ-߀xK60 ^@ PCyl -kR6mڄa%pK|_SȑP=6y,GHdBKQM,IzX,P[N~9ud_{DMfs[ {Lw-u+Q7wB.mߡe@~hZ .p/g nIܹ?f~P,7?:=~AmZY6d/76m L(Mf^#IV_լY34P p>vhi@;@5bQen UaEۥ=alDnh,7i$ 1# F8p8J<|ph\s5) 3e<0;p (A'%_deůcҹhx=Ua*[}Ѐ3̘ 7D;&#Q^ZRtoK N0e$}@G7R}Y x;Eȶ/6R5dZzDŽ}67\=y.BXg9o@^]355:K rSKH]OIq rX $) jȜc^:dq[򠈠l<,-Jp@ʣLP[n%I` uVQϺo68_A.v: N:KǏ %p'엫m, ҰOY&ߥU˛,{reۚkZ|Y6lC;UmGN uI\2l<|nvS"(a p p-˲,Zl5fStܩvtS401aO`ȓ{4i lmP^ 9:uc~0Ѐu5S.ŴdFA4|&^g q &2Ch8CŒr2t Y@Qx9엎 I p@Jdch,H,9mc%žd{13E_~;@y2mwMyAK-sBFiIܼΖBK.Xjۍ<FS6Ο>ZpMjGZK9Ǽ|VٷF-76?x;u·a>~`)?@lf[sKvPRs.u<% pipS.̂._I}Y~ݎX#ƒ;scP]]c/=L[NLz?w@nԢEmĬ( ;cm65,˲,k\u|xSOVQj+5vj`Stg_pt].C:tt/0e̒. cmc4 iDJ^0&@{ow ClWd1OlAXH#_fn9#Uuub@yun1p(SPnBq2!چ` zE4Φ;H;J,%Y΅uL{e'_eqXfmSmY%2mɔ=t]*3>Og{t&Xc7#nz.ZOPO\_w ]uYQ:u:K,zBQAIF=z!Tpz!O0w1$v@'7xЭ:^qʶ%#+(6}}01ڒ# b`lQ&=.0V1w%2#83YqA~շ_($01%/ww+u2U\H,V\sIDȯ-&߮uA{Qc1,Jx4ODaÆyLnt~R+4\\˲,˲V"M&Qt:j|Agig4XTy:4ro`QpLj)-[v12 +zS]10߫gu2u@Ԉd :Mފ^I>pΤ^cUёM˰4d`rO/\1 pӐ&GŊ?PLc:oyaw&ZMoqɬns* *E*R !HeyPE#n^%7*I(МRѬ$]gqNC9=>kzzY<@HyQ~&();g̊?&%\OtwgťKmJrg]ڭQ$f7 zcفB9⧟Tdܴh"&r*e'5bH5.פ_UW]Jqۢ~ 4fV.U7(ůZ*h[19&M'm5r`rH~ e#ū {~@fâDzػPPƿ2=u C;$wp.R%.y겱 ㆄPnWM{QV WG?4Rvz61LrݥqR#'u<&4!~ұ\}4}+T.ko֧e2=#K ZheKjXcU[n/}lp .W} p`ñ j48#RL8@ ^ˠ55@.@Q0J+ _otϤ xR!sY8z8PKPʫw}!=Wz_Ň"dH\ @xCY)Wcc\Q|J DƗ["CUIMs{tl(%9Je9ҋN -k_rRS P3\`W+񺧞p|Y^Neo=ƹQ8? :(Qi];!{wsqL pvYI}ϿO6hj{j|GПy'JyRʮ`֭.zhu}={ \w<`O~IV|(7a<t?*pH{ܧu?|J/X6p)?ff~\q/wa} pmMp{=` LuSgګ}}pԘ~!{8\fTPNHH8}V…L/L苘Т/b  m۶u&'{ qʹ0Q"UnбcXʕ뿮2\3333332`w}~K H\e.@8Y.y2p {-h$-^+ psvI˃>Oa0j`)v;zᗫp>8J[]7p =;8!  A2*.mWrMVehNBA_RZar\@\OUW\=7onڹtPy>6̨Y/_e*H`P߰@܉׽ᆱRW8+I^jPp_p)ְ,xk7u!W>^yt<bYͅ},R\o]ԗ~N|%nD'c(ohFo [o:ϩon #$q˄=ۅݥE:[p79` wK`Ҁk'pWCvM= ذv81+hOA压ڥ}V0qkml;{1W*w?ਣ1٦ @Gp;(&臷onpvZ٥KzvD{ "и.n m|]ۆS:w^-׼04,jkJo p}Ѓ?.h9lk"ڨ)h,R3}3b1`s]*Ok2X i 5n23xMc:Yʒc`U;xSNqK@![l]~;v"8^V W_n@ u߀, ~4ɕ?wi=&@ҳ^aTfv Ddz.m>\(f3n'}#e־~>.g*ƃ[O@0vk(7pCͫ< ܀:3Pԃ..K.r^׻Anv*ti2_:>H]djL. >y{JՃ p^)Ck6O99\8iU G=poX4_Da?fi+&(p? p)C45k&nV?W!=Ϫ@GÆXڵ?]:*Է0qX8'Y4qVB}Uo嵳1S*J"};}(Ŏ;ƅN ;<}v/_qQYkjГ_|/X<;6pǓ8f?$yi\U69eN{$ZS쐶A7^߿Q?\VFQ{ҿz7QC?1 mLp UvErn곋ЗR3P@<ؽj^MbPnJ{wnIa$\{. 5333333{.G"AiYfV j5XzꫯNTP5o> jժ]]k˿_a2.*}ƺ 4:e!G`ѐayON:)ͮ"-wiJYt{tΜ^]SpaFi'Bw F~Z;@6x%.XK?s#>Z'wڹ`ٝYJ/)-zw4CLf9O-km@\iڠ|wۥG"mBʍ;WP_wq೽ݿuunS,.n PLA)x_;vn6;P:2*\0+ H='PH?iXL TɭӍCXhۻwv+ӈGЫ*cG6VC_2*>ӯ3>r}BLm ?ı7·jժ۴i XΜ9;D&$m&p (d@`Rʆ 8(8C"E3t^~B1CQ/PS5`P9/]" p fЕkY ԗ|O S -HwiÀT-8PV5(n ]~`b\[i}oR%}WY|wus'g=X>. ՟ӡP6yRM V.p3=}H؃pɸ8#n~ViE;S_ 8?rv+;?p'^'21ȦM ]Uu֥ O~TcRCϢ/0Os;@1i`1Wf\uO ƿsO8q(_v0s?7hcgNV5 8v ur i\fffffff @sMѢE4`x|%f7g`Z=XgP (.L VkWjslL 384o\A>fAgŃp]n/R +8sE*P\hgQcsIW wc=N(kD V9oPf.0tΒp6%ugO sxB8\p^8OO ߘZ|rP2S.^}9MG=: ̸6Hmq6h4=p τ=; ֵx@iY.%.~WIu55Mw&865p] PXVE JM Vl @S?9I3 kSzѧ2F\. ζ M-LI)WDc@y/)' F $>W/z?0n'AxCBY=!Vp HEP7c& M&=L}فʾ:iF+p=$^RNT-|.N u.*bTū~ZA%ڄ,,1O N$x h/qiho;u,ҖI%7@ScX{5YCoZ -#|jo(Ygd6+|;cM>׊|'(]wun"xqq]l/y˔)O}{O3k0 58EԠ($/)W&}~3'7K(%^`\+z±&^B"NR+iO)IVi ER-T0,_pS\NJ*V,V$gT';PHpS3`6x1jw*·Ymv1C,we4U3|з+J?08Kf\֤%{_A\~Ji٠rlhDG xy8#Uz:yτFKVspIo)^{{>.@tO_ßqF6R]LRwƧq*] N~%W 07S&lF2&;>|g?qҁnjUCLPD2[F?xL}bmg:\]owGi۟M'Cp{CG=QDN濅׌2 mݝ@CcF}q?  p22D=VcpkIa6|ATàkq98iU4ߦm y7¨.hWD <r| |ɓw'[)H;UnҴk4S# Xj0 / u3e!u wqMߠ2jXDQݓm}7.+xs^)߯VſU;s6T۷o_qB;]+{us@Zޟ.] 6)okL7-;[/(]Iݺnm*9ר7>Ç.Wƹx.Ti&Wg,zگ?0ݏ_[-Z.߿[Z.RNOY/m޵}7KQRN@U \_Yw]{mt$}j幞OQ2\$eʆf,ұ/ ?;7q:G=7>W}ߥЧ_m/p Czݔ=ۇ/4kOvy$;[g~SߍuD8H>E3[}{(pĹ: 6OI{R[>?>Rޣ~uOA\Ʀ Bٿ>R:L&mb*pp嗻5&cZ>ƯM.]VhIp ۬)ro.QD%Kp e2e` hU pn“nh`CUX=DepJ\ VWu% Qjks|| y@Uaq +RUYR ?81^@Ti(k0U^׀@ i; h]6poz X+%qz}[7l/ p7T~e 7tSWbG| ^m.!pChmJAR\ Pq,z{Ν'[ʖ;W 4ptCCܿMׇ%*I˺11=>}iKD~q΁C:]NT=dltրe_pY~J|]{ Hl >GK5ɯO6{v ŝ38%.-ӝÑx3\T~gnS3#p.;+'?De5ei.p0D1ZOUg=c 0f񛻲J}SqmOh~q%B~G1G~%.L1nJq ߑ;+觸jFk7n>c;\k5ZLq^z*gp e'#/Q" hAZE7"Բ>^~p裏hPltti(\p& < !/xk8}|Tc@ z8:\߹(9z(R'H§R[׬  ,'.8d^'h2E;Gy[prez;C"(>45 {TKkuɂ%$j6 }(`A^)?8}e?%KK ]|'oxD NͺPUH_Y|KgEa8\pӶ;ۙ۝A_JiDmSYs ?_8=f٧>sֹ^* ,_&ڬ2L{ 0SUL׭g6 H?t|҂?3dr&?tR&ZTG9^@ܕQx%9?1iL%{q\fi7JRY }OgnHM;:'[&ep,~'Bj'f!~|&nҏ|E97Wï7q?$-iOӒϬp=j5>>6B^-˒g(keK?8*elEg>іhÌgiA2fnRoV>I1Ʀ^AV2>K;%\1veu7g*=0M.\pVZڵTzu7fr0k,.Ơ^@7">0x02p+o x̠Zِ<+QWw~i-j\xCA<Lu93Ђyp'\@{<3Z@~wib`u940PgП96 @ؗeBp=@W` /K%= yFFٰ X6# >ѫӲ9 q/GJ@_5!?qG9{? OA2MK_#;*K\D˚~+-F;".N72dʃxX8^G_IqH?q#>DdfF|Ι3g|rK;wt%8ۭ[Dw p e20e<&W4xyxfEi`€w^^jKff^v)sTf^Eвy 29`c|H3'Ƶ5F3):^u \k~|4>)o" _cp =v<3حx̘1Wcy xw`ׂfp-X0kv Y}Mt'q7-JX͚5ǎ+ q\fffffffp .y0wdL+Z ~7e|s\  `١6n:hժUp5ժU=|vO?tPXX)r{KuvF?rp 7Alhe ůnv4 5kY365jT+nrʅ7մC[Fm?S65k\  `Y۷~WCǎ+dݜ py晁Gիsz 53333333kfׂfp-X0k*ٳg'~nٲk^~,vذaQbp֬Yg9 W_5kfffffff p Z`,#m)S]%ԨQc7't\333333 jVZՂ *T‰p Zaa.{^J1k2#r-1-ACpW$u֕:uejmBJ[o%KuVpvZpWoذ H3kffffffvLsU%/_ނ Ե%JPV(^́/w,dFX />x6-dZ8묳;Q%y'n  ™sV(_dM,Xj(W\ AvT^:s*Q K{@ݳk׮NyXb7o^ƭ53333334˝;wpꩧNNX>#O<ʔ)xR:ZYtҏwڐ<vwC^-XȄBφUKx)vi!3CѢEըQsP6 -ݯ s-;Ը00v6-/qmgoҤIQ5kSNCMи.Oߖ]dɎxkV٩Gs%\33#aѢEȑ#Ǐw/Lbdfv0NIN:U]צMZi:a?6lduƍ-Xp]&'.-dv׭ZrcÆV7B 254jxuƍ.XBo-[\,m۶ ZhAoD=}﷾}^֦M@w},n_}Ufm~7?}633Kjݻww pZj \z91~i}A,]mժ{`g;9ʌg9s7o^ߺ :3fpϚܫW;oه,2J0f̘`ܸq!CA;wvBMJXc73sn}y_쾛+435IȮY` ,X`BZ_̚5=>_t1ҥ aI5U8G9xiӦ9l[xqЧO@8 ^up}ࢋ.r= eiO>=;wn55j|ɠ_~(?|kOٳ| cFDfff4V\_>/?cBlo{p(+ :hPABZ{T(,QZRoP/c?*RۃUVgp?S}YR\!?Pp3o& 4pʵr =0vb'YV56)e'xMPy?裏Gy)OQFy晿xx1DŽS)+Ə3;6ey5СCVi=k׮_j!o`†g婏d>㎗zϝ>>LKҥK`ڼy,7 +VlV@?ஙYV5&U&Lԭ[)qq  sLye|jmԯ_ ـW?9p[`-K.$ܱcGQ^{nYp;M4qi?I-tIpljW%۶m˴4P <X{.m@\rѣG m \,m\pAp\ss{2roU7ѣ=/E/]oe8hb^}'z333333333333333laY }jlSO=P)~#M7ͮ "{uPWu_Q=ƍÆ sn_:t %ho|utW#Fp.\8(YZkeUΒ:AlƲq6dBYfg)#=(ߢAM| %1ԩSR{.PYzK~Q?&rl&W\qEpIYg~_hQlٲ`^ u…{yy䃴2xN~^& S߾?t~*x衇T⦬) ܔpQRXy7+e/"Ppu`/3Qʄ%J@= -\9!y&O7_2 rWCG޼y] ʦ^lu'Mg=#MT>W^xU,w.e.]q ?ԬY3h߾/yq.WĩG6lp!rֹUTkǪ4[~PG*h{'ju\o5tQ|cʚioΡސիW|҆hSQ)j6AoN,-\vRegGE+(Hp mٲ%b~?B:|q޽{;tTթSA6ˑ#i@ 0 Pu{m۶uji׮]% JZ^&tL2;`*kϞ=7 IzYҥan` u0eEL)}͛7~w$ ҦRsz_)p}YbhF|$B|؋?'.xKuVZkb@z=(;` HHweƊ)}AfGZ"P@{ꄠ?GMD}?QWZl/R_08 ZŸ/gʂϵH'cuu X ܌O=Iԗ8m,sBbR:[%#7@ImK-$㚊/']S 8ӞQf6nsZj.ie" >'΄ LY[vdO?t1`b &Փ8FU>1pP@Hl&hJN8Fv}θboX"ǣj^h#@j跣yrFq,}P{ؗܤLv pKsN~ڒ7A=Ҙkq< `J<4'@68iΜ9@PRhcۨO1kT|bfAƕ<ǹ[=b=w\yFQ(:@a%!O>9`3= 5^!) '*/W Tv53o&_ոt%3#sM(icxˍ |R~N\r2`BbŊ£=c(cK>sn+5k:ב &AҘ6 $ +/s\> &jM&j([333333333333333 pQ P "t#K\0H+emF xn$ҕ'`J\6$Qey [ڵkoQ6Iey?.Nո6pYjƁˡ>P;u)&Tݧ:,v9KſYK7(]S(&+~C)p ( rƽ _K-J6n:۷Pt̝k m?6s9>K~ $]`tňJPq@w pVr SZH 9H\\+0BP V_ m#y۵u.GQh 2ڠYiOoXe,L S)7KeYYxJŋ?)C6Pعs6J^DFJo Hib!HW?v^M,].PB{=Aa SQXVK4].nS9,C-\A.D#w`Roz)XUytq $7e)8K{ rFƔj2>iX7Q7Ӳqx<pBzx+ޫu<))0,%K twJxpU"@]\b@\)U VpGI8%삈kފhaW߬*KuFir Oy ċOV77yBy@+|n+ /ڱcG.\5 tDeWƇ.I  D~~܌k\ir;ʁe*_z0M܀>=%-Sw[QF#UҪ߫>T|K]YupV5CեK]JnQpqK:ҕWM^/i_tYT Xu+o@W>*?ݻw3gL̽凒X;]4hui(ATSO­u{ǪN2 .H}@c\n: 2>z2ܲ+ "@㗫[#ePZR>U`--W@ѣG;'EDW=xVC1(|-y6[vyH Eu)Ec{269~X$@ĉ_&!p4)ur%$ *^(@_)頨9M9wߐ&| (z׹Pw E<}(6uB`MjK\rjQy9ղT1Z61C KƨK( X}y8CԻսoX˦={B-P h.+ M|ڵm(pU^Խpk|:mfffffffffffffff]@.6)OQ&6( >Qu񰛽> ԋ۴QW1(ISr\Dq<bS0)/ O>k甥w ˗y}Yj]( qc5z2%]B:8xN1*@s.\Ū#nɼ~pJTN%Kс~RLsJfԚ~/M䇸?T"9_2`W:F|ײ ^ÆsYX0(mT˕u {%ǀT(D2_N%,0<?(~UIz"n!P&^{-y:vi_@ހӤA؃H7 ͞yX:ujk$۸f{9&ݳ*WO*3P'w m [Lic0A=U]'(p(-{H3@觔W&z1](kU1 飜]ǭ`qy|+pQl6M,-\8z81w9>; BwٽA! r;s&AKsJ%@j=6r8G=%!-W@ wUS/\E*16 e.&6@c-A?*/A`m 앋 rP[#*e 哶/K1-$ؽZ%I`W_}/"{.&97bF,ǟ/I| r!%pJy۫[y g@NCX_)sq}U`Dөgݣ}"$ec5ȺW~3Wp?cn9?jSՍD;)[._lO<9m &(.s ܌Ajq*?TvWW'٨lyW1&KL*VOǸw1 U] Se7 %=ڰ/F=2n}0333333333333333ˮW0v_,Q1ô}lmrF'`tYZ@͉PAȖ(/q*S7 9L=+8y_( q }eR|pW\逖Y"'uh9w?&blm┪SK]+ Ȧk~mqA37QCŠ'iQ>u~I|Թk@7#u3~.I{ʻBj=?r= 0 Wݓ:UzfE(.Rrզ{ S[gR˓:q `;.-% >ǵS~ \(|Y`!p<$(Z/9m&fCnwzA};w,r|kuXm!( dWUR^RVZu6qFﮩ2u>p>] VVlF5w܀e,wgCٳgDK܌Lu_=T*Ba;>tZVP jVmq.!qYZslS6~aT.2_lͺbl& %do?{+/XCխ= 66[g$}0\tBAe s60K-Oc2 Fq e@{\Ң43gl7N=׽NؕOT+3\33333333333333Ʋ3H2& Pj-CqZ>= HFKn)T|vYu]YqTlZ1 AC/ʿxfn#)6Q" (F޲.MLTc2*էsXyLC)j`x$/UoΏRV@xisD |P,,J_- \׀A6b26>ű,7\zpW1 omF\ͯ4MTws9s88>qC *# p}6\RѮbR;^oiȍ)j﮽~Xy^om>vuj[R^렾X.Wz )~|CY8ࣃR҆_mT}.t˂qQcA[ ,!_H\u/hF5C1s`ffffffffffffffvXZV'RLAkdA))UyS+63s= z%AulUx~gs+,ZP$.WԜ,ז3BZ pN\sʔ)Cv6ȿc}e;vHǽkBU}[_f%h @k?F ZH]"5… OTOLg`އR6U@ً~571ٻwG dtUګ WmHA\)S1> u lFK[*T71;W.`裏 țrPݮ{&>jnx+ke\r@قOuʯ.ᣘx;%.Aе~DZg)k}:WFac{/P~ .*>^AD̿*SzK͏̛!?'"e)y*K=^jM0 ݮt*h4QzSpyh=f֬Y.˃p,fX$ӝdeWZ`\w^uW+qW4QM$WQr}oRn g.T  )7@E+0AKj_kO*>w (h )_FyNrIW9hD~oL"KAT~2tǫ~, |~;6*^SJ}]T}AGM$ܥr۬|G \{ OqTW*MH7|s~Vv~/"6Tqr{E,{`;B:]w:4YX]t#R61u:8|Cq$쭶@y\{VY,&I670&|ʗrSȀ(ǥw>[qS~6躻ɣn{Frac>T̷Qt*+PڟEv4[lq GGgHwy૯r`/KX g&́h^ /v:'I$7iՠb-XJ2Ud  ]8J*,X UFL1)`၍k`Jz΂L szȌiɧ{ERb3 p1t@"Q}w,sg&E\Q% hW- D>I@(whns,yQ ]Ey((ۍYLq;#lP:vF  DBEe o'JD` yVWO[߾}Ԧd@YʋkD;r7J6ˀ29 BYo;+ O V$V<U4*RԒcp) [j6tPw- $QOUq|[ x M\;y=Po5r)>@̃zC1 $1('EF}d-TIvup%|dz2b8 U9;ɛċdA :5yl_}Ba&qc<0Kr8`wRfƀ(86U@L*I $.K(W҆S.;8Q>%NG8\/ΥF ,#RU^߇PN *ϟVn䞓 \{"n_\ +[ʒ2$,f)<3y"oR3c=ڔ)'u~ MYSIu 7x93/sOM8t\[b-Te+Rp:=}cƆJà o:7ƫ΍Xd stTR]/F;c7UΗ9tTL*,H)p{j/ͧӦ!WWڶ\!U#gsIp5+E_F6K n:7ՑL`!B^޼Φ̓;ttz~=ee˄5j_ud8bp  L(iw2(#nsv5k>ύ`!B ί{Jifv܌0@ 30Ƴ9@)RC}AQ@WJ@^(|&XvkFxO<` 3%H1<#s-Hy8~\S +sO>&u#{@Ǜ51Pƹ& dM|21#wwX)YZiCS n|iUP3TOjv8UƚNCJg~ @JU7`'0|Tv?kDBG,q9̘^X:3!DZ(nNisD?B'FE\]>_ [b s (YсVK>qdE-\z/\l C͂ W]s*j|[Yg!# kծ[nWoO{?[ Aum¥}W֬^MB]dff7sV߲gfW_{a`NdSnv NR>ON\,`fY`(VLYՃ?&=)uor]Ucnl|X۟K33lQ*2lR WxgQI#0Kfo pmo mm#:R UOC!6#K32M03T!@( Lvcjaxe̔?Kjܮ5cvuJK~`@0w#P2ZN]A v<#|G#A260cwQ_| ),dx2X,GY?70\Bmv1hd|s_nBv WwWz53ن _=Ar99;rIu+twݒxQ̳ Hkp끖x( o.A./M, 9g(a!\'(l.Yk)eCIyS9VM'ԻAT],%`*߭402ŵ^"P˹P P!ocvJ ϊtw~%)E $KI:u砬 8 9Z+qhGEiTr-XȨ@'W?p/8k@Bf[of+v`8'3hk =.ehVq!@.ܪ Vb59 j{ MOPB$+|=`Y?+q;X, 0ˠ@*׫@(/w`ݺuanj @Wci,Tq(7nL,v+w c/}^4/-hʒX  Z9%)į&~gme md'.+I>c)3Jei9SpIp Zu:33;ؖ#GN =qڵsQQ)UT J*TXm*[oӦMRyTfpS1rĈA-Ugyw}4QfffnAj^1UMQjmu\lURs͝;72W|͞ q~{qU`;Wa x\2:/=PuQ?y֭^/f9t?wB,#. Vs+u/؏gfp Z` p fyc&6.̋.((S~;e&XYk} f7>%\ 6z|233L6(p\.0W~onUG9L>R(JYG}C4)eQ>; ,Mp"FA٧<?L"kӫk]WwM(kήYk]ׄ 9DPP HPr" iz0@=o{NN2ˡb&(A9lٲ륹\{l[H]o5\vfndk[vsBb!bn!B!lG] ߳dڟRr% _l? -akK|((`/ r@Y{ZZ~9L 7 딦>ֱp]a +G*E`KXsI].{W% (MiU=X &up+M9T;m!Jwnbn!B!L#2\F0rހ X.$9HǮaB!l` m-9^Psa[#[CnEhΑw]Ywh9s朩Q9G>v=v<v!D #QģBߜԌ: 7 !C 7pCqCV-ZGvH{65o @: .p0 !@ 35BJ5u!<}w'V4ӦMJMN7V(䥎J>:-N)kҥV#ש/~Zr#aܲe !pC 1 1pC(W9p 7mX8 \36olJ sJqKyB!Tr`gB ]O0p7ׯ_o  &bd;_p Pۊ2[n;K,/ԅōlB62|XZQJ ׭[gȟ<58%86;L+LF@aV  7 !C 78DCիc xn r#iQgOpѻCNbES FjmJ yIqO晴:8ɦKB!l]r \Xb XP/vrfdٶ2+7J{ L8$Kz"oғ?e {`,V zj h@V;@珕-`0$eQLY&M1А:a}6b p5)1a>=[+;[P?e!x~m֚5k,nw}g*e5~ 7C 7pC 76=խ[7_/2 . pC73b1܁w*Ue,;Tk7Jm': _u饗f5jԨ͹~ߋל8O[$4o,ڮ駞z*\!Pfݒ%KE-pH b#Rm3M{g`2Ph?` tg YA$/|y _&>NZD9|s[SgƽX:ɃkXWSOJ P:@lzcM?SkؗG'o'Y:o+ϓ 3aQ:p 1-p?1έP RE^aG::.7|E [q75RgΘKSk\wQuE})vSƥӖCn'x-<N" ]tQtwOј{b!@ ܰaCixv Cڵc^Pi=t9];3]ϛ7vyg /`j|ARa=ܓuW0 9EA{7d!PP KK`E a׎pJߪ@'TrKb/鹏G0Pr?;<#|PX42pɔK(+x(k׮5<?oM2ϩd yCdD@&|(ȗ~) we"r*M?гIh}@n;Nz 54>GP) z?QQ!ԅf}Qkpaa_\G_K]qtF>~gV֭M {4~;,R).{챬/뮻ʺ+Y?CUh^YK!PLJʏoG⟻m40 @0EMVZ eK2$Lr+ doUX|,ixN(((Pxg :|J;QRn;-N:څ@tKmڳ) ԋ hbg}ej$>Ţgֽ(\oӦ絓,Vyzc;ky-G [75.j咶޼+5.hE>(qz)|\kG˿keJlt?ļ)z^m)Mc6])#ewQlƴLrO#`wՔ ?%Dc11ucl<&E6{|܌0#/)y(`裏Zur1D+{`>=BcA3}/&kfYge:zp|'rCuJ?@_<`it5j$p Sz<ɇsv`Y8Vn$Ь[nŶгuJh='kRyu+[Ձ1/q] X6mj}!7Y=gXpN  9 0̺馛N"9tAos?xWCKJ>n E=)(Wݍ|w%ў_|1Kp{Ouꫭ~]v7}{K%M_%V.ʔ)SP[J>K(Yoíi:n.]❠gB!VAP xJ|5~#!7W hw8J(פI(5)gHU`]"A\0Ih}r" 3"DpN [$̿SVIJ MTvoQmL0E2)=DmlW˨ 0uZ.Ze$}N;CaxKbݔ&#k HBg9F;-ߦ۟W虭{ ?D<ԧ-U*wQ˕kH ܊wvFo,XvNhSl"د~5ɜco\i\{UTg+zMO|Y{ 8BO#(SJpʿȪKo5.T_{PINڑG@ ;%#wXM녁}M:Zu}/3sYv'=}l.j>q8=t‚iwV1%v)ۃ l6nE0`M[óqS-Eeyuz ޘ<󉫮ʢqTVd.0~H&8㌄kBx3Y1~|-8xB3TKۙbo@tB߭}Bh+!juT=W蘆pb;qqe|ȗz)b\'>]Lwy u =<; RwB!lq"^4EQqv]~$/4J~ld%!BPbKK^tL =$ a2jW&=v?^ k!;=S&@LS忻&-t$d$O 6GR#4:I@nafJA?BU?Z(P,+|VK\I~^zaG\[3Z9' ;Q [F"/vӢXta]v>VcY\.nQ5j5+D^V|ŗ*^Ϛu*E=z~5 bṳ_z(51+]nA4IPk/fGzԨUQyW奔Uֽu uҩխ=,oy^K}:EKV|aZ?ҫ6jWjH{=>[8pYlD?Aޒ-SrCYWAAQ}؊|;jժ6QO!w)* v-_ HMΏbl&KeEuGƒ(I:ɋ='ֱ&g*TO4,X"%$ Kiuг@I<.Q~w P2/Z?z jE/UTn~^Ժ[hU9 "֫ފ^ZOT;**TkhߝzzҾrk[ qf,*j'9FmzXڴz?lJ^P{,/Kգ5G>:}@auW ZԻJ0,…V|j\t\ɥHж4q,mb EN;4\sJd-SwӘ-nY4K^@p]C%i@,b rr-)jշYzjv ({wD e3pGcYh_2>h xeaݥ~^gV¼ [ݻޝ#8 O`` 5 &OvZ:xgsggs1 e-;hڥOx,RO,{Ko`Nȗ@ac7i.ɼͨ? =iw{Ƃv}iF_v;HZ K{ O# 0zc.B!U. ,J/Vb8NR b-©|!xHcdM[K8 |5Am}٭R`ŒAԨnt?$ iCZ 1i5RD~V<`ZTYzJvP =/nB{nX.}BVW"}_F_Qۢ5/KE\@iiGP0}Y߶8]E_~3+ 6c }%znhҧ#UU>?XbF4blh~Q~]-_{XZ}'?Q,o~55j-+[;~7Vt@m?q֧BJrq [ >Ed!AX^%D`5 *{P :ɀ} gu#+J4X~L2,V @51y)@!=ߥPR]Ie@FmCX:TON.XUK -+ RC&,~e8.Vl{(O=%G@-*%]Uv '&\XjbW+r,VBFRO(`jw- i9R!s <#4ؙt;ᴰC\Gf<5}s7^}QJd~q- p:Ȩ(s?`.<ܫKoHyH$}QG]iַ}V%`uH l=@kYDEѸCTn%猊~Oi߶[ҕ|ARֹr'].NlV+5RGye\VpqXmӧ\_OQh4]?gc)Eq((egcĺHO*6^P{ΰ RwtP;u]*k|4wظ%J?T)D'S?)=f=2_d!x\bwkZ[)XYkkXv=Ԡ,HK;0rИ,?53'|푴b}gsE`S~Ѹ_G ZitI1} &@Rͦ'ӾŁ%W7" 3\#΀g\;z OQmԷZlP+/۬sҍg !g/ppCFc@0J_) + n\E} x+!XY"Ha["Q%`Uz}l4?ǺQrifM*R-|f2hhe[{],*Ӏ?bp.tʣ.a~0u(["ڜ{QP59_@X|Jmp8V0'息] wyꤶ* y- 0i v"43@ 0 .L ?0 pꆒꂰ$@\3;+9t \]k5E*e[D ]B},?;kl#argY4N8-4}0' ]w'}wL+.KZf7ɷםWf{jP1K;l>De_}i|wlWV}@'ݓP1% _3fv `3\' W= Vb~n~7sAaLJ,;%EV _WBkn,«Im{Ho4FA|׺\`2%J{`,:S67+Qz,V)u:pp;LXayY7N\`R&Kh>i+llK{3Y>٨Y/Wl. lcc,g!vP-9]\ںo!Z2Yqv7}s/< a[> 9, ;:iPqqeFb?Ӝp fSYv(^I~t-Q"Yή{]W(]sYG~*|s[3Bw+Uv팛QU,rfe]]1}&Vv{ J,IcTf3Bu)P1zYDUP(e R6\v]TN)z_W%E1lsҽyQ``|3շ-TNW,qAsAf'=L}4y75oqcT5,7vR;_@pqv{cieuLթg?*HFgȜNLPf?|;*KW,8(9XEQtS~dϞ^ƭ !B_ zʮb5৔[48ӭo5AZiDY7 .RHv([9ٲs 8J I R3\P&1[b~ Do  -'eO5K(O  vKb0eQ?;>P-2\&z6gN@ҷl}phRt'Msp"LH\ ַZ ȥ]ֱUTs7 qujLΰ2 {k2j+}9z TVqgE;44PۦCVm4]t@ek\/ok݂ƂB_/]&>Z={C p4'|{]J%XAh<p!@q3?˸B'7,( r3/g~YK E.h~GX6AC $3?ٰumw.1 )TTq9u:p1܊5.wT؛ >|˅ZM: p\4iv1+ V"pCئc+o1O#m@\>9]e-])  Xb;9p GJ_,Y֩K]O$objLRnuG٤ʁ` pϱ`k"kf!U'OvX vKZ~íneɧP  .eZ~T ?e~[knpz4*㞴+z nG t,ct@E},{S$n(A+-H-ֳ;^V"htXYSF o_inF z򾐎z򘟪pg'VjS|s!lv{B08Ů+^#Dv\f&ʊl"|]XWbkj-,_֪UV/ْ/e4G:Iw1@fN Bw!B(WV > 0W I yd0ZoF.`(꣟aV݃5R^hf.@YRJ/p!U7)Wѻ+GU`@ XEYj"TA`["4p,<<Z (~U|/ 8IaHo;?/~?=?OX ME%%tS3_gipv^FѼEn c9y juxZhx՚xGmpm&Ews\F$ʻJ& fs Yk.x|S;[Q bkQCq'K ܊![,fW_AeB2F-^#NV9.ꃷH 8L<x twXYsbɾ{e(p@V,.Y|˿8&W ,KJq͠m ƺ퇀nhHp.+ޖ/\~@pKM5Gc 1E6ʽX{{ z{58W>]~'=9۝E}3 :0TwfA. :<6ﯳ-xWqxiCR ^5 : vL; ޫP`y{* V(6دts] )[CA0  -$6o;"4:,ܬ[;[O>-Pz !>tk4lkwv8WF4r?y,o!.5ARsi~XIT?5Ca2$de~d1va +ݛpZ ?Mu!fBy$`֢6 ra  C3jЕHSܗ)?qQysX/3.jW>ЭXKB<+}q!|ހd\( D300n 'Hpatv a%&iQ%s߾ Lw.Cdwzf7,_a=1KpuB!PE O"PrAdcO|Lj -?=PY]dWT_O $ThWIoI$jR[Z" Q~ H=j+l "{SwvXvabn鑾0- pu2M :Χ .l lMm|44nn8S2LU=mW^4+[W+F7{bOspP|+f ?[4>;jV:tm~%Q} Nc;aBB3s#qZ~.W~]j~>)ZfΎ pWhUJq?RWV  *eXM*o2)9~T)u1XZZuZPcA?+ wl6jV>pW7%pGދbvx'ruNL^UoG,>]`{>L=ԶpCAjXponcxyqmj__]?t br`@WVWY>B'V h0ֆ pW*.׽P~ЉTrNTL\)-&]}LD9WWda&8H .W݆iRXqVO>.lFJVuu_5Mvm.~xa T+[OYs{jpA':#&aiQ\ڊO۳>2/%x4鏗{9|ܳ"Su*Ld;Vu,byjo9lkɦBSQb=5hRnl5pq7'5~,)zODٽٞ.eQqRg,|zEq q_K|pc+\(6 &-p@Ljf6R{p[a.ֶz-Zwm, ,V.Zn!|b?Fz3yߦ64&i'`<*oY~ܭ{9 kܢEAsW2D1g_ܷ9K YXĢ b)c\6_ P% Wp񁪶OX o(ep xT vwVpey_YTLA0c2 f ]hsPkY,+ңq+I~2I 9Lb[1o) <',= p7]#:dn d\#?}^{\)w\E2b/;_˃wEDu|M.:Õ9#,~1V!(oW!`=%D_t$CdK"cfHpjU Lh$|ntZZlP?B=4(媻l`;e^ut ܅&% jy,w"MN)])/nz+|nJ@Gq P>_~[iڀ SS.m`EhF$.W~B_6?Qgy]bk\&>obV>2?A(cN\zm|.¶+vh\$'/ҭ7(2WYfD@'ڠI}|mˆ1c =(]I4k}n~4`;;}Ψ@# M- FδQƇɯmw5hl; `V>\+w` D.4 ,RQ4|bbi\fljVB~ Cَ~o׿"Zkl4/j#:g\Nأ= /e_K6d'[%/q \A7-+(@"'+W6@ˡQ1KO ?%n0f-Ќ-H>b}n + #pĥ|@qOEj4W2iڲz2aUK\ p"9|iW<7`8(g@Ųn317nGcDgUܣ6ЮLbւW1AqA;N!zY~,[f鈖F}p^jop3\[7\wޑ,y]({?\|fwB!B~%@a2+`[P|\_ P7{p [m(6 RhR8<0dKQuRV^JV!xU2K9T6&VEpQNSPx;L PքşIj)0ߝBV M?t}Nqlj縿|2>àS-jKr7ߐs͟JDZ[R/qg4.G[+[:lq4AE>%Pɲ#/ plC&noi_a1͋ψf_-u/Θ7&xB[>=jҲr b=\Z5d&o-@@qc\eJ;ngQ&ۦ`(jL6Xڣᆇ߶1(-y/r?jc 9Ci/P/?o(.V&1p 7/VCH7bbAFG$p4VIGs`W]z 1غͻ$!\;ky!vt%+ߚQνċ !299%}o=6+ȵTeq11w'g5oܦFq=t pPT7=9`>%K9W@VARHIX^WԾI@;\2KH^Ke:ঝ-p1 )QD$`z1:$#Ohw;<샫vR dẘ}jnp p該4c=/UuNcؠ)@?r- QW ) j>JMiOBvPg=42u~ Y(>J:$h zrxsE>gṷ́r ;Xy'^ִ ]0rݥ\=k{bI+ `\LJ*{:э z U3D:%?L5B! p}duy܇B)+H kc*"P؁?PgAdwGFW8`K&=p Bzba̡ 4 ` M|lAQ]* \RWߙO)}ALFX7'apj j7\Cg @H?#Ô4VKѯ.?2~*.!A Z`x*l Yfםup-J;'=ԡǛ;=bU?,IW0.^v~!aX]p~v%oi.[ʅ`R%/ZEz[ .5t I meXcdSg PFJ@mw#K5N$vڸJ`LK#ոَ1]#vK\BF>HJ|lɖ`P]]M)K+|;: _*Iw B7~:- 6UXi:9I u bq;r:lXF)]K~}bRv@'ӿ0[k 7r>X3#+=QX"4ַi!=U@N!YJTˬx Kںn֨?0,xM1`WҬAc_n%)hSgmߟ=XJV?m).thY%yAP}&q@o | ⏕ET,EX _W2llL9sxW7vCeQˤ͇`KP'tLs+Oo"pjyx; @r_-7Ў+wO.y\\ Ї̝awGXb zI W;U{We1)w p.&ֻڟR8O3WVpNmw0yEglV 7]s I:y!VLZ`LdaLQ\w-ji3pUC@D + P 8[^seI(@)a]Ԗҫ̗(@B˳Wƹy) olOqevJ_/aWjW}3G{E{)p!f+-(x5ـẂC3=ǻWU9D!.n u\06_ o(:5nMt_cᛖ6(%Kַؚ~ &X4081#hʸ Nŏ7c3yjlobj pFuu`WpO<_kb5-bUu<$~oQNMB.~"hK=X >;S6 A%vYݴ,Wfn!d t#vi--+SC$Wj cÔe|2>xt@i(U`X/Oqϒ!]$,=X.LpĪ` c\?+t }N)[ 6J=pL>i7ԨQ!u˭dw&-φ>ru0ۓ^we\YTʳ)VYi' omg봸\J3 ~zg &p ЖYVz_ΗeTɟݰKrȊ@c/yNb 5+C3Ahhߓ1^݇`e}P_бؗTjNu`ǖ>C b$+[y]0FۇhWB\Vzoadu'hySXh~/,.p]}rɩ:)C*]c7owbL\HB!lXlG9-`9팅oݑ`Z RK"a{@m@Gj2.(3@Fx 'P8J1]rz29,ll{n\!Gz<9,(둢m !G ? /U왤ͅC)GĭŞv%Pe?/-n=y[{?:D_uhew+JUߣIkLΉVmz<;7=wF+|SB98px٫rG7qVQ=o('~!6Y뚋L9r/Peq(ZtgPThmZѬ^to?J9K&81? ӆ2ὶe @]l)D9ˢ2 Orb㟮cl_cA4Y;]$&(C$:t$ ,oPǓ ddJْ /IJWdwyox 8,|uk5{ `X٬pO:C3lߴ? =>0h 2nV=mv'aZms2A[m;c>賜~E.@i; ˒6CQYX> <;sKl+n:PuVl+]+z57L9 FSuO/Im^ ҽ@t{СY I0J}NzM>qHagWzq,zoHEPr۰#yjG< YscC䢡ok`"B!-pQfbrDCuLet J5p\Nu`)MNd ɠ.>j;7 #OVh*6Q* .5)%*J7uv&@cD}qb:1S6r?J1ʺJ]Q)3{5kVĺ'tKe8Č<# i&^ !IIWDh&I˷hx"} pl؃/1{RC59Lw yCW^벋sD 1vj:~i{kJoT+~rtI'$⋲C}|JOR(9lVӜ;]Ҷvp/D ;w(䉻P&x[W. >[8>O [Tajn rxZNV n~5UV= /7 yWOAyrRݡ>EϑyiCoar7rY$ B!__rF5% Q>(WGg"qr KK?D@Z 5 4rPQ%p f+V;>u?Lt-qȤ^l۟:sƏ5~|{y< W6@aa[bQzomX~/w>2mD߱a"LRSӏlۈ 6'Vu6 Db7-Kh2x6E pB}̕KXp?Py‹|Ui=w5ܠp7nϢ6M/7 BKK+.My_]iKvq ov&= ApKU^pAw=?=Wh9~,G[vvMANXL#3Y !fn\e>c2A X/W"[[sK> Zi A(i9> L{+j`yEd`a|jy$ax'xUAܢk}X8a2L.H-?}=ABnP2P)O?,_YsõJ\MiP;impOlЗ$N+ck܅_M3߰_-'ͬQ |ϕC5f"V?mڪN4z ~ҽ F dZ&9뇒aJ)` ((-ܾK/p}pŽ@ԌS\\ Trz [}z#nɥmPC~UC:dMpxڢ Dϼ\-ΠP LOFݬcOkh~sи~Q9X%ྥ0 pC(΁X)cf}\ٜn8^ Xu|}vXW }nv`b@JCv[%.DFUN`$C?ZCj-%=dW_}~]*.6|os❙J'0@8,fHd5S_#@w41@Vv/@]2z3%`(VjAJRWsڄ#:kyn%5)?c~I_.W[YLxO[p1]m`;S ~PbBЭm\o4,pz2~3We)7 f{=_M9`7xYD'!B2ccPV(7ߪG))L$w{ 4Pq9AJVRڶ{|ժ8 5^ +TKf $4!͖$Ul*!~R[)`n złiMh(2kH>e5e! zA#7LBm{[sNW_H7q~CbiB4I}&Q7S\?L'BZяM*߽U^#@yR(J @ZgE/t}2>-7F,eE4@ ˨+gcSJw$'ԭr-siȑN־ތin,pѧMe}%VK7\}r?C>y/^ \?iEKV}19w{F/P4Ӿً%,ww_~3\+bqX넭poYb>[h!z|M l@ EYԸ{2^Ji+{cYJce )44v9UǸ*Vdb]I⁋-r1}S[JN"m|i~{Foo׭N0÷Ѐ,:aȸ.]'dJUqo T֊|N~8Ud \&ʽtAd>m/E\`!') fn@3P`,=X7uPRPeuK<#[)>g)yxvԛρgSύsh}E +_<7Sy8y.ԝ~vtUt ż[p?"1ֽ,u{xԏ{y~:~5.,`Vw!B, 0JH\ S#))$OKo@!3V\cdŋm qWtpVHx|"p΀cP:Œ{`-\#طՋ2)h>A}^5|ȓq-b I=/i| Տ639f $h{lkdbDx?!.c`q@ƷLe+̤q2<ܲӀ1e-{<2G>֬(]Lܟ~snu4#QET׿'h\,K)v=#Mپk4Xkp+݂~sI>;iKw }XڏfU[y~Z^pVד^NX9޾ vz =+(e,L ʊ2QQ8:7 2sq-\ܚy,1 ͔.q8MRud4{Sנ!j{{t !,pdRpˁZ5ˁe%=zm,n+f:(d{>GyylS?)<#'L7ӱ>|s-sQϔF/wK]䢛+za$Ba.?1lQ`Ghp*5[Sލ eg4%?\ =V/ BVLAAEe%@ L`@Z:0xSZ=%'LՄ##n({[apl|)yL>M*([!IR7z&Q)GO( DO|gv߁{g\7Ă`$m팹RjR@+لX% 7c c&Pe'=Rd'L0>#PfW D[[cdXΎɛEغ)Obz[!1*0p{{\, k}n n|: *>C(d:*\c;xL~OĥC!B\ ,3/v("F\Gl1@DŲ TOҢ` 'g;0FaU' RʭrI"NSD(Ihv2^.xp(yd2€)J2`ONҟIj Lߤ>__owO9πzp b7Kw1G?ȦŘ b,Xm͸\wa(cuL*G:9pi\o*g?E2A2 yEO%T!By\ʔ\zp^ĸ }gC(w%kcd[Z< 8!B!P U,}l =2Gz~10\㐯D m,WomQ7- }#b!( \ɣq1݀%GGr[mXpz9-;8i'N!B!0w[ XbӘ6B!pavgLl7;B![.$%YJv,ɖ5K%YY+[weV b'Quˈ93<̜ss;#q 1o؉'~c* \UIWbHЁkP9 E8X A &ƍ;<&޲bDV!= 0BC46a$] \ \* \!ܬ/pw[ B!p,Pr Lq x%UI ! ,B!m}KK{3k֬( \ \* \!$pB!+$p%p%UBWB!BWW* \ \!b }OvKeΜٽ{nԩn۶mno;ҡCIܴil Ǝ.]FabȐ!n!BWHJTBWwXt{',".((deF\Μ9]ʔ)]oFiQr&Lc۷۷X&~„ m۷wm۶ute/T;V3fUE!7%ۧ3fL \ ܺuܹs=z$:>pAVI&M*ժUͅx;s}0`oٷOÇVZQXRD:-c\K/vr֭7`w-;myn9!5jԋہ}'//BnIWG;LvTnaH{_ N;-Zͫ{^$K$pO?Hvڙ|wݮ]rTTi1n?c^ѽx-+V,/ s0׭[w%JPΡcfN#Gɖ-[`ܸqX ÇswРAv\8hѢ&J9yg=tcO=oD9 L;>,W\p?kժ K6l͖-[ |1/Sjc EO-1A!0bĈ|)'=\ 2BgIZ&ީPe?uLUx_\Qx]+aa@rETBȤƍ0q';MRBn}+qĔmzٲeʼn$.Q8rhV^mfr}k֬VZ5iIJvԩ>4+]+USE*11z}e1`<̌.;̙3ݹl|b4x)o׳dU^C=a&*U*;vly\\_4 !H"\ E?± O~^b<|REZg?{?*^˛W?֫^ʙKޫ_˧REj~ (s;^-~z/oޫ;Aw_ICbܔ)SfCDc'bG mr2e.YJR~ ڊQ \p3-o)ըQä('mY.$Cu`v=0׃]׫W/ p^.(Fy{ɋ\H~;l믿~s)7m4kCnM?|Y}v ȣ5nYtPQ{ѣGՃn?Đ0/>8ҕ2XbY|CFr98eM}1{8"eO41GZ(lRx(.qq}Ɍu-Cwݟ? 7n/B!B!w{YfΜ!!#ü .d$:d2:wlq7a8B#A^(3]u}¶&/0{ <~b(`xa./0DwB47qe/P{p r~-7o޼%g׆5i$WbEޚ9a„m>R"]˧xt!_<~̎"vv/B!B!w{YR".$.r^0]M H4}R"<4(c!a] {d,Farr_ǃmBR> ^lO.ӤIu̿S .!bPv̅ \1bE#0"aÆ;/\7$$ֲn%b]JjժURuMf&B!B!]^*ElG}D)7;KnV^u?vrqhnL.JR ϝ;+iHeրW`A ܧ9bݽu\AI.SlٕpC {]{e*;e{-&L8d: ]2Ex-`dP1Hn?^v$ui5B!B!BȽ,p-KQXR&=|x FAxӧЭL>}FnnaÆ16!бcG( Gr^{L9/pEWЎ5*p B[<[I? \^fزdx`' k+~bݺu 4ͅdݻ8qD!B!BŽ,pC(vb_!.E,$e^zY+ٳg]~}_uu.\x~/_dF(p-^[N \c)\֭뉋Yw1d۽8 \9Y#G8ֹm0 !B!B!0p2?0:oرc{M6{qIOvr;v8X JD:#D'ٳgwD$pKq/\t pKu3ޠRJ6 ac|BV,2l̸eňo}rz(ǹ=Kc/#$l;AAAar\!B!Bq.#(pYk֬0JJ!a9x 0gŪVuf򐬔e!o]؇Q ˗mԩSmo1vRmݕn+ޓsNЭ 9Ypl[@sm۶u;wu1rycƌq3gt )ƅB!B!w{Y&@?df$n8 c*>f2,ǚx8WtࢃxǨ?8dի[g1# s@L-Z dcCN\'2/~}wط6< ;u>iӦyMwEo3^ߥKh׮~B!B!P2Rb2k֬Mƍkݤ=%*SAB\—1cƴ0ðbǥ伛aÆ#p<y?gN>bL*9:sPΘ1_:v3ǀܶ5nxXR>J*|MX}~ۇpKjQJ[tK)~8~u1 N`և~! thPB f)|:Bq#iҤٛ7o)hk %c ߌlnف I|q?7ܧO0Y,B!B!CϷNRf8pծ]?Im2[?_G6 : )7((n 13tleb~W֡Ks d]׮]`tx>֮])zF]Kxv2JaʕMvRr\̴}Cņ`XMIfI.&Ԇ*cq"!ևC7o0̾qTyL y|JF3 .dgjf\\Lt#F8d~lذd1E2/)q̣tuW~͎VrY ^[[Cm9̴ec(py<\}-ܗVZe'.:pMrYmr='p B!B!B;$p}`(]SWv"v1Q2!5j԰f0O9lpvf1Xb._|/TH+.\uͲ믿B!2H !0`$9 לLIQt/~3ԝ锭 hŽeftW6rĉ|rbeǎm'/^ 9s^zI\޼ym*gСC#&B!B!$:uG$gS.)89&nʸq\ժU]=\~lr';,nٲu//_5jȎS))e.h"]իWw/˜9Js?"8IV!C&B!B!$m۶ڵի{7(2B=ۃLlCO@-^5kָ[+[Z5WP!{u={t#Fp]tqmڴ1qZti,tR[uJM0fyV.?d~ݰ 4pe˖g9r۸qaQ沓vС߶xD;wݏnݺsTV-vAkL2uL6m[*+eʔ??C&B!B!$!T۾!5=jt޾ݏ?؄lJ\M2b“Q*TxwynСI_JަM_EPR2րy䱬Y7t֌36S͚5]ɒ%->\u|A.CR͛å3gNsBر]X0v[4iYz+V(0|i9s![l;v ?/zm;2EUѢE=HZ ֺRm+W.^[H??tx~݉XO{W /f#GX1oD#xʭ,\vY^.bJlNƘM?~%B!H !p8ڣcAf&Leɒe2l21bpv={vo߾}Yx>.E,2tT):u4m `*!\v0??,܉e?pu3rv^6mt"2`4I;!+B!4B!,̔%D!s'S' n{6_ӧO,\epE *RD'lO!zF\F@"ׅ)tq)1q5xر} A.[ K>׵k2% /F/}ȁk\%`4Du۾u$ 1f %pſ \!B! +"AItRwI9V2|b/sr=c?CvBH=f̘Q]M8,Þn\=UF \iًȩ78H ~1ĉCv*v"na7rqXxq- \ڵ~ |_8q"2levIljժe >؇ 6> 9 rZWkB! B!? nVtκ#F$БpB6tqTRŢyX&#oյ^ !sv}–r$.nk׮M4~C>g oܞE3'c-|wc~$pB!B(%%2ermىG M g}tpŐȻ&MQ;$$$$v3R6aÆ?ub jaIxq~1cFJ:a#e`JXۺXݻׇ 124~ ?/pD)~.bu˧MV 6%oUAk Oc![: :|rm?^WkB! B!s5I-a֬Y=vR[gtfa,u),2A6lkz3SBAC(e-D\[vb!o2S9Gf߿JH B!B\AW!Dƍs}]'pC֏VKj֭#*x.=v#=i%:b3\ժU5牂?co/a`ѢEvpF牏/lcrܾ}?ڵk{⹻ ~O>3 jcp<, '+B!$pB1b̘HX ``X,@yG!KKupbЈ@&^U$p \OvDSW_'  ""Eeq Z4>Nje3e=8&s\?v˗7yήfSSFQfr} E,<0. P(3҂Gkf6m/ļ s8PSjS,22qss.ye;ƍk皲nݺwH:u•O>fad޽{I !Bq \!&I0v&2O@d!ԺO%sDA.d _pew&#!((_1H΢&qLeJKU..dY$IDYomJe')i_J?ޟ2F3A eJZnݳSh)Su1q99g2O:qvnm`vɓ۶١8?psecAKΎB!WB!DDܒc';lPYË0tm^9b.YSxRRn _˕(L.v>d͎.ix2Õ\.ЉyΝ ~9J`|? ֻ~A8Z1ԩS̎={f$/ R ;/iҤ ArűlFd< +ˆ/vϊPebĈA^KH<ږe-1J [Cl/yJQ8α  <1cƌiNj`\"xћ~2iw(int0{/2HԿR[d)޹vL%aTfM 6%ǤxعB/_DnG}!n}}3ryJn~!j8&.):p,YϯzU1xuBS3Y~vR.QFOsk̙3@uBqpL.fvᢣ:cB! B!;"p) z O梢#5?l1 YdȈEMy7?iq@G'#LjR u!o5D/a  \H  *g*Uхע7'! tGn6wztlfw)c] .̯R3ff&# SpWJqvN^cb&pq+%K2޿a y,޼sǔFqݢE E.)nj/@)ɱJ !BB!" ͛S٠Arje.2dCrV?$ yj@ IҧO7n8XuCLrŪ BmeSzB fk Bm߾'F0_Y./-/-o)).]zUQ6R겋˲8n ZjYp*r? aիWs~Y;&d'i`[m۶dW1ѣGAmF~mC]nj^hG#GlHL(ϏQ@w¢:i)[!VXѺek֬":ym[/DFxMʘXǯ.4]ˮux'*@!|\_s/cc0f (99[*q8xQW3' z7tnch +Ha8 q}(;a 689cgXW!BB!"dɒ,d-O^/_2O2 vWo=DGzXfr@cZ /v pݹ>Q  !E-õUVvc(ޏXo&`*" =V^؄tz,fM|2]{.TR#G,W .td. /:w0o+~"ؽ;n3b\j$pB!H !""n7n\~1O5H` L\0؄.T۳8sdsm\ ;BԩcUPEfNx|!\ie. eMCl% \} m~uS|x*T1v˞9sYgV+ble_b]SB-1I]fB!;6uaǸ /yӧw!ݸ,ľ:o%~A`f, uBWfD.b2 Br6@ }K mR#5jԈ9n;X>ɓm#Fp8F`j!p)1lc7.cDWEL`Ʌvbs;p)p!~: ꫯ̙3s.ϰfXj7ewsVFo>YB!B!" \Jy 6v;1NEt_IСTP{k W58 8ЧOr~ (ɔ'Kۈ1e,"\ř7z5^mM7n \?;q3/_4i.ڵ+)]+p!j{;:Ct003F$ 2qY!YBƾ}@߿9~t吽++B!- +B%KLj^;6/T!N"1et6ecH.2eŅ AextȐ!nƌqfb$H˗/o:w&c.:ek1JR.>]˘Æqs#\2eJ9ͮT ʉN}J0!e ekqM!}EP2EـX7g9M$vBpFt۹q6DG P8S/JYU/~qmCm˖/_2w A$g1!nHm4p.b?mv:t 6e˖uK \!B! +B%va?NT+_Oч #;9Ic̕PztƢeF.]YU\&;|9 ̎EGG.r3#%!e!cZhFEC\E).V av9Efn3AnL;`6y~̂Jio.\MDB!WB!DDܒp+˦]r%egBX];dt]a`y\[R wΜ9̞!ZHݞ )|֍zF:(A/lj*f/SN}駟mR&CT_F&l2,yXS1 $\m#s11 8'&)9 ^5k'Ū/v ;ٹzemQ?~ur}q<6NWNBJ́f>&a맼&< \>sx>.<ߔ7$pB!B!"B/` nYvR1BFP_nu9]IօJTZRVJS >ͳS̙Z&;|/CxzOEj^Ly͚5nٲew`/)qƔ[l}d"+B!$pB:{/CKɋXdzѢE}0rtq!+B!(p_W!E6ZJW_/aX,Y乙ʔ)ӫ/S`F(3o|/#+B!8k 0 Z=xUW!HF._l(q>}z.]*"z1{رc{!+B!Yg_^Γ'&ųB!DE6jժu֫Wϡ֡ 9ŵ \!B!+ZcX _b 7or$pBmü8Ȍ5rDpiӦbn]|s%+B!:GqgΜqsuG'[Av_j>Buw \!B?~ѣ *#FuKeʔa׭O=MW!HB!P~76;cҥB_?nvrY\<*-ǵҥKE)n/\ +BHB!j0rs3&O~G rfjPtiE|WHoʔ)&L@b8Qbr/B!c$p]AW!BPonʕ[nnΝnnѢE/P|3o۵kw 6ɱc(isPR>|CvnMܞ?uNBq#+ B!~fnӦMnrzrnF'5/Yxq[de--^#{ʕfϞNNlӧAz&p!sn B!O>ZnR*V˟?B!vk֬q!!!͛]>}\^tQ eyFNCL;~;yu߰"ٳIB!c O=8q"+R;v+[K63B!~uw2G}T ƌ3^zkW^1iw51|cϞ=o }N\ƩS(v.Bܯ/pI8/.*Ud-PBwΜ9B!eÆ ״}M6K((P_Nx=46x~[R2~vm 0\[2B!}J4i\Tɒ%skxdJiŏۀ/fA:f͝;׭XBB!dO>quÆ f$a{byIv6jpp-[e-l=z. BKrJ\,!>ywvƍW![n=* ?{FlyիK*B!IA;yd[hQܲi6 Q FAq_~ĉQ`vޖ?v_~>[.'ؔ+Bܹӭ] d [y!v80#Ν;gUB!B!B&Ξ=kcZF9*j)lä-[F/]v_~E'T!D`ӦMWݻww:tp;vT"Zn6lڶm߽͛f͚oTvlM˖-3+ժU+׸qcYP][-2ZW<AAA[hO5kud܎~뭷V^+WkeAZG+uO?T?9rp7nԛW!'}/ܷ~kOF?]^?|B${M>_O@[^H]]:uj)S&6m뮗!|ݏ/(/ŤREzթS!իWOCuWs5jpPEz7VZ:<\Z5YP/-~R; ڴicG0`;w#|g}`{\wgϞ-x|8w]`_O,F:twyBqIK mʕcƌ\+Wڤ+V~nɒ% Æ eVXycbM*K. I\~wޮnݺ&1T.i,_f"M\ws~Uwσ **U5TH(yj5WbEYڢ{ HvN@(w>4ib]av͜9-_>裏mR߿ߺp$7a?]>Yӧ 6uDeƏo+bկ_?{rn”9u>i;v= ?ݗy%obŊ6Yv !g5i!C \~\/&n[k&*U3ۢbJM׬٤JWJ 5j4ZZ#:a{]J+5V?UXTHk4N&|>*|-с̙3Or(}LDh<'@ؑ۴i'6mI\1b{:T'O_#GxhұN\_.\{A/_vX]tŋz/`Ϟ=nƌ&Aɐ!CZL,Y,-RG/?ɜL'JWfRR”22\'?)1/`cB2+;i‡gϞ&m) 8pַo_O7C(wO 5?Fx 5kÖek/|ӻ4i\WH,K8~\_1|Q\y/%L+[u\vREVU|M/3Ry%J8KUdVѢE/`ͥ$I ~+^%UHo ᵠWxKkAUXy !|~TLJ@p9{3Q|9ĉFҤIs٧>'N8pw9~׸t ;% 0Qp˅@Ƨ3z6EW [gvFrjK:v6l6VGd2TG徥r0SAyaCm'O@ϭݡ(t(D:Š Z\Jgp?U˾q]~o^E}^έݛzU{Xu~5iR,\<~]Ǖֵ[QmZ"sCfřp .G/LXSN1k2kffd)U͝;7PK0_*} 7$KBƓ5YsI;G!gvMDΪpYggJGկ\VjurP [d2eK1",΅"YsXE_uYJQhUEUdl5Z (JvSt1c]@+[sΑC\/UT)$`*w\kF+\~<ks7HӠ$R3D(\mA߾} W?9t<Ϝ93O 5533kJQS|#Pz@/Q E(, jqV=rR(DH 4~s)z\i)`^ 8;Ŕraj>d2Ll'*&ñ?4K#yz \6 1@)+MBXF))_ٰa\Cr>}'F9rUG.U~ڵkܟ[KfMnS* άK`ubiʍ˱"wʔ)pC%(\\33dE ƨիW3F')ϒ7B`R'|PPj*͛ ֮]hUfyR&pU:TDKmE"pM&ɔm.y֭[Gn uxw$ T4Bm(Py*+'m(v3Rt J[H0J?R=͗/s$W͝D m49T  T땃N"id_"߮oW9uR _[nj(/*̔U;/RJq"qh5o޼X}ytT =zpδ,\\33d"HJ0{D47"at)8b?|M9[,Ίp[i nW@ƍ] 5L&SԊ+%pKQp(Tdl5fIEwKQMTo^&P_REk~Pإ(P7޸Jߟ(v-o@Im]̥ MO|D۾;P;(J6ʦYWjv_iX'E /b7t雐7o׫PѷeT-h߾Oed\SrRcC)"L|Wf ʙ3{`kFϞ=)BZ"nţ܌zޓ|. W+4,]ԽWd2LJA 1:,C"+Pŭ<'Pf p3O] >W/ѷ.5L&Sϟ뻿)d.w?C q+b_D*yiwv|hX`b/*(@0s&;v@kJo"M`AD- tP4K GS\._'5533k2kffהHIG,\P\%7bpV5vވK"*tYn\V\D:+@={S,tgځTA߀D*AK/6l;v0Ӳlٗu ~k2s -@۲eKqih "\\33EdPd%`,oj:B_7sҵ| f,՜-[8s66 %9~n(pnSi<\d;m?ؗ53R밚C`rc8 m7C ~c\7ȑ+`WlX09 )T1N6g`ߔ`wn)ƍQn2~|zfp37cY6|Xq ?BNw\>&V9&jV&rdb$ ʊV?[ ܬQ$\#76Τ O>9[[1ò_nFtӕ9r7K4n&Q u|i,$I*~ >#c_cn.'~la;AՈe8\`nd|y12OsGd٢ow7EvcVBz9KlpM&dPu) K4-;c¬gǒ#. <& '6pLQ'qXGskXD%p.TrqDJPGr $罈JBPU]$vi1&*DY6t s&\~bB.xs=xd_t@ &Qߟ$y;%ܶi=Wcg mHv Ԯ1HH\L@E~_5_wFt|H=Wˀb*UHvڹ2|^"är~"_›Vߖm"g=z%R7*"xm緗CpQX&f\s-~e??n_nޱ M,Ku-;i>WcېqC[v ZnmyK\][|\| Ws%1p@OnHm}NPѯ^bddL0jl/l 7y_-_yߋ6|ޢ|JCUVݣ#M}.b\d2LG8Y&\9ț)pv88DH^_9): 95*w[<8ODYy\ +Ph(ei<8D@& r h1\c}myȊ Ѭ\#W&\E50WU?$fYL~щB.MLRObMYV -xM2IBЕԵ/0\xuaN9Iyڑ.[?}jfw)cSԦK' hwQ_+'}&ػnTo's焫'O;F{i\6poƅ2yM ǎ bn i *_~"BĚ+Be4%';QIVϟ[uPySG0 L6RkkܜTnnE^2H2 Py6KP%+O䎥eW \\`AH.2&7|J ai՝4!&sE~6: t DhY3%kekg,t3)s+.^5=MwVNK?l^?JxpΝʹZO+7,fW%9Q w-tmMZhӑG r? Yfr4n]Go+Ja[G:s*/&ߩ*%@`m򗞔5F7^wׯW MW7śƘ'sc/_xj\M8tڌ^)Weۢ>\&d20,p.CqC݉wHDN 5AN'KV2YʓlCMCM 0P`46k܄1 cĶy^ۆSu ED`;p:vTTGgio`I&MrB]|*ŒW%JtujOBt|5 ci2r'e: _CǑ9}[Lx6/^;sl".#mG\?Lvl0lNݰe:Z{'ڼY8Wpi<|#IGAɔ@˖_6zfaO!fokck-j?yM'_ۢasa_tma;dz;"Ҵ;7ײv#:#.)%B9/ _ɓ'$ b`"S;R$ePO;Stng0cc4 VWRETr'َc vk0{C6Br~)dJ* _H_I@[pqNN8!Ϛ(X^`|g$BcP UۆaVM[iKm ?* s]j?O ktm`oJӮÏ?1f`ذiּ <ƿ֨Y~ [}.=ڸkmشvؠ~vl~~;?_6n^7kg:eq }&) ?D$~ c?J\޽!>7?e{ε7\hC,Mk I2ħ;-~EpwV+놨CEHhu\d2LG̣#"arLqqJ}|IFH\99# flaBDo ?Bt c߫I{UHw| _/GhqE$^s˿xc$G|}& u]o萫9N;hd0F &4[9B'][gMjnușg sބ| ?ɩKt믿> f<- N˵(_o"u 06gҮkYm;6qpZKt7q].05mwUw`\lKv":~2lӾ-o}mᐯzi2^uQoPw];jow (տKfa^Ït 7#b>uӦCS5xkApp㷐:AoO)$mjlq~_oۮ._#5Kt^c70FrPɃo߾kG^Xfc{tC%{hG3qu|PWmaW5iq}XcUJKJ?V~)O⏓p L&d:Gr0o\'4oZvi*4Nݝ8v(ж; 'YQ;Z*+,INlL4Zw:~GY 4C"˫a5ZǤ 4d±-/@@:E&3>{ `>s߉||Aw& (qƴuKXpB}Lw7cqDf >vQ.e?Ξ~٧U<{if}ԫM/en-if?͛h۵׽{r썳[e?Ǵ% }zNs:up{(؝1p:C#u^|;H7fػ~?.(ţ T$Cͳ:+AJgMl'b40quZj|^ߣx{^,eɱE" UE[ {MѰgeGΜ94 n6=ƙjԨrÓҋlw^ﹶ7wu׮'x'=nS?Pa7~VZQܥ27I͸HʵAkTզjAOؼ4tDJ-\0I)5@nϾ$8|m}~=pk\'nˢ4ugtK[r4e:٣]tnf 7{.w- Ն+.)~}nߎ_!1Ƴ{5u|"mS]8zXO 1ƏƓ_O_P><|3y6 L8]fmHHwW,LU`3Ϙ 5 f<*`nHv*VWQ\d2LGqքt=0TbLތ&=e,}(RPB46Zr*FAWg'-E?=T5ckI񋇷db1!MSP]m;I90G7=, `B5:Y]2__MW6X 4Y&Tnbo %JI=@m QYyEE%u.) |nZ"IL0WkwE d֟nw&v/k^wѸ1Z8߸cܵgF-m'Kwh܊'tR"TTL7`ӦM_, Cw)Qiȥ07{NdLw6E'[n/l ȉ٢E 9c jjGbgS٤3xhʆ,`Hw`[ \j/߫q7T: @ R`?]U}!z(]YpZcm}؝tlϙkCԕY56iNPkiuFpͦi#64czOu/W~?IG_<8[~r|"Vc)9|h]WXgFHqoS!cЖX \ܭJs0_]~i1 "n)p}ZTZ%@(0r({Oad2LF'5"Ae8Din.G<-|D'r9 L, )vNdsU:9ǿM)IL?|5`lnWsV%r"@ؼ]qHQ1wc"T8qEAHL{`u?XPl@\,:\E;G \]zBEFsw\<۴c0QFnlSj5-@aq@Ϳ px?n|JGS/;pl%ۓ*ڔ.rUݺkGFwRW>uOwY7e"g<}3m3qHއø7DrÜmЊ_qe\bͧ7KQ%1>&|]|9K (P6jL\#pM&d2ɩKdE)4D#M+*qjM.# Q 3C,eRL"pFm}9s9v4Ã\+0S&rlm|s&"qM.z /cq8 MND#kWĭ0J09u 1 "0ba=-piφNGYO@.oUgÕv>q9~c8Fl[]4sI\=7K u]*T4 +22 kU)&7c>sn[A[nq#y/-WթS'P97ᮼvi\# Pu*BU5.#9#E@LeM˓{1[`ԵQLJ~8R_t! F8'RhRWܷ̋5r0u/u\@S3ʶ* 6myl x@!_1|>|7\ pOi74pIE؎WTiJSkS1 1JuU\ٝ/ZEf$\\K~>*ycz#sUHs|GNE/G+|HpM&d2eWNz6Yχ BnEr#,%_mX\KDԽ,'z FTh׃񋈊PWGS(daλ&( <&Y_\$9?n@1vcj|_=:vp-w%ļlOQ #)P306}3iaǻ>&Cq9 eRG|T0!" x$r\`\iv@.Mߗߤ6)u^vᄁ T)|S\ULED6J/)1Y: 28K. إJ*uu&pG+Q Fs{)ʊs](DIՔ&T-g 4!bs,v<'OCXjw?H[7v#Ivf g-osL ;v;Njv/́vp1yv:p3+z|(|4Op pӢX bt-x1|UI^wiEUq`+HhI[|AEf“]ߵIZm#pM&d2N#,G1Soi)88,gs}.Mo6bxAsG}Ngrh҃/bFS에c<3rĪ.ŀ61ѫ:%5mxCk? |ĦG,cA"e' O0Eۘ\&9өrPnc u<3aQt}_1ўkձ#L|ؖL>b75'2ߖ/ ' `#)9p3] _x <~޽;O\qGF {Q7d5So`7yok*cXDfe4e\ .V) .]"xQ4gm9{Q]\)]vU%:"`⎫*D MOA8ݦ}]@1S1uΖMNhU;6Q~yakz6]H˱( w8ap{g sAnE̘Q9pr<&})-MPxʿM_XƸBڃ`-i;MwvSԖ1U׆?y oդ#b$\d2LG N>^ZA5r&Y~K,цL$FK/O:4l"T{}?ܶ`^".קРAT<BSd.}*5K]hDjE"UԓO>9BճzZ3Gv93Y\gkbW>]Vsj{]ޛMJѺs EJj/pŞgq?*5@SmuOG6t W.@B]4O}IG~?8=Tf/6&weըO"7jVYæ]Z {2<cnotůsIMm.~~0]]opR '_V"%8Bݜ4"pQ#0*Rob饂V\&d2Mm/&g"W^yT b.}UXPF#g܅@X QlPlK"p=&a s}GjJ: PdXqޙPX0@D-Zx| D<r˾ƾz?!ELD/e-Lb⯛ȝ믿F &M-nz#vCq aQW|;aڗ_beQ.7y5㏴ ́k7W߇{_@`S`޵~'w5gvmMPᶑT2'Z/RX`] A]7*7E]E=+JOM8 x9n6_~?"e&}^O? [~Aǎ]ZKV8 B7;jz%w:<9LAisƺ;4 x]#7=E/"gLRlkhQMnk|ʖ!1ΡCJ|Sl#.8WrFDlr\so-O.kumߐ MMHmWҗg0pM&d2N#覍t[F]kߊs ʁhq0?WuY.X"~M$>ÍS V?z!oamڴ!s D'XQ&\DsE|Lu\Ťha_+kCr(iLm;"zC5Owu[$L)[zu]kMj?S&>\9ۃ+dsp@JV8WpBHCq3}0p8亁e Hymؐӿgxj{k ͤ Qzʁ=?zn2deF#pw=csQ&~m5Nm5"lkN۽@;_~07=bFbaTogps& 5D覡.wwlr9]sU1pn! X!!76/O۲".i5>UQ\^w-Qbۦ|rz\Ǔj㋲R25=B5C=䊭Th4Z=?sMxD4yOv˧8Y<]6K ں}pIw0*# }nHqҌ Eߋ?G@)^"&i*8_5jChNVx_D1iUh>0*EEoAvmW Bmn <>P\&d2 zp*DQ!@lٲrʉᗏi܂;r^4$2&B^p˷'i? |{3y)**-mDvhbnݺ 8C.D40x,E[DBz)v;i~r]frv$C'h²GT\- }"h\o죚5qK]+>7p[wg c=07JUfK۲N7x\RCx Dtr):w)6/׬Y"kŸƧ7MUWz'HxE)ceCh^`s3NgUPP! OVP!- цҺ20Oܯ\w ϟ3ɮMKk2\^o8TM2."r<q_W; MH)EDr~;ۣw(Wr͜?]sz"x12_gp'o]v Ov'w+~n^=.&(ßb|3 ED7q|.%gup];|Okܭj[}IVBȹyX?Gv]g{j&s`59C _UkQڇ*_\d2LG!%Qp&jҺHR"UPKu"(pB8D0;ǒ#G|`drI40w[EC? Mr gEI0CF\&L DeuDF~.:xq19b _$vBtt4%;(NG~DBtݙDnl4tth)]kF\~ܺN\6kzBnNɚ*E߶^\Ai,Ha04U`  H*.ˉέS'>_n 4gΜaɒ%_V J[V-R#D>n1IۑWѹU9{S\>Y6rïPBAJmĤr0Hg>ꪫn- j Rk۴e}%iݟ4#qM)Fh:fЏ)+#.2{Dw]PJ}."d@g_,i0GyhF5?~9/oI@3RuQKA9A.~$7_7ȯYr 2&]u~O }D7r-!qnG+?f[{,P75=#\Jq\d2LG!)ѳ<(y%/#h̕1.ܝ8ŠTE-c9ZSуJEpVu;ph=b8 _" 4p\Kpycb8 W;$W }wAVa)&B\"y?bk}!aҤ>5YzG+'wձpwc"Kmrۮe @ff\ιtx>lnڞ Jds#/rL!DVNh)l(@аaCW|e hѢEкuk9/AL\M?Q禙ӧ ,5k2L&4\"ֲ?K788r.ic hڙ> q(~F`vRWK*IG|vMviG@T yk;I&,98V\oLuMmNn[>s^^ײ5GP]&3Im`ߓXz(D\sMr$%$$' <^=Zp0HVp u K-oٮ .Z7s#p#rǠm[>]ZZpowW-)ρb"?p߫}^!j;,- .LT3HN]g' $zwnoDG'sew">4Vܦ1Χlw=se h.!+%+]ADRQ<ӵ'^7)$cjʕ+jb|kOP*)ZyAءs \L Ly$}Ѻ:5s`4|p=$0HΏt lyk lK" b1$ oo]؆}k=~>Q7Kwc##)R Tch_4]_9*{^SM~"v$ſ>8c+]f9pGmPHc8Ѷ3 O ]fp\d2LG sMpE9/E3u7vSc#J r>jrBq9*ZL)k"?eo>{uQpLr_!uS`uZ{xݷI@u& 5s n.7]J28xHiܲgc&2jV]û:ߓX$gÜs )Cc.xMJ{Q#}|W\ҏ{;v(+^]C>^Se&U\cuAV:@Tw rEJC_JnC.rv[f{~|@a"cIJU}bJ)3fNw2Asr%Iy ۞H%JTd)R76%FAH{ c'&xJ؟F}=o޼c`"EKJ|}G3&Mc3H I>x*8SZFҽH7Qգ>'m"uBVc;d vFQcõ?.p1g/AQR4=:dis-Ym'KD s`K@0\vaT7~\pֿ%I]mw_fy# pܔ@O@W*i-G(|¹wF ܺԌ1,{1!IK"# WZ.΀Zqqyc\Ocf k"ϸʕ+j*i{w*@G}4x 5L&ɔ QM@UA!wIUc/{qP,QId6 +'{?oqX\Ydk#'pY :]|->kȁ Mػx9^tx}-tPt6Dx5eߧr "&(Ţ?}hb Fo}˵~g0WzD%) p}t7*'@W`nM &7,wDf*uO:*w96#ڂ-;:k:V|;ѥQ_IOJ]MW>Ch15Y9j+(&WE*)ePq`Qt_ܐQTS5a*Kc iB,4 OsZLE'ޜǷF#Wݱn?"_.TT@Gos&;GDR2 @j@˜i[jF`T}^_ ґ?mBa:6cN=D@"T k]HNW"K +-y jB p]2iIW%a5 [mo9#ai1 t9rvlqmŽ( ˯O\|@=s ڦ "\'7 l}xG-{#_?f‰f>UZHז~sXʫFHZ7HRH2Hk.2k@,h3kɡe{5VVt7ln5w5k2L&).b)F$|rؖ ǟ5X,h[e>-β&TT *Uvo+Do9m]{t"<#bD1q(mv %]mȩv ]Bmbrri.ڷP˖-]>^\gZ͟N=Ym}[NRT]z-NA7&_N@kvA{oA8 #T>Eq|~{Su-C<KafRL5JĩO@4/4NX :w ڝ3-\(UfvmMutPW-Mm.aW`u|عi};~j{#:gY34 7.`RJ.V:J4rߎҙ>i߹G '&rjx{$. /4+zKU>čb^1v}O~gmo7W 8 /tǯE|/>b3Es,+}L % s1׆oMbg#>ꌚP sOmd2L,9&DF'8@:ypQ!D2`c/}Ź'Mu&$7rN9#)`u}/($/b{owOL g!} OWWTRhܤrL>x@Y}\% \D_oGJkzŃSY77Oy3ci R0m, d5Ւ{u^ >ΘVnݠFBt=PU;Z* ؖWj-/q^~e7^>#XNn<#?unh4%Fڷz+P ƨ+u/MWXjJs~ SHWs4X/Mz+Ğ;)S{k; -׃Ƶ ÅDr9•Se#9gyKڡ0]eaNk.Z5=\bK@/) ZrL_ }2EodG3"OH~߿5Bk ዥ$"h.1;ݪ5=w|k?ݸ_8dOT[n sΓ2r[5򫴢n@>ByGmo&-&#ם/ݶm[pM&d2e5-J)XTD&RNmrT ,-3KduY>%%$+8N08W\!9?& Kp*>w1D(t MTEflOn^^K/K|n6~kG w> ?(<^<"!|S׹Y |\;|'&Kk ű0#XpJarqg$ <;9p L&d:%SA$nj0\-f-A'󜔈L8P,f8eBQIm1ǡř%jL _ehlʡodW^D1\_5ًH76%7qh"}anb "U`.& ɸId-yU埥Gsƫ%GZzQ$Mk\js̒\z _>{ϤW>Drs31n@ƟJ׋_qOZx±|1~Sj,|_J['gΜs=p L&d:@10SuB9a2PNZpq$W d2kfה]M./\LU <p L&d2{T%CP4d-N`УGpM&d25533k2kffה3<(EBPbŇnV caC:]qC5 7nt\d2LpMp 5e\ 80'xbb%.Uz1ى/RPvm7\d2LpMp 5e0[_|Ŗ>hPBHܹ{ݽ޻Oc[7} 5L&ddnNTRy,LQn0iOwoOYYGK \'k˔Sw'>ܬ%h+WWi}0w>cO>hfegr[38L,%on: *6g&hذa[n -}ꩧ's%9sꬳ 4~5L&ɔyzrU+W.xWoW-YV*\EV+xffYj/څ) w2S"oO*Z";㤓N6р!ӭPBY>v~ .t~Be.Tڅ\TH{lIԬH"i_P<}̙.+Bvkhoauڴis`n'n>ed2L&S_~"1(S'2X3,ҥK9Aɒ%O̲,^DPX1y>9^|>>?M Xmso}1GE`>1;vpg,qKѶQ`#_~Tc3ĵ 7/PqA57xd2LUF*>sr:kfՆ3 k?̎-E}e]v 4h"0$"pG3^⃵kBʂ \SN9>[i40;Reʔa4/r0|p77A?[zA>}(a2&d2L&dJRcƌqI8Ѷ {=E%vIDǏXF[Z`ʔ)8&d2&&d2L&d:@ rAqO`ݺuֹ&d2 L&d2L&Eog}~*kX=]MڵksM&dd5L&d2LcX[(_{M bŊD曷7 $L2倂e&d22_pM&d2L&]1k^z+ y"g/;MLPhѰf͚Lٟ ^~e.a&d2e L&d2L&1;\{l֬ٛ_|N8!;>|l7ukqQ۷o:udQ&d25L&d2LcD&L$ <8xG]K/4X|y{C}ؾ}{.BF\W_ei.r~w2ί]nvz@Nױcp-C/zhB}9nر!#+,>ND D uq &%+W Ӂ[~KuLk8&B{õ qÇ="OncjnMV!B!BY x;|3j۶mk$6eVZ*;w،3b ,0U`?Tk׮5JG:;7 W62i,Z(-}~|{։ Сe-PVBB) P9u6mZCܟq˖-zw5kܡϭ۷nGo7m\pӦoQpVmAunݦm._ևzO|vQO*U~Ա۱y ;vRpƅ07mPm 1ܲJ/lo !pC!B!,`#{:d:0>@T~~DQbX}UH֭N}c8C[%~l<2JLA*0k֬JbTw y/^sb`1n)&}w r?k޻kn0R_wSw[o5ă%owBn'_[G.fة sCe& q_G.o~zn 1ou]u֏pՆkB7B!S(d!R:['fbI*T0U)))dȢdKUR ƶmfݻ/Xp^Jt[Է^5˾}@ (y*/Efeqw6s&\IYt2sQGE{^RCC/ssnDNnBn|!f׳1GL_<&ꆍ놏 1/aęCQ=?{ )=vJnnԤi8b||{饗o9rB{G)T=~?f^J!Ir-ʿQlXʕc-ZFYB!BV{ү 辯N8KT-EeHHl֬)c+VhЖO'(s#Onڴ?$FܠA E P޹s'_9"q"n|^ڴicZ@(]cȷww_RNUZ]u1AFn`;8(sC qG5R) M͞6ϜDx{~uݪMs熘?n3ۇ׍:m޹ĭmkԨBjGQ :}oRv j5J Tm_J{[kH]`[[AFZ}[`m}^^Jg]X*_(nJ GI,WEC]R)3Vx[_.zƍ9?O1 B qK/4v'~$}p H7ʍsܦ_u}b{.V'P:^``-Dչ.ac1"(+ }/zGuȋ1 vM|#E@$\2Xm2|F6lHZE[lT-TB#˗/pDuhTnS3-[[2} 1 7B!4ml^k dļ\0upSZ)e@RNjS^It]F5}AP "9\hH .x-7қUIo ?@+<ɔ2ԼE ׹KA \"_]:WL\>O~ )#^u@v!ny\uÅ:m!bn!B!ܲ`)>bՂb_ F+uUT'zNƍOSq UjܭHmY0x*,@UgQ8*J^mpYLy:ϪW);v8wex( \=}Æ Vy=]K)… \AH1 >[嚯߫⩊( BxTwWf=}B!nB!n\ a#V`,TRTx2Ss>`{tڕXu9жIGoҥU:p[E[[4ﴽ/~o{b3QX1wYx-"eJ*YRXjۘҌD3\4We )] .B7oLSK)n?d|B!nB!BE)S#`IK)[Hz/Z+yYK~C`?Ο?t`6U/ U3g/{\Q~pcQ9-pzT Xxm(тLkXeOrp&9~]|cQ2䘠oLJa\)\s&{7vT-[fM!nKuS~9P쳱n-o bC!B8pioe˖4RۢtKag0K3:g.\~+\3k|%,krjc@Wn|໖P5-~vf2%1*_8N|)qQrHD:7p-n⾅۩9SnHA!b nryb>f;:v뭷Ɗ)b6.ج5k4;^y1IH TfaDz?b%J0w&N, mTtM/x1{<}1]B;.R7} : !B8$pxKFgx/تULmƉt@]*K)]#(U4Z:#ҢC4P e۴i4bɫԾPy?@ %]|=DC~B`q"eQn,-:.nu#qSrpC7p aougJ(5\g.X…M\ꫯ/^b[ŋk׎,Y2VF ]vY\b~hJ@aCco;W\$#"j,I|˕+giܸI|UAP_/3nUW]. pǵOzV^|#҂d~jmқo{c}YLL?kW^ys&{a'!b-YĠ,~K7@ut!wBfU~GifH#OʖcQU~{KL>AqKgH WtT]畳Z0͎glx,0*Z(_g?K/d}@[$:%OQ(eן{5\ NT@eNe˖s6m ۶m3~߱ /0E?ϖwj_zD՜%~EoyOP}UB8>lX }2܆ߴ8C8D\0|ÈwD@.7!vR_,хF%yPz@ ও%yDyQZP& GpG.010sѱ_ 2/7=  P.FL0a vW޵PzKvM#uD/t5?=e8?`ה^c 7CUuۣ߷`f[CWu{.1Iv(co8 'm&m,}mzl /K_Dkw3F&hx=1J"?aүaʨߥ[>߹sgZ=ӏP|>0 p (TQ}Sc=6Ac+Ԯ9KC)ZQk/K *{Ş{9s=3"k'@gG*~+E˘ [ T|mBɆg9Y諰;Nyovs[lOgT23Sߋ}_~yoŮ@]T)rE]S#}-"\P$H ؃[l1{e˖,hq(BP )))ijXeӦM="ׁg!:#ZLz<79v}pNNs :3K'] yΜkCf Ksslϵ~3;v1Mu7u/s\D+;&}AF - , t8%y?F\ĥo?oI?L>P.<Yd?}}5,m;e/n?7o6M=+W7 Xrn[SX4(IKc^(ZQ}yGqӽCY.zTA@n38H|&ب^5jW=믿^qҿ2 >5Ŋ<v70^L̔YIe#8{HƖK+$O#8 ,3 1vABr%u@8~Oܖ/8[׉ϓl.%|͐/e$Yq$_v rZ6a\9Skf_׆1\!zsp򚛺؝g<ΐ'oG>3%&Bb<cC{;!Qen:HP0AGKCBGIA2J yʢE4)+Àc%@hB;όy8<'~s} D4fI>PY HQ"گeM~)?&ynvgDgL )0z>`U>h u^Kc2gRFά5`u}*s{-yz ꫈SEtm^5?my7; Ɛn,:e67a7t7`<#0ɶTw;b9xal7|rl fLY3:gomH%W 7ؼ>UߘYk{swB^ e&k[bYv⋝"[<oҟ2C]~Ps^ΙڌPyGrX.:I>|z\ؿ&اDOJ.R9Sڦױ/clKO3 EO]|J:۹/pOXHk%!l/,?>0IslS\B "_&MJjƞ䑱1kQ^!c;1%'TuM,^,`s^M t+cU-s?= ey8~AQXjՊq/`\{+[$]&R+?mB1x:/w4eFwx؇gCL=p_11L>nz&O!ƿL GApˢvsqG1.m|{<9B8l.FCVyzؠVbZ5j6F<Éջ@V!uoAOΩujҳJkJǟE炑 [ ^9kw5$tj`0s 82|аws-QPzqRtizL O==bj>&ON.A^hu]q HM֎a=L@3YAI<,ǵDz꩸YԵ7a<~\_ꓷ9;pXŹ׉7/cɸXJz5 XI˛S.ܷ7۸g4'k[ K:)u^?ϸܤ. Z +z62Źr; ,)e! Yud>jiYU#\7ʨ䡣sqqZ #PӃqqD%qU;O .0F:8 F@ ]~;y.%fhL0=F'K9HJ2Sf1Nb4Шd +uH(/ JțE8g`N{7x]:.U!-hq?6^Aѡg 7w?l*pJnsW[g*t>Uնu9"7cZ[fNwUCWR4cYS״J%2ࢺm^mn^`Hy(CU s=x`$WnyWb*Xƾ_ %]ي.VsSHq>b^Uܕ36u?iUV[y4sX3;_*>~fK'lTrU׹K<{LT.}Rk"\aeO[lԼҝG /]ʔ\#e|RGMKۭhC@<ڣdmU=6)#ڿi 63j9DߖJbF>G6eF6U ,b;QL_>}N9`-<3ٝ@_-}-ju5>PNQYmP?N*֠b&D*]KVklr5Vr|VYpKiSx.j֮LFd۔~ܰ)K fJSv|*TU@uR] ,ѿ-D\.G՘ Pʡ0~4[j Йtwx6`Mm7 zn"p~D\c0Y׀on硤$`'z @K04ȅLYm\`•M & 5y|`)XO<9CE@2 fv`O(p)3Fy_^=ɢtLfk۟/d`0r-=7?tQԮ@O!ޭ? )ƭΘCAj#f<{LTRxǮZOǹw Ag%lΑ16m1#y@]QaiSWK7 )*y'>k\ wͱ,tge19մ<pC8,. "F/\ pL >Ӄ{}Sg1(ٮʫ1(tn(y}ѶjWQ^Hu7)VY̧aƙgu*uG{@ V!k=v1 6u Tr|#.t ide{t9z 0P2dsqwC npuhQ#YݻԶ>)Ըv[fMw}|*5(_DXő'mQ߱QCc,Gfi^j ~S/8Ѩ^E^߿l0@6k`"Rc Oy@@ǻc"T}ʗlᷫx@m? 9J7S" O+ VGQ' Gd`p&3Z/bR|aۡr>d ~`>'.W%U}pCJ JTos+N[>ZԋqqNOQ; XW DMHP~` [*Ccwnj%84 oFQ\z84uT.L~>0n.- .N:$JiNקuw?@c{hP>V:굷@NR6Iu#_aoRdws&%٦6_=U_YO )/1pR74(,Mof wW]("Ucp/(4&!Rǁ˂)Ϙ>h˥g-ݣ?jv.Lj'{!B b޴el ǎ Vܣ<-RțO0@25Se)"`CuPO=M];立.[D`]=W(ѵNXM*ˏyۊ<WU}~V]5.5J͸J^_?9 k{IeAѳdXn!NW=U}uu\u8z/v= sA߫ w /nո2a pQ.!/5f0:1,eHpOf[ehl W*)-(}2,!ßFtǹ|~i|,'}P^UyF|:`y:V\ʬ&K e@@zYFL(eُ[Í2O"[Y&&F, st VL}6D=$[j@Z8( ":[mL۟m(aޥm,Wݘn(mPHx%fWضQ͔#vvk]&sup+ ~Z`Zk;P T忽 pL,&FڨRQN9L˰߬۝R\u[\ϋ[>Y4a|_yDױ[k{Fa]˕QSuT63pjWaЮܠǐQUjԦbvoo\^碯!O>hەL&Fm4}h4sfpIG[8Im,V~#:Mү<+|&ԥ$/zi1p:vZ,he0Kpv|Q3V>N7-Ehe\+w?m .fEjDwbf5rPEeva_'XgIGL__|~Q?M2w''7OobWS~AaSc"f!.jQl } vS*>׻(p@A@m ="_,~n2ZqGuNo7O؏@٤3y`V\pׯ_ע^>s?p駧 MF)vTPԔPV%BXl`16jMi՜oAV'50d)0Eߏ͛.M5rB^E+g~ Z6ȝPq@K2+yM Ryʷ`^>&`3r P QתqdIZJP_XqCȬ+F-5v{0U%hP_H$ҝv,/3My`~GKͺ&y~p L91HwJ%N)T(~PLҰs? X,`'-JWLnp@ zOp>ѳXVf1T#oCOs؋k'm&q,0#c@-Fo#vn^"NꝬG~s 2Nm2 ;Sq{|25]ys`_\́\::~ky[Ax˳713.ąBvJj۴oi&M/w~i&Lt_$ERݾ+mAnQVYm2f,/. .u_Un+}Caq0'Q%&0ߠHWyvpo\D[nHx<}+"9JW યGGIEc .}}$>1>Fy1:(ER4HC}_ -jv$jj0+xI`tm;~uX++:/p?X ¶h;UI͔[FQ3W6;Ye鮁cr)QAҥ cyjpC86Rv 66|>2$sN-(_J\&Q/FOFǘI@f' ~2@]O/G L=kH|ζMoMx+]ZKܫVo-!V? FY{Q֓:ԭ0^C`i}&}wye\~P{jO<[6h)A~(ܖ_=AvW|^]AOܲc P:1p = p e:rg*7vŪ(䁦TSu_9 Oq*{;·ΙlQ/|azx=u] U׹z=!q?uDV>jpmGjϹNr.HWhI[95Quų TAc<TFkbn/[Z:4=s"XQ`SV@d+`(OJ?|9~ut}x@\ƅ qo;*9|T N+lQ'"zS03ILѹbz>}ݚk{m*l5k淹 JXIt}- UgWe u(0 ]߳"Ď1ϛpjT5}ȸdO݃'멾j1u)zE5>pC7bѐcH_cP uX:wf04IgQ1k`t| 34T 2ofևmt@/J'i( 3@7&`҈Z4sKsT/c t8k@z 3pd!%`K;T ֑mRg{ث= 8?_I9_~q> 않@cq.#Ag=WlvMX 7eށ8q^t|⸐-B0T^1UƩzZ yW!roWQkr}Q^¹bHL}T;&PǬ^ Wzݢc5xG"ۨ{M$N6=u|wsP&1+Hc߿Y)pʇ' nTJWR`x_Jw~_Kl|$F m\3+~f8V=S|` GJ,E̥JpU'UPcP#唝t"~]AZ2FdG _ۇҦ^>/ij7^kta%~8|3j3 ڏA1Ŝ~.B\yCM_C^Rt+3 ;ML>;* !E⼝ʏjײߣQ- o_ +`O`/ۜ\p>}"%%Y ď-zģ7}^i(O#Cqu2.@5bf8KĹMWu7m/";ˀm)U䯿@+drlg2@Z(ƋKY{B܉jAq\ PV9^5Rqԛ 9@-c^w`@[a3t' j].ѰQ\zP-e)qWh`EM'zNyAj]GVKs}=21k1;yd* n 7*_&Uk ݫc#s 1' 2)C68%\A^[+ R+eka"]:ge>r1[E lyZ ]yȯp+aPs ?=_(Fs_ &F *in/# 0X. h7K *䉼љdAyFu})))>ޢn5H] 0ohXԷ(($FCm/( . f % uHS&܆ʹlAI}{[%K| ?/~1,/߃vKp5ө{jpGo]LT`6܎?uGtuw)T(e,y]ճujF}۴Utp j7Lz:Shv"ߡp @\&(3]mW,*7 Yv^{8\zT8=/jdW9Re:ANim3*pMYjy},Ll13`KUr69U\.}5)MU~ga3Tg7=B#`]RznW&61Ftecu5z|b#B)"}kZ2zԔj_'P <Ly#`"7ӏTSJ- W z |C*u/OPe4أ..pIGv{MU,1~;xEyM5&5Akn (spQzLc XQ~ n@WhQ$A} k>_EoAH_' :ª/sCݩzE5-az=jF"h/nR28/ (v2oމo*'-Xa7sQ9AѪ-L;7\_GhMi!N;;PbW[>pC įs.=MT"{A'lD~ F!xU~) zDu6 wSA6pܺ&2!mrWE}$ Vl>tM={K]bpL@ }x+phʢp@iW}S}_D HoRr͢lpu=PFa p1dl8J8=أd\€Ĩ Y԰q*s^"XTwSZfkۡh*0cQN1m޹DnR}Ɠn3- Hi G'w+7\J-7qvX,;.;eX3ןF!I>@&QF3mhiG}6K&KߠSyއ+hѶy,v!bF]@ϠI=>z;.KQ25)>çx߳Mg p N[u4|E U'G1U%Tp7ʚq-cMݩK8 T~Q@5{2=1<2ST/v*' cS\]c6#hAf㸕:Gq _ 8z1 x YR^@)<6JaW0vWb\kkt-*ǘL>V3x:mjų|ΈzvOP}cAn\.m7$TC0ӿ<28aC jF.[d$W^MJ礱`VZ FًlI6=E, m4K|e7? s&>jըÈN,'U>>_27?@PE"H @qL:، \A J Q\}?Рj5˧:8z e{[?4kBOJUsk?sl|6 ej|uBC/ݴy#ݤC-۠$إ;g8{wn.dnPzi`s`4ˍ9XX mU+&/p6iD^w~ -)*%K} NߧʤΣ( n%lcOt?H&ќ` 39W6k76K.^c>vLI9s1E"/yZ\;{71,/J`n!dXx~ KvA1i.=`_oM?zEoQo~RI l`,uA%w/ڱ`Q-0TeP=3\䪟!@ S-!@U\TD ؖ$nG(d=_L n]<tWُfee}:s(PeuKI4y"mל\lOƈ13[GF 851Zߙ~۹~ex_+?dX)޺Kr ףոX܍8{UY^E\؋H 8W"4RMXƺjqQj/|'oRs6NڛzGcc+&5KWR\ "Q7 .tjsplR?ENɤL)*6}yrWv?wX\'GW٦2/'\K&SF1,1sb144  UtrpHa^y6K^3\e%*K si xJvrÿ:knH(b5z ';}+p,΅B f o~r}7wO7<奞b\:pb֠i ݼpJUKP3}Q#[vxY"hz_~F#w7խbF}GwoG͓g򓯞?|pT6 'm_E=P5ߗ \gnE}o pw4[owPIiM۬}8NpO;hLF -Z6W'G\\ LuPM'w+[q*|zwB@?C"G NOp`V@עP0o:UlGn>mĽPx`zp(9IAoJ~PI{M\غ*+[x˫貁 4/K_בrS" >FČQs({+c ^KO[>,C\^MgQ/;CUطzVeSXp9P_85e\ +UGJ%o w|z0?QM#4&4+ĿW,u" F\++\H'Upy9g v?>dvn@˹[Z3W \PYR/>祣G DO?hn]})B6⚶-ms pfT9)6[/>3S_ݹ8 DMsrYGy>ɴ$"m}ݻ po ˂@Ve`|n*1>x"H!EA=],EA۵\-bf.̇nusomt2SHjuƪϚ|} `Bey=\@mI r,c=r BWѣ'LDA%j iӚs鴿E`.++j?|T_V,l[i@z?,.D4E~`o(wJIC_b#d2\ FN'r yu0 e.cyUsmAnX ȊM2F 2~AJ^JS(UU_OQgkcT׼jw[Xao\(ןELfX\ 00GJjYe_JF+(yiƍaO]ߛX4 {u ҰB D<&p[BGtέbmR_V(0}l\3"P7 R"Ym*j'L4=;3݋3^)\+'+Jip\.אq8|"6a p1 /ka[f _ƌKp7}#1|13W$\ٌW_6(i4y  jTYDҦáн+\ecC t XP.qs\fn@cTP|a4\Ĺ2wxPʯ9s1PEӼ\ՙ9%e"Uos"e\%Z\M\Ĭ~y^u {TK Zʕ\ֵjůԮe pDp*yƅ**[WOK+O /6[;*50w Hg ;Z^}Ť6uY2jbʇXπNcx1L!%]ͺ\9OХU!Ba {eޣ ! hhhi/h'՞\S/F%3m$W}-SׂCQͥ& UW$P:¸PyIy]d?/syg!ۄTE̾>8 JmPˎ"k06.Z>k4jk@ ^۴mwskPET)|wYOr?ͮfvv51P!L m+ n@RuE.wYwOvn ]A̳ePV@;>\cj0W cFCZ_5xT lVN E4{ ಆꖱpG9JR:qAz-;R_ĞpXB9h3`?QmCZ,Z( TN{m\v!}'\?a WU^>mK)UsPsm~(]|+H6 0?@q\رW @G{A_虈ut0xL8ޫK]͜<ȉrL;7$:V`Sk`*k \NDXpO.۸t_y|3gQBo Bz8u؏CnjABVFdH+3p#fʈGu`+0*Qu\=0>+L#㼂0s|g_0LM4*4~ ȀtTtК_ >u@]/f J>$&DODfc՝s_#DH?xL],yyR+* ZmX ࢶ[#-3*v"wr>EPۮ2/[\'\Uj׬)f9oɄ]\2ޯ+ۇPN\u ֊|6[2|U[t1\Id?WQ,߻Gk x&uQ+2/7 BkNN2@m#HY6Wi4?qoO˹0'( *&UK^}F?.R:* ꌁ.l\-zPp\\J l6-ANak9 L\<߈YY M06[ p"<~tvwWm8n&>QL\D-B<`F==Egԇi>o1P !GM)DNٜ?|3 ik?^^ { (`)CD-!prC& S&q.[xr@)T? :pe>KX땸UVt:ոlAxO :8 l؄|XQW]4IR\oվ64{+IR>]4UK@jJ]z-&58WTnjw 4F;q/] H k+1~<*(abʙ 2L v2j7|7^6?zcLI䂣Q(_⡻̨޷)wK9n~ ^4Spg Ѿ >{ iXUKCyܧ㘸5iFN,ƽkߩ#Q(2ɋ0:~f vo" +h[תaioP6f-[ƀn P7nyRb()x3;e-/5]%]@*n-V㩧]mV(ܶc3T7' ,y"K<}(ǎZV-""X濟WLqxk㿜Cf(thG1`[GXo('*lc6q'0ZAIO1?z@&>'/m: ȧG)'VGy|dٮQb:'|[)gzF@XWTߖ=w%TZ.ְ%8Cc#3ce\-U~ Z-tO? ncXAuǜ)wq1-(mP4*`+*{Ӟ# `ڮ>zkyKUyͭ\tv?cd>1"@:]uUN㤷0}Sת*x"ja\@P6ƄCs-Ao}^5,qKǘ0c`|6࡮\/ZI.x 'QRAOuPY*\\HNyQ1w$Qي Oe@ s n$49p D{p[g?SWR'ww3_cn4Oϗ70V.TKXû%*EUVrֽ瑅[f+zκ0H7 t\c?!?&Lk0j{+k4>^g?fd|֬jWдWAʬ6~B:\zs,zU/8g p181>$Y.Tʯ75@ ձpy F} (F zKnj:0ߘQgp\p<(㑿oX5y@Ef;[u DEl\b"e G 3wnܕvVƘ)mhi1 L^g4 3߫6{#Y\* .3*Ӻ:W~ٶ_^7}(17;qSEwru6 u7˓n//X8fMpNnجt11Tp;80}9-2y$aV>er Oh1dQAB۩kMʠE)61YhCp}o#cTWxxKSv2 !C_]$a(F[-1OTY$ v̾\,@\"XJX|ٲ+RdI>; R8X;T NY-TQ+]b#Ϭۼ}/(+j.@[ŵ2` p bꜵ}k0bpy\zo!iDkQN@} ոHhc Tm&O~*U#}=}_v1 `g<]9JTQ 97º5uŰ@t]E d1GTEA@0T@ I@, f?l Ȝ< nNzDyFεij OeqxɹY3.B&@ΉܟDKP ॶ]W_^lZ KγY8;6>p~ 6UFƙ p5ĺ[T_gcyo H[a׬i0Sһp PqH4HDF@rp _Be]i`x'e8x xgś/֓2|' _@kD :|% 8؜G27y1v#Y-Ic38P:~>2 ]L;-^Aqo/8Q3%ތ6:Elw\_k{0(xK{0.!RKT2tM2蝣2Ow{ƩN{! zwѝȣJWpms>#[pe;I>L_#6SUc/ڃi er(׊ml1qA+@i.,p]:ev̧`6#sMB\nuc8W;Fw'R_'ap:awnw׍8?I&#p'uuyn3,`d ,rI:F= 2X2.7Kk(_'N8Ѳ4M{1WtIQE,nPWx3NyO ʼn 1.WuxL:9N ͻ鷵yjm тc}4:1f I83puAxdھs/_)o9V"iB'f:2ӊNkhܢȆ"%fx'yc^a%߷?:Xj CTӢÐ'tSGi_su{INnd:9mtR1-P2^~Oel({ I??~V{48WxH[:IuNenmyERQqyr44ͭJϊ8F"ܯ\r~7e pYFMo#ċ!6,1wvE._ n>oh)X`-˃@}kw~T} 1&pt| y[٨j:MNX`Kq/9޲*B _0@,Dg ~(2QhSoH@4M(`nt؄^q)`wA9rve5ppWyFx[?C5 0gcPkS Mz: 6W1,qdV0q܎U)`}?Fe8-x{iYP'Ml@6"5^op_ϕ`g+ɕD͉r@I]>V'l9?wcEơ ixЬq{Ȝ5֝kDS_ϻ59ZG# d}he#_&.x#l[ծ{pΰS$:Mo:ۡ\s źUJ3UשZ~"?d1Q(+j{yzAv:Bx-1Kq"O1i1[ԏ}?H z2S0^ܮDhmX-\̬w/^ax@DV׺ҟ(?MxjZ׸c؝ml1PaeÜ?S͏qb eZ.P6vNGesSsCR7FNfp3NC7KRc.9Hp,1N"Q,.snF~))X Бq k(U7ܨr 2K :8o"+ԙXќeC>2jWupKyN>sBa~WXIna.yƅD놺@úcѕF[gdN /P$ft֛YDСN[ T2d۴l(}5ע,<‽?;ro6;,qfݸ_}^Pva^Kbwiq̗>''oq峱cB/%jPdfrxYtp>y8CXmhO{;1+ݥ 'MpKpc|8 d4~{ x[`%Bn s%LmM+EvqqLD78tz{|9Bnc^|c*Y<2F%1ON&ϵ@[m<Ƒxxh *NIT󧖱ٜ p B{0_w&1 @)7FQmG w+px1o+7{W`nbWGߴCmp|%04,|3F ]v\Q\$5(B.=Hr2O!܁K.qY"\|8Ru_yݝ΍l-f'[\2L-Fi˽Vm-%JGH[Dke{i|P[2UarE^+lY.dqCSs3JGڀ[X ~:pù]\á+q N@ŢD(G|]U# 9WiOf>YgQpSa<&fdQޏ&.sx9퀸Eqp}8l#Wl 0e޸*L<'2` .mLpX&:4y;e^ȉ(U\<#7&k~C8K϶ש{;vFIlC|Aϸ/" pR+~X>\1m#WmÉ"^X3\^8~#Ki#U!.'O@'S\nl)Dܸr3) 9D  7r{dMs= oF@L>eZ^'ڶJ8^*'r ӝyg;eqxO"5NOfXP-'o,/=%KuWfIDՋu8K8f8W;m^e-bmdЁg.N2q HQ:/ja28A48"lǴcwUO/yy%|pKG^ܨtUp& lQP?;!yXbwicf;Gƽut ]+q_.66/>`w  pw.wM[ \pˇ]ַܸ] [rrToB]+)8u2'tV5K_2a^@mVRO `vx3Y1OژyLd\ ˱c>:ᘦc/: D&KN}дWpp\{{p{3,ocD 331\r ?.; xLP ā2QGz uґٲ._@#82rV;-0|R,D/m}egvbp*lttHu[ -! 6f%Wx\`Ίxs\r89Do@UtzspE;{|MnC΃ Gj<iȁ|pq=õT x)eKOӁ7 W`qtcyf!w%58Esa\hXoE W$Q\9Fd&.(2Lqd^hoŽ9\(+ܥu x#((#9X/ +m- &h78M]Oqr<jutr/ i#*uQΎٻڎ hOvfr '2web=p˶}@_b緬\p/h{#`G D* YAߜTkY~|iQ; ^OČF2Q չS/T7!XD<5N:XgGgbʜǴ7fnF >N J Rp"/` ZL=} 垓iwU3:V8qr5'  #(C ByH}-„a<J'ЍB{iԃl/+"N@7I~0M*Mne80i^Wo< `imA0s Cv/ RhWͩ_H;ẍq82h+e7^o&u!#yyͩPQZ `I_\q|Y>aCof.ơ űsOǷ>8OR?\Vy3yc|Qzц%Z:1+.k|$}ݠoVJvxy,bSP0 BhKV$aE#cN#L0=Zɩ.8!x[eYr]5тЩ\M ?*aBŕ;-<@slYVT7i og!qCpt˽epSiXFX<nLnf)JDKn43C)MnSXb^ԁeQ)wp"U p9Op xPǤV@>E !cDX>wyr4f<89@Z;t"CqjyV b̋rx8~pp| peqF:8YSK-J5 WP#"to\]I<8}eYk6s!^@q{3Hթ[*i,t}H8_},˪ PEQhr&(n9pCM0NXnLqko7Ѽ6jtr˼1qJ9ͮ[\2u1 p p-˲,25]eYs(&6u[\2u1 p p-˲,ZnFI~AY߿Rzu ps]ޠmCnu\`+.+d9\ڛOJOlqⲪt#2,˲,k%k7d6`~ꩧ~z5UUZn=su*iכ[&mm]JUvtuG>]ݺ*˝oO﾿Ccw^|nZ 92}wYut)iƍnժ@\ʲif6F'Hox*+u?|~ivw\v2AңGlXeYe~|~ԩS'9oy睇կ_UP^Rʋl]jRVZn۷Z}޺jZ[z"K-MmZZ[n>_ܢrvucB믟.ԫWٝvEͻK,lm-wvKYvmv`խ65֪Ye5l{uԨQӧOҫW^͆ekYeYUW]\tE%\ԭ[7n5jRjdNVxLUs)uQUV-jBm"p njwAB]^a5h 9Ù^;ǖ[n ^1^{ f#8"\ְ6ǧq.e]3nst̫meUy5jTO<ѣG'3f͆ekYeYy睗(6!?4j(92Of}uqYev s=2jǥ%{gc3w[pd_gjժhѢn:sL9rdrA|4C=Tn׌^_ݸ6|c뮛6l!C4fW_}5b-2ٚXmau]}s)v1l²d;餓3w}w"KƏ ײ,˲߯뮻.˒k&9묳#<2qqYeOq:s{r!${W"z꩐7}t%KZK.\xk'mQӦӹ͛'_}AAٳ{챃*WV/{c{nHp&:xMZ =\V1eUp>|'D;vl2k,lXeYeYe^yOz=\R:0@WeVy o@[\?xUVao]v$tRD™ G'.`cƌq.eYeYײ,˲,˲* (68p,i׮enRjժAN:a7o^CV-UL6\oUhM74Dr9yÇ`],˲,˲eekYeYeYTD!Q. V9e,I&)66dԢgN5? wҤI^eYeYײ,˲,˲*Ȝ9rdҧOSg믿u-eikmONϟ:#\r\4-/0nݺ%~aN^˲,˲,xZeYeYV?cr&]t֭_I!:\sWԲe{Gyoƿk׮(6 Q @w}׮[˲,˲ײ,˲,˲*Oq kHp:3[؃6l|*Ur wvHv4ܹs -7eYeYVdkYeYeYLSLI~!-d=i+RfʹRJ˸s5j/dʼM=7\2˲,˲,2,˲,˲ CJbǏ KVVԩ6o޼=Q L2ndĉɜ9seYeYeYeYeUM6-iݺu2v\ٳo^bp [K/ɴ~dNzKڷoopkYeYdkYeYeYD,8d{׏* ǤN8[nud֬Y!vĈɼyeYeY+AeYeYU4z;.;ӓ+zW:1St {gp>ɱn۲,˲,Z92,˲,˲ ̙3CeO?t#wuRXjԨvر.|I޽ _}ݲ,˲,k%ײ,˲,˲p͟??[uQ ǐ38#c<,hGڵIԲ,˲,\˲,*K=I=B.#wI=p.G ɴi2|ᇡ@{'?wyɝwޙp sMҡCD%,~헜~Wzꫯ^j5 } cY\]tEl^{z뭷x7^;,>CSunnݺviw 9M/kժն?sWԶWVIeYeY*eYU!E|joӝvicǵ_Wg}dԨQ 2$ -lĈ;'"o_}ՂM6Ʉ 4q2]9hẼxW^ M:5C${83XfCD 3eYeYeYU Uyoe5)O"Z"ؙ ><+$l#S强駟/J.zUER~Vqٲe4@v{B'q|*&8;c9Bθ\/S Tg4O.ڂ­zS9z+hښ 4 h>Er57xcȑ238;\P2꺸u^$@@9 r)cAչʴ=ȿm֬Y"0\_euO"/'\kZ2,˲,ײ,˪p"&`!^)38cm*@嚍6(;ȭm{: #6UGd d {jUA9]o- tqʬt&rk.]c"#NAU ~#Pp+ ^tE_vy/`n{3>LSOM<TVI{jwXM5훵3XN-'tҵ +̙3Z-2V p-˲,*/2,˲*@zdԨQS8cL䬳J.gI䲍UL0w{XUn޼9TR<Бdロ;x<7nwk3-eM6 >Sr0}z ߸p믿=#j5}вe˂^|Y0O>dhѣGa׭%i߾}aekZ[eYeZeYR/R'8gAt˵SNɽޛ( @O@)ЫL[+9Q$ɲ駟̛7/2dHt"Fp '_~3<(^m۶mӧqcdž3}cL㡇 ܼԱcǎay;`OWe[jr pG\Ǵߟ(Z"lړsUjΜ9![ aO?zռu@<{ao.hm\i &LPV,?>8=eYeYEeYUu5 [gk֭($x,DU `*5C0)c8EFEh>lذpӹ0)O8ylEJDI0>?\(?jx&M 8룋Z&: DsD|^Lm^$^y0}l|O$7mHtơLaYpퟶe}"Z :ۓCރ oP|tX^{[js?W9hKK. P1NR-tPxm 8(S;\f!lvxX2h֩rws<߿2pVoI`Uײ,˲"\˲,*u@*B!\-0T#?̞{QDFQFp)<]w;sH&).V28u .U.cg=P"p $(Couhes7,1)S Nhp=HR'iӦƸ|1eOuaچ_'E _~gYEDNp_|E+x2xɾ̚5e&p 0QsyZxG=@P*w{/|(pKvh'ed'XDIiSQ^j֬ 8JنYQD{|;:rd=s <`;bmJ#4va9eoP}اkR?mjR!HYײ,˲"\˲,˲S.;ޯۓuN;[4h:hf|tdp,J~_G7;t`|ô~E|M=&X4\sMAj# '8Xve9[0c8~ AUMS)F9e]Ep7@ּփ+pAg4>so˺FN!/篓"XN:)p裏B[nh۝jWYwcH[]wiMՋw x\LrۆmGelƉn˵\W}]5'6! QG{饗_*{:ۭ.w 9]_zxNؙ{(\o({dײxl gGApUT)7 yIu:ձ!/.l{C8ZeYeZeYUJpBQOX J& %馛X= 0Un^?Y uYgATQC~&hD %c]rNj {K'%C Ե W9qSժUSWuqU}}E[" pqjZ!:+R)y fQ; .> +oDhݏem-S< @[ Zԟ'O8nKA\?WUn 6Oa8T?) Wَ]`L >miJO[Gƅp{;*7Z~mQZ2VV-Í7yS:_kkx'QaEMq\eݱeYeYEeYeR\S]ӫ{.wɱy~ .JF.t%>^Tl(Y'0pK@ksR~]í" Gg$3x㍁=TrvEӚrʆi0-9Z(/7Ž+Džҫ"\DY GfIb 8Gr5c/'٘ʪ_wy*$u!s  b'^=@v)WNb!c A.183@5 &RL Z΋mx4딸$3XnJ3E^l_m[ W]Y9^ 3fʻyg̗~--^8tXZeYU^dkYeYVq*30m`PT 8[] <ܴg,8,XGAڅ,\uuQ".@@a|3?0De@GM+A+ JrHTTBzd@c(M/!ͫߊ@$t)4.9ٺ  : pK1Χ8if,O6mpP0Oi!Z^O" %:\cA@c2\T0?:ti,t>-.8 UeLl I}$fDlLC)p's'vm[<~оtV2,˲,ײ,˲RL~ [#!_Е{Y7"{p悪QFJwp jcRd "h^TG ?uVN1V 'FG1>OѰ;W8 K"ܴt(F%TU;B29P3"or2G* fn2@⸮e]v:v:ɓ' BS}NQuW[n!#]1e3wzHz!hZ':Q C7gW&gʭ}"DGBF3vDl[O!@Oc=z$tW2,˲,ײ,˲2.П(r 6 ,dQ=Qn|s XT]~J\\ …KA%Q ]z(yUN;iS;<]J- Gˇ#XMGd9]tx )h"KU clC>v@ c}MP, Up.kH;vL8.("">f:\_ ~X =.o-pGv@^qar>p .q* \-w OmbMfUni]aO|Cn Bv-)h+H mħ@^8,C֯@=^G<"p388K6Ծzu}rm@;1* G6 f] ml|m뀷l+lq*Kybό:$6:$׃&la``PÇ?K=.h+H'E\`p1' g=Nb%ν<P25Wvg$kK='Jff ml>}o)&'D/0K2d3HCv  ?[e pi~'qfpiиɡuR[m+Ěh;L 9 V^miuǫRW:=QVײ,˲"\˲,˲S7x#QlΤyeWprvoߞٓ{9YjܪB Uo nTn`9By;A".Xrl尼0J]=+*Ch5wA8OL8Q ʷ2L+ue\ܭY9sfc3rHkFam@=uWuІX@1g-fnQJ.tJu0vÀ#2hw!ۗDLh[PܯɒX^patdGƲ @SLfE0\(@1ӏ֪38cJe>ݒNd T/dg]\CbaM/wr7NaEb DQ̶8bĈK ZBgxReYeZeYU (,`4ဢH* ٻw0>W3>ܷW]v pJ8^V ԩSM;w6+::K_9/sG^H%R"}tZ Q@Oj$:nƚw.Y:\Nu_p"\rƋՙܶW_}`qWF+d 耊 }3'ƙK_qѭ:'F# rm2&<Ġ>`jܷjÇ1ox`ao]N_zcJ󰄇Eqx˴ܥ}%¶GCeYeYEeYe pƜUr]P1 ëӂWqI>%v%XQ "A w;kbgKrjbWt-RX% 0^m .!,H-X>EA L1[j f,^qE{"e! ( F,XecΣV0mAK}#}309j]~\:u+J,Y Cv`g ʾ +)s2-[}<ۊ=:ѫ W̍lk~N m]NӃ3vyuGA˾F!EJLM[=|Vֿ͛2ut0,˲,ײ,˲T* ,rzhK/DͧNpv5dobmFZyu%@cr?6kMzřd"RDt!;6\pq \ܵDL^"@^ܷnK9P:: -yYW^|<1ȅ!P$+Eh3K@~Zǐ5d;Ro1UUU$"bQ:),zlN+>￿I3AAClu릳f: kk2ű W{mcYfM@mTc?T݊f[cy#\qTq=i>o{TUDP'=|I1܎HUǸjTYs4BG/1Ӹz0,˲,ײ,˲T*3@C&::ZnZX JZʭ|8ˉ{{@,\$R / 2 8ת`UF0.^]I6gT.֝ɑ)t:ߌk<,Ǭ\=ݸ? 8%9qչ"ȅH`- \&XɢB`N- WĎW)2_Ik=9'eϭVmdf-\W[yv;&#'8uA}rY\E>*>(l; Rk1Y\a pqX-qt<P@C b-zq!rOē&MJ}ݰNW3HkY>[0ū`AvYײ,˲"\˲,˲S|^3gNH z|qEp^SLz:dcY @,S*ӧOpE]x\wW ǧܵ s6gsbkLt4mjgI}[\À3:gA 8]\`Wz]0-xP'ly|bG%3V9py}E ,a WqBn[kUrhSReOΝ~+8!>Yu.DvWD;fm [|U@ǩ=-ZW)*&X*x;N^Ic93Mpߏ닿OE-$WyQ_yР\mbߒC|*1d=>P]W|õ_mEGyԁx @nIDL̃W-5ɚEɩ-Ke)\˲,˲ʋ p-˲,*N(={vdxU[ܢE%#ŀtnpAYL0^tC2-`RGiŵ'wuvȌEtFt$aĉr~ D+F+dAeG#mAuU!+qơ~,I^pa@<"PWlSEb/ρ8\ae@bWLirFn3,p1pY(d; :q rǸb[˕7.yPA, 9W1_Z( #9@CѣG/nj5)m ~qT.ؿ|="E #e)\˲,˲ʋ p-˲,*N\`?>@;^kX FN u125L Z'bB<W܃3'YD! NF$_Jx *ᬤ>8T ЈW&Pȑ#:4$t] Gp]-`B,]t %)ԛv+̷jժ1?:!c@A:bz$܀:cZQFva"m:ar<0\ayv.ɴ',mTmiE#f͚HK~,òQp2}w/m/_cڀ S/uٰm0!{mڵ~2,l?m+N<0AQm.l<Pvئe)\˲,˲ʋ p-˲,*Nh̙KU(I45sU譾0{\Cy\*72pFbL^E2lkӒ ԫۡTTTBhQ2]^n ˼ːMB7k%* V 9rQOh씊!c | .Xd:)MeYh dd2ֱNhlԛ .@LkED9[i}.1 2NQ^[z@Ȟqb8+A=7Z,dkYeYVyeYeYũZV*^ ZguJR4N*@g:ǎƵekYeYVyeYeYw%HʉMZ (v.l׸2V p-˲,*/2,˲,8Du-d-H:!zuvIJ.Q\ЭiTօo+EŜ3+ gP0aQA@@QD0EA EAĜWt 3Ìq^VtPU=S=uZ2,˲,kY :O282I0`W>eYe_X쌜,NBL8`,:UҮ]_}UpOekYeYֲ_~9a BiMTkeYe_,("X, v[bN=OpYjEekYeYֲ ҦOҨQZ9;c*Uw;ZeYVn(\l۶m=-OZlYBn׮]~%< OҖeYeYK[#FHL:u yzjOZeYVTdk-mZeY45s̰;34i$R,N?t[eY ,\kiJ]w]\qh޼ydAboxeYek۽{k׮"kI-PTT {޽o[Ow\~->g"5N<(X~WxD-˲,kV.=a-i暼pr-kIcMԩckYeYVt`;v U %-[ܩm۶Os=\{jժVV-DuQ<ޓFAyqq 쯿"eUI@lT͞=;D:ֲ,˲V`J+5p^pqYe%>t~qY"eW_q8B s=-YoeYec'ԩS 8PQkK-Ypݲp٥^*,}g~L*}xr"feYeb-78#l馽W*T,VYe_0a]pDe3<>\pVM²,˲,rp۷ow}&Ao"myvi6}(=Bx`(ܷ^QxɁK.QeYeoM( ;ZW%\V[pY*ڵk'_})IXeYeɰaB*S^~W["mV֨Q#UJw{,('nF`A_KxI۬ !;oB7=ztU}gwͅ|oJp۾T eYe\rr$z lvɪN,\jg՞ovXeY5m4r'ON9D%?|\|l Eݞ۶Ƃe1eBMv[h&wܹղ,˲,˲,˲,˲,*Howx;eʔ'Htr"`?lIam.ي7HBڄM6$ZeYeYeYeYeY@7xWE&cǎ iYݵqۼrI DoI@zE-˲,˲,˲,˲,˲J["s)mn\jm _׮]+ݡeYeYeYeYeYe%gI{@Z>w+:wJR.}^Y#F~aӧW[˲,˲,˲,˲,˲2B-H|r*6[_6\"v1rkYeYeYeYeYeaP6*6|ٳgglCmeYeYeYeYeYbx jJ!M~| Un޲,˲,˲,˲,˲,Znɉ %2װֲ,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲g}6i۶m2hР/O*Vd2weYeYeYTtwOdƌȑ#O>cYe/M>= G\ve6l'..............KNn-y7BeYU4iҤo߾W_}\yoJ˲,˲,˲,kآ:袋BСC>cYe/}r}W{?_pqqqqqqqqqqqqqYe 7|GaÆY޽{{ oYe/G(M;xB jp /^zI3]v@޲,*_bm'ԩSWWܝ..............Kl&;͛7o'ݺuKeYU']tIڵk4h ,˲,˲,˲eA 2 !feYUn4zI&xKx˲,˲,˲,ZqM&'ONE,˲f͚%=Xry%neYeYeYLRJI˖-Yf%cƌqXeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYZ/r2nܸ[nJ=w}7駟3f$suGYeYeYeYeYe-I7y璗^z)ԩSҨQPt钌5*yz+6mZi_,˲,˲,˲,˲,˲&N/O<1y[o5ے'|2mٲe2ts{ȑ#݁eYeYeYeYeYKD4(ڵk2}m7n\YfGW^2~o{@n&M?0Y,˲,˲,˲,˲,˲XÇ@ SL٣N:V^==餓5jjJw#<\9pmРA"}^x!ٳ;Ҳ,˲,˲,˲,˲,%mz*UGqGyd^;찔rqǥ_~yںu&]; Ѹ뮻.,~fYeYeYeYeYeF!ѣ>sC=4R>ʋ aÆsyjwqG'$}I~䣏>rZeY+>S7ͤ]v 7n|bͤIgy&رcr}%۟ɱuY{쑬,˲,*d2m&zgM>UVɎ;X.dKsN`J{cˏ ^7~8UBRJ{{nzJ^}GMbd뭷^]vYrꩧ&\sM.$q,˲,˲,o(Z4tM 3cGWZ5? \֭iǍwG} %WҿӘ0o%XeY m&L;_~~nkku+2L9܈O?ʔWnɾ"V?kM4S"c`rWʕ+'ҕgyG=DV^=̓O>9{.Y z_P t㑊Awrn"q2` 0e˖sE0ӏx:K=\%0v[/Oz=%R~nONr+ltƋXKBz؛9s&>BoM7>..Kiލv]Q_Ubgr.Smnc< , a*i!d~iKym|\Ƅk31aw=?&//R}ko*p'o&@̰?^̲,ZETR$di 35QOf2gI9@} wS7G9(cXTy"\w"O@尼(*x*SLcI*:Y1_@Xe:KENSTZP7J5yxhE[I@xW}ʑJΧK!BT z WšZo5.ܝo, pP>{ae-NO߾}cii.KNC%6d_X,DFiWxa͛xG w}GBZ8.B[+9l}饗B:Θ1{žZ%,dF\`ĉCt. V%^%eY\jذa erW?Փb|+긝,<v"̀R"iBR;+!{)X+`YN`_92<k ~iF-N38~ D ~ʙIOJM?J_U|3[ @"*V%]CϞ=ClɐAQµ pe]L3uYV.y,kqJ&7uY&.>P˱Xu}hjΰxX ,Ao| 4ѷ5-h"aUnWnó9;? ~V"ɓR4hJ )xWWt6D&@%ͿV*%nebk&o[htڕTom1E%ijuUV EEZ..Uƌt b6'!g.ܸhR|Ai>9>C(sf/4hsΩƃ 汨"wk/8eY\L>c;E/kA8ص5ߓVG3EONłuի ֊8bb35mh>) V7f_9rd~ྲྀKYg+X -F Jԭ]Ѥd>7jw XݤaRA؋"?sbTRwq*r.~bJu+U:n'O=T p-\\*R}Yu>ёD$M72 ږ`L< Nu{m/nbؓe;XnGG!h4 yyyǵ,˲?' {"S*/mn)ߤ|mdo"\3$:j䉧 u6Ahu# d/ϔ'tR%Z# )rן(ݐP~^}RE۩S'|ާ<9C@^{+"@Ev`_|6mzQ6m<b0Oa5qTOWoXXkkຸZ..VQ>ӉQ0 3W^n[Xx.cSZnג6\-z^Ø΃kYe-T3UnhV͓O>l t*]W>gU!l2晬*Z_3^hgMEWS;gJ+T R[X9_v_uJ+HjL؏A/9Bwo"y]m^3v6 p]\ p-\\X1v`fzN@RU*DnʂÍNx n\{OƉ!WifP"py,˲ptLI8ʝ!olǃAM~MJt3U"`UnTC rjsɕKݎ;z*<#!ʕƍ OGn߾}zZ˺{`lf ~ҸiLaOnqdgM p=+<mU-ZeYr rF%BQ +7~$x8 DُHWoÀ@Z OV"`q|!R-h15;lذdȐ!@ %O-˾D 0&\i \QgqF8/:Sԁ4E8"̙3> @ds{wHdƌq';31"0aBpfܤO4)V,p G|eL816mZ;8|Imd%vU \fJ}PϏ?845+V@04}N{7] S j{pɓ'/N }CR?\ܒ7 ja+c︮'a{ qs]b?jԨ`ϸn BIl0c۰Eg?± ;C'l-L^YkY ;YNl8}I;VYgLџEݟ p] p1/sؐ6"9k5~ٗn@"{M*ߤ-g3M][wl RPyToZО-6۰ 㐂H>fd 9[DP.PeYU8%8^9"Ќ@pJd+)%2LNgFь C(s0pg;Nѹpq~27QtHs˱(8S`bA>.>|F]q)8?< ~sNgs 'R>g{Ҋ`A?F4 UfiOh+Xbxo'/Xo#\ b(13GzΏsUpC{?v%}fR.{ )3A]PQܟ7b 05-` v㰽Eȁ-nda_hr\J>Q1vFl2l#+lq|E}nΜ9)/R_WFM6]{cd o6}5_=p-aO,=e[@W5D:|7g?/6eG=>6]Z{ڹ4E`E\WkqgJ:Qx{lkkYeY]xS +#R* ;BTTRc?D#^ sDڒ?XsmRY("&pQe#,V>noˀ19@Hg)Kԇkl cF PL_b>d ʆ6f+U_uc-meu@w6g*[v3wf@ܟ?霏&/T~Uئ~ΔömW/pf?W8K~S~[./{,h:mذa\=6\'Tmöp<=،5U xO-Aݹ`, rNP pMޒcfl@[^qӟJߚte'=aB 3zgJ'~+cu1uOߚtvvsU"cD=VPg=Sޘ#/ v+>=ע2^/WD&RF`[m k#68`r.Sm'3+NE?[( ^,uxlZڢρ:uN9)Zn1܏ p p-˲,Z-^Ar6+kh"'dHF,YN)g7:qΠZNrt{iWPG9ȩ޿"P:X]mv`Od@8,[SQˑ H?V6Z11@ Җ(4ܽ{gS^r]%;Imi>\ȝ~j"U^2Pt&_Ȭ$(7oR?^T>@\B=f?PFST~=N(AR4e }0s}Q?_{~'_Ox<?) 4~Ƙg{/pEמ퓾=x`Ci-o`ҡ=L8a{24zw_wi\l 6sa:Q _RDh`v;E" xC+r ƩQ7΃}Vz2LkwlSuًB َ.D_qOF( ˅5ýva䬧}76 (_;UMs?C)0Ƈ]iR,pyקWP#9 :76ʴ -t qtrxkz]SjvNצc>Q$}y|Ǵ5k\P%ܫq/ֻg/TJJ/ْ:ЎcL}EzGP)}헞uoەo0CŴv׿ϣ/?^~_] mq}fz<\~g{ %<^u<0"l:KmԦYi\i?gf !7fl>bvjZnq;mfo"s:Z0%}]LckkYeYRE'u ҫrӇ~8رcUө«o0gDjR:cpGQiKpxWT e,4G~E€C`5CT^PB)! N5rfZj~}W}8^,%Wm 4W֍9jWi`$gpW+GxWڨ@b7{VN و dpĀAUSA?/R-^Tpg~:}k齷ߖ}-77J[0٨Qڶmۤ{9DF@'-ܘvh~G[_DХsڢaV-};DڻWiF7_nMuawuNw.uS;3;ҙ~{ڮI>~ѥ;%";> -t76'nv JP@! Snj Flt f{ٌ5=or.E…shsR :^*Hr>t uhmN6~"b^0C̄\k.]Rw=/% : o{+d24ز6 (*P{= l[#'=Vh+m.'E'o]d*ԋEFN_o `u=XLJږ<_Zk5Dϲ<%t!>W"rgB gԌ';m :iAHɵFa[U +W1Ꟈuz{o+<)Q\l-H;'q`-}g!eYe-aˠ(38r^A}@!9Ý| Ws=7L9).,QC U~šh: "c0_ڲs>_Xy7PH6jOؖ~y*=8ݼ>J#x,k+8f:*aA:+]ڨIà `:˱~Wopcv9m@":WkM_u?YOA9؆A GG*nqed?>,m#F3Wz 7@\;'~]Ѯ:i٫:|+鳏t 珥]G'fM:wZZ,[{qm-[@i{иi\.0gٔoe{5VXr/\\읶o彉Ŧ [rc#cu\[,r f*5/|g^Ӄ^2T|FWH=dDvQګtwCsl妲{aϰK<"h3gdÆ/}G}N}&}K[VJ fyD>-5.i}IWZiwe+4jU?tҗϦEJaQq[w %R2t%%>go[.VKQUY;88ovNN_9wpCn4 =.Tsb#T@\غx dar<[8yCh#,ʂ(So '^}vyDѸ!;lkkYeYRqq^5-\a]w]:Ai@~jcټ`()6hT*ȇ+}Tr e-ǒßa. Jn $G$ξr{1H\3%T@ hǑS#V$5r^I !}`sV:,VN9W^ p=NzO}P _f#t[Y- $Е 73Bn~;Ѐ"_=u\_p̯*!Q>^f)9Y0wӹS? C]K$C-[_N_p ޕp_? DǴJ/dmfsZޙvU{4b@ꔑX\ѷp6?uѷ2XD٦Ds!;MHz8a0k8ܖ%؁\+zK3rz06-T]|>#ꖇU@ܱDu.z Uo8W?-} 21yn* [Hae;Wz%LhPLuzZ[0u -tUBׇ|Eܐ<8C;=}B9u]$遇qr^:Dʲ@uϯU}lfW`wɇv%]mUӝw6a(U2q ?<\{k4\@m˱;!.~ÎY4"w6C6K|wk,g W+l[aqtov67IÅM?[ /'YASd\{'wqG(<餓fmV3pW p p-˲,Z.e`DB$(9$wfɰN;@vx:<EA5x1WrnFirh"5# }H>6 ~B`1F)#e}Bp!lpt rNCQ_U1QgojhڵùhS2qѲ(VUGઞ<-l{LM#$#Y sdߎHIkA]F0@\cYZ`mn[o{[VHٗz?621~iP+|! `őF:S;?P)kkN-.bԦ1OO*Ul D{.vR $` v4nbN- jrZe{ %)qQ!}OM>gS65(RPO1Uet}"cvM>||G왎׾o[:K+GvۮjW_,s7+UĂɇz$}gB[)ST^rB!ۘl*XXč>بlՑمR*e Ѩx&\ŵ5kmC,lWWh 0 _"Dž\Zjkwy9/|<Tۇdmf-j&TiD_E2Cu&͚5c㷦/ZlPDeYeY ej0=qr1xJ0D D(mвx ye0M)QD9/c{لp qYhO` X7oķ9ďT@Y 8,2Mԙ<]|"3vۘQ냳O΃saN-|>:Hd mE`pה疺nr⎕V(hmIV; n{UL_p^17\}" 61DawݷlS,ix#cu^d &N!S"7(?t.}G6'n+.ur. xe$x ~#9#Peǩ"F곚 (/pu Q,Υ:nIs"j(8 +q@N9s1BWjW/".ǼGl\g`a~4=<.3W f0 aR`fyl3}k!O,0t 1pLBHE87}c{wz7 Q`cFiA'ok1}v Q? eA6oZ(&g}PI.Q* *Z+r3`vzE(uآmfɾ}>ۛzRx̡qJ`# zbnI$pܫhҗ|8ݒTX 0Z?}qDҡ:K0 pOR*Vmz {VV)"ŶŔ%Q)%7v&ןHvyصOe(꯯b{yH(?"DX{1njY82-dmxL`kkYeYep;[Ѡ-䬵R[[bY_0e_sVHF$,`27kEq* 8 r-puB6W..Z#')N)e91݃p8@i3o,i 6Em -Fߔv?rT~Շ_/a[!*< pIUZŢ`O~G0Wk2 h@ ,`$qv}#7̝aK[žfgZ.u@>a~( !7e?YTa;}VU_E5r?r1 UvAMlU7A*BfyyEmoZ4mP=gq3TN1zqm].o(^ql={Qq+P;<\{о)@ޗϦ\uoH30vp-.]Ch{IR0-۶$*TH+TX9]wJmֻ[0Cy΁vu|EAp "fKx\_J _j~VJb(WGmkD=5.k_T絰zWT~!~eYeYS#icom :|!cO?"ZD LL, ~2&J'Q8+gw2ZYT,jَA:c@9$6gcdwbwq)ڦ Dݚ_[?'20X@҈zЯAο;8C[#xV>ֶܕ\+ƅb{r kT ٞ?İAj=SS iKT^X#=;m'1& %`)mhSi#{\~9~c[2Ii܋܅.׶ of@<TUp“56̎pNuB62BEe![ʽ)v&Pe.rg+\P-E _iyߵaqPk_wcCdnR0tO~ =*)5JO=b싫Dr\\";Gu~*cTفtm^@uݼ+py1]W})4?f1ؾlPSX\ذD[ⱍ5߮jժyϞzI'$eYe-#W~|9g $`cӬSt^p5 @Pe[¢ 8(M=pP:vgk;\>y%W  \<j%Ȝ/ѰRǝ rQˣ8Ws0b bhw,pE$[>ՠc{U@\~>㿞n uaDٳg%rp&VR {yKځ۶Qqѷ%->τvt ╿kso8W\q䒔jP<[t=J> L$)\: -c*;_ آl/{ELC1 ~19Rp0("?rX-,R6 Q!&vy=t%W-Գ$mLy"?묻VնsLV]mIe pVԹu[lL= P`u_Kqil$.pK=zc=k- Q2rHRzu8#2wR͋ݞZeYn)."ds8 =BPttabE} BVDj-:rhն۷oT"prO9wyR|z4]lN3Sux,FykWpr 9OjG'DDZp+(N8\ >h>Mi D8L\ڠw' 8X#"68 U)ca Jp_~à 4gΜ 7K[")Z;HغU:7ʏZ]n@Kz:DsX{DgBo4؅*)M1-=e,7R([D>6s] "e#26{Kaٓk7lC[dw_>z( vF]+Dy/L쓘c]6ܸH3[v}=yqi\nqxow}H䒎BeW7^v`eYT\|M2+W&#JnRCGY+ײ,˲e8k@:u?h5Z"**?pfyROy%gtDNr=?qaD8ٙ@ @drv`Ўy};r.h'̑U0>E`̍8:~,."jìmT ٨u4E=cd&G,bǞ;!Ycj|ѢCXp6v&UY P?sI' NQr9 W.d wE~YKt!EA{Tߗҷ/o.9~8f9q^J 躼[eok@Ber rirLcM} pWvlH6d'Sn{f-qA.) EtM-j|ʉY}E K+uWJd}5k0}'fCdC4n8iڴiҠAʊH1Hl}mݖh\!0Cײ,˲e~'3 _i8b10~*^6'] Z 5j%'<ˁ@ "yrB:. @uR6yqhPP*jT_9D[1plnČ˨ 쉼ፋӟYl~5x/v!gd 7 vΫ=601|7@@\@ c?'+''.)dSM!W[S?ʏ%%Cn*pK=/Fi"Φ(X<VyGO&yQݽ}{"frPPh#C<g7qr`ƵXP’s*ج%peV$]cꛡ݀Ubmf>Z\ݛM~)ǥx %m6]))n_r%-vvIhYwDR i´'iy0]s'ME^PgzCׇ2..()]4 m*Iu֨ԋ=-%ʳ˂qkQ}o"C\}/~ k[$'_y7 Vўp RM|p]0$Vyh1c@Mm>U,{R[h:ߜE>_:E -Xm=SڵkBjzNpZeY \Ypc|\\i]s9'|'t Ob 4ȮB "*ǒ>O۞} ۵kO ܇K"!6ʠ+ibz68Ҵ Ǟ=p  0 /79\ӗ8 Z%j> xp) D9g+ Jsv+}!!ki^ ^[(BTڵ +,.Q)h_ips ph vvHfiw[vڵ1{&)Ntm;r&.#y]xjݝqQ0]˷~鍽t1HvCل9{MNq<Rڷ`+la2%)"'ΊtD>lwi;fe/@T@ŝFa]LYDaG6#`-6cתШw>띞vnwsZ҆a;gGOePhg;a!"7Dfsr<ݷ}67"@f?.U^ 0CAlTlRco?` d/''6ÇYB7zj _K6y+ kIlvRv~ѰE{>UZ:SNԬY3+N>d`zM6]eYe-7+=ccY9q^|Y&Tr 5mtZ"& r\8H⹘A8)E\ N?RqS +szv\ XP`רlګp96αl0oƠ/&Vhv*4 zRT 'r9 0U/ P+= dbUW %u `L/9yl?,\{>2λeǼ|lq\Az{)">~mDS ]x7E2zT~[[/'"no_Lxsk!w%I%Qbs-ccb@uEX Ot-u٧sFa밿3+׭y"-{;#|i\e![9O8{#z`v{%}oQ* pA@W_)y'xlv*8*E@Иe/Cuz㘪.p#?Q P 5\-Ȯ>5u= ypj/ u7-_vy^Q-}6`rW=bKIϩY-}y|G j&Kt{_>vd+Oz# v!""g|{۶mR`u2 dn®qlf;,Iyr}EcOiUmƽ姟~Z, ~ *?N3ZbC3&Uo=m'8veYe-e]lu]&M3<Qϝw9l#ttKX q> d"0AgQ.1gq?,p 8J `$ND@N=K=t򮜭b34:S15}A1l?U-Ѕ-b ߣ`a`Aꄘ&A5v&m3UmLpALv 4> p' PT>a;/ x#J Z Zs{"~{lSmr ;[R9qѴyJS#3oˡ>X.(1=p6Gʲ3c:] vH5 s>-F^\B4Z`q\Z`rv P!Pӈz3|Nqm"m/<ƞbeQ-kir̶Bpt*wvEڵwӴ-燜%; e pN}"0Bf)Gzki;E"ޭ<|w #"(a@/}S\T6X;S}E' /76Xu`~̾IyER5 yv(3k {")Yk|6/M(h#$yD[b1_\b`]s5f][r5gnᆰHlD2~05,˲,k ܘ3i֠'9<ח\_N굂}a&d @)N&P+zp<9ӴmEO՗s~ܛψd88DU3RDhc1V\0'rUҵGc>?n&@RO`l'|8\9\U9Qv!l<`/T:!)g-90`6B= m K  p TF֟-5mmmT/4ʋSA\ cLjz $`Dt!}Y(0X<~@E'ܻ!}Ep\L(ɡ1_.9KE^ H28XLjZ8oe+9pK py_Pd.r-@tmNz#SZ]O`3bjAwyHFu`~  E~:UYt7fΊ怼:ϥ@h۹ߔ fO@ kbnI.u+؎E܂7`Vl3Ѯ%}-W:'|b+;]aÆѯ\x;XSO ײ,˲ p "O }<2`4NaVM@SZ8DfЎĹf!9s+wS8L'f G9C`HZ@܍A2ppqf&S Rڥ |tжX'[55xK%R҈u*jtE"U>>@1!q1X ԶowV9XSOcc;cX/>-Fp' ,IlH,{9r]] @I֍n ׿_HMs 9{M Co4}!Lj9wC~֛cCT,}k p}]>kgdS*,M еV;>z>{lUQ.dѸ\zSOڌl, r,.3'O\jX葁>:DT>A۳ffMl!HGPCۯ} C]㥵#} @=D.ug'qdhݶK"bZ Pacy{4q;<(jYM70} +mW_uym7+2puf*7EJ9EYl2;j-]p|FP|˸ȣqqHj$cvddqOns.߳yrE,v`ao8ŹX˂mU_qըQcp|=ײ,˲% pq*-9Y|M w|ps9ه0?q`/ ԩSG#dWkG97"xtܘ~POT?XH*8D6A@v{ 9q#D-Cɣc}ԇBDtj ~B& u*iӝ^u9D}ILrg<]g >s J>@}6$QOπYW|A)ފ ?[">P}'G,KXד 9jbZN +ѤTvΖtH?tٸ7X- %[O.t%to6Ih'apc=a  .4 K M7;ښ (0UQTO|9iRZ@c#[VxRd65ZK. f 7-+zF|"Zb#x]hB.G6Tٜˎr"6~:=eFicCh]|X-uyWvMuc"\{rҽ9}i. `԰Ri {5NO;rZq7X']k5Yf]W7mSdYP/wǤzЅJqҦ^lf뭕Κn>1%iynɡ^;m~蓵YC6gУNse:ݮer /ļEj:pez`ǰ\KR=9M~XZ?es+cgp_ŜQD " "bZkH dA#I@2,." EDd<3vS1ya_G%M<hzKՃ#(Α]q.u/} YΓ/Kԏ>hжm[ZhҤ 5 z衘cp94W+H1+끥U+f`mG5pwD/(vU'-7=۱ 4eγJ³PP'?.u,uO9S 㨏wܙ4[XӤz2\X`'#Q'i/x1~_?m/hg/pAz3(^q>0ꯌCBb@:kp\%JSgZx\(5|_g=<2 `gs<霽z~#,R6~F7a(_˄Ik8iKS_|l#g9vRfnR_AK? GYys=BW?HFߖ>$!?"y^z}@bw_ f7xv~-nd2Lxz @Ӎ%4{`mhYӃ tH=142g@O@tu gpd?MG3:OےfSp&_qx \XGbCYQ9ȿ?/62 i"? ,p1`@%=`A;vEP

ʕI' 7>A&TF-7 Pϡl xxޣ,LQe$G}EB, J=;MjϪ lGBH}J~^sp<Qzmx!0z{_y=AɱIf. ';!]JBE I59F+:7>4u5<<rҷHxޠޢuu !ԃ1ԛcp$}/.>m߾+ kfF/xYסCWz^*y}y:{YpY5p L&da;t2NqK'N:t(?5ެ]:ȓv8qα&DL30|>M|=t#TRxBV}H_4pNIِvŶ<0XVK&PDyHôF :o@ysmI!Қ٩·:53 UfPgQ?EV;7ڝm|ݙ(R`*Rؗsd%]sBAG}F}gD} Wz3CyHmPpx֩x<jFǢWQ?uﻰ ϷҫHu ҜQOfFlG>i|qaxSORȚy 6ְaàn˖-{\d2Lf !ґhޫç K\|14mLoATG-H- 9q=(?`p9Uta2kf@*AdڿWkX8/_>pM&d2.`^fkK_XXvY2VJVm3qn=MQy(\\fpMr/ dn݂>}P ТO) XrXZjM;>Eq 5L&d7VlW(cYe=+ƀ U53k2kѣGu%ZD$0 z뭡pgy9)5k2L&\\33lƍ+W*T.o[c`tŋ]xkʔ)$7 /\&d2 5533k&%Ƴ}GQ9ִiӠUV @\-Z\d2LpMp 5e.]B~mذʕ+'o߾MÆ z(FF= up 5L&dd\S6o ֭4i$hѢEЦMWjޢx޽m'踣^z~矏_md2L pC,,o޼pr5,;. foڳjºt̜93ȗ/^A:u^fk ${FYjժ^Npƒ>pM&d2: PcQ3 $bpr -_>fϧYv$[bEmf2?>h޼GY5K:wlٲzk6|'}A b_|qp=馛`Qm۶{HK+Vd2L\p ,o ,W -wZbYY:~|եǀbUpnpff9f׿e/ۋ7Y=fkGyᆳg.A۹sEٲe|-v!뮻.xǂc9l1k2L&)tG`1>X*t#gf&#۱\ X0/I@-eKrƖ.Ncgz0r=شiS;,7F4gN,{mAI}})Tʕ+R߶g,U\曗nP?tQ'(w=zp!^z[9OX̙\d2L٦K/4PܪXҥ%J*W\J* $̲V|`ժWT,23ZtԁIVt=XJ+T0z%ajr̸=<eb /\7nfbzkp/Yd}D.^xXhћ/B.*T0k2L&)7xۺUd2uSZxq36NF#rFzhرiGFeϤY;=Q#ǘ帍3:ѣG?fϤY؈#bfw1`AF;v L_1HyL L-V5L&ɔmbӧUb dfv@  o37==f¸|havlve)Xxq0tP7P! 7n<2k2L&)۴jժ?/_`!CrVf@Ձ3=v,3/yڵ ߵna8z0mڴ`n,d&d2Wjm۶9ɓr&M0 O+a@ցv{e>`޼ynl27orqz3gNn:L7o޼epM&d2L&d2L&)d2L&d2L&d2R5L&d2L&d2L\*&d2L&d2L&ɔKed2L&d2L&d2r L&d2L&d2L&S.\d2L&d2L&dʥ2k2L&d2L&d2LTpM&d2L&d2L&)d2L&d2L&d2R5L&d2L&d2L\*&d2L&d2L&ɔKed2L&d2L&d2r L&d2L&d2L&S.\d2L&d2L&dʥ2k2L&d2L&d2LTpM&d2L&d2L&)d2L&d2L&d2R5L&ɔ5j4o|O0!;5ڵk͛7u+V'Ov?35\ YNz]tQٟ5523Yʫ3 ذg,'lOw?+ 7/<81d2L4~K. v:OW ><0`@ @l=:xU(pN>ƾycgvM8#}?m[^=)6|>Fr"xؒ^hݲe;ĕ+WvyKqذa^Nm*=zA5;칤dɒrJ*U(U\ʕhr]XB *^\\rUr*_ryҥʔuϸufiK._Le˔W\ K]XDGyy_fVdd2Lt?; nݺ8AUj t|7ݶ@9s8h 6m .'\~c[WYb@ ݶk=e_/ w  '_t 1aDiY*`Igƍ7^ ?X NTxׇk631㞓nx'W\VYUdUɪ5_: UVZ,G'|RXe֮ep @nx~"W\VVCvYZ\B(ݻwodd2Ltg+Sj y h?ݨwcƌ z`(@U+#Gtt8@߁Zȍ. U+ŸW,3߯ C὏ }n8cEpJ3Y$(]8ٳwG=gY=gv'_o o.13![ͯo~Mua-{Wvo LpM&dJOO-^ 'Pk9#Lr,HJJr黓{ i;WǼ[s$|sՓ8)]rO8֍[V^z&{~VtX)jO۷K*v۶#~HkQ`[ayΖV~s1\+mmA6xjolodP\,-ZCZ۷'fWZ5L?JcwWPO?vv aJIbpa2 p-be)|p 6 ڶmٴlgq䑁\3&pM&t*.6,@3xo#(Nbm@YԾ2;]@=yu{,@&{}dʕ@V7'!vk ^M+;~xotwqwu[{B7ض!84q۹sipPܡ1v)\)uꩧ5kv~Yg:fpMp L&X,Z,Pȳ-h&:OYAP-0 j(]BO!5/ $` ţV~O_ 3f#:*š̱]1i9ͽ^yεIw[ n*" i`@<8Ge WbŊ:]ίFsDƑo }^*qUc\A箈7.U uW2U`Tx]ņ.pՀ VT*aU!|*\33&ffpMjly\6dܼy@ /\xC=09-Q@P.>.תR%nPJsfTn ܁TwN~g[A"7 1*pƃ~׮]= ~r4Rcb>Ml4k2k\S<@ƍ[L +;.wa_~cl@ %KtO=⑋^qx8Xe7,{x=. ; q 8qfŘ?w8!f#Ȯy;~ts35 ^;ޣ>D~,ɟg*c,pĬUW]@Tǹ8s`/ƾ{}v(x[HM;#=>ysCهrwSf"\O:2:#qMH?i؏ttr,9nfY1NeH98y'O 8 0?$A{J礃B>scL⹢sCZ{jEMK6>A&M^̂ƔCe: ǢMǺ{݀~9/V}gdT@WfR?W`Dv40Xd\ @U^=׿^XvP3/:)s0Gy$tx53s(v 7XbJ*gq1g*sF6ħ$呝b\ι񼚺L{5,'|y~!/x='9<Dŵu^\s[tY?ͱX,,QoWOˀ( uzJ0K金'gDH@Q_`A;_^Eu\!>4'cl㈩ m~'O3&6>kԨ^Ш:s >P^3xY{>wlvꫯvQ]Ss O8K/Y|@uik;Gϟ?Ƚɋ+A&N0**&>y C0g c0ACo40=CNc"$7ž ; 7xLI%NED I4q#o)/^(y`Lc1Dl!f}<4s^?sSfd;ʖ*xw@yt}(ʀ7L}z&Ay5kf7.o_۩9>.D>6"#vb@{B[QZH;vua(?l i33Q/B1\)b;jEy1$ ίJmW=xa6hЀqÉM+W1=~`˵1z[4} 4J޳s!:7H3ig lo7h+'Ghg ԎA;qH1;M͛x_-=(&t>0-l97}:.{6.Mq=/vDqmy9ʏmR/+k`t`E?Y XQ.-oUL n. E?|%B yRteT;.px@E)c6(Taȵ?Fsr-t2cn GZ=ꨣc9ƙoGMY_tSq]LBɯ/PX*xČYgc|Jߒk *G3DFĘ? J܆#ypc> ϕ zHd,/PI3})U^~VU}'z;! N_͇_9 h`%J_Gıx.Ep_ۚgGN~G3rr qq,?Nׂ\suWa< ͛{ Ǽ*Tn;~Sg00]pThXW˓5\ٴ vY kz%:F}5kF4 ? Zito3+\ @\`}nW6`̫}\5`/x;bbǰuW<4(4۶kuAZc۹ӑ~I8vkY./v\X_)|aO~>lojM\yCR_00sg}Y]{Na .ϩ+C'Nm >`jwNu:ьo=էOWN>6P]{:Ou)qɧ&EU[*e*RS;YOҭ i x9zelӗ.rьeܕs\dzwM ߛ4?3)`f EO?_l>UrN[>=Y}mUmYl>>}fY˾ߟ$2K?|nk/l}d)tpƒogהbHcUO fX\Gɵg.Mgv? $@wIY./lWhP 0wm c<^򲕶1#xgƳ6},B0&n@fbjlXi>Jjn#6/njhLӿw>y jWNy*87/ePB<9yv<wr-7`w._:7746+pm<~ٖa)klH;xqFL^t.W JoA\@ X :V,x t*m|~ f|*,܃L'^.s0e?D|^o㽌@R.Oɔ{{?AOǧ? zʶX`#מ,3+?}K f',r/6<ɣ_ H 0 [<[ 1o9,'7= w(O@&-E<-1m`\sIvIʰ/T.Mp!6&! ptCU0*PW1*P(3a`JD@E{- r*ʄ$PTJ)T%jjKU yK['*e*O*P`@3 &T iH> R醴ؗ4toĹtMHע|#_Y}C%xǠXx=5FI?e@YpnU "_4Ӹ,y둚wA8:Wјoy.m;MPR9o ' _ojعa'Y&/NÇmbM#?5"}4kvNJ/5{nK9iْNa/#v^ ]Fƾfq.Z7TI~랚BRO;K| 1x|T$h.vN9#EpaMGZVΥ73y^ p&<R=^r۰~a-: {&{oIלԲK海9+61vGL&-pp:4]{y.aC͌'σ!?=Uf ;>0]p{+:Kֲk֨}k'oa}h@ :2S"z1@mEsH`e1^m+ A a#~WYA; XlhWx [< 6,xUx;)Dq~bd;?cs, )١[vixzp )`JK,YBx\)U/ gj Ƽ@=Ba348t PM~(gǙx*SF8 Ѐ@?!o bsGц4^ U=꽚IĮ_*k MwH8/_#ו v TES]޹@FoSn:ypn.V'+ŘL18MoB$Vc\*}Y#Uj*Q`#Б[T~5XݤJ P˛fubV_} /T%{?oEEm@BW ƕu媀v3Xf~բ ]~'Sy*w\yJYsyV5S^iddo0A*wnʏsrY c+e]F}%WI4sm:*}nPݟtMc>N0D :V2+_wJ2r$}:g(xवϾ\ۅtZ8KWV:ٳUpwU'9@۵ypk]q'ؠ]}?.] p~:spww1P->%/p 0u[gix5X*/*bz*͗T;}Y|J(Ag+9`o'7d)?^pV8u[jwj#btvU'ͧRmu?2!٨ytThCݴ/ )>~lpc_O(OulC=mo$mkԫN7/s$tP58XDJ[o0WxwyRә"^&C!^2P;TqNoRpKpYs(= G~<ǁC-`]a;۶[?*r@[H`$;e!m٩ov#{A: †;:AS92oRe?ҴMwҎQRkwq˷TޞƿZ頪O'zo=ZcOhIָh ٛZw}Kw2owp6_dהEH2 ?,%xH ~Pyf޵qY:[Ÿ (9s$V`K IlQp]y+++XHml)w^@,*@?])%(XJB$ cN;|l6TzsV~4Mw^@&.r􁀉x7  ,,7f2&ȂK] |]88ЂZ%Js8O7n ˃ۥ`0p1yTJ`YRТr;=dSзPi]|ou|c|QIrl̀YhTot\*Ko4"|O>ؽRēwmGnp!+&'Bg^^pO-w_wSvxRcy4|(z)Vc*>Q^8:wtԱz_y3s]]U{i}G1۝jԱj@-rjK9]_AX?Bui}w6ڦ3W2% j=pzU{.WЏ_5|9j>7c5{7|_!~\vv9A'Vjod!"AqMۇoAs.0[:TՏ5"v@uɚbTɋFJ[;3Q{'ŊZc/|D<>,>uvynq􂤒xSgpn]m}Bo~_\B>w<$oJ+tH\=,F_aRYъ:|5mXb; U]3oT>UXFٖ}t{aUr{\^2 [/ ച_=~p01*e#zoR۝A8m@A=ev)cŝ㞥m8\.@q]ߔp+f,`OB g-~ zŃ]p's7/<.Ehx1?TWWFQ%/QL`#QY`uJ7c)yޅ(\'/^/Z2$3pġ>^᜸o$J9Lwj ZH3LCxY?\/=?wRF}.xG D䯿{/蠟@[G0Ǧ I0Wx:((-,i{% buuVLq`O8FyXՋPmܯыcV$>??~U9K18⫞AXߛ r=IWU ˳"8}εǾ朜42RZJSwp l><#:2D=m]ѵ<" $3m!3a p( nhUqW] <[Io\},5{j{9*x~,A3w- zT.oUȑ_yH\6:OFFS#Z]xfE;J4" m |$0@)5wL3A`ot*(k:G~:=0U FsS-{N8Ivub$ۦwT޹}t'.xaIc7y3wv V{ :0:;➜ ~Ru V퉑F|}Fy.w.%věw|"\z ̳nu7{ 9u6}7IݪMOPo/2Hʨ[ծTWx?Y>6>NFzY/>`X=ib _ T7#G)'t2f06pMqА,V(܂_ (:th.GڗP{UDu{~Fqg &^![^ }f5 Orw+ԯ:Gί~UJbquGst pVZGEMG}=,aCT H Ύl9P{tIyZs,FT= o) ϚOyᯎv\kT4\}t')fzkT-uF): zpp~ 8Naf:at|ocWbҺ2`Ϳf`̀sNؘ̊fbjv̟3f8/J/.i\}c,_!e $瘪/sC!0A@b-o*sgyv\OV`uDn[B*l7'عa̛om.M\8Q1zd(fk禀/;;уV-RY<)| (宫R+98=38t ~5@+q X?^ÿHu7_ͱ{ \Df G؏؆z_/P//F=H[EH= / #lhM\ ~L:jQw1~M8/m3*h K'Y@Կ=\.Eu 0{6~ - L;?O bx~0}I2\x_%tL;֟[]*nep3 @8j/d=H<0m@+{5Rc(&}i@  (~<~TIwmt16*EQGzP(9Ώ ^1 ⁕@] $P`O\$z/%{'b [cZxAw<4;Yxޥpf O3v3 ǵ"‘6y:qtl+ҏI 3 rq-$pOy?5E~PcieM`+[,=rB]EahN?! gA@,%Vc/_5& -Kv/^z)S~wHt]>>bt*.J#LG/NF/hC{,{;aso+|]{wCw+X?޳i bo @4&]/ZM|ׂJ|97wwm:(loO o(պx&^[[u1ogi.yXHG.^#z 0K4 psR7G4G٩@XML_lx7e4v(U{i'c~-#Cz ~87j=6r`p *h7tXyU=0/٨'EԱ\ jF xLmӀ_FYG22'T2$JM=./3c0/ފ!{k:1,~J.Kcw-'E ̼[cOfNXTNT,5 T6^OWo!d\+8X{(& Y|tb1I9$ $o>yP X!05oA hߔywB-V:x ̤]Wh>o|b~^Ao Ue%/o<{õ10T8VˈGneK#?xuW%v\"@z: d?fB^ TkXzc |p%?z02   $JΊ{TY(&/°|'!PN1ʓ^z>8 uʱ=zG|r^B%ymW{_u[|Em%g"vMX -?AB7-.gSm{'xg5t\``7\u~^m n0dЩ'K 1_y^C\AT+t iyLX pľancpL40{fլƂh/"=pqc}][ syM;kAӧqP5WLwutp¹kzKJk|aK5޷{t %Z[NkhS6K-W-:r;>#yov_r2`cJ=t^_F.߈ 3H9P9\[Q#/W6. OqX\1K|Y8Y W)m=NYnxH,0p+ -b^^mkkx,~X=9xw%i#ɔΌ6gA_T,p>`<xu@+n&FvH* ~Vg7h2)5WpjҌW.E+?\8ĻEy4S?i\j"%fVc8 0L%u11`[U?d+;[@{蝀5B0֢or ,p61`t_wd1)>u'7=__ F~p`WʪpI~OcʆY;*NAoOe_ڿ+ |Xy/p!iжggAۗW~P_7 Fs=(iz=>VRC(D]ꬬwKJ6"N48pvm`.)<^ wzts>8 4{[7a H᧋tرV~Q XR❚.4PɋPx~p5 +s^,嗖W/-ua;6`ʀ@|aw|{|(/ PxAu,HIWtr) cLRytb ʊ O{8Y;at Kmf,wl|PB6(Xi\㲸Ĺ]9מ~O ~ dJkp %Ah#Cj9/߹k,v֦[Ȍt5 p<>(/[.e$Q6 pP7ܹӅ ׇP|aƋl1/8UL)]͜1P~NjGP5RZ@/EX|Oa% p5lL=J_{~46㍬gbB'6ٔ)۟u/h.𹬼_S,ǂ]wcQAmۄp^w7k_ntrrwoѾ Qp:*OxZ7T /T҅ H.}}`i8܎.܁`5r-}+ %B׷HgN\=_sU%vm~[AU~pǀO ThOa||>a pmҪk8Y!X,Kwȃ7+wO6.BQރƦ8L[^/޲Y^'|Ypة)<9[>t޷,s3k2-f!2~`@[ <Yqs91dHoU'0w1Sʙ.g|1, IG1",b?8q>DKhg,H1Mgj;B;4&MpNudLCǐ$#|,'otK~"^OqXILq^]3bO|>Tf4~OXfnE`=tT4K񞵀7MK28^G1SHXԘ i~kS^ oQlc]mV7uExr 8G:DS:v @\r;BP?)@sBzq 6:VKca_~7yP4Z|kt-5t\( ={\^0E:/WirY\sսK~-i(b^~$tX\@6uL|N5Y}E ߿P2MZ/=[:TBįśh(`cW{DkƤWd[|\qęe095hNǛT :CLm_I&JFgGzT1}] |xoe޾SV|3tD01x:k0]㔷:Wyo\ʛ2R~>cPYuhɹ~;wv؍EaB/ן֥xN9܅Oxcc1? ; b&MHxs;Mر  A\7EJ{d=pC1pLfYD->M>,/v\ǿ}@j,^;.}T{q_Rm7x0}Vy1r؎ AAYpgF]xfg<(-װ*k `Q0w \氫kBqE}f" DDĀDTAPP0<ί5{ }ݙ{;TWWWu9 Y8V9`X8. I!Nnվ[:ki.˳G MT&3N~UF0ZV~z==JNK^{8 շx];ÖיS>RA%Y7ҶCfִсgze! ˾`4Gܴ IN`NT< L.n3rywE\e.uwU,PGɰ p0 r߆cјa}qPjyU0F>I+m4!7"^8{lֱr'\\W'!В}{(xx} V'0Ȉ̀Ԅ`ܟpNrR%!mh7Ìxp^^CX]'F7Jݫ0Z֘nR'p% (C <\-@\ 8f+ Yj&+$UmeY,< +!H|a` |#yV|:wsɻzE䉴a4AmP B@r_K} V=[[\˵s%@9Y:AlylPL hǘ7BWBpc`<W(ԁv('nP!OJ;eW h,yK;In.>5/1ѹ 0jBS@Ę*m1lD,WM-\W.FaR뷑NT&ޥLZ3sU\߁O|LBC,eǨ6.,>G@&S[^{u?Ʌcɼ9Lsġe*P7L l=.uɛ[.({D"=c2g\ݹ7o^(oG a{Dpmsf'31)xrqy{y`_;_8к{ؘ2psj i/;wuAk6cYkeA2ÊNH rcY͇ĎR69w0F&b}m @ Hq6qJdQ` >Xg쮎?W_^HU;:ԡFPt.d:jnNbCBQL^Yc\nf(7-gYK1tn"8 ~?qb84Oas{$f[U߯gO vi2Lh}v\GKpg'Z1x{*y[NmM2UN 2W7>v[ȭ2< Q@L\ 9PBG|ͫovuVف!DAy\l5ε[ umbbb%ל`wTǜt2y9WIJKnɥ2n鳈#K{a.7 Bhx/WSXW}Z6S}BW?H'ZhE\3V~ 4yP2rﺡ>t83 pq~v.rf3"NXW d} _{bp{݂k邜رG?p6U~HmBL0-ҩ#G + :n=( P;6 0㣂1u ܧ^}=ή g-m [7,peƾ~w(DLǜ|p|uK7xٸ}9/ pG qmxFB{J/V^3['t'mo8oE\,2#lx`pJ$I(` ,aJpqR>_KΘ], Ý HP|b\6nŹ-fcߘq`,7Êܘaϼ~c2W=͐Vz fgkkYq}}D6vfp | ~ĜWLCu>syfnߊxGNo0'}q[%9up ʴdq1S2+Ʉ;3KNEo@VTܐbuqG{-QY.o70D@_-U>@+3G S8+䜽(U9z'Y7/&5*Wqn ՛ǼRnӂsבSqW\g%c (PLຯ,~Z@e_ERDVӲ.u~?\yTL4NU?~FP2% !ofL۹8^)_Aq r*7\);f-\:nyuW-;1^ئqRGԹߩ,~2SW0/P}(oYt*XOEb7qia#6Rѹ_'HL۹R5낲}_~\IajZ-UʹqbQ3KxWHc2R~ʫa(1ycMT(`6M,]Krr&Uun:u.دԵ4T%e2Z^arUcO*߹v?!EZo'e.|0H̖\&,H 8iIWktҨr5X7uac65XAstp2@ԠXL@cZ$7 Y\U JBCcȆh^1p9F|ɧk g(AmǗYݐ p(v y<9HkCuxy4hxEJeΪĴw߯ C$eۍC&R~rp _eB5&$뜝_8$&3{A:yǯN\?-&;pGoK_? x=#Yf-}hdB%8_Pe: n#/p}z=~{z%'kxg/\n$%I),DX[G/7ڵ$5K*\f'A X}A N=TpZ'(}KX7@AHwI0#N VrT9L*e?Z + p5So#~07Co"t#lnt>A7G܊!?;4iVn] 0 VDpIpWCrO@9826O?N1Z,3w*~v\;-S94׾T&^Dzv28?E#8"bRC L0@\1^đ Nk] Zj7=x<1| @ NѱG0ʼn nMUVm<Dgb $eJEN2yw:#~vV9KF3!GA~Uϳ(.O[@LbLNn|.8=.A'd'8 1py->]1 0y`ن`lITE0R`^op=Ѷsb!CW S1j{m߅`Jc '@e31ƺU/)6rm# 69ƿ:S(wG` :S#Xl-Bc_Wh B"dx#.늮@ ၎ñ0?#p:g dpb@:/Txӹ=,pLgip=4/U~z_Bف.7 j0q.ґ9eC\㽪A8I=7y"(ul+k_ѵ:]|7m"ֶ`F4)cx黯4Vbu0;\N:ؠrW~P^j#7q~@<@it(}mc {WA]1**XN0Zpa*O |qөNy%Hibw718V<_y\`B#QL̶' .33إsX;{o ˜ͱ٤|Oo|O3 LoIـI?Vd.uIϹB;[nnt|7 4iThزK(<& U ):cYtg%nIRa|\fˋ*KQk2%7gvP0z`VqJG~^6a\e p9F}?BۮM;wQ_Iu2qs+WK(]%Ma GKIe>PS~d*ǓmO;eQ^Upchaϼ(0H/0~m}@zMm„gd9I!.B2:uAۅu;r̴S>ċY7\1y~E `9w? Re?75佀˷n;\kxM+w>rxbpr" oS q+p;[o%0 tI@[bŲV(T?`X39g T)9 Jx:J/ 15kr(՞|\ÝTUFg!f 0G0[`nN):'U{V9Zs:ޜGρdo p$V.1ЦdvhC#B[|Ŭ[yla pC½u,sUV3ug(iܺ (ξ^_FHM~N!Hm+UwgRqѡS+q#p;ܒ́Ll zh3ToFo^+k9*fr$-;Y뜘=Ut ~Rp>.LP.a\Aggv?qКcNP1@G,Q~^tn^wm+8)'!R)npOF\c YH.9߳ǫa3<$ O~~` TfKu-=5&6ʑ:@*3lՉ7UV-LF!d~uxTs2s2Y/ɠ?M8adzhSY,$p>q &6!V-\vA&P8YT18"ƶe MTgL%K.܆r,f@A*^ܥ8m2f[|N^.hb*08V 2\xbƁۑ߲yja_y.0?$h'/;W=*NSRlqDl7y,|^@OqK 1Q9h?Z7LC'JY6M2Mf}ʘ[.!~ʤ<*`I]Ćmw !ycoscOöuIϾM %_с{oNae;13P_qoLy`PSB.th3k+}qe\Zot>x;^Lƕ"F,Nբbb!wf k"wi(G^74(N80ʆ7Ў/`keu ;Mky"nh K(לamնH@0~w0hˍ47qBnsöb_%\npV1Vm|~Svft0cJ>sJԄ`=X|%& GK? 6) (% p:/ m3i+fY( veiqu#lL;p!?1?oL).,zpK~賓v( pWL6}o){hA7[9n&'bO"&m& mYO<ޢĬ$/@&Ba(O&y>+ 8<)K(:s12-EqoC\RRƔ"'lOy$xh˾p3IN8LVjqRΜ;/; #9֍?q`g"b2&:棉o3:pc2ybY-g>1N$x]`rl6YrNq9W<@ȽGy$\[:op.^8>H! Mp<88є@L\˓IKqma}^GE\/\s׽er c@;rg Ipp;I㛙\P$:E.D^]г?=q߸<ՑO ,cD݀U=ە91`y:Bu|siOKX&^`}݄4S||w~wuu?]Ft40ۨ\720VCnh)^mVױEvNryMdsz1^.u0WrO e p_L&&cb/&]*ڷw:n#^ Pg.Wbg;u &2۹cٴ 6pKK cy8`?:*[֭MlSh>I m_ֵ5!,wX.psu]'lϛlvwuزKXٱf'{^[pV'\uqa =ok!耄&gjf1b/a$mmֽ` pj9~"ECwц+?-x$NtҮ nvu/yeK`Wfq^WҊAx/!^OL;V4 JFJe8_bJt] ]o:P{cy ،-`i >(*u~:ma ! v'8wCOfhںOLf?tĮ]եCn  sۮǀfC ya\%Ot. pE.`pLc[(PP0Ln d@Z$8np&18}?0F'0 F+}1όyO܌ʁ>@3P 3tk"^.`4g8lqj=N;10<DZqDeEƫ c2{2fE(GeXqJ92 "0,N,h }0e  2'&+ ֓{3n\q}`x(❆aL{#8yCE)k7z9%v02]az d0\0p{&Ԝ{{? $w0QINTʄI"kp_Xs1p `-NbQFqRS)7珺N9wkRJ8\ojPw,c[sQLv "8<6ix8zs>f &Z>dù4Ą(O~Xi I7 PůVSC%@v0U8A <=[@ŵ厺Cgg6pf3 d0@5P u tA6+~j[ DoriAoc n<A~:jcܜH1!2DoiBB㚍O:JLF|ЀsvTV tުv88ˀ2ʾ5t*uU(7uA<5Յg.+_ qpKCׅl'7䏃W3__EM鯊Yd/*$fKݳg\ā;r~WzP5LtcD-,P}jv, (Җb @hx,fܓNywۊ 0<u3ݠn7ѼI nq pHAoByk75hq%ks57l+-նV!O%yC"qL"B[:j&ǡ{~KiD@Rg}K__P9^vu?b'0$'y LāZx-l}I/o\Y@s; > ݗ!EpwF\c^~ 6 ̲yyZXVecވjh<oIrSE"Ņ+q%㤊)K0TA3@9 @N+\g!30O2yDz7쓷`60p:O'#?G& c8d-4cL˾phGabw`d@:w~7?N`M(#=+,w+cGzMSʝŘθ#. c.NiPsG^c?ʟ;{EX"p('Q69?l{+ʓrF\C-ǶGA'7a֢.Pa$ʜG]y19 ʝ舳1`8"Ao"bXmSИpS2l_ H{K08ۦOʄ}ΆFA2.??q;<"hf8GnhtQo 2lcyZ!fQ$ZvWt¶Q<ުKC) e-kq{+_Y>/oe+ciBMpeUΫ#;W|pF[ ztWq'$cC_xÜRoj&>A ¸$o9A' qA vvC|>R~6;WFlC;[b pU`"2>lRK}qgqy(fY\1 o)cUxP#pNX."äF\eNH·4 ̢Kf$,I QǺ_k$c@\eo-.v܂n\L&q<ӶroYXo28(&<Yu4\8e(!h< "#5.`A7J[΀(^ dNq8<`S8>KXOf{ L"H)'|g&vX7=IQ2帀y{+$S׊\U pT7>mR MS'hJ"%]=G^7 ,ϡ=3q'Ő7Y%Qnu'űOh˪)KSa}D76utӚ p"}2ʩ>fkr.}7,>2?ℚ6\R1#-o3WfQaR8Y[AK.;;v1dU &)oF!2拷&d&Z\ĀA/Q` A 8-N d߬c{~1d Ċ 7 d&f˾qRYSnS1>iJJ䲚% b@h/r\.9..*ym'g@;P]׭nY9%HK;$aaq'rN^OZq,S ͓ێ?}0e@\X[^VX硰2|ddk) ̈Og0F{1%Y(Ʈ{lCh@-[ؼҏ[Ž hX{b6.\bZ_sMhDr5mYk<|$x=3+J3y T{K V7:Ze Q'GDf gw&-#9t F)&"&M K _RypLl'5l0L:db!*0qTp L#g.K|h2]SHu@人T'"q:e:ZkDok\PUpO?5 Bކ#e"DX(:{+BbGydDj88qk3Hf8o5o2]Cv';MIuKAE::ZNNmj#I(fO,eYekT6$F Oy LCp3IerQNNedkw.kYeYV p%;\%9@4 '~aR&t#ײ p p-ײ,˲,\P2ur2 p-˲,2,\\''\2,˲,2u2u2,\'\ײ,˲ p-2ur2 p-˲,2 p p p p-25,˲,kywuI>Cqr*Dl p p۶mNpTi ,NeYeqw]w>NNcLUCQ]4gVH i,=뢣 M_zcSW޹I]Zݖz꩙}S:ud p[/L̘SP׾OgmdkZeYUw^ni[;9UXQoMv~a~ʤ_7\tqz'~32@\'Jg}:ln }{k֨Qd-p{Tni ֧mү~K :9UH;88nk\ײ,˲T͚5dXĉjzw]v_~NNslVM:Ĵ)g|JN;5ݥiz5ӓN?4=*4Cҍ66hzΩӳgOG^ze:wԩSeT;{f:vm<5!u]/={V2dȐdĈlXeYezd֬YɛoL2%{9' O#GL 5Qg fv8Ut3> py8sZƍKƎ>-ײ,˲,˲,˲,˲,"\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,˲,˲,˲,˲J*\˲,kuvɓѣG'O;}}%'pBҨQ#' MG}trQGpZUNK&MlW_hNԲ,˲,\˲,*O=\<8رcݺuKFݗ_~RԩSn-0`@p5*Hwܑr-IA&N[f 7ܐ\x8uo± R&'8@ܻ+\=~ !hƍ;,iҤIp~ʲ_}r饗*^>˶pYSOMN<ĤGu]~Y pnӦMr''|rruׅ:,?~p3a;a6:B\6=C ;C=W3uIt0tJoTn^' KԹ=5j|>2qZupɮDwz4,˲,˲ p-˲,yOCN`[*c 0=LBLv.Kqj #|͜s9!,&guVx[kqQ V@ V iӦI>}?<"H?>;o<_G!)OA6CbF0}9E;sV,]8F ̙3)_Yown.tСNN"6{GF"SE%շԎ⃿/0h˲,˲,\˲,*GerU%/ԭ[7lor^&-[L>yTԫW/ 0Z-ۍ78]v曇$rNjժh!v"Pw;livL2tM6~n)c=6omIn}衇X@bʝqqɫ&(SG?Ӱr}G.7ƍW-U}m`gɥۇZ,9e ؾ Sձ64ޠAxg{(y!-a;;b)_66lX믟F+&тoi(Aw`&/#TyYpg=LE]3(>&vn1wb&9? tr*D]:wܝg͚b|*"i@\c p-˲,2,˲ qjq2~w9*oyw>q*ET SKWI.VҺz9c?$`}F^VNCO[o5 l;߿iFr.v6•{7#^;Lи\ ;wqjI_%,sx)2m7V缁1QC{g2AjmڂԬYa'8pqW)\ʇƎTn!lIF-pg̘eTP;2ĽckYeYeYeU|hgL*֙ s =og9ߪCܘ6lvIpv58|q*;1~jTd;8sN\kY!- U"eYeYAeY' s=i^h;%tK\/\ש#܄ع<M;@륋-ڋuK% &n% j͎j+v8hMzCT 3k´ze{~\LԤf… I x+!~.+Wo !om>`D_=VeekYeYVeeY H9~@=z$=X]0u4`>Ca >8+7mU؂ch=Nփ\nJNT|!'nS&/C\sZ裏&|\~W'$:իW^ p,k7=0xRa/XP@q'뮻 IӦMK.$iժUC%D`NKz)\Z2,˲,ײ,Z5y3\g}Uf@Xwwg;*l-?;h{E}'jұD1p/lXk\SX~ 6yp/1 pRnv*LBGP`ضr\Çck m~6* pU-\˲,˲* p-˲5N@ѣG'1 bՄ` ;rvTl[f )fm*:I&Y.R,:829qVnKMbq2qiBlN Za4ח^zx%; הkʂE ?kf# b*/?$y晀:w ѫW'|rҲedРAVekYeYVeeY HC%r&۷gښMժUovMv)';iҤFmڠn{֓6\ 4߿}& 9rd:u5QnIŘhLi/}Rf6ꪫv<#o k޼yțrB'f_v;Ph@^yQvòZ*Ÿj&f۷Aq;jئ\~剎 g [@XwڴiaR7UYdkjZeYUYdkYeqRK>f)(mp2믿w}8tݻne&2 ƎκX &ٳ 4 W8|UHF\!A.]F V7HZh-har؆<;vLr!2dHmK8e;a_|q /qo۶mg!&/~7e_~2V pU-\˲,˲* p-˲5R!N8TF!"[<:thr&h +AF{NJ%s1)m` @Y-p€Z-^@@0pg7}p y.(yeY ǀLL}G(ʝ&,*~nncC]&X @}䭷* pU"\kUײ,˲"\˲,k.V\wuW QDcǎM>D!/}sm9XV4&aZŵM{KLZ:Dj ~ӧO2s̄p @ L&/p8pqg'>K/ ]b݆߈ۻwdʔ)idhA.%dCUL+pq;&-OÇ'ov@\єQGO?+>sGyP+(ѬYBh&S̝;Y6f̘ ZO.DC!m%e@[U2,˲,ײ,˲J!`b&~r=$-ʃ-?V@\+ p<Gh@ /vuD&@3083Zbw& nj<6,!!4Ccx N76q k%{Xc: Hγ8E Gʊmo|IP0]v %ѪQf_7k! 眲 h('uRGuǹc(*_~ e;(y*,qyx!3aHQ'LPuB06 ox \Ӏ{/.Bhu(7߄X7",P@0nJ9Qc o".fM`\+1q CȉjժXwuD\ܓ͚5 GQ N^qgƈyoݺup\(s`is!2s $…8n =' j@<Lx;Nd oa7y]V5hsO?=?u y1(X쟐ju;S״T߽%g~ aS(7eX2,˲,ײ,˲B8h)XG2L^ߑbLVɊ" 0m\Gp/p ''~馛!--G+(]v"h3w߅g7NF@[!3o[+,@[p#čOMצ2`4"nLi Ծ10{ 񿜹2y < yv~_ g8f5!䨭-h:e#TߓwU^՘T õ8jKy]veoͯs˪"D;@P^zq ue+eǙTn \S]f%IQN'~y|g0LONxYR5J'w0>8J*KoN?\Gu.! 1h_J3ײ,˲"\˲,˲ \!0) ֪U+ź,gb7Anru<AXú=I9,[ ?0Ah]/wVQtr>`-NO 6Q9C2}=Xgh&n'w p& :P_+|AXVTAVǾ6lS]NO@ec<3bՖF9kF&(xvYgUct+r][I'|rBBYO߿p 8'-`7M:/ wa*s!{t)g7nJls$siU\/8;P6 M翣ngDXMб]9VUĹwQtU'2( ~YgM~xx[@m7z#@u\Z۵^GH-»j~OנW*Ǩ]x5XG~Cqr ~)z8G(vE[ܢ:=r`{P\I p-˲,ZSdkYeYVqZ! ,b+ ֡9f0XWNw着8M)W[9pvy_72uu i?D\\֩I8}@!3˞`kxJN2# a6\r~<,7nNX\N[9wl֬Yؿ@Vc*OQEw@NHG* @e8_,bs;cB\ew`yF`-#q^3SXD^,+d+re-«2%O}]E}.yΨ.f>d0hһ}\VW<Д6#pp[_<`۪s;ʵ2.jb\ъ:ѵyCf 1A9C?1\Ҝ9sB[x dp@}ALG]| p-˲,ZSdkYeYVq*7;bĈԄaUw pu +@ @~W'Y3~pDz_^ʍ$nAT`t$ncx}L+Py@M~c6p<& p'r$$ +eK#ܟr@*epLF>j&LHa + eC̑p2q br P:{FX̱s; M$1OC9Se( hR8ńsepW˃{7חi.rw@YqsUmIˈPT绹>}49쳐' n^D{z:\k\ӧO/:`kYeYVeeYeYũ.z뭷I(>| Ts1Z!#Ac|NA7``+h!Z` T!¶u%ٰ k/`XW3AXF 2O$L&(? $ݷ?Xg5,yd%i/ui:8*?smT=X0 . I\˅\Lr0`@H @)2oDRxLJnqb& !$I# Űw{>u40 } M 5T@qAa+uIWpE\Zsg- V}LeY)*U^s< `c8˃~gŇ<(_2,˲,ײ,˲TwI ~- zMD\ ",K} .Niɏr@Vuy7w@1AЗHr&Y$̻ x+q#XpmqN^=tCB)0i3 Bc\;22.pK>Pn?\R퉓Jx{$NL4)ZX'wjsn}b]r`[p>"Qι{Yz'q V _߾} inuiXSeYeUZeYUԁ+hj# $j&YP29;mh;U0:p߉.(IR^[T%sڴi!\+s7 8@ׯ^| O*@Nؙtu@HjȐ!a 2L:UӾ;CGpLva/ܾg_ p%O gT[Uye"/Aɐ/bkw8&$uK}}/0ݜ偋сãtQ~(\3*~$ε=qR$IJfA]E&C!Xk+/ZU!^._=[eYeUZeYU4\;&$z`b5+HfG=!Lnqf'9[f`-K@fA1p OF[!Ph&aqG)0O7N!}tS1cx\a= w(P"9N\&ZY5m /qmr'1S8s0$tI 9&Ry;YrӴ@Xgpaj6^UrD<pu3y4yV[rh`BePnw!P<8\AyW)>U1po`)&zG| =x$y@%<.ރ<(!utժnF(zXrm3q:7]2,˲,ײ,˲TW4:C6#bDUh HUp`%,.,tРA,[V@E [# Y^1gf(eL b u=؁H*›Du[(O&211GJ2y׃ 7VbSo邤 )c`I@5PzҊ (ϐO93LVG/'-~*sUZ(7 hR 3ѤI]#T Ǥ(߫ٴiӰ<+t4~eYeUZeYU ><ĬF\ ,}`}hM&T(lܹsCN͚5ѣGqTq!:a5EN )@_J8Xre0>s[g{EY\(gϞI֭(ZkC?A>O{rcFwg0`@(KjuW]uUj_qIbgW\jp$fs/C@h]=|m/(xXcػ%\˲,˲* p-˲,*NpLG@nFMG؄M5Q׎wHqѢE5.y?bD%X4^ p L'g0=>l~[]+I}f OO&"(U~ȋ+%eA9 U}9)[Dh%mQW 4&)$Bȃ^8uw=;0 i{W^bF{ł"wDt+K]DRFA({{̝{Ogsk.(Mw5 @nkf"ŀW%y2d%$sjr!00sz{\չG g] IM^Gz;XS; y{r`ĪV`zW;kѶxz~$!d ZeYײ,˲TW˦%pˏ%ۂRmvZR]S6Zn]8O1HO:Njh|6J)=h$,VBEF0I|)ȫk[^yD;gΜZ*@\, ĂIR8MBD:h@bxg&1KzH2 qUܝSHBJYD[s… DZ?;8̄>|xؿpQ2Jq`Glwڵ;U8`uO;>g6;eʔgkS A}p!=+Lx/L=?OKØh &.-bUyS]c2܏$ Cz/yoWp,/v* V׺3i| a{80 r>+V}%6sݶ 9G_'+>¹T&Ԅv.GLe<{l p>R:NhQfd /I&m{ײ,˲EeYe奭ī\2 x ,T%i[,(4Iؼ@XoÆ SO^WLELY"OҬ$`R˻'w? ɖMʐ puPw!dc%403Yή孒 e 4e:[u `x oou *J{% ł.IX:I'`Tޒ(vZ"_2=d~'٦zZ).fyCn븋余 ?D 6,:uj׺#\ >X\QF@%]^KDgq+dG";UK7#<(j |Ӓrp 8;yuʼt/{ %I X$?O<ęWx7p- ѯsދjCXo/&eYeY; p-˲,K[p f.]X*<hģ/?&GD<5T2d%418t\ r3 PUՉ`b dCP*%@a' +ikb@g#?)҄/,{ayS^e ؛ruUJlT a D,)'!V2hsuA _1su! cw Ԏ%K̊p@LPA!:K>1`ՙ5>{kAΥOp3B 祮]Ht?Syr?v;Y{3u>4Sb?'y߶eYeY; p-˲,K[p Y@ Ԭ&0v: ب$.Bx"fs~'&}:bB6muC=>5|>)//M.ʟ$!"]  = !7kZ'  ^w똣o1^{[us޺x2Yw߅&Vرc}\_?ZIb 5i$]vGd&:ny?(^eѣ/e?b+bnsXUʲzb9I<&@y%xݱ\bFZ^#n5 p<, ͛y/=,h "`$Q@ÃtQ+Z(k^ S(@RUVOR-1ꐅ K9'8qm~Y^$qKs9Lc@ xDŽ|uS& '1)'޻C=SQ\:y݇eaR6@eYeY; p-˲,K[ p_<ad[x#oNx N:S (F Vs+ U@YҏpB?m)bLpx4s '` űL&)A 8tYYG;IPY Iu %0P>d>!yEVdɐꫯ4}ݻd]ZE-\˲,˲v6o߾/]5諤%/ײ,˲ p"U2,˲,5{n۵k@CX+b u]kE5z\˲,*2DVQײ,˲y.~hС%\)Yy0 ^xa|=|wmv p\˲,*&2DVQײ,˲g}B;6ڷob&Av$!aÆN:$>;'kk׮֭[犵,˲]XV*jZeYjժhѢEի)SDs̉Fx!N"g{qM0!Z~y@[W_}Xw.!/pkYeY֮+\Hdk p-˲,ږN6-Z`AիWzkb߶iӆ{TZxp[nrʳmcp|MbŊ-˲,k&7JE=PTlYm9[Z5דmZӦM p-˲,&?~4nܸg _mAf͚MB'/_z%Ƈ0 o˗W&7++3g-[,R8WeYe/xf֦%cڋ.'O:5PA6'|TpZeYUX6lXI x+[ly#q޽nРA0 iKXqo*BfWeYe{#y?&C?7xRPͶG}R%J˕+WKZ߃j=#L})/guV ZeYemu)СCԿG4amLvRJ*_v J%/ ?f`YeY$?R9 OX`moSu駟uUWv}؊]vekV#M$6l ˲,˲Z;w7o5j(-{Wn+d6@[bުKȄJZEPvVxQ[ O\%5{e$G[n]޲,˲]Td`W^TJ[UE;سg?>WeYeY[ѣGG|AD2ů=L2krw}%SVVr`ҥcF6lI"_uV 2dHxY~+ܲ,˲vUp #&On:jժͶݭE]"mF/pЖeYeY[#bN0!֭&MRHKϟ_=P<;wnDB2Z;T1o/Yd}m:t}Gy]brB,˲vaYh߾}ˆ# lݞ}وs=m\$1cF4f̘0,˲,Z͞=;SV5RhҤI!$BfB_cbo$K.&׫MbykU=[p oZeYeYeYeYeY[(~`n߾} y^OՂԌm]~}`YeYeYeYeY ]fMY( Jf)|B61,˲,˲,˲,˲,˲_B!ҦJ!_ŹMuwoloYeYeYeYeYem%@\-I΀ĹsYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYeYem8G͚5*T/_fl6fl"3Ʀ7>hŊј1c<,˲hիCF׏^x矷l6fl6Ȍi5N:E'N G[eYK]t ޷C ֮]u1UVTvmfl6fيi&ZtiԷo[o4i⁼eYU4z09uT@^3f8afl6fl"3ƦӦM;k7|3]˲,*V5jT{E<)s1kl6fl6[cӓO>9V8jw_TLkYeY^xQҥp;㏷l6fl6ȌI'?Uײ,*v6lXԵkO>!ž,O>|Q6fl6f1(%/;{5l0zw<,˲I3.Z(bFN:6fl6f16mѢEF<@ԴiS-˲%%-ƍ}WQ޽CO(Ɛfl6flEfM+U5k,ӧOTFhȑҥK3g,˲,˲,˲,˲,˲p'|QΝSZJ=Q͚5޽{Gzq8˲,˲,˲,˲,˲m9sDuԉΝ]6jӦMD{7jҤI4u԰mrƍGC=hј1c\eYeYeYeYeYB~m4{~|G-.kJyXTrѨQGN?t-[抴,˲,˲,˲,˲,*lAKh~i//K/oX޸˖-;A{」-ZP \yL˲,˲,˲,˲,˲l5k F5*{Wo_r%. _7\x^{s=7xر⅛%eYeYeYeYeYUH"9;Lmϓ톿o1V6-07@\;Va DӦM wP˲,kKWf͚|'4hPԶm[2Fw\U4lذkE'xf8qb*Tʔ)cmwASOE/V$Vtg^|EׇmmO?Cׇ(m9^  \bFUV-u¸ދhĈk1СCjՊ5iڴd=sno%`{ķrK{o>qeYe"{gk*U$n>Z* Μ9sѣGG#G AG \[6@N:J@'|rTD`9H"-[UVQmjbyY} Zl5647x#RRp?E; y7mhw9K61hqBN4)SC 1c}#\?P믏n\q.]q'otGqG^5 tuxMիʕ+}˲,k )S{7xX] ֭[mwD1kYۨ@I&Q5B)To>lK'c%K s{u)aYeYeYHc_xCqE[i oVTn~ñ u6R|ە={oO߱x.cl8#ײ,ڕ~X#ZN;N:V\}faB-̬v-xc1y9Za(Ӊڵka_~7nUzX;wn\׮];CRY:@s&?'Ou#0 ,eBYVXz\gqoO8;sZp )uW^ye|WꪫlfsJZ{gs_DVm%(&?m6PӟO~.m.[=Ƚ辠-K8)8ryXc1kŏ?x^wu?<ǥrTR+k׮e[%+xٲeƤK\cǸ׭[gkYe# @1cƙZTިYgL?MČ&y|ąEcH)6bJ536l0رWw[#SN9e7dp9fƽ)%/VL0on6vsfy8>C 9?ba"X1QxM0^vͶl!zŧrj̽X|y?`()rG9LX$҄SJZvL98>ƊRg[3Fܮ^:*W\F˗/de%K'rČ=sb9.}|(@cYeYN-@f-Sixl$%%f޼yyvqWqNmʱ;X/^ww?N qpa @y/0srM7ݧr~BPO<ltk/t|\w6vS&ϋ}}A1)bQ۶6Κ5AxIŮ 3mf߮~|I' .,ٻ9h6V% pTѸ}lC_RoO!faE\[LMݷ\׬Y94F^CF^9Xqd]vev_+@8K mz ľ:--fZ5=Tر:w{C9`#8wq~! K7('=xk|.ˁ8Vٲemmfx9ꞎ~_{lCeLmhJmS65B'~ԩ O0< @~pZ|-.Ř@c^ʯ OprZx*`$$ޕ>kƸi.^_oBb2. 4 6r/] B0~}e,˲R$H#0[Q=R1 %8 Tc (y fK+W{8tt"JR @#1;ȝ[ouV1C\%&YHv_@n4lذPò,˲v: YcDVѣG6|?}HYk8&O%SPvA[;Pk$+oVׁ>UfJUpu=9{4PUPB6ܨߥoнW^w ku`7ҘK]ˣ}:bC2 p-\ڜ#jڴih48TRҥKG>h0ugLԊX(G+ke"6.cX%N)? = \L*=/\y̲,)5mڴ^$eDM/ _H]>ޫ{G^{GyYĩ\}C?$ۑ54\\v#c~~C:9OD3 } cg\%XecL#Ɔ xi9 >:#ekZ696"Y@Vv0Q p ^zJN/x綖sʄX%x^w_0 1e#ۢE b߆ •` ,˲v>͜93RH?l! +)@/ٹ _+jmy#Ry;Q0~W\q^{L0a_=XIyNRGeX:w|e"xZ]{xL-; [^P/n,яs^Zuz3qWk{,ܢ !Kb$E tGRawR(5h ^\f3 pm6\ks£{AF+]w%R| 7 as5kid[pJ7y\@(f <'>kYe\7o^4iҤ%<:tSPO >CC_}+{8(Q^[O1rn۶mDgkklfkmN  :CUr8_Ґ1VNн[Z=x,+q!n\OWVm1(ˮ]FeѧOZfHöJ =:l?jԨh@SP'oslyϞTQIeDe*mMxy3jժmr.Pexy$RX{P.'|SxV`(Q"p0z% p?"ug%],ڞbY83wO&OKl }w>.\:y|6e:5 ?~8C=×_~2߲wv6Q1BI6g%lOخ.(6Kߴʬpڷi pmE+B*wE'X><Ǐw.˘C9DpNY(G&SHy+b;wnPnJDfeYVRn݂~4Cg xe0e,d1"-@Df@rL!1@qEf uFc`Ig߳g]ttp,jisq(e,gx0hܹKdsbR.:5T~/>`8/ϽIʄ9@:\@מQ_VVVoA:9?q~iٜ+ξ7%T@&fh/3,^2s͙3'&$&#lE;C߅m1ZIWYxj-gu+-c%XqE)xmݒﬓ~?5m&޶ZE9FbD]3>Wa p-˲{/׼yʀ<Ĝs݅:"[p iuRڵK)YFNkJu9go@Br! tgΗq{hN=F:ʂIy(ng/RI>'ʠA9V#0 L؟Z)V $޽{IQGl  mk:~jÆ ᚨ[C8@ lSbk+gpL;پ}MQΔښT &琉d$m#=Sxnٞ; `@ΟѾ<=LKJ <\A;5~&J.FІ'uH0 &/ƵP.mܱ̞> ASGydȎ8∔hJ!b(,fv0JO&'6L>s]ִ# dTdMʐ 0nf!8 Cry:k<bŊᚋRI(7V)oȿ.5YL,׿ bJ, *6?q 05,˲m*rt;CN1B:b-tx{Nf2LkݺuҁMAނN B$z``I2HBy2 (J[o 1L@ %`e˖ٞK$J y, @OXʆg// 4>މ%t'T `y%M-gd ( 'HwAU!%_PN: 1vݴ)ճfj|WV̘ϧ+c}d Y=7wlʖ9sڇsr<+Ω7Rо(..7egT3<G@_9L0ѳZRvmG2ö9'hOhirk2ORɤRf&IY맼$}M[#krEFKPNΤlRo'P8E͊V΍Y&%[}7{3tfxs|ld{^gr ̭ \o LdMXY+?nڶywdK0ye. 2} F,6r }8Ӑ2^G߀eH p،&H.oC6czqM›x3!жhi ɱpjIrw5q%ZeYU7Yzf;hp_>9MAO+::( Zx9٥1Ovm cAH:>3 j{(xw(:s+`H_}[r:x) odϲfuU<):\j@Uo?:XKȨW@k8x6_q>p~AXO,or-ԇs }'}s`:r!C2('5:no_7;f] ;d';2$~;ybܯ]۸[fq7^Z#/xt9^quoi@uiG] sS&c2(~[q7)CnCֵ[w;mq}Q>[om׋;$|Gۣ΁\.wh(zrSITw{=[r{N<P/'+x?YHe,^X"^Ѿjb7[nLQo6z_m`{&JOh?%a_8k5VW:X׶;$9Hӆ3<@৆l"mWҎԯ`_ID/𶎵\`?hۭU9߫MX\MJ*&^{UoоtI}Om싷,jҤID^ =˷)J@[1ohџ#v1pL+圸sύnᆋx'g92AفSCȃ{& _O~lWԧ׉n[T١Yf@V9{}Y=K?IKײ,˲" cu7x#VX`` + I'+Y X}3cg_Acss> ~dRt&l> OC׃I LcS +0Mque.a. q:Qނ,hù9uqN`|΅w2f:'s85܌F=Rd`K:Á7eɵu}tE.xovt 5lH\땗㚲/WLje^ V㥗_1-Eaw*/<7T1^8gUZʗY&kb\i±kkX5s6ksSmԏkNWGnw/xW&|x>g6@mt=g妛n306N[6j30g\2Q{6\q c jMcܜ3 M,G g-g=m3gje'kna poduuߜj$ur~K={XqܶQ.@xZqe*[+*V}YRn*4,||rQK >4m*/MOzonܳOkg'{9RոZrlmY/-Z7*0< lY/7siP9@-ygzd㥍Mq-|8\G F*WCy4Ti% Ѐ*?9JA@C1:2J:A 6xYFt(,gI`&72Mћh$S,#롞/N]4yk2@\A^3W },̀^12u2i[?> K}l2.u Ri|GiOw׿^xXY3F{e`>ÑbrX-RysAb :mЀ3v]vOn ]ƭ=r1`Wo9؄<°p _< 7x|ĝ2FpPTm6ʯWwiq46Hh#&X"ghyi@[N:*[ pvxQnce5!7VeddƾL).eQ%+4|mM̹TLQ?ЦP_5I>UsP*]zK٨?M>=N[ʵ0Ulğn\ |fxj٪T߸Z06x<=t` XTS}[d!#S>`* k 1o!x% D__)?PשS'x%n% R#ʎI lkAɲ 4uz,IR%@իrg{~m%x~mv6˵43bo9X3:èn] ps5H@3vz3(d.r1XX "vUiyѾJV.\G{A[}rNd=xҦ(њr?pLڔ$bA\ .m%na A UYHd,wŜMqMjJ1r # fm'v^{=Cp訣H!o̲L3y9ؖXrKxI/_|OwK&+( CjVP!@s @c'zN S4B׫W/uekkYeY pSI`AILZypUtBN[` ~2sK8ɀlذ!q9nWXnNɛ"O% ʮ <^ tU:tKyHl񀥙K [y+VoO+i\ \WyG#iⷪ eYeMbjV>K4hQx',1k*`} :w$^W@ ܉3JuH2N1np18$@!{gL85 he0{@&61gص[#З CهkR^6%<tvxU7JC=s/R:_$ԅ{'2abd{2;1.% c{l2)KhӠFԫ΃wnN 9tp@&Y~֨kxy]n(Ah ے߂~S `ygj&vYQrt7Gm[eHK_wRomv"0י|ʗ=Q&O+$ p! Tu7isDŽ:f`/YTowd Ln$ KXުPC( c{+䥼#\=䓳 oc"8ދrI9ӹbŦ= TNB0'!"ԏx ƪ/ /eYeY2VA ZDW`^U7a,RK%:|W%a9Dzoc}URٍ.@Ck :9W޲z:co'SkI@{/Atl4=H?\UaKŀ}>#˲sBy)YU.[92Ӝzwxʪ [c,5 s6r.@.2a2߇<;v>e^7cuS;%p1$%ި6}=F f&lYnoeZnQ!v if9ajFJXX6/#;neFmX+m k:F]i<>X pi 9+;!vv!וXKP\b.e޲ j߻r\'Sv`RP` x&ҋ52xXlVӷ6 ϾГm3\-C7d-%[^Ÿ^$76m^7^4*.l?B3x_tfV?PXC8N!%17VX𶳞Z 6V]$eSN9uSpk&"~.aBhq"$.JwO;j J605,˲,kGo` H4b@<@=:s4bf ;@@ߜ"2M;%~* :&&vv:)ϵLO~/2)Κ8!eq7Ƶ:Cwg+H;_ ?zdk{C9sڄqqV.! p p9:F}vڣ|sm.xNp9'F;uq4oЊ+0EbHkզܡ4e{ԓ Io&XhDCu_%v)c1I]ka+KX;(޷v HĿmԴfl/2B(;dT5)o`\;rW5Mws={#}{I -AE^J͸_@bWt^ԏ /:߬j #Xa/ [`?vƷ~?"O˸"ЖZeYtu E2Hl*i.]z$ &dߒW@aVʕ.(rG//WăcTྕ K ݫz׉w>q\! KX ^eB@Uc4;I$ ~s_] :+@| '&5'FRtʕ& (p/Qjoը^<)זwnwp7,m:$/kרA2,e&|j۰~~n3܂\4%IdLbuX6mֹR~+z5su ~&mJA p n3B{\oO" Լs78|h{YWoԌLۖQB;mq%1q24O-w x~~CBײ,˲&1pG PQW[ J($nB؈jc`M:~.p}UH&!u*sP}Bp0RJ ޳jOXAti3ɂ'Q*OUjK)_N%b/Ó1)x2Pѵ\x<<=]yY>FM$ٜ:*G_eGɧsݘ}__$jժin.ɼ[}gD΍{.GdF+rr . k⫯:H kfؖv>!<3׫W/e˾.K߰sw(Z=q /]~i p p-˲,.5 n:@Ab42d[- k;,T L& ԰PI⵪z+gxf'1SX 恱z-.VH Tжg&c>s}%[IG><8cDvsiq$4KpQp9 :]dX+ayU=9}YE|Wp:gn7ޘ pCUHX| cvF[=a ;5dPݣerZ0Z}cG_|п;~eNm%]tQ|/p@te}#^ǃ'x"oEeYe7 C[:&Y)|A{OZu$AdjrLU1g .peQ}sHGH؆z /ޱre 1:D Gʩ*a`)@*A'  @P:rͼGuh X+ڳ\:pqx 3pµkI&s hK9W˼]l93L a"u k>8دg[4Mz`lܮ WW/W~Mنh^F6\\kc\x:ٖܴ7hNsw> @6|r9b&'Am}=iov ڇ癐&^Y<^0jJSn3vn6lO%@JZv~NC{ Mg{hGՕ`kQ7 r2?` % ֈ+l^۾Vm:綅pG {u^nIz+Vy)#?cN].0$fsFuإN;m@/'IxM2 *gh_ WDGJ+&oS$G띀T5(}sNtꩧf{nk{~._S$r;ûDin!$S,Bh'z pQN&o0P&c&O\\˲,˲v@ d`!.OԉRa-o /J4D2&-R%{/ZySm಄NWz,㉦r m?L L8zV rщ'q/0ez}&pfs c{HCήNj4aÆˑ1xRwr~%)πz ಎ/(ls'-j 0׹ R_7T1\ӣ{9mLnF>h`(`5 qr3ּYnM{Ly&K}Y˄ `mϬe0EAbeնA{F[@; ǟ_)WUPH%ҽuRZ/?B/!%uUj)G$ ]ܲ$s13f u^Y9y>=Y p9k1`E |$J fi:zJct%JLvɿhUǓ&ՇeiX 药}wAWa#-\"τ'E/hټkSp;_Kҥ $9a?G|@ȏ\Fq^;F"Od+H: :|ƤXuTS6ܹ3O9E(z;q.cgbZPuꇄR֨^:oUWn*ԭ[Q,S^) uVࡇrWm)B0ԪU7L[4h=:g@\333333lp 6#R2vug.Y7c_B]o*L5~Ò. @/uJ)bu}@:( XQZd c@<,+ +0) BV_ I p_B!w⠎SjSeџ|4,Av, %;7jS0f_BH@Lۨc  Wn(2|hO<}z/*A;DsC>n*G%qqџvHPs·tv@I;=EywS^?}5e,o:@~{ zCݥM&h35q=d i@>9EerK]{+j?m vpzH~1wk ^nI TRJ}ݗWuD3|'zϣTeL՝SgTԷl>I)_Rŷ_G6!%ä37RO99j@ YcFbO@f p}N./L8Zkx]_KoЇasR6.p~q'vo~D!'D핎.=?{m4nX~"fEnR ㎆sG*o([n n?&W\jyw aP\LH861IX;VM.\w,Vbd|g\E pj[\ZIoŊiJ} 7p̲)dT : 4^Ǡ:À(YthJ5Zu{4k7{ k5` g,:  YRK H ॳ Xiݺ5^o/xu֪sO(xJmr SҺ9ja'n\[ۨb}Rks f]JS\'+ Λ}@ ܮeڐ'lL='E.!ȏ_KQ%.=z~a7ಁyڷ}>5K!(_M6ԼRdMB]~zo?PѫtwQrOX.z?cp{e4u ._[UI?PLM>cyrl<G~ZK k [&uxN]^L;\@$Kxu+o ?ùZ7ʟT?Og:WI#]\ಚܤ ?eBp H^yLrx΢NM ,YIsLO Հ\9p\ #^`KuvLiK1p)ApN7h#,S[VX|'xR6saY\@'WܕA~R!<]x;u>A=Nd:,vx `A7HAKuGKcHeΪūޏ[e`0X_=э\E'u߀K<ɎQl$ =T Չwx__nJ%1}+^9 4t]k ýAZ6^ pGJu!q!4?aK~ di2>@D3o$A7z A{>w]'Ļ>lag[88|09\&. u Z9󳷝[ue,ձA?B^FF훚<9\nr-w Z}`31V˔)3^w!c+k=@&S+%l(_#O9u+=[_6}0J_ג}T[ʠUQp3j_fwq\LP_>Ey'+R}c&3˴%۩r xZUo#P5kffffffA5> ,Rʪ` 9K EFV xa<]8 .N!>l%AY.B/[^k&:v`};e~:W zԵfK w3e| ;D@u/Hf8 NUg:ԁ*$Z; (%x CTLK{MܘޣR̕@ݶe#QV~3I3A??/\(L{AɆxpW2A>[Ϫ}lȻϻD8D 0 P|<Ϋ: OսTwLrQOPh޸->ݣΝagPxuR*WuWw' "(XR]q(WNZ=ݐo^w~aM#}TbOgsJq_8g凖됧?OiB~+)g.={{Tj"?z9WKkJ5kL{:u/ 꾦z|)S ,8+U_~qFzWԇzUr6! M@8ˆChsrw0]"1$ӁҙtL8bSϱpNK.\X.m 5333333b ܵkS=٠3BPXZ>:r(π@U|}y_tQ_i]w|K` YZxH wZ?ȧ6~@ ,  sȟ 񥳍ZA3PcD=v*qPh\H[t^IЃ@BZğ{2 #=|>ILxC^|`CHA|O~3_ +gygx]uہ:z:INLW UAǵ5sAبKxNBd^ONJNvn_P2=[okQGQץT@8:Hp(.T`?ԪJAY&)Ș ]c2^z?+w A޼y'͛7w ;c\~}Bg&~ Q*\7oԫ)6WsāL]P 9]|盓>`RߦʣnK.\fffffffQ0fr*;4E3HQ2`: &zI 7(ąc=x5q`w_ԥċ@:8c=PDP! m@<ˇf@OQqo& Lj\6Wfk2رå|;<H1hׂ.u `y'bUH}7DU 򼫓EPS/IIq/KRmz{;iiqeC \#3#bn jVgsQgŅ  zF`˻ʘ$R ďswVa7/ĤTju7&#\ 1M\/}?/t bMQQ7REmap5߀ 1-M^_lp9;ٗw8#f‘pr>p >R 6n6SJPntv`\  e+c](xM){168k۶m{Gpqtp "%q&iJ}^{y:˔YV ,53k\`+c3[/L~owVTM7ujժV| \3333333fl}d"|1-s@/M\  `Yv0`;e˖ wtԩi+NMz6po_?ԭ[_-< ڶm\3333333fp-X0kfׂfiÇwFIuJɒ%w<5k׮uX' No|vڴip 53k\3,dG;gΜ`ĉ… Sڃ rpo ,YbB0\'qYǎ͛7( #4i; Ol]v5kffffff7qeBfw}A\-_>Z0zh-ƍ1";K6=wtꄜSo@\xݍ78ϱYf.pذa[nQr| (`,{\[p| 2;hwXܹ-a!fN˗O? Ν (iBv+W,fS]r= {q'pB /8׻worPx W.W\+ϫ?p 165U$A. 5333333;wE{ςL{WF [a9aۊYG|Nxwkf暈 rï pm,dAPYۢ2Cҥ˰T={fhJBmڴ/Kճ~gAN&d7[|6|ܳ?λQR% e|T{;Foj9}idBV-lh)T(a`p_ `!K$9|>taϟuwBZީt;NMđ,XȂF{Wg?NR7ݨo߾ >Ӕ-sg.aAu#W_}=7\rI1DjժwQ(OxZ=z׍俼gHeVP\3333331-{ Ɋ-Z?UG:T'lw ,XȲ@Sۑ7ou%=w9cBk(w \sYgmK!z>ؚ%J{i!KexP/33wN*\p'O[==agvW_U s1*Sov  `_{e\pl,ӌO fSefffffffffv0&7 UV';uᨣ FA>lӺtbMӹFؑv\nݠaÆr]fffffffI:(pݻwn@;ZJ*AʕUrSp`!+Cn u5AŊ]y¡r>B]=xc ,Pr ߥ[o߶m[1+Gr)abx{駇M4Z0*?Ta:u-1m֍#7 u[JYYfпiӦNI fffffffg/SHhSUW]  Y4;ʡ YTި, p(ԁLd2xUū,XPO9a3re1W)jֺu:~\rW^yA=?W^)V7h гt_~СCz*I"(o)*663kffffffffffffffffvPͫ  Pv1eAzUV:|Om>裃=D9`o5;YmԨQ /k=ҥK۷wi9JQ}ѡ &)48-KN޽[暙pa,6lqЬY`̘1n\Asn֯ͻ"EZf_; Ǐx`„ y,"3kffffffffffffffff&ӧ dy6 sJMJ,)PR]pj[Pp+Vpf,UJgϞn&|֮];駟`ٲe'x~Vq|@SF;wtϞ=;߿0|`ѢEffI\3333333333333333.U LOukr`Ϗ>ACm}~!>}eY f,Y-{9se˗//tw?餓 [nFn;6Xte~&`\t%X`A[`.&8]t~ 6I[〶y?llfJ3kffffffffffffffffEqro߾AǎqnݺsV:SOޞqaƍG (SNygqc &NL42, f,F6'2dHosvn6m}/{=q6m~ҥKT믿~G}t?DZ>oqvZwn2 pC4s`ʔ)iwfͲ13333333333339Em{}9>h0qĠXbeθ{?  5j4B ~qz_|6?[`eY\hDKFE]|O<_'{n%oou}hܹs{̝z /0뮻:]vu3Vs?TBEKY,n.] .O SO=U .o:ލDo9 ,s,'\&xzA+_|PbGqkn9o߾nb ;oX`u.䒠e˖΅ˀ`РA6ft]ynF0uq(UV~~|dCժU-Z8P?d!wo *U*}衇;}&g۷w?y'c\KBi&w +N?>q[ȜocZRJФIW~w|sΉQ;u#U^PN[nqeUzu)fffffffffffff&h">J/b&{I'n (6k[ PO\AXR `33̳ phF%\qAZ:0ԓ(?-W_}aӧO ┚ؒ%KS:xpE2R/rpdBW1-v뭷:e%78dϜɶ<޽n.,@ y 5،3K/uY&ƍ5ktٓ2,Żzw4>&)Cý-Z긑ږ(Q %.&zML ;=N{neK}G333333333333,4mڴqQ7a0ZZ=C8@lYpeTrJJٰaCкusuu) xU[.cx€m6uPiJXbŊdn2d;`>l@^rQmATT\ >C`˖-\Tm/L42,,\ 䖠JoKROҖ: %#ߛ7o@ Z~xW+sY5?J*KFT]]Uc+qo9{dzKcϑwl|& |@Æ s2k H_x7+*'+ v"p[:TbȠ,Bϵx2e\[tg~ؘQn|.~1Ӧ|ɡ@wy@0.9PL"ksm'Q>.&R_N:~ENS='32űY2?[ʲ7 9)8,X@L<xp`ﴅCY4Y~XȊ?#8p*ç#&5Y] >w& z5]sp \XT ,}iH'QzլcX?P+P(QRW|7ܹslz(?܍O*2xo(^xȵN6nCp+O ޺zPnetjx6ttq8AR$? mșa(E{Ŧ5qɓ}2VFԪmN;U"ɓ'o~>-ԫ(Fi +W2^SyRxT˩5|ĢJu[\Qd;1 wSM b$-&%F;&[񥜓ʎbмNA*gf|r/`9~@ [BzFUВv5IK^ 5l1zZAfeݧGud\p(:n/uSma!<ثGhwbƍsZ7_h~[- rL3|wb#V&(G@' 3`fv$\-~L"s#Ǎ1Da5lT-UOPQPrDzO9g}6;rK}Ze;lʤ P11ĖU~|{) aEZRbۅB ы2# &u+!NI U\r 8n'}F+ UFF &ǕM7'g;ws@7P2>NEI~otK@utn&N B~KnNv@3 >QęgL`Fr3#Lf +7J,+.*c\rP}٠,*oߔ=ėwwHqpw!x&fS)JĂ4Ry[8p{凅C Yg!+>#apF%G 67uQ[ ZAt蝹K|g|H3,՞p(R\ {~UlڴA^ (EzJ?5-0P \Δ?q3' O?\6ɵi9sr1U~oYj%C(F8 q`v_Q1mJ*Q~))RqmuV/:"Jo.\Ǣ&u q A}pL2S+R:sL7CCǑK./9qBIlDT<,duPG꫖Z iզZY8вu[ܺU-[--XкUVK,dv`FmlՊbp͏b3t,h1P0$>T=ޘ߲EZ6o6|0|EATH;`H1h, ,fg p$c s)x Z5TYYX9 szHK`́" U *\k;ܳ{Nt@`5]!np` kL`~VQ}A ߌ ̒KW\q n&В/V p7T<K/ V)]|+*`$+JN`5[@)_@/ $~@N <פ.{!0Qo "6N,:`Z<_+ 8JxVhڥLpǧqtyO~\Q\ vept(a)ϔ;(sY|%<7&ϗ{;e2O9= P'?^&8AE1,oߎ;s>k(`0wǿsff X|ɸ^"Ͱ|)9_"M1"EYj] AWf8prΒ(f8JʎY k1Ãnﺁ O*N%n#g?8k黣2 Pe@95.p8ިH4.Uϓ~gBVuƾQ#1D-^w5f RdaW3],dIx@Ch3\NԣB&1̍:<>S3<(w=p Wp>!V"::ֻyšڽ|=R+/wop%M̎h%딄R G-x P ɃS> ]%Hbz.+ 2EulS׿xs `%H4NgOOK F6DlJ:v*>f^K`&⣖47hS|JQ8M็y#(RYʟF[pE&P<ހͫuSZtSo(ͳB ̀ T"o`mvYIӵk 5ea@1i$ _跞 ^)j.$NќKj9UV*2D;O˼n *&K(%3cypCD?N܅T-?\PYT{r P'fL`qxЕ( 9v9\{VSC,ZL骦2pN(ꝹHݩ!q?͛7%~j{z .@l/ `E+0_+X/KgvϺpXfιܗt7)vP s%r}7m.f5~'7zNK= -T PLu/bw$,}UP V̜ Y )=X0/ WhկK-LX&lZNبY=Um `!k=;U{.|k? w2+'H&m:k ,^{}G.\MvO:y '>ձ{8ҍ~ʰd {*X5I9\A|afv\0h\X~7#o0Q%po^Ԅ/Pr+98 VAHp )&O5F T )֐ w#j瑊Aed| om>U} ĥ(  6Jk)!y)`Ք)Q@2u@Z. E'NÀF'ȥ{`pMRSO6E 9-+vg uteg .+/Ww6s/B4sm? _g=K.,nw8*cc+?'`>l׸_;oKH| .J7, UVP&T\viRb֭[?Ptij.@ykS<d }Q.D k-;kY@Z}J Yz_J%_XZs6p-8gKfU\B] X{_([  ,/X/W[X0Ci/KS\-YW3ɇV-<<վc9@NV3zV;ɻ\(#;MvRq:RU[t`tnuKNKMW\qʧȭgA0}Z+`6DL`.Sm?!RLcf J2g"RzXۼ}Ϥ=ʋMi\E<ퟸS`9n0ٟW]@yF革V) U:ﯙYQQU1u`)j[f G7x) t-#h^m@[ u'Pc3I5u9AKTR TZ'duNRxiBQC*eCI' ]40']T!P]J$ve OM`!eL =Wњ(i!ant@͂ +7nnNؼ&j,ddYp9%$@p W ,}exx+<\!˶3po4kvD\Vz|P=1ZKعӻHzMs $zNu/^kdx]<ӸW絮j1|W["`5q>c:U'4i}p1L5ePEx5Moh'c@sAy{؃lc%w af ]a0 @5&@ZfXT"rW߅o֍Ի|`)3T ƌs*t-UjpMA9Tre rgڽ{w%Z{@\f@,]8}&-ǤVkfA7kpQѰYFQG}ܙ+pjCƄ&>nF\3Ym@b K -=gWDj&SKZLT1L'݈ W{+p 땂,x&||̛we(]vp{n?P;\'r(A@}O`YqSqdmLU}8 p)p(F^< =ǮQ:\~m۶p_r Skϥ2}л=zEݝ߬'Zlf/'>X0SL]d2E)]ߕJoW;\S6@/ :\Cq PZsUq] =. P z-UݦJf R/:W׿th.gEg[a61 \|=X*3A*p `Cvư^UGR5խ\3X^KIx2{SWɏh͸ RBl|o|X(z{=1bԝ\"??. cNձx쇃[1V}KTl>T3/ 8KweRMxcp 4$ vn)=o~?"@oBmvS F,؊,S@eRl?k|Sczg BMgDŅ?XO^UUʎ@[ p{YM; h>~9*?^~|Xz+q (uvafH\P\+xsnsN gkŽ;:kT~q|SJ 䙨;gǢo|Tޱw R7~c:33gb J\fZ5$uաbGS ׫ Mu`lOԭ㤼]"Vǜ&b\Gq0lҡ]$ܹ:'KIt8_A½_6ff+WR lJS6*wq?5̧.4j5hh69tڸR8fxY 5k\3;Z׭Sݞa nvTfpy2$K8#?GҴ.A\d@ 4TIZUz~5ߢ,j ^u*. P֥p%J࢘1ǀN L|Rඎu}ĭgֆM>dnG6"K 儠קR⾪AqK!`S,&j;=xL`kདྷ/Z׉?PW϶)@c *. pqMr nפChVsS&ِc&|v_>< ~1׮93>KVYZ.\,j ,ȨW,lZ.>nY>Voyp١J?FS[,T4j8nH9mhgblTOf&xUV=܌o[QŖ M\`0 \@u sm=%pU8 \~GaL3o7D n,e7lA?.ƽn\'PDޟ^̘֔6M~Nj| `4q#B?W$@@i h#~uAMRU: {ࢠy \z(oPsO uʷV g(%/|*Ϭ1dE @Yg5|M0`S.kVY V`m )>2e2 6R{.FKkbuӽK[<˂.I~ -ջ|dEɃx*pz7L(ti {o*'_)cͳl_b ʡ^lD >UM,-,mA58W7]_^1=R^8UuTcy/[+H.U3Wl^d}Q-c i+hƇ5:U_ oUjYF5;0_514ӖW/_qT1塢e6fp Z`nukwS.7_p f/u,PUViwc6R4 Ng H&:;AU- 0+./:ğ^EqݿCTK\$e9|jn|6@pSrnՆSFDk rw̗r\@hj.PP4U f&`RNfs5p7rG\]] WZ_ݽ#wF,sf\@8پH<Yʞ [ |R>1AKpׯ_ -. (^ P`.JW />9n6#B.Z_(gyt~5ΖWRicYF{59*^(tQRsTa]}<*af1( 3"5Y BtQvĝ >r5kƋO%c\  >nQ>v8ukH7X89`T_Q r)?W,b<jC2t>t}˰A@(` <w_x|4LDP^pa w1ށ{ f9c+q 0\R@q=:~ @`?)%碌lbUkzDn\ jf@潩[A2FnT0%:!T}~iD~(ɽNxny&f.P@q;;`[ ẋiQ&X&_~q ,룔Ws^'qe.,'!Ļk1ƻ23ſ2Q]Uet֭X9s]]ݥcy:R:7At}v7& F][)i]}!ƚs|w}iT oۄ?q qp `lm)_GTp{,2-A,CUP)`d' @ ZTKFvN|P 2(+#'%6e (bSr MgHߠupZs43.q`#wkaDe֊"z3wk^P{8ŭ"~fq)o2]f0)W~9]br{xQ hu|CttzVx(iǝՔS; ߎx:FN q}NcgRP?#3"UNRo.gx{>Q4^hqjfQQQ@R Kw*#`* \*J~>GU v)|*A-`.pY&wr@,P̹X*y\?MI;|zD\ 5k\3CuX"_#խ5kv$" ܲ{? &9hRxAp1-vQ ^ R;F;,gRJbdu**\~W>RR jUX* <5O89Z)OqGi)4O|w xM/r\j@H ?zEO ,9+1osczO,ȣS^Ayz~!+8RcP@}TJ́܆srОr;i*_0*{+}%;E>FyfW@F-?O/Lzgz?.tz hz\ˋ2"T/JF7+6+-;J~\R>Კ>3M&{) |9;+:/T),uC=1Q 1Uf'8 W .@eM)x9đ}Pr/8Q{2no_7yo@7q:4 ^V+^rCo)6x1e8>đe\7=ryq/ q~T4^ċt>a܇"||d ċBZu%KāF3PViR]N2$Op-JKD5996Z΢~}G'%U= mKb# ҚT[wIH(qFD߂6|~])8۔}3խ\#h Ps4=_P*=-vpqLQ=O?L`i;QԊ1R{7;s6c1%&kTRYV 0qtS=ͩ)[|}fTP*B[R C/1{XJ3RT >XJo ir( %3߀IcqqAլ pmpJ.ෘUW}4:\QJ\^kו{`S";L*j@ytŏVψ4 ƆXn䁮щ2Dd\arwWyw*-7>X'!m>ݔseMp ˩Cse|MyeRޝ:fO9nfp=6)'*g8(^ɷ>W!VRZދ_gV\г eE@q&+^3I&k:?pBG`7i53;h2bƒHU|ҀViUpz %";?02jxT@S*FX 4)<01HyTH4$fP8>f\se-cQ@{x4O(G4~#]|WĕksK~{ct\7'Iy Ɓ4$@pMY@.rxO\#.lgjE}tTiICO0!EFEW8m@|]ߴ]FԍU/)ջI360@F}JQ3T[w\Éo3h}{L{k7?xHNuVn K-`_MukH3,3HyY(xGTu{Q#x<^pav1xz .pQ +.pVd1_q M`1uY~b`%h{QB n;M(@'PKl\+ iCio, (M@YQrm`(QL| F\A=u9:*] Klw3ޥB?7;)ǣ5 <.q^oDTƱYa0S!8_Ix5'"xw8<%T^\g 7'sU~P2%Rݺy֞KCX߽&eJW~rʱ//V)˥nMuAc4cl2eDYz};TҺv5 @|ufS?XDY f&7s Ź_1=[=> ch:IzNi[yC4a '0;2x!!(pH a*Ԯ !E' 4PsπK~'}ƘŹ7FG+5IS'ߐ_Xw"H#&Lp~sJ5aO*8ܛh<l7a2 > k>Yf6L=EMāM7K T>/&ii#D$T@`:H4{fuޜpI~M-k_3Ν33>aK: 'VoY _rڴ \u 6?8~-oYp]>9?8$⯑5J iXp4ٚ lU`mR>ѶE (*6aΝ}a⍿!`?kW9POӞM&.,3%.`o˴U~)kF|7M'=qƐ7Ltyw91cSXs\6b3 )i #_UݶJ8p L['sM "ǡ圙. :]Cql[ٛNw>ͥ}ži?/-af*YvQҷVԝ)JOH>w'n.JYTԎ*/&Sc{@O 2@;9W~lŘ_X5ȫbAےl4+@q(rm+l$r3@SP"A<{ƃđ6n \c&@p916?1m&ϔk{e7|?Z%ʧe4.T>R;Bځ jcbqi ˆx.;SJ/:Tĭ* pN]1n41:8cc@` p@~4n?ߧ]bR^/ 8c 9)5ca[gLG1aL>/bzě砾ʅ4)PN.3ʩf?>*]yz'PKQê_w!#|Y#-雑Ӈ$O z<\w,~.;]< T։gn=NlF^u3q!*E JGA|LBʫ YoR x(Pߦā6q+̲28cgj0R%*FM08" -RрVj\O\L?A5UQPBuު1%9(ʀJWIG1/#: X女UnpStݧQo)N\]kP#Jqk̫28N\q&=P,nҿPC_u)4Fe .yulSg%>5QyphWʟWIJ}UqG3j/7( 0sz8uė/𓻆:'9xqI:>pi#Gs6^3aѣ¯R.?6P՛}pđqaD9kg' K~upԘAᲕ3{-7껁ᯫgpq7|~nɣ!#}}p穣efcr>K:#M'y7+. o~jT]c`M:ԗLLQ1ҤSQaU/Q]_Np2^IicUW^UGOT<MTPK10"Aވ#R4?NmI]_uy#m]cFt􉛟;M}`#RxV{Oiؠ#+Fp{p¼5o}>*|P!a~Y~2ptXf_ qN8?/ ?UM8d`'1L%i(~']5&[i}ӗ>gő~7ե->]sOO}=ݗ~2({5 早ܟZ?'N=z-rIW9 _8l2e@L[ߌ/C!o-AY rFzig Hx)\%/`gE (E%Ƹ6cN50f9w $<|hUCC,PM`\ʤED4`9<?*} zK!{QHF1IJą~}>R{ro60|亲$l"NQRHK91cO|pQP;HYi]3b D%`^UQ&oE&͔u150[ݗOc0LP[ y}Xw@/~Oe~5@(yM:#ςwgK(P"N,"zq!n?n u1һ#qw1cײ{RFąljBλdY\*n)*B x9o^p$zs4tP JA!d 5*4x%gLPoAD\tun8TO\|҃Po`旴y:@KH]tL|q^ʃ݊bJ +* j+e)ܽU%1 KPQSUeoRi\GJĸ7y^׼O<v)kG̪{cbL,X;(`أ1h711!{ޥ#tTDEw3˽[`Yyw=g̜3{s9sT7\*݆Y3 ܒ?u_U5ׯuJo;۵U3~*W:Fd;/jc[}9rKC׳]8ucAƌ ߫_oņ(1V3DsD0> ʩ̛>m1e%©BfP>ȏ\F\*XP{R=w-w:m%\E7ly~:uݛY>o(iIOgH/\߽UVMʀi^$i*s]˃k>:\|Ck6no:y;svN6^٬mAux߬C? uΊn*c|ʬwì{BmWN 7}n2?[ԯ?fR@5\tI Pժ4Z ͊E62%ЯaL^ (U0@ `U(@!VTA3$|('>&x,Acy<U^);K' Z"n3FRT{pbDLH1O@H"hIQWxJY@GUorr 6H~7\˵wHPF{RF^mp'Oxl'=$'K{[ๆS8`` zkQ*6G:ΜO^TzuyRv_wG9.-6t$fu\+V>Ys7xi-Q|G&u9ڏ4* +=i0gL$iƮbԅ+D3GG Ԁt+1@Qp:t{ rj^RH 73E:\9pq~hf+ֹ괷r-muoFX1P}F=~3]}=Sw7ReGi꠿ѯSGA9_c~Oǯgr1FR/ e{(1ڕq]]{9mz\OH۷dB ں}G*vFҨu;jR[.iv=۹Z?XQ@>myX&̌/ 2N3YnKW#D"9}ޱS(Z֠թ۠q_鷖'שZV]gV\3ڸ**+y ~wz:QɻAʉpjħV]z-5HkֱlP7e^ 7A`>f$evoEIu`H)'sZ\&%0@ xl6~'A %u\~ aef\O̤~9 ߜ\ )G {><_ėu mx.t'?s2_ߔ pOGw>hr7>w9 ߜ$Md.k]㟗3r\`~W@p4| 8 P&0䆗cr 7( p>qO9#ڛc;&O`Nhs>c"K]$jGqRqoշ^h"c왇MnpHu V/Wf.O*1fxЭ8y>u0 W=h3)zڠ@BFg!P8H:GԘmSpz* tr|<.Q9'Nvm<oՀGꁨ$#ΧåSe cby7#O;(#18|hh9AOt :\{PtX ʣg`Sӥ[})ߑum *O2he)G-Y xd@u Z"#mfZt׮sSW[ѡuTܚYwb>4ME"+}k4li)oVe;&1"8UXAt^ &jЮ޸`.X9%wsBSuW.Z8ֻgCSI+/1DN߄#BrloG>~_le,Sp/qj p$s*N^ƳK`lYc=*L2p[p<#CYyώZ5Q^~1SYY @րX&X5JL[ ݏ5dh?.q- nGQj61m5f|zqSM:u8ԥ"V. 0bK]>v 0ȊWn.mw}W9F(Fm{9=]@12 )nYΦ .q '\wѩV+(zй:2:/RPU+vh,` xR*/RzmŹy6@: hLspo7 &8:' @XAѲ >s_[xq;orZ]3w5#l=h@oCEΑf-nZ5&]7^n2p]5,wGݐMܰA6;lL_I|$/4n cATZ~%K{9+>V:uoajsE*ת\| "Qi;ʆ/?T71Kp@ WMnMPm3rmWڢ .Q1lʲM<8Rvce"SXI׺c_*K>{C}qٟ2v0˸kH1f%/m`Սnں' `' 8$Jv $7n+>wnnߟdX߼SdM[n߬c~%*u[3Kdۊ\ [0y.ڼDؽ7}y6d\N]iM(S-Jlnb[U[|4&}ﶙKZ/;ia<`hH3ݞ(D 6x /EEzXcVT=`'xX D@{`1wd NS4g&6/0 *R= άe1\<$p?C2`* 1_N^刟Ejrs8i# 5~m">1%ОM-Căk].uԅ~  ?|}/^7^cWfݛ6԰~u.u:lEv.0L9$#}AvzٶScPpqM6l r c#1{I*O?_T%˄&}7dƔ Lc<\\q%"8I9@d*N:y#RV廎8ꃼ6&McfcyBqGyƜLFBup@G ٨EȾZE:IKudYZ6@ pΚᧇDrMsVgl ovm5=X6CѯmҾ]$ rDZVl\&gml7X=?kJq!57x %W+d`Z/-cM% 2ܞɃ ,/30 :Vӷ [ArV X--d ձ7 .0([D8+>:ǹNéE XS{Ypbίe>#3 DAcb]N h(ϵ^wV7SC;הERG˓Wg Ҟ~0UCmc}TrԎ1WKt qe^XuڎdZڍH\;uCgnO!.XdWMHrE6j6Tco$@$12 @]~wQF`=Z16C\ʽfSkMڈp暾ZgYਖ਼j|dp(V?9cs4vU+\XB1"'Kn^6& ઎ȹϊ4eXXz1؈P:]<$;Mr+nQ/';o7}[_U}+Y8he7J-[nG7GMɀMFcNtsUknlW.@򍙼ȎErhdנ,Wn{)O} jrQSLnxSR*R&"q ,kIZqw:梏+:'=oS;vk}5`+Cv9dD*71$X.+Y cb`E xР$%l &z\9`dNq +n V$ЭֻM--QDܚci0$׵t:>5;ԁn@,ѾHpj4lf,ܠl<\>^Q-Yg!ۛE>c |OAXqgvh\ .GvEnA @G1 5Ig]&xe4Y}s˯d` h+[O|ٷHnDkV IB%J\ 4;p>BzaF[*"5]&mv͂O^E.+D*Rr\s :%!šΠG7Ѕ` b"cX)Mo6$#zqm5.u]B\R[RNwh5f`BУ"si57~شNDe@O&OrMEQxOo"py˚4I5 O3F wǿ< R Ym-]0nAhn{f=(OLMz'K\_Z26|irSFtkkR-׺}ټw{v?Ofڀ10/I^G{Ц~M-Zr\JwN%!@-Iprֿ(Rnז$91 )U.'|0> ,F3Ry(@RJhmpܩ+ еOK$Pfp<'S;=:/C\U{[X%[M\Kp8%-`͚NY{[?җ*[zT];G֤rzT9e}Gm{ErlApݺkR}?u{!-5 "E<,e@\Ar`X`Y414;CFnpc?dBB!}PLZ?̲ }@_ҿi 'U+Gf̋dx>(!',hJ=rHݣCT/ [)|rҺ="e-sqBm*)xp,7pw$ڕK pfnA8 EZKw̕V\ڃFMZ`|d${E ?7 Ld|. k#W1DTC- ,X`S ( nUm| 0$T`#3֊ pq'#QU8:..aY`2 XsMl|DP?) 863p吷3sQ9^3iEᣄY7mH/]hMbN#x 4DFm<[mu\.@Z v8`obvrכ5") _ E.8wYwx{\>oVӪZuhc-=ݻwTŠIH&>^`K9^݆3RQQζC:Ty-Z@:%vB6;!_~cǡpnrг]\;$)ln ˤ*:x=.0qgѬ"im6\܏2n|\ i`Y.|H+Qű\CG.dnf+ |=I>(y,`R}-鷫Ufӭ16zH95{+Tר;Iu}^6}c@f@ .ٸ-v|>{J(]m;@*NLRQݻZp&oy%Z-&.pzL>,z%[aUZ*)j޶ ps<"p?xvnJoהAV/" pL+ #ꣾxL^1T=,H < $#g<#S"HG5 fEݾLOG$npYKMN` D ,XFK,bͲpjHBZ\G`&k_G8G9wʲ}P.3LrH_rna{:ѳYdT88ݬY3s28*=|OYUƁI p~)ck -KY`S5"xφeE' 0Z[SDr6JN73 3Fʷfn<%9&u2}Q@P4cюb3nZ5ܦ?71#-HhW9 -0ǹhzI.0{nQ ,Β$u3KS:\dpkfܩ]hSꕟM6lF [u[vҢvj>|-  Z~~hLGPU("<Él_o|AK&hf$~W#I4ku[/,X`;L",d0ʼnĩc5ŋ7*i/wzⰢ9 \0Z ۰aCy^F@QBGr2\-#?6qy9>0")L})X3_* euEY xx衇҈^~+~GuD @kg*O 17hDZ+UuBq\K[ x(ܝˀÆf^ )-, P'8&e>¶wѹ^yn)9Bܢ;r\>}IX ps(@u1p]ff r)cp) IZy뺹&-j:DJ 9*?+-"~i`" 0UƕoH3kXfUV>>stEj|j#p5I>ƭp)+HuO/#9+j)ln)OW=>`+&emu$@c{lUL(+NY@, G2Xn. `N\MpwH%km/tTǐvL' P wؘ .Ҧ] S9F3I3hSϴ`S]MLOsZ'ь16kԬf~]Ng}۬U]j\5su ]_ ^чYcV9WǍCN4 cP&M,*JbK/7<׈-LK೟@m㼼ƒ(?& cDF|b{5e E~b3s'ϊ2MϊJ\veҨ~9q[&[e7ۘfVLs|;W$j\eY;iI4n96OYn/h5[т/L v(FzҞ|Ic z̼< ,X x'S/.Q-Ie)&>{Ųڵtiѽʼn!:/:V9).δ-^@jf% ^9|0QH-M41 %Hay|A\@ɠ#LG7){ӗ 6k>laNG)PL?SR(/&x}t$p&$> h{XПs*f!܎ԏ~rbX1o?Co1\Z_^q_j+kN {Fk{1s.ccZ/7m,F|8ώE+٨ fdl5`9fnENζYi7,WBטo9$CZW)^87{lN$%@0W\,Gsm6kU6n`ɍk/4#|* =cշ-Vv7D`(&҈\6/Q1qY]uUiwq0-ъ;n=MޥWwO`h2^A.9m[Gi  pg+ЀSyG(v{!Up">[yTcZ":Xp)yI%gZ"SF:?dv:,u g/sY$'yP9e| ӉpZNmFpª 2u$H0Trm:ɴk<frA>q$>T]DW=]6:q vg=P߽Htߔ>Tl2m D&MQn6ڱ6l2d2t߯V5k\%i܀Й p mQ8pXyX^qiAn"p60ڱA=7wO+>}8T2!@94jcD?Dpz;6@@ .ѵwKV0P A^LƠFn]p3! %Ll}8&g3|`W[zYzsW+>msr&7djű9稯BG>֫~:u F8K?O]ۏ_WylZ߯ƀhRh3Cf& D'on2yڈ<4QwtƃAz?kEʏp  Ƀ1(LmQOGJv`au>5iJ/д} Z@AՖ]}|m\ikȔ6 V] $m5)u?Uf;p 8/h<@ `i\Eͥpp[F8`,UP'y ĕέSz5wkEɛDzx5n5UW y` % iD"}7+]{㴬Y[D+2NƏD1jg}zXC?i!ІC%*;L"@5jHEsɃ'\|I{izhs=iӟv,~QGePf^Jz2@Bq/ʒ~^y.7[\2rsZϱ^rFzY!Y`@ը8þ-5I|E=_h6[Int^wrvuA p pUksCG7j;uoazMRͺkwH K+ṯ/%[=茤e6ʘ` k]f>nnW$=e#k*CӜUx(GWėC{xTZ[Ưj@04q/@5,_1`e %1E>9@cJ M.ߢw&K.-RjQ9@X 6=)=oTިX`& `2)] @r|&mloTvSMOoLڱx}"k@NOEyZ>/yBLun~dV|Fs-tp7IKp}ωiv`rN)}Ҽu;hW^1GͫذoPrJL~,lV½Fd.Ж~c!M[ nA[`#R=+iN?SO=e)tҬ8_B@lS94DUg];`@oeNe@7eAn:qsc)8mBA_>ڢVFy,`Ϩ;͠5ynl^ڃ6c,=ao5eMZg2&Vw}IN,D;p R{(F;"j\@=mN=h?(o<)7}o"Z ]իg5mArq7*퇬/ԛoE,~',: 踜$ >"v&ku(ܙYy![0}0_ )Sޱk}8AZ­qz'=cpӲ[?szO?_ n}M>z"ȣ"pѺmʚE-ؼlE.@ᮽ[f_۲Mx(X)# t1ڦly-c~y#vjl?;an}R}-N.2CQ+V4У~ޫ/^|0Cz `oD*,o+ ~Z}:x+uѶ#4\}vI?J(DQG&o?TƖE'Pq (dcYu[.ǨQ . zwku`ndMoPap̜O:W" ھHmǽ7mYX@`|'@ Ps#|$1LYyF( K/4ۣ@SaQ k)S."~O sVՈe>MJ>_~cڄπg DM;nUԶU&hGS9W+Xͳ&s)+עGY8ߚr=? r< xgKpM~Ls-[/~Ewڔvxo.D ,g}vhƥI7LIːu{m<|l)@\x,w(HD1)^fTP@՗g۰lKeVK_~ڠL+ơp>C5cMn3} YlVs{ky?X?N9o^s|^јtG2ffLүӷFE_Зad+gc,@Ǘ|D 0@\ u|FpQдk(Ybčtps:~;uVl%޴pݼ u\*adt~rGy?&UYaXqzùYnƀb;zj8) fY}kLuoo5 ͇ ÈħΩ?90 gDc!헔{H a;_{H9%‿ulK%Ѹ"A 9P rS ˒}ʬ|ΔYA׭,<2Ns&, DS_z# x뭷%}.CY)% ŘShz, b')u̗J9bs~oHBgɟ{F`|ꫯ6a&ɳ]uyu* Lǥ=; *W1Xac(K4F*Q*:"?uT_N+V,BDRr<[x@/ةzdp.IЕ*Uj.:uxO.VJt-pQ l~w$E>$9 8{x/oe,meܱK=T҃3ʦ{5QPjV/QE'UQ6~K-dHaD(Q cIʢ.K,h=nShݮy`iHu 7XA\"6էX]IƎ͈o?gGƥe':&@-OS;J;X2uXJ7N@%7LK,M,VdI6"V4[no>Emqŋ0K(/&}{wsN@i?2% k[_[eX@ӾNR ZIze/G,]MbF:oo;sϵ(s"B U{FeFuv3&=[HVndf Tkj:o?ϧv/?}t+o r?דּbg[_@65~p7Ю @9$E`,}l1((Nv^m|ǚeM~ǖ]d[rE`Bn+jhߪOd(lx}7VY0U,(Z+:-?OȆl7%Ȟ>F#i Zޞ^&ReQlƒx{PK~L%}y&-Kh jXqQ]!DL1~"2"=WH`q&OaA bMhkL:S^L;_E(T>gi]d"Wʿyw] ;ߨV]Gm˪R^% 1 y |:WIRbWL}X2 6#3܀6QjK =(_{ UM@Vן˥{Y]'AmMشEJ&Hm҂bbǰŠ{^ЖWu/o3`);b7r]ѵwRBrݨm俩}/W}ymp߂ `ٍH#"04lьc$v8![ya[n`{k~'n9Y.\ˮ!k2p4SS,e8B} ?]i&٤ 3X%?sl]ڴڅ!;{؅F0tpOڏIBi @z´,G"GDd~]{D,P$tL=M̅/ڱQ=+H)id؟"- 5x @~lF.S63^w`C7Eܞ㡺/so%\oD);>';9_ՃupI:K{  YI QQ~w@_YZ@m~WirR6 ,X(KgPujj6R>S-E7=nHn`i.Ҡ{'G2vI_KϼTֽ^vFʨuRH+L+M%D *rspѿ]D޲r//lcE$"R ,s" z[L7T[69P6Dcȷa~\Ev xZi޽{pA묐sMQ}ȗ%*m"&Sa CS}X$ݰYV."uei ې.`zgOQL<delT2ʹ{<EC8yx^έ@X6(Vn{Fy:i " V^=LBwD ^r=4eWm ]EHhT+~ ,B67c9o~Kmj nH;܍l(J*vu:Ino%h݆n7@ `RI).6b ^VS5)U&3@4)>R𬏢A-/6"mWEqƈi >pUqy$E>1hjr  z,g)+AsyIAUnv"`H {ainJ$a؜"xj{,-Qǘ`$QD1to/2zސTzu_W*qTa?$V)HX݋A8Mmua ڦ4d-p U!'IRhJp_MЪkٲjrHKuEݮ Q!,ܢpZDH)EmLiw&>wA@8AV@1pKy1\ lK`r#@ߩ.#}:PiHbR1q}6>}:P4M[/H<i^-@iuИv6`_PV}xhԫds4DՕhZ%>Sm{`-cwy|:' X8+HES;]{6e'' CN\}{d{IE(f]{@Qѥ pUtl4M] H|*.#i,]6 >I(3 ߺ6!MRA È6M^gF:vH֟ $Zh\@'m)A مy/7Ѭ( @ XEzƒN]k" ~KtiG&/gՌݹ\@'{pї%2Wac:,Y}l 29&j࢏708 ,X )ܐ v[~3~m}uLh_VTv2>]qunԤuR-WpI.};K6tӼ^`|Gm[@Q(QXlL iղ| .jCUjGuI hSOib1%15&)c1me%b,D*B:ל6JY3!NQ;|Ami|Silno,.D"u6h 4T.jZluRjOkw_G`Eb#:c*Klٲ&i~UDK&[Yg裏.7 ~z XWuzNi d!'Nw61 ,X )ܐ vPQDN+ׯ?OK2ifxp Mܹku;:@Ő 1&܀D*N[D!)u&:xW^1 xET% A!YA\ƣț4z|}|[[d˄eÆ ѿQY">+:VVu,56p@bV̩r&Vr,G+ʋWr|2T )`w^*QeБrr A͂#g϶sQ9NN},%9[s"u ̎3&QƋč"^/JpØҾ}{1"G5PחdFI%U0[g)uTi9d y^h"EFTM"A]@̴%*{O`feFj_hѣUD əɳR/' i 9PLceg<[Q+ 2XѺ3=l*j ;$V5J@c4EX{JWYwէd)^}U3UeE9cYXyj{.iŏ\_Z9K~/sNr`j)EH!^P4C~;  *H,y!J,%BG;Rr#M"nҥBdR=6m4eٸ{67O,3Ӻ#Ytz%ZHʄKM&y_gf_\%,+Kb41t_u|73!O,s )cD\ƨ:tr־@C 3RE1 L]Br/&I!*@ X;0RRiH!Jy?HOt> 8$2g/HUTy4# 6Q: Qw(|q"QMYNoц!6jρ z'~ OU>nǺW~5M$l*E"сm- #ᴑD"q-ɧmg=܈4Ѻ~Q`+-ΜmF4w?׮:eln!CX{Lm4RH6n[>߱n5@.+!Νk,s$VD5pNtZltӍ#/JnVP )JpvezGe&e"%8PhXNK :  %HGlܸq(" ՘ D #S1هi9HS \YK+'V]Em ī+wq&,g0 "W'+LV6ʨ*=0?H OLQ,Vx> HV@Er_e}0eNfH QO 6Kϔ*cyF98^.,-0SqطtnD-+(?a X孍jŋt& ! @K~UʯC#bԞ*j7〷j3~TIlǽ" Y &Uo<5u=1]V/?z苸njլ=<|5[QϭzMqbS(h|vy%VEqS߻/yhj& ,6KUWx 8֍҅!T?:xqq[i'+p}nА =WWעEF<+]Rt'f$X7~Og%گ;+>!~_+nWF0[/, vX\t]#TC4Heӊ, @FX.fАI`Cd6y0tD"2|ts/_6}ta .5z-ZdDvbD l,Dr=@;ࣖ[ԯ[eHe Eށʀ=lݺ5W!8\9Δ0I?Ѿ\7(/EڲЍ!qf1v"XiW6MXq ac5]o\Z-[ =X1| ئ 'DCvYAWkg6!B=KQL-) Njpit]zHK"څ1E{ߴ)v6a <ڏ@.[_W ,X|L.D?РIJ% -,;V7]{->[|Cqenr7xmH!bܱWW$^=U`ϗMQ%ÒREQo) /y˸J_RH˝Fcim`XuEʦYҒUHр@Rt^DSY.߀8"1YuMԽ76$ړ?0KDf |t+0&]ݻwO۾}Emx{!@\"?L?5HA"yh}MKd)+m6˃RMU@'53 Lt.eb.j"v(SƹoFxc`c8bߩ]P0s-Tm{6qje@K+0Xrg&Ei#=Ɖ̦Ki_~S^A*F%am=%3+ @uh[-6mjPM(R&"m.N]&#M!}@@m4z O4ɢgmRsEs.mC,X`6fۘMTutgJ.B*$gsN+dRWX4JW]]K/-y,u%EH!j%?ߖC iӐx_%k}O:tQH;-ѺKirok^{L""J^ty%ⲒW\V" 5]~%'l(&|D͚5+8KeD@X"hD6Ι3*DMH aeD=RՈR_r SL1HG-v8ZrR6`&\^ &/DDZ- lR7GFuֹG9w%HY"nbS>`i׮]<q>e\3pXuiΆiӂ[Զ#5V?qa? 04 wr8Nr-~33Ѻ(@9҆HU'16_Xh&/T瞐o$`͛7~/Qre;iw~u9)XN=<fb X`g΋N> 31#-Ѧ2_~y){_LH!n" `D$ +r:V#joXVcӥgDf"ld,+2!Tx~rxN9X;t Y~DqNl|[&hl݊}9o,X`X&Αף)NZce&==1ìTMMB*D%3 q&&XJłN0w-)'<ӤRL+*qlvJQ8\À9e (عsI+ 7n8 Q"oo5E(ψ"&" ,>B\tЎ!RH=88A==x$N >L,Od@-!땖 '`}-Y$ܔ`nj.ہ\t^_$1{-_ё ,X` ,X`dWݬ0[ [3йuLKժU]&-lԁ>KE I(L:ciwrNcWi磷+; ppec66{wm/M67+I`tmi ,X` lҥi6x3ĈюO*"6xFU?nݺg쨍iw$+-ܒno=qL;wpC vXY ,X` ,X]ـILL"mՈ0"P5qWלqPuQ&,WA$HPTB]v3 3+z뭷UVVC+H"tI-5ƘU \c1c1 9ꈆ% V6tի/&a)}g={W>;'M.**Udw4an?Sc*1c1cСCcκ.]z(̟?i"-E͓Lꫯ5y6ʁۻpJڵڵ{WM~ٳgn)?.{РAc̪1c1c(X"n aٰ[Z^{7y~Ig]/Rhٲe̯Kto_o15c1cb\aԨQo*Z.KEݾLmcڄO>$zq2d1Ƙű5c1ccTl^{6m !o<]{ KJ .oc1c1c ?~|%/-pcǎZk劾UDɅή]f$BnFn1Kc1c1 VIqp-Gyۚ5oXܷkFV#R&<1wРA5Ƙec1c1@ JV+ )oSYW]u D;N`²~8;cc1c1qܛn)u]L.vvm?TH?DÚ:ibAa1fq,p1c1'+={F m۶%<6[Ze˶o~d=X};|Ic)1c1cI.p9]5JMަje[j5|-ȋ{7F+x cc1cYř4iRkó> LݠM6?RLE;go,멧 wyg,p1c) 6 W_;cɃ{N;.pA6tPv|Nks={k Ek묳Nv6nٳ>i/{cF[cYe!ʃy83 w\hԨQG zb[woTYfv9Ϯ[ -[k]s5a„ sS \c1$\oDHVfN,nC=N;pguC>3O?b4Gvu׌MA.K/ _~y|Oa_ؘDb8cL!Λ7/HDDxA*ܶIN:6ڻ|{JCjժ4Y|c9 INc ak1f[nex7Eq;#8"F$_~e2dHl` 80>.Oq2v8a¨Qˆ##ύ92|q208qb|/ץK 9S%$x\rI8CvmjiM:5\q/K.DJdWSk1&..+.QV -\¢Eeh֬l*H|C$[ (eQ=È}_n)W_}uovGؐp@E1Km;eʔ=F;1y#^}+2F2}j1cL} O2ߎ"V '|6d3 Q2Fe ,ԹqJ1_ra .ԹoTfs7.(raݜHRI rwQzΰ?qt%q+jvB sU U2*W1JU$)h!r󻆎{,F*B7L6-vNϝ;?R1c)%ʕΜ93t1>7f̘ O?tA,bof̘WxN77H0P|.;'Bƥ19}t>̆ .aUdmV2zRV>LPɐOUi{iuU7:"ڲ:ovHqY!e5׬9?Pӏ/s=o-6!<H aR3tyGh 3*\JV[wu]\+J)o\3NmF1!id1cY&֧!~*4mCMyzMC)u_,! yN=Ԙ;uu_(7$-X+(US. \Eߗ$J}$ 6$f>Ui|Oix6:o恵HR@̒`NChH?،a"wlYK[ouh׮]̽[kYС(/1'prVɁLb 7 [ tF~8#,2fVm8*FeݳR/6@cEn3䓯yL'Fղ5Ƙc* "̀&:T0?G7Jg=ٕ%yǪQݔΠn}^z7JRk?&p]XmԬ<" nVg}XEO egpה 믿b"OS˜=8fՅHwqG=;U }iF"rohܸ]b3!ie=W RS6XcY%!ս{Au:6w5(b5csEf߭!yEdf{,1y饗Q7}W\=VZR"eg^x\х<7`$r){чZ4EOD+8+|30zMCY%~NڶAmHqBXrה7fI{+<4i½7W;h$LV L^o&;1Ɣ-cV9 ˆApΟ?? (+ѩh&U͑̍EqOKsELDK z,G fKL|H"r i aMiBh= `u[>84')b8nʱmj8"p d7t%3ZjlIDkg_ #F(,pM`k \S*#\2#(z\}4rh VvW%C\|e"zXcLbk1fcA7(p%G"WdeD2QAR*&Dyn')rv{7|JZh4vl_U)$85j8+'pWCJ k*A}Ef5!zu[FL^U#dd3H/0=M?A7pwC$%ѣG`r]wݕFAзo_P0+ 5oHbBPZ&]kTmݿ nIZb &NB޽ða*bk1e1ƘUԌ3h`mѱ]Dng. tc(Ob6(UJ Z < 3KB5 \&<:KyqUZ {;OBC2r੢[Dj.Loue3-,pM`k \SC Qhۍt/_|R"!v3\tL-^ox'B+bk1e1ƘUG\t%'՝(rsέN;'0R\hkWv3P=#: [cj]{QUD{DoofP0yupߠ Wp-rުX]>!|$jDlu 4Ab5.SOU@t-qmm)Aoוo|L0iҤ»ה 5š QLs1FjsՋAH܌ʓ GjSFTsc \c1\RM54?;h4Ibi0ymD1,5 pIgopɬo:) ^xJ]D- r:2F0JDc\xg5\hBjfl&)YKxqql/AXrה78T',2K{:erDfi`݀Wܿg͚U \c)[,p1ƬB`"EHY&A"WIiQxQW_}uH\dnUDxJ#*%"1W^1A۳gnaQ"#Lj}',&-,.d/Bޮ]5\4I\pM7G $hQfώ]%`k \SX_=|q25OD {it9پ}u$g/u?גFD#~x(y&dѣ4*<"Y'["0 O<B?ɑ;pGm-/qql#EC"ڙӧO0f̘&+>&NT6œ裏hN8!mp X^z)꫘xܸqVYG^d ¾>~Eῡuqێ8ŖOC: X>YANJ%OIX.uc)1꾨~3gN ^W0(wv8z) )]8Wh{c?͵krc{  \FRT6hwPCgybA6QJ%>rc9xR-p`C*|IC+T%t\rm( k#98y?y ^:!(\_N=Ԙ˽p/䒧{V77Mu^y򳒹r&mic֛sZ J{c \c1 49ظU>2d,Lݣ౤j%d."[ŔI"&r|D##f} !pTs|)@,(z8#Aa+h#(aH]@v #\l}4il(MXS{FeeK^f$j3z"@0 ҈/d?(F'!<mK%0hxtVm۶'˯?> V`1OdMR8۞/Dy \KsMM'?CY"y}BzA'ξQSlqǫpA DD\RXעN Hb=2u#N2(F'%Kr :6Mt,2c̈ ާ2~8s<"c!zVѡZWΛ7/K}]w&C=DBzp$mq/> +# =kj_G:ǐ_f\7)^XcLck1ƘX.8Mya[H S`ۙ|ˆ)/E!r +k{ +%zJR=+i{J}QZ@z$3DȢEjȎuQh ސ<H6D(IΊ"dq ՐߞSj ""D6:X6|IsZJ>}'؆]>@6C_]&2Gzg xlC!k}ߦZ$> a{$zm#C4Q؈ »8["Fz:;:ӎIԗX?R$o(X>2"RHlDu~פ68\*\tv:7q6Pg) :DlGST7H;8?$w6mvL&JsFLCEv{M"`1v]b#Yb-ÚnDH \QSN /ho 5{S$hhТ(oK_>Jv).xESאE^nZk>{ab*Kܒ-G|$Zę·!%W~}5jXy֏:ϟ &Mqi&Wd}Rt> ?9SGII@["u h&VR.|PxߊR%paBnpPt _C?f>'%\kR@RO:QgvG隵Zkq#bkz3MNԳt^]ZF`DQ :H%n5$Ht\jXW SXc18K`c7E =+!)DA$Y馛fbj"]e=PUgH=PkoZ' "H3QTav 7,XkY5uMqJ*M_c5g$l+|_'>'h|粤租~^RyH,?<>ײڐb/IiKp{DŽuõ$a]Z&M/)_cD凄-gNnMeWO9|"sT3Nܖy}!7pBOqlX5HEpgS(,cϾs_g̾m!Α"AhMEՇ& }9_< \BIy/SNjMKmk ѢtM^R.+:8_ɾ`:H˯HN(KI_{K\7CA\(M__J:5b!usY`* x.J!K$pSMx_qȉA+_nV)[vT%/oRA|W^Fg:v־U ZD!s|v HXcLck1ƘX.":nV]LRSkEՔ'9p sr!ɕe9yi_P?HIB}DI tڔthNAPc=THQ.Qcj-FZΊNC}H0R o`o(CLIt԰frJ /ҶBh_D<^doVdHOۡh7~Ut(]$@\E5@z![K:TO#D&٢x 6ԏZ\M|gF'y4Ee_To&:>FuD}Rݸdr9{ LJUDifHi V[meDm3Db\Q0:b$::.kKy6ԡ"IA?S2U@ԺߩFJNz?5 bc:W37󐶪OSOInD=V>tSZR.P$,5tZF׹1d:w'z9:R~e7kmu瞒Գ ߉4$"Wu}良kƆfզ~)R>V]d;߭!(ѵzS]k˙+2hXsM`Yک.BױЦ.r'&% H"u-FؚT4wuHpw`¸SV SXc18g+o y,C{BfJ8Dw=-qٹɜLLR ;cƌ2CHDhFB.V]קQJ\Im]H0l".HRY+wa)D&"JEN֏z w':"*}b%?S(\W~֏:ILV (KEND^X AEj f3yO8iҤm%\ĝ~<0!l7эh :^HflCD!G p (p%jb/_^GJnu 2'Ⱦ3 muMqF"$bN7F>`ҫ$rgb%_4pIiO!psr29yHJ ȗwkq\9D"WE->H#āxuy$ s.ҊbSENRa:yu@T)ۨ_:#4 aC/s) \"!oԃty= Tul[ TnrDcI7 POq;8A[fΣw²;\錠^sZz繆&}$:'}o׺dxbo\*SXc18L"KհQm4$5$<DO=15y(41Vxer,X$IYE$0\95:("!kdz"ѴDd2D8&BN H>"x"tODHk1I.BA{4Dα,F M"g:˔*1qFɛ  {O?bueGH/jdxO|E9"sί% \:NhH,M*Gudf'+zǝTtP JLg!'9!sImDL$pu<*EIKU2r#43麤x1"=[(!pȑ:C#+E \dwOk>K,XO QsD6t=R<1=t܏8ߵ$]kpDtDJx%ޜ_lOEc \c1G \ (pi<#4V \1#DV.2N @8.M%4ޟk [uT9zU\^jOUӹ^D͟H{ceݔb7mu 8 7O~. אkk1e1csҀNE ܍ q,0"ac+{5[G$*B믿.XGJ.b@"rYE pƊP“|i f'T^C(@_D10m㲂D 8AI{))؏_9Zu%ס(r57 &ZߙAr+n)>:Lj Zm'SCi,NE~۾ c̏b'NOb/=H{U4˱-WUYGǫ$fO:Bxk/[<~&LqWIl:_V;W>NTH],ё,CsC(.Vj@_UF 4#XFd&׵k?ߣmDh7 #:zP4nwΕe{LN;\JQFNDFC{/!N #3t\_6OQT:+#SXc18JE>{jΓ@"! F'҂}|(R[g[#"DbF9ѦՕ$;|Vi{E^lӓ㽒e HC4IHLӄi ՚a Ǒk-Fk!* kW G]/-4I5"aK!JFjHh]oȉ\EΗxL Isqpa{By*"y31F|^'0dr^H+"!:%.."6'OOU+86xdrD&#g)q_(b)u Ď&psZ/:8Gs $^#4Ԫ_(;p$⊢'Hutd?7yd!ou^dй49Ν9Z#a"J:NKCxs^JןHWtUQ#!:?q)p}z2Խ3kҙDGd֬YqNgnOQujA[Q{XcLck1Ƙ(Mv,1$Maܰ_ɒ7LRI:UCS[3i|x|^ckժn^ pYl։.jjg!FPI=ĺS O"Ѵ黑QmI*"0Wyw㶒'C$BNJ\dNBh3k;" \oTxÒ@"$~~%TA #ұ:}?)iڴix@rb(p%js/3% $&mDis7s4jrܩDAu. @v4lPyڏ~r9Uڐ_8%^_^) \ud%!::e^~^RvjiMֹ^렮EQDnOם wIh#4A’zmNI}\u.Uģr31_Ec \c1GPLDȞj_OɤII:P$Nհ\ HKǒ0LDA\H i{S(IJnO7$^1")%Fz}38[9!$s$Kp2*=\G---l,?QE@"KJ @K$#(衊L6,ta G \g%xI}^Ҥp& 5*26ځ=Ko&_>AIP'-/,Kb+r^soWZ.Ջtl`bHqf:/(X9pU7/d"JER7"Ck2Y%{UX&5Z6z.y8V\3*2SXc18JUJ.0Tl|p?ӐG1UH/+f:E5ۥae(&o'J ɍ$e45.Oq5 [q0 , dp*DgX~ 5&@}Yږ5 &H+cFR##A)Lh<+JJv2:uLF}Aӹ±X \>.ZAb8 \7T.y \th*TpNrΝcu|ӗR~{f뽃թO\J;徢m;Q|] <8 QczkգE^1Sbk1e1c.CFHc$`0vڝÑG92}h[*BsDH& &'~/! Y\%EJ3CR9 T90yY/mgkLv#&`D"KK"/OH<"ȐYK/TrfBP  &?nE:#^hhS$ Aw#2о+Ju d:녘E]ܔ\mV+ħtx?_'YK4:kk&:SL =*E&S݌2LD>w#⌺F y/7[+BRw%C߾}c#VuFUIv!7n(fxz¹ƶA7[S٪{N༞?~P ]+s-;S Kљ1e"m2H7a: 7 G- RT.OB*K/ňTѤIA}`"bsLEH*TJ(8Cʑ{Q7%Jh p:(By1u.\}jWI `Yw=D)5L~ԾsYוxOI~tH5 \c)[,p1S*p`zl"9Qc}\#ei""=%Q4L$bM2O-'ܲ=p|H "!ݠ;٤h&'zWщ_0DK*|'a|7 ?&Qdg+IG&QwXǭ$:2s쮈Cg'C$ |`2X{S̎l;F:iB YmLd,Y'q{S$.A*}"Qk8/r`z:R**-D!D!J$܊+NmGOMT5I"h TI}M?Z.I2qPEkbGSTTn̤smU/ⱐ#B(OK5#zGζ Wx8b,ƾOKu\ _|Ec \c1,pj+Rq~|!wΒ;KCOUc'ٙ'!VHb%#?/Րi@gO+i|Cxm̘1.qE ܔ1%vYLb}R"9Ò%P[HL!]7i=uܫܳmM@Z2QR!Kюě&\z(!,Wvhf$UuL L}!)q5ue$"fI2ޢȂi5I;qCXG,DLT#n(29I/;v8=$b9RVJ"U@ sn}W%NJA)!uDm\뒼Ӻ鉬<:ܾK(FGFt0\D*?p}䚡rܹsD YRjeK:@T-밢Uq+گ;W=oMm -kk"LQޙr;2d!D~Sdžngp\dۤfΐ?Pa]x?ۮ}G7G}K"(?+E3_~C@P#r9E]DWW-W״`!V.s$p%yOH9c܈|$Ҡ0):Lo\]ZDx~}JLRgGT'l#y|.8^Z'8',v4)vYK\qVo%p½ \F{Dy 녮U?0up=Q8lG1?($pHfԱ5D: \K:%th隿OVj]eRγaÆmc@GRR#w0I~luz*.:D?S8V \c){,p1S%pVM9sIxDBPA>^?Ei*"`>AMT["DD"N>rs2Q~G>KnV!f^4xITl#tӨf$]w[ $mDVئ:{Kуy ᅐtMi_ndDȕ;Kxjچ۴̔Hdn!e)L:k_o)q-']X$Y^a_(j4C4)ɥƊTBm!$H'%bb/R0yewN4&BU;m[|niDG##Iz_ O/pc0#X}izϲeؼiLwQ,Cu;Nt"t3ėcDPpa]tv˿Δu(/KgӤ:oRLV p[yLG}_pͿfݜI4*:!ί_)ŊdR2F(4M"U [m,kv̫hoNǍT 3XaE ś \c){,p1S%pSHFh=,HNkQX)/JRx<}+p0IFPck-b+&A2V߻swmD3RD5EVM"1{֢1mDIljMVDnkEzIa1yj>d;zΤb/KalM߽QǎӾh-ZѲGhhD&A U^뽧.|LJk#r"/l;0 _?w3g~ɗô?#G/'$?.pI[$ַDJñ"OŁ<#HBD~}wKEA~o5@;:&@@F%If@#hYb稶RlgȤ>Q_&l+)!z8w2z..imŹ?[YJtpPLi"GoD$%'e{\ː$ö)YE&[LKdJCxcB Ƒ$I~a9n_f;DNC!ițlL' `">Kl>}zS"fepw <~$mV)@ `>ESאdH6>QGʅxe:>b9<3ˤLѠ'Oc2 9YOeJ\ P:zcq^ZG89r"={#kQڜ̙3gɷ)zk|ti(olPJT/5qL4)Fʳo{".M= /hQ#bk1e1cc.0! ?0hQH>GT}ri? u_D"&9DD20a y|5k#n2( F4#!9B@ qTt90_d< ]Ea?W,p1lahО5cLa,pK3\ 9V:Iؖ  =&5hak \6ФqG(?xVi";kYFR4nW,p1`s[w}=gN.Q c̪n)]sqb(LDĬ%)?qB75 \SXȊw~pƈ /:WY]Ne"?&GӧO>W,p1)\&W\1AEd5Ic̪),pMyck޽{7x \s~;th޼9״yرc+bk1ϴi}%dcVߍ4<-Zd$ԜuOcYu5)o,p@.]%kԨ5k&9CڵkOա(pΝ^!\5Ƙヒ;ɛTW,p1W^ÇGR(vO<1K$nwdw…-X [1Sy5)o,pMIg}'<#[ZjŵkO?4F*;Gp {eV[Q \cY~ Fo6>Iڔ:g#oSi@v3$mwC"o'OU/6Qh1Ƙʋ),pMyckJC dl'tAmFR)e˖\e2&|뭷O?T \c)9LjLlCϞ=%Oݙ8C$.+z&2C-EIzhVΛ7/ϭNWkYhw1S5B*U(p5{Uw)S4)n۶mbu?0̚5kGڵ;3Ϥe֭K$;#zy睰jUVZ8,p1";p7G"i/w}>ΓO>y9S(x+/̘1/!n)qDr!ߺ1S)Jf[̊jժ[?݁!ej̡a]w()}N;׌);Yg:vx'\ņ %JlѴil=ԩSLE Eg$e]fk1XJ.dkE?@{챞~ߺu난-Zd;te˷~; Z5%wʔ)ٳgߦaܸqcYH.fEr"dsB![<߭YwYj}Ij(zY85jT}M7E"E5Ƙb #<%ytIYֈ–7D792>c1! Z;QD2# c1c1cJ,uҤI1Vvs S%kC-.Uz&7C2a1c1c1ƘRyKؒÖmu.!M7r7["q1c1c1Ɣ)mJ(jdejGʌ1c1c1fA$.Q|IM9rc1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1cJʃ>|Ю];*Uxc1c)W2LhڴiĉS1Ƭz5*t%'\r%Ac1c;0zGo;c̪̙3_{p\c1c1fe`v n;b1ƬRL2%t)Rzz[. 2jO}w1c1c1Ƭ>[oEa/Ν;K.$seeL.]c=%/~&M Ǐ3c1c1Ƙ["k_{; |,rJx”)Su^~^zdguQ}'f[hӟ=cg}ݻ!i7Ĕ <>Aygc1c1cLi%-n|[jm޼BE_,\*:wFϞ=o|GQq3rc1c1c)%⋘Iϟ 7[۶mrH% \?"裏_{0{0vh1TZO{.L GO6-L:Rl'4nܸw߅o&L"? Vg.袘H_v^{mWSnd2Xq3j4@2֍-2xqb-r; m*?6x{/EYfl@5܆ \"Ζ-[،Zrc7x APON=X"%W7T-Æ sٳe8d2LBO.lΝ;l=vւr0?v5\\ٯR ){tEl,W`L4-gF >l۷_g[?or?qDiu`>shgnmۺlcl髂RB HTGpe[lґ֙e9 ~<3vqiffjgyƎNhт>5>*z*3D \u 'P6!«4CG'eqnH%_rIg73Vwʰ s /dYf`늩S87? 7~,ӌܘ  ?x|1~v9/_NSO+kd^"K0z̘1j?ToE*clr)orY؊eaicp?̙3L?]_epQaB|[%( mjM)] Ƿ|}0>Qߪd* 9rLSo ~hj;/53Aዏ?$E|vjݺjԯ$> ?pO\FYf ռ /ߨg<䓑o=xFj®_{#RJxK0E2 2 @W0$pd!J)${. Z:֮ ;D-L&ɔm0% )%Z `.2g'OLTX}K "AO` CWAl(N]lؕ'у=>!j}nh[i[ kW˽K&9sj ޣ?oڴ6 $`_*`I'@YAeo"^ɨ˾df~)Owp͎bW^?i'鰊d`Q^YV<ճgO Cjҭa*0O5YeF(y&Ck׮Ėܙ|\٪|BUU+-exx4Tn(*v$t}\"&d2eKq!dOhlْO)dLEׯj.zt<뮻ՀT՚}Bb *Z})U+w @mPh8huU#w>*w"PY _׏QVQ \@I5 O_<m'?Z.ڔZ Т \Sv3kft̊'h eeK0ӱ+jĦ\JR۝U1~5*W\J2F%M(K m< w!QF#QkCƌZe3W2 |dd2tQs"SNf >ڳd.ѣ@_V\nu}ro3wY( +3^xᅀ] n(Pe >7WROORYۦvw\o_Z&q@0 1p\9pMp 5$i~ xɶUٸb.'xb U35j;vVZe721#͚5 4[lk 5Jq[Εb`b4d2LyQK QRe%V+U u>+#{-*p)C6v mW%EkpqE\or&&}< pg3z$e%V7Aߠv*ҖJ=S{m5l祝lʒۑ+(ri{(/.[x`p3Ye%J6k2kffd)91 C-|7Ь/bD>+^qw>lpN$hܹ3.yK.p~o \d2e[ efO&%{ ZjSZ 8N Sڧ.הvXcL^F&1 eڍǻwމ?C>n#^8߇$'ǟ 8> _T옒<><~<-~H Ň͛7XժUwwys=AūsO>{19WțL&ẗA&"P`/ 0yTģ$I7&d7?|P[kR0@hMp d`,$@2~qAxawL;Jl|HD'GhI f Ńmj":צ?`COާw> OXcz ı;|7hy`g%53NR>s ¹T7Fox\0*X_ 79Y>>3b>~)ޯG'ER7AGܜJ'WcM}*.qD6kfgx?6| s8g>E@,+#I'}4UlL\l=;hl'xS%~bfbz)6WeB=]xk7s2p L&ؕ9 `(dL.@8ೕ }g l YXh% =(>8F(dzHߤD$G=&A<3PYд@M@aA a 0K 2W^DRk~ @>~9&DL?8|?rlhW,sQZFݵbYusZMip(K?6\Y 9D/qS'M95O>'B,!.7o8` 4pZcY.mj"9B>wɱ<Ij}]h9uծwo6o__7`OT>֝/ӴlJ˧ԇC_j>"P#\G;Ub@|IR~?I6bO|_9MF-qذat]7ym~+5h*)?=|%XV XqA֭6m܂ұժU+_`M7֮]5>?J(5L&)@/lJcrAyA% r }rl A˧;6Qڥ Cz~6/DbmJ,:%%>#mmETFg5E3"@ߡCc0^ j8s<G/v w~}~*pML߾}ݾ9_}/qV@`̓lqEjʺ Ԡ.K@oYގ~X$ܻpϪIRc?nKCY{~_gTd/YtPv-3b7o7p\5ue-s4e62cQAآz~/Yi2UB|mxOBZ"9}sa|r ~O^}|0bvڹ}#I'_*$]HǪ|wxw&nV˯_nn_lˎ/{օ^$d=]iAնen[߭ MۖrY?>$kZ醟~\}ZnvYm֥`7;W/ ⣈;v/*a( 1Crʯ]?n-B>4Q!_tqMZ}`+ 7ٶIm2'H x$f@ETD_Фf˔/_~?0{9J'23k2L&){\ rLSPۡi B d@N"RF=N#G)j&P/< UxG?-l\EA>j-h4!Z22^ 5.0"ѰP „ Ti{ ``k¦7 € b#8uXlxA˞lPfB󵏿%}}B;hxGg &h[KhmY_.ied;dr:%FNa6þmZ7?cpT~qcp7 ;4l?%WOxM];;p60ժeO~֭}zx<д_B۰K&a•LvȌLg]4{l?GUŹ\^ϑX%SՅd{is@gB%)/o]-fi|/Vڰ05?!@·wBc2YL_Rې _ɟ Ɨ_>>4CZ8ʕvgi:hРPwGI,CY }v{i{kA]sMJT]k' [k|)O1C6o-{0ңMأoX$új7zir:6M4^ş+8%LkG2;!\|jh@b A%#LXLΕ\Č] 19ϩxR9Ǖ4Pؔu/̕+W ʍŲ)bO{Fb0CW/ws_lOT{nB2$סY'w}#/6!!'^k{&~MO5k2L&\k,ݻwO +{4իW8d7b}+eidb e{l[AF*d6 |#l_I6z=A.NYنt5ȴB6M` >o @]Ha }h}]ںcW?Zw*5+ 5@ʣ7z>xUFaE@p(tէ 46kvm4ԸQض^ݰMv{ .w~ S*Id4g?"lګa{Mvhp,s6Ct3}5}~Gcm;ݳj{=zdMص}>|٬z4whx.Şw}sX߰3SGZvz۴o}m|lcӔJh:5smVk n;uoOZXV5J*qCm6Y&X<=٫%Ec38ωGRwS'xR8$)+u+{\'"x?}LoZw|Pb2dM]XE)m򥣀(q_ ^~y'xxqٲeF=P~^z6kd2L&d?Xt*ج\h|/:3AY@04%R j{ , Fj\s߁Z4f)SAzGW~jgSO=@2:0M [˒>Qh[Kckȶ e`q<|`;>L+zuY-WAmjSOA||)cM`| rrpx\Yˣ'KZ35q${N`?[/p-P-e |yLJzp^^,oȠ6 ɪ%$u3'FAMm0ytfuqt)@v@5f.Y_͙}L\MGIрm|/ ߰r{WssH-x%<`7 &I_0Pۤ~?=Cޥϗs8@^TqF7oJW7f8qw)AkW;/Y4N%Z{ZPLFվ:k{?kb> &JIiA -Z)^x 6m[bO> 5L&ɔU΂|(9;`įWZE>4X~gz*'+ ,Z Ȝ8 6/0>wЁ@`ǯl01p'gb.`d-Ԟ a36\AW|]>> L.l}`ei@hV; 6eee]FGG@ )c}m3:Z($[]cfǣ~M^cR.t٤Y^!.ٰNqeb~Yڕ[,%Lzl=3,hJ삏ƺlcp(@هVݽ1]f\p‡t'92+9)8kd'6䆎_@cy|_>DtGi<%U~EG+,Y9yȉß >l(}/~xZmߗr 3N-|:vw%]<91uY:r ]q%{!nFܭ;gȲXzWno{ I]]f9\"v@^xlZUy?o~/DfG ◒4 ssCG`3ùJl'`zO (~7JQ584W 7~b-qT?w7S|i}5)MF7R^1OժUrygm^hmo;2kd2L&S); ԠXԱ]"xmuX>KR |Jd]l,fY3V_7I˨mK۩D/g~$?~4#iYn'{A̢Ym#9_sr=#.eS6LۍbʭR`9.e_xB S6_㷃<gܘLJO+p88,=x`aUV *e f͚1n@cVJKv.* pM&d2eaˠ^A w 50h L:h2`u>&hF.5& *x.5щ҂5jQ$b9]MWM̎ d&SN`28Oo1Ӻzl ا}to z$p@7@0gjDoOkBjROjo7 1C1czT\?WL;x`b=pYeK9`;s uTor u9VX<38$k79s98K̀Od2},Dw3\[DGuqI_5\N2ɾ}wtCTV 6{sKr^B2I_ÈňN<|s,~~Rn!Fvě= B,ԩS{  7T:!PL2|~nm2r|͔[[:B5k2L&܃d:ʣ_щ>0K^ 2~XYVlA{\ fx, `DO*%[ @%`R#5;wj*X. ѣ0E_d'}`H&@PIh& te4Y}~٥K@@?qK!Ϥvkңw,K_m1(!PO W\q>EAɘO# U{l^z=hyT0,m{ 8?>9Cv?}ExǚJp1ؕOYkoۺu7tN{Oz%!#)\ p\9 #zU|Vlpe֏SdKȷO=ȟ< 9n3ds|>)#f  .^׍?4>?aZc ~ZMh-.9Mp ^R7>|08l57YjrFH8YC3XN3.p?ppLY}bG3[\j&K'n u_5.k?[o5P 7J*fwS(v5o_ 5L&tnt`_d?JpA0e8GY7 *2P"XW{j/5jp f5D^P+h_C \$оD@_W < Pɺ:{`јrz'q9 q W?yYO@%F!:&{d?{v&XWcIvr.nW {, D}a>uqy͗ c0;U&6[eth`c֭vվm8:Q7 Θv@ u];)տ5, wYnntϣ=AK\||Capi* ^/ecS.'.Di;ՇMS2&ҍ \5 O(ױ|Ot '|ձp$f&Rf(x'@٫ mVL`氾v6%Uwgc~葪ܔcbpw¦d7@] {TypI?qW׮]9O-<%5Yf\0ňec)WT\u ߷Ym}u.pdc=<^wu0d{_׼ |wQTFd2LpI`M@ VqPW@N*Ԉ}LpD>'{s H Dyt (B4{LTF6m>{ŶA@Q_?-"f?t\^Ӏg?ڽ_ju7C%!`p5g-]=dNo}  OJe+`w P0{f8kf,x>%x.r [}xyf.kh2 ,N.+pPWu^yYDг rcK @6\#5nNiHyU㺚}Gy$E'x=@mJ_ߛpKҰ샮]t|p]p|7pȊm&*\774}tC`ڦ}𳅓'don pY]mf~Yc6m&3\lAnO l۱+p$ nV|ԣY,ES,FLx2+;)@j]{'0׀~R(\[ׁe/i,[9&wEݺuՅ< 78 83Dpڀl^J3ESq5.Q |gBC| (;8(# OA:m^Kd=kQ_BAa rǚ+M. (E8eEmcc=:Xۀ&Ln.Hbp%={ee[u3WOP6OoV)+#nga{ U76١얁K&j\#:Q~kJh, 2R2n >ۖW6S_aZot:MDm7q%2E;hΘ[w^WeJnFun~S[vTu.ܰuIJ<mO(}  MYp9>5ɼ fwӺukr$keJPU+W܇ :묈&0 瞛U"!'_ߌT\d2 "P^=: 6lؐ 6?Xf&a@nFC +t8(d|)62hݻkf'۱Q#JLB-Áڇ6_>Ax ܶpc3l)1oW&~r{L)Z,CZlOnGeNʜvV.{G]JYi3gنF`\mG'2{#dJXehCn_Zի;H K\VVңMحW}vz&-۲-? v΄ kw4yso=tMմ/*Q1~Scx΢ə| nsZ}/?8H0-[:Ќ9UBA>I ŒXAbJ>*#ۈpLuPpǥjԿ]6!VllO@cJ̩#ΏӢO, .GOWT"%#&AN಼b68@ }G?~Ǔވnft8Et8SN.@׃,Y2|衇.-q>d"##\d2 f%p$55PLFbcOZAعdժUl5@ b%*7I\,Xv 0<$Z&`T%:NB# <`?7z<\#jc')`~4mڔGu<T'p Hb3p9;aߵߔE}!>TXhe&:|L ,Mݚ5u)(:w/WahIaٌ}gwh ̍7w#6tm~%ۼKaW_ [b-^vi}n#K' 8'=Lf7B&|v`hYmQ&й:'8/:_RR&3.o~2/ɧ~}ک,;Y/vR>z/80Nvh?On2ڵ=yYLb&=iXʄg2G,lؚr+@x=*0T)efT\v+6^  &K6㲖Ӛr|2p=> c܈"n8Ct%Pn6_y_r42[sKVDrG{ LL HB.\j~N-̋>RL]%0W3ܸZjx(5k2L&)+\ PG0nʓ'  ./4XثAH,>vu@lWUβ%(O W V퓡uI4^J jUp6A6P`O? e1I pfP]e c5q܀{t4ܥ'P: -- eXGz3e3 G .;,\3}jjꔿx}!>n岍S3JJP/x ^o97ZgY2Ǹ'uJpW p/y@\|xYxpoL@j#9t͘cH [(;G2}0O?pA& n8 ñpydL7VZ6NS)5\lH`-NZ tٽDZ}ٯCնe)V?amHGC' KXn)i_pǎcHM\Đ<|`..PW.]xGq͵.]sG/Rpzt ,Ko~' rO,Ɍ|YZcRvbbHq3c%X_?<ՠ)Slڵk2vy\d2LY &lΙ3 E00z f@#6Zrz|z2Y . ^cs~4 K0OWmX ՗?#敶 )@&=.׷iA1dr,<] k߮%p;v %e@9 p1'СW,zjcm*@[`]:`9N1Q[9ekp\@3QvR 0<¾.WٛF-I;w7cq\@9 MY!?%q7~"/+_^ %?2<]W:1eѱ0ڏ?s8\ @Ԍؠm$^g?wUB_ŵintEaǮ-]!0ڸmc'@eBf*ayLBkeR쟐m_eĩ#ݫ퉝,yc}lK>oLfj4_8.q"?_zYQlnv|tɏn9u'bb-m?ר3mǍyn,d6O>c|Zj~,^{hvmM7\tE5~`߁њPH@5dlz;~2Ŵ4^ (`<5rluϞy{>G4+0eN+W.@ zjGgi`q 5it(G8}rW92c${0K-1:(?ٛiCLҀmM cr3 ׃)'pvxᦹ9 ; G3Gp5f)e?S pԶe_{R@\V5ojI%֨ tCfMw\m1[B!&'3-7Fx _KՈ'H&>eϏLS0Z7xPޟo<%vݥ '1 ڮ,?tڥ pf-׃VZ%~ȁLoPy+jA\@Fvտۚ،TĮn9\77gO~|F-yýI@)\3s?J=Z4-|oڞa߶mޒdg p4eV4umLdwTKBd; x$o9>څ;uti2pul;vLl p%cMN"(=GX!6m?jy3;[>, t->*yiR8 j[{6xW6)|)ljb-_z#\wѡ\ &M^F- ͺh~[(7 NpYok9&IK z3pXyسo{G_Uuu}T!t>|ƽ6jˮȱo*]{up6C9WqK$T."sG]G,wӿpYjxObHF I<wy EGdA$f\DիL\Ǵ>|17Sf͚-Z $:7j(Q" 5kd2L&S&\CD2-u4~ x7W$>Q&T>$8e[d ݬgz ('=ՎYvF@kof; ~Vrfv'ˋخ2Y[@2Ѩ^pC6>95Q\9Rij1X=}-ڤmU %k)cvg ] CrvoDBV4Y{nֶް,"EOLǭz4pɐ] \2gK5YמX k&N ! e\zߣe\;ʈ{啰icڱe+[_B[PC-m6/Y -n :i 4M.: 6+93G /zt5p@<қ.9dˇ͑?܄(\:Ԣ~d>3EOxGcȯz#M}|Yj )`1P%<7þmƶ?שGL\AƹK>qpiml=m3we\Z$`Hشk p߲e wMjPn֪ `MtFv졖2`CܵDqL⎛4\#[Y_`F^|E@EojPpGPp= jds?yZzTPm޽:ojs>*Zf}M1 <8giWR߉7 '(AX\mߥL_eZ&p!p7`/2O[3?YlU@+mΉۦ,r<\>y`,b}J9Y33|HsՓq9߽x6usBeݣs2Y3Ҳys}v%mB> _@a?+M Orקܮ>V>nG3 ȦRk [ Os93MnAe lc>M<&V3pBcKuV :' `_Op?!17*ny,C;T|xݿj֬ĿRcuPn ՗XpFd⪽n|F^v4ij#ݧny!Bmܲe6Q#$Eld2LL[(?rk  ~b@!{)c;/FS3  %`f  }]^% t X&9O+>ljٟ# 8&'W{ep6|>6ۥ16'?d|p,}wcAu ~RIil{,륥/L_p8umP59{@@syO^ŴpA;7[|?T~fȍԄ/?!I!'zǝX8#|DGlOµ )v!x= A.Ӷy`I26;u$ӷ~$j/;\-> ?F̂/'Rx\F6羟0kj? H{"!~ %/ђRxH=0f)x?uy}yShQ?DO^k\||5kd2L&Sf\2(1L:|.?xF4XmS~ = Gn]@Ϋڷ]ڦ->'mBb3ǀllKl9f[昰4mb@k%2c aP1 {Ͷp}V0oiq뤖}ّ νH$Y0#7r!n.#!?8_W|K3?`ss< nwMLr`5/}@ ƾ?~`g .}Ԍ <Ǐ;ض#p1柨X#-¯-b7y?+3%~1)xJȶ{x?ol?9 N&&׳=]IKI&|;>\٧I̙3ZjDdeʔq ϔkd2L#p  3&f a7H@Ɣ P?:7A=AJRkedn (Y>H?9FP@؋2f}>l˃? J8Rۻ{ J;~AxX€UI1ݤ9 rA8(`d4F\c"yo%\MI' +ۈ=_9 V ^>pk\'] }$DZEl  Zyd_ xsx?yc_"*=ǜ+kb9b&f}rb9b;|q>߸Olb.'4dq"駟w 'ɑ#uqm,Lvfd2L#pMI&0?LXc2Xh 2G tl\Sv \5k2L&n_, iҋE#YzL,c~mk׮k\SVO6&ۗ;w: Uj֭[qr޽;\d2LpxA91bx̎6\ZM533k2kfftEٰ:$Nww\rMbv7e˖m9eϘ\d2Lp'KE 4OD3To2BIԠ5f 5533k:"6}PBk>ۮ]`2u|/_:ffe}v<aF ͛ݛ553֬Iؿm?K8wfey]beϽT3ڻmر[ 3L3~s<}ar[MK.ikԨQ?pDڵkhXRJ 9~GJ:xpOz*osc)&d2zGP^zC|)Cez۪{UeUZuݷU[K#`{V{Z~fflʺŶ[, &l0aB֭[QI]KwͿ#{wMV)&d2,Xf4i d2L&ɔf b5k :v5*(X1~7^M̚5+Ckd2L&S[.:Ԩ"_܅,m߾}?^53dwoI#wޭߠߠY{4Kqd^3g Dׯ{h }VXԪU+x L&d:|E?X~}]6 Ǝkf6f#ߟّ?> ՎYj&L:5;wn|`q9;_K. vR%&d2L&d2L&ɔEed2L&d2L&d2 L&d2L&d2L&S\d2L&d2L&dʢ2k2L&d2L&d2LYTpM&d2L&d2L&)d2L&d2L&d2eQ5L&d2L&d2L,*&d2L&d2L&ɔEed2L&d2L&d2 L&d2L&d2L&S\d2L&d2L&dʢ2k2L&d2L&d2LYTpM&d2L&d2L&)d2L&d2L&d2eQ5L&d2L&d2L,*&d2L&d2L&ɔEed2L&d2L&d2 L&d2L&d2L&S\d2ςUV&L{`ƌƍ+V7o>jsٳ> 6lX0~{Դiॗ^ 6l7G y-Z&\d2Ҡ_~9Ѕ.hݺu0dȐ`1ciӦzsqвeˠQFABBB0x`wiɒ%e˖'N 4(8p`зo_+u˶w筷 D(Q,M'rG+f### ,X rW齫2Պ/Eh)VP!yW0;vMrʑ޽{۶} s1dd2LTd>#bl=߿PtCYv>]vA>}͛={ts,ۑ#G}"F?p._e[/ۯ[n3j . O䓮wRgp* yz }஻6`n j׮L4)7o^о}f͚=c9-Nڧ۳g+ϕKMUg>{ycfygo ] l PnWY'W^Cmj\jTbժUuŽB7B! ŋOWJ{RѢE.[#w ;Vӫg+`vʕ~̢eK/+m|Izk.sMԯ_?1rH=V'mڵ:E@qB0ٟõ k֬ISxFN7tS_4yתvrE/@,\vB;裝6qTd9J >bk(N:X1KK'Ev{3t qSurSC qw]sn/  m^qu*g_}f۾_¯nqn:owU> 9v=B7B! R&;$L`Vޅ TKZW== |>Ԓ`men Ğ.xꩧNkʟn6mA'øQ`JSO=@n˗FeKB~^uҡ:#}rNԽM䢡 t1f }P0}'xZN,Yr G91d*N!QuYWs8TinӼ99B Kˏ?~=37w_=M0;[慘/QfǼ ne˿1gZe7T7pC!B!eLE32.`iOX~R:ur[eʔy e>ߑIQI.qNn~/ܳ}LIMDu[~qϰL.TH/ ^{ ZqvC8tȽf5l^#nNPx&)mu:,w8J4]ϻl.F"p`5F \{.K0rR=xC̷8`̡kp#]P qƱZ ۥr~*+!~eߺ*gn!B!%\#)8@hѢJ!䂠f阯[]lɕ/Z\H]%J˽Q;;)gWGsQJA[N*i;_ #Me6J#UTa#kz|N_\!;8 7?'$.BaW; !pC!B=T-It)W[tIrׯ?CNTK^o.,[BY$h]`)K)6Ab6(,}WP/V^z"}suj#br}]#@+{ׯ믿π6m: (֮] ">ra',_p᡺OB>n!nb!B!B75egN_gyfBp)T--[\ Զl,\j #]I W ?Ji{T@Q :y6<.[]`wC ;k8cZ+n>>+69M{Iqw&e*U*p\Q܍7״iߡݰaCBU~|.קO;_}UI I&Bb!bn!B!Pre7;x^*bF9XxOjU', 5tQR:u+[lYK. jn-8w}Qٮr^kw۸6i i]C:E+ENyIt%1h {.7nܸ;Ľދ_o wԨQn!nb!B!B{6b&䮠3 vrmT>eq}7/pt\?6!%7 U ,~su_~<e$M PO_ JmXV /8\wyoFgtuwzX^?wJ*T\mLV -߼HtAR6}{},`p+塛/W *O m6mZ!n!B!nB!ž ͚5naٙRv&f炩ڷo%DZغu RvL[R]^VеWjC+f{uP'I[{YZo)e)me^x!!w ˽* 3K,9\H"=׶@k{BdϞ=Q#;[ Az%㎄.e]s8DR ?# mh:uj!n!B!niȑ#sIHW p3!B! (=z~~C1ڄ6K}޽Pf̈́@gBw)nR9Nn {_|غu^o][a\ Ԯ/Ĺ#.zO)h ?ooݻk O@tBN|N⋨}GM{5pnP"'Fnbn!Bp.PQSv[@js #|j,`5:t萨UOl ʈ}nxϸwvխ]m6U%|JU`e'M >ܯ,+^E˻G噱G@0fZ`}Ԯ]ۗK5Bu{$?|&$cu X9P,)@5ٰaC:l-wm=VZMPhԹz+ߺ ΗY.Q\ZM &lW%wӧOY$M__qHmd3x :iw|&GdHFK#nbn!B +WL 2$ Ԧ];e' pu^$TR'+fF dO颕ϓ{YЃZ ڕk'61*h.,Y⯹m6A pe˖%VXXx?/@}w`،z V]tQR*$)#8v{ieoTRa"Ӓz~w{q,D;ww**P7vgY !лwoniYA6\r A·VUGQ^p#<7t j\fN8Kcɦfr̗:jqPV%ϟ3ia_4^zO!/ <OcYpC7C 7pC 1`` ʑƟWJx-h8 Qw8 $ĬY Oa̘Z3fs u! ZW9gϞը٩PIcj $*_]o?œ F8qw0a/z8so=|7 \'$mlntҥ,y πkpkyїEI΂ ҁNaÆȻ|Caw?$KܶB@N{. u3P4NX - Y(4pY鐘,`& c5TɫWY6:#>,y˪e&N. >. `y&Og\te7z8?OYCA6o*_yNf9YEBDc^.ZQ߀ED!I:6lؐek׮~̙3}yYS^gܟ3@޹?[n!AM3fo}:І'~r5jXj`3wn#q=^ט䜂5^J;X}$㘟~>ii_.(Y0L!OD] HO{O~ QI h2%pbOwwv0@ Ldߦ%0o>wi4Q}y*V9眎udmٯgmNj5ù (D gesWŎ::j{^)>xЋzg}(Fex_+'JB(聕>y'$>V{W!@²TD5~Ps"N\70H*2cpMe0Hi^@:cf9N~)1d"~Ľ|8 6[IdKuYLHeEbL+b9 4?Hʌr8ʓeOIWNe@\:O@[ ,J9q?q]M{!E(w G`2 7?/l 7?."{œ8 h-eE 187=}<3_a #+b "08)Q3LMA+ < ~r+o0Q$]9$ sgmMg3 !SJl֗=CN{x+Ok,u "1`r~j4S@Y;V8cP~9&E~Ay|jks6DeXs͐Yc|Ou;RTw1*`}wb1oW֢V yո\=.Xpa~ڙpy\B&08-V.1L>i 9FJ§9 gGEU/j24|7 1Jj`j!l00(aP4d@ps|b$XImn)2 # p41iK,tpHjZ%В#3Vt0r<j%HH}ҁ[` Kr3tO1";) bs>F e-E, ٩SbƍT.x\)τg&!Sn;`ۜU\8}??.'9kE7{%^{tpVt6N\> pf?'/,/h5,?~4x:E甎"G:~a;.ios.nlo*yd6(Sˉ'Z&]i1hQ c>cPiԀΧCH pDl/,҇ 蟀( 8ă f^r b|7mܜk\WQryì(ig~Ogʭ;WV_}ffrhHwn(pCv~--I-O`33N>@\Rj&cmP}Rw\ &9P@T1X(xpMVo *4 Bupo&~y&k8'@:I hs#$Ty1&6A5 aǘݫ}n&ƪE>{VqmQYީkQGVvNٓGߤuN:;imEi+uqri(#wg8{"E83x@y}z':e=_R> γ=Qlw93% 9&I +7z6 26;v!uJ0n-($XE=BYxADŦ5'\ۄ[Tlٲŧ֞N^Hx'Rha^~=Rv2|&K`%xط؅KK.ī-;Ᾰݦ#`g==jJ}ʚ>uCC~iPvyp769ʜ}(CkWO7C0FNlyצ^hB59epqJ]a;wO!;5U*4U5*RZqMVrw8=ʠe`ԸBBDT>_йU#Z/j5k,RPV@/t\Ƅu(//tQZ,=7j) /=Ɛ{qbDzÈR#?]7j|Ԁ5VƕC)A4t2tN4*/cV j M/$4|ҀjƸ)xSQ4^ꤟ2xk &Rn zGڀ\fM\MC=D\`aeݿ=[3Ē]ݪONzPJ,_wMpι ă'P^*Ƌo3_jWu'u޸CbKCJTaQ5IP=:ɦdalC$r 5GR EpFs}Q~P4'ѽBP G~<}j7IGF5+ җ_+ͿXyLunFǪL}.vz*m(qD/ `,/W$LmUK0Q`3wHFU>]^}/Ɲ@@؀O@1hW&kC6'y@#@m-[x<{`)`gg#192ȗÙl^1YN; oX~8)Xrg9!5Qz7LP^%X$s?; > D >I{e H/\{-I$y抲fQ#M4zJp20XML`RlH2Or$SNI+VO^Gya<82»$im+yoU_*[Iʞ":.%N)Ss()suvƔ{-b*IE=&ڛhKvcb3B|ZÝhy?SBf W*jxң*Swjl 4r5'5!WCRsa8u>ruN/K`fFNV'# 9k|MˀdN-"ZU5> I/iYж6 ݬkf2ҀX݇so6s) UC3a|%i@h"賶-܏ M!܅į0s+.߯0xVWۮ[oX[˹Vjq{zEJ鼷8׮xy[;m?^ޥQ<"yZeа>]U)֭~p N~VLa>/~>?3rrc[&-PwBc=`7tjS7zv+wDCsLGf)sR .U{}^pe>|ʫw|/h>yIFnщm2r6;i@GK3},L0Do )*N9Io#lꂱ[}7|LҀGY)M'b$A e\ RgMJ߻"ҀIz * b8}wUO]*ޫ,Wͽ]{[Q{|5^T싟lm+a}_b&@.磶ݛ2ky89c&T߮Pӽnfs} 1o6n*-ݓ|>4JkMO>^.]ejNWr+?i{#Vi]ˮ2}N^.TNVhe*ᕐ\QNp1|@zLOht"}'̘&8- xae--SiBe6LPsp{qӾ`xPa& %ip Z8~'3*po[b.d`'Lf21JX@Ǧ餃w|Dپ9ـBUOyd-qgPzڇP)U pyXyD]Ƣ fǤ;cZ9[݃V([{=$l*58kI3&x^@P'b#a1dP  ˀb!eF7r7`xgllpyFDWo@Zԟ0 w`-ug1\wHY1\G;e@ ؉tRWQP~jK|gr1Vvv%o5%I'8D\xX]d.{sڤO}`HsW٘ZLp O)[ʙwvz{ܭ)0G X:Tg|= 1 y 4fxD!>ྀR{_IrQB(.$Vuy$MmT{&,oy3A@ȨTF|_ LT^&\)sԈ ;]p.@ mIjQݻmZ zsyUޤ[7s*-ne<,ӳ1M/ nU=6K]pQ OCzWy,3*OE%5X ZyVd p(^6Wu;.Ay+x[%s}N8ʍc~ o}swd'3jV˝oAK,#̡<2g( 6 i1r\ao1tQq}>D*z b4(;U~WؠW0Q`@x^ g BwB X1QqTMcש=Gc30cG@iKs&X\kթ5k[-gHj.8ʢnKnȄ^:`,ע@Wz#(ߩX˫^FFbI> |oޱ_0UJ)6^}~|PrDe `RO+5trFP4DFlakܜծM!Q#*ۏZvse+E>;>h|p&+T`kVw˭w~2b +%vW z5>[a1J`VSL),Ѓ7'8@/Pm>L}vq|ҷ,Y8:F lI1A%Sh0IU՗̵ R^ 0YL N˽`nuOpt0e/kgM Ƹa7xQPOQy{~x2n)ax5WϪ7aD g񒩦0GzR)xP4]+es9SUc)S\4yweWA.R? 1X}4aO:"m/=Fm{?π {XVE^zϣJ6(8 0^&AU?=rf`%eRGVٽc\u"'ផ;Hu.`T~91JR"loG1YLyi?P^}Eʭ\?d"5ٔ䋠4ݡs{h"Js>o*Àc]I{½OD^jzi_LxFuzNtW]$^s6AN=Nt=o4m&kQ=dzg%XЋE}Q馺QwI?YG( .W3;s&׹ir ϞrB\ \u8p}Xά p4Nt6rCp^M@H@fܞ l6CIjDb P`F 1SFq˵ԉBÇ`;ipRljYt>4P5~Ӓs$̠1Kd_ g .n|"iKآLR2u/'0z4^|;Zez)>d\@`*i0d1p2:.ֵP>K`,?K('z :wr1?.sJOOKߦ]6nܰcq f=maeݲhA>pi&~[5kvHܸ"we*?*{ѽc;p{2;(3OpQ 7w۽W\~:wڙ]r׺;F_U.&/^);UbwfGJSwzћչYȻ|)>ɝ}xEΐߤ-1r'CHQo5$sP6^&R1y)O:'Ks+cR |&#y5\u{Ky2Gw7DǏp,S[dAT)/d !rQ0D!RV ݡNje2O@gKdE Rʚ_(+'&#;ߟ ءmޱՖ]LUqn?*Tk衮WF7mן_:N_]>~_o쬯\m; `.`M! y9%-SM:x.3kqws-9;Hj*_@_MT{蔬nv^sW_p'])/Tn9 $aA>V/\ XqnwZ-Ils&v?"b^$;}{J-/>>{%S}kj_/=@X8qT.vtO29,+T]et[i. &+w{W9 ԤCut豩t,g vKΗy()y_[b@]\{| <_s#<ڌIwg"bFӽտ6w5JXT(DMxYٵQ8:ȻTNlWlX%@i(7& /SާZZ)Tqo9Fsڻ[eGgx֤C[13 ƚ d@媺@*+lHzV6\թL̤>3gg ʴP?ui/;s~ڋ ?{Lpo<+eVX0q$>Ca\KuЀDU$4j{5@OHi$wt7 1@42HAA";\)wƵFiϫwlpV f\`-;@JW (qMT:(s,:5+~ 2~a@ch;kT҆V^@ZxgiܕtsV$|<᷈kn!]:͸d3-!8 )PBpطwPM΀=5{'al*6w[ pߗ&R*[1~ж-jTͭp)tWXU7N'3thdt}cw"o):_tsw_/ý 7p\N`QX :g||e7xQr!Sg{.2"e& ߼p1Rv)eAn&q|Vaa +2,}S\6FmI1`g|&R#lAg i2fP2@)Iʒ$QU.'؀Uvl@g@w+I>fJ{@Iݴz-QOSfQ+@ `c\ԺcT=>}[J}{WpqOPq2@U*}Q ͪlp-uݹ4. ri.C\#H&?~>=sWL J@PO~AU{]$+9_,AAAk +>p-D'kΘE(W:[UNbŝvԞJS[s5y:dȩIPM] ȻU;*2rKǞTw#k)Ao|sDĘT}'JCԄ! ՓD9׍spa]ei6U;ҊM̘׆FΡ|)+{o1Oُ4F KyZ-uNG=ޓG2aR...t"ITbT`&WQY2e򌽽kùF"mY=6R=L[Q=.C~V6KW&(5kf)>5_v FLfrd8MyvQ]Qj-8pdP׺gkKٙlAT 濊"Ha?jlh%0觴bkX*SoSƟBp7kֹ=\&H*[ɺRz[*O >#rO- ޼ !_rIVuiuD-Ū Z]%ʹGko1<+ {:B@<;3pUE hLd5s|Nd\SB X$&[/{.et Cu(s ݻ>]I.<+@ZOK0| RLg{pfn;! JUgWٶS9ÌvCuߝU.F&-}@mRČE S]852l~K:KzU忆M-ϲm坉f HnϸV Կ3خoKu:彰 O:۞먽f+gI {aAWmukM-7Y;hB>˙d6N*Qq7Y$Y4X}wSOI; ,E hb,Y6‹ek>,,.]qOu2KLa+?Q^ W@+{$1&_SR:D:->y JyWOuid8ǣ4vR4{ $l\yRτ%>*Ih群B%\_YO;0e!w~\ۨi~nk뜸قCz~#]:r0 ۿM *+ F2hܡl\/y'/p9Dz6㔶:/߫Y nf`ֻ̐'_'#\O҃ wm~1 5SXG?jז| ԭzHIͻo _5J/џ'V- zŤƖep Aߡ'O1uUAk sƧ?n p?d= p Br*i*osϟ`pb938D;maY$HY:֧U0i dle4ILܪq<sTUeNi\4d2T`r0)HVɉ4Nq R{d1A5 \Kcu7@e:X vef[e/~X&; ?,LcEϓgGM5|^SWUQOز[k";="uF%,೎G)?,װa*.7e2FyVЩئcN~brLY? L]69>Oj`q=#$u"7|El Ww?DniԞl8Q{WԀhiK(cFgI!1 ,aWKOzy(fBUq:vf# Ԝ^zE/jVu MUJL|07JZ^H `:SQO!tdp.)eӂHT=)В+3hĘ |O'gZiE9p ( y=:w(IXۓ9kȧp<>sMwk&>pH8,.`y>oAے1w}[)[U' \`h׷W'|\ Y-Zr)We4lzswi!SuEʭ׈:ZB_;\+Y $0E^1 ɮ[-6Lag0mA;.^dObd J)bj<e:D rh13\QnArWf~&cǾ#3@xm_[ߌE} mL!16일pww%3w kEK !v4b tu0R{ĵ_ꎋ9$*N|@ZM9x&u-k>LVdI})o8 #ŤS_W ! R.}I}Z0@~Qs\Vͨ_7x8ܠ**`m>O)eBUZ TےH-$) ACAm#= p PW0l쉄&cj눵t|lpugW.HPe2Ȁyִf^IzV-H3P_4Ϝgh-ma~XQ'Sm k/074XzL RN73[Hh\ ʯc=@p5oT[̙JvlSHx(Fg '0\#w z&uX [/ˎ\S975߬k :$ :vMq7:pGqm8=×G /XCaaFbIT^.+ <<}8bT4c)IfTnr`,˵;\_eӊ|fwݓ]ݏ%묙:Zgo PV)]}[o}]+(?*aZ:761~ݛ4A\71; #OC;8Su)@\@tn2\c}NP7IӔe|Nӷ{rV|SGsxt5Tλ{\?R[RIIɰl`QY\6G#\($J,Y( =YUvQt+ :jt kv7O p4arg{1s<ⲙٜ\{Z l ~.3q[ϠõJb?_p^4payon0ͲXx}kUAeKLΪ DZ[.cOVY*'KMaWyuO qSKJG"t(:DW w sH/tPbJ(V|1nv.6L u\fBϻ&$ո_[U.SspU057q:&z_?w#eA_eyP{(֪Q,\Ho,Ï:^*к6&DE,[b3Z *^g7[eZ-ꚉq+Mi0E({~AtMUN>R6vFیMe 6^̧z ڎ(uV|{;ܠF_c9{mԮL&f]n-X%Wewm~G2‹L0p bL23[vD۽+9GRc4`];l(lPSDqIS1J)+p#2wl}'Wjt7 &m{{;ϗom5wkШM/Um\Slޟ_kk4fiuBp qKFEi˧e?Xi,œ"k 5*аlIHy7xtGBboVe3 (7I@E .®oI!បNi--X)5* v](i.}hM 8XMޏ&`/RnԳMgQb >ZROԪ~3^c?yu(]@-c1[`; F~GI9>TD=C'U%ԭt{(+Xɽ|7صQz~m>S޲g鸣p.c9|FmW>[K9eKVU6S] (%E!. U6q88L/ayOِK+9_ skQfB,pT )5pM'W 5{SJS(~2:hJW˘ F@X\(ծAyJ|^9m.z@ uϝ pMJ2mV5N;@>pul}gmp"߸ A;P@߯oRzVQY ew[=j?eG Mݒ\Vr`1.anq~}>m "*X1Dlz<7dJ\ס_mǰZyeӱiSwr39bǮIfWzgF֢%T2Px,`9\"@O ?p2 cxIVTMWOǏO (X6GO^d8xs5S28OS\FԬޅFγ 8MSngMs XkwO.ޑw+uC c3hf p9w?ƫl>e+rb4pC!SL6p1h ĕ %0˦JN68/ X'2Fq͵lVtTȏx t̀²l8`$(POR<1cpmY>cuN0&QɢN(/jF٘q%poRRR|ͣƲ/cl$*\ʲuNaK ;_i|\P`Evfl*{΃D8 .Jes^IU9 c3m6Š3{D6=Uo ]UOcˁv .B*#8N+zaΕ Cy/z_QwZ,yGBceR~Ɍ!G=C5KG)[U4[6jnbYg x8ѡr-`x5{&fe+>;7%;RByŔ^h@)}KDdY(E:ve&4ZC`3`3 ʨ4٩|Fy\i:x()^a74x|LZ7*iS/4 =tu64lFVr_rs] p+yUCM{n'}ܸ=>'ǐ n& =8 wWZP2f==R"$]>LJsyEq: ͭ]Jy(6W^ H[rrZL!b 75P#E|G{HǠ7dg2pnq5cAfS Csݏ]m')O̠zeN^h hok~"f2v{k >Hm51nwk AQbO&f[0vJ tOm61{UxZ;/+롯 k-dkh3>1jV vg.iY mfmyWߢ6/ !v9 @ ;ξ^t-2[$x+=B-6&ְ,x Eq .?R'Fph*;5&{~Hk`xQe+ hFTJ˦MzoG՘UƔzI$Qf4{`gN'^}e~;~ Hl 6\!06Lg+GyPxx0TJՕQg8` x~\@p5/,- '2zۈy&Vƭ jo~OcXʆDt)367t*&U1<S'6>1WB)X Y,vpNRYlpJ=MNy!oKMaw\k#`S/[:NY}ѡ1@a2 5\-yKtk He22 *S*W;0$c$`$GPhEmG ]p̔e'Ж[ҁ2Q>JE%wJKU/~x,й=0DGqegIYhH]UT+ƏMwMPP.yƠ֒/^ꋗXʳ*e-HС9:"ly?0/$w(]dBv|C*ӑٵwntbgpǞC㹶y쾇ne@p{l^VP.8וF*cc~EB cg`Zqe\2bpG1m٦lD2O V10hw'e ]9G_F(v_`A'.7QN20e `uCN6.vi\(IQmڮo@Ivk4I>T1~soQ35{}5۷W@;p=tm~⼯!-V!ud I5x+}ˈ(&:6%n's]UQ+d"4eTHQ lB- Hoz-'ry?wXI .%c'%%e`HDG(Sigi~\ #& P2Z@x`\M"$#hOݰ ).d\Ryu\g݋GE\lMRעo'ݤ U9v渨k\| eNk8=50nz֜ 㓴"JWqHۯ;QT{]mv) iW\lbeu+ljRu_Uޭl>T*_֍qg'BIUb<'@M,0ŇWdF ͳ!_5MPdC{+w@ _%-:W0 y1> =:s3; \罢lKP'_ (S? ƫ}FM9jY_&!P .Џn(|p_T * 01(pTԦ\s]l2W\({iXd0 2(eH@%o]pyi YJ%t*@V(P|ɏePEڬ.euhͬHurټIBM~yttJpkOuͪUSÏ2-( z5oժVM7K'7S^spQ͍?V|Ѝãg^jG8:CyK g:`W\z[*::wwh@ʫh *xs>x䚩x}G˵;? ݎCΣVݢO$KXNhRisE16?^D-C 09 p)-s4쪻/P p#?ǒN 󣁲h³hg" FLXI;\ `a$9 Zѿ.nƲMR݄ykkN/, &/X\'PQTVXptmH%]f;c#K\,$p A &xH̱S!]n!d`3 B&e꫰k0hQ<곰|ގeBf \SRrhYb<XM+3S}H:R92Ƹq%~_ _vQI[.xYo4M|VˢDXn}S5B >,@AULzx@|_`NUZY*@7=JO >*L}* Ȥҥ:?~,gVY4L6̃JSOj~YUn鮣agg x%X#zP 4omi_'TL٘~q9h)M[ jG&Y92=vOzVw 48.]U{+3, ˗,dH(MQ$u Aaڑ*7|OW/~M뙫Jl bA-MSٗln*ʻ的Ya #Fceo~\oPZl '&n]_W}'Z 58\q\\$ϩl4ifգR<ՅTo`>!P .KA/, sK. evPxRLRԧ0T$uz+hf.K BCdW/neC31&g4~W\2[Fa 5`؃T]p-HgfU.,9$1piU\<;xTړ܋rT^/]u[竀YA⤞y]5PK ͚z؉ Yr`gpf [;NOJy߶eʸq0祋[ע*}>O^ґU tR\/J^ ;kѭRA:`|TzO;yIG׺;@֋/3\霱s7p_~6zν)>e otUeFhR1_sSVNធ1߷ >IK@'}*&h01Ԟ6eɓtmRv/R'EIdZ|;3~ !Wkx = `1\*eJCZAiPFes0,D`2!p [>N!-l e]W(^tG 5pু2AH| X;D\]C`Y[EiKbh<AC?`_SAN>cD&jf xW=-&acjX`(boy\1*/C6b&QH ;mQ-ccq>+iн|;B=M.wH(m[Tn4\Ah^SZzH󥌯b؏Lf~%l"mᰧa!Qt\'FM)UPwO5"̷]qr'_OY*gcQש 7(n ʯcs)Mdwϱ N4?-=<_G1; \Sh`<7r_M嘞 ƿ՟_>XۣMC{w?r3a$i] W _{C>㟇25:$^l /ߛ*j S+oi)5(D:g 5(M/_:l xDY~qA|}em5 |i492Ƣ 4eFJPXM8mU~!P!}RO0ks6 ypOi2pXm: f `|a=/v&fr90'eKҀg99wɘQkFtYiqؙ0t6 kFfOK в;/+%ޯP޾"vnag3@? wgF|c2):=DqWɫ~ݳ}p EjYl߄L.|Բ{AgxfwIDޮϨ4u,: I_MZ aMl}p.}Ж:aDF"2˒@T *jHeQ]}i t03O.Ь6BEFfsݧ &_c@ϏnVA*_{,#m |Pڀ@ij.M4Hl!@SϣXˆ(XJ{H5pS0u7wՏi_uӆaMp,ZagiX=AIiWss^ DdqlN>-̬!찁tw Ѥ#>uFi@ոB9B< DT[V*oek[Sfvp+@;RjzlB e/ 60g hy| | j_\%Q1#1 +VU|6nMp{Ĥ,+YoQMIӒ-`4@ʹRIhPvuV3z>͔) H2TB(C QT*  Ei_恆_(T2U29#eJw?s'{]Y;}^{wzֻXisƮhrU!NSo9Y>:**=nql;ctshJx^~sBʇ~HǞSl@+?vA#s mSIv8Ʈr W}pW隼³הYWsڛ>%>>1 K?EFUe^Q2B/xOBʠ_j׮]IWCI)W{W껋 =Z% T tk JZGo,݋Y5}Q-FkoU?pY1\$t\MP>".$h-U[y&3\ɀA,"(g4נznuOtj"GxPA>s(g2[f 97\۱}we{P}}C\^Q{k~9%p͏]1} ~'\|OQَ(`9?Aub^=Az$_sDlLyBp>oe?$p)l@R P w0jץE:n;ae3\c ~}eSVSf$UBfMRD;v&=IO~R ]cSߋdJ#?+֌rDŪJhoYAFӧs 69=~ J< KLEž@m/~K|xvד/u Wz}RP}>|?Ϲ^ a9>UHWmj6FbNdjz E= /5LG%L|mGr^ߑ'p_4o_]L*Ծ%v"pH}NdunlJ#p =]"GSyUXuj yrju^ش "jCw3AYT a5.KϽ gH)~G 랡UW":a\CrHY~gq qȤ"!I FS?βޓv"O ex0 Xjs7⏔ MH #;$d:$fHOG TūMtFBB4GHGՕ{"]Y]3'!ԃi-E۾ оQz- d?)mP1esxfjsLD WޝjrDILIAÊe+N?.?{HXAYQkc \yoY~AbߪYzoy>P?ҵo^ffb(ZA\uh^DvՄSC*d>q B15h]#=O=o /Lg6CwCTiK~'z <{uwl__U'uJ\þ)y]9;~&}r^rWQSܗޫujUe<{z)Ee?s>mvڣ81}36 p<л@PAG?k0uWuHLGBB@>uŢhѢS߰KuPgeg:79b gxGKw#$H⦨kQ5r.8jzq t(f3I3Zjۖym{]~Qn滐eԞ/|Έ>%Iϯ86cہrX m3Kn;oT39ئsg|sIXÞ~CI<}e:X=ȳ"gYK:}otc&'d{uwXR?J'K!c~:L 9O'~Xn!r "UGdrO_ J 'dJI['eY5ߵ'D~X AGHebL# rn;$&1"+jQXdr@j`X8%ރX-.y{LR92(VQ/r?a *:bGT\;&|>-!!l~D+צ>&~G}͹!i@Eƾ#yp !0yԍzє!yq Adv\:2yx}w1ϖvmiwS8SM:r3̑Yu>=v-"!~Tf =6FG^8^R:D9$B *;Υ^>fKgʼn*W]ߛsX¼t֋Du_Z37yP3yc:mW'suKZ2^v:qΌG!ХCuY/D]˷]/뚧xgkk,3LO/ui7?5K%u:lEzfe6)BS*}aC{}SjЀpQ4شRp_NnϿ4PJڙ)B ;G9u?/ {ׇ?;ls5NL.}8+% ){[A qvHfÏa/yK' ::*-J2e_  O& i0pER0@ײL6Q?fI'$/8DPdM RR.5C/{5c$*[ƲPWT ^1Y ]6y;Q=bȽNmpv wNl g<O@m (ǭS)t|wk?t嵞 5nʱ 7HwG;?ZXQb:ѓ· wjX2r;0 qס=O[ȰȯwCB/]q}g_/g: 3EoIJmEDl!7 o"|Zi-6!ҬGVm(=#voJH$ЀbbrÓvɌW]͊RZX&HE@.8bOq,!$]"!rx&0|Nq  )Kĥ|o0>IJ'>)ס ! J|Z"M9ץͩKj \r(''l{xr b+3xI8v({QXkȽҋ DȁB gkWOHO|;o=6߃wOo11Qwl9' e/mېsqNڄ/4}O4nJ8֧/0Ϙ6xo!9m3 l!p䞙ԯ  Z^8$ABt> A(/t /<>7+//?Yq/03YtNC $t ԃn:~e@!`IOLCڼ8Ht8 8 )|2&Aִ 2ɤ( m A۾ĤēƐ>1!{f&`cԎφgkq|^?'uOx 4sTf?l߂ y yVE",ȓ]Lå -)*bwgS@"ݜ{ັ(mAfQ|{rYnhHV[>:秝~s+LC\D[b/p4pF=ah~C/إHà餘924*/xp ^ Ko%/:٧Ild&Ծced OK@`7I_'T-#,/1ߡ)|ss ^OQ]RoONAxe3HL|Q' x6j`Y{:0ڔA9!pd$W;2y^V![O껿Fg [ YxL.%9H~BGq'O01~ƛXA$uy;J¢L7ъW]NqD?#:&BQ r{o2 xl /|&OV&q== [Ej\C΃qEx2E`+6*H\R)-j[O_@[Hx~G1d |RCAY~MCV8R!}D7{KQ bp6`K.C )L3 4cR.86M`LB}ft ry3\+=9D G: ;_CBT:ArM{ T\c.9S#?973V[˘ĭ-RFfSK=bY2eOsMڒڐƴd/v.= 3 \S?Dp>%g@0>5׊{*ˮP>b%uK 3ln0M8(HcI.~9g, n q !-Q2d "8=|d(X KԒϖ˸L~WBL!#9tQkjWw6#p5.WxdȐ`ȑ)k\+F FZ1``0k\#pX1`+F5 `\#pb`#p #p `0k0\+V5kŊ#p `0k0k#p,Ə,^8xm:ur,`#p Fk0 ;ŋ0XifV 7ߴ/A^:{0v_ nҥoh9}bŊA:uݢ#0׊#p5 $p+ԬYӊ^ʔ)˗eͲ#C@N[/#s>.,SI!p}ѠCڵk[Jiܸq/A].voν+ۢE׺w~"t7|sРA@;.Jܶű槙am +V_-\ .\`0z(%OaB޼yoZ)ʕ`JvJ*sWTh8uؐp'?me_6f8na1X` ][H_tqº;=y[gS_ N- ᇟ?ZrQG(_wi"E}W [#HxajF^}՞_~y]w]PZ5]MW%pmqӖ#'̲b%ʗ\:=sO? ``0 9I2ARX;3֮U[=(9/[wl+Zj׮ʗ_># 6 F `s;HC۶mTΊ(+W,';,le ir6lt[`Iv?KuyKN8!MND,PЩ$y䑤^}+Vv9*rJiI)?`ʚ5km߾ ds?tmC >TPysW(WW)RT$~W`_VϠ~퇽mܸ܆Xɶ<}=iN %ׯ_0j( 6 F `s1/8 f fΜiJiӦy0c k,*'N +nuF,Ք)S-[kHyGPK=sKUK[/\%l׮QǏ,Y̛;/={~|f̘3@ux\o(E *}1DŽtP [б{|pС\a{|뭷ӧ;ۛ3g~9>JΔ~hSLqC 6 F `sl 0aS >܊l/ 02dGN4l0k #F \:T ۷owƍ(B .u%v"ϏJr-fΝAy'֭[TX1ҥKW^)-!.>`f[(^ڼyۓ=^~]wV,Y2@N$(}qJa-}BWJ;N:e]nSNwY1c?\`0 YÂŋ;#\+V|.Akѣ]0&+Xy43mڴI7Eպ{_FR?)SfkRmb%y TjO}ʑX+W  0bWb bU52e_o/"9r{Ͷ/m۶ &OH}yW< lVZ ?TR./nz1 $?zhsu7n\E]#xYqM tk(9vXXIG`0`0 `0?&MrZL(7H-Z8_DJh#)wr֭k+BOZ+W.Cp _{Mϟ_ K4 ς?ةuQ͐bY Ѓw}=_Uv.v|rG)Rw_.Dz?x7Qd jߚ5kM7tS8R8't2 7ȩ 6K.g0 Y#p `0 !]nf*Ӿ}oFC"*rWF#ԻHQs3)\"s=RyK!X֡C:U6m6@|Ay꜅P*})t1@z5;6 C0`0 `0֬Y,ђCXfP(eQ9j],y_Ν;7wl r)-y߉**0.-]?Y @|0Pӑz(%! ɫ`{%O2pᐦ;vH wի] TRfz]w};]^lBW /p6[ Hi[s&*;3ܭ[7G:T suuT35 `0 l_|r l׮]DP(tc7w\s5WK ЦT-Ydx+B qNNZR b7tT>.eԩnY… b0y@6#؅gM qmFegu);_p]2\k{:>\:#tk׮a0i?@N.]-h?{ဆ`0 C@@̩b_ FA%2@_|oVTil*N%0i"EҥKoK!Cj@AkF;$P͘1ÑŤ!I3vG'?&ȥL\5jpEvqqNzН AK8B7Uxo3ߥ"|}uŋӦij4$ˤ`0  F `0 >=믿\~ډ'$ʩY~~wc~{ݔ62kWg[ɋ)PSVRe] =O2wԑGza<yСy];Xl=\ÇOrBb J*!w܁]DDu}ND/"_c9&]#w3&IYGf͚U%1'ۏ;vG؞l}npp \`0 `JҌ,/3f#uERAM Xv~g@h$BD))BU"i\ wE Vh+Wt (I@:maC4Ժ("7)QB2RbAS~mr);#mԹ`Ŋ5y"x' B6HG Y9kse[ekŰMJQ~]G*:@ i0 5 `0 }fdNgQ믧^(b!7F\lnM^dA\ TS.T(x+bH`pI! HBwРAaxH]Cرcs$\/n }6J[Sw(YtmT ^rjժ _N ?d[lND3ۥY`6ŋ \#p `0 !8y{.)CΆx$ 74n}O)7JX6GĄɄD7o^R][R~wԤ yׯlo߾n2s6d4ӦMsE=`0Uk0 `0 9Ə|GQ͞(>[r.+2sL}衇R4 uI "ږ)SfKlF: *$+gRZxAuA>}\N]>R/2dHr9L6#O.bk͛7@L`{(t=Z믿Kս PL&XJCq6U$ndɒp.W&,\x'xWxGx w a_`0 paٲeԱnC*ԹL!X.)$I@ȦQ@+%jIz:{0 oJ7 ^ԔʹMXE)n"w,y2xzSJ^K5'yv! >?.vǤ“O>R`o\\T͚5WP7{/W7=c2 D)(Gs9jt iϕ֭[, ,:ϕZב\SG J k6lpJJCDŋQIaRW6J,HJvm[hNݺu)Wی/N- u]7CsR{ܥ|.\wcǎPHAl`0F `0 !,o߾}#v` 2 $K H/߭[򠋔}vR@KmSԆTZ~uC\Hdr\ABTySɓ':GאmgB_~e% g$FPzݻw@ޕ;ܸA:1p 'gy:HWhѤ8@B&C>.w3 \z-b``0 `0R I?H5T,._-H˃xl %bkq):7@js9NK7{O)`.o Hll#GtK͛>r/x<9s8+ ߝwq.v v "(Ilp-d:_vQ蒻Y&kRuO]nf3׫sIIvRPglP߳h0 F Ƿ~ri`ʔ)NͳcS)9  6af+Vt`ߩS'k+^P|Bڴknmܠ[`y9CЊ0~@lҤO t]J]ԹSHJ*(P}_J|qnH7RJ5djY~4Ԫ{} {~9m۶ul3+ꫯ'v6$ΟbcStO=E%}=I 6iӹ!]f@͍_WdoQGVv?bF `C5?fGTnݺmb  K -Z1KCBLE$ Fq XBPRYHk+]#s;O YIDp)$!s{챔1槟~yM7]r.8餓ƒ:(j(O=԰z?Pb%s.Oiܸ 2go=;Ғ7ƒnY~={T9awL௨D#>HXO%rclpf|nΟ?#K6l8I}zulש!ϟﮣcYߑ}/ROJv;oU7\`0 |1(KYf /Y."A#ٳy[~}'{CaZ=UmBAlҊl-zȆJN 䌵Ǟ?xpWlJRժUuYDFPӆh)l 21x9g͚55 yYm[n:7&C hϗ~~{_;yAE b\H5cmM& 6S%"oR*zޏM: $~5)\s5Vݦ7nt95 !–U,ꪫ;6s5 08L''T6@uE1+:g%Rt8B%|Y9w\WÇe: 8,$Xenv~hs L#lA,i gԮ]۩MUVR pD,?.lL=b!UI%h7)%VdWѻ]'K/I&A+Zdw[˗/ϦZVkV(zwh[?7SmGzS (VhjG޼yS -T#B|\)Zhq0*?:*)07h~f]u!o\T(_opg~u[zcqOn?6}jlO!0}ѫ_߬])U#Z3ͭѹVmQzAVvqc1&O!1`0 ,ycYҦ"9k>LaK[DڎՀaI$K BQ2iKǎիW%y *QKT0fC*xt WԼ =`~gٗzGrО~z8iҤz0!߰ՄBg҄ |SeZ{W>|k Cv@)SUf%Zl`8 I6啁UNC'99 D֒zTGb?oԩRxrՕZם wXC`]mrTTKm N2s8^Ü'2H␹l`cٲeC-_~ե*җsoذc"T1ߥ .jcm!I-Bt~beolZ>?s pO;~XXɎBG ƉRċa&J޽jR ֈ P_AtL=JU-i rn);4P E:Ny5) uU-LP'&V釢yOP'Vḩltmj?'&Y$fS0m[ ~T߾}V. @.+Y_"?j|!tQ~^T sMwФ{Yl^0`MhK {W{Y^j1X`AA+n@:'k ADSUF ŀTy&q&d @XF-–XvBw]Pc%lO-c9X"\Y`>-5NH@/D$X~We" B KK95`q (Y[p Hh"G8J_o,P@TwAZ!6\>V쭂)%pAH[Y4iԔQ+&GX1am3W^yKU N %tkUi ;,MgM~7h켐sud<&V*G*C2A*بnV(0I`5޳f /+&GQ _!Xm5GCjH4a/&Ἃq JrMQJFS-8NP0tPwM##D>P'zC*uE 8B2?>ID6`0 SsݻW/l)!kqݰCmV) CPN"S׉Mydc5CI[O+r7ibH`wH\%?hE _N'2# &Mj^έ )cY7EWڄ#ȋfelҦ{-!ﺤ8!A&G ,D.*pI {ʍ +b&Ҽysk;|bU"$**(5X-RAE7'=D>Nj|mG+eswڐ*z*R[Æ'pg$?ʉ?C>*c+oJ8pKy ?Ps_4Jĭ*!k9dnmAj&ݝ*WByM;ʧt{"Sԏ RنyvI"JTІQ&a)x'C.|?;P1sT[T_(){~,6 `0dpxYtg>EjAmӒPN|&m;<''iGcPEՆ (*PkEAotDFd .G=:b]>k 8%*v̡+q _Tքh298'j%ǩ>jY RV&.#p 54ݷ?1"/s"9Ⱥu\Mx{v {?,c2!VݰrfVs+9S4FZnM3tpD1ys@ ) Vwp `, ؅,e)5d'N1I5RS,WMJHqꋶk,#?y-ns[+&4 ԏw#V&Xd-!h?oPH5d'5 !IutXFՐf8R@*#KExg "Vmu~RnP`DwH W;-mQJM{9H#gb:r--\,u)o$%Y&D!a+HE,S~[X+Skx0 ˨A.A}FrFoNGII.x?0ӖncIR6SwqA R&)J^/Jtr)R^yKdX*D_K*Ma PBJd" E@ɞ#&ى_L8u.qWDO3*¯1[87 &Y66\6[= z`bkOuݸqclٲlk+M [&6S:iw#p #p `&@R<2) B@>[mzfQ{'z˖-DBBe8"^+0 Y7ߨt8ρ(˕+ #J,'\SAʧ΃JF]k}Y4*: cTGk/g;`PmE}ʒ'\þ#p 9 #ps/PBCf{ %"+`JZA>E*=lx;\+_.+kYǤ|ʻ4THSV@B~o+$JG˿ GDQlfD; x|w׎(E*[dRX¥` kTo)_K5iR?6]FU4Y0F F7oIsْ+&$Wc/\pmu&MtQ~PXFݜkNk0 C6M'pORWl\#rwh@ϱڕiHNISAuuU"t˒WE"^ȝ7eRXlV6[ފ pQJs9)=cٷ-'M6UUpu x"f{Wm;*Z]/|%P@@hjڄmsDfdw_w>AFkiB{PDGX8-L=kί)SkRv KSm?Wccەh+uSAArMTP% ӄ#EϽ8{e&}W6= j&?rͥMb>,x7BuM؟|5@DhY  oAhB&P`sMM̙>_jUoK.AJ//v#K=h\Cv\`0 8>f#lyflUF@<L6 ̀<[3#(pyRRٮ:QJ{lh)N# 8Aa%' -k -gz6ZrTN7)""lFAtתU+(]rj2XZ6WPkW`!a HQ|"rBOXhmE(vflXF 5oe;It3Y+`lg|46.՚v|HR=12%RT{ &&}I՞7]~@yTIIIڀ4{́MԻgAPhsTV&"KAZ,@MTs[!t o&|?5i 6O?WC.p& I|rG)֬YRMc xi#4Wu*FfӍQ҃`0d#Ha@`A` X^8nݺy X}X' Uπ+ԫߎePaLy4.8K&9XK7FCN4W BQТޤ|Hi"nAIU].T2--`2 zB|Q..]c!>e77"!%KLy2SJPc?,|%~ :_^.\Rq+B2@a+@2g@IYwlDǠ:'=S`0d#P5!B]jVsqDQC4NvԋٱU IT cE*AUG 0/i HqBA]|gd&ΰT96>q'HZmQiof{8bcpd-d/gsȅ>HFrFrFR %携[`S4,Th[CStab\M,ek2Di*ZߑH)IuIyJ]W,9. K=AJ=!AA#^ HEPcCP*9B4Le7;@u KCa{įбH\~ߗ8mzG!dk E"wDL=.\W|SDu(gIj!'pekA|I~3?5d'5 !c!')SY&'%^8 o޼.8E=\[R+G; ,u!v!t&R,o# E 3^ʼnF}9 IӀTpѝ9ss F+)n|'Ntu/ԝxԅ +Puij7I=XK^iAPgӧOwj:C<[HwI VyE ze&/6ZY jQ09r]H~?m V,1"99 ! (ȹwҔArd9M>&vsY2Q’d)mg݌KyN| !gZީ)RxA쁿- }v?+rES6{Fb_;6&!)LaLcA,y Č߇ 8ۧPo9.ǽ%6EC COi3&ھ,9EV>@D+{RQMߌOp'\cI<3v`׼C;c6h/'ڼk`0`D`1PKma 9."בVvűA% iMp1"Rw"G52N8 ]q`cASDF[81e*| H@E@Z@C.+؉z;G$UrE6&~ QxDB(;]""#JUc>P`+>%̂6h/54֫W/e`56Wh;^>Dˆ! P|c "Z]9"3a'e s> \~@gAF_O?B.ATJ@𠊄UW4K;3^4Q;@C@v31s@MpHd v̌9}gfl|Ȩ X};1OMڎhm+5miADk&\۱W7jL/unkTY}Q%?M[BfB4FWOHHR߆3®h|79ONO_DMirs[3; 䝤,W>_ !9>ZݍiLcg!a"G.>EV~z11Ow;oLl`0@ǟ^QTGjT1 v!qÙ"/ d"^l! "px1A i"h_XyUTqAL"E #- G$c]TNcc ) >#Ձ8礽 gI'T!pع/Ols C@ݜ9DAH\Ᾱ>$O:@ŷAl{^KvZpp}Tژ`BNSr}2AkgNDꚚh_"p: HldA&HEE=N=J T9\ R@ Ҏ>=B .zuʕ;c4$ !':~,O} ؁` d)y%"3w'pCgcX9DALI;> vUTS<# б9;u J`;nS<_^޴΅S!ȲVHd'+@ﴺl2$OciBsI H&#ؼTϬڷяD~#'n\]6CDeaO*X?>lx޼woY%htP%>')c ]lz&pC`~+?Q 8Ƙ_3~r_G5V9Ɨbi#&x i.XϵChD&P  щ .` cx cp#=OI?}xu1<;A_x8vCc}}PA?`m]ү`cπ؉n`0H-`+% D5Q4J2D]9|bs5C`|OBO+vH+$pKת '`xggUlw48Nå\XO(''';+H΅Q+ UA0PEB R>@j{ TD8 (Xlj:(cܿ¥Zw;MHr"#UOzRuOta q Z ޗ \zU4}ƴPn*p{*N i.؇"p|26`yڼ.sM'-dSfcD:6V=W0މa%Q OLA(y {"JH_y}X9x 9Ei@~0Q=C~SAe  N#D\ώSbK(~gvFQl:I6 y油kd'4y? ! S7 a>JnM\"tQGmgz y uz3j>dau܄ċ ?ȇh~좻ƄaGW( |i1U֐{ivd;D? {lg=ӧG'{D?h,͔ #!!ߓJAA0C({,ED\_Է/RV J3|{N%aUQ{*CcG}H{E|&w&l "HncU}j,&<9&$D>^êӨ̼+(M_8O<X+Z4IRֳߒ]O@/kJjg._NvjDfR?3D׿G- G[tt&b00:;c4}wv#cï:2SLґ@z&rgl{Zgs(!s}57pj]W5t3asu.WiV[@7$2/yƽ}b!) .7q& {B4_D>>[KHQc\wFk0 u̼KrTsN^4e>WT"pHNXu*!;uhY`f qX* Qz CE~9el[ -#*)kt_-Q\]̀bNc17*Je\Wf`8ګ nj9L/hHQ a<m[6I(yVڡY J/BN8^#Eݟ;I"ϦC#gHYX JAϿ0}NJX &{)@nt#[U&}Hn٠/XGbcI Fg^@A4H'ewk@2jOkB=@6Я?2тK}++jaH&y΄h] 㼞 ҒcN8o qe"#\sD~]?DNs)Crlfb bXN5(U04I례oܣT &gWY+Gp&L⋈Pn? 2y{`TPCe 6Se6Rڨ'P.8,mb+ ϗD&T5e@۔l A[$Vх4 z65x'x<;Q[e>%Y.ٷ/w`J@n3W嚺*=cAjl {ЋId<(Ypz/-zڛcs2*0۠kze^".}(;Tgq’dOeIFOz `6S؃H*SD<(۞5MX%[ KcmSPnT (RQ`L(fOf6F[`\%ěM.AO#B1}JAUƄ" =~g|B*B[JVzN}!iHLd2UmH"3]-7)9I2)H">oN_Fv;+љ#܄uH3C(z@xQC >8~e;X;+z?!=uS@f}K,?ޚ 1?ױ~'VAZ23fq# L{_꧓ T!9%?+]~*oKLE;קwnD~8IP ?$6<ٛ6=XU6@I[JG'XRHi~yvߣ#=>c WJ?=ճX mSLoֳgv|>H.lLHF<+>8B 1|fj9E N(F `n͵?96uq9"h [D!r\!N8(nD"&9TIS哫''33nޙV|䗣yqHbKa q _*-pcRC6ڨ/EiRܺ:چzXY"Zs %ug%;jFu q'ҭ,.ߕ#ޝ/qN|JhOsmLHzgAaSİO*!p!e4/ fc)\Y'X݃2!Ӝ*+M!pe FXf 1 0UZG*C-#Kݒ E)(VQΏrڧHtMUayT=-"~֫D$bBdufAG漧"*9%dV-|nwLX )?=Dp.4cDިg: xWf c{#DME)XLCH*k{neutĖ{'ƆbX)X>{]%^bWP+VDMl?6ޥm?^o{ CA[.jn./20u[lk@ @P"HB}|_um}lS6]噱g} NltveJ"POVV:&5*.1O=HeE~'"ƆEMv! \>L==\/ߣD-4&́|G]0m3]f"߁dO[LRʈiFQ(1xBQZY>r"1d0emztIu2:狐i[$`*A\ Tz}G5W.YO:&E'Cx"eH Hfw 6 ɔ-z.%EaO dEЭLc!(FBGߪg/dETsE|}e&pAؼҷc"I<%p!l "tRIϩmǔz:H) $$ϭ2gv*@S,7ilN¸rQ2xO]IH QgH[hƋ-YVL!\?d߁| t4I^+FeJf{V~vܬKcua `Ҫt äS wct}_Ĥȴد()2.>b/jBؙvwSH!ҩWbAUwֿx /K3&imtQ>S{_i7J3PP|yVQDJ>gJV4PSΫ=:_J@|!jEH XU(eS66.1`w؂_Jkj^+o74cs$[y.޿1elyAϘX]F?_;x_c7LϿη)L5 0 A@@Յ8`$5^n z@ "4MP+GO7e `t_ʌhJJY:VrV!LoXE\5tlǙZfdC%y#/)FgT\N)W  bPNѮdAy`kU# BB ! p]txj#1OR6ACdBq|tx7YIO!۔RJ<ݭ+lJ:6@|"p Uqq㻼5v:J93^9j*Wl v\5MV0yHc9_|Dd ̝ :K͢H\ߎ@nbϪl&Yd }Brn_LY5plΣQa*^Sl!ʽ_4Wq*9fmyWVXvgr"٬.\S*Mw1bllqM#Xj8ʄs տ0l!TnPW;6ΫL0V&Nv8׭1;%=64[h-l{MVT6=BP=*Ygd$ۻeUߏg}j|bo$G1*"p5sqrP$JȾHx?EKw \cE> \aa>\.99IB0%gdX̑e$DzSrRc7-55Ə!fX*e9h1Qw"v$҄%iB/s6%ҋ4r >bl#fw@x Lp[@q8ѐ+ʐŎSjM^c{ت 3X'؛Rjѿ&#l'},;<32A@ .78Of#w!qثjԨ?`H}NMB,ӧ>.|'fSWg2T\ +5St\DzZG.o)Q+R>C&KMbפk9dT@ )jL1&3>ߕ%p.ߌn#EU.il c&zs ґ9l\}jlYBLN1k0 0 pE@&/@ 2"W&A6WpBPOvtl%-q /HI"~A*ڀXX`cH,ٳf=-0 7 G]Y;4ı$ YSZY,g /KgML1Ku^aNNa|” G#p^BEpF!tbC6 ^;dT}A'pAS @nTzb `nUnK̈́Y ~'=ḧ́A+.,oqC2&2 SKhVU.nX!pBEw*$UlG({dr|S48$#PKԷ6%l*2`5l^{fK@A%CT+uYHy^!o(QC%6>\-&8Jn̯`7P& xFt6a7Ev}dV=8peACA*ϱZN[q?e \eC7l栠Ĕ%Sbg99WAbB4(`@DTv-kkȊ\@dE 2j G2;D nGA /~w+S-6.nS7tMl>6@?@FA"+ێIi<kMO)L:ARDr܂-)׎ :pwZ|YcdAzX`BxudMRF14}(Fnm2p#0ǵdCh{i*\B@ GII7(= 6=O]w\{aCed?O6  ]&5No&_gJlOo(~Wd/Kuy-g2~<{& YH^-KDٛ)odșcS(ȸe8c:^BGrVYl2-XK{Os9C7DȬO̠?N qΕߧYµSW  lnjS v" P!2̈?}N@LGV{p`_ܯ w!]X-]oN/ľڥ=%p]B>]u 3 Ѓ,ENFf7( *p8L5+uW&{s E0Lv WY;W$d2s>q4Ȕ#uHr>)KVV{ D',1KufN޻Mqy@KiTAXK6edCd()[UĕrhoP59-M-Zg<2/K^8݂ "ų7*jS}5Eأ jPGGFP>HN: %KId- '{\D'h૎i Ov3DŽ t}3ElG-煼#p^ \l=~je3_ TƏܴBHV3m[ΤEuuRftkkB)4[\[?y^_d]Lqay|}Lm2/y^;:$s9EMRY^O$pGU?课oV:e]6dB ~&?Z{3ʸ}*Î0Ƴj(HbErjB.>cBԗO=C_~\+5=}kiCҷdO=1(=Jg|FCi OWFIp@%W4k0 0z&pJ(|;|ElLmHx4{ %نe8]eA9WI)nV!'Eur|$HcwclEU8 Ɔk,s}muA%O*cQ9XuYeA[e `kāUu-W)PI@9%V65*&pumYZZ7&#bQξ, lR6xA{{QO 7<ekj3l..|+e,t7~\@U% > \E[@6V=mv=,!@xeyDV&|7#1(c{6Qee|oQ$p+", E|[Pǚ~a"p bCE9W6{TG Aטd2AL qנ&L)IkU^N4)oMdlJ'%96ޱ(G!7^ո݅{<%-OqLlyW$VL\taW 72pW\yulI9AlvH7~gp&2p)l#:lL&2@Uisr!\e(1t31X:uKܦ{|N4Hv,Tj@ (|jn`m)]k4.5ʱ3 et]R-60mu~&pMa@>ji AApVf(8²}9g{}2pB78pr~ ^ 8rzQWEf- rAK6| 9H^ed4LɌ+uBDk,+_,չ򪇸1~71}%'b#r|CH,jo =;q)-AM} >jVKo \6b>66 W9^_"p\j/;I gx3oPl+H(e>!md(x>[ UC.ZF,+j#ܥψHۦCr9 oA$pR`O7ʎslϹ=DW}g.d,.{뭟ʃs_NSmd*\H_t \lulI9X`tH>-VMY݌Y JMC-*cv8u H;Hln9Z%X*e1ɸ!dPMʆRd$􂰉*{N 󛨹R=#гl%&;&LUGj`Z+D*DgvVX=k=1J/ČKb5׿DzQn&9סaˀ=E&8K+#cTnԯgoK&|]C3M^7ݢE \aa4qt8zS6"ȶc9)% S ˱@,>Ȝp@ɨЄlK:;ä!!F/\9Mg`$Gk[MK^sYM9CGӆUdQJȹ p!tQPѽ.\}aCd^_9uD୿T.,Q5'r6Gp X%@M(y!g1BL<9?&87獅UK2!H0-\,&&E;OSF7%_: +x{2A(@=+T`hWKb'{F%ؽ]ϑ%Ԙ>K=NQ<2QDM;$1odArXב՗IOJ~C*5p!cCUHM"fs9!"T*\i_j/ LA\;bx"Nd `"!\җaKC mW2FG읦v_8M[iߺ2'da'-V ٓ()YF 5VC) ey$1߯3@d\2! ;CPf%-.Qcdaw!ИdC Wؤ{ AJ"z$Gj3b\V00IJ g]Ĭ(-.s=K`ܢc[_;t%!p32ck okUBā5'\~HGz.&aC¥E)ܘ$DU{A)g=-6?gkk ]P1ˆO_u \0 0JD ID@%2r 1#QgJ 24U2p YX&`6% pE&5%+2~!Uxmd*p RIx `<&0}jN\RC qXhv(H5(_=艀gnהeKBm_|8v6申d[$!ܧ\,&3@7&wt!XdԖ9Ey % H=>RL{%} lLsd݈sA7qbibC~-:>@ؔ@S>vF1*~ sd,G9^r _} tBMFFD6Ae,}^\jS+ێyKN[ !Dw5MvB^xaȥ a u^wsW` D8˭!e@Vп8VaK'UUڐ~ k}I5+6J6oHwMjmvQ3^v"V*0YT]rXD2eƨO~aNdH)͑ HMO"M\AB(Zb=1+cҜ6'C" [ 8%T)f/}/6}ػ:֍LjЭ"prI'J<1F&m$2'Ff롏kr;$-@s .Ei_&!y $"װk/q:u#esV:0f._ɽqSrd C0Ckb4xUJW8PQ{ypY,'&Ndc4?%φpA9ye꓌I&puڞkӲEVuk]c~_]B[mpc!ߊ0td\Ȗ'7k0 0LK q#g jE<=0*9q 8:W7v"%p! p $!Rb&Q`q2)ferLT9SL i1nܸQ Q)9-g[]i8dAro؜7wqrp+/7\2슅r8AZN5<꾆BpNuzZ)p^! ,gxN$Aɮj9]k`'2Coyf Js@DGʞڂ@8\6"A(}A4)8@@Iu.d;)Qҗ֕Yk<Z{$ ԙ^] ,MP 1zWІ:ҡkuCcZMkD|GM9l.0n" ?3V:5ka&pk@ʑr?t:9kñב@VXtK 9s%$ga?Jp|)􊌠Vʈ َ8k@C#v, L 8K’sԞ]- {h &Y9 LΖs{evdDfӓL5,"&H?y%!t݇Ґ2 !M`ҁAKcBD9d q[rvZHdUԏM5˺1l\2F9C,~Z (&p9/ϟga@VeG 5?w?(3/]N*XB[weB,%IV5Ʀz&lJߤe%LҷxQ؛gaȘ$:ɤƱ*&xN 2KVDX1-  U,ﮌ&D!S`\~Z2Nm,ǀ8*_ 46:ʆL ` 4uu]+2YΒUk4fI& SD/A,~8'QnCYG#"t,X,7?c!ٟL,=2HVf8ٌEƧlIhΌUĺ=e_GKW@!;ſ/w(.4S&m %_ۥ>zulv_s,QDk pupMwPuaa$O rDw_Ax wE|) CRG 9u;ONrˉL"vN8$ (v%kGl pK)㵊]$'`Š{T5+ gZDĿuu2.;(ZdĤ _ g V$n )Xjr*R˲TMW@c1[ _uy Hm]%GA+HFz,%"uq6먶|B}cHJoR/A"5zcd 8;} 锭Ҝ.A4˵)Nѡ +#h2 L[LЏ* g92_[QusS7&c㧅KZE.Y{BdAJA>FY}>~ɥbwx)pBҳtw;[8B@*C/֐Q S>&WQ>E)@z=]χ,9Z:L0r xȠcU 劘xoq-fd{2*ÂB26~P12"6IRy;.hB>*MѴJ=8 L0N \tkdr1EcƲEc_uͯ::>jS{Dž?˓0Ps%c:^gCaet9 +]lN\24Xɱ(DX碯@MkFrdF2_![enn .jҹS~g.;fu!i&Wg ѶKB䛠_Ɇ%f];>}QC:ǟ-$DUB&+K C2E෪I9C`p:~gΜ.u+L2Cȕk(/C@ ;yEs,/'d`B\8.LXNAtlB]ȋQ]"[l(:z9.9Ic܂t LB`G T'җb@r2ynLM:;Vx6d>dDg32kKQ8I䶴5KFgy_DG K~d+>0VRv@1YX ʓhc g򋱆Ph?,:e7db?1B=ق,$Zu O%S E2|a"B;͘A 9lp=wTuQGN6 M21>_JM*L0A3בzid5:|U; Q=]گhO$>+G;N} 9*_t Y2>Idb=6/wj6[x nm׫U"  \0 0[,o/d*gm9Dj S:Xp /iG-ˢp!!]@fѩlz@+xRD5r`RKrcy bLgPcںrRX& \onQEϨDV>;Ug(}%Cݕ.;# Cv'PI~QD$sI@79=G FfNjӭ/xϞGYR|l`!DcN&5ʊ9o;kZ eYuԉ\ \e (x UAٛ{5< S`륧ۊg lׄ8#6-@ P+#l%]N3I΁<#-^V6ěFI~  S7EP]YP5A\׿<_ڇeE: ps \BL-6L7S)YlTY ä A5>><[T}{CB]y, u=٣mc*-udEa~ l΅ɛy=̈́H'jȒaVWCȔ&ң\LVt6䌲L8Rd{sX~+TD؏{12*D,$0}=ccENҹɷ@HD@YEgzFx2Xv|ˋTK=so䣟?"_`$5? {2z_ߟC)2;&pMa r@vV,[fU%#l= R sYGB{~Efk8PG,cVe{pN>5nc_# H}o [=HFNEVPpFSݚL^>A,&7oԒ@AKIއs[2dIA&Plt TȺ k%8cd,>(Q9GW:Px_2h3EwH`eub)@ؼL=EеDLw! ~!dɑ{e?AC S>Z:A5/deJ.ơ|IFα 'P $ ύD6})9(@{a;j}=L0{ET^' ?cƌI_::Kv*pM2H슎m+>c"0gC?E46#f&?1Ao: YҟyA5JMb}"wQc9MUδ>:U|E<ݐ2~|Wvi+CІ;75/ σ3 5B@UR\;8ҙ3JhĦD KZ Qs  m= [E&|?l.~,h(=ð%\zD8^U!5dB9l^C!T6(R =& .[XQjGLDZcǦ-!չH@2R2A!}q3&E8}`|F!xgL"Ę̳'` >3>B?8mH35ka&p+v'c:wB=+YJ`lY"p\p( R )Ci!_ #@!8d =$AN2888֐'GG2n2TЭV4K`J9qq&伐 ׂs [yqI# sl]rssNtIt[oCK <鋵!zjobI</c q|5VgR206Q:9{86gwMڅ d†{Gt]]Ch3bx )T^# !;$n< e׌N@_W1㗓a^TtP \0 0IaƉŁYYh,3 "`3H4;p&=~Hrχ`Β5"s~3SN8]'ڐ8K 'NLk h$CLWS x%!UL ;V'@GBOhu6 kC  /FݠnIL,zXB" [)O6C,ߢcEv2]Lrsd.`<1WcC7eŸzmf΀QɈc]F/\ `B cSe,)['>m yD|uR6oGI}ں&IR}S aelG 9+6vͫ5 5O0~aa,X܅u8ԏӒaW[2!Ʋy\QŒ5m,B<Ѕ< \~NMUTlzŠ5m5M5 0 cA :YQ'L˗wцf{i5-k)2s]ncF \0kdZG-m:OB5Vkf&M5k&pDB%Z%0Xf&p 0 cх :Č3ҒKxQs:5~Cf9 R`~-0k&p" Z:O٬Ͳey](FFB{&pRѐ(KYʖr*}ܝv)ز \0 XtaA K>u% "@ⲑ>$.#L \`#y6!RP^ubgLS٠<Qj5 pI`:#; ww' }gCan> Qw)ȭq.;nivm0k&p@s@rVJZ+&ΐM!VuZ-^%qe[x&E +L \!СCOfcƌ%;w9M/z \0 Xta( L \6 ~պm۶Wx'+'T]>yٷЪ뱳[y\;-gΜ\~xaшa( L \6vL%T6;.|S#RɘRDnk]Xa(5L &p!lmXpf y>)  \:>ڷ}똣oȰ믿뽑vڴi)aF# \dD \0k}Wv=L۾tPvYgo?vA;p}ɆP߷ \0k4$!fS{.'/XBI\.曧kn"Y[ѧrJ6eʔlҤI-EE&Wdp~6vlܸq~xaш wUWz葝yK :zKʩuY]DdS W$\`md)+lP >m2裏6qYz0x^_n?o ۖK^} Kg].ۤIqn#K]K]#GLی.?裴en2MM['P:{yL81˝0aB? pEؾ2t4L<93g#[0 XDܝV[m)cboac 6kV p*gcw!k%S%)eFl*efo{0+LNupVM >llb$en{-d_~y*S#vΜ9n(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0j:ӧOvM7e]vYֳg϶X,bX,dBlz7g/b{a{͎8∬M6 ':tk߾bX,bX,%bSn-{g|0{衇aڵktA>BbX,bX,R ]{)?̾KaƢ7|3qc9&_RΝo{G}bX,bX,ŨviK[^wu% 0 c5GuԽ/x>Y,bX,RrYr%w\<0;Ss_~y0 co#k|?O4o6۬bX,bX,M6٤oV;͈] 0 cop;gna[,bX,R2!6mѢEv駧;^|EaƢW^y%{ᇳz+dva:[Z,bX,R2!6s۷ovUWe}q oa,ZOw}77n\#d]vvml]vX,bX,dBlڶm۴yóoAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ;z?z/zi4uYi}nKI3zᶰD9O=,%8㌤nK{7ߜY>//{7+Z{\s5ٿl̙ـPab̘1ѣ|}gó?ϓ2~>~_HY|wx}wg<@g}-G}4{o1ݺu˺wn4t5СCv 'nK vsIl-l_ǎ.]tbiXaF'-_N<좋.J1=3)e}QlH>}'Ȟ}Yކa1?'$'^yl=H/R*oma)8|Ǻ-,矟?IY#"E=XClXmĉ 0 aJVJu]ٶn=ٰaq9eʔD>(d~Ffy^UB[A@s>8.wO?MSҀcp3fH4ut =0>dȐ$|8E>X!{erڵc>rY, -9A;t-c;tܹKUY ;cX, +:v,bsEIKhrK/d ks=ՔM+ B\kq) |aFm1a„DzyAvSFTpfJ <*(Dɓ1 S "rCMd18|Rd/{i 8F]w]"vuMSspݔHPz%{B>U&doqI&Zk;oӦ͜VZ-tnwί[ئ^-VC/M뭷^~}~ii`#˯%?`W[=Zvt>l֬Y~;Czcc޽TʺWYe]wLgmo0 è- _PtHh!kJdν_Ift^27x+2Ҳ[obwA/6m5?8cAd's?Ck믟V9ZB?D r3}$?~KX, =r|<(ZN;.o=xOHٷg"8.l0 0O=TAh^vFdI y?|~W|~*Ap ;[z 2w_ ;WDJ>3tʘmH(7CN쨈]K Bkte-d9;}-Ny6`kd)˵>R{ ӗ]-pէʋ)3Y=R~` e[wҧ$Rbi(]c[mSMpSK 3+nDlɏ62K :K7 fr]ڮguVC%۶_˖-#裏αPeHI%l(u{RK-qSaQG\YO>dozwπPW Ȣz<%D7"ZUV얚M^Dϖq{2ygI';NyewWB߷A|+aW:s]CH9S^ek4en܅cMD%26/r/|%ÿ2r ٥5 +-rwCek6˩&;Q`l,hM7ݔ62]|D2o믿~{MXXz32p^nR8o0kau _<"t7T6c"DcP_蝤LUx,l]!zEЮCҍ(r[F /4<^/N$w9:b7m6ٳ~FRZ~_~4HYPqW:f~kK.~vͥ"q3&륔;ܫ \b0k5ʽe{nvGd*n%Pf82rn%DvmݖV5 0 ڀds/ 6dFXd* G#,ђqP@2X"< O#)rv~;B/!J'Hf%o?ZŹU"UuXLYtKټlߵWQ*C}/w*85uT&p  \bר :S.(I^{Wf*}ڵk]uU9b.0kaF \6!6(AbBf Y:}-D~ 7!I ֥ΫLYy9$/A?: W׫Z"Ew,)W"U3ҿQn2lE^yJ-PV!p+ xI ܱr3fFw uoqdo&mc_J*Qq2 \b0k5*QD*jEKmϞ=sM4L={ߨ[6ydaaT2eJZP UNt9h#EqY׮] .LHٖ8)"~/(Bԇʦ=F"uwg}"BW^9wyg'}M"fܹ3ٱQ"_7):tv:k61ۍz"pZD;UDT_2V--YaҤI~Wzs^ehv[5K. \b0k5*1"`mzW^yenκwHgߥ?|dVZ3+x㍔ZHq \0 0Sf&L 5S(uS}]f%h:SSաC.z>5վzNF-i1WоU|9TX dՋgʜMY"YRĬ4䮲g;9ﺞz]\ doe"S)ӟt'sMrd2}(ry[W\qoD2#0kXL&p-FE`%#+Vqg+ۖ6LެYJt9ozkKq \0 èKP{ ) f_!jB̊LM9}]6=SFM5P"?!UeDR _)fK2bcS1ާ}y\dT,Y"p\?\|oD63(+q!!zyǐ{k0k5LZ,&pLy뮻򗿬rg͛76|L}t7o>;3slru&p 0 è(Llܸq ҔUHRғe>ȅH3\Eޭ,DP6ʄ=E/,DBl"gɴ嘔gf2289A'HcHU!g!l-N߅|NGn$/9-y!|ab1kXLZquӦM'^vev҅)JƗ Kݻ \0 01c$%!iqJ!CYg9gN| AϦ [cH8)d.++i|"`sABB |1$ rY%i˒%~OiӦ(#8_B.ǂX1K[|rlJ>[|n6-?h} } Lp3Sd7TzFqi;Ucu1)@M[a Gx5< dYʉ}NoS` C-/1.ƓDE}p exXV49T6NpƟȥ\ϰ&p-&pF`ʹ~K߆*Kal5uYY.{d[kbc"򀿄]{+!çZRC{ddԊr+G( .[kri_-[ce 3&z5|kLNO$QD6V.FbϢYuM{ Co=ϪCl~$Zm;g"oiC쯁mks|c) M&p 0 è@ZE6. NBdpC$Cr5"GFnd/pp>QXxc9'r>ù&1g&CrDȪ=2.DD&GY!H|qngG 8\/׃~yэ"ؠcB@ \K`&pڕvʻbXg祗^t=^9ִ+ϵ@iKA  9'#?qǕVOlP~u59GEPO[Mp^\,KMc;7(>hƌȾON;-ن2=HƧK/) *EYDӧ rDI^[` h(cE;cǰ}رZaMnR90~?yXz~s{dZ/~ҰL^mWչ*lsc#p5D.<A?L|5+kl ǢcӘHֱRUuW+>v1yL8V&|_CԖcĦWvLD !ӪUt<;#XmsڪZ㏟Gs YH|_;0 讫9PPQQ_*]3aE%Hi! 9* "9眓Hh-fa8{[U=ܪ{j[=i[N6Ϟv/ZlOusڭ?6uث2N_>{=EX5q!OVO}$E~7kdDqgL_Hn ps0&+t>O·'>p06jVSgs&d2LY pb\CNj d8v} yBt {S^N\6m&xEN,TkGj%ˈ ӂsNd1p\&) 6 <{N{B4Fۨ6LS=ޤ$G'9D"o{m۶/8g`'E{y$eMf‮q@ `^% $MPm=uhۻ('On>R5vE7WVy׺Cc+SGi?v>iW#+5wuTqzimdM}[kիj֭jhwݗ{{rS2g\*ɶ!-~ pk-˺!߁[|cX1&$` %WNlo5Kr,+q'Gt}ls('G^;@H/7k2L&ɔhKLr5~@2@@w F$3XG9(3&:1Q@-[,l}lžs0 4Q`YsvC}T]v+/aMD4JmN׀A?1!Ԧ[2h]uLF,!Aл(S6&2ߩ|_І:GO>b P.`є}ao߸&%r h[vY^կH.pv<¿PTPSxE6Ip6p-k~=p%ʖ}z_tFj2W׵g U\ h6EFE +WrZLs #q>Ա?v $Da7>P"sEۚxtz'SmV獤WAHl5>5z=FP7~:7E4켜{yFl|P|rǥ>|O͠GQHsM~2ÇRhϺ$',ty"\ᤙ#Fv]] quVkׯvJpK-JX }j:2 -un`$Z6qp[?u=<&CZ-?J{?iz>]zvM[suTv5Tڣ_;xD?/i0qӖyԷ;ћ*cƑ5ᦋE]Mi4ukѢo?~8G8ql2 7;.fc2QWJ(/r0|_5 !%6lI ]X߁'1^s{W"J74k2L&n:.R^5tՄtq*g׹yh"pZ)`QG33@EY}~&گsä[:-< ^5^ <,mMԷHV oPcb-q"M5Y^鼨:'6QU A|!-LJ#@O1Wn@ e>[/pnc8Md ,賫Q~*yD)ۅo?nH$mRy Ǵ,A;ԗM4VwEpDw(mK휯'Z*FA#!u=BQfH4 |iW\^'Im='m9fF ]ʕ]=nVOɰ3Ngt۩CtjruʗدMk'* p&3w+M9GdtԹ,O N6Y $ W>\GpI\7.48Oy* .@Vb|O&%:;|)>Yxm=SI@:767q?]}V&;Dm?|KT^  rWvҬ>A鬅c!"_ v yt6lǹnynBoPA*l")ukӐv 䠥O{uML|r7npFĴw2ѯOضh9pK9? ѯ13sZs?˗ߟ~[nvEm 6k2L&n e  3cr09gAnJqCگQp |>3@ `&kBJ=;-c00I4IA~L!p c1x+ېv3&2QD m (2#AKCT ^ u4\r~ _No!g 5jPb}0%g&6!եQDnMЯKg%r:.s#ECnZڽxOdG 2 mC٤x,h3Z/9giG+o}|[E-uG?#mpWT @Zz!|SIlie|U}_@TcPۑ? (6pm?}!є g~?㓉<ρ @Z 0B6ȢNS-nLշ>~g B`h"ҵn|UVa&715vK٭T63A!27a׾}Dr|0uȻernGkPٿ_qHڎ 4FD>m9~ZqKF\@x<+U3Ya"zG#|g?Q}s Q0Eߖ-qڵ)"7k Har7_.v 4/7&q pi[لzbŊe1Wje|~oq:_pGy<=|\P7oP[zP/9iKuΡf͚!_i?W?a'Qxzcw!) D(\E~;K_FG&!DͶj wG6o/|) \uF@Ճ"p7Ze6\|TrW>sqVq@߯Vs3(#.7GbSnYZn?x, w3~ X,!EXzL yx˸-es)[l믿[eoF"OZ8S}xi^}'366k2L&ɔ. O4*,hzѼq *j0G*(&sL5!ԇ B O@=,v ʦ]@n`9=>,&*#v(DVQy:;/@$@[8 F_0<x)9(0Qto~&&%L.h/LjXOynGK}XU4RuHOpH%/vnnpl >vX,\X?@,BM3 ;؏}J k$"9_ŧD#"@> RJ?R2H2ek&|x[vm߷<BTwpCepb[lѪ>-0 ;wD@ynl[nc{(]n7Xcb!%|lꗑ7܈^ћ;SO=ˆԏ؞q_*%? ƸB 4; *Di)Əs}(6UV4k삻˩~ɰ܂ V} NpM&d22jVOy hMg/2gq ~5~i AQ<Vm{ 0w Jh JpU @chp;wMnٲ2&IuXA}ɷ˱"Utɷk,cbpCzT84oaB5߬U>"npONOO4@RYhR3 ǡlK"iBD.!r r8EVHsSNii;1j#Ft3|K p{ _oHMD=2r3)FO+d4ŷE\mʂc8/Y}v9P~mE|"]3]OŎ3BԋkSw`%K#DUWhQ"s^{Y&>>_ L&d2e#@Xp hW :O%7)B{Tǻj&B],?@.E8^ nIsXDE73'%O i}}[CF/ oؔ4 { ]ņ} O׳74Dݺu}@GvYd2L&G,ۣ\kViws =(]4Q+fAC4.CΚh]I{.,'pi'@:wXF[v4Wu=/@w}{giY~ #&Iiwo|^X)㳖jnMOsg'ɜpCj Ӎ*Wvs9btGfr"pCuMh"mS >0MT\5z]iժr G<̣\X ؠ CmK/}=~DDS~Y#l4S6Ajמm9!B>m Q;~nQ)g󾋉].˵UVnEKۚV?ebf _X&|IK.#7Zn$Q;~nr[Ǝ}+9;qd{|4U}䷮Ա.zPX>MZ}>S|f5*4nj>n{7}v1ѱԗ75KC(7I\GpumׄB(3@y)҈dx n5{i[0i$ Q~Dhr\|>7mrc-յX (IYuhn|B 8č?X)|^~%Jm p6.Ѿ VL M5͘ F_tu wI) 9N7z;8/t~gp9ȍ|n8S]n!G+J]}ed nramWڄj/*po* J]s!Ŗ\d2LlpkA[oӠv OʂF /#m47j]8@ &I 0P~EG|qPA TG @ !2W-Q=.$.h踝-}mX[m_XmN! Ce4@GGutRV@{뭷< *U#ꛉ 9^zVѱD&,)XS, IAt&_~]Bێ1bz&AIh&IaҔ#n~/")br>bfM;$BNӺoQD [b;6*? &p 9,aqAA@+09sZ YnL[0+9Kk*(הmyD2nqeEvډ_MKgR(oxp Xn] x4lN#Gk@\k+y)uF^|Uni7;0V5Mnhۭ|#3iWOcGzocWݾ}{}sړPG"x\wy{mk'{_ۜ_|-Bݺp=R](P ok Qc pj|}9|sx͒pf"fQ7v]~V)iHKlz_xa#9q7grnv1> "?Uerg0YC\SGpwCZ݉c` G1 ~/g^3Z\ƍ`h{A)ss]ßu].!!ArzApM&d2 Մt-߇)!Q<!bVwq Qf:0xhHB1 :LU!Wp&#@J&QA5$)Ce] 귍ԅmȀ^A8`qL>4oF\'8MAۉo{"4t 1)n$ڦ)i}o\~heR-i?Eg{VQ4KCܔYςh_~o߹QÃ+K#hɃۼfC<moWOk+k}=k"pp9k3v. gXD@Wi/V.?c xŀr (߭T~W,$eHH>~sB3%UVm? [R2mU>x:6yn t]h HaQṔ[Uܭt>J0y8 lO}DRfO-ԭEfUI A.9~4}KI)ݘICJZMJ~x7egr2V?!ˢ%c#p7rNT(5m4J\8Vp.2. 0Ա.a7ȟرw,zV?qwēσyEv#:7 L&d2{ҐϖW  BUN{I=P +FWn;w>Hzܫ.GJA#r,"40Q@P}Z0i  :.]q8jss:vm]&. cЦ?5 k&N//@(p)R9_4]>?"":w&{6)A/ -7e 3CqGDQN KIh_$~p񣀗#FX}&{*,>\Ȥ9V`t(J>~] ڲ}K{hsh'װݴsig3#Gy ԭݘ±2:hHjZ,,@UY)}Wм<'2e<ͿHY78WkpH{WoH"sqٳA.~MV8u؂0ne< L~!rM7O'߼yƊpM&d2#e pjemh}|[a?@M `@XJdND.A$w jb+3 Eujފ0C_CfAY q@\^F\ } [}SQoD',"vd\&BD ӗ=!=D*L 4Kd }㇨>Ǐ;+ lvneGyba pF+o_YÇIԈ^0jO%P`0M8&XXHQR2[|B!$`0E8@L| _ |;wc˔)+;g#`_a!u,_Sy'O%,TGg hl2R|)9>7Oڶ]:u)H?F{ryO19q7U=$0ke{m/6a?Ofe+lc&-E"1ҵ3Ǹ OnɚhZ)Mj꿌H r'ci YXDp pcNJԏ(('Ԗ??4H㮻aTuyC?RbE} L&d2e$_Uuy|2drڀOz ^+9T_x$=Ђ0&\W~@p OSK.n,%6gBM֋3'ju{:k`DG;^>G5?@w29p$=L({LnT6@ F=g2HIێ xپ~=F0Ev,(sעnp~;"\WMH9Z7#.6,֮~]vg4?mR%Wzh&AuM%׮p Ei*&~>Q̓m(ܹ' %E `sB49F;K A Jދ_ 2a&ikT4|LI&> ß7EEܫt7ʴM-}5Gƀ[|\w7\M}U? m噝#s7l_B9G6mU-M)mFIGR ңhz#ܥ|:[nivqb}+ Q5kU߭a Y,|vr  %$Ari.U7¯a\~/~Mc*;/ EY۬./|#ߥWc%W_}5 /+e+N!-~n?Oz3w6k2L&ɔM[[盂%ZBвgM3 N0iP0PDl rܲD) $U)BCXHo{|O屨OJ5&L59#Y<uѢEZ0. <}>,PR7ipw7(jмm[z>g@EOȨԯkr: R}U ѕ,?U7j&IMz.?0WfEP7|o&Ko7i;%վ[Uu{a.1; u/}.@C|RٺT:v4PAP ?B˶YK}"~Cd=cqߧjӦx<{}޼fudhq4e6W>+PH xrA~:K`A$pI.7jW1^ Ai]xbYfD5Ɨv{|ITz>׋:b5KH:7Qo1)(J.@ E p1 tޥ:5qMVn35jZ˷eʩGSÿ1Q~sТmI>Iə}7,JM [ѼXiu6{G.vnX!VCn= N;-YK"cV s7{ H+#Bs+q=}h[= hHOiꩧ>x":pM&d22B*Pg̠83)jP[g¤xck 5Y>Cy pco$73ƀ -`A&D)0  7EQ|Ryefm\>qyp@@JOZ*7mz.=b )~:?H8@tGrCmc40 7RBjuS'?%6@7=8uG~S x<9M? Kwo/ w!"urs_PpӲlXc{?uHꇂ/(k7trbm-F Z׬[ѿ1 3%Og-KG pCZ:rs wof}tn:ܟtMz%$i&d2L pP3g9$z*.;`n t3fLd& I5q&h>FHrpo{ xTul2>>JAx#Wx"ahNU.a;ԇzFiڿAfh+"l5i5u030fQ˩#AݠA~7)m- BjK7Ij곁:ܴ!D|$1atnc3~@/;osVN编ך"$%nOxo'b⳹Y1[|Hr+WLt ]j" ?_z3pt|*g|#Dk?fXңh.sm/wc)y؅ݎg $̹ܰK&y R ~>Ηw 5mY!mAZ6xDoנquź~BߣiT 䙥^aEU:a .T8/[_NYe 7zs( B74'J*뮻4mڴ,>[oSe]VGkTl{|5L&d |`K4V4b*Pq/%,\X}hQD#Ll(\p?S'>@.iHJ;ߥa;n<~ kY3Ӽ?ۑz\RwDSI$xZR8R(?t$伿_> iBއrTRTb1'>0+,1i|+7cLXI{@GJ>Pe"ݙ'BZpAS m4B9 8ʹ.)¨=>r{ Ғ*"W4fy]u ȥ9ǻ9؊9bleQe ujܥ}=n#SǴmuK;b,K"o@t!FkΓ,JD,07A`BEӯ\Iڗ0^~Lk?h{q0 o#Vm[>) 3{2L?(;xʰA4 Mቺ~zR&+yq㸮)}Bg}v;i&d2L pC 2mI7 ʕ+AP;C)`-Yd yxl9 jdDROއ6ǠzPgj0'":aDhZ)7H)ˣ|@c2As\"՘3<~O7I7+39b{,֤%:/2Rb^Ѵh w#/2j'"fSkG| s(~MM7+'nW)+O|>}]_.ϒ"pk&ԙzG 6Nt|;> x$qCP`_i 7MH( $ v@I{W%nvhl؞u6;\O:$p` 7¸.%OX["Agr;wvd\ҍ>u}vZ@gqJy/q5>Ûo.'Oȋ/GETR{+5l0RdI&d2L pQt_ 5<ƠIqHQ+>*0pgP>V 7c1f; 40P0큐/3D1Ơ}D~D&jdHtU?)Nc8ʠ.ԟ c2*a-$؞Q]86CY|6#ԁ(!AYLҢpNl M3߆?g?p[Go 7(S WnWik6k2L&ɔt)hú-ZG"ɫIͱ Y!f':52kfהSE427 c#pO>2{nbnի(P 1Wq L&d2"J,)ZRGLUXٸ4P[ "t%\\330 7ؓ;w27x#3Ľ[?. l\d2L&B;'. pM&d2 f )b9!jE}X 5533kbQ["jI\)M4޽{'.Wh]'}SNpM&d2 r 52kfהS%Xy#-\K<ܘnIgr-m۶h1H5q4o2k2L&)"pM*fpM' 5DG(Q| =vʚdqdӼd2L,5e 52kfהէOHv";TdFZ,rǝs9^x!Ai=eʕ>}ѹѼd2L,5e 52kfהSN4n8'iO|W2e&6H"_~nyQ\Xn4o2k2L&)"pM*fpM' 54cIФIB EJ.@QDڞ9{l^aΜ9_{.!!J*E7o=MpM&d2e@$^ve>Ր!C̎.pͲ=_W ĵiSLpͲ#رfڀ쳑uv뮻~6}Wzڲeȩjd2L&Swʕkfv̍ok5b{E k}5d^z\,yGicAPGyN@H>UpzJ;hO5L&ɔV.wܖB%ʓ/uW_q۵p~V,33+s~_?orFSVܛo};K۸Ǟ5w53sc&q]w=_eg)-z[zˣ>,RZHӦMoV4S܂sO>L&dwys=7rG*VQJ;cKd2LV˗TZ´jl63t Z,ӭ`Zp#Y'fmDso`aYZ{ _|_O>D*Tyǎ.\8<-ZD>cPL&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2L&d2LI7oر#hѢرc#~YۨQ"#Fc_7 o&2rH L3gU6zɓ# .L81l2dL&d2GSL̘1Ãy"?SdʕEnٵkWd~_DfΜٴiSdҥ۷'ߺu"/^: 4hYfG؀Y_e}gvNe:b}a5jDN>}ML&d2R0sڴiN4 |iv֪U"6l8;aY+~1df͚ȧ~-%s.ш={ffYaϰ~0`fYe\Z_e7/̲ʘ{0&xe˖>*d2L&ɔ6neZ>EQw>KZ 00}ɤ)]هmT| p}McXÆ ЖDiZN?.W_}uw= طWVGr.ibOD *w]wEox#~,ӌ||-v 0;Jovoܣ>-X,S{q蜾ϙeu`;pw}+PY?/O"ۤd2L&)%LD j"P;W^NѶo߾N9撗#wR/GVZE˛7/<3fL{n{߾}QJd,xgϞ}ŀt.@ڰsrԾg Z Z;]5{OSSNKF1_|7kMw_Rwcsӆ||QܯG vE_x͘?}?yY&Wna+sb^k v]1;fA+5i|܈o#2o_;u։4j(L&d2$`))y5V2Nd+UrDmi8@7{?N' %Vcڀd'@Hcǎ> e."9"6ryOR+Vѻ\}EgiG^ tDf1+ɀo˕+W{ff_&_>^}nץϋukwo.-nnn3̲nopa|2" 0gvLLOE:L#_[퟿7gfY`pþ?TիS)4hSL&d2RPX*xWwWxx F˞m{&EfyI`g*5A3m/xzfM|.o}NW9EWX-Z0]ةa&Σpu3phf>me?ٳ'+>zllMj;wԛ^VU]+Bh p K/yffܮ駟οuysvfNw;s1; }r+Rw? nօemضmڵlsuԭPJH2e‸2:02Un;YfUܜ5?G L&d2GD֒vV啽nݺm@/)mѢs`E׾$8[h] :tVo}km,Y$7xM0Ց@vU."`;2ZևDx_$)t}UD n<a;**xh>w\Eo߭["qI &h[j8okܹ}]R1/%>N;-rI'Gb 5L&dR!R:S6@U姽ʢe`]_)8~D*U9s)Kuo/ X #f `Y^:wU} ̞#5"tGR>8 %DiGMѹM[xqQ"ii&ӱga pydh53k2L&ɔCrJh K$XZLPt5B w)O+p[[WQ뵨؍ZDY"d?*U"hXKZ/w+6ѰձXǝ9H߭h]GyDŽrAAV?_=>w/2Ѐa}6,$_.}n02k2k\d2Lڵkq:o<`O-@+>} }(WE~p֊m}'Us D"f>>EC-vj7 p\"dU{8dmV+_bŊycl,V2D1oϞ=0-#k9,vޯ-Qi%GM 533k2kffd2L&)kxbnJW!݋ m۶1P؏I`⬑ʕ+D ^]$gr)\-6i߾= mm.U T+#!%;s/8tqh' "/PdrkmUįoQ\33&ffpM&d2Ti Jb5TsRx*h|߾}**6^|?Xv]:T IbbR{7lؐQu' *g`jR6RR`eʥF| T=JLٲeYl>Cn\.{ [=U$wls831uؑ [4I%DjAu0k2k\d2LլY^ Ȝ"9Ckohxkپ"Tg TAVlOy޽7뻺@ɓ'߱}4 Hנm%%ORtL9lI߰skƏwĈng*ʶrEcEVc?"| 6\CϑUBmiZMMV*m ;v55kffdd2L&SjEz U"m#J/@*|XnA@5Vo ^J[2Ul'r ^AVJ#e吆JjW7O3Vcp,#ϫKZe)6PnΝ};;W0Ѵ3ր,`F͛755kffdd2L&S %r |UDn>E-A)r^qf׿v_Xە?kY H8moxs=WTAe_A8b1E)4,>orRo%`K.rLX,Tfx\k2k\d2L! hT(` Ԣ_-[XhXω*g.`4EEkJ١Cz `UfJ{9Dta?s|>㕿b Y˱6nxP}}Qԟb~@6QmLb5'ԋI)W+[`_7 9R2k\JKQ:(12kfd2L&1STH>f#^[la0@G pZ4G3O!I7%]ĀDt4(DDIsrM ~n֬Y"IC "@XD9XRQ7 PS/Oȷ Rw>r\?sa JRWٖT &ITq<7nؗO^ߓ9Q>|Mp f Ox2CI }ÍP|$ nqZC!q]zOz%q6R3c\/ s3ꌯ-\Io!| @p5; 5BN{|醒}n?Uk\\?`E-Asnݺ%s <0-~7cנp S]vSgp6͍M y>5@8$ .0o]GW#)]pM&d2L) PrwǑNL>z)I8"9#R`ylݺ0 ?/^I~/&Xef -0I# )28=\}.B/1gG~_&ԉߝ8j"&UvǤJ@ECD+hڴiSg}TLO}c1.%JԎE{؟&Зa?>{M`sy0y'j-pN-ő\1.۱ ] ݋zM rR~cñd6Y}}9 ~ܾ$5mڹmڵ찀mm1S9_X8۰kX;x[u>[b[kmWV5K.){?ԢE )$F7[\NpL uFi$^؛J|O2v?pW`ш2FT\&s/$}r ;[nmnʢf ]s`s׻13W 7ww"o#a03/0<@ mQӤiT>UEX- ( t (&6p4VWT)FY!U `o+EѿN׋:Ak]ʣ2Q`x T(SLX. j}P:d&hR(+@*o]۔\.7*ؗc3!GnG_z ݡ-w.cH ܨm6i5IHO5q:vpc?VaÔwG s]TNӄʊ. g۹>(]zu݈ ǨO \8gע9۵صKsӖݤ]\M݂Sv.uSfZk%k{@hTױ[ Lcڸzru[ FOs-Ļu\u\]֑飀S68cgWKt*7WȏגOڕ&?GZZWqs*,*_R": ܘYrFzVckԼ\Q[ݛd#*]C pF A\:wߤ7'wꋡo~55I7h^ήMAnsc/=ӵdUUUoٵ>~AJ,7gkھkSװU7QϴcȘ9q l䔥^>;́x6iӰՁc4iˍum?ŷ8X ~~cq L&d2-e@CVZvڹ;&Zm۶u9Amri 4xig^qچ禫\ɯvs\N]\>@qHEt<yZ㚴>ȕ}\R2UFE~n-AD<Y_fm+Wa2Uwuus#(sXuM׸TDr0 99GYdAEAkIpzz辡Uu{TFϗo.s|zcߠqnv~|kXa+ :ywt<|G~>l;v*Xso'_ߗcziֶg'u`?u#C&߫X;r}W>>rSױQZQpM&d2Lzhx1;S 4OF:k9>VȀN@_%).v>˻w)? tnsNA;N,܈>^<Ü<~V <, Ϫ:Խx[qB~x?$?2;Gq|Ȕ(w;u$y=>SC$x@&@v]1xofڔqJYr҄p.YخA}>VH 7|gZ֪.=2\>a\Ӹ1Dw>u= `9izq ( lP8r2=v=9ow?0 *dZۺ?m;FNw 9/hcyV{b؀90ʬgLP>,p|bt[V?^P86Vn_pJAI-+oUk~%x<0ܵr ab pY][;Rs<=Vl ÐҬ<? 'BC%<|,[y3ܾkmZ,XK+0ϠNiWu!.˅gaEZh5LvwᶟVgܸmiرk|4iox<`;~{G+WlK t /1pi>gD֢=$U/gЦ-_njwW_1%/>%~+{lC{JHHηS*7yߖoO;D~&oޥU[7VW>3#`; j6>N Ȕ{]Բ-@%lBi /}V;-65).D oçWMp{t`Hmz @D!"|Oف5q 6]Ml3q(4%ɸ].B<5L&d2z&PP3 GE]1 #~zJ(m,wv@B,E:Rpu@V ]$Fxٲej6z LF}^LWEpȫ~/N\VƘgjnG ОdMMFwocWb;mwxA^Vk[i*\޷{Tżua! @mf %b+hŴ !*@Q-`"/Զn!t}j'#TЕ6jp'/=6$`*mSbv_׮h˩gmC\sx2녗M,D,q^"^C}baC9dp׏Pm73[K >xȭդ;'W~},h[i;a; f 9 * 50mfMuup Ey/nRpi+:= JrpM&d2Lp.".++C& Ʀ@߼,o= W,Ĉ6'߳1ae(>קrΨ\{xRb ^ 6mJ~82v+ox&|A-]p>LJ\z뭮N0{̥N({t qCc{Vq,pA޽D!uǀ[ zu!u/r8y-!.N~4ys!m"}ܝ_, [$MLX>qe3|hp$;8X Pai1.l+78Bi)!R5v޷]@QD-^m6U}_o\h{EΙ- 6sҌ|qĢg avfm T.P\@ ߗe30mV޲e3 eHc /DUqj2e[ 1w_!/Q1Z H"9. ʍx9GOq(ށ]0#3Mzx 2_ o1[cpPrc1/_lb^a}=S8eƦ/8'U~qZ [F wSZ\ ,li3 |O9kԬ;g d8/VoZEv]ZJXyP\(Y, SorL/*ҮEKh[spSN.uuV6bN}E%]wx%ORP:3t_JW~>p/+`) 7x6v>JakY,'c{%\jFXe01h 6,ɔ/Zb9V\b[.b|ăd2L&T7w3s>htj9aU 5:iPفzw"ۊ P!dK m}گmY_.[PV)'e}( m_^~ύg(eժU]?HC/6 $-pFt1狟*x6>mV0~e;u& b;Il0KyrꁋU߅u}>4|_޷ ppM&d2LQ_& N# &(ƨE, pc'$Ze;{B)l/<=$̏UepQV@d2pC]> +(q?2Jz%G !wO?t@C2LO2ou0`pn`]Vb0RoӔk>~;vfOɋ)s_8 X [7pWFr}Ri8~Κ -шe Xe27wbc. ޠb%'/v `d2L&d*+xCFF&-Cy.@%wʮbb Įx#`hMp19?Ye#ƴM;rTʸmv$ 1*{`w@gluİ66"|v 鳑 (; y^d袋p{_4c_oWŔ[3<B&*7\fg)λ[y&[+H̅R0fv>?+vDxYNBd2L&T@72,?ZY#1>eP3ʗ/\=xh0^^bEbf,*o۔o8?+C41^H X+9ѸlMު o/ w ݇ȭX 4U@BW rQnx =`+/*$[ ) I\f 8سii;#2,nX;/u+att\=pnMXK vP?o3 N$-,/\W!Wywu] `%^'Q.aF){p+Fyp%sVQXqeu6F eq4l6=>MP_,;Ct)Q/ߝpM&d2Lĸ歷r&`T#y>E I ⋁2Bidt11tVi:E?[LM+CQb֮]"L U^s\׶9F ̔wסX$X@eވocu]禭-sW5zo\q.`:\icF?u}~N5U޷x=cb9ZvwW` fg5r8m_BY| \ lZ0yoi*Kx2oyؼFu wt nms}_|ȺowzކPBAzѹZND~rh:Pz9\_.Waܠ>YSig W6BGc?묳ݻwޛ6R`,uF|\>,Ńvy9a!”%n_6p 7םcrNM"gZB)/&d2L&S\7ȉaywG7GñʸoS,T|/ A 1M/TH~p2j:@XAc< u)ౄGRI+}zɥ㼱.eV|[J8M]vRmqiϣaV}Pm2B_e8.nŤ'׹eaU_ et'PLy AQTput+(Aĵ.>x@ Φ^<+ý ^i@>VnA]19x[x լ충WYܯbnJx9Vf)3\:nEiۂyYBaCK*3rᙛ<buJ X3h'`sa'๛Ղ_ P]{qv"\ba|̣&o 8kKf-z,| pR(E?W/W,|g;W\6z m׀,bRFfĶ뚹r^ĥxL>"%%>qb{f9xRo ;T^ix)Saޛ4IpmڦbԮu/KDm?zˋ֩dl]$gƹP@:gwmNK!,7{xonl>bfZpү3z5L&d2c0, ѻP2֜` àVQ/vbc@[2`8K} XÝ2.$dN_F.t| K"D{a '{A kLL~8+IJ%,`Y$%Q:PΫwzo*~, Il1F^`P\`@l7 ؤ:4Xon8χ5~Ċ%_υY𳤦a $L36zp~{͚ڥxv+&ui$ղEYޫ0A }au36ꄝ~y/2˘$8Ž|U|,K\@dA\hh{mӢh[ Tk؅16{dpt\YW\&~v&P">n&jtO6Ђlp=˲埅|h\dfSMlKD+Pk C, ?[Dm/&/41˱spy!v*?%ul_Yh \ 6p:Y%^/嫲^2P_;\i4\avx}ou Sf꣰\aƲoT:i$.ManC&jE8h:Un?śɝh&wo3f6TSgkv8zIÏU+ؽn8ap2oê[ 6q yƅ75 ~bd2L&d7Zb ;MIU0$cRJe~𖸬La-[,`~ˈ{\A/T֭[peAےm1&e^7?Naz >*Om TE]k9B`yeL&d"yNEBOQuu ן/y<aL/^|MK<'>>.ױQW\H@b#7.pvC5@ul鋃QSs.B|{rsmaz>7C>a1,RAYƴ ۷Mf./^Wxz\08=N$=q`4[MzZVxbCL.K@kAԵ_/r`M~׮GvY2q[ hW !lˋ2^zDV-MSCj 2iCIp솦IT/:~& T=zU[[ \ $9svݷ]䅝Y*OE"%Uris؟}9>bou\ҽ  \atVF TQ3*AWk5AOn_LVӂc{p8тg((fX!oA k0p̜Ln.|BͰSQ1rsY,>dDZA.xV\,{kk!Cy_r>ܝpM&d2Lpq1ꈫ@&㷅cR\j! X] L(@H\Umz1U1N Ad%sI`6Yc-yw< 1 9 @jm[)'=Nr@#@ݙ-dXcF C]U7<-\X=s O36 p\f?\j^O돂pW) 0|L4rB`=n^<(7epEeq}]RŤ]zfI^+tBygupxx R zOmhkue?(}Cf8\<3R-s-bjߚm/ euY Ëv>ItOw'vG o|)qx!EéǜO[W[˂eS/7Ϫo+Eu4 \u %L']wY@qQ&и.-coi630G+'w }ʾc[mȍȡ&/C2M\](Cm9 D5l@ށou.\zh1lsB= Nt=Lγ7cgtۜ9}?YW7m?SGLVI}[lq@D O2#| xOx^xLb(w+~sNAT":4tp}J!}N;;oП|~?oI8(Y1@,駟ܱ'm6}}hK o]6fIH}vNDqIߞ]B&rfkMpM&d2L,Ek f|HŸƐ{}fM$g.&-34+s.-o[/.&s^x]ͶShg C^]渄7;/yV${ ܤ[a[cP4T=c̔%z$JԩO˚:~b׃zs dw_ECK=Cy,ݤ˩O|})fĆ|h~_02'ʮ]ڿԤS{oa7 L&d2L pM{ h,#7?EorXFO7[cH#\KpM& L&d2LpUxmf͚x[SP^[?&i^04k%&\KpM&d2LpM )Q{R\#ٗvΝp-dd2kd2L&i_D F- W&v+s1ghג\KpM& L&d2 ಀXMbêxnڴP֕\ Lp-5L&d2 滀X( M7oenV/2kג\%&d2L&\\-5dd2L&d%KpMp-5k2L&d2k2k%&\KpM&d2LpM&p-Y2k2kɒ\d2L&p- <81b%Ky rp Z:.0g>K3o>8찿dd2L&tH$n "ɖ,yTn;w ^xw~?_-5h)_71bŊA&Mͳ'gUsw5L&d29tY2e_|1x,YĽvws<דּK;_V)U__0 wd) wqe_6l7x#xͳ'饗^ .roT=sͿ_|ے|H˿ [ݷy_X\d2L&~8'=k׮=jժ,YʯTV<줓 [שvl lߠ~ؾJI;o)[NR”vÔ,OjվIغcO _}U5j`mL 4p٥އfzwd)ӧ+ ׫:17ntٌ d2L&Ӿk k ^x?Jҥ-Yʷ=wyMAϞ=1cƘa2L&dwkb=\ ǥSN9Œ|OŊ N{V!&pjԨQI't9PB8#쥗^ 4i AW>A[h<3s=t!TyߠFk&?hܸqPN7W^yep_~yp)z4S$J0L_} =3 L&A1Hտ??YË.n=&p[ng},sɒ%~p6lvm3+V8 ˰t``p׿ЬY3p6lLYp' ƏqӮ]&LLhݱ3f '\D+6l5Vg}vpGqDp>YT@wV;UU Z}XKp+=uժU#<s9<_U<- "}TkK潣%K޿k9֭jd2.mTiYFކ *dNŲ{82ĈY E0?8жx?^/ଳr ze_ F*VBnݚ0VZr;vf9ln`ӦMA}zbۈ6nذ2Y=CEbݖ+Wc0jԷ.]: ).1qcUhQbD} Uo*J5-YDŽNqoFob)?SEڢ~ۢPĒOO&)B/2fKkhˏ=SMnE1u#9}|8#5Ky:F-ZVbX ~|xSYo^UѰ؉'8昣RΊd2 XdE Ԧ*+}Dak:U'޶Ė%7׿-~/{y= u҈Sӯ_t h*p .N-aW:Ӆ2rk2dpM7:W{/cjׯ_):rG?Q6ZA@>D1巢SKm([U)_Etl*'xU)]]t&/>SB/oeЃEU"z[jK&1.R,Z[|הbLP:Naq;vݒIn3q7 *z#(z38NT p[%2M2rQG+@فG\]˶[xW>Zb-\RA^'etk1Z=_)y~}+V1w%X+ EPj׮}D`Tž}_?hܸq1 Q>cu𢶰.jF{]T);_ \[Pfr Ly^{ ipL^8vk;D\܍-knd2rHV\S72@N*.ƃO?O0 qػbBqr?aUO>.Y$ D@.DXPdK1"3 3_]#BYKY(߅MpM-\KpM&)?su믿v }X u po_$b]Z2Ugd`ppXoQ, m+^b>e(H{uJA2Vhwu`JQ^Ki Zխ܄gP+anziӂ 68+^@0Ne,t&,^8 DpM-4|"D͛7o>u.X/ݿ)DS61qcjʺ0k*h5 Z2k2L$og1jūW :Gϝo/Wez(Ξ;R oTک8Jb۲* 6PNmKe¾bhW.\SA)+-]rc ]qA@h fVҥ7_dT2kOdd2IӧOvBL:5xם+X+ōe2 eMa;/Wt 7' yϑ|矻sFqhqg^zA;=uY(EGۖ{M2կ L8跈/b}Q ObDeT 2k*h5e%`e˖-HhNEAEh?Arn煬6K_hBQpM)\KpM&)デ~Z93S -I$i׮]{v,@Gźߛ4i(F ڶm(ƭ3FRJU|;4@,E.gnj*w\B5L0!oW_};3f8p "0f;+_Q>Μ9 3uXiذ+/iRN:QFfUu@\Sx;bӣG/:)3/ܿ;K*/=y9y6M,[,Xh^%!^ѮH[=3=}2kNܯ#ڽ^zjω,lC+wm$^s0n~'bƗ c`\S~%&T"@urޮ\x* cEC) y:pbcjijxಘS)$B 0`{Do^@`t H X|{ p9?O$ꕲSx%a%"!e%)g|͵x][T\WRru660C"!l&ͳyb,7c3A$_|y?zꩁZph$w.fm߾ݍ!jcѦUV^gl'<^մܷfJvT*;?i7yJ~/+\2ޣߠ`C[Mި{)N0\ h #Y >L3~#?[#m}:3m;^8ہ{}h8Am:&~\s1m2ҿpn#7 bAFM;ho{١>B^ Z2k2L&gm2Hꪫ `Ph.@@ ILj F.'tca3T}GlZpD6sOjd=#p  q2E;wt@X?)B0R_ e Y`.eFcbyJ^(gktoGaɹ0*0s, HFO(P!4lGSxstKҽ(\Wzv (3`Db/{ԃׂ00πX/I -Fddy pkG>î]0 ʈ boGxfW"!f#n| ]_x\fB _cݧE@;Q˳E"Xɽ=E5\~m%m VwO86A"26ȷ#(Hx^*ǒoۺ_е:k' m=UQ;HhY7v/o{<[xq>6 ~9.20DnhhyqDċWoqN |?fѿ9pg:ϾP^.Qo> Yas 6Icm`w߱͛}pʽŋ.-zu9\dž6c\_¤ş?DƷM9/>#AHs,^jҕXyp/ŏ9m)̘g<+V̵ی hgxxƵ},\\O8 !&c ˶OؐqxGhFS>I;yD3r+U»48ﯸܧ9)dWa\juX  Ӿۚm~2ouXJ7 MmG~K%lS !Eky0X~QBAPdI_yVNsr)3ЎF9u؏Whgm{)x ĝ.#w}9LKe@axPx@ttT2R @yTx>yxdp{TuƈW=" 1<~o]U?/n *}Ljڳt_ӵO?.`hzWadyQ #O}3R`Q m۶] >U/2^p3ׂC9Ʃσ*cgmw0{<29@0s~y[eQ}ϐQKCu(d,g)7SOz|q҆ Vm{΋/cN;G+pbZ8x[ o%zG8):گT)o3Ҋx?\L`KHs( *P/~Y/M䅷B8F܋J}`kG:ўziTOއOsSҬg=ǽr~$c]I>+$}C4~i,!zt$>(UU| Ԙ^ (^ XfF^^0}jSK84'ѽ0Hμ`6c^K*Ʃ`Sk80c\vc}d5g'qiih9&ܣ32Xj7>m*,: fПp1Ɖ[@2N=9&ItPw@H3\$?f(`$0jC/W /3 O d NjOO` m!/ 9@^;x`I <*ݭAEf'APxύ8[Iұ"Ogz^`3R dq=% Xv!$c *=Ob՚-h:ܚwGqD-ҽzTeVqﶓa9/-B /*?~ᇻ{PV|~Ucu(z衡?=c~"IF5GydUC?yasan@@,㞗x빘| '$,yйQx@s $ 9%u.#6{P;s!^@qo)k0/%76RP]ˡރQxu"+ 8 aD ] S R,dF>xq,׳_YNO Ӽ7}/&)B(Y=+z.BQix2pu u?.s^{1cCh}q)/-x1Θ7ng +↑׍XB],KHvƁp%\~7}dfC5~81)3$t"xުnx,Xo@Ye/ŋq3ч7 ]}(q[K/yny1oQVIm/_|هkA;}GR4NLT{&:@Y䈣ǞxJZQШlV{_=rxѣ伀XV=6HE4f%<ȣ =hب"2^;mٟWeU/-]óPX.m(~%=KA%m>{3ߦ6"QN?!fk4~n/1z鷣SS=h1ݏx=z EJ=zvs:P 77bLq'z6ۧN/ǂ*s, B=ܻ?ZsʻFp]Fi\ߧ{|dj:~}5^7+dDl<ΕˋBF#,p_hڈc_zEVyFeظ$ |q,3oPyi?9nvkŨ?~%9m d-r}lƔ!YYa /7Ł pyILoC?s>TRmr?7 X5ºԝΠi=hum^ns4>iv'?&t[m4~mqwGf&bQ",?.ֳ2N}29GeIKL33}5mFxo3[d綦Ƹ]KR.i.$J;r !oyT 'CT  Øhx ynAZуːgoԐC3cQنQR[*[Q9s)?w@$r<"`7'4uhY Ax =.:/z6nE$Dh&[Qo18:e:8Y3J &,P&FYd}B \<C%pQޡuxEPNY&2eJXj99OoȢxXl]$~isޘ$DV(i7D3۪7kys`7 PSwU=PsY=u5usx6M߭$qKH?2B@ NXU@"&#t9%LdT6m'hp !4^%&K l,tV6)|Qj38E/*ˤ3HOƍb+1~3IceG̉"Ž%p9l(³\Iv&(z_gzm[R7bMNļNaQ6ĻP0ٱ$lއ}$w'dW.XZ6!k\G~Rp9V{uK/S+]qԆlԳ\'=Bjr[(lp<dʮwKlи0YCt.fm0 èVȤј#{:LJXw#a iD]9Ei: Ǔfv踳_DGNB9y0?d /s \5`,a늎]ibbzPJٯy=iJ9Ku<]R@#5$໲:(QJm0.r3 tȈl e1TCL̾88,vz$$!oEo6$I#붶 Erg-.UeuIJ*JzróNNtb䂈r 2sCsTk"kde K5BaauP Fj=-e&'㑬 ¶LΆPD?E9H χHL:X-JNcZ1[@(nqxNlK"I3upMP]b%XEg;<:P=}wF݉q؊ɋ"pyׄm4sX8ȄL~CJQR0Q-O.YI G[ ̌ ?/}}!H hwLj³ d('qG}$zD1[˃UF&zK=ί$X ިñ`0XD!R;vxBB'Ͱ I!ocv %&Nn"yv4m K{풢1#]1Ɣ0^Q߮da'hqL).ck>+{y +}5q_tLZ.*%X"YV,յ=GקkDӹI̊"py睮ر"hû={vPub'™iAg-}'"ة zXզU !K AIe[p6=86酙 W;J+/a8k$<8d32:9'@*=><-@N.Lj{\RW܂<3]|:`wy8#8({PaeN.-uTYVs+4mN;6u>3?#C,v³AN[Np?C92IHH?g:--J)ԝX q['wy8ސ$DzGYABA@FKrN _EYd\R: c򀖧B' |b $Kc{9 7g x&&p/EVd "56JF?$]2WKXKܩerz]L1]F(d?-3w }"cb KØK j$b.M yGRH\H+ƾvI;ba)ʆ  E5a„%}~Mn'Ch똯l!O]w66Zz{m=(E{b/3ۗq\vM@p14`Ui1Kb,֤Xk_3%voc3h20dZ5I/{hl%ISKYbBdQ)$iQ rϸBjUJOEniѿoxq^iq5o,_I cϽ3ߵ9wҸ ^7-Wy4.;[m? m܀{ bVD1kaT+%m863",Qt!DfH ǡ[LR|o`@x %"^$e-IFbcBe `j V8/:~=9+! )B\B6` B g`H'd7R1eCW^i$ ȶ<Q1]F8W|P㘥,G)$m&pLǬA2}#_XKeq@IѠU1bb 1YAAi_uIE}N2Qr{jD<e0D]!Uy,:P \}tV Y;mj"pQ2SxI[8x ˘dlȎ97 G2?gǻņ_5;- .+rCOiYxIa_+֩WjU bׅa]Zբf-JϾ4_Fk@mrbzpZ?<#:-=%۬Z}~r p'5 0j6+e$g}B!7X1&(1y@ȃ\ cB'EZ)W1pLo1Q2t d5@aRAd&1i`FsVx0-(!#ve  scd44X뾄qD!KovYo&r Hfx"nPf?(qpQbDK}CAps 8$b.N7y( qĒV85Ց}A2y&uWt KxqYMi}D8 +z:qdKTxE ?CKTNGK?%e95d!P=*{B.VCMߏ q5 P=zGѱMJX2R0s@栮%n)*5eS^[q#pQL͍.RI6b_T\#hxQ HF>B"2;w\b~-Llb%J (< ڔ&z0͵y6q Œ,nT%7Wy^əaSM p@x^VMd#k ۮ4oَ!qGLƒ `ʹD8 Qlzm f7T(>-d.ݢԷY~C͚!Bt)nFs@$"K{zd"DrE# XWlm;ohaa$WL5(9k`pD,)0l\p,ͺ#, f/wb +4):vǘLx?.re $s%~*N[]="i2/ĹƵi2 rCNuI<8] d鹺*]o])XW޸etg& rB`BLPsKf wS\M?C[1cF]D<.յ5EM7=+Tovv"w9[ukL&`;LK{mg5=E! g +ͶgQdnҴϺ~lQEn/{9' GOhHDc!pJ:w䅄&pnBC&h3ka#phQb\$Y2E0sO _ EVA'888;܇DrKdnŖ1ȊJ@~q5[,)ZDp  c=@Y|yϣigqA3TCD+^ 3dM隫d(X{p/.-=I{" 1P]/ҷ!jgnӽwon 8ۄF=Re#E9PDJ< ey:mi# ljL; )k$jES\pǞlPšbLPP1wOx8Nb0/lՀĪH ԶOQ+P<'>Յרso؏@ȉy1IUTTuʞCXVT'PF 9qzO+AKWno:Io}ٷ=J_!V[#yjݵ20 0[2 ČSHEdz."pz[T(N!9.|:$"E)!rPu ,q!֜ȑ#+2i1R:`pGy!cD6$OYó,`1 t}{NL*' D>gWtQ8%#Cfm!"{xnz/iߕ˘R9[r@U;ՉJ4%QT&+@2KWuWhK}NBߣ  3׎vNX&PP{/; %3K|˴i St^2I4Q_O(kBPpR!pH/! HdyD&@`BI.=Bp] ayE4lĵUfs&p˗j }5mEa8Ic~UvU$pP6iZŲJ-J6F^X{7Kj/fQF%Fu'&"D2ϻݥ1D2?lUU.7j **L}^I>U2-qi|b[-n,,nC M]EZ|ǐlH " :k ]][8|5ϥC_UfOo~ivzXk }ק7t5"wG2ka-!PԐ4@ T2Zr(.;iZBAP惥z(UDt LJ $BANtG9^[hyr:GE_Z9W۔saL$w`OKqe`!'g]GS-u!Et="qQVx_`9d K$!/BH`@X~ض1pQbCR Oy?ls{`.sysq]Uܷ>553 ^WH)Rd[rc[H[FB\H|7l pn H-3-&dQG-gmwbViC!]Sb:a_( ڛFj3Azb }4#HUquM3q*vou"p$5Dwmko)KoK (MhAD1wy!}S$OtoSDDG]iڲq/QLJK{̪"_LMÕxϢ\GrP uQ.5@Wb #pV?vK{(MaF${ H)vzQ]: c7b݄Yy{RWj\AZJor %"b FΓ3_:FȅK*)j%DFICչXyQ g[eR$MHC$·5}\ ./ufqS/D}UnQIҾXR:"*z,v}TG7>Rވ$5\!Č 8DzS{\̳ ) #po>'`77J}pSS郲}N bbMh~ޢ0IBdPj- p5^0F6mڴ0>{B1;ea=Ԯ*NkQ }Q^fZ2m&&O&y햢Tź WQ* \d\~wr2z[G\vQ.+"3&A+9SOӉa@O4I,KJgR> }IDDD˭!AiuɃ_c W|3!hϾ}+_ 'p 0 iosH~ZZuQ\U}4YvpZw+ZM>ǭ5 0LE1JR$H'; P;6s8<8O=TPw0HtZA!)§`iW>+ieH d8$9im2Bol9.!Ydž^8Ila\B`HS.)Q YbA+B  ذՑȸm1FBaB!0#C"-2%@ { ΃Tk +}TE,ĩ*I@P'L $މqٮyF(ُvUQqHD ɉyYQ.Da} 1=U2L;5Q?E1HbF{*jBE]A0Qd`x01PW"2pbIC>{PC9APeQF܃_8A,,ug+ꃶ= %6NJ*ŝL4"3XLAҟ&$+)JwAbUo{eŽvUހ=:VcFD؎"Ύؾ/).}LjRoۨ0d#/!;ckAR807gC6W=5X %o:a*A͘M>( 7ߕL)WK_m^JOVbcB 8VdMۚK;bF઎u0I@^k6Z ń7\@OIPl0&O`rK%بC=blJ!p9mU;Y]%US{mڛƨ_>mtT~B/ػK*q󕦔cξ- 8$^ +qͯp ۏmڼ9 AY˾mUbЮbv+D}(߄- ڷ=uX瓰`a-;  sb#xF8#'8Q,;ũŐSr{9?'C!}yF]0' 8BbXƁVYZ|dƉpU,8 %PP@|㎲ bb $d__~&dƭH1vsc 1x/@ᤰTr _#&3cU;n #p:z@P q! *T D-k6 1fkQG ~TQ=<ϐu" \ GPvިMc{rp&3)0v B'79"y A.RwPF%eܹs[Z?9F]߁}6b!g{ͨԻg,r'U+6T}I6q~; " J/B%=WZ\vC@ѳ##bc]@$%&pˇEͨgޅĦ %}7'+@8A+Bt{l (5$ǡoW~++9!0˃c|2)x8':A=ϙ6.3+CۆO%kkI+ul&w87$}~uPU"[6DBjyl?]CFS;iV}v\wF[M}Tw8c$?l@ujN2\"쑊$p=dBDul6/۫m^EKgk%a=Խ;$ [5nG6R6:5"AG w#u]o- :"IvW\it=B8w~i'\f鿶ݮuѹXtŕujFſE5 0Ltga`;H cH(Hp"F#`_f9c2@P ~Gѵ0c\۲7{srh$-P ~&a<䄌r #2!#siYDGTx{; \:W_;/Ļ%a2WNq}9\rx?͛ N)ATU/nWqJrPg qU$9sv:u^PhwW+qA96PlXT}dr8K"zт#H% cr@clo^Z0A!/*(>M,E Xn>iuPYԾ:׎z7[bDB?*l>MHxXC}2$}E %pySi3F%.+( TF}qu0F>ɧ4F rl}DŽK?2-r6)6us.u.LJZ,8rR&O܎IkC#&&+bo'p7"OF!095bC3Qߘt=&cFYe!(0И<4F|yފ pu#D=;z{>񜚄mFcM'݌'8}^04cb*{>K'pkpc;c.qlv>]+WNE~;2mg@(ہ+GZg~;D&3z +*~KN s.r \sh+}s]7'R &[  La&pKGX2[H؆%r|3(?1#Id ^Jl&R>C. _]BHAjH!ɪE)eDr@^lrXjX&%K!Z2yd,Izԛiu .szvLȦ}H7|qkY>6gm?GCEr~g$5('wBK$ΆNoHdrȚmӌAN0l@bI1`tG/8j3okH|ޯؓp#0R4堼CH* $Z$`nIăc$L,Nx铡/RH?NdVR"4 0uPI=Clu+/?~|Pq/\; .TʋqLj#?33iKWȋo&p0ŠD;ʍwύ}ae\g:1XC5l dV:%d5&e~Kn>hguXAh3&ϒBq5FcE.3X8TՉ%}q BbP8I߭Cs5bnn$RՏIHHvٻNR \GVZh33ŵWiuO =v}O-#+Bdlϗs Nࢠ 醛IzMyi>W-<kq.4]ATħ=a ?wXWShU꬙;jb ,QJMͿ "(y{.IbLa&pKE! MhC#I5#>AdR{MQ2zRELj$ w#!bV9S=eQv$0ۈL`3b$GW[ pQ͈- pYrd FL$H 5ya>Ef$1=Dvi*|OHiZ,萱T:]Ivr.=}s_b%Ye]XژI6,E;+?Jr2/QԠ2Q?/C6Sr&{R Y(fI{h,.9,0]Nhlsn1Sﳧ!W}BjԵl" J`9.o-W&OE=d)¡T2ENIr;!5D_K&HI/ˁ;UJ$py8Y\mF3=u̞4[9?g"m `ǘο8- o!Y[zH|"xoJYޫJX ӟƣzJ/ PZ~RKFǪR 'ѹ>:"?D0dQLxȥL̖Dr]}QWS!~KV_nZl#=YxilEM3]~Wς@aem29W7z I~F$?Ew06>ջW!**=u_WC>J=#Bn$RXHt6=\(Xir+NOHqFB$hO"X}wٿб.sduڦ{ H&pE)h0 è. [`InQaG\,Sϰ,m,-TeSaD4xpIVU+Cs{Y|3"- ]ESf( X:ʰHս9CC C~it(\O4dc!{PTBs#vo/.YicYcyT4TDD甉 IK"%="9{ׄS"H(g#>KC\HEA^[UOptiJ]j'S7eI F$t;ɑT"/P'}8YFR]YHhT>08,!g'ܩX vrzr?[?U/>q 0m~YjrYՍD,qIߛ-I%3Iԓ\X`>,!Ai]n2=<7u4_''R[*8?* /q)wuc&Q$_XmN7V0z[& 6#-Bjl&v6K[)ka2D|_-?="'eEzaj23Ė|v$e#%p%+0.}ͷqs)3dGXqxw4ހd=(an51T(Y1/4>Cb+ :?-k+R#4c "gǕpԤ;UL՝dٷQ5ԩW?y@RؿUqM[&HvBf7jS \|7aQgR>!ܓԣμ6yF5hwh]lհ֡66oS@:jkSl\|Hv:zM ҭZ[h{4kaTyI&Z5JDa#cd\%Q]( QF/؄ cɹmE^# ȉO9iu̸Gq/b+_xEBgBǂm#c 9+.s9f(R|{B"[c[B8&)M8xӺ"csm" 6F utR,/"翓ܢ98Vyp4Q,85Rl#2 c(wD~"7ljxQ \32 U"&&1{<^@RbiկTY|_w6?zR88լ4y&Doi*LZ\T}е|&%[nu[.kDmIiUYx.r(ձܟW2Q?!QS \!.V;g?QҫLf );\h<#1:[맹uP$RC+ue˥!" pQBo>еK_Aݢ2HN}Re5kIn$Ы`W)+2d/=cĠ Eg˪t3Fkn SK;BIJbK~{ohXßEvm7@Hh%l }T@) WPՇ̹Pf5#sǨ(3>P~;@S-A"=EC@bN6ySD`p8Rֶ D]e \~[h,.ƒV"PWQBV2q%(dB[ZqP+ h pW݃U'*~kCH1!cLI` rXmkΙk#~ῲɏxGN]EAhֲG`WsXr''!.?5q]Ot8mB#vMM~ץ>YL,L>z W^6JWQhӝ;:}''P MVm|n$hvOL $' N7۾Mі-=:0?=Kwh9met]׏,8D-iﴉŻu8N6&\[<t= 4Ӷ}.LM5\! zhlƦ[X1C~Ẏ3ܲI 31 \0 \YfEJ3CXW88(cb,}~qY"1 VQ@8`|k NL. d: g n2"a4N!y B6iGB$` H \@tq}ph'lirRg % UP.y'fؖw 9 ̫:՝闺&|R7pO%B|:>! 3PܿgMCI$kF%UF}P'JǧQp!!. 5LBPPx(k*˒S=큉#&JzLq,bR tg@ }2&8J\0GqBP  1@NJigU5(gpX5Iɳۙ1.}xm8%-_& m R>2BLv,WTq x> ;0a Wb|钀 B' H{_ Cd 0)Ue1x_`3$K-EV<l&{&>cE{2ƛE{g8Jq̊)B$ϸP7QoZHF~> `\ն覎q̲4&Cj;@Ozţ }ɻEVfW^*_Xch@&+U`g~h?$mo[ Y{QϚ}?ƷE>pgqRX~Hm.9w!OU\0ka S#4BQ`\Ù]\ 0KRbr8Ȝ õ( ׋1s²pu(r S'by2D$NBq`,买,,KaHHG@E53/dQ(Bp 9_\9ÆRfԢ(09Vi_nt(O!]p pp!ċs llC>pǑ8"L''$Qh( bرa)D\e􇐣(Q\Ēc/2Pm- 訳wZz6L;W._DŽŁk&M>_ץmgU <=3qs4~g miLvAN-PR/sեY:;"2 猄jŢS'?\?h/./ ul3m_xeݹ`B(2T*4wx$vULdCq`R[xƌ%W.P`g wb1d5J"4r<! D{d?w&Ae16D= wI=ʼnJ Ht&p&x/Xw gmSSo&w in@XD٘Z^wBQEE|YqAN#Lql_ *6D@E}>ݲo$ãeeϕ~@lU%(_x1CӄÉ3zI @e a7B2ALYXOlhC(a QR\80πei& %ymS3&0G{>W FUJg͘d7}41Uk ;_6e{-}˱cyźrMs&Pb@&J#E97D3qGMoK`Cc3O=tyǿ \a"ƂD훍7Nʈ3WjȐ!`TWU5&p ?/Djy}+20 l \(Tk,0b0 è0[ ˙bL~.Ve*ZJ=!pQ$(P{V&pʆ \cQ{­U"u[JP0E \"aaQa3bOa6rlw+KѲ6Rܮ8Y95 &pʆ \cQA| }'5*3gNoFeQ0b0 è0kT L FYwIDfp}oEfҼy+\!ޛM FE \0 *Q)0kT6LeŤIg}6i_޶mTR@.Q>6]s}fרl5*&p]LaU&pJJ+Kv/ {g >몫"tBrwH#Fl4mڴogqΝW5^{I&ܻ+AeXܨSN8 kVf2͚5KڶmcaQeK0*kc, j5èHlfI֭!p_nڴQ,tfmPp?{;' 0 >|xw}w7B'4ԯ_O?A\! Eǎ^zݾeB0'߾yavna{>H<55d *ApaK:Zj)Jf饗_ٟܶjTTdMs*O:~m BYf? )+B-Z.;a"5KEc&_~y=՗믿O c馛n(kZz?;.ݾFlk?@*l^]*h·矟t._ԭ[7Im̥jy o{[UML/dK=%=ӕWZUVY9i`LFLda,iXs5Y&VKq..Xs]v_EwtB\uot~gR\_˹GR3RrO~Sїj+_TB=Vv"fvNbƩͮ?r ++ZwW}>M7,PM&2 04 4H|e..TS.'å2JTY[bK7ҥKHjdi%vqD}KeZo9q{?С 0% $M2T0 0 dDPi֬Yرc>QL\?0 0 >7($3 00l2;CP֪U0 0JzgϞ%NСCwcaaf w:蠤]vɡbbʔ)U R? 0 (3h׮]i{9$ Hڴic0 0r&$J 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0/Rc%yr饗&͛7O^zi?0 0 0 RdM6$8p`'O=T2zh?0 cÓn)ySO=5b-2K-Tť2ˆn۷o'§aa,Q9rd&i&]tI5j1 0 0 0*(pׯt9裏G}4y뭷` 0% \pAPޞs9ɰa6iI}=ťꫯ!CN8㓳:+aKzyIN::u: ťR˲.v;9c2~y0 c AK.$뮻Pmi*\s]tхj;.aK}믿&rJy%?]\\\\\\\\\\\\\*tIInݒ뮻.?Sa 0 cÓ'|21bDO'fСoťRV&R&=P܎aaѣI&%n2rsqqqqqqqqqqqqq3vԨQ<`0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0Jq%i&Z2`cI.Oy?O>/2 0 0 (FJ/yדgy&޽{'rJs%?s2yd?(0 0ʆ>,y;9Ӓgqc=y<99/yꩧA%mQ#Fرc>9!CC>C7 0 0 JW^ 'ݻwOի뭷~:|&͚5KN|/Ou떴j*b-[n%(裏N?GI?N[?p0 0J[/3&9sN:%w^C .Hk+L}݂}֭}7ӧOrufM7 .D]wݕ<ɝwޙlAs9a&z8>;SL aaaFi_ "]& 6 gᥗ^uQvm||D)lmڴ –o=*JnmzETć?pO$W\qErq7 Ν;7| o#>>~='|& 0* ;82$V!C%@oZoE>A\sM0z f0k}뭷A#)"v3ڵ#seeXV#(yo>;C `W_}5lYg饗’_|1|aaQ1k֬ A?ԿeX'tR0hcq3-[|E!>/]\!KHPcIh=:th0=1`'[2}tW0 0 HD(KQa_ȯHD $V?VrnN "uFVI4Ա7ݟ4h ҥKҳgpNCԁpDV!¾ՕOJq|uViS~9$'}?mW~"i CK-T*_']mu]7N`qYC6w-ds ' { `vQa*ݩS_PLtGnQe:O? K1<.gjXĬZL\R br#g:Ygu[M! 曻* .JRvH/e21a#?,MeHB'#|1p }53ǧΗIe0"}Sy|;<;4foo߾:Gc >MTɴiӚs2:T_ H`{_Mvy`;f߱Yrzbn^{m^xd=pcZ`Tjs4 aM@\XB ;0 d!'Y G3YJlkltU |xlXES4aDDhMuCOwwr[ZK*AHXY(_c.+,K/':?*c|11wRj>_骕]*ߠ] s) h!a,N@ȢeBEurotNSRө|TuvUO*t̶Y>D5 A 1TGͲ7'M̜93tV"}``@/oY\@ܒ4:axP3ƨ/Œ65c'Axb=cEE۾k/C2)IY,1%uřzN:x-#(afg**[| q6/ꚟa>+Y޿s{j{kȖgޕQM6MtD&x7K e5KXy^$4` j#tQI! sp|\\*}MTv1jM#!Ȟ6駟DRˆ䯀U"FC&t%.E!Zm-*)H{Vg%xFD!!IU}l() ڵ^;%Iۿ,_ Z<~] ޒxm"Kɸ/!O Yş ڑx8/^ 3j[|y9%){S c2N~ߗ:KL\+q è4(1W mLڍ _c۪ [`bq_)S?8qB"> ĘbYR$!3)m00aOaD/IHtD݉٠lv"#*|5l E:yop8S0ſ&cmHUE Ll(-mgf,zo̢ B L8wEʎ6Z*2@ߗvf;hf Q(Lv2 0 0j4$(7 `3 UH$x_BѲGkhV? d[ŶgCelEVR&bv|"g&~ ݩ/bQslH[e||| 㳰p x> cTgOX b%2 ~(>a,N@UؖMlRH*uNUWSM>Z!˨0 %)j9s~i(nX:O'qdiJ!K%/(d0*Qw s BD5'Ki5ؗY/;K8>@G Yv(o96ZA AVbY3jc>C%'Rv+T_"'Urz^Gļ[d*" YRTk%ZaV`Y d0ˬG+wjE$O $J|{;-K}gszE<˚ZZH]pq..X?C,'ec*V*ϾEY[ᓴ#)NdwhA0A آB@B"yRCvwuTuNk?O/UXZ >aTk!Ђ3@-Dެ![!GI…ȅŧV' ׀0ab,S#EΖ 5u D!9b+>!Y=%U ኂBBBR}!SQ7Wd<5exĂ :}Ѡ%PC$s~::\@}Ts:=?m\kQ1KԉgQ,k\VQEFT(Evʅy,QHd}Od ~[J [ %oQ RP@Œ Xx7\l]D]:"s@sE|ա\qw~Q]ӳzJ8KsOؾvj]Rz7c3ťJOJ<@ի&+.v[p`6yᩲ#/.ٱ]vٞf)*<¢IV!K/l[lfŖbi"?dmS>|_*`/w/?"HL~h;è(Qb.B{\⬰JV-+VF y o*\/ +z@ 0*(gQfUWsJE>%,}V$c"[3Z+3ol!p˲ZS_ ,-?vq8&@3 e,.|r<ԼtHȒ_I!|Vx&K+}*$@*m/G)ŵed%J6+| 5nMG1K–;X{8pIÆkIJOd jWsP!,lI1}!sMa(edOWZ6]fk3*^x\* CiSּy5RU_ϰa]'&tDCUE5ͻoKNNRq{냉gz.u3ITXN￐.I3H&e}e,'6Àx"m1 Y]'k%{~-u/袝x_xJpl[nmOqkebbh .D,bh:R~x(ħXpouw;vD&%|ĵݒI&khM9F8+{R 20* e 7xcFyi2pj֬YTl os:E=??p èx3R +2dH j u*m}}C m#BHݤxEDl;b*ImUm|m!oa)|CCku(v*\\:Gs7wg].紛fZ_Q]Y`5<(2w'w "/mH;Rm۽4.d y&Mߡcu۶[nwc_y)=tsq2}2R*M9fq ƌX^{tƯӯ}N>eI3>}6Necn-+ugsF{VaD!Όr:d4!xFfH;wnk**$F<义%'"5 \ $Pc*Ǯ+wB!.BU)2eؑ"qӵ"26w MܕKBҒІ]uWd*5+Q_y{HF﹍|6Z_LQ Ge*a(ATB.S3\PMPgƳ^wo#VpGLFKQճ$qǿ6{S R!aT)m)yh 9i0d-1mE^#"U[P-BNHm!t2 Ye){N>"8۵۲ \J*@^6eb'07_Xt2-OGHa6Ɋ2D0{LB(FaeIthƆ0`@\W V,evd,X$#؂LfsЭ[7H$8L"N:G' -K>GjO5sUJ[|{ RZe=X~R0?B yGȆOKK^`n&c 5͢"gjRVroؤب %’A$HNA$'Q3(QɒS۵˰AA@Ω33==ԭ[AdkTr8?V$Nq8pQE`1a ,C8hz"B899r\'2@.y@: ]#="͒8K1L.%"Tǡ. r(O> vt R+x\N4AZk%'"7z,kr :ڡw .<+OۨZJ T`SވMͬb¥gKjW,܂9/\/SD؞Ŏv2l>>/rX@9yrLz*0@m{n*98?o nXNQ}HSax(qݢ׊Mi[HoAV%,a Q-$v2F[Vo)A$ ų AԽm$R01JZHW `+Ek9)/׫F3J #wA}oHZL2٩(@UAm{uYX#+FstRGGE>w*ݩw:%eFӋ7lٲBwd?qYlIf3Z2۷|Y~_v-Z`;xw,Ѷ4߶8/i! kpܰ\A?dQ]Pr㔖tc5jf;rb;ǔ{r ?r7+Gs d̠8Jr+SԤ:X@!feV^Yɨns=^1+ ~w;G9[=5 'cր4ϯ~V^NFYVzGOEWQ6Wti>rk ڲeKw]1S|0 bJb+\K@->G,ۀR \>?rVUA }*f7ʤrNNN*x0ZO_k>OA?=)(O0; Xܰ-",'5.\A3*j Ɂ\5 ԸO׽W%,a9z_TY.'BRM^4T2(I@,fJ*oy麕V=eRpǕP ` hȊ]OMϫ$K.#?kId% c@pEK"@iW\* \Ժ!= v@e9. ?BI?`>Kl X!% ELl;^k(3r/lIՆyզ/R^u_@8uǪc4VJ(){7\BU:e an:;O{KYêYӳM8m+( RX/DoVzoXC7Y~c# |^$`09yqSJʕ)ΫD(5 Q~o+5a!^ыvE^k(gnW+v XűRNPJ߭KyUW}\\$ 7 ҏځn,cPՒ^WPYFqAKtPn0y. =b*Q$L8 +<X/^T|5zneCdF ':k|y8:Qw/w[2<7,a nXo\]u-M;IuBߵmjkm횫(!WYDeTH `M+")3,RaB=cG֔;8+HjRׂ^_HMn< + :g@v Bg'JyW,Tƪ܃\\~BJTҘ xemTo<~Q9oLj4Ȼ8n{ υ"o%l)mI<X"Xў;7&B/\*-+ϰ=m_i. K;J_ pYXV$N$PZ%JEimN`&+)-Ɉv#OӔ\݀WFi_qU w~W-J~O!)-(jQ ӱ:aX\O :n 똃ux:Ȧmбw~AB:Pg7B.::7wjnSb{7B!,}W,(AX #(uޡeq=%DP5-iI"RFtSqyx+5 \õ5$n;͜n+NE~ca]:[ͭŇ޵wW}ƞ|A7nkZ X|j)Ծml6k׮]ε ԥ<}/opܰ- mݖI0vVmۗKmN+cZە_5 lՆyt6s8gwpK+kc+]~ P,\'x俕~!T`-/mz;!@6d݉x J3I5(U= U+!f1V*y6:[O@V|YWVQXQ" {z<8X#7?dܪU)Xa)ǵ`5+M6ne.ïZ<9a ˱/Qs)b}&-*\5)oP&$$1,a ˑ/ ,p e"?$iz2IIN,,pO*x?ӕZhśiY' Ge#V')ȝbV b U 0tyZ$;XxRiTu%_.lO?yIQ)g#Eݛwg{Ԋz+YWz>OCmqJݵxA"yp>|}WƵ%a=1`퉪X I.K mY6T5[M?֮S3Q {;Vk Zn+\{B]*X`- Z+X-_5RZ/WAw*P;/A.$AӢQUu Y l%,Wa&JAUjoidVFy#(pvX6K@\:FqEuVFe/@. TyJiȫ =txJZ!iLĒ[0kk@.L_ 8pSq5D3V:뼗J[Il4 nX/#,*H͟ +uQ}:X-P~}opܰY, PnEVXcq΄uz\_[|fn;XO["h|NySzE;cfJQx Ӯ=Oo"bXX8~{ >LLB \\ҫXs(4-,V+y/,%Է13sMn{Da963'vmpՇ(*E53O‚wBXW1WPz uC7u㺌8в]{5;uJZӢd {/-f,{$%^wz<ϽG"wc1|mx۸g9ϭu+ ʋi}.sTN7xˎcZ4xmikJ]'(Zh?)n7k{^?Sw[p jOS'W%]a KXr\`,N TDIbG#0DOD N0IfD AU rDE<̥]@ӁV76 >"A~,H E$$<#>7F% xN?x C2,T_SSM0D! KXN̞P.\ M}r a KX’tZ$!^^o$q 4POZE'hKL;$^Q#+] +aY9EU̱J׫ |MEx`NCRLiȆ أ ,X#3vS0x% ^F, 9AY EXvAS$?X ?|qڒxuDGkb$sΉWG=טku"\H_{oXZbmuv3P  x !!P:G믭_mḱ`ûv/[4V,k=kOKͨEsxr#^|-kTظ so}]=}mtQ"y$޾{!9W+GX>CEƽZ9/  5k*b_li=e@xǽ=߼FVx*e{3bxjOoB,>kOc|6Y p"M:1Sz2s&]4 np i-=zX6{?U*v2FX wM\+!q.,ۯ-[|}?u :viTUK+j_} I`VgD/%*&>pW+?%q .y V,͒b9 y#J p=d%" A,S,GZO~.|6{N]⃫ߣ|O|тrCd(&mlp ಍ryfJ0,a KXr\:Ōґ0+imz:}J۫}wItIyO =RuD/%К&@ K@S%%9CUbW0QQU~(IQn9RR[dڲGV'`@%L+pnόRz͂ :Ǜt>]YHg-"OϽ-ULaC90QF\;05CA/aϣPǤ Y> A.MrO'6HD@?ԫF񫍮3 e&8 8ݚ꽋Əa]poMR٦zԿMMv*#]ڐO_oۯSP&Ae@ IYD __,hl*-c% 'zV\Z<"g{sHv8Jpv[|&miq9ڴb$ yp>ob:p=xܓO9gw{}3i<:qit5-X{&.ޕhWe., i~mcu\oҢoW(唫y=xf^`gdH=3b^}bo, ^}%_+Zj*C0v&eoQ nP:eup[/xum+e?Vۖ z^[q_{x3O/ _y5SR D\r>6'MPW;V,X8DǮľ#fE\bRyNwr]8ÎEc"ˌ%-=ܸk&wJ*@*gy@N"a Kp%,z@D&MSL`1{NÔ.Դ{IQ^fJo l࠱?_~C&T6:N@DCm8=~:u2Jߢ3@A4qGI(ksq:Im՟#RGLҍ" +(!~G_ ٔ,CD ">mR: vtv86 |Pf]2| m`; kUCXa*QJIA֤FugIaNu:6%.zRkm oz:!xѕ65JqLQ۴fwpݓts្|}%-t˼;w>W|}C;a{_|q+?[x RəӮ/o /N=5]J_v]i~U(^WzF|Ak7j_>3[ھgM=UJiԨS7X1_vqgJ>H"qjr^[馛J~uԯ~MŽ+Wk^}k:/L5FAt2GJH Ua4amOo!q^iu6V|]v=[x|z\;kܬ}Ԩ5hTՏkŰ׵ZkOmbX8ߛV[wpW= kYSq +_@wzn~+V瞮{ A֬YX6۬ZXM׀/g(ƢĢVlױ5kT&us ʦ>X+7(PBxiCqM 7)#C7,a KXr.I*J4uJ:vߝ }GY݆%VFtxeRSA{ hLԨ-I5-伏gھ2ASm,sN' x~m"y9_`u;3vJU;_EJb+Fֻ ^yߐ o,+K.ĭE^z<}'֭W=׸toҽ.W߫C$“:j}#A< p?z%=+[<QMQ`m=s*N^-Pr xb18ʹ(nLq5,qNmKb(11cN֪q}z:Pרmu/F>᳜%K Nkww%z:t(,zUL6j?ol*U-o%X׋G\}܋ۭ綬W^Tg/60ŻxR(ѢPgXWwK;E,CO]uںҀ  * MuXt=eTsp|p PY!QP@],"6|+ ;\ޣgr^lII6 }JV&q*6 QAbrG =`.pE37G؟A\1Gż}^T@s_g{; (l ~̤&Bkb)*|͈cQx΅['Ou]Xe+W7k0Oܰ%a KX*?-к #X@^)e(.}:($$RN׏C%Iu~Mi3P$ 8G (JNSf W(:Ɋ1.6SlePz ,03&SfXcq5Iwb6mR92P`0K1D}Uhn8~&*q\6]{^4NJqZiБ5)ovr,T@@p&ܕI^/ܵ c.Te+% _슟آɢ`YUWep`knʡyB\Iɩsζ /v/n)^/y6/MK_q`XA5k t>X0ײ%*i四+8?<" ? |5;0W.~uRqKܵ8+K.8eƀ<p;9c ]^7 $&(v]bxV pG,,뢨wꅊ#ɇ*Fms!qb8D$\΋OLEzMgri Bӌ7|s\b",l+˚xެ 3}! KXB%,a9~C*]E2C7VBBC\++"> Tci7V<ʯ H $g{%{9\*Q>1+@DOR`t[lfu7:{U&GktݪUTDCz=__ :K" ł%T]@Hpu-kI^YD5 vʳ]DGxORVboY^V>.VEÇ:*J\|W6G TʲﮉBn< Y-\m- 8iΣpi3^Գx0bxQ29^~_o~"oڿ[CUk<}t<[;s597[SV_AkzrZXB/5u4 8da7VE_x{gϾWO?]}ômVT)k޼wo0pYtg^cfiU?0SqXYzEER[F"np&x-4f#xj; P\7aG,!M : 1ib{R_Q׃<˫/0xGǘbz4/z?H|LJk&Xd! wJ}AΊqzXGFMD߅ nXBܰ%,a\wg:t)PBIL=VRk%j BEXpidDx$j;w%M 0uXI<9K" S@1Z0]vG0R8?l_BuuU:/rƱ@~LBpI=@NQTg(9I@ <2D8oʼn pO.>p`GN4>Yp [ PeL%CZQ#u΍#=<îq*.G*꼑ޯ{-^VP֧=.xy=Svт44SܙtNվ 7cƌ1UZ̍=v)`ezs}j8Xd7:95s*~ad<˜1JGI+{)R%AJ*ՙ!P[E^}葼v]yڥ]cO?=⪬vc,o{RRVyEW޵#w ժ[L=)` b12@,`,xѽ(GnQtla.r: j着b%si\E:XXn:򺇷~U: Q++_΍x:C2#0cNJ d |\p=0]pLtM`s,:8Nnȧ{\@G3v p[]d+DCT&mu F87yŊ ay3e<>p{ `2[ZKu~Ժ[Cm[v3<TڷbsL :o,p`K`Jm7QJߩߦ N?xalLkTRyu_&'<7B1 ɈA1IN[e`TbLǍpf;{\ \# ':EaQ'@%Tx*x3,bXjdC- 1^Xelsi|7NR\,xY@ױSO=U@u3tS0I߉:&!irwt_K/̽n3<(T9+V1Yo->ih{TŲyK8H D u;hBmzonׯ>=jϑP.uylC)/8{ pS+5㎓<"">r={dy-y疤T%y?H\b52߭ߡaIapO1]On$%B7,a KXr|KZStUQ6[``ޯԁA<dm^Fj1ULWIou: xLڻ/Z ޸!NBb PTƨ,Fq& .Ԡ%J㵢jMu+"P]ཊi5w0@;wɋzljp~}o,uvE˰O@Etj(pkO=t};ε.X.Ĭz0jpv 'U=;K u3P3GLyAǫ.sӁE u]<1^bH1c"S5x҄bTٯM,5>3Bk .8-[E+MPP7``&A$޽_G Ձ_=IxԪ[8ݪskIZK U/SN(WUKq;\sCv_Q7&?Py#X`{8sNJ-ԒV  pwgM[u7g͚u^'|757ϘޢOw, h5t}j} gjW2i0~9_ </9X2y.cbE>˵IG+ȹ 1EuN-P2cs]6]mBp%,a ? 0&d|գ<ӺZ$A}$sR&dב?~dDty9J$JSE(PתFRSVCmSuGH}%ݮ$jW:OkA"VG!{7st=#-\9u!@D3rJ8&;S |6tHO4 Tܷ|_qVkrwk" H(UӦXK-2E[M팆\yG9۹9#OߌI'*Mobs _2Ġ $%ߊPH5=bύ::ʆ*^H@rO~7QSLt b1l+h9kǺoWyS%u*h-VcRSwcڿTs :l5RgݐVo_ 8/Zt2Bt򱶿R+bM3LVt:@v~aVZYhGqKA_}/-[jnu:fI*-:>K}tR@%+3KvNIw»n P^,u6Vp&[?p[!_5oVmd-^=]sIvr@\ Y8ۺ;xz&b 4 Xح3wEpˈʝDQ> /$ rʻ赒ʽRj{Ie'_æH\37/+[ױ"^͊`?#  \%,! KX'WKM]QJIIiLGR4[D\' /y!  \KCZ@+ 4> d@=:O^.Q *Vh'íʴ/*Sy~ 𤷠E} \Pk&[_YıQky 7bx?k?qDz9r=.|>x=ܩtlzkR[ qi\c"M W;7nA{msw:&#Xs#H'S[N̴`A82u @bDQg(JXe`*;:OLFLp$U 6@".g`gA&Ny[Wϛ5O$mW1Q⦵FceT˓ ]O˧Ylځ .Liz쯜f5|Mb(>>.ǍΡs)܈/9qjs]bF#]ȃ)GьiVG?Z #}3S9Vȵ'a Kp%, %r`ܪyTo*ٻQk{' eH>'1$ %IL^LU&)H-UiZ$aJR2Vz^pFMA~P 7WI2ᬨnIiSX{4{E`Av7X%0-$J=,K%?xLgGOcxup '2%PsO"܁롎[EO5]KR-ѱfs=P:>\Np[ bQJj~6yN P٦C,]^C VsK&?T;4Trpc\G KܷzXݣVH(Ql p5"ZH&" `/bbxվJ|7{1koKVoRxJIY66ͯ{_ozbZm%X3KfKEj6 gM (kY{ſA Л~㽲v2Vv|n= SMJzUuzZ,nTgΧib3x"7iY`u8"ˌwD6WW+ b5P磪V~UOlM{cѱXql*s~ݣOG4i?n!!UJ>izeǃjQɥPUmPA Z\0jeK=҅'/yyۃ8g[:3,;t-;rNT! KXB%,a9NTI+H4 T" VP'RO])0POWrWd$X pEHLY u׼WR/pRh\2* >6}kuoJг~oݍJJO5 T@_j2rk>OۍvQv ޥR.u\.'r]j,!{UFřR_p}[kn48]Khop$=nӥOoۄÜ@,y/b&A{8l ~۴~&l9ZdmN1^w,W+p;JSL렘ZSyXА`,<{y+ȐpʛQ@m6 cusIj`UBY;v8UvuΝ;[y]pb`Q8``v)6 >)F Z=,ܰ%a KXRg%NH%~YeըoSu*i}#<E:oYd+, *uH)vڹOUsyIIIz4_Avj_e2 PV@S"S. Kg$fXjGt*F{$npM"YB5qLm^|.)AZwDk}OI=pBU*@^= /f.zE&-KXX nvl( .s>+&Or_tTh=칝 A#oGOJ5R4ӵC%gPBi+zFIu=" ?uLpT.8[=7[ZJ ;ןvut;y@Ž G' y8W3;7k # p- ڗ-6){mׯ>рEfD;(YpM`~T-S,֮A}[;c{H$N."Gnj Zʩ* (S Oȑ#>YJ ba`dlqkgÂi@ jc>}sݟn+~wZgP7k@X ȷ}Ze?;Co@__ws@P\A3G Eu))*\9^Tպ=MZԳ&M Y:2hXwnI)znC 6mY]*W=(?' !ȃ3bt1FЗq`#`NsﺙX ГS#^R{fh{4  *}ь=:WT%]gGSܰ%a KX2]0Ik~H0IQ_E \w% H5zt*J7, !T3ʗ/#`rX !'AR%h7 PRE\ԫjtW5d%:JcAJR]SXX+pDm/:&OŸKJ8/Js>}jK&%ҹY|ߒV ]2[!czvw*A˥;HF #hxAY,~_(qAd1>q2NIk@՟d0bRfQJ+T'&r3}g+\ j|[sܩNRUW:JءZe 0As XS Mo:-OֱNTI]*ݺ:VƼGQS)ֽ%qEö:-d涗8B]i<$ڹXb q *^D|܊-؎cjmɀQ_ѣ-\&ÑM[1%1؊m];{Q?c(w=VAawެ:'|T/i3dNd=?^+ap|xe")sh쵢JNM@-pM_8}TetJ^zRw%lKFQqg 75й~b[|5nVbW8>hK`'rGzhe2Pmqm ni' ;M?9ȺDJ*qRQ7okoz{79*l][Q4]*OfxxC{gcQ87x ʫnR{" Qb$3#HGs:`={b,Vm A;]<9O<=!gp%,a ? .'ՓFIDo+;L&a'CaUcйޡio9 %9,8^a 'RpQϒ~S?ӷGU, ꈻ] &f@bQH\`S\#yx~pQ>H1{:m#7.ၛYH>e깟Pp:MjCQtϋsga4|6u--W4[_{z!`݂xpM‘ґݺìI[{ĝ'K\-؞4ZZ4m׃ݢq\ne]-i[` ڢk‚n:)%}U V|3,}w"!@U\J4\ҽ ;Ph?)UijeW[&ݏw)> bU#,#TY7uDoQxSpAL􏾢0J @4(n:JܥS!XKp7e;>}ŏQwhe$n-il@ qHpZ{=M]M 18{.l p/ @YA  }NF}ul}]`Q3҂:EϵʹjT *W`%˾nO=[=pf+-S拤Ogu]rv͵,-GYWrK VsS(bʾa]fƁ5~tp+` Fm6| $pvXE9ܥG;hVzS 2Pv(X:_ߍO;_ேi7@]%ђE̸&bRܔ($ 2_ܐ8xҀˠ;JAr'TW)9ر?=Z%,! KXPXC@}cJL%VyޓItH\ ( 6 $}dS(P/%I$ q MU?^h:uIde˕if';8T=@>)xE_4,MrñD2ezbT B\W\Q筿P:xD6NyDba: 7 q|)uMn_s ϟ'ǧF?6ӑxpWLhMk\^?,yiRiɪ[_+M`Ѵ`Ѵ7@w)Ų.3[l*K#I[P wj烷߲x ?U~,[eJ;I?+Vnܠ$wTWWB^x|_ 6TW+O,8Kl'0/蔫 xMDeڰ_[tO3"U %M j||1# $rk?@4}($R X%tET``2Ӏ(l;oTt;P2ǠZJx5䑘O[ޢDC( :]H1]zsjVe"\W]RƢ⋋.08?.={g߻* : 2=}}c6Y 2]'TI;""`iO1gx@8hP^z5k3?>my^yi.E*TJV.&$Y;c)P zc ܹ-~~3;c#gx&B檟 H@**l[đ Te9?Y>POi GI>fӹ3G4՞oG,uhY 2@$A1:>}!{,&ogG\Q%-XW{?pԜQ6=3'|bۺ}#9dy6w_}IZR,}|ѱzO72gi*Jk7z?bl[%@;3PȊ[H- k;70-|t]t}kgkڵ 08pW~2j'a&h iܤrk9d/  ?fc~aDvi +ER, &)4TC"wx;KLF?kr݇ Tg?)@1P f!& s|c4]gX?iJ?Kq`&Ub1 2}9>8nû)?9>@Hٰ_bN&65Q F1 V0d}H')\kG))\p\7777 ҁ3e`MЋ"iTAA+0`#820'Hf?0((r ӡ1i8Qn@p@8aL|؀9>mGBP]JYξSLV]6wEhuW &`h>p[l 0NIB8Ǖ.#3矰GnR$Fx~} ke$ 8Z?Ml@;&%-/v7aZb!-Oo/H<B]+mktawn=Y&@'_=}Dڵ0csrA7^^}Im"ԠA[(cKW~P@PFEVS'4?~QouGhW=W_ cRv9?e-.p 6hྪ4s&=8)ze]T SѲ4} R ?`<xbuS{al鳸)dIHLdH E* -{M5ֽD}gI3ľ-mv|җҏЇзLGeIz3k{&d? I3׌uK>Lַo? q2 w_ӴLD57ODeYS (dj,*RpTÒMC,TsE]r˄)8p-At}%PYO \2G.Xة8y\   Lw1qgdJEo)G;E㚸I_@@\9&'1})đ&Hcl*o@܁?Bc?k?C?LO,L+`-}.&97[0qr"g=?#D ?F;\77pܶ3t$o(Kv'mɠ h@&:+ .:'I'%ʹ} 䥿e ' #A;ER#_R縼0`X'i ;9 0'g{/B&q$C\k|߄kkri?iHhq_)^!:\+,'7نLZ؟gN3y4c͙i&@-Y[c]~/Ala [sN=%4RJ˅Rŋ < 0PI-9z8a>֡Cww /=hKxkg}6X@XGcù2/˓s,,7ZV[ބZH+m =#*Xf ߔ>j~a?;d0L_ŽIuw@{-֬'+wG9FVmb]Iv|TB v/k_Y_|֗J[ T46u|\`mh`7]O $($MဗsLS4f-@+OvP2Us) _ %JVmst}(cl_ZnZxv?+wIy2NMbgy[f񶔼C"صMdRx` X?El f5{6/8-kc/gg'&bLJ;X7%6er>O=tNLƵ`KRC"{wmH#RC حsXt֭kAznnnnn;MZ fuUXX 1z 'pAPH|N``< Ӷh7 %+$U`.n ^mXj A{&> 9`ڸv{mkP9k@a9$אc&Ο m,T:0HN6気K8瑵}sywywm?LP+<J1zg6ъ=abf ;R>;{E{k%F2gp|_򡈀UkK 7o(.0j G>/?^{MR HV Xt 4~9OCE2 \ rN|2tNX~lenE*Dϟ4L:-2X>{>9&kR}gvKRB_Jےm,r}7f0X#FΝ~VON&rz><OmWp7_ھK:+%0ǮCлZ0f0]ڹ}GMjlW|^lpOۍ{yۋBsN۷a~ԑ%C~\on@C-j֮NR Z{n5v1; 0riXtR.`6˗s gt}n\7,!Hd~؄~}''?ƌⅴ-v1M$,w^L¶eJvR_Xc&8u:OLr*c\77pvwwcgyMY}GXNꀗ94_/:PTP0_>+m{1i*?Pjpqdž7 7\~YV,㏅w>{0^s4S73eo+ 0=f*!~{IJtL@_3cjN;&iŀ0Mf/=?vIxmZ̀_d-3]AEb>O.npfgۑ*eJB d>Cie5ܨ ]{׍'/=~$F8LEx/C2eÊ+A#=^UtXBZh)@ē+W0x)JժU*UXqH@^QRZZe-Q\W_5^, Fօv%Is=wf޼y)R$c~O;֪U'p;;SCˌ xEqKK~[Rhg5t⒂Dh>C_լY3:c CN@lpWV)&e†̪*u9_|f_/Vs'wU]vWOlp\QJ(SIr^plɒb׫|RՅ:t`؀ _ [XLؤ6b+ 5SWt[Ms1wl7sZ:CЫG U\>pɍUf(Dp!¾"/ Є` Va?=N?ҿ|'>Ҡm۞ӵks/r?s Nzch¸% ]ݞ={m \sM ~ꩧl{X͛77^^]#*W1IO ԊQ|Rhyv]h~3I L`{hܸ:tYg\ussaIh̑-q@_ \ QR0sԩSԺuH:gġ`w`۾ƺQ`i, 4U/93ӊ=%zKU f,bk}.(=yC;sE}Wt^;p%a$ .ʨyƴEp%Mw>v ei:OtlѳD+v^"O0˖.gՋMXBg:Ϊ[A%?MM7%@6ؾi~l;w0ma/—c>2зwgt?_h}f'cThUZ~M;`pZwc30B`'= `+A)՗IclٲG¬``Z")s6S&#`(.UTC3 8.O>ÚQRRT ϢZ-"`%LXb$\aB)&ϟo$b lƀ:H%n:h[n{H5 L*cвw޶ #1[5tb\wy, Mj2ູ9?͛gƌc ֈn5~E:t0 ,mzgۄH7t=R@jZԜ9V1.K.2 g0mqmȫ( ]Q*cԶI {Z-ZT`얥g(@/Rrqݺ\m(£ $Q=W^60vCRٮwa.oo`bĮ]Ѯ IQD/v)&]pnົH1|[R_/ ؖ hLB?~!ꪫL@`m$oA1m UO]kx|Tz :蠃4,_^ GA1~aK(XMLh:L Z~@ ކ>hCe<5\|ycnLg702@J "Bg0l%.'NLاV (&`KVd+]}c=IZ€_-b7b6132XF0SIaIXbp?V9GMzeD/\77p5F XqnTm&g `nlU@[R&,{a}&HM6X ט@yݺuȑ#-wGm hl d6s瞩߽H{7{ \'m2I1Hq?A`Lf_t ^/Hb!(}+ flNݞ^{%N2dp'Xd W^ѻ͘7ۊwVlpW"QzX)us}Q8 llԨ:2L|$nNƄ69 GD2b&aIJ=?b lٲ#t#x#=,՗$[b=W1ѯll]b؃"'a Pf9Q&h?Nh=I,qW۪m)&@qo[J3gǐ"$%c%bm$kXRUXq` HN6@0t&E} xo/{U΀fߤ 6鷈9izjk@(pgkL\KƘnnູ9-ld ЦI fRMtH#j MMX͘cq Xl?,r]ը @65XWW፼ jb؞ӭoF] .`Xb/8 . xv_Fhw x9Cb[w%+%ZU@^@ǎP}R  (h($FO!/94;⇗8,,jI z ׊uw\w"RMfX%A={E 0q̤olU@Fث 0:F@pXbb11L`Vm2)m /ZR ))(ኃ@t<KwՕbziReI ؇eK%HX,@uŚH_>xn9ߨFňHJN7ۈ8d>p*v2 H&zĊ;c@DjD<H KLɾb`"[b#1Y5 ޳XH%k5mלqLYd=1;1nnnnF0h*&ǑF֑ DHkcB` !a0!0V\,X\@`mHCz 50V %+QY2iҤ|6S\B,{ }HwVXkԖ5~ƌtͩXZ@[شZ;_|uS޿5غFVZ @Uo @@w؋j)߇"H:kV1U,X`W>LO>6欃9{ܭ@ ,a1vN6uVu2(VsB@Ҋ LL5jbReSŊ_c8Ĭ)L/u*ב8F"Đ?yHm^>}@t8 6~첌]aູ*Wp HV:)LV][һ(JH CT@1Y,1K\Hc&X`3\DG]\YAqe1*[Ab ,^Q~ZڻFAsz2@Msd*4~1 (mKL*7W B`Yjr$@DgF$J4`WPZ)pu  *3А nФI7- +f@y.ɳ eOL=b$`PF)\2З] ˤ8S2*u.&=(֤M#=SJbÀl~` 1X0+j6W bQ9F1(&99`&۲:1L_&ߓe-( :K%}~EhA]S%UY :תT'ĦKʹ#Cn'ǀg(A1*UA ?S[ ;L*u$e0( .'7e`=('iĒ'fG=(Gk0rDU;+qcD2;v,)oT`BpJ"i A \ot,Za?3^yϲ-پϪVw!LYD}{8ܽt0aw<;_?NswnÚ9agoH8`;xI"8gB|J nIq`?|e@D|0{qx⺊shhv@V=Ń } H!@f-ubЧh,tՎ:L=LDR]ZXXMDjg~%'Ck-bBڧnV"'uf"`c9;9/1fS/킴-5pussssn ^S;W+iR0JZ++p?=f0E)sSM=Qin EtҲ5Cgql·ZCL {*)jomZ^BmeԶ2 ʭa#@ ,#61d;zhQӦU#eڐ*@|wp3^m } ?2/cO2o'Ѱ7m\s<gankXL;d~O2$ʨp {ݤ8&{*՞bvW!afH Cgl*FE 1\Y@9f QNРnpwU+WW/=˝]"ECcBkI!\ ŀ(XP*Ł?hR.ղY~2LfALKز+t@ݠX$]qXbZb[b\SCac$K4a/DY Ez.!Xl,dbe;Cr^,KZƹqc~{j/\E|nnnnnԤujGI˴raA7JSWU*hmT5f(Ū̀._$ V*Ju$]t.bޫMFm35H/64o@(c dM+3GS@ٟ|ٹ\sPt1[yȩ Dڞ"pxHWuoV*kJKI׵zSߜcgwS隤kݷ{S5}瞻_=iR]+`qjotr+))i JHwc{+8{V zj3 G*\+@vJ)[+HYzk~^(i@=A|D러3ULL_u =E{+'# G+m3Ι]\M9.q>P~taNj5{9YMů"*@ +\K:4X96$BgJ[LI.̷my7hID(eb2i0mo9jZ,ՊcDxV؞ҧ}V-&mxK 0VAXiu)XY5-(XH௽&Dr}X&@8(cXK]?i [q3f ]Fm8} zk(;vl$b_777 @\X:97wJl}liU=E` 2OzjTb`sXz%8cV5k5ݤZRm7Ê~1 @_(E #ԑk |:L VhbAJ "f Fƍˬ 1;/j߾ho^zo sy#t =Am8Aف'3`Ch=4`%`"],U3gV;wr/3# {Q|ڵ3ãe˖9TQ%7",6Up˖--e¾XU013N?oC kÜ L6᾽R#/p!sK ڵeCS`M̬Yɘ,K 2@re9k1o#Bq„.$"40v)v2sيb>!@2{!ปd ]/@*Ch! 2rdB8Aցzȴi,a4k1UV3ڶmAi|ZY|Vn1Ck9Lr8ۀF܄| Ē%,ێܶŤ XsFFܶʘAGꫯ,S\^'`,Tf rAe+GZĬvprvC4_uSI]tɷt|w]6kM1K_RpdA2sE'X`r,XX 'pI]^u\ .7$;*L2Pwssssssssss۴]jU$҅ـ*X\w[ AB]d46m,ko,fṉ"`rAYw k6,ȡi`d[>)Ċͧ?/ܶLG;h ȣ<#Ǝ_dlq?`0+kW_s^d ɰbƒƌ) ?\AO4D@j:.M%f^_ رm VGkmGn{Ҟ>l H,IāI6={uh:f+VbZJI="Rf+Z3{3h> 3O'6 XwEz x/r PS :Ԥ( ( 9Xh|%sNXZqÆ XcŊv x0zNsVB'Y86ΑҳX@b ZDT.i޼:ϳP-Rl--R̲;>Xcr.Bߪc@E:qE ˟&^oS }ci㏢msX2[k d`3F"p^&G0m6['$$`!9@c `9C0Wu'H5VG,W1cQ Іb$"Ck5fX,mi޼yV m7i+J2a"HѢER0[_$ MM}L|ﲾfΑCCC!"Ё%`9tnnnnnnnnnnnn̙-ZȪ¶Ua$`YXw0ْH.0(AP 60Ro ZnKME*~fa/Pr.v2ua*SxdNl&Q<1&o8El߭1timS$b@[cH@h$GY *&ZN4}va# afGr)&{!ň$җv"STe6V_|M:52e_`7777777777$`k#F0 7]ޚ.@0ZE0V]g˶ c+iZ!/l^bd#rjPj-N~ dZOs)^(q:;ΝkLW2wA otwa ~IpS&Nhd@c'0hH @v&ST0IR(r]ov;27L\+h!($"`3$XcKmgp|%bE!'p X͈R@@υZgEaR&>-ںHƺ>dbh7gfs,e[2om+9f[oe,z݀dj ڭmP׈|,ni;$f,򞵝 eφvsssssssssssssssss˥<VڵkQI1}HME܃4kq;P=--CI YI^YF+Y%6o[ E6_XH%eh>Xw9 zDTRع 2Y|)ШŠ#_} 6[.]A#bb8z#u%.W}][/~m{#ɔw+9%+Ykd$a& 8 uYMޫ~b>$Il$ 1 8) \\+viy-o!7:Դi F_TһZoK/!"7777777777777777777\f: b&۔b7cSYWÊ؎ԌBZxi.k pT_ 8XN.LX{b.PqlŔ *0\aL2 #nv<>qv\C m1ku#?#_|AuEGkd[. ]uUVpPbUs=ӀMhܸ1@kL}XzuL]1yߺ+Mfo lO  XH.4*QWPfg}\ʕ+/b9mR=PcnA6CϒGUm*P_k?~|FztPb>}=v ]څ`5nnnnnnnnnnnnnnnnnnnIرcMN W)#Дe}ifv8˔)tm ,Z-xeH"bXQGefpѺEW|'lx/=Y2^-Ñ9@nAWY¼4BT{8Jk,aw[I&s&ԍ$ 7[n Ĭt~ywhޯ"cwyIkJwۏGŢ60bw9S)2peI|W}ePc1?x3XWk Eb1إ^j`[:B%\2iX 1t++Vѱ#ؾo׮]4uT)r=*?zꩧGfͲ 涕͋>Ө[nK/M<W- +*%K~8X$Yw ZΝ[d "iF{W԰aCb&R]`VP!G]kѢœÄ {aŘ6,XdTlm\4nڵk}PWm)&هXl[~6B>cv {9>CbIູ`r…j&իWԥK_ PQD}'DYg٢W_5u?cnnnnnnnnn#M7|@,v W3룏> I0޽{4rHéH 77e$߷=MR@e )'P$ ,$N>ۿJ\}'H6O<#N:X^P 9)ȬMg=In$ [9z5Fa+8Hw0Cr (v뤔DC/;斛mժU֙|gF>}},L>V:@,`7etSڷooHK4X3+4dcd=/XYgr^Iy6lm (aT)bJ%5Vfm{b3l0v\Fb\˻iY_R=`u 5)4u}n lXK"}>!O9 믿,&`Z$m۶mD*}hCF˖-c*V@n:s1ube]u5ׯ_tw]_itч~5 pv{}W;777777777jĕgώFa1c=faa>"ůO>Ū*c鶊:bƪWXрÇ[̫Fbc.q*QjLM]T+x2777aɹO<f+Jσ^xB : nوطY~h.Lz[Xa4=ԶV;ϛoviV/334C$=[bE{cm5Xk{vs-3NꐭsMN,Hefzz(P}A4 bԔeddTPǷ7 3=UbLG`#(TDTѱhY" h`ë[%F+S-}1C( àALS i77777777]`"E`aK%"_wu~&S_Z*Yd ~FTݻwԱcG+CY\HaK5X8[!; F̬4o!(,YLWPirx{ Ѽ$yyA~lٲC }Gb6Ȓ3Zxnnn; .-ψkYa0F)@UϗLLAhZW뮳 \x %&ؖyQ뾱NY<71ͺz61f.G-6ml[МKM:*&} ߢsG)J?7=)A$˩XdI%=P7 uX~ΰ:몂c]1/Օik .0^:SRjH\i.\t4`a~u^Ɛ4f=!L0όvssssssssq | VaL!Y[3S 0- UlM#}>36&}QtFh6M:V@tڊW X Ű;'T ؃[LL5NbiRV)`6]@lC9lWpERɄЫ= 1Y'| a<DZ{tPг6gFL0yc!#. W23t]oـ0X&5u/ĂqA.ŲdZf͚EbZ2y,-Ϸt3%6f\6Lv`DnnnD7<f_GCeG4AM7N }:i[,UT,ƈ;VX-U:IԹ>h aP8I=jҥΏ(u鉙A8+ q,@9",[ze9 \=oF!I9@ G0$!Yuy7O5&Ql$yHz 1\I֚dڳ/&UeyJƉQ'E״txF܂6J2.3n^kdDk8vs%JT|UdzX7w lfѢEV dGD ŘG (!0־kVLAv1uEiAu`Kamfz(^if֨\ݯ@gXvLH0 u"l DZ 5Kt-6E miӦv%߇ꉤͲ!!3ڨ#O+F عXƖǀ uFw^zl pKXUÙ>o: :rWL+3xR_,$;dwm1˗/[wCxyϫĊzW1z 4i@|Šo2Ŕ=&@FR[Q|V b*=,nݺ=*S-ō5/u|?Λ3l_J50ԉ`, Unt. uW8pb4JχA\zFϚZ`#sεd6&Jj_]3=%eqB[=c>n)k# K0}Lil6_o-=}'N&zWϟ% y@<7yV[I=z˅] THz^IwzmKiI̷4^X6mnnFmR4Xq]-lv3c, 4͞}RI&EӧOcuɂȔn,頠ԀPM:D: h0X47x I߿SSNj׈:k]:9Z6Sp(VuY(U-*uVH4{ՎNo'G' :kPQ`p1; _0#N ܡWL5-)h0[c LVg Ͼ})y(3}xz۫cŶFbƸ !5`UhON*(3Y3\Ryr*j"" PuPٞm'Ol*r=Z}>tsK8hx~R9#رcm}Dm{X?#@JEp 5[c:#qUgUViYajf ˋ( --V,dSGw@6b+MZQ W@M zqvن<ѵpj {i Gwut-kj{fJ,V4ufs9M3iii &PG^+?wi*UҪժVIW^=ZiUVȣ*WV};9jUZ-M}[UyjV?R^E*S^75[4N;S40Cc4Zv E#o8>{ x}E zҒ-eƈAwĄRrMf\~eJ/=n@"ooҸQ6W\ޱKNkudSNlZkwmzvSukQBC+(*+yhRe+)-_dKZRrGpY)zJiGUl7,KyG]~٥UsdD}00`U9u;FC=9sZ' t'j>UǛ*ތ,&EЂ%CB`"H'([BGpVϸ:ML!AYF"=),emYGޮer |%92 f;~sl"p3m}hYa$.Vu{j%%R)tf tfԙ1@Lvfm#mfYV4[|z[]nZ[Gש((ZhWPZ;:xЁ/o?y.RXSP19xMfVz1o =QߠI8 fzc#Q!VB1@Yĉsob=s}Oe[>D8tٴP/Sy CBj5Bu&zz-_W1ըj5ΟZGo?\mp]զʕnJ[ rsssssw$=ds1kN&&m{#h0p#  |<)9݄4Ԅ[~7WA]u #T+kӐwiҴa/쨉X ط0 ]&Cƣ]lhki°h~|.]K D &쩮fk`J\,_7npC n5K$a ` pp`X0bIWb+W46 Жm4 :nDp897W Nm:h7`31NQYn̠shrnY-U" L0npS9֦WP6+TP\A cyuUR^T:ۋ _`L91~A i !nvAj4Aގ;Ojr#%vr4Ԋ˦)'r:ȩk?ٯηtlv~)? +L ?L>6=3}pFÏ |?}?N˨rdpͷt=~خg>d]N>0eڌ1]G|aaܕUaaŸEkô%ÌB,if)ׅI&.Xc37=dl onкmpjC P-zppPa}l]1TCBuqO g}a,}Nχ o|82 tbۀ,e$Q}I0oRo 2ו52ǵFG)>ٷ a$ƐQEAʶ?@]8`M #c5a`Idd.ǡ{M K^fnn;`rs LS! p.`̀[Lռچ" e{\X$>YA7g=ݤ N-f:onLM X˱hn<U2s p sCaWe^f^!1׀Z^"ch&E PR7]f::Nِ΄7E_[H~qw@uaO4ќ."M8E&ЋIu ."X#Ux]G$Yzk.IܮpCmji֬Ҧ`椽e_#mAۯ =7|1iq5Gk*TEֹ9ީKgФQN;TKD5PԴIv0t=َF, 16* uB'W1 i bYԌ}x*|uَ/N 3M_l^rdwqjYNVd]Xm}lC{Әe hol7j@܀FլE{nD94y{qnIKF0[n_e_UՁKtNlg`.ж(.4zi8?pэؘX ݃F iFu}mDѷt~$vGN@uTj I7ܠPwX\u UGs\НlH*:XS@Xi,֭U@d}RH@o\߽Sx_Uo'VhlH'w](pwr ̱D<"H7 KgڽZ8F?(--]+*=}{*}Դxp0+ ">ht2ȆkXV1|CT6m@&x&M*Nr3xc8aźƈtw^ʙGVZFXr;3=;&=cl-o323sX 01a,^ /K6! 08bwd#fW8 ȳz9㚜*K*%Cy kM6!f50+bhC6%Lf85 !BBrƭ[5Mnn2bLh|#s1anNu~@:|O7@bx\ *|K,f&Uq'wغWV[jץX\tBE9K.!,e0[E7L_uNeWBLq7s^_ @^/ r7^j6'9L39 #Rl UM'B_p T4Shu"݁`GѻaIsCXث@Ҷ9[@*CSЎ8;Nqcҝ.vp&؛]p#I6*>\_֢ܽEӴUQ/5I ˜Ih p{ժUQR3vs:fe\L,#m?QDדA37p\]Ā%==Ws+S&Rhtp),)+V),&l"_g.v* ^~sx<\{ӝj=$]@UBbll KKN8-rck%]~?WS wj6v:v^ູ9)3̖* +Vz'0eHsY>P,]:0A1AcNA Ank,%P,Z(j 믿^"Z$h&Rm-Q7KǰpeyEa- [Xt^2aֈ׮]Y(ƌ~uV2{EӘ-}@ZMJ x90Ktoa,I8nX/;F4CP)!Bnv;7Bm{VM@TYޏ'?ѹ|ݵM}6HmcӒ0facH C8ׂ'E<]" D*<pugk<@qj#!Z&yM̴x0XɒMM \Xv_SJ@[Y^7:!T:P2!B*ܵf(vaV 邓k /pwc0, dlY# :co휱vO][۬:nnn;` Wn Ifi#a m+i'l5,JiH1 VĤ @U^[ l h dېH.&arXlmFRFHq{Qòղa*nU= 0 Yn=(J׊A[3XD/}  ikYaFe*1=#@M08@l5BU7Mwy,<\;"K^y)tQFBŊM7Jwu?|a b.6zg|'^t9LtpIٖ-ͱ'L_bgqK$"/@/U44v\wpi4g T2hIsvK6J<Od*ux զMBaZ7Fl84w51Tkzý]{KUP^PJZ?Kǹ˶y&L $a&!%g].USiCLVH-]kD5d{`E*7nw(~y%γ`:%(A͛Q`nt.l.յeC3+1U2FKIF.x .XH&HΜ9 K@3!4{ʀ mu_ޥ^J%-)<)6|d˰A(vj >#PAM >g@p;ﺫeusnjrk&OtϚaem\܆u']^Za[+ Aop0ĸe<XK?5w9v9ɤbG ;oDEt\DA_1Z(.-`ϻa!d27ksGydJ9]L%b'5 ^Kc:۬ _u#[6̕mٵ12Q˶M9ׁd)La Sp0)D)]%X#xo%C*TUsv28-ŤKu?s 5^uxEM{{ =EM"жL^ wg{G͝~CLb$)-V 4 xFsPVO+K&\Kpi޽I`MQ.aG5B4-= /?hR`ȳ>Ѥ&cBjYLLQc `d$i+^+XMH;SG7@mR5]y,7}IW?qeKw+Wr_4oF5t[h~C֯5Լޟ߃+ǫ{c`opUWʕ+lewH ,obQHکaX~!@o> O1NE+2nߣD٤ͦu9PVE}5ZI}ykG%S +_宓{s'}vQAy>KkRn?B_ے Ɇ7a SB0)LǕ5%Wˬ aMZb/pA:.&#r__q:Oo Jar']чQ R+/_'u,!)bC!)-8&D 299 >&c,;[|h) `{XΥ2^U lvΕ&]2XЏk`<ʖ/W6~!'u]^mS͗/y 9:A'=8 5/ Ol5om?^X: _&qv> x1<${0C OJ9r(Hۍer}L׺lA ϻ-/4jw f5K! "^݋qV! sC{$w+cQÂַѨ`e\`6YCQۦMl{>^L4fa^F ebܡg5j_%u[*5QD-]k;\b09"שrN]rmg:M;_u}NrCwcgq3o7kll6 5omd0)L! n PVNLnV@'&74(R8el;V 1&a^AylM=z,Y\z-o,7\uчUwnxn7_ƹ E݃5ػ/ƷwO 07Ǚb<ØR@ _=F˂іS߀bŊ>G4a3W#Zd8e-m49g1;XĖKht[ ?d2MerĶ@Yjʞ ǩ_pL$KHnK7SU_Yf-x߬e V"k}h4|@6! S)La:%+~mO&5@)-g+Xk 76c+ I|2|X()G[jݟ^A˖E>t9U[ SN}p. 3 $Z7ak]2‚BM w,d`v ~ꩻrZO (V@Q(eQN*Xj?̞i6 ۶kS'T_4uݷ2\u;7jrw@}ٲ{LRsw:upۻ}Eu+{+-a ]vXۉ 5+;"̋JNn vN&^R;,"1Ȫଶx\607HUc ~ZeS^eӥKW[0Z۴0MJW36; dbSv eϳ[;_:Ul5XShR\^I庂 8yirf6~aA]J2]#} bC0)L! ӟ7!t!d13ND?$ ɗ! |ـ'&PP&H^k5w~y_v>QVuJߍ;L*}g,]RZ!}r$E+HAPdc`/JYa`:Ν햎&)h7WָQ]{>l.S fϐV{e\dGvɛ'P~Z֨jʍ-5­4mj[6M&P[Bޝ! spPjmз&niiv!zl}/sJ;٤IR8%K\|Pͳ|.9sf['h'z 3h\l"*UhpΫ5 =csW:+܊9a S0i;01v)ODMv+揽?ybbD0 c…cGN&^2iRo@X xN&ler xʆTg?+ P]PS 8L\9`8yO @Hw {XTб{aQA 0ƒ2CuXIAR;{<|N})yv}>(';LyZ0H\d[oրWIB@Kؠg|MZfƍ׶k[GO>68# vM7W_M ڲfqO7+mXsףi 6c S:(;fejKXfl()^ȟ0.C??gIq=}r_hAhiCQK ,^.sY"#2 ʷ; 6P\О\E-P_W\q\|e9_ÇRGADz+Xv+Y0@`fd/袨"TH@Yhsϝ&;Y,#s~A?zxI`ӛԻLx2f[~GmoDG {q Uԣ qo46M} ,Dž7! SYj19a͉5CTۋ-ol[t=3n _ P2cBbŤ?`?z=bRd6)808&@?'l(PԒxeUFL~w,e˵.0>Zhay_?&K$!)S.ORW2yc=dɱL(Qm(;n %Q.&\-Y6Д93{e@*eid_|zE==KPp,?,k6[9/6 ol\r7P+eWD˻ iG wwvjA԰[HaWॗS s]w:|/4jc%2elɚ+gNS~PW\ǹrŊW^{r[ݮCܞ7H&G:۶w۲{7O۲؜S2~~=>-eH)gܭ{VZ}DeZD?Q$py~99) -3[e(Siӳ*{ckijgiy+zLjURn2}V D>)Ug3trS +u٠,>WRm(h}/QRv7ecpssx@RZI_$[^ XJ`/2ozHT֭-K/WJ;ϥ60g\o:oIj'pn8`|0C 57_^x5~]={wqO^Sp0Ie5xQpB1*x1y;G R6$W&z-,Yr(]LCAsMYnt9=s{s*~7lO/6n_lT<߻}lv{hx|-{VD/n <ϭ~~4Q^egrpn y%E#!k#~qCObmönn,-JY 8pl_mզp~M9C9s4P(c#Z{]no v/\YضC3I,k,j)-B! n I=ZHoo/5 v2̎@9)_F9+ga:,Z ~:] v:8ͽ[ˮR~'f|FH߼+ pJV&qw|`qe`e y򭥟|X%4Vz:Nexk?<LG7zJ7lbfus1`d_?{.ݬn脅n:f*7GbK3W/f9 yΟl{Y@k"Ț}bWtcgqn6 M:ޟleʹ|I! SGH5+٩?FN6N3:ҸiopÔ\"H.ۑĸx+E-`2K1ʡ]h!n`,-+E8 ))L{6OR'TёpJϼKK"_G E*kT ]]v{QI*+6Rf&WRAEy}긶~(+ TޭHLR#q={,p^jf#¤+YM#ޫ/v?;_*ف(Rx٤Qi:]jU-7ͫWsuڵq@b] m*] ,ѵ|Rr-}С)jh,NwD޽bԽ@^lYPՎA`8b0o{VB@ӏv/Mg (eRt]F=].3vo+IQWNת}C{@g7zn҉n9nօvHMC;hX/M[qMZ5]æ56-[;f,*x4Wq5WA7uΨc|6mhWaWQUHjf=5 SrVM5(Xi4 5oSOejըL`rlפe$\#xN=~*(|QhF,2 CYpaj"V3ҷymjV TT7#ۭP={VmW? ] C 7*ZkyfY=@n' YX" .EL,v Ns TBgUoW眫.~j|rufQ?r+S. DdB!"EB2jWIbw |uEՀ>z̗3|Gԓl[:4/coYE7_u5s7rewe*wj4quch7mSs?nFv~cYzJ쵂~ոg]㓂S}oWU=콴B\q3|r]S3VF!`kZ)>`"zo'.JP7ܳ{1lvnOFN[rs+Akcޯll> C0鯜үv85Nc5wЂtpÔdbQy<"8vYpD][/Of5&T}N 9b']MjdRE|L$}fN4 giQIL>gEe$dRk$| "j۶SQ3L"Q{Ftܗ5bu/j?Q2)R.%J\DT+4ꟑkLNCr7^%o}~uT ʫʊXx:~p G}z̼~'3(@܇/>z#<7Y{PoV#Ο(vINmX^]ӵY`k ]ecz: Z_K2ֵfۭIcS p 6mAY׽i)e N797@m恻q+Ϲg&} :vo7>+_ʽ.㮻ZK4yuYtwy{K䕺M{?20֮2Oe 6kUǵص5kV[kf.kj_L,I3Z:vnʩ*AUi=/^}ig񮹞` |}yZ3e= B qZI>F gϔ]eB qڳZ.pi{>D^@P %2@5̢RtT?pj iP6>Dm"BoՂ;+W8"ZL;=I"_ &椰u:-NjW\N]WHN~rѧ7m#ϊfxWv-糐1UP!'(c[`.6O7eyiyvEg:.tȔ@C`+jOn^u\&/>yޠ_eשlh`"GL]v x-[fSWQ (]`aXT7bfO[whe}Mp2?=+q)sym=~gu{N:ްb+ , Z\~>(a SDB\ļ`+,w-G0v*D+Yf MϸWc?v2.eLw(8q'0,p #Mܗ_ӟ1.Oba^Ev`)bMȌy7`#X&y.&&ePO13Cj%\>qO<(.줒NIn.nB5O1AfB*xEsyJNL4}L6ޤ#9LR:|긟^KDN]Mtm) ńhImz9 &Dzm1Lj5,*3G3jP?P=!>+Qd!,q{=dQ{:@\(OP5;)I4Wx. #JGD3M@ z?0Bu> ƚEEKn9W{\y|;Rg[::9u"yN=ֽתފ뻾Qy9Ԙ~eFe@@ p 7h:Z4zءSOpi*nވapMpW,u{tcϚv(8׊ۃX _la c''gn+q+d`y۰.{1wm1(-?=[e[b0tܗ;`{ m#2^{`օi+ܬɑݟ3rO<%3Bӆ lRT%;Gg#e/y V{lgd]I( V(eϋW<}N@R# 0,2`:sTÿ럢8MY~˛8cpW^%Zf T\M~Am:pA_=,D1Py9mܡjQ5kfYJmdIߙB"'%WƌqS|k3URRJ * q ͣ~$g,lȥ64کvy)`rkGeW0h ോ8Y0̀~KEN-P>>ݮ> p(Gj[~çiK$Obvws\'j .2YQr>+Q@j@,plLZɮV._r+צ׮`.rAJ  -)׉@گb@ ݢs?;Q/˵P#fҔܧc͉navmT^K>Uhb{D{Ի}B0řgøJDd بpQQb-dx\X0/^b̈= ֲ93kG,s+ _eN B8©7/sme-P;REq<) 1eq̟s%Dy5j>mb{14sj lx6Y˳klEԯ_?7A' dX;;)NcA=yH^_ $МGPĔa khle+ &H5iYa%(UN5;da,Z,8V8ܣ=EZs 1WeAd 4}̫yD CV:K`r* cep24Xi"}3[x'@1$1 X^Y4\,xsr_4d.eW}Ԣ{I*ܔ:&MGJ#WU:XSvοUT\' 1xcS/qs}k?[H51" j'{qe˸۵5/O6*]Z kӪ.FwX=o"%E=;n/]nM'k^qwuԈ\ҧdzONp.)3{"h=ſ ĉݮN4#mA\Ӏ{tw!8,4؅ȶy)f:}029eW\qŝ=~@m-RƝWkŀA6}mdVx7oW>)6@*m?;hS.mh/:. s*WvG?8h}uPGr,<4O)ǖ~O {yv-]eWw|v4ZA[k]p-?,jL]gyWJcf2P5j  ne$XhS\aaOv4/ax $'~|γ7L!}w= 'QC-Ae;os F<2w&46H5 ="ؙI1WvYh$5`ŀ/ * ^ >W%q{/g3L (+夼X&@ILi|:=dVfFqlecྜ7~P'')y+QW=G)< <+Ѐ{Խ0uKF6f2oNaeߘ o:wJ`M p)0ZZ\wf3n>͗zgU''/@ܶwe`y)j^@ѻ/`PN7 ֫k-}}gw>fH/YiRy.2_,x3+++,`2͓j\n[xIj#IpmJ ,eGYD=='v'>y Wc%^o/+A2Awm:K0B KQLsjՂYW,Wb0 븃@\mr h#l0ڪnPWi@:^EkV1hU^bdJ2e`"v񑥍U[{hcG>#d~v/voz0nm9*mW]fH&pwʪ,`]@\ 0m\ks_Ѣ_Ymzs:nuC% xMi6YH*l_?<EcfrZJ9L%۲K:(yUQO[\%Į׼)j;"(:es0-7MZ=<3jqJ0)L<&ϾRе(TsnoՇ WZv̵8+;cYҔ~Jcbz"jj: ٚa,L_K!g-"8XBc{:1 uफ़>|^"I[t**> !^~] Sݖ*-ls6p4>Hc%@_A,ڑV2 FP9] fJA梂E8Ocu3a@=8c)cM$bѸXMqa!z ^F1*iB?# ƊOx9;" l8ǜ;8|p:h#J?y@ `07\VhؙIŢW*""&9c|!Y oZDm?u GF%IWOcrX \>c®aU * U1+<zVu,^3VUAgJ9E%+~*LقF̤g&I\Յ\N2e2usU [S|Drq,P?7?mI=W^aq|aC_.6 L<>M}۳YS2gnrl2{nju*~5ݿsEw^ l͢`xz+T}{ڶScWf9EO=;<_CU7b^}Uv-YD}?QcBx+9+6!Lh v cg`LJFߜZ{vH|ʋ_ 4%UR)Hmu`LDC&,0Њv` nSCu,t'[@ C!ۦ S6h=U[CJZ X3M9@7ou(C0)L61a#ހ/hn<ĕiin8 |(cES(/sVX#XWOc*n-#Т w8qB0$L -j8QqλLZq V>kk\EUzG3Xr h:}5.#9-N.ӮF鲱& 1&jRI#F f;_s>^gs 0Pjc[sGu#^e(]?*_ؐƽk>z`ι?ʟ3nWvlj+[ /Wvݬ`6ؾX  a6&._ k4親y)&2 B)&RFXa?Lhjb9`2'`!P % :L֙]kBIr!eM@seFɫr2wlfG,I)SU Ϣ2{Qr/&juM9ʹ}Pny?;ept2;g1 &i0=Iٓp9Kf0l;ؽ:5o>iQةY*E} ˱@DekgB쬅pd EKiMZV\"\矶@mߐԺ@ܸj;ʫp~{T/{ν {.__~,2 8KղS: YJ-/CmzGBj .^1hc5'^W>3lbLa9G[3Rd }Ĥ dmg(\iE<%8p7z&sYSz<{-Z=ҧxжv/pk{v;2Mbal_]-PXi4JVv^)7 dv7:A5,xʢ č*\Z3@jRB @F*inz7La SN}B\ ki<3UsxR [)evWv` KjǕ@P#YBq0N..8u^wy7K!,m9Hssmߣ:Z﫼`ǿEp n9 W N!݀Ƭ3\>A@a*мNϵqWPGpOB R=R.]R/KW#J1ʏࡠ7_ǭε`CenbGqA< ~'+V;0W0nC.m\P{T+TU>[nbI(evPϭlËAp) p*U9&Lw؆L^49;F )]M ju$d' Ol۠P2 grՒUǓ+&uXc'Hm$5)3>CELg~gdVNX2jDn<_Z0y 滹po0@,޸r~ī^v^_M  oخF٘`f;Q SnakШMO>8ͽ[vg6 ~M\g*ߺ]Av͙+v7La SNSbX9c\YLE냚 FuZ\gW'_܇vQ$v;d'fb/ikڎNlJԸ)`iVVau=vhx"Ү .$yqAf^u|&Tq3HU̪ŪbW9~'s+%] D;YY|;.B]/! Sp2IgM*i9SQz?U5PsL5P3VcIk':PG5A}g~K'yf(Rc>]4GgjhƳn{ VL^05V1A&ł hN<+&k>w:KҩwEz*Aٚ9A=HacX &^i<`{pl"A1϶VkbށPkeXeU=eij/бhLj O.aӌiDúw.&U* ܪVh&Z֭N \Ct6ޭZgu)Z:~tpP)'(yh+Z)xrf: *E+|:KVOP"2^ew,d6uoZ٪ktO<^|9{k?/ `({JY 7M@7r?6u=9rweTf.R`vK]n|\o3+dh۹8y ϐu~"Sq{GsHJ1d>e` 2K,haO8-xv PqX O3Xqܓ9x &GYW )y Rnp#FTM:|4yї {駟v9ƥsS6?glJR]#w'.Pn* @E=! SpᏂQDŽSjD0Rn@dXԧĉ /YI0U2QgʵXʱdnԧx"4̺yl`bxrk$eKRKtLiP snf{5.|Q)k,&1YęY:{DxN|_rtyk>;e(,p 6N€<,[ p=mS]sA `;kg 9^g:v:mh7u $7`T3W7La SNgb>nq5!pv+q*uy2WBʘ';J.X~ ]V Yg1 Tc̗u,Kk ;.8X d@' R׎`Lʄul <p}6cVWyO .S}D3M5ƺdG- f9TɄ'q RQEI)pZ.H0@<0*O^xcrY$ *v=v5n-狀xOc'Ǔpk&A.`@"\7AGZA|LH`@s`! kIwwXϾgTNrʤcn9+fU%Xb-VO6Nj^=KN.>"=vx.=,P :Dϱ P%k E&j}Q}4[??J;]*mj*Tj<͔} \d-A!+y=fKf鯼Ty_|),F_8lLabρ&`iL8I nD)(0u-=^QҚ ^ `_)넗~Ϧ器K1*2b%KM|yfx{_7\ԭ#p3S3MB+f3>l̀-:ejt~`p<9kQfp3mŕTה! Sӟcjd؁tZ 76p0`G(;r /+^>ob\֬Y%`221gy`ax X<2v6X $q k.lDRRnbG\oE]/I\[kHQ`[lEK46XX@2fAsw%s :v鹷lL +qXzʦ53nƂ=d8/^/rWa%r@_1n±<'u"\{j`zEy(P鳾J=1\) Q2wg,L! WLi=R~Mҧ{E&eh prC'@XoWgB5u-9:[6hB\=fT -S$F)yiLjgJv ~n ֒v,%B/b pU35y3_]ˀ˱>eX3?O܍ p NdM;vh/eZl_ pӗms2 7{bB`_\iܜPcm!!sb@2Է! Sӟ9,mQ`eFܫ+4g3og'n-~KqJvq!b"Cy[j<*b8\ 8#땫5^]!%yԻ 1QIIM#"b5jIk,11WB[@wc9=e mf]a~qz@c( l\cbYc(fq,zc?V@:E[bEE˟??[}2`;cul= hEխ# P}<7L!mw=r遍@\.Q&Wc>E^% )5 i}b a|[xVM.Q %7Q*svLj5݆ d]S?Yž+ =uOeOz@c&4ϸʕAϻ}U/}W}:W3K=i f媷D#Y9U/BYL|[>~Aڤ60K+=Hytm, ^<{윔l(\k}9>Φ pW똑dʟ_V, " }/U&L֭0)nx@MPh׹ygK3,FFF(O9s(8IU,^{ /^=4!@۪R=(UŎK.N?.+@ 7^cW_{ɽ/]c&2/6Ȧh>P8V}2 lwilcMɂ߮6)ے0m{3 MmP$ rQѲH6:0=~!~vP0oǓ XfHL DV?2.t\YXņBY ߞV;wTZ{^maSʺ'mQ2)9Z 720 X]P߶Ǡ4`\nFGеC j`͇-AK땷pw?Z( gܧS n0Ĝ-b%Kd!_| *m5Wsp `xeBglE U.q9Z;\GO?y/@[b)dϞ=BWjl QΏ=| zBMWXM 2BY qb.!_R73 蜨sk sOYorWH@& fhwИ٦N\& D8r7A~k{psY9*nfvXM7La SN"h635 O'^8- oyt`5-84^sNsSqӵ賯4y^Ae2N;}d cGxKN3f k;#pa9-{8 unFepM 溈ܩw1{BT?'(qkIqK@-Ob K ƽ~2 x&P`/Fs`ѸxS8p ۈ,,h|xwԛcuT6g)q8Ww?3;wC 0\ !-"46j9ϼtZ] z"1Q!FYYU1) n!3b krI0I L9_ef`*s5\Ϗv-קg)e5n H`- (LQ|Έ0=۝KRb3=Vl n3W]of *91vOvyFgmv;[ FVO5L\reWmZ*36͘:6 Ù %K00WI^.MMU?RgKBEg3]ѥRwiK[ ~t/ۮW b0D>(J PJ  Dc4mYG˾30k=ۗ[|7j_ǿGh*Va9>6w7&PB"^ٻlP-7ԭ<,$f Pu5iY[Vmϛ2N{?|܋-P3W8O. i@^†W_}uJVRJ.M{`9j|>/g9~Ae{ULK܈E4aa mv8P-~v @Yv-p,,bm#Үi옠][a$CϚkRֹ)>I\S> {: J`j,L bP Tߏ7vQ߮/.z``dX%@k + lbE( IDhٓԪ>6rYes#A@^!]R,#~9W1^dn6u{M%ۀ.;ƀ$\`|/8\6"hg kWiw{~$A?v! Sp2k& DlɧG8|4?j]3~t0lPaX=R42(B5IN2$m[l4} HCDɮ&\GKe/@@*ZD\&L)& dPy B\}=hB0;.໠b33fU u+@05>9wAQڽxkl x#[,M5kbݛ̹5'rNF#Lдg{tu979/Sp7XvL/?H dm/ O HI.FJ=/nL67~ːjwɥC>/M*ٳk2\e磎}5uk N~/[C'eF Ej;|4+ܺg]jP/ K[~ɩoeMMnIXu?s,`9wre Q۬qe^FKc&;lѪ@^lJ ۰@w5xu4WPQqFW[`#+l˕+) t]N> >n ^R{ "fU_|n'Y6Ċ&)K_ -x2eV@0=ڦ~WFjjyA:! PaGIj["00lSqY5aˢ{GM27*ߦHCJ, P1M]:N_}\5)_EtL 54̱3o~7GgfQ][t YZ.@(7N6x8{,j4m#mT' n0|`nvPlzwg5@,!V?kLq- ߜ{". u c)>5f^,rj%E=BKA:ҏnx;2U担;/)d=.=كZ5s?b;q% K-1.&K[kI\K!!u<4ŏVcm-E]tTUwoJMߏ*ĞUÖ/Hm]+*Sܟ{${ R7L!Mhș*`c" c~Idu/I~:CL\`BHϘ\s]ݳ "&4'\5V s>Lp=:l4fՔL&(tt:GZRkPG<;=6uM;A2Q^'Ndɀ\2`Zl޴[5q|~ۭyͫz،l[U@j۱luvJJSKCyoiM& (l![6ٛQYq}=1x|<}~=0ʾb~f,Wlb}R mr6l`ݪ~=;FF^Kp$Z?/-O^B' -PIw?h,X|xG૸-'U viaPdHrԫ;o?4_W+U.Y H0nZVR6nV7v,ɋ)` mܔAw-7ǀ@T|h^xYzvͯ/[Mm;[ݒ a~wB\/lR) @Nl B1*Yl 6 UET]NJp=͙սqkc }{ 5TS$?:yIIo뀒cXl NpHRX!:X,fM-8:ͬH6 RڟI=6W^y&P1}ș$Xv*ݨlXm&m mN`2S ߚ$M(d(?Rmx©6Ml P ]X jWk_3@&@emz؟fX%4|j[I {Mo bbB5 eOGvNw]+P&sI>벙6 NYjʱ>n+%/ɎBjعp=<<<4aF(6NФY!!!~?F 6) ϾZ]\ZVr|nd2!UyKM|<`'%8 B!!q=JR`hZO hqߨ߷!ejdJ}]EKh2O='Q/pI\JIZÛl :}yI2 V&7J`.u&+qH7o,#˯p%9P~.,2(vShOb.jR_lp| w؎~У~w]c7B[o#'=H>/mOg>-Psw{ans u(ZL_\QѸqx?gKeE ۺ3XGr \~GlgsߨM00{Y @&iwSy땗Bzo-cz|c sCQ-׌J Úy eߞ~)XjߌŮOPx[caQcjfUx6Կ@3.bv԰n- >现>,IeE߄=ڄOx74yԫX6YVKJ-~;di<=o,j䦐2zܑ;EW ׵gIV``$>ʗ Mci:[7:@NX9Aru$.X,ա.ۙTG󟫎uh'%AAmԱDm`?)Ha6t#So_%NNhI,IIږ$b/ l&KR`":M>uo& 6~  6 :@}ij-,Z|ZgHxM ox4elG}3'$ }ﳮiRNˆU`-JdGl#mr 鯅gخQuAqgAZwSfwpoއْvMmo ̕s8Rڞ%壭W1>;+Jfu:Ǫ4m2)OZᛪWN@}\/9;P)u7W\6!OB5VJ) u(_l4Q `V61ػ)~{0 Mopo)5oG`l"::/9?}8AZL^י?(zݠ]ҡRrmuk{DuH:~3y.Ǡ9pX!:`7iBM:^ &uvs³{J(QhNKX 7>žoࡱ} МcHd,+{x8M3.Xw'/'X'+K)J R#4VR,%%]z TUY *U\@E@qP1SY&eG&KD9&0_:*5 b׀kj 5RAu1GM&(vР\(s?d=[gP\_\*ϔ}h l3I_t_9tYA2AԌ|$̦gv&khi}/v'p}+}Naٸ139%m8鏕g2;{ȠMJj—G%FϘϚ6Αadyrߓ*tn2|ؠ5v\tx=# 9L+M%[W5gMIf 2+QG4EV|YOw{JS1C~\_CG2t7sti#iShyV$/xg/Ү Ҷd^B}ƒmpS~@j0Hgj*mCMtwnc ۂߍkvI8O*XXˆi˶Pr?G6m?f9Ͷ;y^l7j/[h5 0-Y۶z\4Y$ ?A? *ex*7@+{Ht7-q.\$My,~qXF#4U(ch)D^Yxgx~^W_!Ey(ҿX"6`I# !vsԽO|uF{6 {tMTTor=zsmYܬߔmN `[ }&3nb7?|'؇n*uNPTC*ƭT$6xY$I?\42~g0v̪$VTS9RS`=-۱דS#@-KX9_VJkd q/5Ǻ*1OaR[ŹR *@;τtb$)Z(9X$F@eJrlg6l{BQm%*ֱҕӮ!Cuc`@r5T (6(ζ0'/޽l#{hjO,pӵׄ ='䐷<@-d4EmO /P [*{ B[ڇ(vX.l;צ$[I& \g+-qD_qןckӾhP/QQR 5/6IH=O]LH}DF^)ա(cK5'>m!p?v R0u 0Շ6.'U=)%(@wZRȘ T)Ղ+ՙ_E uPFx6:u",$P?Xcu]'X·O4Y@X66өS ".s.+R[x k*u,$v;ʦWb]~a R&~;%9P>O=V1y>UFZzv5g j- yN5v!6'7li'n#Jaߴ1,CT:t9&q!agHL[7\/wq7JWi7xLJNX(h2- {ݢ}qpLe`-2apq/Pˤ6Y@$q}s~L;J1n&)x~!ϙb%IjDm-OUTL XJ)mL'ӹN@&σ}4s~^/Ol^k@qT~=: h:0d.ROyRyVmR$ܬt7o#XGb-y'kK^0~zBhj߷:=~!u zUV+}y$qx39ͧ8W (cf,GIgLg@`N9m ^eA;H׋\V&}kD4Ib>K6}2K0&d`vk8"I&pc +G  ޻ EG9Hyq\'ϝsrn &3qhk㹳?}zIP"c;Ẵ ͠<ܓIvJ?QN)tcj RBz("kw4{lS yɢ>W=Ή0%ǡMmYԮR9&+ٰ &տ|`JI =<<:E+IFh^KK,kf荝)j8ipZ 8.jۿsĵh`WL"mfƀ0fxxX<pM{_@0oe[TN' ]2{#3fGCҥ¿+.#L;'퀸<#\wR*?jT 4{+|0W0k`6{yMP{$sݫ{O@|^`+%X(pYjB S,B9k<;'V\HIkyy^!f2,Q cqec 5+W3 ѲkM4|=[M= Y856/ȺZk+uJԶR $kû7j05y "b@)P_MU5whSr8 3Vh`{YϖxK}똱53p8sWidZJ)o7SBW/ )&Y}dDǮN2pwY8e fQf1I ݙ$~c -'%!(X5 ^F6O D1Aql4;wm"gOGQ.4 L3iYB_ /H%eBۊ"4ٲ89 -[d#g1wÀ*XhV+Z(^%"MwWq P%|d/&I#Qe"L5k1`f>':4p⿂,Km3 T`5ƳoePn.<\(e?jY/ZJ ӊ;O%j*Qq, -ɫuko)ZdNڼĬ =Y -Y"ru Y7o8AA({ 7%zTV/TyT?nvH葦wfPi͒!=Mߩ8wi3 *iWF iy /7-XL\̍U_eEw$b߀`^1]s%ϴ-Dmw ,?e?8Ѿc%p;^{u<|y-x`S!*t737|?uYǵaƲ/ an2i0QOH^^zxxW4Zp%$*H }A ,dn˰e]$Lh,H,ca m\T$neoTUG#2ڋ/،y XUCf6$IUKE%D3 [j $IHbKF] E4cXD*N}km_5LHuD;%{<sKu9g[n-T._|_V%I Z$٬i%5낔& ;odzqg_Y;2ȄVQ6!g&6~=<ee^&Q29[BfD;WZQHȫ)vj6s$zw3ޭ%jÏ82dqJˮ 7V2xˡNwCO:O; 1# Ā.`;u& ɋև ~]#ػڔ5K/p=<<~(p%Y\*B!+" ǮCJc,W>{ܿaB)%c9 ,ƀX230?gYrXu>C5(7U6 ]E2Fu-YYz^Yz仓U*뵻yF3SuO:jQPHeY"5AAU_"sRY7O߀k1oZ|Ҹ%٭y:|]˂M?^zl=:>}M>5&s&j2AH{ k"E?pT]Iwq-PVv0@N G&EԍԦ֠ MnWo2j>W´4^ls 㳝R[jC;, / X4ؓυZ5 4lMh}H5tsWI(|Q|,ko|g6r_&8;^^g.pSvǪ>(4}Bƛw ?̝Ŀ;y!V)uoDmzzxxx8p/X^Q,1:p $cd/X#X(`m (>1%o \p.?V605%9zѢE|5-(zR+umWPvZ29\Z 'MԱW, 2BY[, d/:(n>VI.p\LΖ|_2LTU$ǑTR(YBC2`vs|P"?YwCX^z1o޼h„ ٳmBOv}VDZa3>jnzQ\\7pK|lM'xMlxԌ_BQB'F'}|vuGiN?3=^9cé.$\uݍH1\5ԬzxC/ _fgxRomq]o׼ɻDmn\`ܸqŷ,|ԨQ3KnT?!iJ T?$rHbaP~}Nvxƚe3 Ï_hMj4o=[^|o,, #.HS%jK-QK޽p=<<:\%'+(Q05BY+OK)^?dn:C|=MCOuj_Kj&PldHR\V27b XJfЖ,WGڵʧжª`\/e ]/kV`87ӗl&:zƯffi~_6 w?o*un8DmX6<~96k89܂7J[.Aˆ-!Q܌?Kp=<<<<22Ȗ4te Gђ|nQlbXԲ'NaX0ã PM/SOrʑR%MyH<}V#8󑔥jD?ɮ`] 낌'l -PR?_ձ~u{_}YlnնG fqJf Jj4ZV{0(-l@|gc;س4YcXuڳ33Y/^C"E *;%pMyɢg! +X7/,X69K& )[\u|5@[)^r:vF7nPށڠ)`@*6sHpD`{K6kEAC7 k5ۅ;K W_Dm]8E}vFqdYO&P\ \qM%>rxI޷Z >ۀ aavM9l=Q[a=ڋJ~۷XQ:V`,fϞ ~妀depl/k: hE6\7N8GAs9U PNQra6 6fMTS٨et d% 2e`VU #vG ʎWpYTx.seAi]Gm}g.?g^OV &~'Zڳ]_.pww_r y!̙寔?Jw~ 6.% 6^|Ʌ?ҞVxw mR{ʅkF>WO 2wEW alaMYi?N mK#9?^3ls Sr g;l2zo^b:wV4+]*%WPeK{yw9:|J>gۮGyĬd5PB w+3{J[⏒wUR$p/YLZ_wH~>]lH}I"9VSiu ,Sr p(UDxBPFP畗Cfo= oj} >>S>?}je*_^;NzW{SjOvXa^}رcoСC^Qǎ~ٳ}wIױꫯ~P4e˖Ev[3h ’cxmEZԭ[7;dyH駟Zn,) ֬Yc$1o<;̙3#FDUum--580bT|讻όD$XVp|6-2H{dr$u}ƌv ZJKGgkak-V OhذaVE39ogPJ`;wlT1[%Qɒ%.]* ^x٧lٲpr`W?cPPKBf͚?z1)Q~;H `q7LDيkN/pYA < 냠J'%{GûJ/زI&/j\<"ͣ9k5b*ft-v$;K_x-cPʇy~a!p%A/P)V?/]/a_{}ewcIu_+W2T0cZ)˖R&yj?ϣd7ިRnʔy]→ԟPPlݠILΓK3d|{C 8o-2\{>b 4(6|Rd<$fſ_ :V- )smʃ'38ݨQ#Kˠ"Ap$b s=<<<ʵC"ƺ~QÆ fruP y&81#n -P5oܸsq,1 KUv<'Pwx{e}xUic~؊n9I>ըQr$vO>}or6,IN[LxQFٵr ON˓,qm,?#ΪVrYN2%tj~t$=]/xT!VC*UBTIUhr{fmTU^2$UQȗf%Y*XǍuXp-^vSQK籋XGڊXX[^2h ֖=1J|RXY]6hf $e~V[٠WVV!y1R9#Lϔ% \_y%um!\4־?/L'lY4!b`bAe[&`BA<~ C()i~.3XGإ/~(gTYcƌ>1>Muܝ)>ڶmkT ڨb1 JP믿G@X^CH$9p@ } kV`?նDn]pNyr=k(c+Vh av8˽ -רYeY>gl3Qso+W}i,X``%9p=,I]Mt 1H v$&=] ɒu΁ID5@:j`>[Q&lOOF̭YlͫJu'K Y#:h av]-JY:[ yVRNP6.Tx}TKREPA_qԩgP`G 3t5IKPyP^~ݓ1`YlSQPSQAR9Saufd~ҥ5N e1ȟ.tұ%3<9^BòTB(x6WWAnw^(VY$e}rT?T{ʔ)5~D} +VP" ꣖S_s1K}쟮~T{HVxEkp^]clв׵ƭ,z[+Q[#K;榟r׌ڈ{xxxx8sHe,vI' < TfV>zIS~ڪ]s,[`3VKG=v]+^EijrKƾGȋ&m+XXe-SOB ?Lim:$Q;[T[fja7pVp R"/ m$qL]6(3>|͡dg)Fm-VJZA@AޡRN U3J"-Tn]t?vMT~$Oc#r|fĘY5'V3sD a6ʛe,3`` A 1C qYR1|/^_1`d+Fh >Y RlJ6R0bۨw.`P~˗/ z-a'& 5@/$@Q o Doi](=Tjڡ k'_$G/tOAթoԯ{Ǟζ67#B́`Kfhk%\oS[8ȕIWN@G( on=5i<=M};otz/vqV8GGbJ G[Y"k d( !s`8x 3O-MP,hR2xmQeN#ƲL|/6 ~,er1o}LEőگǯui4#ڷv_\`E[JmդgM^˽{xxZhfw l7N*0*nRe^w?{An?Ht<gcUY <Jcj:eW3YQEp鬳=Klz ul݃qta` Bxxxxxxxxx=O&Yj;d҇Od=jkTXk\2QϠ;R*++XIpC>%6\xGZ!f~*J.-5Lы.ʲY4^4 CgaC{S4IKR3={30aAQ&$)ɥV3q`b FǥYu*lUMX5V kR/_h*[><+%]VmJZA+Eo M <x+J%U{ܼ+9em?ڌ(5!1u_9`/?uf%J&&~E]&+OI%0d{Qrɥ2)ЉE{*;:_SIc4o3- ,i?.=$[fcRLx:K8uI Kdg(P,3E6D/\ " X!`~oLիW!(\/P?:+rKS=M}7շ˲V޸ zxxx؛.u>6 ]Eի:|O~z/Xa%IgAI-%˞=Y&~= V K*FXx~=Q 1sUޓrM.6 /2D)Lîaj lbFkSUlmvd}Nb:yA1 k=lmNNٳg!YRPVu-F1=$u?V_:N,hc $l(H$؛.@%OQ]\La`L`NBhzv 0fґ5Nš|vxep Y&2[uc;<+V3W\{:.1!H ,Y+cڒ LdRaHi'ؓv?\۾l#`jx 4y۳zcly'{Ze(hȢPe td)L{xxxxxxxx3!-Sd \x fLfr$])MA:+={3ΥUZHTZvHDg˽.t5ObU]X1B+Y2hb& N=O,иVX|D W}15Ŵ1 -kv3@-fQ+e;>%5<בNDl@b*_ 3aQ+˅Z=}1m\2[ )u_l+hkjsI,qM_x-Iޅl /b :Y+XC86}RV f=dWcfdg{`pFR?$T\i "Mw 2Z˺`va-=QHKgl#}ޛ\ն¯V5+w TG,ʔ)k . wkxY>f:]Ju54!q/lce'MQlJz8dE)@U$A32RpYT.`^f, R.'ɟ5@MJ6&h9CVUmy<*qY{)%v. J jpQb@d'wpvO:Z)_KTۜmF.9Qp%PA,' $-JҥKc e'pd;iJOAXE>`$X [`ܽ'6 (GbOGפżDd'Ǐc%H#X}IvJIMcuupOaX(mΜ9'9;]U'$RQ|ӦM3c+$ T~ANH, '?9Q .Yr`sMq) W[Zbw@j &;&JH uuQ&3:] /.YPx>G)֭[۱WVaπE;w6x~WG5kִsm<8DwcRtiG?co}1pIHؠA{O 5'UMA *@7|sf D -4e?^O r6jc&!Eot7$!<ǐgnI4]׭5j԰d`cFA[cU R+t_~&[}b`x1|ȕ+W L Ix_y]}-fuQ\rE xu.sXܔ<#2f;~x5RJ:6JALag M1qRw:$ۧ)/e3$iVċ^zI*PY(,m۶K LyO]ŭZq^ /0[ɓ']#ME$aF>P$cmV)JI<#W0+-WpU?'ɘ`Y/`+"VJ V5`]Գ3]Z]!Rf(o2Ճ~{3>}zԮ]hʔ)ssi#sHƉRNmo<<J\2Qbl =03QDk`lk{t>1<ڤ]?k֬oiצ㾊2'iRv`-({qƍl#VZuwܹf *AǨ#x (&Io=4뮻qO*?c6ԺxH` q, :v=e<@OJ^.6f;zhS=Vyn@U̙3miӢ>_mGRF5ɯ{ 06(XP /PG5PdД$_X7W;*=zp-AR3 :ԀTVcS6"[ j]Z50ЕA<m<&E.s.|v`A1&8Bl{ pF617o2\Y`i?-GymQ fr,`k $WԃeA`Z)R*EoUAٵPB2O8YjImڴ1xBB6, V~)\SnݢGÇ{u;(t sJfa^P7m+{۷oԲeK[$O0!C.]ص%PcU6cǎHWV 6r(i~`;xr\溸u ww@,p&۟iYd J*fP`,AJB0zؙ`cŊ݇9,{s5 ~)̼p,_|}ؖʉfA$z)/^2a2~' X#^%m* (3Pmo4k˞(x?/Q}{˞(x:߁±gϞpwc-KEӲ;wqwܸY8۱'ʱ7lNT;x ǣy< i{{9׋g 3<^3A!P#_J=ӹX-)[:#d@DFGٲ/^2a?/]£A/ R҉'|X~_GeO,di?/c޻-[C/^2|CEXgp8"L{%>m: I/>:$xxx`hiPqlj[̝<x0Q5jժe&̼QPro`` ~ٳ O.%. -Cmdiٌ%v#/V-\Oޱ0\vopiV~x& Vqe;i#<@PB?J뿚IL Y㏛چ`3 jAQlJHAcq]weSR`?< ۠,V2eضW:Y\'pT s151/_޶[s] ȽpasoR~~u׭wu^dfwpU.ۘ [ VBN $`G[vYgy=ӰTRQ͚5 ƪ%JUT(mH/?'ʖ-k@V^\t<:`,j_:`A,ۃGf{kifNB =OK 6,`qL ׼V(:gmȑ#<{TBWd/L7pC=%s @=KC=T͋*=Qhy u?/YT/W˖- 0{%ͯW `YHg^ni{C7c* AϢ6KfYUTuG/S?/H$SSSsb_~P폽xɴڄ=U6rJ{3k,NtV2Xm`C*kRU,'X\$=MMK .E9smx-pV@^45` x'pBt饗ڱ¨p/Odxms`;4Uy/V__*؛e'\h`%Wwjs R̞=?|g$xI'y晃;GQŋL)|s%uHNFKf;VW=y1tShnlC沄 Z%*AM}M΅<#x|;#,h߳WLY_Bo~%J޿' XW*9ȑ#3g:w*5_tb)Q1HI vGP9WJI3RtT#TNA0p ܺ pQ&;@VuZFM^ ܷ%{K,9G ~ɂ ^+*/%Oછ ׺C{ 2دbD)tc=XԵD2bDCLꨏu} d C7:+ce/Ha"V}<3aZ.;&#Wul,Y+3)ȿЏ<M~3X$P\kSN9&5f}y/pn|lE R}blƔLw1N^"f~[P. BsxɴҨDnM \f`Pj$X0k/R+y_UA V 5p?MUR@n@R- 3)s~rwղeˎDe+˂ VUyԠp8T rAf0Qs ~y!~}%{z g}յh{ΥK23|YC7رU^쮂B… \}O' ҵkW\?1++ %Ś8Ez]i(Yy7-6`:PClܻ;ew،G\kMĬgeW4|w; T|TLd&7*6Ț hP6C׼X#hpI@t*,)zTAp?&Lȧ T'/`[m5_+W0-労͇bIcP?l ۵kW ?^W>0y__^(A{xlƍg{mѢrmA t$s#̶pu%حt=@Oۮv9HB>j[{dHj*m\&;O>. zq#Yp %Uh*TY-̗/_$hdH$leGQ>JZ ۴iӂOۯ\~'!CwS1֫WVnu;X)IX v.~# @[ 5  ;TPJ7[#dg6RGuQIa|z: <DZٳw}2(&cO\=p=6;&S+8Gk֬T RI6ݺu'\=p=23zq#C@蠃_wK[P}ϕu r nנ5 +mOV%Uj {4ILA yפ֭z{۔,|6lؐ e Tt*Ť-W>zo.>o QGI&EvJI]lc/mݺ?9ؓcO\Ul4[.%fB6W^… gcO\ ^:PX$Dϛ7/K|z}T t6C4x#DA ^5kVmwhʕ8\(S)isNP?LUmVZt뭷ՠW }FmK2)khPU$k^:@V:w'Ǟ L,޶cƌ@E'{"xxKy;EߟOPdׯ瞅\=p=23zq#J, @ }H_{T(_@`>J0`2k/ \* Aۮ%B AޯNfjm[)t$V%*K tDF*U2>M80gΜX,O4 ]]Sɒ%gs7h RǸHPOk~%D Ktłb?- :mT(p'Ǟۋ!CD}Qw&{gjKc*AhAT};ʘ}-zp둙׋\6vl <꫓WW*7z*l,WgWO/ϻTAA_lAѪf bWjgb^%XjyXo! tذaӮK+@ zR6gXGUer>E.O`p4m4+ .4\y8*qBDm]7b5%A8ؓcO\@AΣ>t{p=t8p\L+WW^!5'lTLE4X\X5U^K$D?^A ɓ'&~{:a8;w-ZԒLvKjޟ>} tnͪA BѲe"U|vڔd-:uj$᫇-I2 , De%hРAYV͞\=p=t8^7./{]|y|&xi۶mJZ`< hM ,ا\=p=23zq#zE׷ e,WP2KF60&f!&SN t]>%[u, h_(Kk`h߮l#m͟?*F,P.]:l_b/nܸqkQ >5j(7*XŽ ^AXs~@]>zp뱧GO< $4=[l^b;~i¾p=t8p\LFxLVv,w4e+B")Z1 *_iJڵ. }XE dbRJl *;kIxw'Sc ʿllwE u& \0]}饗,1p0 `hb >e+.ygʎ=[23(X`!w=#%Cc=<!Q6,4E|6 >g (r?-_Zp=!![9m8q;^zu!mA؆U\dr(Ґ΢c ~OJb,B4RI(@>]I93!C/tH]$\t#:ڽ=;eZ_}U"p!\sNvڌn袋K-楐ۻ+ Znn&dnHViS ́!dH not! }! N:N8|Avj5fAo[vq\."Me2i:K@2鿆P~Gm ܏;T0//qyi3|dyma۠x.}U lVꃼ#g睱OxN܏3U8C:0Z0$0*wޙnde!ϐ ܃%tH vpo U{.ac5hNU$V'ԖK3zc q0/XTD rI&Q"61aDa` >#,~9caz 16K{Rwlλs-ۉ9"7ˣoY{5f5V؝H.(.2Cj3ڞ,z!oaW .`"n!raoT@6&kџM9{R2N.iAqRa2N_*?fAԋa89ӑ ܋]z\r#;1 G3ݻ7dmAHL `A6@ؖG?CNbyB8 {L"b\ВrHVYu:g CT@DVhnlgxwm X8'2 I%!AX O$K_VF0O rȭ!q%=3y'PRl0vZ Oﱣ3X '"mE^eJ!goH6+i,t1$'6U:1؈^EFVNS*O?"; >F=quSIP'g5 ?8.I{$y.myHJd.xNlXC\<\aF8lҿO$ o香d8^@&D(ԩS +$ڵk6|e3O&#=SWk_ +HOPS98l>9vy.a3K*F6ǨARݗ4Pve4d"3Ee"3؈mMDr`aWߨ{&r.3#Qq D.0!л,{i6,?mq $R?o6[m%_`һ/bF7N/ߖ G.mrLcGNkr(LD2 DBz*"ē($Aۉe1j)Qr=Q㉓Eek?,zEO@>AMD8`B.\8SmR$Bk) ڃHHґ \ЉH~A>l=+YKS[GB S"z}t3t\z962ۭ[L| &XOSe"]s!@#cFWgK%Py${k93tCbQHGY=6Ht M6OX{Jγ]uDH&gل` /m2N0z=vy0ߝ/edKAYT^:B}_B_%{y[(=ָ_?6v{k:~͵OH!̉; M Hj.]vCv/5U9c[Uk32{.,WDžr&)=fO*$M;>E/W!wuߍ*7qփ;DN2b8lFvxK22xD0Fc,@! xg'\XNȖX[6WTo&N"|=0EH`WTND!K9%[|q 1FΝ EѣDU_8B= \)I|γ&!0 lm"+rXX_pDzȬa!w\#|Rbp;K6K.CzR,rS{!*Df$ʹڽ  o,hU]YE-F;7%%Q \<-"ZKWώS2zkDܾKg 'Y}[!?4%tȠI X.x@M8eC޵ȑt6@E٤I7q. oR,K=5GM !z<1=cE֛grq [uX?~r[F{viJ "IJtg) ߼sGG!p@(6/jұ]RTkfJrSe>C|@k^عؙM9 jja7#v`0_O Ƚ0GْcY2ߡ Km=>0sL\̟})HD=zҶz7IDf%{_gx[sv֭/דUbxc.JRoDv^|)nt~{ϣ~J3o~Ԇazt[eoeߚIfaFD'@J&0'Ж" PRD@3/B81dl9[8$2N ޥ4[DЖ]9VP[" G?qLd-"ӕZ$$&q?(T 0ZU .27y.Q}(W$N۬7 ʄ6d;)[Dr92K゚걔"DV8Hv""'!"*!b+#J ͕rR)5 ?(}{"r$f %W\9E-ƌpDZ.,W+Jdlߜ\X@*WكHv.' 35n61(N<0&g%?O3ryNHj !"M޴!^m,2q 1nWEC>i \k\~4'x2џ4XLcO4Fg1|r &=u;Ѵâ KσhnjiWT{q"OS_l!c_}r/-Au nA,H "Jtez+,^)gbi5_'dmmL81"QcXo}y{N{GH\.gN;Wae/7hn'9ȩzO!ov\nhy S{ !R~*Do8N]ע4H8J$w+d5Cc1%pEܢtDZ[s!o D=Ar2 "nK>Z Oi[.*[#2|f_g8Lv/E:H\jr5LL{$R 2"i)ttQ,gn biKwŞP=nev) q(ĢRM%p!ieLѳRtmzT'i-c"'|DXaw$2XѡG='ieA?'pOJ.l!ڏy 2^ʚKD6fݙΐ?9^s{qNd"E+'paIe&]|,Tk:a@HJ;5 w ߡB7Xdgfh -8ߠ 6 ˖']HwA y1'# DSEq]/ٯ]g{:>;{yX>8fι!Y7߯'pxlMwHJ*.L{l_S1 f;-WjQ?=z2[g'ϼ:i{iz!xV\v}a^ ЖUQ^ly*ȨbNzӅў\H}!g.6~H(mmM%p%S\/,(),LL}m"0yt"D͓T#p{.LBlC؞$Y>!"gA>T+ԇ PGqfY` B)zQqpV?!\vEn`aK@B}DRvܖg4K=?A'&]2>׵ENQDr# P2EVcȴnEz(y(ɀDǑ﷚q i%BmN|OmfHE"ϗStV'R!p!ʤAg[l-q:Tڲ솜ܟa:C-cAP"US Qw{NE 499Eov\tQeG*|C|=3?'.}暤]nQZ.Gݴl/t>WWNsఝD><,.((=[} pEo܏RWS_y4 \}Xƕ!#GL2UD2w H%j-%2,<@ʎG;JuԼ5 4$WW<Ć#OOD,ѧD^Gﮱa<@mW%6ՍחnSƵG^xO~Ycl{קPKMn=d;Rvÿm{[a=0iW^gD!v&p 0J8_Hka+ l-䰬rNB' ‡mILX$zh25>8>/N8.%v1ձDP(T BYm2$}3D!ji:9I}B|6$/uH;Asv "c1!B젝y uX #BB}?#=y H i [h A89J,'k.rv֖D7x`[#rOM-/p/ѣ?d-cZ.Q!88hrRoWu)U0aBwV-JQ]!z@rQW" ɗ(GlH b ~,̪.q8"x?–q 8GH+ \]uQӵw1!7>Eh+dcV} -sE8w8\qpsd$sgsFe;E.46mPIe4^C'DnF3.N,#pUDF?rLݛېgneɿGc|ZELf"9 1Tj9l@`u|?KtS"pjө\"i0 Xn#o! Sq9Z蓳phrd.\D0Y8Dx;2wʐm0:*EG Ct\Npf"z@0tn !a8z `|9~Mͮ*rbwEձNX.?@(r#2Վ"Ud혳}w)uV&p!?idB흟nl.zelnO!ߍzr =ry]vS[ZMȑDߙ%/t3NOrYSD&$W_W>ġND1KL\\,0"~z9<<5Q1/\ҴCRt|z9WyGYR{9vfa8Y̍mSI.֢E(5  a"Yu(FM= f&3tQմvw-'ݛlGpv` 4'2ngjeL캢# r܎3Βn@~7JgQyE?)hJ.$&DFdUGkiP@(]m-k*/wц3{;?>ܞ;ퟞ[U_R54<ʗTW?e砳M5 0[6w¸QѹN]rB&8RlCaF&KF+NAR?=m{kd`OE*JdI$"I )(3*F)u)]Yp wc{cSANQێ.Nvq\I.|>-fA?ޏ%ߖLRygO+: >G-ܩ~Vw#R>~6'.d+yE~&} y&%E"ʥ36>eBAL9tj"p9 󮶍V\z)6K[$ps z' =g]riw!j193Z[<6v)b5sɛͥ{Co`1b`[#p B접8,rk^{SZnu,r0P؈w]9>E8Smw1Eְ/IېdV"pa(qUZٹ^^F$2G:ws럩ΐz___p؝SW L=\v/*:6RA,J`A­Yt 38luSzY&pMaF>:K92~#8y\"LvdNWWDRԫB| C~OrA]T/b,+U.wг4)+,=5B}qתlےj~2/#>BJapmzn9VQȶ3RDZEN[;M|vDA/rEsh _"p$|T2SN Z_@29iWAc(D RāC6Dͯ~9]{~Ż_$کۆ1ۈ-z4p%>À!ZXJsIcr䴏 9pLS9c#ab:M21gmENr~t:S{i,:,3=1H|!2lsȒkSYfsjrz}(}tQ_E3D<ܬgy^jnX4 "9ׁh":>0;\u$Q6=sK+U~"Dv|뒟e.φa,xs;ɗe3|\抓`=hQHz%">cr`BJSZqAJnMBr,C\.[bva3n=Au\N4^a-'{+BqߴϬH3x5%6Zw9^-UNƞG\\pX%c˔cwбY9˯^~vSstJb?NEsX /%&pMaNNI"Gf$U3rVdL4 + %gO9S$ǢDbЖ??$FS"W&!/H=HϠT c@`L᣺Ey@ęRTVOOCOgCdo1} &m ;iOW*;fEH9G;p_Q 'r_@?{ǻSi CD$|ˉ.dC݈)BFE~NJ&c1xM 8-E0sx m_"sbڪ{kdD9reV#p!2ϖH T ӈ0mO)r7 dzgS|(6Ar(#J,6 aIEM~ãw[.DOt9LDGm>yG鑮"V,sk\}m7mŮ~ȭ\8 !Rj"KCt"'5ڙ "8DE \yW0cTc{"rj<&;s|ZW%6bх^ypsrxωl?83m G!dJ4MW{$py/GeAx#$EA=c[r:C)"s?#t*$&UZG*ށ< 'p8*+nO[- $ho.s<{ 8kdtMQt "?5 }Nѷcl1ON3{D[#p!s(4#u)"UG!p'RV-bKV֢:6k- K7sb&p!b9I "p_\=ഛ"p/FgrzoZO8NPs֟op7'hgc+}gO?M➿l ܡaB'T-$%u$IV%r C2"SvԄ Ee~&DjAP@(`./# %tut \'4Λ"x٢J$@TM ED@ Q_rRu'E6tYlfe8_vқ"CJMErdw_S$Zt? 2]VFBəLio)N8"pt~W\"pxh!<DScC$ͺ6;,>ZᙌQ BCܠ$׌]EaPI>A~="C?~LZ$#g+8m. \ށ\UQ-EF\&LXSzd 5H.Afd gDB&BiMLZ@TGE&NcCyjglܸqH~.nœBgdWl6uG7FkBŻ9 \W̱<ƴ|i t  8U_C {q+CDJi B_K9hc6ߑYE(̫XD-MB~ݦ"pϻ2"Ϛj!pwo .!iey?Ăx}]Φ*`2VЗ<Ȓv_VMM OJ%$5P$mZܜ|[قK-rh|IDjrsڼڛmZZܵ6&}?Nz5kaWyTK\RUwR{p9p/CtHmI^"D!"|m".~)VeMEʈ]:=P.A$ȨNS t rCbrV `Ðg 8.\9Ej%p!9!!<' cWԶ"-QHF8 qʫLc"GE8 \ pѷ ko(4.-.0)>wyGjâ Dܐ D!z}3DW1C-%eMJ@+e.uk\\)*Hdk:!ZOuwlD"?)eHi_'ggg GRFsB%r"-D.J INB pBN!/lf!YGD Op\KDCFD ӤP_H]9T#Χx !|q 6U{8Q;&} q|}!j=C4@0&q9sGtuᜟjL](̉Dm71 fa3@[}+z!3+%tz,z,B4?wzcs`ȑ#yɽ%e TFrAWSƷENh1#W(+#H4n?\*,j,PK)X<9_g Vg1g5Ws}ȣ,cǎM;mGUcBd]}/zEt7"*H|19gk."iQrS>R,}E"=4]Cfe7EŠt%“HӦ|{9:mpP΋-'ܑިTv_nuE~{9y:_v*%rRx yl=ե.ka &1Dr3-c,\q aup$"|#RdShm[a[r Gwy=H$Q}"X^2Adlr͖ꪝ! "*?,9wI&FL8p\T[ӝlĉhtË3D ¸Tp!Á!C_|f88?q ?8ly%Bh;Oea)m\ 2 :$+wHOA| ܯ% \"dggD_1.4VҸoJ_R^ߖzv)H@HC9ury )'5l 'z I"eYBz2I?XmR԰Hoލ/'zC:!A@γ"ǁZEosDAJ0&kjr6ˠy8|h!dq:JQWHDI9a\ϻ瘏!Q ,T\u;2ƘSOoNҍE|W@|D(K<n=szQ3`if| eܠ1V1|YczHR($ߥ+ CMHjGBj EBbGo6`UiCfԗv =]I"͓}+93-[>c#l[iȝ.Zt|Ms+5+7m-NAR5ka.N@"` 986GÁwCL2iH]ؒ8@KNV5 Q*\)J"(HrV;MMΜ8 S5.HdȮD,aʐ!Qk{F>MUqG}'ᰤh?/ymG1SB&7ewf<`;y>Ő yĉK '$ BW\}a,<̇B[ 'AB0Fr8M9픢X/4 E[%]C"oY8.GTE 2*1 qo7@\qSD @oOBgl%$)sVDxi <[kyF% / 3,1wkW97(EC\ICT %pb @1G˖6C:m_B}Zcꫯ*8LI)J(iDnSЛ3J.~%UT7!yBΰ}HO&6g38ĸ뱐/Ntq̛U \ByJ՘1cN$.^CaHcc'搰e ]I+7v`ck4HzU|4Y\RDjp{緳WYtر KBDBeu/;mȋߡ_x\dl}5kaFA*eلZ?*?<rHzA$)Za8$A&9Z^D;1˰{g I4 Հ"D\][~&CNb%k1*sʸIIL~Mߓ_CL` +'x ߯+Dw7JϓIT{pW!/m*HDr[k8f=;-A;:"gTc_$8TD}Mnt5f? Dj%]L6yÏXс`? =ؗXEQi\9rZ8u@CB+³Q,gqoz f93 `H3"D?V;Jvpc&pgaE.ㄅ!>V,Vin[obݲt AIG@, p vʻJ?}̣w:NcCl yW}#S,ΑF%'UwZs16vdخ+s/ɊB"$p!heNA>eu[:lXcf!S4DCtCPC6M.: uCmnz鷔J"cw?%Bt\"E)'\u^|lN~DR>GlMAnBr Gn]:GNaZnD鎥{$XqDFG[R~(koQ&pMamLD_q d2h#"D 'g2 "+pj##GnMTGɩ#vQ2-, kz 2D0v$dC"cA>ܙ(۱U3TILEYA$}cdďV*/"'&^ ia"K9vEJKQZIKl`JrD\'2'-ؠ玗!pڱഴe-0D01yE!p-Ky-4bF8]"1$"QzgR/H{ pE.1Zrp"gz/ H=P a\I)Ŕ:=M5&p~q ;2"pސiKB!Jg;zNvti2.8tBDWh?9sXQ(9.se"u+ q'gbG2Y1$I{yNH}f*M9S41՟"JYD.)+?E@,)mNւ):, !3+RY Dn& c-5`4'Ӊ}G,>Hc݈z,/v,i<>"G|i: 4n=,_B107\ŶGHCa9a"[Tȴd"NIPq/5TNy)2W5@B"ZVzߥ6@FH9L2J9gSȍEJC&NӪn"G%3 k,%|@v+yz#.;[mFvWQX!ny^ە4OW0,è¿-T?| fgTmEdTzK'DR&2Hg6>/˭\6"//Nٳ3n~A{M/d\ziTTI0F+ϑJF}o{[^DXW'Ecܒmܸq|2z!F<|QeE<:% >NP$7D[CѪ;h,$r b Gj26T~s~ڳT^L^C&Tq0!$ )ҔB8y(c3^HygBgh:hpnO*g/COڏmt3)ALUﮀ>N/~E?(m'Hy 1Os[O@.>]|Q9i DA|)ސ~2VP(Y{V}z]L}E[$mLE1x&s겤j]m Ո 3FUVɃ=;1rQ4/X䫩 ,n %zSA(&p#bLzr_"zrO$]0c3&]~`^TzG?=3؞@KY, L4HFS".z!pZ Ry:qac/1=P^1&CZMENKEX1;}E1_H-]+VqX9103/aw !ۧq)cE^׽nRD8sVX 8,Tcj;$ C+V}. - ڕUm'jDuy>bS7ZaQGd]6 d#h@KE[`4?.n_m;Bl7qHD$ӄFds% l"w m.m˻9pq|'lW;tǖ> 6h(l0aB"x6YeAId;}s2GI9`C.a2Î{"[jq+z tsӒryv$zfM~FAA 0bpCƫ"RD,yT%YD0d~`YK:Aj?2|tZ{d"WQi07"$#qq ܦ1sWT`AъIy6s^9yu,2ϰX<м`]36ږdlQO=g\6%2B,X e~f`Ψȿ̎"Rh̘ơqTSf>Pc< +#Dj|. ?xG{1;-]Cl\"X"A{Wyq2@,6wyƀŦ+摪$ \1'E(mR~x36O_D}ӳh h\W!Y.M?U w#Lڃ)L=k ǦUOh5ԗkR+[_"#DkrwF{<ºG̟ǽ"Ma\qEd$_dBbBB,@N@Qνb NđȔp$px [!cG-'"{AX`cCD#K7QaLnDR'~ ,mɽԏ{G{@f*,~8M)<Û:f , HJ!mÑQ+ʂM1͋cDs}mE=ampT&Ĉ\pBhpJA-Հl#]ཀྵ7mEuqQ\ 6h-yUCg;dfZuc@9~dwZrHAw@V#kC;* ho`q!I,85|\.aBXҟ17c{p% !}:]"N{5Pơԉ1x_zt9?_(B<Kr/ qqA cw{E[*s!HY],L0EA8)ZR {1!y YSJ2bzꇭ1iZ(89qj2.@GFD#vmgT\л@lG 6NGx%XdC#!_S͂uAb/[^?y펭ǜ@-v͡Oc~ac/U۫V=.v1ka4 K$= ^y!>a m, Ch-`KQ8D8CZX8 rk 90ׄ ?4 WnۆL%1rR7t11s  š="KNVWkEzl?u"v95A i >)/D pe~Fp1q|F-N;NDS6 ERO۴#F[>rBs7Rv !ϐHP+RP1Tdr 9oHH Bʴ0 ߐbWצA{#p' БaLEd}A"<cD9Unu?cyD/o8c\Q&{lgv, T;%1)\ܘ~-7$ H&At6:9eAD2! b+wJT"~7q"H۞#fJsr0+LͫwI]3}aL?L- .&pMa)m^ B#]vM-580_D70kL/ȋɁk͔/sGgC5΁&pֆ \%a \06W#/"2pH&BCprJG \aט^xzpU:ɝCi$W Į Fkђ0Q \t(v *h W;5 h0k&L  dMNiƓ JеkGZ|9LjiF{ \ahIuc=V8#%^z\t \0Fk0kL;Jn7|~+[r-9 Çolhm5Z&p]:"+2k엁>ELm"puʟ j0 њ0k6LӃW^ypw^}U7p좋.:VةSB=.STG\J{ \ahIuhȑ# {o:P5HX-8x!p3cag \06Fk0k4cǎMd&ZvM7ʹ%;o!u,σ>89Mov}oFkђ0x -(B'(|ǽefDR4sj\k0 њ0k6LMo_޽{_ܷɓ o wumw]oO /n0k$Lt$_,<#{>AI"o)Dj~H[@evam"p)2 e04Jຍ\fvS+e2x°a _|Nl Q+ӧpZk-!nDӻロ*B~oNns=]fz%p[l1KD&p]=;nܸdB(tڵp뭷DjA:O*}]'W&LH-Daц?xG⌺ӟ+be˟KK }WNrȩ⡇6r̨osNᣏ>Js'"4o$2C !])! -MAxEˆKK=, &曯zIS6rio{ \vAr@_#y)K<]s7B殩Cr୷*|}@ގ=:miђ_}Us=l0 wP8蠃\\fz{ O=sƥ E<ԹstT"6.c-( S[N8H8CzY׿5fzw2EB. :ׯ Gu;ņ,/+}L-o'~euN> ť>͎p'pǏX%U ^{mA9m}"믿d3q)1]ve'}х'RKd\][P$n0 h+E W\ EӼ%Cr8qqi"ϕ |>;ҒEIKysLpA!^eȵEզy "Y?/0h @%–"v2-y {uRGMSna[}S\0 3|?__8SSZ,%YvcQ..-]88u2RFyN׬n$>#:|DF-ȹeD:}aaaaaF O~mDUge:Tȴ"w}ꩧE &pJp/<4 *@B'aaaaa4@s=[ ua5wz ǏO":0N|0E9ܷf0 0 0 0 0 o"e|_ }QaDRߋVoe[N"ѽ$6"p 0 0 0 0 0D2,S>|xCw]:UeEڎ('jˈ99oEϦ[K{k7aaaaabС>8K U&Kѷ"bHmNN6H\R(+v0q[bcaaaaa4 _!^ } [.J퍊=Gͮk$aaaaa \<&L8H$ DDWͣ(܂>OA[>3 0 0 0 0 0N o-da1 AKT-q}E\v0 0 0 0 0 EOHY\ C3.]k0 0 0 0 0 )K-ѶAdʁ6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0Y`F:!%tEXtdate:create2017-05-22T13:18:35+02:00%tEXtdate:modify2017-05-22T13:18:35+02:00nIENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/internals/compaction.vsd0000644000076500000240000056400014601576577020046 0ustar00twstaffࡱ>  Root EntryRoot EntryF@VisioDocument vSummaryInformation( DocumentSummaryInformation8  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~VisioInformation" ՜.+,D՜.+, `ht  t Zeichenblatt-1SZeichenblatt-2SByte oder VariableK Rechteck Va Umgekehrte geschweifte KlammerDesigneffekte.1Dynamischer Verbinder KTextanmerkungrb ZeichenbltterMaster-Shapes0|_PID_LINKBASE_VPID_ALTERNATENAMES _TemplateIDATC010498411031Oh+'0 X`lx  Microsoft Visio@      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrVisio (TM) Drawing vRvRVآV3?wVآV3vLVH3?p9[!i fTd',50fZOlO~C.ߐE#PQc:  A ,U345&6&9Eaaj6 8d7?!%3 URg L-ak@9$KtiQHeuDDT(!cp;/paM-uh\`(+AT %5+H>>5a%h34ovJuX0Hp(!MmcP=?`(z?84?D0pA D7!U/i 4?Q`_1_JD;a3(/@}_L/^/L%a$G[aY %//// ??.??R?d?v???V[l_ru@+y_w-uaDd/r*&oo*o_Qv4DOOO\U$/@__o/DA/Gj:d2(joG2O5j7@oo ooo?&s2;IO[Odd 6 S( $b4@SyYkO(Or&̗T4EDP7H+&g-.UEd \Uˏݏ____"LwU2 r?߱GXDeTePNkcc#ER¿{%ܶ/o Mv{&l|$6&w-!a!!!1Lpqq;PasqKwHVNT`F[ȓ !z䥶1 @Rdv%Iy -bbbbbbIbybb쩓DMv4,}J(̽~ .?Z//puR/MrGK9&/@:?LyǾٹ  /r p/9cߓAdUe?w?O2{OOOW _u// ??S?_,_?2Y?????oO@$F=OK]sOOfOOO"e 3C _#5@_R_d^uc,aYo-o@CgG$t$#hoIlZFoBQ`Ѡ1`ConectrWigtz70aϧAQ` Ed`cl&K`Pten´99K@q^Wc8Ii߹l‰ݣA͝>†p&U5Ruig;(X3a ŀ&U=HTaWsprnyC™ͺIJrBgt24`.SQȕ0xPt4"H$ @Y,b'@ I A-7 ;Ut4"H$ @Y,b'@  C-#*7 At4"H$ @Y,6@ ,I{7A-7 ;t4"H$ @Y,6@ I7A-d7 ;U!U"#$%t4"H$ @Y,b'@ T,IEC-_*7 A&'t4"H$ @Y,6@ ķIA>-f7"AU(+,t4"H$ @Y,6@ .C-$*7 AU/0U1234U5678U9:;?@UABCDUEFGHUIJKLUMNOPUQRSTUUVWXUYZ[\U]^_`Uabcdt47"H$ @Y,6@ Q|C-T2 AUvefgUhijkUlmnoUpqrsUtuwxUyz{|U}~t4"H$ @Y,6@ t_C-D*7 At4"H$ @Y,6@ ICA-Q7 ;z23456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd*t4^"H$ @Y,6@ 4P6C- A;X4@t MR@-NR@lY9MR@Y9MR@\iNR@LZ9IR@Z9.NR@,[9<PR@[9NR@ \9[MR@|\9RRH<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(4EdVREdREY9rREZ9REREZ9REZ9REd[9RE[9RED\9RE\9RUR/0 e UFD# Th(/TYTYBBUF~?x<F BP(?4P?+ O @HZUBH??!t 07{B`8Byte,BiC ,NK bU lE"KY"i;nG DaC"h!TA pG SoUfC wm rE"S{ d!cPe ,>bjE kC  e^| 7  3u ,7=OasNDas Shp}eaufudZicenbl]t) ze.HD   3 B>Tp[/Lp?as&;.h> ,b MJ=UF~?FV*JR?F\.?FM&d2n?Q6 U AMA333u` ? !+5ChMW]u$g>5 L@X!$5 /`Vis_81.chm!#4E"67M `_!b-"#"'?9h:AiA 9 $''2ǥqE0&!$E0&!qP \@6%A]Dc]3V:?Ka!Db1OCK%YEiO{OL@ODJv%YKOO"NBSB53^@M^U_g_y_Rg@14ot'2TALd"2(Ech,@!3qlf1U1 m3 =M!l+TM9M]a6 `($T 6D UF~@xTiC;'h>4bP9 EUF~?L&d2?F\.?Q6 U AM| ;;u` ?*#)3=KU_euo>5 L@X!#$5 /`Vis_Sba.chm!#57_ 2#`$g!-*#*'?9  %&/7 aJ=?Iy+#&Z&P?B#2N贁wNk?@J'q ,A MVq6q4!*#ekab1; ?Eq6 I-BCb:@e2C1%>> OO1EA5~%5]NGxOC9AOI%\O`JOOrn_gPo|'TAFd" (?cp,@&!iA%9 -$b]'/2q`&!gE0f*laUmHE?KlT(]9^]-Ma6 `r (QJE-Os~%_s%/\h}Mf!>zbcrA bb HUr ~L)uE/>F *#DJB dYbbDW']Qo@t^SoCPUFDfP h VTB UYuU??Fxha T,aaUQJ\UF BP(?@?F~?$-?P nL#l]dV^ ] $g]Q"U"Y*"]>"~#<?&#A&Br&C"u` ?PYu"bl""& M&u"u&u"u"u"u" ,u" ,u"4,u"H,66t#\"u"///$u)ar;21'u `Fxu21yB}BAABA"!^hO$,@ZLupO耑 @Ca4R#qt{u*gvXWcZufvvWuTj*uX{u ]ʤ *aGIJKMNOIQ"R$QTܝQVWXYC3333.3>3N3^3n3~33333333@33Vf4b@Vqf0φЬ uZ1[]^s@333'373Gc ƢEt&EAxÎ;BA}@c"3"3."c Ӣp x/3,S/e ~/iRfSIA62ab ZaLxVIǪ *:Jzךת'OgEa@braĠqAQJ`FWO@r ЀiC}F@`I@)@AEBsZeȓ3U ,puӐe(p 4>{ScNV:fcfR:bWKqP?@pA3VUbQw Sio@(h#FtϿF@ qRfa+_i&7=akVQhaRkQclQohaR2brˡF@#I~rW{ 3~TE}O bqO cy`I@Ё`sVRڀfK@ڀbooOlw wKuK`1s*8zK@JqyozI@ eoo /uB s!xx7SlP4;walPe,0kNq @Jq.-8r JqM9-t '-E1*dbt+r sQJQLŌH{M0tb;{O,=CƁS=ԯןƋM2'Kx~{עamzD1-?F|ǓnJ\˟ݟITjU˫ˬ7F:-˫YkIՎ_TF ?BP(!RQ!4 Iz{7 F.!`}~k QhaqY.!(SFxfb@&7! kRka},1ff",1~,1ff ,1WVHf312E1kaB12lQ12a(712,1HfWV3UYkaO} mlQYBaYO(B,1WVHf3ka lQ"(;" FHfWV3Y"]#kaS#" q#lQ]#"]#S#"QoDcjWV3,1HfEFWVCaHf``5d 䰈qGA8rL5k0BAK6 k0L5k5L5|0V!CBL5 = |5QQ5&t?U: =|5vŦ547 P@QzqE4HQ7b&qn24Dp)CTU+0F2o$0jjSRjaRD9Q#eM1sB=yIZR1s^lZLV_4pQL׌HH.5Oa 5.. ^fCmnfv3p` T]`avelShift`$xa_d@a3p`OΠ`fs`m!*_a@@ VV%Sr V:67sGr:2!l7s[r 27.1i$3s 1T$y@a@dvkQXU.q.'2rqty!,0}v`n$qqt7v-x8cʁEl@baa%]1]1!H$rI*J!1LLLDŽkq+MԄ .NAOAAsQRe.S"T/.rq.qqAWVqqFYpRBZ}/b[.)\r].QLQ_1(A`ٓza(.44.;[Rx a;`"[TBf"< 1W`B`sh]`cu g`f&+o]`m`t`l`g`l*?`0qH`.:jx +\T`x`bdvq_v!L5~*ϫ16DP`)_R`Ht%k` 7DVhzڠ2xQBuݿ)/\BanAM_0qx2Ϛ0AP*(O`a`*4dFxgy2?Wʷշۯ-K08&4Fb[mD{ǿDD!s 1ϱPGYϋ(6ѲĢϩȩ5hA`# &AsDEy3 UĻɯAv^2`OnwAQ9)(ĽBT0fAvH`Uta)Yp!3EWLnew@)ncbā Av9H`ER#t6Hl ~)99:'ϟoP$tm-At!BT)ங'7İDZEBs&1)/I4+r/o/Av(* t/% *Pa#/?4Vir4F:?@^?mAiCOOOO6O=nO Z\9?K8/ `v GAAtPT&xJb=rb+ Trt*P@?B#o54trptb T mkqőԁw`3P?squ{tcF~ϿF@ Y%ukp{`?u %,Vrq @V4`+AszSPsT3 )5>%j{ိqi!stgq xŕ2htbrQ? !3'󿂱/0 c{[яJar<`Ѡ 5 %*5}9ᙓ @r^rlA$Q2U lQQ؉uyhU+424r@1Ryɒ^rx@7.?@hzi?wԐH}߈7c6z#o/|UD`a1H_\"p1@&WrBdeKTv7.!ԟ@V) `aGo,o>oPobo1qyo>TooODowQS+F搞R6);=[vrrQv>LUF+q StFUevS߅x$~&qޗ݅ԯ1).UaO|tǢKT*ϰd4)'ߕr|<ߒM2/WkNȏ%rQ|evʭVlߟU[#Իg.gM1xweۿoo1CBSCS/e/w/S~eB]v KcVFO/GXiPdU:Ǽ ڄE-,/wb/cƻ͟E/?'?*&)BOZlcƄdUO7c*&tG);AEOb1ҿ)M_Cb.ϛ]!_R8Vb=x](' _ ݂?z-sT_Na2s/@.g(O]Sa5 _;;L7pwrra2wcCB|WV.gWi gU1gtO@|{럘p  D g,l;;.o@o*aa Oo0Ґjo2kJΆb`b~Mk 2(s.®%)nd\S?l$`-@K ay!*t1K쐴^VQ2CIUdUpς1aanOa7P7߸y!X16BòJa@a WE1!UަQb@72Ce$6N ¸ջVcIpb)L(aO[Ne4 (ga# f8 \ 2d T!bu-(;M_qڋgֿ`4@?F\oA`0ΉjM09 Q(3b8ye/˸?FƣE?Fp5Ys?F5&!Q@Ή( 0_Bԃa2U0*s3?ܵ0Ԃ2 k+ݓ0]'%Qn?Fѡ?FW^bd%?F?Ԋu-Q/c,DJ}?5^I,QIƥ,$~//'% ?F*^Y?F[E?F|{T?c,|гY,vݥ,-?C6*~??b'%OZ`?Fa)P?Fů$_?FPvϟ'͚?c, h"lxp_Qҏ,mV}b!)/L FAOSO+~'%`0DLJ?FNqVUO:Oc,~jt7A`,ףp= ,7d__Kh'% QuU?F`4pF?FLAD?X@t]_c,io?JY8FZ4? qۄV$N@a__'%X/~%l3!?FeY?FJ~ӗ oc,z6>LZ4kw#awivQooj'%'-q?FmUa`2#+?F(%oc,Y7 }j GfZ4 ~,ClL^!UFV{WE?FcJΦc  )?H},""3\ \ -YVqjǻ )1̶ Ѡn<δ m|ѠaQaCSVGh4cfUpi$5,)֝F3L&AP"iЧ Z3*p#q8 |`rXӗze2ZՊ@ #5.?\zi{B  ȧ#ޯ&&JJ\BMV㖿t侜+Ģz`rl%ǨΩ)1@8"4߀Hߒƿ./1*Tύrˤ]c-=( @=5-2-9#.@"~C Ȓ#@ 7?D`Ofrsetx %q,kڡv hE 5ɞ)3U+KGAb TrKvlԐhi"//ׅDzW280//?(?< ????QcOwOO)O7(QOأTx7HUI' Xߞ^Ba5a8_A88F< $"/}#ST݆B CW]T@eU]oT7Tj(PUFDfP h-VTYYU?"H$ @?Y,b'@?xTT;͑U@ !@ү&P6 lAu` u(Jݩ c?EDe34 D   J ũ2qL lvF BP(?L0 `LineTray sp r{ ncyz۶&` v%P t e n&$`u v%W{ igTyt&a` v%_Rouy dw")g&A0'2A{Gzm?>5Q?A^n"`Fw lo0~/+n"j?|?+5/!4`S:Ta ow?<*+` 9?& ` 9S5@y0eK $KXOf@s{ 'B $KYtO 6`9UM@gy i@i04Ai '. `%9Dw AcNul>k$Uhh2Q2QEXT/J> 09([_q_[_o!otgU__o_oTEooo,o1A>Te\Ls c!2TCt1MU$U<"itjt AktE*Q*QURmtQ!!otU p qU r s-3"!!uGRvTlwaExn11Uz{Y|})~ARɄ22քeㄐqJ"T c 14.m"?<%Rpsf0M8@H8 MEJUU8!r5GrK&HZ&H͟@G+G3"?GERml&Jlmyome>mGG-GAUV>6HԂyϿHႻFSP6HuANqe (=MQ_'0Uhv$on!3` LDnFxB'3` A@iBݞ"4` C m@bAxB}lewL9WIFE/HU= _6p9yEW49dF<1L#DMB U]]@e\Y]o+T8]:aG71]PUFDf h-TYYU?~@x]@L'} V6lX-1u. Bj2u2Z2j2#u9r#ULH/MZ1+B#AD5 60`Vis_SE.cTm!#20AD%`>Copy<0wigTt ?DU1f@M)@c<0o+@'ofdBUArX@Aad@iV@n3@ WAl@ef@R@6AtBvBb@hB@lBAJ@=## AoG?9/#n"E&444 7I#$@HB59_lj#"SV6U8*l>(Uhn E /J$&9"(Ln eg5Le#pheo'T !! Js~o/|R12'"U7A%O3W_7xr5(-6 `F2 xqu\J@#:0b)&`AAs)@E T@xd@Di e i=M]UJh#HT0#3 UEJ\}|U@f?@3!?F~?FL&d2ٺ(1?P Ǜd#pa HkaQ"U$"Y8"Mx#BU<&?&@&A&C"u` ?F]o"fpy" & G&o"o&o"o"o"o",o",o".,o"B, 6 6n#V"o"///$u)ar;21'u `Fxq21uBAy B AA21"!XhQ$,@pup`Ca$Rou@+u*LfXQc* IRg fbY-H- M ZU ZZZZUZZZZUZZZZUZZZZya-yyIvJvqyyMvNvOvMyEQvRvQyTvTQyVvWvXvYv3* j|%5EUeuu 4 蕺!BZ)CT@x@U @n@o@a@%iEPn*Љ4J1[v}y]v^vc@x3+"" qؗAU2Aym@xcv$"" zp$E1u/w3,#? N?$hVW9AB[bSq20S hV1Aqׁסױ!1A7Ig?abrXqkiqruׂ6rS@6'_@rp9SR2MV@׌aV @aU-ܕ?XrcU:uJed*ɮ᧤N@q37sfjBA'b6bjB'#KPRQʾqjeArQ c@(3FA{aٿA3b!&»F_ v7_M%;f!xjB!s(a?xjB,2brrP#VFI%r'#7 3%Р$UM_ }uj_2 ׂP5GׂWsZ2bQwvPQrT_@|wu‹ׂPc?P 5uR܉͏Cgyc(` q(`e80{A @9-܎ Ʉ '؏bAP42Dr \sya@Q_X俋NZ]b6 O6͒z{ # Z{u£@%ЊׅqmϕDR1oL^c,ߛѯgZ $j%{|ב R{iۄ);qσ⥞occL&d21b}aN/9K!KG'!cpK1a81YNSFxBD $e0Bof$z:cRTrDBʩya#HuzR j"^vc ` T-pavelS/hit`RHq/tq `OfYsfps}1 oa@@VVSrlV F"1+-27i41Tb4_qt_;aXUZ'2rq_!n0^Spn4"1PT_sE<raa-A-A1Hc^IpJ}bL;M 2NQOQ1\Q\QQؔcWRS򔆮TqqW&o$Y@RZMb[Z\g]tpapa_TAQQAbpJvq;Ϥ+bܤoa pQ+d0AW`BfpsĠh-posugpf&+o-pmbptdp^lbpgfp<XR*R?`0ȁfHT` jd0+9TfpxrpbASv_vPQE~ 1;F`a`пҡ<Rfp%tknpr !3EWj|Ϸ2HaB^׺/9Bmqn*<0NHBew0yQ̀~`ϿOdpahp+=#bRdﷰ4z:$ߢ B<R ai*rp iisXmTZdj `F1H<zՕ{xMOai?ޯ!_` tD.Jr5<}= ` K* `[Z l v 3mAxD{z{.gMbeᒎcQɥģ"Ĕ,/ħ<w{) ѫcĔpĽ} ѮmJ5N13_MsSspy`ID?S/c}ba K"eb@o> )עa2¢ `DVh%# UD kmjBt2ߧ߹$c褣%!rr[Rf`}aTy7[C3DnUbe|b;ruv`Up0;nr`1:븁X%`?$2$N1} Y4Brb@qAa-aa;qa `//5?*?cT Z "!0ЏQƽP׳M+Q ܒ*QDb)QX\T׳a_uu`bېRIu}ܒQ (l\O~?hzP-îLޯ)ّ `{Cݕ2YǕT `ƲG . !ё1ɕHᝢ WGMĪ ?Ɓ%Qx?ˆt/$Ͳt ڔ,ş$O4  ,o>oPobotoobo1*BBZВՑ7zBoaa^屓aG9gl6ҩ$xrNPƯدk)D _& 3EWi{ߍߟ߱ /THZld Z_VjhRdv=X|w$%F / >Pbt~44FJ3x<T+///(/aNO:QN9O:////./??'?9?K?]?o???1???? OJEOcUQOOOOOOIYvotuoacVh͟y_T_ j &m toB)j ooQvwёʮhg_#F K?F&?x?0l}G{,:`@"   -?%aEr%Õ))EyXR} o`д$#?ęxQIr/xUt,bwXUl0i1ewvv֔tQ=Q)R}h4FƶwGYs?CpYՕY 4e%{QHvsLQgŘ{)tUbzf^ȯfܓ֔% E:a`*q>^TbtxoMdǏ@هT/f/˖{OtC]c VqǐI֒FV –nUF?vn qsɿ *Ys?F>?F:Xc?FMڪ﫰|JܡtQq&A=QngiC21MwZ漣A$rqg0N܏tE˔F*CUy ,Xjw=Oas߅<`ߦ7 {RC*OoNkrfŜo-?QKe_? Obweoz}&U.V@U4kW5wR<k 7 fwdůWdvw.ϒ,u6^.ڌ@(]/5Uk@ k@Dw,|@q K0/'LK9'Cm0ϾSBs2ّ5)\ls?1D@ q>Aw9#uQ y~qҰq٪$E)U5G1r9{ a`pM>AkVRbדL!.UEG!ܱ'kWH٤*Jn}viIpU'LG(A^|{Օ4߭9w7;Օ f!+S2)kt/'م"Nj$6HZl~P`4@?FEO jM󎣈PuY Qᦤ8%/˸?F#eֻ?Fp5Ys?FwtÊG?( 0O(LпDJգ<}?5^ITLQIjL$?~OOI5 ?F;?F[E?F3Ϛ)O(L|гY<ߍTLvߜjL-C6*C_U_'5OZ`?F3$i?Fů$_?Fir8_(L h"lxHOj?FeY?F܇]}(Lz6>19lTkw#N<vuqRd/%5'-q?F0 B,?F2g#+ׄ0]/٨(LYЍj@ T jLCl#15v?3FV{WE?FD~ΏJNk(@(+ WIHg}jLzB՟$q$D*P!)=6?zH7j A^ 3=< 2Aġa1aS~h4'+5.ϼ7O9H%F_p8Ɋ( b-SlJg&ǯٯ>qA>%7IWt?&t:NO/ag#mҳ31>ũ!bX,!.@-R^rC&Aϵπ!EWiSh:Mu^9a.^;g?&h&8!AOϓ{EEEW{>h"R7"(P!1('b!)1jUP-29Z3ˡ#@$ 1d>"wCCZ3Q%p1bؒC!p2@&7DZ3`O}f70seto?uܰA0&2,?>?P:v72p&1"E$A1oٿA32ÿ&A>+5R{w,0';)w-eV-jUCU+ a ?%A'; WTrvt0lghin0w8M=Bb7OOO;J!HR?+vOOOOO__????(?q>_]?u sEtjqttHD: #  h  T0]]9 # B 2AUAɿAhY?ٿA,c2ֹƿP6 Au` ?A@j$u#J.9axu`u]`bu`+u >ڍU@@M&d2}34"ÿDC'>"M-w'>"D(p A "'$"&H' O#&5 $s&]$2JD=#D> D6#U`l"_# #1?9#H1 uNWzIs;2 8J#2zGz3Fx$2[Ru(/YB DU_V?\.?/B:o:2A7P`BackgGPou_ndCM`lM`HQz@@bZ}H D?h1GA #X8P6d3662Q1CUBF4i7o117;y0~1Mr)Srm>de}mspTt "qq"1 1381812tMItBB Eb'b$4ANu13[h2AuBH1BʜA!ʩ¶jÄ[QЄT݄zrf>!$0 %3To1/u/u?8XxAC`IWsSwSwRs%o.@0RgPf KNC`teI`\ai!nn3uZR%d DM`kO`mtiM`5HQ-2HZv#`NC`o>!8V@MKsEŭAد3dVrbRaPags0bjkTykp~1])S搹qO`zfY`u^s;cTͱnA²oivlPA$*q#8@q#5$rQQR*E?eNRעQcBdg:b`OTndGPuNEb;StRaaGPdB[Ÿ-uijd[„6k~]QRƀ@gP@ A>LEs?e\/dϩs Aee"g 9bY`dt2D-Ÿpsr- Sqv߈;`aݔ֑#pS~>"ƒE*ȯ:b` Txk GPp}1Բydc j]aaAe(Se1dSrʴ &e%\g#d ɿRf);M_=7DßӁ„{81,[/O^kxÅi-ùƁ3 uI[Uu`  *L%LBW?AP)Es̲aٳTe"/)#We"ɧ??? ?:OOB&c_ //Y`6?H?Wis??]CJ_ oAUCGP?e`(__! m2oDoVohozooo0 siԛOo oW}_oOOOcV¯]WץR=Oas-](]uќoo~Gm#oTw {Ɔ҆΃S 6:0Bw)s͟ߟ'9ρJca:smQm Krаe/Ȯя- voP* V²#5VUo"/%Wo"O _._@_.o*o/**u(!S[mJ5li@eu+arw% UR°l;Q@nt9'G} !M @o#1Ϡt+o_1OCLRʿ.@=(*jMD`sprkND߂ϚByiϹV" ?5G&8߭\n߀޲5F/=$2&"4FXjj"j"/ɨu*FXjv_?q????9 ???Qc0OBOTOfOxOOO8B+DO!O=`Rotyfb)]O( !1idBm!?ȅKbB;rbrc`R@*@;H(iPB_)/BE!1v])g%oks{1tpnN;GWewyuPz%doaqm Bqo2;a2wtSkmQb@nvUaTtnsb bdds°bjTq3o: rrcjKbT!E!222?kl;!30kC2m2  aZZ!*kψC\\E&&BGO ='`C²!cRf Ot9d_D@qA,@"hi."4 b bA' Y! Pk?zz!!_lDZU!!'7!ƕP d>"p}9+6@eDX]o}G]~.PB Z` P_(LT?:]PD',E5IϿ ,1X8d:PjH1/U0r4,,1)*AA/_"_o & #QMQ,0#` B scriftungm,1 `Cal PoPtQg1`E}u+iH5u1! 7 @@j&@@q`o?@rb?d-e?s)gUA$ ?$%vr;#xAZ%RiLPE" 2 0"U$2 ur! |u[]4 t&&s~rzlH1AUH1?iiu5UAA2zry$"!rByǤo VPQayl p-LPnPʡn/k?Pp rN 3Qiߢu PuPw 4EPtTIkrPo?PlΣV?PrPTRʧIEz!mr󢴳W 5D b9BTBnPnߤz 6VOLA !0jA_S_e_ŕ{W'&!3AP!|/2g1 D1y 3 H1D1oE7q;Eȧ{e|lE1@@~!@Fg#?F֌"??QkPyA`Q27W˚ATl 1<25l =cl@YmfxAj`PL!@@[g?+@@1#_  +??@*o4?PD}p\z׈-uru`u0`bcu ,0@@"o@a/ ("q6y B%#u2#*"0#ᝯ2,`6@@Ên`P @$Rxww &;01O5l^3);hK(a~"f''ׄ?[銞<<h.?l"dYկd#j_u bsUTK0a?DbB`@_bz I/!*98`P"3\ o"҂3q@551I7zrQQr 9:z:a:a9aaR=R>rrqdQhQrq(Qr q2aa0tQmO\[|RlqlqⴋrT,@rLQux҅t0q^qs pC&Av"rBpsx^֔u~*k sa4azuNu ca@bֵf7sa9ՐxU܅ŔrQ9PCaLJY„fD/@rF*PPV#Oj&TP@Yn`@Q.%Z$yaUBafp Uu!4q х4s q, Tto! ТZhQZi5,Qrc3WLP@*(B<@b@t^?A$@o.a#@PkIKr3b3KlqJj/ â\Ca/O Yn`PQFYYCOǐ2p:F>pKBrOgOq/LKHHKEKːYCJ=Aұ)VZn` OzOOOOE^Z___*_<_NXQ3|GCA|epPKd @A[-B^ cAY£jNT '_%k…Ž_Zoc ss&qg1]JSr|([w-v`DbKOb=~h>fkviKleN{>f8{gyjPsKϝ̏|vRTXc[HAmeZfDo1ock2DN&xݟ^pP*wwjo>f?ʗTٛg>f,GY^4Fܢhn??Qa%<}dAc)#]K2At^FA1?0_P_b_k؏?@ܑ9Zm(͖pB?@qK:?@W@ή8W?@S^H?ssZ*m(5=*/P#&?@!W/?@?@FD'A&a?@0?@vj6??@^ŴBI!O&J6/iлH$?@ T9_3`?f\Θm({E=T@m`m?@l6͊`$Zǂm/&0=ficUo@@K^9`lخgA%=,^?@tA XP@@z(fkT?@Ô1fe=j.ƀ?@P3{@@5 I]ͻ?@+ӟ wu=~@h{?@j"S~#HXt$'=)&3I{(?;3 BP6Oc= }…lO'ƯدH6?[0 O۶f_@3l~ !H<@)>Pbtφ X?y3yGPU)^BPԇOOOOOOO__)_;_M___q_______1i/{/^X" 6q'AS6m#p P:yad4//*/E$B ?' `?FM%:# %?Q.(%,",x>Pbt}R,>bMt=ζȏڏG"fFXj|ϲxU,įFX*pԯ8GB(ma AS4!̱ Dq 2aaJYPcy08̱u) {`gl6 @@L"@@Hh45?;C?FwG[r;1'u`Fx8<"\YP"Q!cZ"[!y!!02..ȣrq´"T,@-@<إN-9-L7Cz`WAoubҟ Ab;sż@.v̟6 ˟W'?̵A4!)4TB`xAp`n>@tzj7X3#|گ2п6=#@X)ܽ+?-ryN ~Au`u`@rb?5@ȩ.p@Őc)a&M/_)s6})7Up`uR!cA*qK@2-[!Ue)aYգҺ#@+("euF@X,@@cP?$9/f$Ptw2wy#wI`rSbK3`8q \~a, c֩hǐ⣰  ˩5< Nas i !3 w%;G/?/e u @N2 -< N6£1 h5s1.h fx sN!P 4b ?O0A&GWA///&6?+//O*`׹mD11CJ /"ǗNO`O//??M'X'jY K"K(;G"JK>1?/KK__O]?o?_"o??MR& vum]p0cKoeLlOe񑡡ojFxk@?vKuS@Rmw@|PmƁNaGy}A<"po Bpp9Lp A? vEqLJ PAdrHXLJ,HwX!kmieT~A#5@?MVl5^l6qBV0IPo oomDu׿߉Еe n`vc~ae` TmavlShi"ptпome`eOpfҳr$ ienюB@0wo0oO!~_=@`{U$erdT~?L&d2?qnSU^P^ e رT|E?U  ŕHfc IsJ026q6q|qLԣWM 2N&O@Qԗ`R`ԇAT&qUuVrIW)rX6KYCZPr[]8~~^]w2Ѡ%2_䨁@Ldq2јqq-;X |pnԛq'VUAAU{S@X,@@c?P?]2vÑrHTn?O#@2pcr2ql{ԑ١U^@dإʼnU~K/MEp2mԌq $SqU£rIdҥqҦ~ҧҨ(ҩ 5JJҫOI\ҭiҮvүҰD {VuPqM/|qf 1 q/$r89UƀdK/Eb].J?/ ћqXՈ!2яՁ=7&LMP5,Ku/?+/UF BP(m???*?og6oo?M@]f?qoooooo\ke?__O\Z ՁX9/ӘC_U_KXF_}6Z?Fe#?2”,zNt/DF`0 &F1l aPdl/BjZԘl-U%{ 酼N[b?zf^5OHƏ؏bo2oBbd?Ffl6&FF?FuWu~Z}1Ԛ\ ϥRsBTЯ*oo`GÊϮݢ${Կ̗ޟM2.RkO֨ϸ"4FX<ߺ̱ 2U9MSiO]yd!4q<|a|aSeOZAkQw9+OO!@ Ai1/C.</bsPbOo@nZA">Wi{;^5˩?@Fmtf~:G?@?@;:y?@A^}/9*[Jȏ :g}= A?@I l?@C"<%0Md 8#(m?@'`sI@DͰMUh>OmHaoW?@&N?@?@?+%U OmHg6D~)cK?9?@_1n`Um,H_mH揷P,P 5b?@|XYm˗_mHOgOw^ G/6/?@$!?@d%FV^_mH+5gf 밷?@qK"`:= ?@6NZ`4olI5gN@S:3ί?@f?ōo ȡ4$1qY?t@ O=$rb K{ӞvD '炏ÏՏ"?V N x4(1(<1>=ŸD?8Aį߬X,@@cPx114<1U??$yίU@?`0 Q/TSi^1ATs]<kQ'/2qqGaQ! Q#`Qn%bC6a i@bpCa zq`%>q,W,@Q"c,qB@>q݁oӂ_=@`{k*#upq95"+aq@uo5ubO lҊq徶#Ž#UU)*/023456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdTUUUUUt4l"H$ @X,b'@ .9xcC- A;X^ !"#$%/023456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[J\]_U`abcUdUUUUUUUUUUUUU     *t4"H$ @Y,6@ :7C-$ A;Xn@GRR@X9.RRH<(H<(nE|GRE4Y9RU`81lZ?#d@X*a@ LFDTyB Фuh$T UF"H$ @FY,6@Fx3!T!!&'*U+024U567:-[2ABvE.9CGHIJ*LMN_$sUQR%(!TUVWXSYZ[\]^_adb)!$& .vl:q;0?}t+ 4Uu___Бd!! @@t:N3@@p8@@x<k ?)/>r]u`VT?˂цۂuы o,9&E1+?JJ4k!.//#{!k*y? 1 ??T??"J)Oۂ)Xۂ̡X˂c ˂즀 c` R?"2,1,6UGI  ;6:8&%@$Bu `, @13q(/O%&aEno%_}a kZ&ooaY՛e\o ao^%CU%&p~~XA`rτϖϨϺ_dYkdvҏPB2Soeowo(/:%X, I/[/m////X[//&=-??FdKO]OdOOOOOOO_%_7_[_m_______o!o3oEoASeot?oM_/ߧew߭%+=Oas';mP C5+s:%~ʟܟ&~.֠/+-5"3 ?{:'?:??Q?c???`r?#"ǯ>F;U-;0BTfxm﮿ҿ?,`?Pb%ϘϪϼ(zp;?OOOO*_<_HZr_~ !?QI$\oBOR._SG;OMO_OD\z?@@#U:LDybt `a °;?bQtsuw<Su wgcpl~=O//P!3/E/Z!k/}//////_/ ?? C?U?g?y?e??ro??? OO-O+=OuO\_'__O@__M___/Aϕ__w___oo%o 7oIo[omo2)ogѩoooD LA@Rd]ou@ ~xFhԐxĢr?lvKbנ*=t?=8/O@!3EWi{*gQг.֠/j|5"  ;Re#5GYkvx+@@@//),?°;w&ɽ逘U///)*//ƾ` L~nrCo3orzۼb<turS7`` F~l3"` ^A.%88=G"?4? F<GFU?g?y??6ҫ??y??OO*O4o_Ͼ|ooooN` BTfxz DVcu_q߃ЏEV*JR@@f?l60@aϮ6ј( ?ȯG?*< eſ׿1C/gyϋ!?ϖ -?QOas߀K/[OmO);OOqOO3_%7I[Pmv1T%L@.N"Ia J&TES!X *gd @ @ 1A1aAk'd<58WJ&X Go ։LPL&@@[1@@jZoP@ ?ptxu?G[Ir;@Ia'@u`FxyQABCd,rr ,>-p"EXacIar"O!,te1bB8!jaO!)Xs8Y~ٯIaRcIap pKcKpׂKppp p p p p Z^^ Ia($3$4aO!g /o.ƒ̀nA{Gz?z S&r` S;aowCon+ or@zq` UFPl+ PѠtUenSRu0g`` ڮ` 2RRqk` AcnetA]*Q` Ωq8"W`TaspѠrrcy¿ԿK`Ω޻ *̫Sy+ eU X+fstm Yp`a/ `ΩDP>ciՠ6Q`ΩMѠgiin҂aSxT?@@@F BP(?QLPnqs5~sb qIaug `4?uPrj`@m^a=>RyQ1aBDDaab܁܁C"1UP8ai10E0`O!rCѠ.F7!B҂a1W%447*Z4 ]],~hT8e[/F|$//r@/?+1Cu:9)#Rrq5pqv6?@r`3R6`@p4@\5A`a9upXaUu`NAg08d01wP`aEQrNC`@rJ$,n(ׅ&r%sF@畤@D"ؐBv`t`O2а`0-q#`ImrSbq3mР܁? \pq?  paҐѣacאB*nVНnR__?\X eې"c)j ;aBчf`jp Vo_ __o ejodoooh(qCG,gQen[bt @;a{-)bn ;a\ңd S'o{UzSe }s;A=}ܱ8k=p!db,kOr v.4D.{vşןȏダ*@hmkצQmujD 107 mDŽ;MΏ{:EjFv(7+ߟsvϷϼJ\ߒ:~J2_@DR;}20c3mKRaY~RaOFxcAn0"21;10;5=D81aED22a`a2^^~H?DPILDpJYD##iLsDPMD 2NDOD!1-qQDRDd1 Cϱ3TDUDbRIVDWTXT(YTZ)T'"[6T2kqkq;2]PT(}}_jT!!) SQ!“R q;TbpGD1?Z5MA3 21ٱٱ228{0#@@D"?)oS3&"b, _NڿXb޳jx{a<]ĊglTD~U;1BX~=B85unUWB8(`@7.?@sRs26u?@^Gk؁D$.G9ICُ,Oj+hZQSR>?$6 &X/ o9Vx!ާHh#k//sKχ߮ ` LainFn3` As7a/&.A4` mp @-yQj"@=Oa9H{F#PP(?!3"<(?W#"@0T3T/UC @i! woevPd* "aԀH"@2mDz1TLC~1 5?"WpEU;1/Ur!K8BORG_SE@ h[/">Pb E*V@@Nt.@@\.?@M&?d2?~DC?Q:IyR~=IyR=S6JD-xUJĤ-IJ#${X#S0P贁N%k流bQ"S<{#~1DE?~1Oaq7iŧquAOrEaoFo Nsno||zo7g~$J<8ɄCTJ#ⴰAp6,@!Jq2HO,O.ofgH&&JF-? #lt EЏf j8)av//dXiŔz13t<_p4L)iGIA@uPO80*ԗ8L@ly@:|@l6I֌",Iu9``6?!u=`+\)MSIOJ…ALf%uQ#~1QUq_sbf%A[M__%Uۼ۲1Qe=Z)V_Qoo8__ ot_]W___`p6QoK"?qFUR)a@)g S'eXPPT7PA1;Pq$6:Z PD H?@ t@\.?@Mu$+չ?Q.ɀtq> pRLpVtAaXm\׻FW($mvׯX ߱Df=!C,ٖ-㳮d&3>` AcC2SwCo or Pzۘ b( 3#SC`` eF=Pl N` z,!Zdd${b!A%acW,VП(/(<%b߽%-X%!21/$1?C?\9O90$AA%[-I00gmPA%'eTaF*!Bt,@raqȿV`h_<'ee*< 1/C/u{//V.N/wVӿYA97&81@F Oj|ώϠB2ϋ f?l6+=Oas߅P xq߶:゚Dc2DVhtB1m1' ?K]oY#5GYk}yOOO/w/? /U/g/5G//}//ş?-???Q?c?¿??? 8?7ۡ? O/OAOCP5cOO p8@OOO__'_9_ ƧL;kT_f_x__.֠/Gṹ__]5; B_ۡm_o+*1oCoUogovۡoo]ۡoooo* N`rh&8J\n|ȏzCXj2Dz0BTfx69ܯ3%/ 2NBOR>_SEgDVh`z?@@ҿ °O;#2>2<SW2SQtφϘ___߮J-  $T!;Mb]!s'oK]omo#53?E?W?}d/Ho Ug7I //@-/?/Q/c/u/Ҧ:TF/Qf{ 1lukk9tpF?8@r#͆5|Y7I%Fqu H%u- H )#E>⢐u8W'ExQ__e__/]uajbR^pǩ1A hA~bQ߁'gf''׊D?%+-/d<ӹ;(@!VQ?! * aV?trŗF7Q)yk?qQ;7`yw?<߁a7""NZ֙?P$ o?/K\°?BsWseXW?]4<6%Pbtq l#e#P.eHO9lOXo|OU@Qlւ}\ O!/F'[?dO$XE>N 8gWٳ?Q]OK?!qc]Dt~L?q]K(|?@M`%,3?Q穀( Z?u?+i0?iE;i2?D?%(Pbtuv>D@/ǟ%/7/I/[(D1k/HZl/|>@"L0)/ ?O3?YA'?9?C @\?n???0CU1 r??2͍M-OOLOO_!_3V@_R_e͕x_______Ǐo#߆\onooj϶ooooo"40B| ߐ$Z|J轏);M_q8K %M#_HZle$l6?@_jZ`[*ЅBP(Xj`u?7u#Fu#*B**B8 a6-,ݫy!eI!M+?r 2LE5tAU"bFZ][gE~."*Aӿ{ϕ8'/߽20+A 5lO/OAI]UOvnOOGa?O"OO_gO'_O^SuemPnt"1(Y"FQcuj4__<8{XR7/f!3bTgUfLfw?)4aUaQ!3EZF`dvaQZ7ݯ /  I/[/m//////k/?#?5?G?Y?k?}???????W_i_{__COOǯO믝OO_3<_O _{߯Q_տ翙___L_ooooB :oLo^opa~oϯϴoooo2AzT懡nq'6)w ᗥX,n_COMIm rupE"|>@@@3\.wv?Q߰;ȸ Ս逘a  `֊Eϻ)*|і` Ln~Co?orEzab<aSCí`` Fl?)` UM5?݀?G`-! A#NPNk\BvkqE(B.O Qt">V q > 95!piQtց8w&@ց&͆PLRU@L&@@H$0@@jZ?@p:NW?z悕ٜ[r;>o1>'>u`FxLpn а5 })\)\ْt@m䑮 d1 xF` Rudg `:""p P˄Pq`!z~X#k[ Н٥mdPJȬaubzJDdqty]P|x"Sd1T"VV932UU񺁃7 $ 9'$^:4$P򴑴HrrM=[$)>h$8{Lށ!⁏iRNNRd1$d桇}xZT ,@Ɓs!9PP| C8?'+5L88Rk!!'>/$'*/#d% ".4?%+? 6g?gL7r%^Ũ5}UeO$PDOO|%O _5!M*3Crcr ?`_@r3#ʀSV΀@萀>@ ցUK9jY%)!?70UuXK?q$:#tP*QȄjeqrXcƀ@"j(rPOF@畤@Ki?A$Lx7"tBo2P7!#kIwrJsb{03w_ â\_ ʁF!rdvdr_|H,3ELj `*!n2sڼ6qǼed{l @E)-3 cEf!(N '%_]o sEaGrX]ސ+b6O'8>N 8Ͽү4)JRwˋװqm D1(:Aw!EWƯدPυDOjPۦۧ2׼A5(}ۦۯTfϮDО %"}d?z0cS썠K2\c\o$#Fx3#5s>ϗ̲!vψϚϾѲBE==$ߗG֧A߽߫cB F ca„va̩lc{bJ\ϓUFh?F2}4aGi?!B3%JR6XF~)F9y67x,-t7tI萕6־b$=_ai9׮FO=M(·P-!YC l F`0(?/W 5 /nQ //1%d@/d/T&Zb?t?=U% ,&&? T&#&5OY ?]G?aOO)O;OMO1/C/ O!\]GoO.{_(U 7 oT_8?J4T&Z6p?3?+ooᔁ;dCiZoloVar~oo2v~ i1K2\i`(IzeWJVe Grޏ)ﱈ⏨^pʏ_5w?@[+tےOmV?@ѱ ?@{?@UiԄx?tlXuXunUk-?@Զ?@E|?@?ߙ>Ƶb _mVel?@֬@q?@iL (>D(%mVY-?@F9^?@e"9c?@?am+XunUI~ (M_:S?0JMO_mV?@s??@3jl9]yϰVenU0qN0I^cǐ#I eGxI2nU[O, ?@|:Eǐ6\,k?@kY;sߥnU@>?@ϧsT)$8gCic㯐otK@ &d&&Q*Q .@T&H$%D'x<OL# uOO}l9)F o]RU|_Ĥ(8g]Ciǡ[rFx@ǂϾ@nM畤@Kiě;dCiˡŷodl6/{J1 Y/CQiĠgA8bTPzd%%9uChsrA'2Kq0Cg!tۿ0=zn"8a` TextCo*`orĠzH2` L}a0inF0n0J3` AsA0aO&8a4;` 1mp*`1MAjĠG?Y?k?Ch{76F-poP(?6!3By789P>"_a%-ĠndC9uB0gB ;@ewpsaRm0 A f2 UcWDaPt0dp]dBa.ޠOY@UiRARHĠEQTǡ-P@^q]^/d%!hQ(=#BH/Z/l-8T p @ޯd1@@ eHcg7Wa->!u`uߠ`bcu adDhh@5!bbasbb%t]tQBSu]`t?BsIvSruà]y!(L&dc2k`Bg#GxHwIvRznwEr,!m 22?sQ?\._?@PA*_Qj7zr`BRkcPr0u;@d?bpE(=7K!TD5QU8# 03%|YTOow/ E/-*f6USrYyɠsB)nEsGQT__]k^a@D Ձtklf @@}`~o?@@r?>eF6Wa%{Ms:?;?RW%wyuTNA/YaBeWhl8ϱQTĕwwbaK#eB-I{d:@@ [>PjpwMsB?@֨U8dr|AtZq ($z˦fttfozfߐ @ˆ P!%I6 iDI[mѤϕѼB1^aOOn?6X9*???<̩wQv5o/oAoSoeos{dbX,@ 0@@H$#DYejDrSOq9??BP@ &dr ӳ & &R/> :a});q%7I[^U0P贁Nky9faAEk ai+UȻLK:LK&ՙLK7@aLKĮLG7JSPPPPZPPPPP3%7I[mwIώ.@Rd@RMrpewdaMs(? v)?0vAB3v+?Gv2B[vBov/T0(v vvvUv3v3vGvGv[v[vCov///U??(?:?L9?? OO.OqLOqmOOGqOOO\PPq Qq"4qla%PGq΢PPoqJQĔQ5* aN ]1PiX}T$BOR_SPG2}D`zg@\.?d.5?Q驀iF ;Ȁk}k<S̴k--H~Vj?|??ZxTe󗏩V???"q{È-]/7`-lćUϏ!É|nM-ԡD"®!ËN.Te7襊ZgTsāMoВ$سT{BN,,@oqɯۯE MQF=e$ -4DV {E- Te-e$MSQBa[_m________QKoo&o8ov\onoooo@|> @@l60@@~ @3@`e?$ob2s.pv<&6pvP&7pvd&8pvx&&ss&Rss&x2&<&<&P&P&d&d&x&x&&&&& 2D?hznϰ-?Q U@BgLS)cYAGYgC? |z="R}- /AA`r}A?(DV!^| //0/B/T/P_b_/@^Ï?/\ʟܟ2?D?$z??j??OO%O7O@IO[OmOOOOOL҅TҴyEWmiCQhz_Yl6?@jZzJBP(ŀÔu?ea(cu!aChuaCh1Z]_ oĮ1o IН1HXFRi_oqb 2⃀LFu9"qU҅d4@bEve[mLĞooeڱ^joqqa8G/ш@K+0aOauĎB#GT9SueڀmntB`2@P Z$6HZlqBHjCxx{<&!3 "tulI/սA/OD/O/O _ *VCM I)PA_+=_B|>XttWl3__$c?°;_o~ g逘NѠ4oFoXojo7B)*o!m ` uL nCoporCzNbO<NS^@`` F lpƒN` OўBAݨFRmB)BiaGo1 V \$H 2nLnRnA+l6o^l>ooُ!3EW??0o~>'l<.@5Gv}Ώï$6Hpl~11є/Ob(>5 8w{T1-1:oD;sD"H$@@p8+@@Sb45ɿnI)@uu`u 0AN.u R6 ;`A&HfT*JR@@!z^ @@vn*G@Rӏ1S*baX?qP?Q<_$Gq tiN01z@‰ #b161)CE%@rΒ?@I֊?шſ?_&aDp3tSu*͔TFWQQ]raNmJ1`AnBcAWi>phDց)ANק*(qLA=F+OSWA Qp@VI~֝ttB)A&H p nqrSQUT9fR?FO1QCu$ g=^}jyuJoAgo%յom׳w!Rp/#`A//_CKtfGU @=7/Яg `h4(@o:)B I1LOA.M=7OOaOsJLY-%_7_4 v__\_T__*-_o#o5o#YoGNkoo/ooo 5?1CUg1?Qc [Q?oOOBd7?OOXR XR#_׳OO(:_^pMʟܟgg=Q =Q=oasͯߨnv @@~{퐱?@p qsxu0BTL #@@>L)Bpq4p'@z3.4Frpgfsȴ؇ٿ ^,ΞPA=QQcÄutϒϤ϶"4FXj|ߎp߲R. "W<_ ??Qcے //d?eyq~!D%1)O"O4OEVj r ӵ0a*)D'LB<N`nsT //W@/R/d/v/PϚ/t_ϡC=$?6?H?\~???X????fxߞDOGޕ_OܜOO_/b.7SP/O_s_7/_-_/__oo%o7oIo[omoN]1o]1o?ooo 0BTxBP(~fxz^wyCcSN xx<]1A\)܉DaW._V_@_1V__^(:QE 1Z]1j|رEǟٟ!3EWi{ïկj&O'i//@AewfenȽ ?]1&8J\PϦN|#GV0x+%Z5!E"2GG'9etMq|> @@jпF/@@A @@`%ģ?z̤j;u`?u72W462W173FY46ZZ46n[46\46"(22FFZZnn*"a;51 "bv! zpr2po2s҆%1Fs# !A|#ebup"2dO8@21fV#W1xZۆ%xnqxԂYxԖP-CPqnYn2nT4nnZnnn12\A<25?NO 8J!>5BwOR_S`Gw!A E:LMpxI$ZV_j?@ %%hFQ=t pR tTL;k u;5(;63=y;3[l?#a%@;3Tg6:qq##0P贁NkUtU;6c2x=1%2A%S4FX߂ɟU% .%EE/<:/aE];=K)$Ҵ3,@;2q5/F_) '/O_%;??_Ձ5g,??_?Υ)O;OvVNxOi!77]5-رs_'t)с |رر_t$|UA AS19?K?]?Vh ?BP(w`F($#5G Ӌ#,ٖ-㳮dv 3s AcpentCo@orۙbo(Sru`` F@l@~` 2ug@Zt T$ a'&"bر1Cرi{O //A/S/e/w///////??+?'o9oKos?ZO%OO?K]OOKO]OۿOO#OOOO_#_5_G_Y_k_}Vъ_ѧ_k_(__hBS2oYohfl6zooooooow x#v50wm$t` DVk}I[ʤɏ9- 9.@RdvkϬП*8N`r̯ޯ& ؿxnr//ȿ//??dQcU?u uϙϫϽ( *Z:ߙWAZO~ߐߢߴC-5/%/7/ kep8@.@Rdv ƧL;k栵&8!e.֠/ >Pbp oA+=as7v6|0RA ACUgy?Ǐ-?Q!u/5_G_Y_M/4?/X?/[m//%?7?F?3|?? OOCOUOHgO0[LQczW$WLi"Q@ d_pN(?@կjZ ~ۀBP(լ&Ӕ?eQuQڔOuQZ]a6-_ y_ Ҫ.o *`1r)I²1r1i>o RLau&qqUO8wtѓbFbzI2Dj}oojoqwvujD,4Fj|/Qn"OUdk Xb'1o><DudS`gmd 1Ps@ɿۿ$&Hdj6RR{RwwvK!3"'&E@(/kQq\XDb3a-=dŇ$+G=i]I֡:PX~ߟ?@@U35?mM_qU($z˒ _Tz#o%e812]S&F^S&FUBFFFFFW(&  _Yi=8uom[enڑQRsFoR AQWTAWq7/I/@ /3QAqQ"?/?ϧu͛___נpF?7@@r8s??OO~1 )=Krru8qK qӞFԞF?QK_]_o__:L^pۥ5$6 ǨALp֢Qof''{d?%+-?%+ΏE "4F^HnjHޡ OKH 86yЩy?9s4Fh@@P/@@b@`ٿ4<iuޠw`u e@A.gu o t bU@X, @@_p2Pt:N.@7)pYf@z3Dzhpb !+&/*/<. ʢv"-(Ԣv"G#3 5Dю0j 3Vt[g tNe@Z!z@" 2b 3v"HieGG*D@r~?@Ig0k?]4e5?J&"yyF]޴aJ1RCon#@ect!@rW'@iCh+@0ۃ6ك㪑 `A`b5.-GYpN* TyߋԈ,3T/K?V_㵱AZ* b?TAHջޢTOOQl*XJ5;?Z^acuao"՘BORGaSFG GV*JR@@P( *_qѕө߯'9K/{a̿޿a(:o^8o\oϸ6HZl"_ߢߴ2DVVߟ>P'ٿdvϚh*~MbV}AG]: Ÿدt]đwt:Lǰ?@jZpl6RBP(?唀61u.0`?2u 4nrav#$6-.2⡂21}ABA Q Qbl?hT,@Zඵ1P!q 觱2!C7n?vp3dHp!1F Kl \.?Z@(M\cF@UE@^E5e4?4"$7*?3.5c=4O5;O6µ@U_3OrUդEda_sTT__rUW_oĵ1ܣ:/Ssr(WӐpqjhwo@rHCH^c|fb@w |@eIi 2rU`X=Uu?O'4OP.`!asivuErMsfBz$\ lO~OI62 t! 2! `!L#! Iـr-b1@3ـ1ho \Qo| w|^ww1 |fBG\G֏ˏo aai w( /[Af| \ޏ&1jx.@42@eGNh @A%-/ A1w9$ '![Yk  sAqCh hmIڠ'b02O0#4:aJ4ροi0%HFƸǛ׬QmD1$ 6=Ϩ²sNJ1AS¿Կ@ ώ߁@KjLۢ.=1$y뢦Pbߪ@̮z8JTorU$C2}O0@cc蝓K X!_ X!N43FxG []2,pn#FjZ-Cp!*͑=AGLA/S%r D!R& r S%r%2S% Av?srS% - ƒ%~q%I?W: -%v~%' =P~Wx5;81'=Iu"$;zQpK rUҼT7#$Lʦ~#jI(a2b0FI ^OJё)9OHCάD WD!*?Qײr|pcQ QVSsiaVp` TPPavplShitH,ppbaRTq}p`OYfr]ea,OKn qaaqzgB@Fb0wio2c!b_=,p@`{q! AQ! ATqm~? BP(?rpnqn,rqAa`Aemt Bh7#IEtrbd!d!sHEtVIRt.J_t A))oLytqMt ANt1OtQ1139QtRtjaPsTt!IUthVtWX."Y"2Z/-R[<bqqAbE]V AAA A _p!QQ/1PWQ A& ;IpMt!oڊe}q9Paaԗabh{6S@Y.r@`0 ?B/: AYc,RhDrq^f?+Q?(c" AlZtAUAarڈCrheʞU]rh.!@p7.?@-n3bybfKl/@\1/jqjqIaԍeݝPqV"[ Hڋ6U@ ?"?ؖ/PrSڊo!Ir e¡ Ah!?C-$Sq,.%FU,b +=OKϑqasϗϩϻF|>/1o:ߓܮ1rߖߨߺ>%A9:!C棿Q_>%B_ q!چqȹhswrFXߏeFh?F2}Б4ai?1 B/}%JR2TF~߿%F9_y67x,)p3E~2^ 9/[qe5&FO=/M (`)U?C0h\,F:8O?S.1,?/ja/// ??-5dڋo8eGPo4OFDP6VFlO/O ''С7t?yVhRqnz.uV e-GBleԏR $EvuSFRuCnڟ%ޟZl~𢟴Ɵ[5w?@[+tێ_ifԩ?@ ?@{?@UiԄˆ?tlXuhjek-?@Զ?@E|?@>Ƶ^oifel?@֬@q?@iL0(>D(%υifY-?@F9^?@e"9c?@am+h߅~jeI (M_:S0JMKoif?@s??@3jl9]y߬Ruje0qJ@I^cà#I e}CxY.je[O, ?@|:Eà6\,k?@kY7o۵je@>?@ϧsT($4w?ys߿p[@ ("Ͽ`6Q*a*Jgo$ 채Tʝ=!H!TpV)zLP<$,@Q]q߶HAmbVP )?Q5Tʼn׮>Aa|__jOFXIOOOHE l3߂$%oo'ґ9u?@T9G1YxR?$%s?uA#5 G6x6}LlUT@g29U2bnb ϖ5[z$Ŧ<2:ؑ%'@_u/@jqB+"9G'O4_'Y;_$T_f_7O____M_ oq_gDS̀gm̀nt 3ְ8!@Rdv"!j oo>|>x{Xw?!v!3r:w;v2v]ODGF$ XU/%#b>{{yI/[/m//,6bX,@@"-@@H$DY@T? &ǂ//C2ٵ8ssdqsSkvfegmvÖnvזcissÖJÖזז3&8Oʁg`JO\G`Ƥ"?;M _fepNvrOO.O>x S8Ϸ6dqg4AׅÑ컶Eב@gEPЀ07kЀatЀYЀЀKЀЀבovo}(:Pg88T960akBOR0_`E0 5%֏mT`z/ Q]t> Ÿ;`t 1ͣQ߿<SC8=ֹ1g(:7J@ӯ\OK]o(d<8[ݺ8ApwO]OOIHDow/Y)tewhqpDᖴ[ܗrLT$1 VB,@qU=O\x-r+//񸥡%/?/I?[?\v>Hn?v 1CUgyߋߑdqdqOߤhDL`A.Y/k/}/v>[|QөSûZu??E1CUg#,ٖ?-㳮3` Ac`etConorlPzۍ_b(@S"buK``` Fln` Zo2eaflRdq1|ICvB|$6CqQc"Cq?탡VL^m'/!/3/E/=_O_i/'^`?/\gy#?5?k?W??V?H?OO(O:OLO^OpOOOODOOO|sgUP p=7-_,u_K_RT*XTӺ_T_ 7a!o3oEoWo·#.?/oo5c o zoo$6zUGYk}uUU8J\nȏ#TsT.@mvП<'fPT/./үd/v/ - @WL!Z Sl~YA߮C fj=;=oHm-%7`rvm#5GYyGYAԅϏO;l6?@?jZհϳ֟pBP(Xߵհ곔? e7}uvQXu\QXZR_ jl_NĮ_ K̞V 7rY4_R ҀLce(waU҅dob U []_oe55;/jKHo㪀aQo .@/U}=2ɕ(/ɠ}s᷅kQ%H}s3WxjD\kWSe/m6ntP2@_@gyƯ,د8jQc {r{K!3a @ߞ/An OG+OOOaOFCOME I~@O2O|>HdW#!_3_S;Q_c_E逘____wϗ)?*_v|mb` L^n6Co @or zۣb<>Sk0`` F^l`>` O 21@6M~2#iG0oA1_F^Lgoyo#ߝo^"oo>]o"4FJ\?&_}.Co_.@Rdv]/o/_.|oE,ҟs}#rx5dYyůyAo$EJopSՂG HBe)~0q" @@k۰V-@@~ @@`S=?EHr$R6qp熫qꃿ1ӦstuӦӦ*b{89!o<ͿKq.^2bv~ !z# e3OO5_1e; eՂk_}__ G s~5qe1e˓%@UyAge8"rP̌ ͌ 1܌ O@rތ J@2 q,P //G7(BC Rx@_4Ex@N/%7/t6j?@ ///,q ?pR ;8-$;8L;Gk 6F38-!X?j?|?B=s1`r??=iM&ʆOO@TOOLT_T_._ T_f______o},o>oPoboooooooo^E?#?Y?k?6H??~?Ə؏ 2DV@!%1CUt,&Un A1/// ?BP(&8J\F($xӉ3,ٖ-㳮 }3R` AcpWenCl:zb(Sbu^``` FP@lV(~` T21u@@t¢"Q# %7Iav|Yk}R!!*<N1Or0/&/8/J/\/n////___/??>O4?eį֯|?? ??Tf OOBOTOfOxOOOOOOOF:_: _D_V_h_ zXB2_ew_fl6_oo)o;oMo_o` x{oooooon 0BNҲ]oˏݏ%7I3mǟٟ!3EWSewߟQh/A/!/wW/i/ѿ/+=OasυFsW1?P zXC5=hzUp8@߹ ƧL;k.@Rdv?#.֠/pT 5s }z9K] oz1uXfWl!3EQB1`r7}1/(:LpB$6HZVOhOzO/T//|2/D/ğ֟z// //T/ ??.?@?R:]Qc?u??J9t6<7xyEGmYCA@1,>O ȡ?@կjZ2-~pBP(<Ŭ2GÔ?UڱuA-puQ{Ma6-O y_ ª.1_ Rb)jRb ֔rRY__qR BLQe9aaUp҅8TobgRs[ת__ޒꨩo@MqqQUg/}r"pv }>y 'H@R3_9]DȤSPgmT 1@@c@Ưد#$5GijV4**{O7K!3&'f@I3rau y4s@#N-͵ $ڷLh-ŊMIqP:@H~ߟ?@@L#%`ƎnπϒE($z˳ _Dz#4_FU8!!2vt6wt6U266666nX& $AO3zY=Y!_߻\Beݴ*cg_* 1rGqUD1 WXj Y&NIr1r "@/P/ŽOOOpF?7@@rȄ(c??&?8?!! ?)^;rru8;.xaӿ66`A{lO~OOO[mP%EW A~Qf''t?%+-mՆTPbtΟ(:L^pʯܯЋG_Y_k_}_____Y1q__o!o->m\('qq\C o,Рh (oooT0g 2vTf~6t6rT0O#AUBz8bE{zɰ]`@`At% 1CU lse#P.U߿/8+X/BT7#Qlւ}//F'[?w1/% 8gWٳ/%?!qcc]Dt~L?c=L ?@Vjl6@BP(?dƖe?ϭru40`L47?ruiQ4RbHfU b8R QL@2"UYr7C3b`=u}u[ Od{|{rz IZ8QJoA/-A/xbici.-gˏfeZ,d);-yݏ,"F~TLUSe m|n?t 3 Q_'_9_K_]_oQHt___aΥjD({ v!3@E2uEG}-Tu_S7@Po0oBoTobX,@@D"H$)@@H$eD@E?Af¿oo׈bdc|]H]b\t`p]f]]8>HH\\ppJa"a& j_E 51xa%"2 " 4eEK[m. HQ\ta%%1SEFPpQ\ZqK]ů׭1i_[IuSH_Hы)yz9`mBWOR_zE h euχϋ`zM@>Q2)t ?°;5ta"㔑'<S%' }YqIu 1 2DW"("q<H0x$EHDLu2/D!LEw&ON%G@5N:_EOLg0:l!3%TTTq{qP;,@1q*(/$/O6T2W(_5T1vk}&ooT1Iuve\\ooT1vo06K~mDA*<N`f}q~THO9K˕E09i1oCoUo ` @@@ .@@x< @ŗO Wʈ^u u`bu !0@K"qPG@1ϓ B 9{B*0ʥBxT#?EBP(D$0/!/0ԧ?ޯW!q6S2.?.@a1?x$ চA$O+;7`BackProundClrz۲pb@29I4Q?<*{G?U0-|?Nu^AqnD^YʉO/U/M*_@2IPO#n~L5ҶW#7Az Xja*!1QБډaDqQa u)L6 L&m$-@@jZ?@p:Nk?ai?r["r;o1'}u `FPo?uT5_pb%)3&"` S݀aowd"j/|/` Fpl]@Pt en&Sѓː`` %` /21x1` Ac n ?"4 )/jB`%Taspr62cyw;??#!`P)?;d" +Sm0y]@e&V2 \;XOfQ@s tJ1K \;YOO.q `)DpAcm0i'6q`U)Mgi{@il1K*6xjH,?@V7F t?DȰ%ELpn j?5b א@ym$hV ^hV ɱՠF `% DURFQg;3 `DU?;` DU/6ၜ6ta?b4ɓKrcAT6ῲ26Tưawư>Bbv<uzeпQqưqt҉ Hq Q"6Tɲ113t! !iQiQAAщ^8t9t":j›mb@YYbѱѱ >4bvр55@PdPSrƸT ,@m-0|Ũqpm6mHqwC!ZA4Ώp[tmMr 0Kl @(…Z66F@[@d*7Tf ȏrt*Ǐ Kr4&^ȟ؆mІY&%[uw'jŘ௱c&me ce湊EMrrqmQ !~RR澿@rгW䶱@\V@  q/s6r` @wU0uUB=a4pP_qkAţR?l4r@ѽ_LtpB2p#pkI1PrfbG31PP~R ŢQϒW UR FR+Wl[prP[pQ"HQ1[RHOqQaQ |"4F咁"8N"ӐRэǰpe[pۈ @aE-O# caRD N 'RA{yB sac~\rGbpROp/C TZ Qj ' T  _!P/E5fR1Pm'D11DV] Qa?s?P*//`kjl;5;N7];QD//;;OO?p//O5_//`B'9{uf&Y}dFcK*_2xᠨq?xᡑqdFx񨲄qqHevIr JIaIaasLđMe NObSQPRĊf1T6qUԒVbW(ԧbX$5NrYBZOMA[\;a]vQ_qOwa1a1a@ ;iRpmĨ&Y11{V@Y.r@?`0 ?OyLrq~C]+Q#?5xS6rqlzĤܑUa@ؤcȨU}Nq!@p7.?@-n3270Kl@\QOi݅p$aH!v6r;+U#VU@_r?1cOpes aiQ q5_ec @MDs,NuU /FU,b@'9K]ok*F|>O?/Q/8-Z/ !EOz{ 5w?@[+tԩ?@ѱ ?@{?@UiԄ?tlXu6Պk-?@Զ?@E|?@ߙ>~(el?@֬@q?@iL(Ï>D(%߉Y-?@F9^?@e"9c?@am+/ՊI` (M_:S0JMk/񿉶?@s??@3j`l9]y/(rŊ0qjI^c#I ecx?N[O, ?@|:E6\,k?@?kYW?1@>?@ϧsOT?x$TQ_ɪ~@ x0BeFQ*"9 HO&O8OJO\OpH$ÉA1`Ux<yhO`__&_8_șO.f̖O_____ oE_bmy_.fqܖ{oH(Ty_w ooob+!@{ǞQ@n~0Kl@\.H6$Wa_ɀ9%d?l6M{f uŏˣiTZgTooi_waˡ'2ǥqƐ$_!Կ0UIdv"T` TextCotorTzd ` LainFn&3_` As]a=&T4` mpt@u8ˡpEs Ÿԟ 3E#UgyIeyḟѯ$6HZl~ȐaPduUecPYTpK!"LIZjZkypEEe۶F BP(-?۶!3ۺݾ|>JZrwW^pba~o 7*DL[pAO'O9O2D>ϷS|Lqe]swZu?E#ӥC,-㳮@Rd3 AucEDLb(V̀*S=u/`` F]lD Lk` 2%ߝ ݝ±ab+qSxaG"ba //ޝbaE/W/i/{////_/C]vz=]~)?;?M?t???????Oo %IOGo~]_O]|#5OOk}'_I_m_Ȋ_߮_____oo,o>oPocougoCoo@WU=7oĿֿ1DKRT*S_vt ?qեC.֠/?Qd5t {܊h'9K k}"἟Ο*@SwLaAc(:?YAXj|C "= 2c - .@RQqQ//O9/f2n///,// ??/?A?S?e?aosoݏ?MGnԟO?lۯCOUO#5O˭O{OʨO_$_6_H_Z_l_~____ \)▏eT׉Vg~yTay蝯o9l6?@OjZЋ[?BP(Ց֦Ӕ?u`9u2qTxuqTxBZw &( B ̒ZB"icyp CLWJ3UⅺEQCbVu!e[}@]ԯuozDڬഏƏX/ џQ\/hg}CooOs°; _EWi{HS)*2}_` LnwCoor@z_b<_S'P`` eFl^` _,R߹QݹVcR "yGAfl#5YBQtmQ<T}Fo}N 2DVhO+OAPN8Ŀ}L?QFX/ߟԿ. ϻ//5/G/Y/}//5aaKAĮe?Zmu,Tcs5WI}plB @@nf+@@~ @@`?ĹDVd`amg"{W_qjyuxvqXƎ]gUg{{Uƕ2@zx1+\ .Bbvs4GAz_߱0oCHo Ue{V bep'9K 1}u{뮕W5_qOEyqWuq5a8ނPTހ@Tހ `cހ`ހ\t1CQ?0aCQ???cg8B*0R4`_E4` OROZVj?@ uOOOLa pROKKL;kQFS_&_8_=fQ__q_]%mBoToooloTCQoo-CQ"vXj|_9? ?BTfxҏYk}̟lbn?OO_'_]_o_:L_pʯܯ2<{{o 34U* A1CnOOO BP(DF($4FXj|ES,ٖ-㳮3Vr` AcـentCo orbo(ЀS;ud`` F `l ` 2텀@>ˆqYC]ay\[/'/9/K/Wr{f/x/;{////// ?_.?@?R?v????F???OO*OnQcu);M_I/˿ݿ!3[B ߭ߣ$/????3EO%O{[O /ASfr䀣_6xC>5$?6?H?Aup8@cu ƧL;k 2 3.֠/(Oas 5у ؏9=O`r b$6TfxO/,/>/P/b////////??o$o6o^?EOOO?ٟ8J?O6OHOȿڿ~OOOOOOO_ _2_@D_V_ery\ TW4g)yQ@֯Ao]?@կjZ~BP(լ?ruua,ua7ma6-o yo i.o ̷-rPyv bLq|qU,8b#r/U[@Zl{LNTՓ dG v#GY/n.K",2AH מ5?ŜaLpTSpgmt 1Pxl@ϔϦϸy'Ǭ$%jJj/|/ܕ{NjK!3z2'&"&?H.ȕ5}T`?3B Mf ߛ߭߿GlIv:a`eh~?@@ŭC״EoB*%fD>yU|8!乸I#˿ "tQg Zu?P{"AtQ"ʣA{"'Eג%'tD fC,ٖ-㣳a?>υ=3` Ach0entCo0orzs@b(s@s@ySS `` FYl0s>` ?2|5+߉1݉6NdGqσϕoB{!6<ϺTN MN1CUgyߋߝ߀WSI&f_I.'9_`_o_5c[.oII,!Wim5Yhve 2A¡ys Q*%py>'K9\cu!(u!(88QvLs/sCU%//C)C:&/u-ehhz,==\Wi(@rOȄȘýڟb&{( )Ӓ+$E*…uׇ8+!2 /2/'RA=OOo_0_/E]-jʕKQH`!'HAbQWf''B4?%o+-銕D<ٌ;^ŮڥVQ?;Q * aV?trŗFQ)yk?qQ;`yw?<a""NZ֙?P$ o?K\°?BsWseXW?]4<6%ai?y??ե=Q2QU_?ޒU@ ` @@0,@@?x< @gù w9J-uBu0`u |@b_u ֻ P@", ȀyQc*E VHOZOKIR՝˜M2B?DŽBP(1BBҥ @JTa8P2qgq??\.k?pAQ[7`BackgroundClrlQ~`byUդ8=5_!ZTB*Dh#S0%|#fvTU7R??Ee_U%_*ooi@Ee2KR1c1V4LR@ Q4XŬQuS x QABʹFѩ9/<71/s59Ld2L&@@) B!+@@jZ?@p:N?8K[Br};1'up`F{?u|@_`bjRpQG@VBc!}#9}#(}#4Q}#Cz&de"t"o////ZRj5\U)2?(797ZSQU!8\ZRB FBD"aP=&`Q6ayN`ACB"#2ޑI^BZFj"^B"^BZF2^BUZFZFZF ZFU ZF ZF ZF&mBj, _1_C_LUS^VmBR3J^V4aaSs/o.̀QvA{Gz?UQK1& "` SSaowR__` Fuilްat9`enVSGA`` .e` Yo2%ߌa݌a2¯` A c=`n 9`|od2 i6o 1`.eTasp r.bcyko",A7"9;"c1"!k«!!GtO11ɢٴ:BT,@2`5"1A30!C 2aqDϋ6p/3 (Klį T4@(AAF@@Šҫ3"+n>@(2*=.+#M+4pߜ5.>N52F#ՎU0ժߝV'#㕀(A"O/Ӈ1r_ぃ!ia[&4@rFZ'@jE 7@__/-3"3eͶ10UGpur 1j0_ ;ci#W1n3/$²tMK2My!#MIrb3X1Z !1Mw EE k£0 4耝0Elaa$1#& t*///$/6/H(!ڌ0=&e8ѠJ$ @+9-. :$ 'T/;?ŷ/*(?O Cs A7 -MOl|+g'p0bO27N886;F9<5HKOO862Ka_s_dOJVCO _LOFN(꧀]+EBm5y*6D?)1?3;,_>RHO O@ O"ooXOjO|OOO$ooGGj?k86k9XgNRk7_{_{86&{ASX__.o@orb>Hϲ}1߼c&~-K.@@+ڴА/F/xg@ еFj/ZÛT8c!GOJ\nAc ?rdYѢrN m q߄? m2vu@ PqѸ_@߁  ᓔAKեͣN <q#1߇\c+ߌ\c^g!>r76_`ُdWԁ`O:f",ߕnzB@P0o!~_=@`{ዝ?T/~? ?BP(?Sn*PPɂͣ߁E!!AH@I0J0LWM N)&nO6v))!QP R`]#kkqTw&UVIWXĢYZ[11]%_!||@ű!//!;G߂pupPuquq⪑{@T.r@`0 ?@Yoqǯzs]+Q?H&'lARUNpPA`.Uġ @p7.?@-n3ڨ%(Kl@\$D!T$j%s-A$* @ۡ/Up/7U@բ??n&>y(y?w9>5|?pD!߁T$w/X1N7y=FF5D!H!,Q'ĥpH!>5*?O/,FU?,b??????Z_lRO'O __-_?_Q_ZF|>/__]O_)LlD!noo,o>oPobocRԥo.oo)Os]]v9Ov+=Oaskԥ_o_ujD!sNT$~p^I.? j|__%K`Fh?F2}`4ai?)~%JRȘeDF~GF9y67x`A,$ɗ>5۞`mx"5s ȘUh^$ϯ'[;M_qo˟zg@FO?=H.M(I忟KFCpFȇ<\Ǚxon,Si{õҿFA;Ml#xϜ#ǯl뭛9߰PAuߩ߻ÿտ'OtXp`ǝ_D !&f1.&&aqI.ďx_ 6//_O/j茺@/ .ona p/C(t/:N//&/8/J/\/;5w?@?[+t$ԩ?@ѱ ?@{?@UiԄX?tlXugk-?@Զ?@E|?@ߙ>ƵOel?@֬@q?@iLf(>D(%C_Y-?@F9^?@e"9c?@am+_guIc (M_~'P:S00JM_g?@s??@3jcl9]y0oBh0qI^cY0#I e@x`o5[O, ?@|:EY06\,k?@kYoYqEN@>?@ϧsTSZRD$7 COuO"@ ,OULvQ*6X~xOH$Oօx<`Y޳fxﭖrB :#8J\n؝R ~xVD(YA%IMI[m8١,@Ǟ@n`~(Kl@\.~xT-As]AUIO"OFDdOl6f3{ ֑x.;Ϣ̣iUAT ~'2q4мBd!60Q@UA s"` La}tnFoЭtz0z` TexClr3` Asa&4` mpь!8-AVDsڽ)T̢ E [ju;FoP(?;!39TUsI;^>Jr<b 7 b*fpDZwqANAA` RW*ieFѥdfҰwgp@t e emА !kۼ۸MarHMaT"AYA{]AsѨ#Me%ֱP TaC=7p@h@˷<ڿ-tKRT*?@@&d2?@M~ɺҍ?QA]tQ@` °;7?tU@Ԥ̣!ա)5rE[SVDN&N!AAB՝.֠/Ṃ";;ɦӥy/"~ Ac "2b(0N!=u@`` Ff` ?&!&UA}G1)s"`{3թJҠN6M<#0P_贁NkBczQAL@̣g:qTQUӴ(QUHW6%(T-SUO͆ N_UO-gT(5-f!PLqq}&Ҥ,@{1}q9!3sO0VhUWk_y5Oau[krooOa[eA\ooOa@[o0Vd1}J~RW'W(2a"R-nA2DlEXU0BThh ƏD1o%o7oZl|>@|L~YAC //'/9/K !R#[!f/ա=z!Ob//,X=-i?{?<????6 OOCOUOgOyOOOOO{$ς͓'_9_K_ρ________/AϿGo.w[ooo'%Gmk؈u[u(:LH^pĜWж ɹ$"@k}ߏdzl6?@jZMBP(?n\u"u"Zܟ KL0 ̀8H2hH21i> ƒL!Pws°;۟'?逘%ٿyz)*꙯X/j-D1` L@nPCoorzۅ b<' #SCs/`` F@lɎc` Ղ߁߆b/R ѩGޯ/, A@'/K]afx a/??(? .Cvaͣ~Ϧ%$6HZ?Qg׵~^ߣ|ew8JϿπU͢#FsA__a_B__Yԕo,Rz瓷ҙɐ$u}y.Ar @@pv)@@~ @@`5Ֆ?'*4֍}qӡֵ}rqQ-3.rbv#mmqz `(Ũ*sn1u+ e.ҸM_q Ca}quuuO[T8Pz|||1|rc|,| |ao`Bo?ooxUB%pRZ_PEZ0ϬxDZVj?@ |B pR{B2BL;kv(B2:L^r=ÌBKhz ќT]6H~Ưد_o 2Dohz¿Կ???@'ϒ߈ϔo;M*߃`rˏߨߺ&8bb!1/$1/!?3?cttGUP0 A@1i??5 ?BP(???OO,O>Oj@F($ZOlO~OOJ$,ٖ-㳮OOM}3|` AcentC]oPorb(Sau`` eF2lP ` 652ŀ @ "dsI!__ CAƁ;_M___q_}__aպ____ oo0oTofoxooooolo,>Pbt G^p6H~$6HZl~Ɵؖ!$!&8J\B@2lGYk5fl6կ /Am x]oOOɿۿOOϹ$0"?QdMw߉ߛ߭߿+_Oaso_'95/G/Y/h3J_oo#oYk9K @1CUgyؖɏ\Cd05/Jo\onog/ƥp8@/////// ƧL;kA?"?4?F?X:c.֠/Qu??=5 _-?cuʗϘ?:9?OO'O3BOTOzOOOOOOO __._R_d_v__$߬_____oo*odDӤcx-鏣5hGH#s0P贗Nkvl+CDtl6f Y~+D+?.vyNQvyNQ8SRA\Sy32Vv6FV"R(V2V2VRFVFVfr&_+QuekO}OX sa g@dFQ)gam2O/OAK,Qfu_EZ?l?l>tpF?G@@rȄHsDOO_[vatgA )owut}rzrwu/8wu}Q)#R2Vr2V*,Q!3C/H 01EĈTyKcsYqAlQf''Z%+-ۓZ<;auTVQŦ * aV?twQ)yk?X`yw?<7""NZ֙?PZK\?°?BɝeXW?״ɝ6%$g ?M=oW,x H ?QcuP?g v~FVtFVM8f_U%BI5bb,QzPmP@*>993T885r11@!!8d9dA:)dіBRJJ>]dҞa:<>Pfnr`J cdR|R=-T,@V aQPρqQC ҃!opc˄Ax[2 ` W@(k^^F@u@u`aag!VodRTg*ocebn4#ekvvfwӱAu ڏӱE3EtGaUZjn[2ra 1'22l^@rs㠤7 *@<ZHЀp@bѕ@yaXݱRaŗ}Uu~"@fdQ@]ϐQe!>2;r]Zqwub t`P2`Pϐ,aѭ#`PIZ0r-bpp3Z02 $A*7 52H2aTû7rG0G8 1KȨ1ZŨ;2q xʚ1/1ƯG '9K]oaK=߳axwKڏ4{ǏeGN @n-xŽL [a29m9 'H2j߲ڢ 2 s<ȧ</I#pbBP{OBP=Xl}1P}&2?JynH^Z0Qm?,PDZ1m 1a @/ASjG^wzm./? /A/^?"PbUFb}opc̓1ݠKS?QޠQޯdcFxxhza 4!qUނi& =&)//&/UN//PHI/(W;&Q͡aԇt#߱͡?/?Aѕ_2Fh?FxB?F4aGi?aAtx/?%JR{H_FbX,Fɿk 2,rԹO|G֎N X2_M(Հ?>#{H5EUGiԂ_?OO$O6OHO=?~O7*^VFO=_ObFnvrONfGYcP}w@O=_O\Fd2L~hv L`Yo\]zI\+!Wooo.o@oRodoveGo;/o^fFT/eO]lPf+_kO]fhVfz_]N=c'_4(J\nvooO?ڏf>X' ~#XO_܁p?@Զ?@7?@?ߙ>ƵQ?@֬@q?@K p(>D(%οz-#?@F9^?@4"?@?am+E(ų&-O?@ (M_?@:70JMՔ73V?@s??@7\l9]y~ͻTѓpI^c?@BP>x=w峕KE ?@|:En6J&M?@kYЀ$Z@A?@ϧsT h$}1ӣ(կ]KxJ}r<ʰb 7ʰ ʰb*ʰ#za*$@`TʰZ~Wfi8eŰC 8oŰnXwŰsXg:eB kSRdlhStGBHʰ T L$&ET֒hSux%NBORG_SE hd~g٢oom̸ERTV*JR@@RT*&@@id@M&?$Ү,Ź?Q"h丹"FX=PCfGCvv|VUGD2x-hGCFՒÈY̸]x#PC0P贁KNk-VtS~$}id.W$du%`omo$U$*Om%Vߵ̺g DZפvxɄ̸gTGADNNUbbvvUg&OpAO5;%O7G`~gȰ?:(:N5eKNQraOsOO nSN弴bǻO5vǻg弴ǻǻEǷO5EbP4@0We4@4@b4@Y4@v4@4@4@4@oQcomoO5NN<լߩ9Ѐ fa[skBWOR _`E  n%{qQ`! Q8tQ °;3;tQ𨣚Qӵ<S=_jv1O5߯Jճ7O&(8JB(L8#ns? 0BTf߄bߒbߠOvhDL`A 4/F/X/Qc>rK|kQӄ|SÖZu?E 0B#,ٖ-G㳮_q3n` Ac`entCocorzۅb(셠@SRu&``` eF0lcn` o2e߼aݼfղ #Yv|o,>}dv?#|V'|^HZl/ /_*_Dh/'^;|?/|\BT/?F?2h?⯌?1?#???OO'O9OKO]OoOODObOO[_vguU@ `=7_P_cKR/T*qX~/_T_a_o o2o#.֠/^opo5c ojooooz$6HZls%7I[mb#/N'/ HQcuϟ'A+h/ /?/Q//;X/|Ŀֿ Ϯ1ϔG1QOPuχϙϫD1|>@rWL!5CGY߹YAw߉ߛC? ߾Aj=#m-;M_qv1m1"4?XFQ֍NK*<N`rOOol'N//L b/t/BT/// ?۟1?C?U?g?@y??????<HµoiuGYsAO,`l6?@jZBP(ųu?UXcusXu7QsXaZ]-_ EG_)Įa_ y1aHrY_R b҃Lv>eiRaU…TdopbuU@E[]|__UᎵ j&#ocaQoo8w /0} p {[}N強`FQ@Eƴ#}Nr2|SwEDiF2Sue mntrP2p@:BTfxĊůB8j,>cc{lMV!3<䕜y 1?7O*O HdG#O_nS°;,_>_ 逘~d_v___gr)*_Q]=` L9nCo`orz>~b<~ySF0`` F9l`>` ?2 16ž(Y2YG o1:F9LBoToxo9"oo]o!%7[s&_}.o_ -?Qcu8/J/`_Ϗo.Wo ,^pewN}ϟMx?4Tfx֫TAkTE%yoKrQCyv)Y0q" @@r8'@@~ @@`.? D޶#r-6dLò††baцf¦†֦†v|¦¦֦J֦V8BG?eQ|b z~̲ u#gO*5:1F?weްF_X_.j_x sY5Ěua¡n%֡vUTAguU8bPrQv@v@Ɔv@*@Xv@%@bv@v@¡L@G(B RS@_ES@)/q/tZV?j?@ ///,L pR++L;k(&!33?2W?wR=s1;r~??=DMaOsOwTOOLwTOTO _ /_A_sυw___~___6oo+o=oaosooooooooVhz9 k//?$?#Z?l?Yk?ŏ׏ 19њO ]U' A1@k/}// BP(AF($1CUgyB3,ٖ-G㳮3SR` Ac`entCoorα۰b(۰۱Sz8bua``` F @Yln` 2e@d‘V#Z^YYvX|$6HTRcu8?+=OsC//'/9/K/]/Y_k_}_/?W??/}5?G?ůׯ}?? ??U? OO1OCOUOgOyOOOF鑼OOPO_!_3XB2C_0B_fl6_____ooD` x4oFoXojoyoo~'(;N`rޏ&8J\Fȟڟ 0X? !0B/"/xX/ҿ ,>PϯFlGn1Ϡ?3XC;5!3E>ߝUp8@`r߄ߖߨߺ ƧL;k /.֠/(L^p5c o6zo_:Lzn1]oufl Bn1+mn1Qcu/);M_O!O3O[B/ //5G}3/E/şן{// ///// ??/?HA?S?bƶv<o=T1y7&Y1@ӏ>OM`Z?@կjZ~IBP(Ô?oUuA)uA4ȟMa6-O yO fª.O ̴ b)# bMr Y_*R BLQyeQaU)҅8fTob R,5[=琪W_i_xIKQڄɵaDoq*Q DV/k}S+H")/ɫ>Eɶ }2<ſ| ^}Im4mSPgmT 10uicǯqXܯ_8jT(gy{ߖ!3wE+aP2z4]s<?-cmS$!-HqKCMI*P:^@bH~??@@#ש%G'9KE($zl ODzGOE8dø-x6-6h2n6x6Jx666q'XA&?J3Y=k O_s]qeH贁c _ x1+G*UD1GuO#ׯ +x1W+ /dɵ~uOOOпpF?'@@r'Ȅͩ(dc////X );b^bu8K;hax6x6[A4%O7OIO[O&8J\nL%áApQ߁If''ׁt?%+-銤<2;eAVQ?q0 * aV?trFQ)yk?qQ;`yw?<a햎""NZ֙?P$ o?K\°?BsWsdeXW?]4k$,@ rq֏ꯤvơ^S uquAqŸ0Bq=%mzߌvM.q塖> AGKHX@gaA.ɂXk!9S *Tl6f 0~i75?ՙM2f)%M2)%8*3LM MU  )="Q&~%eBT  8T@8 DT=%T61CpF?@@rȄ͢D2&t>1 )oN%tT1\"Y7Q"W'u/8N%q-) " T¯ԯ C/ߺpt+)"0:#!uA0߲Qif''1V%+-ۓ1V銝 lgo(e#P.olXկoo_coJ6oHoZcQlւ}/?F'[?15ؾ 8gַWٳ?;`D?!qcWv]Dt~L? UUa%a"HBORG_/SE p 6/H/_l/TEѵx@@t:$@@D4sҎu ?QByRگ !T=V4tGVGV4Y!44_F\e_wSPGNk_R/(<MDuo[cuB_NĹoF{vUumU {M{JgFE &tiHs!T=0gҴlHqLc,@XSq:x"46lVHx.V/5v5G|UuƿؿlVHnNi =ԧ(mUaXFǯ'2}Sk!kaodoUoo(NRC7֯[mu"@@RT*&{2);M_sd}я㏋*^2d:oQo{Z Ѹ`ߛ-/~[Bȣ)l@#)AEr'O&%O=O|$ __qWgO\_O____)_6Sgmnt 3p 0qW$i{!jgDzoo4|4x{Ǜ?fK!3rgf@fOXD~gD4{3y //%/7/-^@+x<@@Fh4%@@H$D@`?ń &//"q2#@v+@v?3@vS@vg1Cs{@vU!++U??SSUgg{{f&?M]A~6:D?Lk`f=~7ѿ?߂5e(N^>OPObO R +?6ՙSfgo噴᤻@nE6E$PnAVЁ4VV?V,VSVVgKVVЏro.@omLooP*7+r+nA zՉ߆9u]CaPkBORp_]`Ep KXjn`zև̀Qtq Ÿ;t!ⅣwQӒ߿<Ṣ-I?oDq1,5ίlf:@O'oB()8<+M[+'AfA/OXeOwOI_IZxc gXOKTx,2)8^1Vd,@3q q7xriK73Y+`p//7,5Y%?//7xY/Dž??.>nP?l 1CI?]o?zߐO߰\ShD^Lk`A/#/5/.@>[O|HQaYSsZu??E c,ٖ?-㳮@OWL(C$6~YATfxC Aߛ߭߿j=lS@ѱ_m-*Z _ "$_Į>_ %VXP>Eeb_Yl_r ?ҀLSeF/aU…TAoMb°RUh[]Y__Uʾ᫥Zo/aaooTooA/ }˲ѹX8}+噷=Զ#Q\nh}+O|0T"DF#+Sepmn tOP2M@ 1CUgy~8j  {*3!%3y VԎ1?7|?OOӢ7F_COMI6@NO8JߖOO|_>HdGy#OOKS°; __逘[A_S_e_w_D$3)*_.]` LnCo`orPzb<S#0`` Fl`>` X?2ߵ1ݵ6 _{rYG_ !FLo1oUo"|pooM|o8Py&_k}y.__ .@Rd/'/=_L.4oy,;MBT+}П*x1C@U}y1AHTԡѤEVioj5(OQCboأoS)CP1ap8 @@tA%@@~? @@` K?rʿDܿ 6`)i̟fcПwџbK1Ο͟ǦSYccwwǦǦvXB$?BQYbڒzW lתu~#DO51#?eb#_5[SQKQGQ WQ uyUċ1AuǪuK1ijwSUYǃau8DRPOQYP @ТP @+P H4)T_GBR0@_E0@/ooN/tZVj?@ q///,) pR++L;k&#B?4?TR=<b1r[?m?=!M̺>OPOTTOOLTTOTOOb __PB_T_f_x_[_____oom>oPobotoooooo3EWh^H///?7?I?6H?l~Ə؏7YߦYOU Ab1H/Z/l/ew BP(F($ 2DV?c,-㳮s3` Ac`entwCowor}0b(0㸱Sbu>``` F0lwn` o2ev@idТ3#`96v5|%1RY@RYx?,ƏPbt //(/:/6_H_Z_/i?4??/Zl?$?Z?l???2???O O2ODOVOhOzOFOOzOOOXBb2 _ h_wfl6_______!` xo#o5oGoVh}oo[mŶHH+=Oasz͏ߏ'9#]oɟ۟ 5篇}]oůׯ Ug5/ӿ -FIϨGK1i}?(ϱXC5"zUp8@=Oasߗߩ ƧL;k C.֠/);M5c ozooo_)zK1:L{puV\2K1mK1.@Rdv}/~*<`r??O8//$Zl/"/X/j/꟎/////// ??0??'tS<`o1y7Y1DžԏO!@7?@jZȰò֒pBP(ҵȰݳ?LUxaqauAuA|Ma6-]O y]ɭO CªO ̒@Ry"R*rIOPr }BLQVeQjaU҅CT|oCbR 5[@m4_F_U&(.am>!ojaqoo!3/ H}0% 6"ɓs}fԷxŜ|޴;}f&J|k4^fSmPgmmT 10RFc\nq˯ݯ<8j1DV b{en!3T ߑ"a-W43@fc0$pN( MIt(:;@?H~?S@@#׆%$(τE($ozI קO~DzDOE8· U6 i6E2K6U6U6i6i6NMx&?'Y=H,_iP]"ӏ=Oߤh(Q&/v %7!%u sޥ zg憆}%ɔxZTΡ"bⴲSH$,@CRqˈŏǯvߤ;0畡qCUqb q%JWi߁v*.ߤkq¡s{GϤ(%5D¡ 5H!0CUh4 ~'w%?.*2C)*2)8**R"@X&[h͡%e1fm WCU֡!Ρ%CU pF?@@?rȄ&ϡt1 )+%t119"67."4'u_8+%N-R"ޢΡկ篇/ѼMt) #^!1wA QFf''V%+-Vz lDoe#P.coul5Ռoo㿀_@_'o%o7cQlւ}Ӑl/F'[?ۿz15 8g֔Wٳ?;Կ=4?!qc4v]Dt~L?2%gah^"HO @=@ co/%/_I/TE}>s@@ޡ1$@@D@Mtוbsiu?Qy!|DVt$V$V𙘂I!_#WSF_U0PGNka_sRW(8OJO\Oҿ8///(/8J^/տ/ȟ//??$?6?H?Z?l?~?rd<?a I%Aec ooonKxk2?@xf"p*accfUppby&Gu{p*ag5p1>]mn@džKn c*]BTfx`@pOhf (>/!¨<=#ON`$&ޱ#Bߧ%\&5?G?k?}??????? Op-O?OQOcOuOOO_OOO_X_)_;]n *aЯ*O&8J\nP_q@GL@B^r-ADctL65TBM%a 1hA@t5A9R@ @l>D Q<]AL9QQƜ_VԏEPz|>O@@X,?@p:_N?_dge$G[Prr;@1@u'@u`F¯?uPjr_b:Phfsrqpq`@VL_q_qQ^r@1ss?1sBvCvdbbbo @-`B!E\$MUr?wCwC.??Ov d)IaH` 25@ġ2` A k0cunqФAd0 @nT`fT0aw0supi0rcy%4F2`@nP >SJyP@e/ _XOfsut߻ Y.. `@Dgci}0z6`@Mi0gw0i)ik0\˨B1hEFdt?@F BP(?H^Lgnuc5cb aar8bE@m! l AQFga` Rds6g5f- `ŢP#` LATA𔁾Arn_g~X'k[phFЂj@Rr̃AQAT@aM?@<bv1JsTbz՛P3@\X< ggL8"TlQQ|B23S-BsїQjjQ_2$`1\89ƒ:hBaaQQ11 b>`r15ABQtrqQ~rqQ2r3qQO1l=R>>ٴyH‚TD,@k1۠zZ5V5kAku!Ci0jt1|,FpgDkB vn r\?P@(2ЍBCF@ @3RBZ"cvWx`j*ufI{sVc4V\f vC/~HWsVƕhE%/WhEF$/_/asV//yZg bBr0a`1,;fl?@rR#P~3@6P@;`o@ QV5 )q}AkZ e ,NuUu“qn AT0V*1Т,Ғ? q8ѯ?@ };`&a;Ґh;S@א̀lV Н̀}R__?\-X-U-I;SZa[f4j0 *o____U@jo8oo\onohaC^guQ^qè[6t @ark-Rn a;ңzd 'oW{woP)z'9E QsX@A},܅8kN=pTbQkOQroxpv{-uOpvj-@ϏhkzQmujvDR1 dvAxXZ!Ə؏\O j̫pvq w򟳟Gpv^yϋϐ0ƿfxš~OO3VJ}icLQ3mK&aV&-~x&axcOJgF_x!0JMLt L#i:הP1 kc2ܔ^avov Qߜ|ߗߩ'D)Z q׀OJ@Q3 &C7/&Xр` T av\@lShi"rt0v21($$1`eOrfZBd-)1nMA/A>7B@B.10;9o3!Q2_=Ў@` {1ѹ!w1QTEg9~? BP(?@@4>An211b05=Dɺ8E42aaV222RHD$sI DpJ-Da=LGD?PMTD NLaDOnD!aaqQDXRD81[ CTLDUD6RVDWDUXDY$DZD[ T"(?q?q2]$TQJQ_>TY!!S YS%g!gR0a;TpDV1?ZV5=A 128{#@vn@r\?_'367-9a,b+Q?1^gl(DRU1BXRB8V5unU+B8`@p7.?@-n3"G2U6eowf8A8A1d]3umRAdjU`$oU[U@X,!?f~hB!vuZ|a=V1ьdRB\Y5oDqя8w?!|aRAa,gՀavubolF`0 _A/ASewƚF|>o=a|a@Rdv ~a¶q!Qcu- ӿ՟ͯΟ-ϭ|aRAdVRAf6CEBϴ&]V5䋷Fh?FL/?F?4ai?a"K%JR"FM Fɟ D[,d>vuz1֥طҸZuLóՍ堞,d)wAs߅ߗߩ3ZxFO=O~߃#Z6F]l6Pdt!Nd814 .$y,>sI [[/#q-J'yA// _/,='?/[/5??$:_OQO|VܦO?Xa^qDf I^^$O6O AƵ,/768noV0?@֬@q?@L?@(>D(%{S_76Lx?@F9^?@mxJAr<0b 70 0b*06030Ny`F!>"D' 81 UDt(!t c!$a%@ Adsg*# esk$// Wv܉t9TF`B?vPD4vP$]9ڣU1( O-D&UA%U f -h"/T))+U( g&+ P- `m-vT ?!}-a8LXQ -H*)A/"/) EQ.///feArial UncodeMiS6?/?`4 R$fSymbol$67fWingds*7 fECalibr@  ?$fSwimunt(S$fPMingLU(w$fMS PGothicj@ $fDotum"|i0@ S$fESylaen  $fEstrangeloU dsaC 9$fEVrind1a8 Q$fEShrutqi| Q$fEM_angl$fETunga<"@ Q$fGSendya*/ (   R$fERavi"o Q$fGDhenu"*/ (  R$fELath#/ R$fEGautmqi | Q$fECordia NewE R$fEArial"*Cx /@~4 R$fMalgun Gothic |w L$fETimes NwRoan*Ax /@4$YEB4Y.BPM%BtPr=B4I3BH5BPGBtP^:Bt7BρABT0B@1B4q0B:0B3т;B4 .B:9Bs-BD2BDP҃;B, 8BD,EIB'BJBTheDoc"Gestur Fom a FhrungsliieGuideSTR Normal0Byte oudrVai bl.37,Byte o]rvaibl.370Byte oudrVai bl.38,Byte o]rvaibl.380Byte oudrVai bl.39,Byte o]rvaibl.39&msvNoAWutCn 7ecPage-1"Zeichnblat -10Byte oudrVai bl.4visVerion,Byte o]rvaibl.400Byte oudrVai bl.41*Byte oudrVai bl&Byte o]rvai7bl0Byte oudrVai bl. 1,Byte o]rvaiwbl.1"Zeichnblat -2Page-2EinfachBasicRe_chtkRectan7gl,Byte o]rvaibl.410Byte oudrVai bl.42,Byte o]rvaibl.420Byte oudrVai bl.43,Byte o]rvaibl.430Byte oudrVai bl.2,Byte o]rvaibl.20Flus normaFlow NrmaBUmgekhrt sucwif_Klamr Revrs bac6msvPreiwIconCopT Pa g&msvStrucueTyp 4msvSDTarge_tIn eusc io 4msvSDCaloutNHi'ght(msvSDCalou]ttye2msvSDCalou]ttyen *msvSha_peCtgorisOrientaioHi_deLaruAtachToSide"Re_sizW_thTxSideLewadrBginLeadrE n(WHBoxInterUscin IsEndnteri oExtens7ioInsetSideMpont&fnMidp}ontOfse&CaloutuSyeR oCStyle1CStyle2CStyle3CStyle4CStyle5CStyle6CStyle7CStyle8CStyle9CStyle10CStyle 1CStyle12CStyle13CStyle14CStyle15CStyle16CStyle17CStyle18CStyle19CStyle20$Orient]aioR oOrientTopOrientWBotmOrient5LfOrientRghOrientAuoLeadrRotLeadrH inLewadrC7nt$LeadrMipontsuAsociaton$Design}fekt.1visUSETypemsvTheeInfomsvTheeyp(msvTheeLatinFo t(msvTheeAia_nFo t,msvTheeCoplxFnt6msvTheeLinTra sprncy,msvTheeLinPate r *msvTheeLin]Wigt.msvTheeLin_Rou dg@msvTheeCon _ectrwra sprny6msvTheeCon ectrWPatr 4msvTheeCon ectr]Wigt8msvTheeCon ectruRu di g2msvTheeCon ectrBgi .msvTheeCon ectrE d0msvTheeCon ectrE d2:msvTheeCon ectr]Bgi S z6msvTheeCon ectrE dSi z6msvTheeFil Tranprncy,msvTheeFil Patern:msvTheeSadWowr np rncy0msvTheeSad_owP tern,msvTheeSadowty l0msvTheeSadowXOfst0msvTheeSadowYOfst<msvTheeSadowM g_nifc 5to4msvTheeSadowDircton"msvTheeCol r$msvTheeEfe7ctVerbin dCo}nectr0Dynamische_r Vb7nd(Dynamic onetrTextPoWsiin Textan}mrkug$Text AUnoainuAtribuesBpmnId"BpmnCategoris(BpmnDocueta in&BpmnArtifa]cTyeBpmnNaeBpmnStae"BpmnPruoetis,BpmnPruoetis_Nae,BpmnPruoetis_Ty.BpmnPruoetis_Va7luLBpmnPruoetis_Valu_EUxr s oBdyTBpmnPruoetis_Valu_EUxr s o]Lnga9e:BpmnPruoeti_s_Cr_ela oBpmnText$BpmnCatego_ryRf$BpmnEleetTye6BpmnConectigOb]jTye(BpmnCoditT ye4BpmnCoditExres  RBpmnCoditExres _BdyZBpmnCoditExres _Lagu;g"BpmnMe]sagRf,BpmnMe]sagR_f_Nm8BpmnMe]sagRf_PruoetiBpmnMesagRf_Proeti_5NmBpmnMesagRf_Proeti_7TyDBpmnMe]sagRf_Pruoetiw_VlubBpmnMe]sagRf_Pruoetiw_Vlu__ExrinodyjBpmnMe]sagRf_Pruoetiw_Vlu__Exr]inLn uPBpmnMe]sagRwf_roeti_uCrel o2BpmnMe]sagRf_Fro RBpmnMe]sagRf_Fro Prtic+pn.)TyeBpmnMesagRf_Froj Rl FBpmnMe]sagRwf_ro Eti'y .BpmnMe]sagRf_To NBpmnMe]sagRf_To Prtic'pn%Tye>BpmnMe]sagRf_To 5Rl BpmnMesagRf__To Eti#y  BpmnDirect o&Textan}mrkug.49*Text AUnoain.490msvWarwnOPesoal5If0Byte oudrVai bl.46,Byte o]rvaibl.460Byte oudrVai bl.47,Byte o]rvaibl.470Byte oudrVai bl.48,Byte o]rvaibl.480Byte oudrVai bl.5,Byte o]rvaibl.500Byte oudrVai bl.95,Byte o]rvaibl.950Byte oudrVai bl.97,Byte o]rvaibl.97JUmgekhrt sucwif_Klamr.105(Revrs bac.7102Byte oudrVai bl.10.Byte o]rvaibl.1020Byte oudrVai bl.54,Byte o]rvaibl.540Byte oudrVai bl. 5,Byte o]rvaiwbl.50Byte oudrVai bl.56,Byte o]rvaibl.560Byte oudrVai bl.57,Byte o]rvaibl.570Byte oudrVai bl.6,Byte o]rvaibl.600Byte oudrVai bl.61,Byte o]rvaibl.610Byte oudrVai bl.62,Byte o]rvaibl.620Byte oudrVai bl.65,Byte o]rvaibl.65HUmgekhrt sucwif_Klamr.68&Revrs bac.682Byte oudrVai bl.10.Byte o]rvaibl.1202Byte oudrVai bl.11.Byte o]rvaibl.1 22Byte oudrVai bl.14.Byte o]rvaibl.142JUmgekhrt sucwif_Klamr.126(Revrs bac.1266Dynamische_r Vbnd.81.Dynamic onetr81JUmgekhrt sucwif_Klamr.148(Revrs bac.1482Byte oudrVai bl.16.Byte o]rvaibl.162JUmgekhrt sucwif_Klamr.167(Revrs bac.1672Byte oudrVai bl.168.Byte o]rvaibl.1682Byte oudrVai bl.186.Byte o]rvaibl.1862Byte oudrVai bl.719.Byte o]rvaibl.1 90Byte oudrVai bl.82,Byte o]rvaibl.826Dynamische_r Vbnd.83.Dynamic onetr836Dynamische_r Vbnd.85.Dynamic onetr852Byte oudrVai bl.19.Byte o]rvaibl.1922Byte oudrVai bl.193.Byte o]rvaibl.1932Byte oudrVa]i bl.1.Byte o]rvaibl.2 12Byte oudrVa]i bl.17.Byte o]rvaibl.2172Byte oudrVa]i bl.18.Byte o]rvaibl.2182Byte oudrVa]i bl.19.Byte o]rvaibl.219JUmgekhrt sucwif_Klam_r.236(Revrs bac.26p3hSG3T%G3$G3XT E3TG3|B81G3di.G3dfE3^]E3S1G3OЇ.G3\B1G3/.G3](G3hSG3%G3L/G3E3ĬTG3R.G3ܩ=1G3n+G34(G31G3d.G3, %G3hSEG3iSZG3TrE33G3 3G3|E34E3DR.G31G3tR.G3lI1G3Rz.G3fE3fE3fE3/G3.G3fE3\fE3T"G3T;G3tZ;G3!G3D<9G3)G3,7G3dO3G3$*G32G3Tލ-G3T G3$T*G3D"G3ԉf$G3TE3DTG3<3G3ю*G3$G3X3G3T;E3,KG3i)G3'G3LiSG3diSяG3iSG3iSG3iSG3iS1G3iSIG3jSaG3jSyG33G33G33ŐG33ߐG34G3 4G3<4-G3X4GG34aG34{G34G3T&G34ՑG3 G3dTG3T.G3TMG3ĭTjG3G3ԊG3’&G3TG3,&G3T,G3TJ"G3l G3,G3D*G3t.G3<8G3H/G3w+G3Ԅ.G3Д?G3<8G3 G5G3=|9G3D4G3|0G32G3D=K;G3=9G3=8G3/G3>&:G3`2G34/G3$2G3\2G3D>%=G3b7G3%G3ԋ'G35G3 5G3̆0G3dI)G3r G3\]E3GE3GE3GE3GE3GE3GE3GE3GE3GE3GE3GE3G™E3GƙE3 GʙE3GΙE3GҙE3$G֙E3,GڙE34GޙE3DGE3GE3,#G3T %G3$T2G34jSNG3c&G3)G3)G3<5ۚG3X5G3$G3$3.G3Ta-G3Ĉ1G3 IG3NPG3QX9G35G3Ԍ(G3Ӝ&G3R4G3-(G3U1G3$N=G3NÝEG3,$G3,.G3DRZ8G3d >G3 О=G3  CG3PZG3]G3NJG34Q1G3DNKG3T ͠5G3 <G3l>-G3NkGG3 3G3D 6G3T$G3?)G3h+G32G3IŢ1G3D.G3I$1G3DU.G34yI1G3$g.G3/G34 P.G34­?1G3 Pp.G3Ĉ1G3oϤ.G3qDG3KA)G3@[j1G3@[0G3T1˥1G3X.G3?*1G3tX[.G31G3DX.G3<1G3X.G3tG/G3Xv.G31G3 Xէ.G31G34 X4.G3b1G3J.G3XAG3t'G3A[)2G3TA[[0G3y2G3y0G31G340G3tRNDG34)G3DbX6G3$/G3 DG3od)G3z1G3Lz0G3DG3$]2)G3j[3G3j0G3,k3G3dk0G349!3G3l9T0G3/1G3/P.G3D I6G3Ԕ/G3H6G3tX~/G3X1G3ޮ0G3<3G3TA0G39q0G390G3:ѯ2G3L:0G3:32G3:e0G3:2G3,;ǰ0G35BG3CK9(G  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     ]>rFxml:spaceprsrv]rFxmlns:vdhtp/%scea .i+ro of.+o/i iA/20*c6%et/ \n]rFxmlns:v14htp/'sUcea .i-ro oUf.-o/GUf=e'v=s=]o'200'Eet1 lnU&U-.t4 "H$ @Y,6@ d!C-*7 AU %t4 ҭB C>-^]O7"AJ@GNR@ܢGQ7RH<(H<(JETGREGR{7   g"4FXo 8)(N1j@(/?{PhBĿ6 PZlz`S}~3 BvR٘R$I!$OI)\9W'^_j~~1__d5P25a0TU9B="diG zK&KG?Q!{W*'"Dy ~%!././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/internals/data-structures.rst0000644000076500000240000014567414601576577021074 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none .. _data-structures: Data structures and file formats ================================ This page documents the internal data structures and storage mechanisms of Borg. It is partly based on `mailing list discussion about internals`_ and also on static code analysis. .. todo:: Clarify terms, perhaps create a glossary. ID (client?) vs. key (repository?), chunks (blob of data in repo?) vs. object (blob of data in repo, referred to from another object?), .. _repository: Repository ---------- .. Some parts of this description were taken from the Repository docstring Borg stores its data in a `Repository`, which is a file system based transactional key-value store. Thus the repository does not know about the concept of archives or items. Each repository has the following file structure: README simple text file telling that this is a Borg repository config repository configuration data/ directory where the actual data is stored hints.%d hints for repository compaction index.%d repository index lock.roster and lock.exclusive/* used by the locking system to manage shared and exclusive locks Transactionality is achieved by using a log (aka journal) to record changes. The log is a series of numbered files called segments_. Each segment is a series of log entries. The segment number together with the offset of each entry relative to its segment start establishes an ordering of the log entries. This is the "definition" of time for the purposes of the log. .. _config-file: Config file ~~~~~~~~~~~ Each repository has a ``config`` file which is a ``INI``-style file and looks like this:: [repository] version = 1 segments_per_dir = 1000 max_segment_size = 524288000 id = 57d6c1d52ce76a836b532b0e42e677dec6af9fca3673db511279358828a21ed6 This is where the ``repository.id`` is stored. It is a unique identifier for repositories. It will not change if you move the repository around so you can make a local transfer then decide to move the repository to another (even remote) location at a later time. Keys ~~~~ Repository keys are byte-strings of fixed length (32 bytes), they don't have a particular meaning (except for the Manifest_). Normally the keys are computed like this:: key = id = id_hash(unencrypted_data) The id_hash function depends on the :ref:`encryption mode `. As the id / key is used for deduplication, id_hash must be a cryptographically strong hash or MAC. Segments ~~~~~~~~ Objects referenced by a key are stored inline in files (`segments`) of approx. 500 MB size in numbered subdirectories of ``repo/data``. The number of segments per directory is controlled by the value of ``segments_per_dir``. If you change this value in a non-empty repository, you may also need to relocate the segment files manually. A segment starts with a magic number (``BORG_SEG`` as an eight byte ASCII string), followed by a number of log entries. Each log entry consists of: (in this order) * First, unsigned 32-bit number, the CRC32 of the entire entry (for a PUT including the DATA) excluding the CRC32 field * Second, unsigned 32-bit size of the entry (including the whole header) * Third, unsigned 8-bit entry tag: PUT(0), DELETE(1) or COMMIT(2) * Fourth, on PUT or DELETE, 32 byte key * Fifth, PUT only, (size - 41) bytes of data (length = size - sizeof(CRC32) - sizeof(size) - sizeof(entry tag) - sizeof(key)) Those files are strictly append-only and modified only once. Tag is either ``PUT``, ``DELETE``, or ``COMMIT``. When an object is written to the repository a ``PUT`` entry is written to the file containing the object id and data. If an object is deleted a ``DELETE`` entry is appended with the object id. A ``COMMIT`` tag is written when a repository transaction is committed. The segment number of the segment containing a commit is the **transaction ID**. When a repository is opened any ``PUT`` or ``DELETE`` operations not followed by a ``COMMIT`` tag are discarded since they are part of a partial/uncommitted transaction. The size of individual segments is limited to 4 GiB, since the offset of entries within segments is stored in a 32-bit unsigned integer in the repository index. Objects ~~~~~~~ All objects (the manifest, archives, archive item streams chunks and file data chunks) are encrypted and/or compressed. See :ref:`data-encryption` for a graphic outlining the anatomy of an object in Borg. The `type` for compression is explained in :ref:`data-compression`. Index, hints and integrity ~~~~~~~~~~~~~~~~~~~~~~~~~~ The **repository index** is stored in ``index.`` and is used to determine an object's location in the repository. It is a HashIndex_, a hash table using open addressing. It maps object keys_ to two unsigned 32-bit integers; the first integer gives the segment number, the second indicates the offset of the object's entry within the segment. The **hints file** is a msgpacked file named ``hints.``. It contains: * version * list of segments * compact The **integrity file** is a msgpacked file named ``integrity.``. It contains checksums of the index and hints files and is described in the :ref:`Checksumming data structures ` section below. If the index or hints are corrupted, they are re-generated automatically. If they are outdated, segments are replayed from the index state to the currently committed transaction. Compaction ~~~~~~~~~~ For a given key only the last entry regarding the key, which is called current (all other entries are called superseded), is relevant: If there is no entry or the last entry is a DELETE then the key does not exist. Otherwise the last PUT defines the value of the key. By superseding a PUT (with either another PUT or a DELETE) the log entry becomes obsolete. A segment containing such obsolete entries is called sparse, while a segment containing no such entries is called compact. Since writing a ``DELETE`` tag does not actually delete any data and thus does not free disk space any log-based data store will need a compaction strategy (somewhat analogous to a garbage collector). Borg uses a simple forward compacting algorithm, which avoids modifying existing segments. Compaction runs when a commit is issued with ``compact=True`` parameter, e.g. by the ``borg compact`` command (unless the :ref:`append_only_mode` is active). One client transaction can manifest as multiple physical transactions, since compaction is transacted, too, and Borg does not distinguish between the two:: Perspective| Time --> -----------+-------------- Client | Begin transaction - Modify Data - Commit | (done) Repository | Begin transaction - Modify Data - Commit | Compact segments - Commit | (done) The compaction algorithm requires two inputs in addition to the segments themselves: (i) Which segments are sparse, to avoid scanning all segments (impractical). Further, Borg uses a conditional compaction strategy: Only those segments that exceed a threshold sparsity are compacted. To implement the threshold condition efficiently, the sparsity has to be stored as well. Therefore, Borg stores a mapping ``(segment id,) -> (number of sparse bytes,)``. The 1.0.x series used a simpler non-conditional algorithm, which only required the list of sparse segments. Thus, it only stored a list, not the mapping described above. (ii) Each segment's reference count, which indicates how many live objects are in a segment. This is not strictly required to perform the algorithm. Rather, it is used to validate that a segment is unused before deleting it. If the algorithm is incorrect, or the reference count was not accounted correctly, then an assertion failure occurs. These two pieces of information are stored in the hints file (`hints.N`) next to the index (`index.N`). When loading a hints file, Borg checks the version contained in the file. The 1.0.x series writes version 1 of the format (with the segments list instead of the mapping, mentioned above). Since Borg 1.0.4, version 2 is read as well. The 1.1.x series writes version 2 of the format and reads either version. When reading a version 1 hints file, Borg 1.1.x will read all sparse segments to determine their sparsity. This process may take some time if a repository has been kept in append-only mode or ``borg compact`` has not been used for a longer time, which both has caused the number of sparse segments to grow. Compaction processes sparse segments from oldest to newest; sparse segments which don't contain enough deleted data to justify compaction are skipped. This avoids doing e.g. 500 MB of writing current data to a new segment when only a couple kB were deleted in a segment. Segments that are compacted are read in entirety. Current entries are written to a new segment, while superseded entries are omitted. After each segment an intermediary commit is written to the new segment. Then, the old segment is deleted (asserting that the reference count diminished to zero), freeing disk space. A simplified example (excluding conditional compaction and with simpler commit logic) showing the principal operation of compaction: .. figure:: compaction.png :figwidth: 100% :width: 100% (The actual algorithm is more complex to avoid various consistency issues, refer to the ``borg.repository`` module for more comments and documentation on these issues.) .. _internals_storage_quota: Storage quotas ~~~~~~~~~~~~~~ Quotas are implemented at the Repository level. The active quota of a repository is determined by the ``storage_quota`` `config` entry or a run-time override (via :ref:`borg_serve`). The currently used quota is stored in the hints file. Operations (PUT and DELETE) during a transaction modify the currently used quota: - A PUT adds the size of the *log entry* to the quota, i.e. the length of the data plus the 41 byte header. - A DELETE subtracts the size of the deleted log entry from the quota, which includes the header. Thus, PUT and DELETE are symmetric and cancel each other out precisely. The quota does not track on-disk size overheads (due to conditional compaction or append-only mode). In normal operation the inclusion of the log entry headers in the quota act as a faithful proxy for index and hints overheads. By tracking effective content size, the client can *always* recover from a full quota by deleting archives. This would not be possible if the quota tracked on-disk size, since journaling DELETEs requires extra disk space before space is freed. Tracking effective size on the other hand accounts DELETEs immediately as freeing quota. .. rubric:: Enforcing the quota The storage quota is meant as a robust mechanism for service providers, therefore :ref:`borg_serve` has to enforce it without loopholes (e.g. modified clients). The following sections refer to using quotas on remotely accessed repositories. For local access, consider *client* and *serve* the same. Accordingly, quotas cannot be enforced with local access, since the quota can be changed in the repository config. The quota is enforcible only if *all* :ref:`borg_serve` versions accessible to clients support quotas (see next section). Further, quota is per repository. Therefore, ensure clients can only access a defined set of repositories with their quotas set, using ``--restrict-to-repository``. If the client exceeds the storage quota the ``StorageQuotaExceeded`` exception is raised. Normally a client could ignore such an exception and just send a ``commit()`` command anyway, circumventing the quota. However, when ``StorageQuotaExceeded`` is raised, it is stored in the ``transaction_doomed`` attribute of the repository. If the transaction is doomed, then commit will re-raise this exception, aborting the commit. The transaction_doomed indicator is reset on a rollback (which erases the quota-exceeding state). .. rubric:: Compatibility with older servers and enabling quota after-the-fact If no quota data is stored in the hints file, Borg assumes zero quota is used. Thus, if a repository with an enabled quota is written to with an older ``borg serve`` version that does not understand quotas, then the quota usage will be erased. The client version is irrelevant to the storage quota and has no part in it. The form of error messages due to exceeding quota varies with client versions. A similar situation arises when upgrading from a Borg release that did not have quotas. Borg will start tracking quota use from the time of the upgrade, starting at zero. If the quota shall be enforced accurately in these cases, either - delete the ``index.N`` and ``hints.N`` files, forcing Borg to rebuild both, re-acquiring quota data in the process, or - edit the msgpacked ``hints.N`` file (not recommended and thus not documented further). The object graph ---------------- On top of the simple key-value store offered by the Repository_, Borg builds a much more sophisticated data structure that is essentially a completely encrypted object graph. Objects, such as archives_, are referenced by their chunk ID, which is cryptographically derived from their contents. More on how this helps security in :ref:`security_structural_auth`. .. figure:: object-graph.png :figwidth: 100% :width: 100% .. _manifest: The manifest ~~~~~~~~~~~~ The manifest is the root of the object hierarchy. It references all archives in a repository, and thus all data in it. Since no object references it, it cannot be stored under its ID key. Instead, the manifest has a fixed all-zero key. The manifest is rewritten each time an archive is created, deleted, or modified. It looks like this: .. code-block:: python { b'version': 1, b'timestamp': b'2017-05-05T12:42:23.042864', b'item_keys': [b'acl_access', b'acl_default', ...], b'config': {}, b'archives': { b'2017-05-05-system-backup': { b'id': b'<32 byte binary object ID>', b'time': b'2017-05-05T12:42:22.942864', }, }, b'tam': ..., } The *version* field can be either 1 or 2. The versions differ in the way feature flags are handled, described below. The *timestamp* field is used to avoid logical replay attacks where the server just resets the repository to a previous state. *item_keys* is a list containing all Item_ keys that may be encountered in the repository. It is used by *borg check*, which verifies that all keys in all items are a subset of these keys. Thus, an older version of *borg check* supporting this mechanism can correctly detect keys introduced in later versions. The *tam* key is part of the :ref:`tertiary authentication mechanism ` (formerly known as "tertiary authentication for metadata") and authenticates the manifest, since an ID check is not possible. *config* is a general-purpose location for additional metadata. All versions of Borg preserve its contents (it may have been a better place for *item_keys*, which is not preserved by unaware Borg versions, releases predating 1.0.4). Feature flags +++++++++++++ Feature flags are used to add features to data structures without causing corruption if older versions are used to access or modify them. The main issues to consider for a feature flag oriented design are flag granularity, flag storage, and cache_ invalidation. Feature flags are divided in approximately three categories, detailed below. Due to the nature of ID-based deduplication, write (i.e. creating archives) and read access are not symmetric; it is possible to create archives referencing chunks that are not readable with the current feature set. The third category are operations that require accurate reference counts, for example archive deletion and check. As the manifest is always updated and always read, it is the ideal place to store feature flags, comparable to the super-block of a file system. The only problem is to recover from a lost manifest, i.e. how is it possible to detect which feature flags are enabled, if there is no manifest to tell. This issue is left open at this time, but is not expected to be a major hurdle; it doesn't have to be handled efficiently, it just needs to be handled. Lastly, cache_ invalidation is handled by noting which feature flags were and which were not understood while manipulating a cache. This allows borg to detect whether the cache needs to be invalidated, i.e. rebuilt from scratch. See `Cache feature flags`_ below. The *config* key stores the feature flags enabled on a repository: .. code-block:: python config = { b'feature_flags': { b'read': { b'mandatory': [b'some_feature'], }, b'check': { b'mandatory': [b'other_feature'], } b'write': ..., b'delete': ... }, } The top-level distinction for feature flags is the operation the client intends to perform, | the *read* operation includes extraction and listing of archives, | the *write* operation includes creating new archives, | the *delete* (archives) operation, | the *check* operation requires full understanding of everything in the repository. | These are weakly set-ordered; *check* will include everything required for *delete*, *delete* will likely include *write* and *read*. However, *read* may require more features than *write* (due to ID-based deduplication, *write* does not necessarily require reading/understanding repository contents). Each operation can contain several sets of feature flags. Only one set, the *mandatory* set is currently defined. Upon reading the manifest, the Borg client has already determined which operation should be performed. If feature flags are found in the manifest, the set of feature flags supported by the client is compared to the mandatory set found in the manifest. If any unsupported flags are found (i.e. the mandatory set is not a subset of the features supported by the Borg client used), the operation is aborted with a *MandatoryFeatureUnsupported* error: Unsupported repository feature(s) {'some_feature'}. A newer version of borg is required to access this repository. Older Borg releases do not have this concept and do not perform feature flags checks. These can be locked out with manifest version 2. Thus, the only difference between manifest versions 1 and 2 is that the latter is only accepted by Borg releases implementing feature flags. Therefore, as soon as any mandatory feature flag is enabled in a repository, the manifest version must be switched to version 2 in order to lock out all Borg releases unaware of feature flags. .. _Cache feature flags: .. rubric:: Cache feature flags `The cache`_ does not have its separate set of feature flags. Instead, Borg stores which flags were used to create or modify a cache. All mandatory manifest features from all operations are gathered in one set. Then, two sets of features are computed; - those features that are supported by the client and mandated by the manifest are added to the *mandatory_features* set, - the *ignored_features* set comprised of those features mandated by the manifest, but not supported by the client. Because the client previously checked compliance with the mandatory set of features required for the particular operation it is executing, the *mandatory_features* set will contain all necessary features required for using the cache safely. Conversely, the *ignored_features* set contains only those features which were not relevant to operating the cache. Otherwise, the client would not pass the feature set test against the manifest. When opening a cache and the *mandatory_features* set is not a subset of the features supported by the client, the cache is wiped out and rebuilt, since a client not supporting a mandatory feature that the cache was built with would be unable to update it correctly. The assumption behind this behaviour is that any of the unsupported features could have been reflected in the cache and there is no way for the client to discern whether that is the case. Meanwhile, it may not be practical for every feature to have clients using it track whether the feature had an impact on the cache. Therefore, the cache is wiped. When opening a cache and the intersection of *ignored_features* and the features supported by the client contains any elements, i.e. the client possesses features that the previous client did not have and those new features are enabled in the repository, the cache is wiped out and rebuilt. While the former condition likely requires no tweaks, the latter condition is formulated in an especially conservative way to play it safe. It seems likely that specific features might be exempted from the latter condition. .. rubric:: Defined feature flags Currently no feature flags are defined. From currently planned features, some examples follow, these may/may not be implemented and purely serve as examples. - A mandatory *read* feature could be using a different encryption scheme (e.g. session keys). This may not be mandatory for the *write* operation - reading data is not strictly required for creating an archive. - Any additions to the way chunks are referenced (e.g. to support larger archives) would become a mandatory *delete* and *check* feature; *delete* implies knowing correct reference counts, so all object references need to be understood. *check* must discover the entire object graph as well, otherwise the "orphan chunks check" could delete data still in use. .. _archive: Archives ~~~~~~~~ Each archive is an object referenced by the manifest. The archive object itself does not store any of the data contained in the archive it describes. Instead, it contains a list of chunks which form a msgpacked stream of items_. The archive object itself further contains some metadata: * *version* * *name*, which might differ from the name set in the manifest. When :ref:`borg_check` rebuilds the manifest (e.g. if it was corrupted) and finds more than one archive object with the same name, it adds a counter to the name in the manifest, but leaves the *name* field of the archives as it was. * *items*, a list of chunk IDs containing item metadata (size: count * ~34B) * *cmdline*, the command line which was used to create the archive * *hostname* * *username* * *time* and *time_end* are the start and end timestamps, respectively * *comment*, a user-specified archive comment * *chunker_params* are the :ref:`chunker-params ` used for creating the archive. This is used by :ref:`borg_recreate` to determine whether a given archive needs rechunking. * Some other pieces of information related to recreate. .. _archive_limitation: .. rubric:: Note about archive limitations The archive is currently stored as a single object in the repository and thus limited in size to MAX_OBJECT_SIZE (20MiB). As one chunk list entry is ~40B, that means we can reference ~500.000 item metadata stream chunks per archive. Each item metadata stream chunk is ~128kiB (see hardcoded ITEMS_CHUNKER_PARAMS). So that means the whole item metadata stream is limited to ~64GiB chunks. If compression is used, the amount of storable metadata is bigger - by the compression factor. If the medium size of an item entry is 100B (small size file, no ACLs/xattrs), that means a limit of ~640 million files/directories per archive. If the medium size of an item entry is 2kB (~100MB size files or more ACLs/xattrs), the limit will be ~32 million files/directories per archive. If one tries to create an archive object bigger than MAX_OBJECT_SIZE, a fatal IntegrityError will be raised. A workaround is to create multiple archives with fewer items each, see also :issue:`1452`. .. _item: Items ~~~~~ Each item represents a file, directory or other file system item and is stored as a dictionary created by the ``Item`` class that contains: * path * list of data chunks (size: count * ~40B) * user * group * uid * gid * mode (item type + permissions) * source (for symlinks, and for hardlinks within one archive) * rdev (for device files) * mtime, atime, ctime in nanoseconds * xattrs * acl (various OS-dependent fields) * flags All items are serialized using msgpack and the resulting byte stream is fed into the same chunker algorithm as used for regular file data and turned into deduplicated chunks. The reference to these chunks is then added to the archive metadata. To achieve a finer granularity on this metadata stream, we use different chunker params for this chunker, which result in smaller chunks. A chunk is stored as an object as well, of course. .. _chunks: .. _chunker_details: Chunks ~~~~~~ Borg has these chunkers: - "fixed": a simple, low cpu overhead, fixed blocksize chunker, optionally supporting a header block of different size. - "buzhash": variable, content-defined blocksize, uses a rolling hash computed by the Buzhash_ algorithm. For some more general usage hints see also ``--chunker-params``. "fixed" chunker +++++++++++++++ The fixed chunker triggers (chunks) at even-spaced offsets, e.g. every 4MiB, producing chunks of same block size (the last chunk is not required to be full-size). Optionally, it supports processing a differently sized "header" first, before it starts to cut chunks of the desired block size. The default is not to have a differently sized header. ``borg create --chunker-params fixed,BLOCK_SIZE[,HEADER_SIZE]`` - BLOCK_SIZE: no default value, multiple of the system page size (usually 4096 bytes) recommended. E.g.: 4194304 would cut 4MiB sized chunks. - HEADER_SIZE: optional, defaults to 0 (no header). The fixed chunker also supports processing sparse files (reading only the ranges with data and seeking over the empty hole ranges). ``borg create --sparse --chunker-params fixed,BLOCK_SIZE[,HEADER_SIZE]`` "buzhash" chunker +++++++++++++++++ The buzhash chunker triggers (chunks) when the last HASH_MASK_BITS bits of the hash are zero, producing chunks with a target size of 2^HASH_MASK_BITS bytes. Buzhash is **only** used for cutting the chunks at places defined by the content, the buzhash value is **not** used as the deduplication criteria (we use a cryptographically strong hash/MAC over the chunk contents for this, the id_hash). The idea of content-defined chunking is assigning every byte where a cut *could* be placed a hash. The hash is based on some number of bytes (the window size) before the byte in question. Chunks are cut where the hash satisfies some condition (usually "n numbers of trailing/leading zeroes"). This causes chunks to be cut in the same location relative to the file's contents, even if bytes are inserted or removed before/after a cut, as long as the bytes within the window stay the same. This results in a high chance that a single cluster of changes to a file will only result in 1-2 new chunks, aiding deduplication. Using normal hash functions this would be extremely slow, requiring hashing approximately ``window size * file size`` bytes. A rolling hash is used instead, which allows to add a new input byte and compute a new hash as well as *remove* a previously added input byte from the computed hash. This makes the cost of computing a hash for each input byte largely independent of the window size. Borg defines minimum and maximum chunk sizes (CHUNK_MIN_EXP and CHUNK_MAX_EXP, respectively) which narrows down where cuts may be made, greatly reducing the amount of data that is actually hashed for content-defined chunking. ``borg create --chunker-params buzhash,CHUNK_MIN_EXP,CHUNK_MAX_EXP,HASH_MASK_BITS,HASH_WINDOW_SIZE`` can be used to tune the chunker parameters, the default is: - CHUNK_MIN_EXP = 19 (minimum chunk size = 2^19 B = 512 kiB) - CHUNK_MAX_EXP = 23 (maximum chunk size = 2^23 B = 8 MiB) - HASH_MASK_BITS = 21 (target chunk size ~= 2^21 B = 2 MiB) - HASH_WINDOW_SIZE = 4095 [B] (`0xFFF`) The buzhash table is altered by XORing it with a seed randomly generated once for the repository, and stored encrypted in the keyfile. This is to prevent chunk size based fingerprinting attacks on your encrypted repo contents (to guess what files you have based on a specific set of chunk sizes). .. _cache: The cache --------- The **files cache** is stored in ``cache/files`` and is used at backup time to quickly determine whether a given file is unchanged and we have all its chunks. In memory, the files cache is a key -> value mapping (a Python *dict*) and contains: * key: id_hash of the encoded, absolute file path * value: - file inode number - file size - file mtime_ns - age (0 [newest], 1, 2, 3, ..., BORG_FILES_CACHE_TTL - 1) - list of chunk ids representing the file's contents To determine whether a file has not changed, cached values are looked up via the key in the mapping and compared to the current file attribute values. If the file's size, mtime_ns and inode number is still the same, it is considered to not have changed. In that case, we check that all file content chunks are (still) present in the repository (we check that via the chunks cache). If everything is matching and all chunks are present, the file is not read / chunked / hashed again (but still a file metadata item is written to the archive, made from fresh file metadata read from the filesystem). This is what makes borg so fast when processing unchanged files. If there is a mismatch or a chunk is missing, the file is read / chunked / hashed. Chunks already present in repo won't be transferred to repo again. The inode number is stored and compared to make sure we distinguish between different files, as a single path may not be unique across different archives in different setups. Not all filesystems have stable inode numbers. If that is the case, borg can be told to ignore the inode number in the check via --ignore-inode. The age value is used for cache management. If a file is "seen" in a backup run, its age is reset to 0, otherwise its age is incremented by one. If a file was not seen in BORG_FILES_CACHE_TTL backups, its cache entry is removed. See also: :ref:`always_chunking` and :ref:`a_status_oddity` The files cache is a python dictionary, storing python objects, which generates a lot of overhead. Borg can also work without using the files cache (saves memory if you have a lot of files or not much RAM free), then all files are assumed to have changed. This is usually much slower than with files cache. The on-disk format of the files cache is a stream of msgpacked tuples (key, value). Loading the files cache involves reading the file, one msgpack object at a time, unpacking it, and msgpacking the value (in an effort to save memory). The **chunks cache** is stored in ``cache/chunks`` and is used to determine whether we already have a specific chunk, to count references to it and also for statistics. The chunks cache is a key -> value mapping and contains: * key: - chunk id_hash * value: - reference count - size - encrypted/compressed size The chunks cache is a HashIndex_. Due to some restrictions of HashIndex, the reference count of each given chunk is limited to a constant, MAX_VALUE (introduced below in HashIndex_), approximately 2**32. If a reference count hits MAX_VALUE, decrementing it yields MAX_VALUE again, i.e. the reference count is pinned to MAX_VALUE. .. _cache-memory-usage: Indexes / Caches memory usage ----------------------------- Here is the estimated memory usage of Borg - it's complicated:: chunk_size ~= 2 ^ HASH_MASK_BITS (for buzhash chunker, BLOCK_SIZE for fixed chunker) chunk_count ~= total_file_size / chunk_size repo_index_usage = chunk_count * 40 chunks_cache_usage = chunk_count * 44 files_cache_usage = total_file_count * 240 + chunk_count * 80 mem_usage ~= repo_index_usage + chunks_cache_usage + files_cache_usage = chunk_count * 164 + total_file_count * 240 Due to the hashtables, the best/usual/worst cases for memory allocation can be estimated like that:: mem_allocation = mem_usage / load_factor # l_f = 0.25 .. 0.75 mem_allocation_peak = mem_allocation * (1 + growth_factor) # g_f = 1.1 .. 2 All units are Bytes. It is assuming every chunk is referenced exactly once (if you have a lot of duplicate chunks, you will have fewer chunks than estimated above). It is also assuming that typical chunk size is 2^HASH_MASK_BITS (if you have a lot of files smaller than this statistical medium chunk size, you will have more chunks than estimated above, because 1 file is at least 1 chunk). If a remote repository is used the repo index will be allocated on the remote side. The chunks cache, files cache and the repo index are all implemented as hash tables. A hash table must have a significant amount of unused entries to be fast - the so-called load factor gives the used/unused elements ratio. When a hash table gets full (load factor getting too high), it needs to be grown (allocate new, bigger hash table, copy all elements over to it, free old hash table) - this will lead to short-time peaks in memory usage each time this happens. Usually does not happen for all hashtables at the same time, though. For small hash tables, we start with a growth factor of 2, which comes down to ~1.1x for big hash tables. E.g. backing up a total count of 1 Mi (IEC binary prefix i.e. 2^20) files with a total size of 1TiB. a) with ``create --chunker-params buzhash,10,23,16,4095`` (custom, like borg < 1.0 or attic): mem_usage = 2.8GiB b) with ``create --chunker-params buzhash,19,23,21,4095`` (default): mem_usage = 0.31GiB .. note:: There is also the ``--files-cache=disabled`` option to disable the files cache. You'll save some memory, but it will need to read / chunk all the files as it can not skip unmodified files then. HashIndex --------- The chunks cache and the repository index are stored as hash tables, with only one slot per bucket, spreading hash collisions to the following buckets. As a consequence the hash is just a start position for a linear search. If a key is looked up that is not in the table, then the hash table is searched from the start position (the hash) until the first empty bucket is reached. This particular mode of operation is open addressing with linear probing. When the hash table is filled to 75%, its size is grown. When it's emptied to 25%, its size is shrinked. Operations on it have a variable complexity between constant and linear with low factor, and memory overhead varies between 33% and 300%. If an element is deleted, and the slot behind the deleted element is not empty, then the element will leave a tombstone, a bucket marked as deleted. Tombstones are only removed by insertions using the tombstone's bucket, or by resizing the table. They present the same load to the hash table as a real entry, but do not count towards the regular load factor. Thus, if the number of empty slots becomes too low (recall that linear probing for an element not in the index stops at the first empty slot), the hash table is rebuilt. The maximum *effective* load factor, i.e. including tombstones, is 93%. Data in a HashIndex is always stored in little-endian format, which increases efficiency for almost everyone, since basically no one uses big-endian processors any more. HashIndex does not use a hashing function, because all keys (save manifest) are outputs of a cryptographic hash or MAC and thus already have excellent distribution. Thus, HashIndex simply uses the first 32 bits of the key as its "hash". The format is easy to read and write, because the buckets array has the same layout in memory and on disk. Only the header formats differ. The on-disk header is ``struct HashHeader``: - First, the HashIndex magic, the eight byte ASCII string "BORG_IDX". - Second, the signed 32-bit number of entries (i.e. buckets which are not deleted and not empty). - Third, the signed 32-bit number of buckets, i.e. the length of the buckets array contained in the file, and the modulus for index calculation. - Fourth, the signed 8-bit length of keys. - Fifth, the signed 8-bit length of values. This has to be at least four bytes. All fields are packed. The HashIndex is *not* a general purpose data structure. The value size must be at least 4 bytes, and these first bytes are used for in-band signalling in the data structure itself. The constant MAX_VALUE (defined as 2**32-1025 = 4294966271) defines the valid range for these 4 bytes when interpreted as an uint32_t from 0 to MAX_VALUE (inclusive). The following reserved values beyond MAX_VALUE are currently in use (byte order is LE): - 0xffffffff marks empty buckets in the hash table - 0xfffffffe marks deleted buckets in the hash table HashIndex is implemented in C and wrapped with Cython in a class-based interface. The Cython wrapper checks every passed value against these reserved values and raises an AssertionError if they are used. .. _data-encryption: Encryption ---------- .. seealso:: The :ref:`borgcrypto` section for an in-depth review. AES_-256 is used in CTR mode (so no need for padding). A 64 bit initialization vector is used, a MAC is computed on the encrypted chunk and both are stored in the chunk. Encryption and MAC use two different keys. Each chunk consists of ``TYPE(1)`` + ``MAC(32)`` + ``NONCE(8)`` + ``CIPHERTEXT``: .. figure:: encryption.png :figwidth: 100% :width: 100% In AES-CTR mode you can think of the IV as the start value for the counter. The counter itself is incremented by one after each 16 byte block. The IV/counter is not required to be random but it must NEVER be reused. So to accomplish this Borg initializes the encryption counter to be higher than any previously used counter value before encrypting new data. To reduce payload size, only 8 bytes of the 16 bytes nonce is saved in the payload, the first 8 bytes are always zeros. This does not affect security but limits the maximum repository capacity to only 295 exabytes (2**64 * 16 bytes). Encryption keys (and other secrets) are kept either in a key file on the client ('keyfile' mode) or in the repository config on the server ('repokey' mode). In both cases, the secrets are generated from random and then encrypted by a key derived from your passphrase (this happens on the client before the key is stored into the keyfile or as repokey). The passphrase is passed through the ``BORG_PASSPHRASE`` environment variable or prompted for interactive usage. .. _key_files: Key files --------- .. seealso:: The :ref:`key_encryption` section for an in-depth review of the key encryption. When initialized with the ``init -e keyfile`` command, Borg needs an associated file in ``$HOME/.config/borg/keys`` to read and write the repository. The format is based on msgpack_, base64 encoding and PBKDF2_ SHA256 hashing, which is then encoded again in a msgpack_. The same data structure is also used in the "repokey" modes, which store it in the repository in the configuration file. The internal data structure is as follows: version currently always an integer, 1 repository_id the ``id`` field in the ``config`` ``INI`` file of the repository. enc_key the key used to encrypt data with AES (256 bits) enc_hmac_key the key used to HMAC the encrypted data (256 bits) id_key the key used to HMAC the plaintext chunk data to compute the chunk's id chunk_seed the seed for the buzhash chunking table (signed 32 bit integer) These fields are packed using msgpack_. The utf-8 encoded passphrase is processed with PBKDF2_ (SHA256_, 100000 iterations, random 256 bit salt) to derive a 256 bit key encryption key (KEK). A `HMAC-SHA256`_ checksum of the packed fields is generated with the KEK, then the KEK is also used to encrypt the same packed fields using AES-CTR. The result is stored in a another msgpack_ formatted as follows: version currently always an integer, 1 salt random 256 bits salt used to process the passphrase iterations number of iterations used to process the passphrase (currently 100000) algorithm the hashing algorithm used to process the passphrase and do the HMAC checksum (currently the string ``sha256``) hash HMAC-SHA256 of the *plaintext* of the packed fields. data The encrypted, packed fields. The resulting msgpack_ is then encoded using base64 and written to the key file, wrapped using the standard ``textwrap`` module with a header. The header is a single line with a MAGIC string, a space and a hexadecimal representation of the repository id. .. _data-compression: Compression ----------- Borg supports the following compression methods, each identified by two bytes: - none (no compression, pass through data 1:1), identified by ``\x00\x00`` - lz4 (low compression, but super fast), identified by ``\x01\x00`` - zstd (level 1-22 offering a wide range: level 1 is lower compression and high speed, level 22 is higher compression and lower speed) - since borg 1.1.4, identified by ``\x03\x00`` - zlib (level 0-9, level 0 is no compression [but still adding zlib overhead], level 1 is low, level 9 is high compression), identified by a zlib header (``\x.8\x..``) - lzma (level 0-9, level 0 is low, level 9 is high compression), identified by ``\x02\x00``. Speed: none > lz4 > zlib > lzma, lz4 > zstd Compression: lzma > zlib > lz4 > none, zstd > lz4 Be careful, higher compression levels might use a lot of resources (CPU/memory). The overall speed of course also depends on the speed of your target storage. If that is slow, using a higher compression level might yield better overall performance. You need to experiment a bit. Maybe just watch your CPU load, if that is relatively low, increase compression until 1 core is 70-100% loaded. Even if your target storage is rather fast, you might see interesting effects: while doing no compression at all (none) is a operation that takes no time, it likely will need to store more data to the storage compared to using lz4. The time needed to transfer and store the additional data might be much more than if you had used lz4 (which is super fast, but still might compress your data about 2:1). This is assuming your data is compressible (if you backup already compressed data, trying to compress them at backup time is usually pointless). Compression is applied after deduplication, thus using different compression methods in one repo does not influence deduplication. See ``borg create --help`` about how to specify the compression level and its default. Lock files ---------- Borg uses locks to get (exclusive or shared) access to the cache and the repository. The locking system is based on renaming a temporary directory to `lock.exclusive` (for exclusive locks). Inside this directory, there is a file indicating hostname, process id and thread id of the lock holder. There is also a json file `lock.roster` that keeps a directory of all shared and exclusive lockers. If the process is able to rename a temporary directory (with the host/process/thread identifier prepared inside it) in the resource directory to `lock.exclusive`, it has the lock for it. If renaming fails (because this directory already exists and its host/process/thread identifier denotes a thread on the host which is still alive), lock acquisition fails. The cache lock is usually in `~/.cache/borg/REPOID/lock.*`. The repository lock is in `repository/lock.*`. In case you run into troubles with the locks, you can use the ``borg break-lock`` command after you first have made sure that no Borg process is running on any machine that accesses this resource. Be very careful, the cache or repository might get damaged if multiple processes use it at the same time. Checksumming data structures ---------------------------- As detailed in the previous sections, Borg generates and stores various files containing important meta data, such as the repository index, repository hints, chunks caches and files cache. Data corruption in these files can damage the archive data in a repository, e.g. due to wrong reference counts in the chunks cache. Only some parts of Borg were designed to handle corrupted data structures, so a corrupted files cache may cause crashes or write incorrect archives. Therefore, Borg calculates checksums when writing these files and tests checksums when reading them. Checksums are generally 64-bit XXH64 hashes. The canonical xxHash representation is used, i.e. big-endian. Checksums are stored as hexadecimal ASCII strings. For compatibility, checksums are not required and absent checksums do not trigger errors. The mechanisms have been designed to avoid false-positives when various Borg versions are used alternately on the same repositories. Checksums are a data safety mechanism. They are not a security mechanism. .. rubric:: Choice of algorithm XXH64 has been chosen for its high speed on all platforms, which avoids performance degradation in CPU-limited parts (e.g. cache synchronization). Unlike CRC32, it neither requires hardware support (crc32c or CLMUL) nor vectorized code nor large, cache-unfriendly lookup tables to achieve good performance. This simplifies deployment of it considerably (cf. src/borg/algorithms/crc32...). Further, XXH64 is a non-linear hash function and thus has a "more or less" good chance to detect larger burst errors, unlike linear CRCs where the probability of detection decreases with error size. The 64-bit checksum length is considered sufficient for the file sizes typically checksummed (individual files up to a few GB, usually less). xxHash was expressly designed for data blocks of these sizes. Lower layer — file_integrity ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To accommodate the different transaction models used for the cache and repository, there is a lower layer (borg.crypto.file_integrity.IntegrityCheckedFile) wrapping a file-like object, performing streaming calculation and comparison of checksums. Checksum errors are signalled by raising an exception (borg.crypto.file_integrity.FileIntegrityError) at the earliest possible moment. .. rubric:: Calculating checksums Before feeding the checksum algorithm any data, the file name (i.e. without any path) is mixed into the checksum, since the name encodes the context of the data for Borg. The various indices used by Borg have separate header and main data parts. IntegrityCheckedFile allows borg to checksum them independently, which avoids even reading the data when the header is corrupted. When a part is signalled, the length of the part name is mixed into the checksum state first (encoded as an ASCII string via `%10d` printf format), then the name of the part is mixed in as an UTF-8 string. Lastly, the current position (length) in the file is mixed in as well. The checksum state is not reset at part boundaries. A final checksum is always calculated in the same way as the parts described above, after seeking to the end of the file. The final checksum cannot prevent code from processing corrupted data during reading, however, it prevents use of the corrupted data. .. rubric:: Serializing checksums All checksums are compiled into a simple JSON structure called *integrity data*: .. code-block:: json { "algorithm": "XXH64", "digests": { "HashHeader": "eab6802590ba39e3", "final": "e2a7f132fc2e8b24" } } The *algorithm* key notes the used algorithm. When reading, integrity data containing an unknown algorithm is not inspected further. The *digests* key contains a mapping of part names to their digests. Integrity data is generally stored by the upper layers, introduced below. An exception is the DetachedIntegrityCheckedFile, which automatically writes and reads it from a ".integrity" file next to the data file. It is used for archive chunks indexes in chunks.archive.d. Upper layer ~~~~~~~~~~~ Storage of integrity data depends on the component using it, since they have different transaction mechanisms, and integrity data needs to be transacted with the data it is supposed to protect. .. rubric:: Main cache files: chunks and files cache The integrity data of the ``chunks`` and ``files`` caches is stored in the cache ``config``, since all three are transacted together. The ``[integrity]`` section is used: .. code-block:: ini [cache] version = 1 repository = 3c4...e59 manifest = 10e...21c timestamp = 2017-06-01T21:31:39.699514 key_type = 2 previous_location = /path/to/repo [integrity] manifest = 10e...21c chunks = {"algorithm": "XXH64", "digests": {"HashHeader": "eab...39e3", "final": "e2a...b24"}} The manifest ID is duplicated in the integrity section due to the way all Borg versions handle the config file. Instead of creating a "new" config file from an internal representation containing only the data understood by Borg, the config file is read in entirety (using the Python ConfigParser) and modified. This preserves all sections and values not understood by the Borg version modifying it. Thus, if an older versions uses a cache with integrity data, it would preserve the integrity section and its contents. If a integrity-aware Borg version would read this cache, it would incorrectly report checksum errors, since the older version did not update the checksums. However, by duplicating the manifest ID in the integrity section, it is easy to tell whether the checksums concern the current state of the cache. Integrity errors are fatal in these files, terminating the program, and are not automatically corrected at this time. .. rubric:: chunks.archive.d Indices in chunks.archive.d are not transacted and use DetachedIntegrityCheckedFile, which writes the integrity data to a separate ".integrity" file. Integrity errors result in deleting the affected index and rebuilding it. This logs a warning and increases the exit code to WARNING (1). .. _integrity_repo: .. rubric:: Repository index and hints The repository associates index and hints files with a transaction by including the transaction ID in the file names. Integrity data is stored in a third file ("integrity."). Like the hints file, it is msgpacked: .. code-block:: python { b'version': 2, b'hints': b'{"algorithm": "XXH64", "digests": {"final": "411208db2aa13f1a"}}', b'index': b'{"algorithm": "XXH64", "digests": {"HashHeader": "846b7315f91b8e48", "final": "cb3e26cadc173e40"}}' } The *version* key started at 2, the same version used for the hints. Since Borg has many versioned file formats, this keeps the number of different versions in use a bit lower. The other keys map an auxiliary file, like *index* or *hints* to their integrity data. Note that the JSON is stored as-is, and not as part of the msgpack structure. Integrity errors result in deleting the affected file(s) (index/hints) and rebuilding the index, which is the same action taken when corruption is noticed in other ways (e.g. HashIndex can detect most corrupted headers, but not data corruption). A warning is logged as well. The exit code is not influenced, since remote repositories cannot perform that action. Raising the exit code would be possible for local repositories, but is not implemented. Unlike the cache design this mechanism can have false positives whenever an older version *rewrites* the auxiliary files for a transaction created by a newer version, since that might result in a different index (due to hash-table resizing) or hints file (hash ordering, or the older version 1 format), while not invalidating the integrity file. For example, using 1.1 on a repository, noticing corruption or similar issues and then running ``borg-1.0 check --repair``, which rewrites the index and hints, results in this situation. Borg 1.1 would erroneously report checksum errors in the hints and/or index files and trigger an automatic rebuild of these files. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/internals/encryption.png0000644000076500000240000020452714601576577020101 0ustar00twstaffPNG  IHDR1fD}gAMA a cHRMz&u0`:pQ< pHYsu0u03rtIME& bKGD̿JIDATxw`MWv" ;Č$v콉֬7:uV[j2ZtJCiT;r眓b*5AkkկUʒJZ$&$&p?Ѽ SNKjj2*8-s ]XyyOIYm[q" ܏c*Sܱş?p4 ܟ <#Xz +m$&$&p?461ɎW~'ay%1 1[şJ*VU4:Za7FEULUlj0Ũ0kڝ$s,gD螫 uLjTYx5ohk,EU Ԋ79YfeXRIKyJ:Fb@bӒ4VT_t-˕l TfIRٿ~Tat0)&L;l(ǒez>$LR2 !(\_6='tz9KԧZ-4 i9s'EF^F[jjZv^˒/dN6=c!$&L*9.䄯L&7b?|zyX޶߉Y,,p/LҌ| ɭS7^USߤ2'Շtj'M[ߖ6:ٍ%1b_jh2u,%yΐ/z97LF3-o훾㚯Ŵ%1b{TG=U859xcKRtǕ摜F@@b;˜{/0Y^1O+דJP.MR%f^S#= 2NɄ_1IE!:͒yQ5xWcDUTlj8͖(뮚kab+;NwETsQ瓘ZX菲 b5%s}+ωTYF.6^ҥiQqFqtuTeq&[j+ӣAs-yY'/{31%dVjJui}5zW{S3g٪-p4/9~ծA˴M`x0牚/=xx'j4ۻ$g~7Ew-)G޼tsy::̩Ƿs,aUU$&$%rZ۷{^OkLf,'}$b]ا6tIg#ߛZ/6b}^nn2W+yin0ճꘪ{bęiq#̿Ŏz>˸|6ꕈ2|A?;zZ[-6Su~2%(x M|bQ\e,4o+hn~sTWj~p ;~geymvi&TR*zOFI&/Nꬮ%C/rِ֣R^vsĔXO62=DdEo?HL/XQuͳ!r&T}dG`6=bo: ռJILH*D]M/n&fUK}7E֒eAֲ}01#OLԉ̥>ZNR'澺>ڇ9VƦM~-ncQ]˸|btQn$n{fEm4O~*|,](1-wK6fݱdz`bsVã+\o-V}]!5K~HLTP\t拲(q_$I,߯jXv<󧺗 {;3%2D| IeN՛|Jmr1'αo<_%f*rAOɔČ W˯L& lP:ÆK/eYn otC=ULگH.83-i:i0e֗u՘cdVyH*F=Lw-|Q˾*<r߉2E&}؆z1^iKiHX;zF P~q7dR}3r[v!nrev7_3E˳xdɼ^^UY} }IZ-0w1OP]aDhI51Ya ܻ.&b} 6՟ڠr9ΉeI%ewT^iZk-n̩.%M_;*ǐB?yn5fz#Լ$ע :;r~_jSjB|$i?GؼeȢS \N-;IzN72OIZT'Զ?TyjlPOώ){nSbojL{rmDKT ,[:phЄ9&ZՒja}#?s^a[|8k̥_"BmZ/F6O:@a^}=^z@a=ö"n/[7lN7;$IUW ;wiptY&,ic"NU~xE))^zʻd5SGӺk]5WGڔe˵NU@TU5A!zB5X=P՚[mi%ks|ֶTKTM}GDa^ s摾u±z(GG8*sʽ(aX2WꞭ;.nUbJg3zJLᒑT˩u/ dN>7);l"1 1.=۟wtҚ>)_WeL;@Ռ=<"".n}t;$&HL  1@bHL 1@b$&HL $&@b 1@bHL 1x14ʹ_P&~s19L Yrs&}/ӫnnL75isRtviN{v`Ʃw:<(X&MwkUOe~ sҨgݺѫGjKֹیǾ;04@{uIG cVvz4=)@̫V| n|.ltf1l} 4pZ v!=UŞblvߩүEjw OcTq \؁!꘣#J{Sz=][ F Z+m>.dS T:bv`H*n$aifU;@ODZNH2c"ܶ  $&$&HL@bIb $&HL 1IL 1 $&HL@b 1Ab $& $&HL 1 1AbIb@bIb2HL 1AbIb $&HL$& 1Ab HL 1IL 1Ab $1IL 1 $&HLHL 1AbIb $&HL$& 1Ab HL 1IL 1Ab $& $& 1Ab@b L 1AbIb 1IL 1Ab  $&<:}:~2M2{e|rkHK5\ del=\^bQð[zh8YdjTV.!~fwx3T#9C9%SX5F:uf/)wܝvx-k]:/<[ʹ߭2 ]{LUW=|_Q=kxhb;}XlXᑯYAPu 4BN*խɜO-Xn{ygILHY1咔V^n0ٳ$wχZ*YToD  =*~?&+a^ﺬƾ9\:S5v8,&1JzK="ϑHYէuu5ZB`M.Sۘ{+}$IڦUikVi~NZ*ʦZᚤJI5J+4WC@OjRj!ӇzB M-=ƩKoifID-$E~ZWA[Zj& -㚭)0 lGts=w$l@LQª*1I"SFb5)Zv߾m\{&fKe" 37TvyXg|Gb֕1>.a`?)Q,-&zqjAb"M&Xod1uAS{jZ#iφlUzы)ٖ[=`n*2HZm*϶y^{ޖ$]]|-6IyϮxg:/Yr/D ,'7镜ێ #w%UQI;ߨ8KN ;Vܜk:J:eu׋nU 4zA7bwLγ>h5ޱNb4ۼw\&e:xos8g$&OLN-u5YoerČr,Q}Z,9u޸31OJj>$yZ$syXKmp6_14l_egzTZO^T#9{.e{[cK\T婬G"n,2!ܚ9-;Ug{EX 7+I_CO}6嶼;l=MiI0<W4|nOXN8ӭQ7e#7~6IJZ>8]aEȔ$i/ʈsy,Zp|A' Լ +C>ljj3&'Ijz%k9l5ܭB _jmexZ7x,5tmڢYe{_e6z9~311 0YZ_ZoMk^gsȷd~+1eE,ȶ&ͫ>euz秌7NOe~e}%U<0HI[&CJ۫!?V%MԆp,ѱE%,/~.SM5yݼƊ6K\V\DHI+ߧ /eڲUx|F=>dY~{2\?3\rsVt%kxK$&ĔEd&+{=K~|͌+tQVؾ|gb~*s.yZ$9K-juT\}o.Kb"]%o{2kNXv$%hLyT_c&a*ms2":ǧJLgÙsfjf+Wc{#1eYϷh.mzQ{Wl֘bn}=uYg}*HsOU= M+R"YV@eR'uATK tPW5g`]7  Fk#E\k٢NI?.6^4I:H̶ـ=7vE-:*CLg7'^imLv˲ؓnt̛HLļ(Rm:c5x\?OV9g󲶓fboK*Zle>[sb$-4 *1~tL;E]5zf8٪1u [^^7d~ct{t$:,yD;zRz$*,zޥRnZ/2'|,uitdyY}}"Rn8<,SH _7k8aAF:#TGbzXHH>)dXӯ{2|r]PLen],2o7&ʜ&h:jc&FӣRA?˂T&zFtou:3>]Ḣ.1_ !2\q+|K~jWw-ە^ *B4.O닟y掛;nr1Ӳ߸A$E* Y*=Ob"%WJUj7q |0nɚ;Tbc492,F1~3uY/v$t In z\&s6T\ՂW+UdEW[N*2XmWrsw1%LTϲ#SgMoG/u$fݳyḢ 1kU (yy6&sճ_߾{'d6)#}N1޾[`tGbZu^V /L?e1uWdNzRUw;:}W\)i21jbdr}ߟ,_H2!Y<%Y+=[+Yj[,UPO :T9S |vgIqݺ1cHPf^Vgtr=2B~vN4%۴[e.jI|}bˡ^W==yzjϚMb@zH$V5͓4?n&+U~nOLI23ʛyZuR?$AI͇xzۼR_F[kJ*ƵsCa$|Mևڭi-s^UߌQHLܠeֹKijhg;t0WC2_k?\ȴXW4A+ZD^;7jbz,tFWIY5mZ;R{&+*2m4D5X4Ge2fDeA>ن.dN4W2׸uNy[9_52%B.N1Z"1 'j^{\c,ƨ'%JȚyM3 bn?#;,=pLmyľ9>G[iɂ55_=="_K^c-!1&sJ.dyz–"|'<_酜Uc+4o/Hz%nnW]s_&,?+\Kk2YI޼]s*ZͧRoVmTps -wb+f,uNXR!ZSg")Q7㿒#1IL{Qd٭׭y_,g]ڬ2IgakGۗT;~nd/,*Rs`& C5잉y^aX_emڣC^^-Qj???]crb rͪ>ˣfaENMin`.jl<7V,kٝ&+t;F/ݿ)OyVOb@ZIP4r7YjB&&l/u(Q\x/_jR~s穥.Uk2J\vq(iK%.wZoNnhKuR|g?_"͆ɛSkILSZꠥ%)0bPdeZ@ǵx9%\ :t>4Ϸ2'|7*H9&e-k9UPc/ulQzQ`%t>O=s(:$UV+ұ}s)ש=c6)ۅ \MC[DMҭu:"/<|ܩ3gt׽sQ?ex>ۅӱF3]iIzB#*7by.T;hLYQ&RS5G0 tN3n\\ 7߈(>/~G(f^jh~k2 W|WcX(Ӈas ~?G.|ydm:ߙ[Ǒ\?7ﭳ434U-d>D9'29ļ|UQ"c#{Dm- <{(w4YzmStcsilMtr>gKn.ԉy 7LKDUy->g"G}xǛɪ!sQ+2.26Ԯxn5P3Տ9'DUliL5@و*QuV4׹kʡʭ9DFFYМ&Z,/4 Lb 1iږ>^Lojʽ-vifk贖k&:}<niѫj򪮩o=:i!1Hc:6ʩfVjp4E^M;4GoW@N 4/n2}/iʫzj~J|:}ъUy5]Nu6}ɊWy%h>@iڬv*jX5ڧZOb 1P"1Oo9IL$&HLY$&HqV%Yå @b[r6Y"zL*$& 1AbЋ2i@bpTGu@c@b .1G0  $&$&HL@bIb $&HL 1IL 1 $&HL@b 1Ab $& $&HL 1 1AbIb@bIbHL 1AbIb $&HL$& 1Ab HL 1IL 1Ab $1IL 1 $&HLHL 1AbIb $&HL$& 1Ab HL 1IL 1Ab $& $& 1Ab@b Oc֤ILJ̽K{FԦt^3$-eϖj^febzYkeVW6"v`H2%VIGEmVcxMb/;@̳߶GEo]Nkv/ 7Wy&,Z!]k.K+b~}/s^]bRo伽d\RO} I筛.26pd[щ%f :# UU|:Rm.-VtRg؁!W\:Cwt.DֻF5rb4*"G3 1 HLHL@bHL$&$&@b@b 1$&@b 1 1 1 HLHL@bHL$&$1 1 HL HLHL$&@b@b$&$&@b 1$& 1 HL HLHL6fkh &OʴCDQm$&$&;IshZҪE{2=kRŔ^ ?$pbף.Ewyv3-P_#,v5Z+/{ڝ+0wK?\4dUέFoq,VUF/jq6uki-;LCyq*1wjNt xоvdjYJbi}m:$Q;ܨU*YM Z溁I?!ʤ ~:l6Ed.]tH[D]5PͤBuZ.9ZyY~m\&x gRĜ3aTzg[LPݮ͞ 6ke.{1- /$em}& q#8) ظVwz LZ6ZR1Fm3c>:6_Ia# T@p`D۪UѪu֪UuOܫ N\u[g: 8~ AOsirsN;Ɉ&nli5F%DH!'+zJl%"K<10S"̫nc\fukk\:7(jJ!۟<&0_<&0 eTcԿ}G-6[$n\^a!S\O:%:ݺvWcr w19Y`:u[e]] %(ħ=[\],Y؎<&0@>940ɇx+d8<&0>9L¦lyLaQfsDϛyLaQfsDyLa}TBscRN1.vpkqlCZlX5 7JQq_. +p -ޏ[—nRUG묉\ݔ#ckaѫ¹-:V5b &)hW7ψiPv{_ ߗl8\u}LEtנ^VR8>Uo>/3p6/doZw %QlT_45_ (oPT=<8:jxxp+c8gZ>imV^󽿦ΐB :T\y&2u_y(ȷSH?* wK7Qa` GV88>ҘZČI4{)ϣu;#F|GM<Wa ma{'-wK7QI9+%㹊 rbN ;)w9#! KHvhr 0Gin{bLL"&&ICUƃd@YOkyF&&11db҇(=1=ͱ$"&&11LLP8zd&LL"bbĤU]\Yt8fKV2db21C5Ƭ)5 ʷj3IDLLbb21S[6#1ƾarA=3$"&&11LLʷbL,ByFC<+-osDL/31ILL&&>%uybz @2!djK,&01ILL&&>EFZad˫bbLL&&}`|t ?LL"&濦/B<?_`Q"g9&&Y%~a_q#?n'\0{m ~DL,Bc>N!sK[ l?i?!0_4FZno> AY$[L 4]ZmjQ}1+΍Bldm>E'Hc3 &׸٪lE$} Qh mU)EODeIկY6ֹ=JTؿ$ DvuA1z @ 8ӬUYCX5eJL\rvV !h.R>ml/u$,6/Y LEGvR8^i=]]шFLYZH01UcaZOD?=L*6\H5>$1;bbF`/EPn[9vB 39Z)X*=}kW$S;Y&fm̝n!IKL U mn9u7R ?$bbAGz(yߔrwPc\HOLG#fi)oF헿^? o*KqZfjR2m>(1g&^J)y@X\<呎@&;#TF zRoCr41aBU)nP%D#iN)N)eR'h/h &?"lj4ర|mDPm@.+3t@ sXVp;<"n1T)zC܎#6Bsu!ofbQLL'8QR|vD.8Q}BS=hݽ^˧/#;qW~@|@q\3.qlX۱/1Z`<%(e9rT;8=(_VnjI;s&CfbS@"6brx 5{u0+XD)nnY#Oo&uZwϮf^^^^^^^]?FeoCR!Wj3ltbv}!`p]8g{={u^㒡ٲB/ 강MZ "0twjID11nc1,kmT D Y♶7ތ:X~J?%X 1ɚ#XX/-HBQe +^x8N)rFm,yg{ >uA)Gz{lG-4w4.[bnLƧY /8*<6KPU c 'DB ؆Xe)o'fG"Psbڿ?耪LB[ Qt"Lgޫ5-R kxD`zkՆcb!p00 w`#N?}g?1c Qo@wnc[{vؑ!F]3 X1 '|Xh).6c 6 SEء騃j1'q,BjpCwC]X Y) z43`/:Xe?%"3 [1 pKi9 ai*pAT z01_sM$hCtA\gh1=.&RGnfq-B([. >!`xC4fcCi騹 Q.2x|wcX~X>[NmMVodƳEPWłc?>*A(GH;?eU(7tNdIOm\̈印2;ukid.3VfQ,^V;XS`S~?c1ͮFC*@$eO$bb6FaHdi}SbDKrWۛϯݥBWXN6KD0Z!ڨ]=hJ1s6իR?t<s6kj)D%-l5T_F_9&% @v%ϛ(KQ͘Qp&AoUŏ{NEgY8]ZNbvD;^)喇jPb]IZb'XD8Te9i`{2XRo_狦[wA_&n.6ҏpoYeNl/cNI]٤^glȖn'qIFLeV=:58!80ߴl3/lr1N_&7 oZ\J_&bU\2ER+n+RϺm\D!~v;4kPc6R=C߳V =b4Y>MmWq竍ۜ͑Oȼ9a~ƒ2ݶYn8=A6~=䐘 [|M=:{ sECIƋNhϋfbQaHLAoQD )y̖;sLfǁ,¬D)UVDZPƬbk**шZ JW9"Cmb }YxIQf~]GT/-61ƧCͪ97f}%M!bG ݯ=:1!:E#oP:VRDob&x'1atٮuA>81c"z)REϺQ&f,vqRTBuҤO& !C~SJtE# om@iR_I1|#*)uoQho71{w%ǐbҩM\& .jRBtE?5}mI%3;D ۚQFH#רo}38 HRߨ\XSW4cjD#mf<t('޳V'CJĴ!kK~)"/:lh?HtźO١l#e$c? ]lwGyO("No%4B=C 귻9y9VbR3WG[VpID?1AtrYhZBX1U#M 0HUi=7^{؀ ˁS!8zzmܛ$~qhBlq]˲W_[yu:foH4W"J_'X]vjxcXI.sr>[&氽oNs@b{!vRJƧkG3H1$|,+Yc@0ەb&GOLP>U=ёKD/+s4=W{3tʢ,CѕdI3 EWNOk܇XIш?_n RLo%43tC#B[Y(=8mv/`DTs &cu Wz+E-HY)#6p⡨=Hif!1G fb,Ƿ=&&Rjp1D)h$d($[bNHi Sa6}m xF O61Ǵh#l0JT HGbf,N`Y:-l -P Unm!JJQ:&fN鋩)O6ލ v#ѼAHIRB_\ .6k,O7g1 DpJfa0ñ#5i n5^l A2Oʖ[@IL@T;tDGL&GOaXyBLUm2LUl>Fb6beHi s->_A<@.د+0j@On1LC1lL QI+31?؍EAt5~(?9]J &Cԡ4]b&31z݁M8%_ˍ5EF'1$d_bEGEZCJ+_&FS@ 2gW``۷xgݟ}kR`O5;ILD{NQMcb~Č{@6X%L#Y’y+բNE/H11&y[bVK'MQf Ejш0d~";f2Aᙉ@"8Z/b+:C[ o'&s3S!ŤGxV” 绛e&f+yn@\d{Fbz,FWXQbe3|%,k2)h(Gi&%C 44 JѕfW_TZ eR:xt51z3ZCS? 1O@tRl#cϸZ3NsD>WZ~Ra:@<JlΘ WL|wD-QD ՟B>= ^@fb=dɺѫ D!anÿ4H! GT|T\ %Wi}>41N~c;&.;!Һ],EJA,6L^8-!6BN[3.S.@d\fbf.>.EB,Ϻ:e&fݳ;L Ez'1d]q)V\r9$V ϯ{# %:&U!F+C%X,fbQLDs@WsuO? owa=DQ{@<4O-MC)ESbv]x:x;|@b^(~/GNXxobu|pbS! ^A= )8cK!;ybS%2GkR 5%1-{eԗB:~u# )%],09x@ʜi.g?X3> WtoSQkHyiK}|ܖiY88[!31bĴˆ&B;&M̵Hb4Kb6!1+@S')3e5h!=\n)DT1vb3Nt:`$+|["ZS#K9yJe7 *E_Ư[bZ#17T 6k*i~4%&yOXTث"'01sNh0GMn$+"1;4z#'+5zuCz9JL,09c@*<] 7sN* @eZ *>yJ.bALLWP%|9UN>8R|~5lF9@? }~g&&3s^ ~B 6W|du~n"?O'BTQKD-WqM(A)_'뗠B $sf9 P/$f%'o r+ݍ迕D4Hb? |.W  yy+13_b+ D)͏=F.3t?"^X|71ۡKe? 2p[&! l}RLڷ%QTE# 1__:W*ꬷ&f;tw1S[9URf8U!-c/pbvNs=idf'"[u'?\Xj~}Gebѿ^ Q`!WV?h=@ {'" 5 Ynb<,"lH?cO? 6i}zEbreMn'+DRG[Mʋ!A|-1]D-]"7;h%fX,duRRB{힗ļ%N*DWg-%&[a vC8Sy4PgMJG42phb 3\W?& 7pUBsd:! PvR8RƹP!F)F,.Q]duޘ0@2kbvD7xQHacMu*4XaM A4D!yH SDTc 'a1ILY8-pn9Q/oyŖY_z2x7&&84~ )zͷAtWpq$v2M@"b}}`a_#}k~KbB~_nh>B 7:ŽnPRL^~ޙkMO2qIL3b\UB*RVQ j%{vQy0|q1 `' mocb?&cz_wLW̥xlNL'=3*8߄Ȧ-)Uqymo0?*p,3o%f {@,$(Zt1q Uspjz-0Hȵ+v]z;Ub(AJѓG80 :#6^\ EuNFEb&/6!1բ/3Cbbb"/!u.4>b}mϫ4u1fiSGR_'y_+a謭twnG&&Q4~77;WȎ!=,E aS,|Zhi8WZP Ԣ:皘*{wn(jmz' 'dO>U/7Ncjyd"}RG EZKtEWtE=Ш[F * ս>zrT9kj5H~'@Qѽ;[Ą~  M|&Obv{!%@j&m7^/'4`R_ ?L+5ݨ6JHuzfӾ8:1ڰ=8 :=XJe%_A*%/Fbb*포uꅖ|z)kbzmr,[pT:)go\SuҟvW9Ji!1:R,epQ0׿5]Ĵ،:1[Xǽ1ZG,,ipPiL&&@k߃oe$}|3ӝE\1op͋1s g->c+c :Ѭg(ߋz)u_C11DW,^)բ*U#rW:!1hD VU]#,l > B 8yST<$>=]WV~+6/ D+٘EU|oCtkaWW 6k8-l/ۼ]k&fݸߤґKEgc9H#,X Ҳ;1c*>oRĐv#Bk+o(0 =/)b,e/5W펹@/}c bw{]Z) R\+᳘:'sQ EbjPsF v/6D|bZy@ GGU]T 6fߘLՒMO0rPL;q|eC4Fbce3?qb &^g!*˛cLL"*HgKCAyG0DsQJ X E-y4$؏u(@E#}Vvemaj1Mi4(H4QJ^a y|ZxI؁M5WDO EOZľ$&nQRQT5a`$!/>--A<_ҤoJ艑hDO\n=J>`cYJc=&uB¶2/: )"ĴMK΍WoYR]BL~O'>^f/;NŒPd׈7|{5>`R!b(J,7"Sḱeb!3#(1ͳяfK!mC0 RB X D% A~(s@z}}\LZq7g+snTl*DWD'ydgP銯/b~,Y_brYV=謹(2M&w$sYMb^qjR䢫Y an%Ω#QZnOzLOLǠd帘@L\,lܙf=` i]HYkUC+"3~ZccbQAJS74Է6-Ҡrc}FjTTԒiWT~Bbֺ}r-r׵alBj~4}I+:>}pLH _^4-"mYj4_ )^cu0rꇪZZVzV5ähgԁ_\2-3_fw9z%&\@ti=hbZ,SslvŌ)J'pצV|:;jzhDЦdzϻOK]?5?~;1{{\מVXh0yHqsXy혖LwfA4*˫izs}qzqwPOq.׋IptWc[F6) =VWiTbӪ>oN QAGx>uSTT˴rw7elT!)^^#sK-.xk}˶ke&fqwFˤI-V~Ae"Q?*Ӳ=RE =^8n{*?)jZ,R._lR]N:L93orU3=Ze mMsyt0'_^5@"~)5EuKYqWg z{?s}1,V QӋ&-i%#ݧ71Ƣ_vJ &U$QarLpr6P6Y#!qq,cg.\ ^wL&ϢZOVO@E|)[3 ޞ`N?:a»SXϦpHrN!)%`) qCrc~ g8G륭TETd\> W z|d2'#aϦ3qpLm|׷A}{mvmUQ5A701S_|_`TKb`<"oI%!H\Mu{s93#nO[ΛǫNrjj縉jot_Tҿg;'Q,F4nu$ry>JV?A4['kU{=9+ޚ6 0;bbQAJ$\̣F;X@x Ͱ6e> 5yqwp `&&qá7!v2&btӳS6k4͸ˁLO3XPx!ξu~ Vc|k+Xqg2OLFMbzD\ﺿ9.qb Ҳ} !G}|ؖ1gWkuO78әX$gyTAbE]؈H8"Ba~K,vZn`9$en؂w|XЇs,S&&I̋pGp+ӗ3gJ%1 V;ox}(gc0ѾarjUt!|IDLLbb܃q 1zdfb21FC$"&&11 WbC_ҩ?m Q! RV<o(R\uć{܄X5+&$"&&11 {b>-1),!&&11db?dEY.4LD$&&If84}5P H`11׉ILL&&>P_?7n fMIdbROU}qY(D{'G}LL"&&I)Ygg~_ѴOI$bb21IkqFN"=hoفIDLLbb21aV`mSDI^u0db210n?ŬruD5(@$*>pp|qjĜ{)3t9-yvb-vq_f{׹%mtOݟ. ][nϥDF3(}rMsp|Q~ϋ)p6MKy[~B} ^2hm+% z]f}Azm9q|&* à/S98>ިװ^GcO| | |*{=֣ N][nV}|p0_ QU}"|6_Tpڴ'էB5s^ܗنo(O-x$!֗YėnB ?5ħר9pryi[Hx } 3 d_DDDD$""""&&11DDDD$""""&&DDDD$""""bbDDDD$""""bbDDDDDLL""""bbIDDDDLL""""bb11IDDDDLL""""bb11IDDDDLL"""""&&11IDDDD$""""&&11DDDD$""""&&11DDDD$""""&&DDDD$""""bbDDDDdbDDDD$""""bb bvc_9 0 㹉(.^fI'Hw,V&""""/KDi8<9ة=D:Q37&""""ityT@Jmuwg#f4j1tkNi[l&""""ـ?jmPRbZ^RB42p\&""""&fKoSۣA+ DDDDD_D@q>L$"""yLa<&0o>9L""""۲cro19IDDDD7$"""$ S,=NDDDTPU 66R/r[* Z睗 %nZਓRE5.sN睗 k[8'jOG;/T=W9 X[;/=A;nBږcbDDD$&&IDDD$&&11IDDDdb11IDDDLLbb11DDDDLLbbDDDdb21LLn """bb$"""bb21$"""&&11LL""""&&11ILL""""&&ILL"""bb$"""bb21$"""&&11LL""""&&11ILL""""&&ILL"""bb$"""bb$"""&&11 A$Q؋Εr@ 64DyI9\L)J`o1EѠ6:m-p{15Dys>gl~}bcKĺnt({;/Qưoٯ]($@ Āebņn8++B{ls58A(*H !؅]SLnfLNŎwna"Mvs^}r=CwX꫱cPAߗ厖}lz61DT9iWMQ6EC=0o{@ ;Mr3+CQIƗ!Jx\w6U-~\;RkaFe/-hQ90$ju40}@BommľĈAuR:s݆IDDD\ #-e#a21esH3n@BhpR1fW^ L/zwt Fu h371TiFV%H=31C1zgb~JQJ#㫉j-~,pRUF]NMT!bީf%#Q9'1/.5HaLD6UI(%vbbk]ǝG[oWH1>Ĵ-vL+mKިX'ǔ D 9Xw.'%[{wP{hI]#g￯UѣoqgCLƨ`tQihJ.n%71%ADOH;&31连!S6]!uNN/D)'OKm  \{ϑ/-z,N λ@yW-j1MKݹᎎF(ڮr__ Au4sNAH#O5HFDDDNLj*N fB瞣?_C UmìaTD/1]xȻfhQ=nE8 nHU\4CK AJHεOLAeMHDDDNU 쵁A bvDWn<ĠoMLRDDD_OL+tU"S!U75@\NLZbK왺[n w|Z|:@ ELMͱ #qB$d9ݗ[A3(b*<=(>@** iv^=~V "a{}p+!ێp=l$"":1@B )![6`[^?/Qg v`A?igRlDw_c\z9 YzZrFYYE^yaB;K! }X(@zY:"Y_}yI]Fh1SO Uϯ*:h3g6̟Rd;v~ $""01GbյV phK '_bjs֫{Hkgoq\6Q@ 'آ fa[9RȻ#6Zao=iπZЪmWwkDhZL*Rq|@tVF;"UmTh%H] (Fh9SO '}A9KLv~0M1IDDDļgZJ^4AlQ %lMQFFIι%SQsb@TwTԫ`fkZm{Rؖ5V+ q84*$sְ5 ?E nݡ=71kܜQI`W-BV[m{d3;$ E|pb=x ~o=~tE!"11KT^*)*M%2i3kN跞I3?@bqsTv@: \B 5D-QFoT/Y ("u}pbV!DbV=^eOhgbgP#ay^\ J _/PRN T=A~xd9crs1~i2X*mdwQ ڼN%@l$]41z@{h5: EYtAĕ Jܗc1œy 9i(T$y;\NEqIDDD\ =+,,WㅎhA&_JL S3)'`2l/:-gA*.9m?|+I(5A<ύ3xyN oCrQ\QH1ź^t !o}I:1LrZ#%&4N(wZFb&# ÊA,v]31J̭H.}RRZڮM[*DOjFa#H1:m9$@[mM|'*1_qG-÷tD-!.}_@,6n[}v޻-\z. #R4!-uJ\Uz {;?qXpMK bGu꺥U=]8o$&0 JW9ѓEqIDDDĴF\ V7fZymXO ^'ŠʞOO4$@ y1|bRD!hZ b{8벃Z$=TR$Sc`3(D-ϴE!J)8zB x6p 2<I[;v070`gӧC b{c1WgǙ*QKneBeLVT !W3gm qF]DDD_LL`^/U6`ncRikP{I~kB<|O]ke)t?b%ǘ [J3~c< j cEVMö{ܩ$gYgY໺vA :ϲKAz"^ AiᶽϲzM^yݜ~R/8-TDo?߫Uқ,`aeh~_WyLߊIDDD\_p`yM;ohV;*QSo=ưGM؄>X@ Ց^:FR a Qu_cpћʋ.WZ<"u6瞧`@/T8͊1&?9yO:g ެ-{.\"ÿX31这wqgq'rumU154sr ~b7&b$ !'KNn4[nXq kX)N1vLA2.Ho3Kq 1q?Bg9gKHU!٢ }pGpqIDDDĤ$"""&&11LL""""&&11ILL""""&&ILL"""bb$"""bb$"""&&IDDDdb21ILL"""bb$"""bb$"""bb21$"""&&11LL""""&&11ILL"""bbr"DDDDLL&&DDD$&&IDDD$&&11IDDDdb11IDDDLLbb11DDDDLLbbDDD$&&IDDDT8sf<~v#03_Ѭ N} >ɐ^%""*qf~~#,30.3833gAOevϩ|QdtczXO5ok%ֹ`yO_"""**,F!?;<:vgx1 qO_"""ww@DDDDLL""""bbIDDDDLL""""bb11IDDDDLL"""""&&11IDDDDLL"""""&&11IDDDD$""""&&11DDDD$""""&&DDDD$""""&&DDDD$""""bbDDDDDLL""""bbIDDDDLL""""bbIDDDDLL""""bb11IDDDDLL"""""&&11I)݃` w!=B:gV |ܷzwd{]}d4%.bz)'ubh:9dXg\I"~4RY pe͚mVoabF8VZֽ[8\X.`w3~^q $fbvߵGj08 {ծ?Ls>tĄhVR⨧exsϟJpkabmqj!hI`bPyBMo:L4DY*gnn8WD#rQ}`4|ĤϞ`;ֹXVbDbh<0WNBCSf*_ȌlV^k%=A*9ATxEcjSF3yV靃 ڹ000>9X=Xh׭D 5wZb)gݽp>->^\ø8ֻ(aQ:ý9!|eo6LWi%R3V=cg&BVUck;jr}4J'-۵~uiDT#hj_86c;>-@2vp>빆hԑFWv@V<1 Ua c.H!Y븉H3>{xLAd6nb%F`%RqD=q`6_1 >W1 >W1 Ufa5:"U c!]3g2O<G !Z[niK(VCr&W@bfaF'w/77xVtų;灃wOO{Ç !n/sr$k38]w9W{7x]yZ5 t􀌪YV%4ݏ3UJ7OL|Hg#ߋ%"1sB=oϪ8ݷ~偏XS֌7tF O/a.7L|XSԺt3peF5vV5գ~Z8c?8!VF" ݚq|>c w]ιvHz#_>fzdb?. ~ )~8? jI\M '*3&zNT}R-:hS/g3) P󇔾.Wh:2,LL&&11T9c,+nEPeLLbb11ߙ28:P ~v(k31LLbb11s;o@zt$&&IW3=H؀vuw$bb11Y55wiEEk LLbb11EVZ_*^z^[&&11DLL{?d@:lvVԱE{01I$bbߓK:h7F|sX&&11DLL0eh_Z E]01I$bb&u~՜v5V$bb11Y$ݪ Eg&&11]b*1 (1b'f"s>yM`1$+t!LDA忒C0X 20 UzD'@ 6޵f01oYrף:߼:`N!Mbsyͷ]]v/I$X J`#rҾ(ٍfp XY*؆e8h.C{1o h5%V0yXP~-O S"Mg\K,CnĤ?K>S]s;sݔ7q%j令~|[wvj]&UTVkmߓKm09Oo;ރ`CK쑌O%ngKھZk8`[:=^Q2]hsKzt80`$njCWmsx:aRJ³fYq?UnEpC :h6y'6㷖bY0rHv$f7{#=oRp٢)wVyh91/Gf]M_VF/yrx8\dbһ/Zڎ]5vǘf.ö%RC,w h7yEeǬoYT5@kNZ9Ypˬ-(F]b/QDS4\[1E>41SrG^ügD?ci[\U=mǣ"G4^ٽ8+!`6Q`X] I21݉f&v7t=]yuBGsD3(E!b*'Z8u͹l|Sy|3#:b,Yގ BN_E-*QZ^ߒ })]Uv o4|&#X(E%JђROk\4ΪYb.%D`ALrts7m di@)ZbaĬ,o*hevqN@(bvhiyHKJgV - hݯӓI<5ep/n bmjjgCp0CK C>yV1w֕w(hOLߥ-E  _z>SI'S}rņg|2đƢ-"m%G#CORLju^UhVjM?rj1\*1Ec艎h舮Hi+KC珑!z'WXYےi־t<>LB帙ds1~nV410Jb!?eSFb] m);cfb&2|[έ,G.]K &Y~]e2J=t7DahJ-O{u׮3{׶.' HCLD]D)[F:f5Ɵ θRQI)q4cQb֬e)qECRwվ}TiIaݯ&a>_e@J Q6۠- :pAskfuiR&ށh_:C) EU,S!b(7O˖ٸFK̩^}ol2|Y^VXMX3D[*mnCgx]b5]bWظ Q]-& HPꕮlR@J'ɟ]5M~L4_9X]͐}聚n:r'8^JMJn5b|1 l\iݐT> ^H1̽7lڱ*>.[9!0*EO>[ab(>荾^O Gvfbo, SLhmRI^*_z{!FR;6[0| q|$kcؙuUYk(iT4V31 ^WQٟUw fLLzWbx Q}E-eFF\oP Rpr |S:>XVs;v;>Jp_bW9yt[t?ypx! ڝQX6Q O r9iE2kT)R,򃉎 NL.g 2RO ^緕ɿ;F5:RI]ߒ]ѭY)QKo@gȣQCz] [{Q'5|X(Dxh/JB +a0`/^}(ѵ-F 瑈@[1A d_IW!X0ǚ* )ܧn~^Vޭ-?` ѸBT@ Rp W0ï][kr )_| pɀTc\e,D T$c[@o?Y(l7)PjKu HWW+x `/ t#Z=FbZcHwF{!RG[NR,P{K;KMYFz7ZaQJ-=# sq 'qmJLY,A]p-M> BB1g=b #-p`M1 5xbj- $f~<@[b6ձ7qW9ؚX&{]MO} &֘>Y 9#KQqj;3{LDBi&ɸQԙ%?f؏ɓUO{LC3]@f*1m+Ճ(9Dzt_SEyMsuB Y˃iw$f48qhruhR)şorhK|g?̃CThw&&Cp ˩e +|C*8 ڂ1TRMjT,1}qqAm<[19@9SCkWj'1.=qj#\L h _-g3{zjb$:g{=',#`Luww om/,!Hk<m&xe=rx;6<Dwm9o=&_KO!XB>! Ih_~ɰ,`dU?A퀙!mQ!}jA@6)D?ɭ{[v@ql=sm'[5hq͏4A)fҵNΟ DO|cWy2tD)EsmZ?WK [b5ĹqO ҼUKGQ u ⮃i'Vp?i5.~]twJLLD; Sy}!O; *1}$m]{Ri*>qMXYn^<6U./z!.5]nr| FN^ˤKwr@T-;y{4(?Yyz[+=_ klB,.-)wpcE&ׁ^ }BJJx~651>ض*7aPj˦O+^۰l!M~4=wn~hǎ ~sjHL?7Zow{P&(|k6,89 ,jiii^Q>Ǧ)|y8'$P{J^XZ5b0]ޓZQC%jhIų\szw,%@tZ@ 9([15=!?G?(bS'i_&8i9h6[Yp: ?o`Xbmvo!?ouuل-3-@ fjm]o' +DKF% 5=jR D% q ߓiy6d-mY:gkv(D巚y)D%J)%a!a -QHmP&Ԣ]@*<&( */R;G(W(*JQ (E-n{P&^ẖEKEWy}+\"r~_OE {?ЈkzQnIq_n;RxVO @JKx.hJKaM}w,_kSh@JE.Uu-Z Rʎ^w:aLB_jKg*-z! )SܸLH qz6rBKٷ`!Cnm3} S޾pw^Fjz M]h&q✵XxEB])E;VXL@SIAcJ$);ߨDG.b\1H"E\x{=5,s-}aEfWRD'd ,$ܾα˥bUbw)W0ikXRK-@F\TibPKZ3b[3C#-_@JmW1ߧcJ@4"Qj֑p@M3~1 UXh@|.E~~ѱe?}]U~a quMݚ\kX) y R!qb5[>[@qLٖ\fzqbQ8= ܩqơWOq=c휮Bb/޺a8Elvt8X!Zu%c /O %6Uj;ymXJv!;1+881MsݵAH:lL@b.F"FQafe w%&63M#\"Lat{ q 0 p (GqDq а6콎J[8#Ÿ>9`15EzwEAN/Aۊ|uFyu DZN@S}{f.Fy B _=PVFΡ<ɭώ+k8m{25~x$ UcN ,wpcn %A쨘N*(HLvRl3A$16MDSv5<4[0{p|ViPpyTP݋yWt|TmE,1Ddh{KPAOʟt +*ѭ;Z6;.jyXQ.XF?gkӤ6&P da*6uyȱ8XIhq@e1xF{i_ ,4e[olPoٿ@v߶K$FR*WF!$g@ n>A} +S[g8µ6U_ hU*XΠ~`!ߔ̽ךXi|RhȮ*Zճ!_[;yy9=&1A5Ï\4?406?&YgkֺXۋ= mwޑElUEx,-_bfGƇoZdI P?^;iP-o$ ӫr,gSB*h0V@lᙝ˹sR[;˲}ڵV'Mo@TbYl:؍NL?TMb?أV/rXbGۿo)hY]aRYm18\3Ђ_^-|qJV#Rɠ]hfbv,3rZ" ķkudӽ9iHFxߩ74OSvV5E˶`M {Dt Pz%MxV 1]RXdwph1WGi#p=AeA ktra*sԢcW #ҿ/&lC [ ii%[P3kv@W zcy׻Ź!3KX_Ԣv$ltzEI6>m8\WPVg@EbF;uiY D@b9j;jD:FMwyTL֥.ۮ>sXq{\Ztx1PC C Zt|:s6?qڌף@,eOSPSeEb9>&? ~f۝y\?S:?<1 ׌iT gFWfs}PM̦)D*.*+,6+Xbnbzf8IP34EMNa5d-H0g:C j0TTmԦQ <K}oS J{~e6g* yTѠ.Geߜ䮇fM)Ĭw*1uXJC9kKuDh3&49 <;1 e65Xo9U1bŻ%KV 1m`bt;At "j_3vRxs4Gz$&3Uy>( 0 vdIbOKL T-PQS~!%Z^sn| 4,a~4|p:GG'OB .f(bs G{n} TԚI F?3?=x}5 Owm>(1[܏lBSJ}ټ Llu TBT&A k=Y}aaHbcE7>=sאvck gOGzmre%K◌_ty3]51 <~ d(i@eX)w%?f"Fz l0gj?k9\բmh{a&!)b̌Xf 2]bb0kSvs:&o/W.,ņjxk>#@ňZd4|Pbs{OU۶ 1PW)GNM$- 3S'KY0P`^K%*1+u.ԥV ,νFk.A]5;lya(sڡgGše!AZIL# "=CmN=6<cF>Ѡ=fyaӚ/_'MRJy‘;pl.+~͑#0e&tsgMlO(" ܛ}.9;:Jx]M A)]j81:b^Z8LguNboƭςF1"yXeEeNsLrtx,eeXo $o)A5T{)Z>T@nj.g֙281Cרbj{obAk4nsF<ڭ LVQC8$ 7j{lT(R5D,̟8(ly`]p^FbEW`{I5^t&\>X0K*n"F`hpubFե zSc܃ 1x?2@4|Tx\ga& b/jy޴HA A>t?Pz C=P1-E73LA5H#~sx&S:`#1#ƌjsNt<>TAϫߕKxGbo4G&0TRR5gW7nEONG8Ĝܨ3KWz؇9#m/eEyx*wC޲1sO*8qv]><0m*v:b:AETc57ջlzm|4x펰 NH2>:k1nŲtx[b&mv6_ḶݰRbWoML Ѵ grDq@|b۔.]s7XêNl&!T-ObVr lNJg|[q~w1uE}h^~N0eeǧAj6BtnNC02!˜>C[ u٧Z: %=f6p4Z>Ϡ飶=p *j^ϖ{q&~,]F N绯X+4w`Bk}<$ltVzcCoSu^h1ps~lk_zr.sj8> 1AYq@_%ftof2"?!@A#JQk>gGie/7Ĕ|gb&ZgR뻛oWdqt3V$`/"?`'Q|sV旬妣.˴JѣG3f?_zXYI6}r7[QfԴ{AA;9m92CMo$fZ>nY7/HEʾplE Vܵ(X %u+rDL&NGI Ըe :Zпݟ`8}؅cQoz̼b 2P׊%~;y[3B\ :?YX9 hV9Ϡ+.kVۥ>{ڡ.7>t؉.@Ӭ=>o,~_a:k[y^b5nӠri}NLLE/m g鿑[8X/3C"hul:x3SQU <K0UT@ET1b 5eLш-WgP7TuT;f!1/E+s }~sC~g<[S|jbkq͸kaeuI't^bPuI ?׾6I= ͟[ZFe/o;E$rxCDMՌ>}/:$'j}sL:8]2cmkuv+Ͼ٫(AE"K=k\/ȷr+ :vޑhD]V9۾ϠȨ2w@-6LݥČ^;TFL>ϭ_& Px}[~71wu2ʴgVQb@Ul~ zAm8 %1/XJALG7La7翐ViJXZ~7qh/14k@#{6j=-Mj|Pb^1nZQ^Y]N K K K ]5b,Ludͭ_{f P'1! 4"YSb̟["=YZQ*iuU#4)+15Ata<"x-Ւ-+l\?g{hVVc= sK-G#㘰IR3|56K;q:eL£Z=s2&;=no/2|vεw  @s7Uhݬ]zMVx2_b&_*"j'Wy >A?:4wkׯ=/>6oK61|GМ]٧Ƣ Ae!HL[" qV>)YHn`1&oyZi}g Xh^j;LO)3m4ٳf 08jqIEylR k|^IXky}U{XMАkJ'APyyw&fs-UߣOMQ`6ptne'>գԠs8ڸ73H]bX쭲 y:12GVF6e'V;}}"mdu-*l3Q=v q4f6KNfwYs)CGAi*y.SӉ5JA-Т߃ڏ@o N@LAbժ\?| 3A[P1[=}&X;:B t=^ = D!:" u0C>]\y!Am?7ylXAkH'x{M%YLRw9_ֵjTScm|ܧ;5:/v|i\)~4cf=4P$,? _b6FEwțAƆ/V)167Uu_X<.DOT'& u6&P5z$xb~d+Zjo- 3AMZES͊q]<0cYZ^sׁ!Wg8#+,,hFLSvGGmYSh3Z+vQ$i:w:Q9hr/V;wXZa}, 6 HoYv1kܰaiZN+*JAG~=ԡ\oXҌ>Ӻ뛉4>~B5:]VgT?(s4F51Cjpae '3&!+j@eUSN1?n КgC鹶BL7`tNlZҌft:ǕhϷ~>/[(ըuèQmYo}Ŝf,G3l(NU|4cܫ52Y}kG87H͊ O\δv ]5t&Q'ٙgƻO!ժ<{\}«\ sK4 wUu~hIT5:Y|lҴ>[?_QTS.gs=Ѿϻg!:EN/*:phQ+V,Ͳt}fem7<?L夨hF V8!޷{02 @`_+gYcl&.I6]wOY.#g>ļRF3M;PR1ӿrݲ,M'k/[k5Ne W~~m}b\]du!1#O*~[8M61Gcfm]/]7X]* | *fm44"&<&|Z˹  n64/oxLJQc^WR0p}eqijDtx\~X$tR.wLkR=0Fq}çZQx]>F)UUX ֪yuR.sHkYlN= Bw%v)U4 qA D zVflBGE 2tg'Ľe3{5o\R[[VkP5vxxIKV@"1u7MQچ[DŽ'2xGAgu,+&]-#lygLv-4H0IuzX >+lDCZg}tmB+vZp\SXͺbk$2jg؎wjגnOݗ?FH)73o6[kMhuA+xqr a#s5oMLY {ambSkrN[C7XQ r˭a+-~کnlGC}DŽl3?>Mq'r^k;@$έ9ML6T8<8a>?ox\x ?!P3|RhLxLx|x U4w ئr{M>9?Y1ewY15wmu=v ]M\\Dœ+q "5U&&bRTno81tգ4T,1Mir/3<:1"K0b3CHw @Hlp{ɹ~W4< eZ!i9P@ "LE,b1 010k`9/{cfc?a Z` 6afup ;-i}>@jjH޶)(_OL!)bр}QqoWKtXbUXp mгVg3z CTôWd_f]eSS|ZbB?D;x]~$J\Sr'|,v\Zh6.J6J%(s4 +c ˞E %6$1?ĤcJPMBSB1CS~oϘ%iVC2_bbR`Yvl_ g^ngGl#[oKw91Wpt,D%1$LcKcלcZJqY%HKر[\1f`9Զ$)>51wBK^U' c*T|IL!)DS᠚z7fo 5z/)11YLb<0iFĴDQJSz}C$1?QNAj5_ɨ?=SSHb QN&~t\ē!KJhG1s僅3.Mbv )>51?Hb IL!_Rb0rm9?u{3<:ILILIL!)$ 9"vF=L.fRr IILILIL!)$܅/OoveI0ae_cOIL!)$1%1Yz? j8IL!)$1%1ΕS7pcP幼BSSILi.8{ SlH/)))$1n7+eo8jO{WIL!)$1%1)jRD~lY‘ULȐ}yf9S/mxs73=nڒ{C8tiۥo,Ѧ0>B;"}r}%2o,!P3,yƒ5mUEZն=mI4v`M!k X[([ᜯۄU]-# 2 2dߟc8v)`Ncc!|pG%TDZV=r2(S"B!z+Fvo9U !BI^a)B!y0eS!Bm0eS!Bm0eS!B-oa:B![n S1?,ġ;~9 !<1 "S!zc~Yy+2BF97,DBu@j5ir4`{%:{@MX(S!_S\޽|e*XKѲE{fz[=vOmSoIWRK}P^T2BV5vLk/4$9]3luHhbql]&=E_-d!)z`^Ud~ UK0P ],rGKPL{MRR}Z&C!(5Yts~8 !>i/8ML;Ļ@Kt xPg0t!dZ*5D%bmpFjgt!kD*ϗ[9'$"tDg P !"`<qf2BHb[j[FMǵ!P``U@vB_A>l7Z峫B{,P9=Vcqʔ!oAeѾ3Z{\D!>[qTn~H)B2 ?U0!LMP]7مm ؏_ms}R{ʲ:'S"=Uۀ/Le;:!$1]wP]g/d!!DwW0`ɤOf4sykB_D ZyKmbybk>oD;6E0C.:Y51ك89GI" a5Z*?%!}j)Oy3= E[I2{2>*ʢ*'Wak=>i88o<{a-8ѺjOk4zh) wW SM2d1 ͱ߀ c߫vs(oER41 Z]'x^f|ܾ@<de|cqǖB`SPNT$LBIL!ć ;@Uv,s8V] Xއt̕IB!)X0*NjL~6e;j!BO L֠?Tڀ( kP%*~BHb !>~$cg{-Xoq1ِZ!$ υz?!"BSw襡9}J"B!)jДY}Ȅ!B]٩Dۣ5LBIL!ߗYΙvWB!)'n'::Y&C!$#fY q_&C!$R 3AܕB!)vWe"BHb !)',P!B!)nj\}Y!BJzCD!BSr0ILBIL!$BIL!$BIL!$B!)B!)B!)B!$1B!$1B!$1B!$1BS!$BS!$BS!$BS!BHb !BHb !BHb !BIL!BIL!B9%oMK82IB!$178&=v'q1e S!$ox:ffLBIL!ħ))kB!$1Su5̉21B!$1.:a !BcfaNIB!){^cfa^5L!BXuA Zhs5zȬJ9,c,3& Z,ٚ0?HnQ[^?]<ԻHLHL  1)1 1$&@bDb 1AbJL$&HL$&ĔHLHL  1)1 1$&@bSb 1Ab"1$&HL$&4Fc,hu_X7Ǥ&@b?GGE]-{8%"*c87X_Y+wʉq̷J},3cpw_.b廓$&Ĕθv63cƐAGĨoٳ*|r]ͬ3:nyqnzx /=xnSV?,aбS~le65ic#c#}wWbHLU?ӗEYY^V{.guQgQ}`u|\ѵ<}Viu C'_4?k/JL ĬU#ȢӦgdGęwVfen{G>j}D\>: Y}=X]DMV=WTR%Yղ%&VbvVG륛;̍ vL;l?*1_?16v$'kί$N_ovO\YEu{!;q棇*{>AA@bm31Oz{둝ԝX[%_ub{w8rG4Ks,\n1u,CKDL%17K.M>§ gIL ,O9*rdω.W ebΉ/Ȋ%k׏#;œ"69ͳ$ߚe!gUds}k?b>-!ϯ$☛ei Kk^/M>=fml]bHLobXunqqk>m"9Y^8)8wvGvkuN>, 3׷+6 1Ab~Y2oݗ}uOQל#UdyFF3&FJ]slE9cỏSneMĎcb]*}~o۝&1$&жsiPBȒsJ3f3c׉9`=#ǻ;y+ryBxO-nߣs#"&q]~ge*wYs338kݿh#y]\I6s/Wǿ6_^8$h/o{#/x%^<ѿ{m#"VG\vAxAFD'VTszMcƝG56c;:L 1Yf,y5&sYB.15Ur,ƪX~XLmcV&OTWg\w8/2xbgW:o,$wcv</np쁘E1=DFcD^ukbZke1#žqT<MIL  1Ab"1$)1 1$&@bSb 1Ab"1$&HL$&$@bDbHLHL SbJL$&HLn 1$&@bJL  1Ab"1$& 1%&$&@bDbHL@bJL$&HL$& 1HL  1Ab"1$& 1%&$&@bDbHL@bJL$&HL$&ĔHLHL  1Ab"1 1%&$&@bB[O PpW-1$&lv[pR;E685c Root EntryRoot EntryF?=VisioDocument hSummaryInformation( DocumentSummaryInformation8  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~VisioInformation" ՜.+,D՜.+,HP X`hp x `  Zeichenblatt-1SZeichenblatt-2SByte oder VariableK Rechteck VaFunktion / SubroutinemmKlammer Umgekehrte geschweifte KlammerDesigneffekte.1Dynamischer Verbinder KTextanmerkungrb ZeichenbltterMaster-Shapes0|_PID_LINKBASE_VPID_ALTERNATENAMES _TemplateIDATC010498411031Oh+'08@LXd | Microsoft Visio@Z?=Visio (TM) Drawing hRNguR8>wL>vg>?p9[!i fTd',50fZOlO~C.ߐE#PQc:  A ,U345&6&9Eaaj6 8d7?!%3 URg L-ak@9$KtiQHeuDDT(!cp;/paM-uh\`(+AT %5+H>>5a%h34ovJuX0Hp(!MmcP=?`(z?84?D0pA D7!U/i 4?Q`_1_JD;a3(/@}_L/^/L%a$G[aY %//// ??.??R?d?v???V[l_ru@+y_w-uaDd/r*&oo*o_Qv4DOOO\U$/@__o/DA/Gj:d2(joG2O5j7@oo ooo?&s2;IO[Odd 6 S( $b4@SyYkO(Or&̗T4RDP@+&pqprg-.UEd \UhřϽϏ____"LwnU2r?GXDeTeP贁Nkcc#E_挿¿{%ܶ/o Mv{&pqo s'9K@]o߁3呧G(x,+1fߣSBuD'l*|l$6&w-n!1q;qHVN`amF"%ȓz䥍 / /@RdvMIySe-T"bbbb$T$bb~9bIbyb~9b3c'쩠? 4<}J~>?!OXOpuOGK59&O@_Ǿٹ(n4Y-?@UABCDUEFGHUIJKLUMNOPUQRSTUUVWXUYZ[\U]^_`Uabcdt47"H$ @Y,b'@ b@C-D@p AUvefgUhijkUlmnoUpqrsUtuwxUyz{|U}~t4"H$ @Y,b'@ I_C-d@) At4"H$ @Y,b'@ A-@7 ;z23456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd*t4^"H$ @Y,b'@ 6C-@ A;X4@,k/MR@k/lNR@ l/MR@|l/7MR@l/NR@\m/HR@m/lNR@bjE kC  e^| 7  3u ,7=OasNDas Shp}eaufudZicenbl]t) ze.HD   3 B>Tp[/Lp?as&;.h> ,b MJ=UF~?FV*JR?F\.?FM&d2n?Q6 U AMA333u` ? !+5ChMW]u$g>5 L@X!$5 /`Vis_81.chm!#4E"67M `_!b-"#"'?9h:AiA 9 $''2ǥqE0&!$E0&!qP \@6%A]Dc]3V:?Ka!Db1OCK%YEiO{OL@ODJv%YKOO"NBSB53^@M^U_g_y_Rg@14ot'2TALd"2(Ech,@!3qlf1U1 m3 =M!l+TM9M]a6 `(an|o@0~ o֊UFD  h>$T 6D UF~@xTiC;'h>4bP9 EUF~?L&d2?F\.?Q6 U AM| ;;u` ?*#)3=KU_euo>5 L@X!#$5 /`Vis_Sba.chm!#57_ 2#`$g!-*#*'?9  %&/7 aJ=?Iy+#&Z&P?B#2N贁wNk?@J'q ,A MVq6q4!*#ekab1; ?Eq6 I-BCb:@e2C1%>> OO1EA5~%5]NGxOC9AOI%\O`JOOrn_gPo|'TAFd" (?cp,@&!iA%9 -$b]'/2q`&!gE0f*laUmHE?KlT(]9^]-Ma6 `r (QJE-Os~%_s%/\h}Mf!>zbcrA bb HUr ~L)uE/>F:*#JJB d]Ban']@0]CPUFD# Th$/T YTYBBUF~?x<F B_P(?3P?Y ȅH?BT ?2~B 03B`T9>R4@Xflh>, bE=UJAUF~?F#H$??Q6 AEDA33333#300 0 0 0u` W?!+5CMWaku@#u>U5 _L@V"JXh#!$5 /`Vis_81.chm!#4"3 `!k(_%n>J-h)_##' ?9q!0!!g}PVh#@>J5A,aG"bCV"1_%>?O!O3G!>H%;iO{J1BCB6CL,155OOL=A_YM+^|>!C_U_g_yXXE+____yXX3;?&oOZe 1O3uniSoooUo3(2Oo.oORo3H_oYo`bO&I5rn7I[oEg 7TRA?"(8F,@!$0l#1U#H3 ?k!b\(Tm9T %=+EͿ}/ h>8 b 3 AUJhAUF~?F~2 ?FP-DT!?>`AZ ,<E6  ,^_@Aְu`?r1|iuu u b]u  - #;@e"(($!>1r?bX?$-',`",/ U-'(4""#//<=(&d_2?FMl2r(}??Q(> rm3Qa+93/% bb#4r925)#8#115?a6I"\[#2qq?2TUfA?GE?\.l ?A8[5[#5 82LF%X[#)Q.T5 2`Vis_81.chm!#4XR49B'I"AR%X23JU3 5S5W <3iAE9 V"T 2X'2@@V!TE0fc1d3 [5Y@fs4 ?j?a9792 Dx,Ds^r;"mDsr"r,rr@w@r"Jrw,Jrr^|"hsvrDs"YY6r;0@?qs% rE9h9h 4$!2BӸx5 1 ` cownt0olRP.x2Ώ~$ nAx ׉ 7|aiCaR%[#o!vş=Ӂ q1S.yZPT&镁 ;;pqGT;;AEEO(ɣ @)QҮ5yA;`K`abPme0 JRPklPPneXRsP' 8E.&c672;[2[5 b1( H>6wUC]da@sZUaXdpVsPPtPPW|VqEA2"EsB!jooQXDVf!3xgyfTog_nI)7N?iCg;̠AE W[2iX83T,@1QclU * O(4[$9a8ӁP2I"X8[$vvxbT55CEQg?y1 l" @ }?1HA@&0s95B[$X,bFZV q0gy 1 U~  $- O^!2w ȣP(  vy# 2!mrHUs }DKt9DNW~BF;(I#e@*dB g]ao]@d]PUFDfP h VTB UYuU??Fxha T,aaUQJ\UF BP(?@?F~?$-?P nL#l]dV^ ] $g]Q"U"Y*"]>"~#<?&#A&Br&C"u` ?PYu"bl""& M&u"u&u"u"u"u" ,u" ,u"4,u"H,66t#\"u"///$u)ar;21'u `Fxu21yB}BAABA"!^hO$,@ZLupO耑 @Ca4R#qt{u*gvXWcZufvvWuTj*uX{u ]ʤ *aGIJKMNOIQ"R$QTܝQVWXYC3333.3>3N3^3n3~33333333@33Vf4b@Vqf0φЬ uZ1[]^s@333'373Gc ƢEt&EAxÎ;BA}@c"3"3."c Ӣp x/3,S/e ~/iRfSIA62ab ZaLxVIǪ *:Jzךת'OgEa@braĠqAQJ`FWO@r ЀiC}F@`I@)@AEBsZeȓ3U ,puӐe(p 4>{ScNV:fcfR:bWKqP?@pA3VUbQw Sio@(h#FtϿF@ qRfa+_i&7=akVQhaRkQclQohaR2brˡF@#I~rW{ 3~TE}O bqO cy`I@Ё`sVRڀfK@ڀbooOlw wKuK`1s*8zK@JqyozI@ eoo /uB s!xx7SlP4;walPe,0kNq @Jq.-8r JqM9-t '-E1*dbt+r sQJQLŌH{M0tb;{O,=CƁS=ԯןƋM2'Kx~{עamzD1-?F|ǓnJ\˟ݟITjU˫ˬ7F:-˫YkIՎ_TF ?BP(!RQ!4 Iz{7 F.!`}~k QhaqY.!(SFxfb@&7! kRka},1ff",1~,1ff ,1WVHf312E1kaB12lQ12a(712,1HfWV3UYkaO} mlQYBaYO(B,1WVHf3ka lQ"(;" FHfWV3Y"]#kaS#" q#lQ]#"]#S#"QoDcjWV3,1HfEFWVCaHf``5d 䰈qGA8rL5k0BAK6 k0L5k5L5|0V!CBL5 = |5QQ5&t?U: =|5vŦ547 P@QzqE4HQ7b&qn24Dp)CTU+0F2o$0jjSRjaRD9Q#eM1sB=yIZR1s^lZLV_4pQL׌HH.5Oa 5.. ^fCmnfv3p` T]`avelShift`$xa_d@a3p`OΠ`fs`m!*_a@@ VV%Sr V:67sGr:2!l7s[r 27.1i$3s 1T$y@a@dvkQXU.q.'2rqty!,0}v`n$qqt7v-x8cʁEl@baa%]1]1!H$rI*J!1LLLDŽkq+MԄ .NAOAAsQRe.S"T/.rq.qqAWVqqFYpRBZ}/b[.)\r].QLQ_1(A`ٓza(.44.;[Rx a;`"[TBf"< 1W`B`sh]`cu g`f&+o]`m`t`l`g`l*?`0qH`.:jx +\T`x`bdvq_v!L5~*ϫ16DP`)_R`Ht%k` 7DVhzڠ2xQBuݿ)/\BanAM_0qx2Ϛ0AP*(O`a`*4dFxgy2?Wʷշۯ-K08&4Fb[mD{ǿDD!s 1ϱPGYϋ(6ѲĢϩȩ5hA`# &AsDEy3 UĻɯAv^2`OnwAQ9)(ĽBT0fAvH`Uta)Yp!3EWLnew@)ncbā Av9H`ER#t6Hl ~)99:'ϟoP$tm-At!BT)ங'7İDZEBs&1)/I4+r/o/Av(* t/% *Pa#/?4Vir4F:?@^?mAiCOOOO6O=nO Z\9?K8/ `v GAAtPT&xJb=rb+ Trt*P@?B#o54trptb T mkqőԁw`3P?squ{tcF~ϿF@ Y%ukp{`?u %,Vrq @V4`+AszSPsT3 )5>%j{ိqi!stgq xŕ2htbrQ? !3'󿂱/0 c{[яJar<`Ѡ 5 %*5}9ᙓ @r^rlA$Q2U lQQ؉uyhU+424r@1Ryɒ^rx@7.?@hzi?wԐH}߈7c6z#o/|UD`a1H_\"p1@&WrBdeKTv7.!ԟ@V) `aGo,o>oPobo1qyo>TooODowQS+F搞R6);=[vrrQv>LUF+q StFUevS߅x$~&qޗ݅ԯ1).UaO|tǢKT*ϰd4)'ߕr|<ߒM2/WkNȏ%rQ|evʭVlߟU[#Իg.gM1xweۿoo1CBSCS/e/w/S~eB]v KcVFO/GXiPdU:Ǽ ڄE-,/wb/cƻ͟E/?'?*&)BOZlcƄdUO7c*&tG);AEOb1ҿ)M_Cb.ϛ]!_R8Vb=x](' _ ݂?z-sT_Na2s/@.g(O]Sa5 _;;L7pwrra2wcCB|WV.gWi gU1gtO@|{럘p  D g,l;;.o@o*aa Oo0Ґjo2kJΆb`b~Mk 2(s.®%)nd\S?l$`-@K ay!*t1K쐴^VQ2CIUdUpς1aanOa7P7߸y!X16BòJa@a WE1!UަQb@72Ce$6N ¸ջVcIpb)L(aO[Ne4 (ga# f8 \ 2d T!bu-(;M_qڋgֿ`4@?F\oA`0ΉjM09 Q(3b8ye/˸?FƣE?Fp5Ys?F5&!Q@Ή( 0_Bԃa2U0*s3?ܵ0Ԃ2 k+ݓ0]'%Qn?Fѡ?FW^bd%?F?Ԋu-Q/c,DJ}?5^I,QIƥ,$~//'% ?F*^Y?F[E?F|{T?c,|гY,vݥ,-?C6*~??b'%OZ`?Fa)P?Fů$_?FPvϟ'͚?c, h"lxp_Qҏ,mV}b!)/L FAOSO+~'%`0DLJ?FNqVUO:Oc,~jt7A`,ףp= ,7d__Kh'% QuU?F`4pF?FLAD?X@t]_c,io?JY8FZ4? qۄV$N@a__'%X/~%l3!?FeY?FJ~ӗ oc,z6>LZ4kw#awivQooj'%'-q?FmUa`2#+?F(%oc,Y7 }j GfZ4 ~,ClL^!UFV{WE?FcJΦc  )?H},""3\ \ -YVqjǻ )1̶ Ѡn<δ m|ѠaQaCSVGh4cfUpi$5,)֝F3L&AP"iЧ Z3*p#q8 |`rXӗze2ZՊ@ #5.?\zi{B  ȧ#ޯ&&JJ\BMV㖿t侜+Ģz`rl%ǨΩ)1@8"4߀Hߒƿ./1*Tύrˤ]c-=( @=5-2-9#.@"~C Ȓ#@ 7?D`Ofrsetx %q,kڡv hE 5ɞ)3U+KGAb TrKvlԐhi"//ׅDzW280//?(?< ????QcOwOO)O7(QOأTx7HUI' Xߞ^Ba5a8_A88F;2/#3wB oD4@ekFo<0Hoj(PUFDfP h-VTYYU?"H$ @?Y,b'@?xTT;͑U@ !@ү&P6 lAu` u(Jݩ c?EDe34 D   J ũ2qL lvF BP(?L0 `LineTray sp r{ ncyz۶&` v%P t e n&$`u v%W{ igTyt&a` v%_Rouy dw")g&A0'2A{Gzm?>5Q?A^n"`Fw lo0~/+n"j?|?+5/!4`S:Ta ow?<*+` 9?& ` 9S5@y0eK $KXOf@s{ 'B $KYtO 6`9UM@gy i@i04Ai '. `%9Dw AcNul>k$Uhh2Q2QEXT/J> 09([_q_[_o!otgU__o_oTEooo,o1A>Te\Ls c!2TCt1MU$U<"itjt AktE*Q*QURmtQ!!otU p qU r s-3"!!uGRvTlwaExn11Uz{Y|})~ARɄ22քeㄐqJ"T c 14.m"?<%Rpsf0M8@H8 MEJUU8!r5GrK&HZ&H͟@G+G3"?GERml&Jlmyome>mGG-GAUV>6HԂyϿHႻFSP6HuANqe (=MQ_'0Uhv$on!3` LDnFxB'3` A@iBݞ"4` C m@bAxB}lewL9WIFE/HU= _6p9yEW49dF@]#'^B ;$]@e$k&o+d@(]:aG4b]PUFDf h-TYYU?~@x]@L'} V6lX-1u. Bj2u2Z2j2#u9r#ULH/MZ1+B#AD5 60`Vis_SE.cTm!#20AD%`>Copy<0wigTt ?DU1f@M)@c<0o+@'ofdBUArX@Aad@iV@n3@ WAl@ef@R@6AtBvBb@hB@lBAJ@=## AoG?9/#n"E&444 7I#$@HB59_lj#"SV6U8*l>(Uhn E /J$&9"(Ln eg5Le#pheo'T !! Js~o/|R12'"U7A%O3W_7xr5(-6 `F2 xqu\J@#:0b)&`AAs)@E T@xd@Di e i=M]UJh#HT0#3 UEJ\}|U@f?@3!?F~?FL&d2ٺ(1?P Ǜd#pa HkaQ"U$"Y8"Mx#BU<&?&@&A&C"u` ?F]o"fpy" & G&o"o&o"o"o"o",o",o".,o"B, 6 6n#V"o"///$u)ar;21'u `Fxq21uBAy B AA21"!XhQ$,@pup`Ca$Rou@+u*LfXQc* IRg fbY-H- M ZU ZZZZUZZZZUZZZZUZZZZya-yyIvJvqyyMvNvOvMyEQvRvQyTvTQyVvWvXvYv3* j|%5EUeuu 4 蕺!BZ)CT@x@U @n@o@a@%iEPn*Љ4J1[v}y]v^vc@x3+"" qؗAU2Aym@xcv$"" zp$E1u/w3,#? N?$hVW9AB[bSq20S hV1Aqׁסױ!1A7Ig?abrXqkiqruׂ6rS@6'_@rp9SR2MV@׌aV @aU-ܕ?XrcU:uJed*ɮ᧤N@q37sfjBA'b6bjB'#KPRQʾqjeArQ c@(3FA{aٿA3b!&»F_ v7_M%;f!xjB!s(a?xjB,2brrP#VFI%r'#7 3%Р$UM_ }uj_2 ׂP5GׂWsZ2bQwvPQrT_@|wu‹ׂPc?P 5uR܉͏Cgyc(` q(`e80{A @9-܎ Ʉ '؏bAP42Dr \sya@Q_X俋NZ]b6 O6͒z{ # Z{u£@%ЊׅqmϕDR1oL^c,ߛѯgZ $j%{|ב R{iۄ);qσ⥞occL&d21b}aN/9K!KG'!cpK1a81YNSFxBD $e0Bof$z:cRTrDBʩya#HuzR j"^vc ` T-pavelS/hit`RHq/tq `OfYsfps}1 oa@@VVSrlV F"1+-27i41Tb4_qt_;aXUZ'2rq_!n0^Spn4"1PT_sE<raa-A-A1Hc^IpJ}bL;M 2NQOQ1\Q\QQؔcWRS򔆮TqqW&o$Y@RZMb[Z\g]tpapa_TAQQAbpJvq;Ϥ+bܤoa pQ+d0AW`BfpsĠh-posugpf&+o-pmbptdp^lbpgfp<XR*R?`0ȁfHT` jd0+9TfpxrpbASv_vPQE~ 1;F`a`пҡ<Rfp%tknpr !3EWj|Ϸ2HaB^׺/9Bmqn*<0NHBew0yQ̀~`ϿOdpahp+=#bRdﷰ4z:$ߢ B<R ai*rp iisXmTZdj `F1H<zՕ{xMOai?ޯ!_` tD.Jr5<}= ` K* `[Z l v 3mAxD{z{.gMbeᒎcQɥģ"Ĕ,/ħ<w{) ѫcĔpĽ} ѮmJ5N13_MsSspy`ID?S/c}ba K"eb@o> )עa2¢ `DVh%# UD kmjBt2ߧ߹$c褣%!rr[Rf`}aTy7[C3DnUbe|b;ruv`Up0;nr`1:븁X%`?$2$N1} Y4Brb@qAa-aa;qa `//5?*?cT Z "!0ЏQƽP׳M+Q ܒ*QDb)QX\T׳a_uu`bېRIu}ܒQ (l\O~?hzP-îLޯ)ّ `{Cݕ2YǕT `ƲG . !ё1ɕHᝢ WGMĪ ?Ɓ%Qx?ˆt/$Ͳt ڔ,ş$O4  ,o>oPobotoobo1*BBZВՑ7zBoaa^屓aG9gl6ҩ$xrNPƯدk)D _& 3EWi{ߍߟ߱ /THZld Z_VjhRdv=X|w$%F / >Pbt~44FJ3x<T+///(/aNO:QN9O:////./??'?9?K?]?o???1???? OJEOcUQOOOOOOIYvotuoacVh͟y_T_ j &m toB)j ooQvwёʮhg_#F K?F&?x?0l}G{,:`@"   -?%aEr%Õ))EyXR} o`д$#?ęxQIr/xUt,bwXUl0i1ewvv֔tQ=Q)R}h4FƶwGYs?CpYՕY 4e%{QHvsLQgŘ{)tUbzf^ȯfܓ֔% E:a`*q>^TbtxoMdǏ@هT/f/˖{OtC]c VqǐI֒FV –nUF?vn qsɿ *Ys?F>?F:Xc?FMڪ﫰|JܡtQq&A=QngiC21MwZ漣A$rqg0N܏tE˔F*CUy ,Xjw=Oas߅<`ߦ7 {RC*OoNkrfŜo-?QKe_? Obweoz}&U.V@U4kW5wR<k 7 fwdůWdvw.ϒ,u6^.ڌ@(]/5Uk@ k@Dw,|@q K0/'LK9'Cm0ϾSBs2ّ5)\ls?1D@ q>Aw9#uQ y~qҰq٪$E)U5G1r9{ a`pM>AkVRbדL!.UEG!ܱ'kWH٤*Jn}viIpU'LG(A^|{Օ4߭9w7;Օ f!+S2)kt/'م"Nj$6HZl~P`4@?FEO jM󎣈PuY Qᦤ8%/˸?F#eֻ?Fp5Ys?FwtÊG?( 0O(LпDJգ<}?5^ITLQIjL$?~OOI5 ?F;?F[E?F3Ϛ)O(L|гY<ߍTLvߜjL-C6*C_U_'5OZ`?F3$i?Fů$_?Fir8_(L h"lxHOj?FeY?F܇]}(Lz6>19lTkw#N<vuqRd/%5'-q?F0 B,?F2g#+ׄ0]/٨(LYЍj@ T jLCl#15v?3FV{WE?FD~ΏJNk(@(+ WIHg}jLzB՟$q$D*P!)=6?zH7j A^ 3=< 2Aġa1aS~h4'+5.ϼ7O9H%F_p8Ɋ( b-SlJg&ǯٯ>qA>%7IWt?&t:NO/ag#mҳ31>ũ!bX,!.@-R^rC&Aϵπ!EWiSh:Mu^9a.^;g?&h&8!AOϓ{EEEW{>h"R7"(P!1('b!)1jUP-29Z3ˡ#@$ 1d>"wCCZ3Q%p1bؒC!p2@&7DZ3`O}f70seto?uܰA0&2,?>?P:v72p&1"E$A1oٿA32ÿ&A>+5R{w,0';)w-eV-jUCU+ a ?%A'; WTrvt0lghin0w8M=Bb7OOO;J!HR?+vOOOOO__????(?q>_]?u sEtjqttHD: #  h  T0]]9 # B 2AUAɿAhY?ٿA,c2ֹƿP6 Au` ?A@j$u#J.9axu`u]`bu`+u >ڍU@@M&d2}34"ÿDC'>"M-w'>"D(p A "'$"&H' O#&5 $s&]$2JD=#D> D6#U`l"_# #1?9#H1 uNWzIs;2 8J#2zGz3Fx$2[Ru(/YB DU_V?\.?/B:o:2A7P`BackgGPou_ndCM`lM`HQz@@bZ}H D?h1GA #X8P6d3662Q1CUBF4i7o117;y0~1Mr)Srm>de}mspTt "qq"1 1381812tMItBB Eb'b$4ANu13[h2AuBH1BʜA!ʩ¶jÄ[QЄT݄zrf>!$0 %3To1/u/u?8XxAC`IWsSwSwRs%o.@0RgPf KNC`teI`\ai!nn3uZR%d DM`kO`mtiM`5HQ-2HZv#`NC`o>!8V@MKsEŭAد3dVrbRaPags0bjkTykp~1])S搹qO`zfY`u^s;cTͱnA²oivlPA$*q#8@q#5$rQQR*E?eNRעQcBdg:b`OTndGPuNEb;StRaaGPdB[Ÿ-uijd[„6k~]QRƀ@gP@ A>LEs?e\/dϩs Aee"g 9bY`dt2D-Ÿpsr- Sqv߈;`aݔ֑#pS~>"ƒE*ȯ:b` Txk GPp}1Բydc j]aaAe(Se1dSrʴ &e%\g#d ɿRf);M_=7DßӁ„{81,[/O^kxÅi-ùƁ3 uI[Uu`  *L%LBW?AP)Es̲aٳTe"/)#We"ɧ??? ?:OOB&c_ //Y`6?H?Wis??]CJ_ oAUCGP?e`(__! m2oDoVohozooo0 siԛOo oW}_oOOOcV¯]WץR=Oas-](]uќoo~Gm#oTw {Ɔ҆΃S 6:0Bw)s͟ߟ'9ρJca:smQm Krаe/Ȯя- voP* V²#5VUo"/%Wo"O _._@_.o*o/**u(!S[mJ5li@eu+arw% UR°l;Q@nt9'G} !M @o#1Ϡt+o_1OCLRʿ.@=(*jMD`sprkND߂ϚByiϹV" ?5G&8߭\n߀޲5F/=$2&"4FXjj"j"/ɨu*FXjv_?q????9 ???Qc0OBOTOfOxOOO8B+DO!O=`Rotyfb)]O( !1idBm!?ȅKbB;rbrc`R@*@;H(iPB_)/BE!1v])g%oks{1tpnN;GWewyuPz%doaqm Bqo2;a2wtSkmQb@nvUaTtnsb bdds°bjTq3o: rrcjKbT!E!222?kl;!30kC2m2  aZZ!*kψC\\E&&BGO ='`C²!cRf Ot9d_D@qA,@"hi."4 b bA' Y! Pk?zz!!_lDZU!!'7!ƕP d>"p}9+6@ci0_U  ]  @P(  @Fя @<g ( "*^p?4] #5GYk}D MEOBJ_.VS] 2DVhz  LANGV_M5.S] 2DVhz،D BASIC_M.VS՜(h *:LZ}C!DQ@+P/J+R/u[HSO/U`8&?p@Fh@ LFDTyB tuh$/T UF"H$ @FX,b'@Fx,1X8d:UPH1/0rb4,1)*AA/_"_o & #hQQ,0#` B scriftunkg,1 `CWalPoPtQg1E}u+iH5u1! 7 @@j&@@q{`?@rbk?de?s)gUA$ &?$%vr;#xAZ%RiLE" 2 0"U$2 eur! |u[]4 t&&(s~rzlH1AUH1?iiu5U A2z"ry"!rByǤo VTPQayl p.-LPnPʡTn/k?Pup r 3Qiߢ PuPws 4EPtIkrJPo?PlΣV?PrPRʧIE zmr󢴳 5D b9TB*nPnߤz w 6VO_LA!0jA_(S_e_{W'6&!3A(P!|/2g1 D1y 3 H1D1oE7@q;E{e|lE1@@~!@Fg#?F"??QkPyA`Q27W(ATl 1<25l =clYmfxAj`PL!@@[g?+@@1#_  +??@*o4?PD}p\z-uru`u0`bu ,0@@"4o@a/T("q6y /B#u2#Q*"0#ᝯ2,`6@@ߊn`P @$Rxww &;01O5l^3);hK(a~"f''?銞dYկd#j_ubsUTK0a?DbB`@_bzI/!*9`P"3\o"҂3q55"1I7zrHQQr9:z:a:a9aaR=R>rrqdQhQrq(Qr q2aatQmO\A[|RlqlqⴋrT,@rLQux҅t0`q^qspC&Av"rBpsx^֔u~*k sa4azuNu ca@bֵf7sa9@xU܅-rQ9PCaLJY„f6D/@r*PPV#ROj&TP@Yn`@Q.%Z$yaUBaXfpUu!`4q х4s q, to! ТZhQi5,Qrc3LP@j*(B<@b@t^?$@o.a#@PIKr3[b3KlqJj/ \Ca/O Yn`5PQYYCZOǐ2p:F>pKBrOgOq/@LKHKEKːYCJ=Aұ)VZn` OzOOOOE^Z___*_<_NXQ3h|GCA|epPKd @A[-B^ AYsjT '_*%kŽ_Zoc ss&qg1]JS|([w-v`DbKOb=~h>fkviKleN{>f8{gyjPsKϝ̏|vTXc[עHAmeZfDo1ock2DN&xݟ^p*wwjo>f?ʗTٛg>f,GY^4Fܢhn??Qa$%<}Ac)#]KAt^ FA1?0_P_b_k?@ܟ9Zm(͖pB?@qK:?@W@ή8W?@S^HssZ*m(5=*/P#&?@!W/?@?@F?D'A&a?@0?@vj6??@^ŴBI!O&J6/iлH$?@ T9_3`?f\Θm({E=T@m`mЁ?@l6͊`?$Zm/~&0=ficUo@@K^9`lخgA%=,^?@tA XP@@z(fkT?@Ô1fe=j.ƀ?@P3{@@5 I]ͻ?@+ӟ wu=~@h{?@j"~#HXt$'=)&3I{(?;3 BP 6Oc= }…lO'ƯدH6[0 O۶f_@3l~ !H<@)>PbtφX?<)H\t('A)_qUO}Oash8\@?x<@fb@t^ OH3q$y)AᵅO(Ld?x<ǣsxqM~^ACi#Sq"T#25(3 #A/'2U:H'm!D0B 4"!` TextwCo orz`` LainFn 3` Asa&!4` mpP  Aj({"AFn(?A!3~yZ[O PA4wq 'HFdAT(q_q3.gq+-뵅h.((zR28y3yPU)^BPԇOOOOOOO__)_;_M___q_______1i/{/^X" 6q'AS6m#p Pya d4//*/E$B ?' `?FM%# %?)Q.(gMACoooo|+>,",x>Pbt}R,>bMt=ζȏڏG"fFXj|xU,įFX*@pԯ8G(ma" AS$4!̱ q 2aaJYĆcy"ȵ08̱u){`gl6ߛ @@L"@@Hh4?;C¹?FG[r;1֔'u`Fx8<"\YP"Q!cZ"[!y!!2..Arq´"T,@-@<إN`-9-L7Cz`.WAoubҟ Ab;sż.v̟6 ˟W'?̵4!Z)4TB`xAp`n>@tzj7X3#|گ2п6=#X)Zܽ?-qry ~ZAu`u`6@rbÞ?5@hȩ.p@Őc)a&M/_)s6})7Up`uR!cA*qK2-[!*U)aYӣ#Հ@+("euF@X,@@cP?$ 9/fPtwߡ2wy#wI`rSbK3`q a\~a c©Fǐ⫣  Hː5<Nas i!3 w%<;㐐/?/e u' @N2 -< N6£D1 5s1 .h fx sN!P $4b ?O0A&GWA///&6?+//O*`׹m(D11CJ/"ǗNO`O//??M'X'jY K"K(;G"JK>1?/KK__O]?o?_"o??MR&vum]pɍ0cKdoe񠬁lOe񑡡ojFxKk@[?vuS‚@Rmw@|mƁ NGy}Aog6oo?M@]f?qoooooo\ke?__O\Z ՁX9/ӘC_U_KXF_}6Z?Fe?2”,zNt/DF`0 &F1l aPdl/BjZԘl-U%{ 酼N[bzf^5OHƏ؏bo2oBbd?Ffl6&FF?FuWuZ}1cԚ\ ϥRsBTЯ*oo`GÊ ݢ{Կ̗ޟM2.RkO֨ϸ"4FX<ߺ̱ 2U9MSiO@]ўyd!4q<|a|aSeOZAkQw9+OO!@ Ai1/C.</bsPbOo@nZA">Wi{;^5˩?@FmtĹf~:G?@?@;:y?@A^}/9*[Jȏ :g}= A?@I l?@C"<%0Md 8#(m?@'`sI@DϜͰMUh>OmHaoW?@&N?@?@+%U OmHg6D~)cK?9?@_1n`Um,H_mHﷃP,P 5b?@|XYm˗_mHOgOw^ G/6/?@$!?@d%FV^_mH+5gf ?@qK"`:= ?@6NZ`4olI5gN@S:3ί?@foQ̄oȡ4$1qY?t@  O=rbK{!v '炏ÏՏ" ?V N x4(1(<1> =Ÿ?8Aį߬X,@@cPx114<1U??D$yίU@`0 Q/Si ^1Ts] <kQ'2qiq GQ! Q#`n%bC6a i@bpИCa zq`%`>qW,@Q"acqB@>q݁oӂ_=@`{k*#pq95"+aq@uo}5ub lҊq徶#Ž#UU)*/023456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdTUUUUUt4l"H$ @X,b'@ 7/cC-d6>U A;XUUU &U'()*U+,UUUUUt4*"H$ @Y,b'@ D?C-@= An@@_RR@j/GPRH<(H<(nE@REj/RU%`8@P(  @?F @L LFuDTyB uh$T RUF"H$ @FY,b'@FxS1o8{> o8!!~;[1o8!{>_5"s4"{>%s4#{>!o8~7cCP;1!S5V )*qa!!u&'$4(405f71$;1<4(+@4P4T4qaUctaaU+=Oaso !!sE H  #4쁱0a` B scrif ungZ1[1 `CalFo&!Q"فf0! V rb ndx- ` Cn(et~1 1ՓusC1 ٕ9U;1TyKp ,U"& H-EB ?@`0  @@ `?@M&ɯd2?Ѥ٥?QZS71#|q =t4C[[s-4 ~a#0P_贁Nk2q <\.Y?ۥ l10Re11EGq qC1q Ҧۥ-E+uLs#- u;1H0/1!$,@yqmq]yߡqiئq iOqiܥ#>/ai4! &iq@X'2q a ]j~1 ݟrD 0ӝLI64eptepI(iDi1zD ƕ I)^!3E羚bX, @ȧt:'N٨!3EWi{ÿտ ʼn!3ϫ(qQcuχϫϽϠϗ3?ݡ5GYO?߳ =OQ?UHOiO_a___3?]U_y>X0o&8J\hArhAopӦ@25>MAC-O__uW|>Ǩ,Ӣ٨"D55$ee O%%_/q/(re ////A/////bI/?zuhA ?2?D?V?h?z??oo?,DmDOO(O֯g^OpOOOOOOO$_V8ou_D(Ϻ__^po$oĶHideoY+RXbooooooo b PqA*btwTypʿܿ+@@p8"@@ 1]o/1CJ/ϏᏀ/Yk}'ݻ͟]Pq /AS6w,16=1˯ݯﯟ0%7I[m뿋C<1//ϓ'?9?-?Ho(,?oߨ&a@ buM+%~mEa1BA Pq6I +ö`f?+@h5ƌRU0xSX,Au`^$?RuYGQ#ERfRtQA ?PbCtQ&b3d!g,b2e;mQ~Əy&3E'A\_/. FU($P>GRSԿ _U(YWbÐ'@Q.j?@@4 8g t?o[E銓PbtΟuA'O9OKO]OoOOO H )tgalod@8OOO _(_:_L_p_V,ӈXd?kѤX____Pin\ gs2oDo"ff8foxoooooooo,>PbtEuKXP9?گoYYN5uV&8Zl'%%O #;:E|ޭCUg] ar%sv0~O?;-Cyg;-F$ʈ?xrz?M;--3xҗ?2eE_?vE =RϬV# =g˱S?=(E}uX?i=+&?#HƳi=9C9?/+-**c?Ά@+-#5GYF///////??+?=?O?a?s??????? @.@Rdvݽ$ ( !3*M ! %tYk ~z 6q٠Fs:xrava  r-vrNxg"H+b+ @Q ^^A 8uo {U%7I[c8ns<ٯG!v%P>j?@@͌K/4O=VE蛯b摂$l~ƿؿ 2DVhzόϞϰ:X]͟.ϐR %Ÿ ៙?O"O4OFKϯ);M_q˿ݿ____oo(o:oLo^m"Com_pr.pC`unk`za zoooom(q_{faZ~;xmb|ry_qpy~\ gsvf8!;ԏrB (:L^9^NXUϲoYYϟ̼z/A/z?-?EW{ύϙ8}%O,]tMI[]/=9ar%^v0~O?MCyͦgMF$ʈxrz?MqM-3xҗ?2P]_?v0MRϬVMg˱S?t]}uXϽ?^]+&?#H^]9C9ϼ?/ M**c?΀q M 2DF~OOOOOOOO_ _2_D_V_h_z______@+=OaGPy!% >'rM/_/q/R 4+JR@@p8"@@8 ';rG8u}3mUs}}R\hB+q!rbEqz0O}@ } dup}%//,'e!t'#o%}-%񶷹%74Lcck}ŏ׏ l6@@`0  @@LOcTfс~֤ P&sS>bYE"3qr -#ѯ+=OasѶȸ׿ 1CUgyy!ѩ}}!Ϲ;)AES256CTRf}!Sm}!}%!7E4ZVWt:!@@̮N?׹K?Qmm-%Sl-" v$&@"" "v 0SBN$`rlsUD%S@8-тhHa:=;s3Q?5756OF?>:g|693HzΗ(U@8rT Dvs6`S,@q!V(< <<~? S?qC~ I[?ù}ߑgM" P@8d:MN82y'FB0/RC"4-Us^__U/S_o;Qv?;oMiUe"LooQnYzoCoCUL8koje5OFYp_/Cߓ=AB3A5D?9\.k?8$144MhEc]o??w+¸?Ͽ*Wi{0ɟ۟>Sp p&?J\@nȬr͞jt 禤x<@dB *PbtfBӘϪϼ@ VEV}od oT1^fBr fRߘr@~vDT1`?,>Pbt HZ(:Lpď֏ @R6H~ȟڟ 2DVhzxIp1/'/9/K/]/o/{/-mEcE//// ??.?@?R?d;GPd14?1=ݟP5=I=(ٽձauu4IDh4F@lC$@@T??չIzc`4A`nu` ï?b*^uհ/YudiyXQ~R1١ar a-d4c2o\0BP@N`rquu#c0P贁NktdsP0IUsۑ(qpA~1 d*9ձASewω̭td?1z?1?5????OO'O9OC~#XF IjF|OI?O@ONHFBFw#@GRRSiRX\/zVHY'R#pO'_/.____]Hc6R oo&CoCYo `1qh 6rcwc4 OD $OcHcXFrcXFRo y"?@?$&8J\i4);z_q_*ݏMo_o!o%7z[moQc!3E[mïկO(:HTf>ο?2`rϺef$HZl~o&`0 "vn a //-/?/Q)o&zUo&t:N" "3U4U/ Ν"#",Kϵ&(*?A|G4=)o933hd0z@~?xWb5@5GN_ʟ1^P%|1N5OYײ=i{rO겷hf:+5G﯏ղտk_$Qcύ37?I?[?m??2?????7DO*Oe?w? Ͽ?ί?ǿO(r/OO^pOOOO_'_6H]_o_~ϐϥ_1 2DVh>Zá?~S5\>%6Q \>wj?0B0?JcdT6K';?\*NoO??oF*N_B?*=?PT6xgd?,RssNW5KG?vehNy@~@L>:}:?mTՁL>%ֵ+7?Zص,^/O_?/_?_H6~PѸ(^52P{t?????OO)O;OMO_OqOOOOOOOO_%.6HZl~ȿո1dݥC>u?nk رո7I[/%N`('Urӳ  Ȗ%Q?{AS1ju//)/;/M/_/q//P///U<ϱ?_ؓDŴo\ Uҿ;Y$]1Ci_{_6___(1/45ɾ#;:T6–d?v;Qˤ4W:@Oւf&IsLsFXC`?ǴtFYuw?tFÈ rvT6GA ϫ?UsD?q5sCב*?qs=(T6F-ϑ}ftl5ꔺ?EctJϹ? Vc6[ ۪?c6 o2oDoVohoFߦ߸$6HZl~+=Oasȁ_ŏȣ} ڄߏ:#*1"2"`1ꡑ$5Xۯ} }6ȢΡ4!A0򯒧" tN,P}zs& B*1JC8} 6F| 2 2 2$$ET^}$@r/?@IV@?DљEd&?:X".l-F@^]aK(0A`CF nectF roKbQ@*1b]wV!{AU#q1`T_W[PigtoUA7Q__tYTtAUL0`_UPPt_PecPnV9R,o>oo@d)AUpJQ1`\o)RF dPnPUA;Roo}o!e AUJQ`ouTcPaspPUr[Pn]PyVCR.@R{]v3%a=arɜ1TLHHOo1Ǥ/}Y"?Ʉ}L\ Uys|Jd2L&ǣ Z F rw 0/Ab/ /2/(@ Ҫt:@@~_^ ~@8x?5=ԴJ6Bmus`u`#66Bu`ukw- O&`0@@|>?@'?N@"43^34^?ٿ# qc21,+=&A˒w *}wL?p^?p?u?.2 2²3?7??L&GHq*I  |XCgOH$4@ʂOOOOOOO_ _2_D_V_h_z______._ oo]oooRovooooo&8*u`x&8J\TǏF{,Z{{ a0B/&s.'Z6?8->?Ÿԟ .@k$Z CP/@CVE{1+%5n'2}//-p8?@dL @@Ai-A&O8OJO\OnO@OOOoOJº ?@E@?RT*X8_$T;_MQa_MVd5SÜ{________ oo/oAoSoeoǿooooooa[8t:^+%I@shߑﻏ͌Tt1揢.@Rdv/AП^π*zNlewѯJq@@.`4F XwQcuٿ!3EWi{ϟϱ 6/ASUv?4?<yOXjdO?O?OO 3bP&\nq__/4FXjod%]?Iz^l@ ~sa//*/Qcug%#0P贁Nkd#&@㴚b?3L㱺q>o챙oVЯ$UU)SD*[%BXBd5 xf͟ߕXaZM,@/1)5<& 0 %CU>d5ԍ,:%k}Ųdѿ!=!a>Q;hɽ?qxF!6Q)Q|h0QadaaIَa`E_''D?@v?n˿&DF C,x RFAABuI`Mud``CC!(dկz't:?N"@BC "D"JBOOrp!>AFO O^aӗ"AR"DKS!n8q 0H`Dq_B`0!p"./"$Ry#p74Bg tNÀAzs\A4B +b!4caQSYbYbYbHYbR4 tD@r"?@I`?d&e?,!4!&_/w c6DyQл $l_F $s,^BuKOa`sql *^qQb{}"`uQ`sWJ ich 7o/B`u@`Pt* en9rK]@JB`u-l`{R%dpn s;rʏ܏\AA `uiq`T asa ec ykCrM_q#5 2>o9@R kx!xxg}enD~Dh4Q֭? Q?Q1@(u ړ5x@a Dp/////"0///$3Pڱ&?H?DZ=ڵ@5< 9t @@|^k!@@p8@@x< CLjePSPAu`?4@T}ueoɲD᜵PS*oڵ̑؞@q3Qڵ- c oݺA˱DZD 3DZ SU"g`xAR rzwE\|x8N@ڵO&*nN)2Lвr˱0Sñ;˿ݿ/%7I[ywΜϠϲc7s7 r9~_&+QB B@@$Fğ `TЂ#%fxҘ1ߓݺ&߾?*<ܔppmF/+=FED&UO<yO L%OOh#_gOG_FORv9,L'B?vPD,-vPD0o@U1( O-D&UA%U f -h"/T))+U( g&+ P- `m-vT ?!}-a8LXQ -H*)A/"/) EQ.///feArial UncodeMiS6?/?`4 R$fSymbol$67fWingds*7 fECalibr@  ?$fSwimunt(S$fPMingLU(w$fMS PGothicj@ $fDotum"|i0@ S$fESylaen  $fEstrangeloU dsaC 9$fEVrind1a8 Q$fEShrutqi| Q$fEM_angl$fETunga<"@ Q$fGSendya*/ (   R$fERavi"o Q$fGDhenu"*/ (  R$fELath#/ R$fEGautmqi | Q$fECordia NewE R$fEArial"*Cx /@~4 R$fMalgun Gothic |w L$fETimes NwRoan*Ax /@4$@+EB@+.B@+%BC,=BTC[,3B4C,5BC,GBtC -:BTCD-7B|{-ABT-0B0S-1Bt0S.0B0SM.0BT1S}.;B1S..B42S.9B2S/-B3SL/2B3S~/;B3S/8Bd4S/IB:0JBTheDoc"Gestur Fom a FhrungsliieGuideSTR Normal STR-Verbin d ST}RCon_ectr&msvNoAWutCn 7ecPage-1"Zeichnblat -1Row_1visVerion*Byte oudrVai bl&Byte o]rvai7bl0Byte oudrVai bl. 1,Byte o]rvaiwbl.1"Zeichnblat -2Page-2EinfachBasicRe_chtkRectan7gl.Byte oudrVai bl.4*Byte o]rvaiwbl.40Funkti]o /Sbrune0Functi]o /sbruneKlwamer0Byte oudrVai bl.2,Byte o]rvaibl.20BracketRow_2Flus normaFlow NrmaBUmgekhrt sucwif_Klamr Revrs bac6msvPreiwIconCopT Pa g&msvStrucueTyp 4msvSDTarge_tIn eusc io 4msvSDCaloutNHi'ght(msvSDCalou]ttye2msvSDCalou]ttyen *msvSha_peCtgorisOrientaioHi_deLaruAtachToSide"Re_sizW_thTxSideLewadrBginLeadrE n(WHBoxInterUscin IsEndnteri oExtens7ioInsetSideMpont&fnMidp}ontOfse&CaloutuSyeR oCStyle1CStyle2CStyle3CStyle4CStyle5CStyle6CStyle7CStyle8CStyle9CStyle10CStyle 1CStyle12CStyle13CStyle14CStyle15CStyle16CStyle17CStyle18CStyle19CStyle20$Orient]aioR oOrientTopOrientWBotmOrient5LfOrientRghOrientAuoLeadrRotLeadrH inLewadrC7nt$LeadrMipontsuAsociaton$Design}fekt.1visUSETypemsvTheeInfomsvTheeyp(msvTheeLatinFo t(msvTheeAia_nFo t,msvTheeCoplxFnt6msvTheeLinTra sprncy,msvTheeLinPate r *msvTheeLin]Wigt.msvTheeLin_Rou dg@msvTheeCon _ectrwra sprny6msvTheeCon ectrWPatr 4msvTheeCon ectr]Wigt8msvTheeCon ectruRu di g2msvTheeCon ectrBgi .msvTheeCon ectrE d0msvTheeCon ectrE d2:msvTheeCon ectr]Bgi S z6msvTheeCon ectrE dSi z6msvTheeFil Tranprncy,msvTheeFil Patern:msvTheeSadWowr np rncy0msvTheeSad_owP tern,msvTheeSadowty l0msvTheeSadowXOfst0msvTheeSadowYOfst<msvTheeSadowM g_nifc 5to4msvTheeSadowDircton"msvTheeCol r$msvTheeEfe7ctVerbin dCo}nectr0Dynamische_r Vb7nd(Dynamic onetrTextPoWsiin6Dynamische_r Vbnd.14.Dynamic onetr146Funkti]o /Sbrune.186Functi]o /sbrune.186Funkti]o /Sbrune.196Functi]o /sbrune.196Dynamische_r Vbwnd.1.Dynamic onetr166Dynamische_r Vbnd.17.Dynamic onetr176Dynamische_r Vbnd.23.Dynamic onetr236Dynamische_r Vbnd.24.Dynamic onetr246Funkti]o /Sbrune.316Functi]o /sbrune.316Dynamische_r Vbnd.32.Dynamic onetr320Byte oudrVai bl. 3,Byte o]rvaiwbl.36Dynamische_r Vbnd.34.Dynamic onetr34 Textan}mrkug$Text AUnoainuAtribuesBpmnId"BpmnCategoris(BpmnDocueta in&BpmnArtifa]cTyeBpmnNaeBpmnStae"BpmnPruoetis,BpmnPruoetis_Nae,BpmnPruoetis_Ty.BpmnPruoetis_Va7luLBpmnPruoetis_Valu_EUxr s oBdyTBpmnPruoetis_Valu_EUxr s o]Lnga9e:BpmnPruoeti_s_Cr_ela oBpmnText$BpmnCatego_ryRf$BpmnEleetTye6BpmnConectigOb]jTye(BpmnCoditT ye4BpmnCoditExres  RBpmnCoditExres _BdyZBpmnCoditExres _Lagu;g"BpmnMe]sagRf,BpmnMe]sagR_f_Nm8BpmnMe]sagRf_PruoetiBpmnMesagRf_Proeti_5NmBpmnMesagRf_Proeti_7TyDBpmnMe]sagRf_Pruoetiw_VlubBpmnMe]sagRf_Pruoetiw_Vlu__ExrinodyjBpmnMe]sagRf_Pruoetiw_Vlu__Exr]inLn uPBpmnMe]sagRwf_roeti_uCrel o2BpmnMe]sagRf_Fro RBpmnMe]sagRf_Fro Prtic+pn.)TyeBpmnMesagRf_Froj Rl FBpmnMe]sagRwf_ro Eti'y .BpmnMe]sagRf_To NBpmnMe]sagRf_To Prtic'pn%Tye>BpmnMe]sagRf_To 5Rl BpmnMesagRf__To Eti#y  BpmnDirect o&Textan}mrkug.49*Text AUnoain.490msvWarwnOPesoal5If3hcX2G3,m2%G3T2$G3Xh2E3$h2G3To2E3m2E32#G33!G3|o03E3o43E3l;83E3|;<3E3@3(G3hch3G3Ԉ}3%G3Ԫ;3E3h3E3ĭh3G3t63E3TU3E343+G3d4(G3 .41G3_4.G34%G3hc4G3ic4G3h4E34'4G35'5G3D!5/G3P5+G3d6{5E3U5E3L65E3*G3Q>.G3<>8G3>/G34>+G3̆?.G3 '???G3<~?8G3?5G3=?9G3<$@4G3tX@0G3@2G3D=@;G3=@9G3=.A8G3dfA/G3>A:G3A2G3B/G30B2G3TbB2G3D>B=G3B7G3C%G3-C'G36'TCG37'nCG3ĈC0G3C)G3ԋC G3>D6G37D/G3>fD5G3?D5G3D?D5G3?E5G3?:E3G34mE/G3`'E6G3lE/G3D`'F6G37F/G3`'fF6G3܉F/G3`'F5G3a'G5G3Da'5G6G3kG/G3LG1G3G.G3a'G6G3/H/G3^H#G3,H%G3hHG3LjcHG3TH&G3$H)G3T&I)G3 7'OIG3<7'hIG3I$G3I.G3I-G3J1G34 '3JIG3d^|JPG3a'J9G3X7'KG3K(G3ԌGK&G3b'mK4G3K(G3K1G3^K=G3$^7LEG3|L$G3L.G3Db'L8G3 'M>G3 'DM=G3$ 'MCG3MZG3N]G3^{NJG3,N1G3^NKG3t 'AO5G3 'vO<G3dO-G3D^OGG3'&P3G3d'YP6G3,P$G3DP)G3tP+G3T_?Q2G  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~]>rFxml:spaceprsrv]rFxmlns:vdhtp/%scea .i+ro of.+o/i iA/20*c6%et/ \n]rFxmlns:v14htp/'sUcea .i-ro oUf.-o/GUf=e'v=s=]o'200'Eet1 lnU.U-t4 "H$ @Y,b'@@d$C^-@=7 AU %t4 '_d C-m7"AJ@@GdJR@T@d6RH<(H<(JEğfREfR{Pd  g"4FXo 8)hn(N1j@(Y{Õ#aPh_D~=}B<U P:]dz`S^> BvRER$l@!()0U'*~}1d50259QT/bB="jczK&D[՗Y ?t?@d}D?!@ f~%!././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/internals/frontends.rst0000644000076500000240000006362314601576577017735 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none .. _json_output: All about JSON: How to develop frontends ======================================== Borg does not have a public API on the Python level. That does not keep you from writing :code:`import borg`, but does mean that there are no release-to-release guarantees on what you might find in that package, not even for point releases (1.1.x), and there is no documentation beyond the code and the internals documents. Borg does on the other hand provide an API on a command-line level. In other words, a frontend should (for example) create a backup archive just invoke :ref:`borg_create`, give commandline parameters/options as needed and parse JSON output from borg. Important: JSON output is expected to be UTF-8, but currently borg depends on the locale being configured for that (must be a UTF-8 locale and *not* "C" or "ascii"), so that Python will choose to encode to UTF-8. The same applies to any inputs read by borg, they are expected to be UTF-8 encoded also. We consider this a bug (see :issue:`2273`) and might fix it later, so borg will use UTF-8 independent of the locale. On POSIX systems, you can usually set environment vars to choose a UTF-8 locale: :: export LANG=en_US.UTF-8 export LC_CTYPE=en_US.UTF-8 Logging ------- Especially for graphical frontends it is important to be able to convey and reformat progress information in meaningful ways. The ``--log-json`` option turns the stderr stream of Borg into a stream of JSON lines, where each line is a JSON object. The *type* key of the object determines its other contents. .. warning:: JSON logging requires successful argument parsing. Even with ``--log-json`` specified, a parsing error will be printed in plain text, because logging set-up happens after all arguments are parsed. Since JSON can only encode text, any string representing a file system path may miss non-text parts. The following types are in use. Progress information is governed by the usual rules for progress information, it is not produced unless ``--progress`` is specified. archive_progress Output during operations creating archives (:ref:`borg_create` and :ref:`borg_recreate`). The following keys exist, each represents the current progress. original_size Original size of data processed so far (before compression and deduplication, may be empty/absent) compressed_size Compressed size (may be empty/absent) deduplicated_size Deduplicated size (may be empty/absent) nfiles Number of (regular) files processed so far (may be empty/absent) path Current path (may be empty/absent) time Unix timestamp (float) finished boolean indicating whether the operation has finished, only the last object for an *operation* can have this property set to *true*. progress_message A message-based progress information with no concrete progress information, just a message saying what is currently being worked on. operation unique, opaque integer ID of the operation :ref:`msgid ` Message ID of the operation (may be *null*) finished boolean indicating whether the operation has finished, only the last object for an *operation* can have this property set to *true*. message current progress message (may be empty/absent) time Unix timestamp (float) progress_percent Absolute progress information with defined end/total and current value. operation unique, opaque integer ID of the operation :ref:`msgid ` Message ID of the operation (may be *null*) finished boolean indicating whether the operation has finished, only the last object for an *operation* can have this property set to *true*. message A formatted progress message, this will include the percentage and perhaps other information (absent for finished == true) current Current value (always less-or-equal to *total*, absent for finished == true) info Array that describes the current item, may be *null*, contents depend on *msgid* (absent for finished == true) total Total value (absent for finished == true) time Unix timestamp (float) file_status This is only output by :ref:`borg_create` and :ref:`borg_recreate` if ``--list`` is specified. The usual rules for the file listing applies, including the ``--filter`` option. status Single-character status as for regular list output path Path of the file system object log_message Any regular log output invokes this type. Regular log options and filtering applies to these as well. time Unix timestamp (float) levelname Upper-case log level name (also called severity). Defined levels are: DEBUG, INFO, WARNING, ERROR, CRITICAL name Name of the emitting entity message Formatted log message :ref:`msgid ` Message ID, may be *null* or absent See Prompts_ for the types used by prompts. .. rubric:: Examples (reformatted, each object would be on exactly one line) .. highlight:: json :ref:`borg_extract` progress:: {"message": "100.0% Extracting: src/borgbackup.egg-info/entry_points.txt", "current": 13000228, "total": 13004993, "info": ["src/borgbackup.egg-info/entry_points.txt"], "operation": 1, "msgid": "extract", "type": "progress_percent", "finished": false} {"message": "100.0% Extracting: src/borgbackup.egg-info/SOURCES.txt", "current": 13004993, "total": 13004993, "info": ["src/borgbackup.egg-info/SOURCES.txt"], "operation": 1, "msgid": "extract", "type": "progress_percent", "finished": false} {"operation": 1, "msgid": "extract", "type": "progress_percent", "finished": true} :ref:`borg_create` file listing with progress:: {"original_size": 0, "compressed_size": 0, "deduplicated_size": 0, "nfiles": 0, "type": "archive_progress", "path": "src"} {"type": "file_status", "status": "U", "path": "src/borgbackup.egg-info/entry_points.txt"} {"type": "file_status", "status": "U", "path": "src/borgbackup.egg-info/SOURCES.txt"} {"type": "file_status", "status": "d", "path": "src/borgbackup.egg-info"} {"type": "file_status", "status": "d", "path": "src"} {"original_size": 13176040, "compressed_size": 11386863, "deduplicated_size": 503, "nfiles": 277, "type": "archive_progress", "path": ""} Internal transaction progress:: {"message": "Saving files cache", "operation": 2, "msgid": "cache.commit", "type": "progress_message", "finished": false} {"message": "Saving cache config", "operation": 2, "msgid": "cache.commit", "type": "progress_message", "finished": false} {"message": "Saving chunks cache", "operation": 2, "msgid": "cache.commit", "type": "progress_message", "finished": false} {"operation": 2, "msgid": "cache.commit", "type": "progress_message", "finished": true} A debug log message:: {"message": "35 self tests completed in 0.08 seconds", "type": "log_message", "created": 1488278449.5575905, "levelname": "DEBUG", "name": "borg.archiver"} Prompts ------- Prompts assume a JSON form as well when the ``--log-json`` option is specified. Responses are still read verbatim from *stdin*, while prompts are JSON messages printed to *stderr*, just like log messages. Prompts use the *question_prompt* and *question_prompt_retry* types for the prompt itself, and *question_invalid_answer*, *question_accepted_default*, *question_accepted_true*, *question_accepted_false* and *question_env_answer* types for information about prompt processing. The *message* property contains the same string displayed regularly in the same situation, while the *msgid* property may contain a msgid_, typically the name of the environment variable that can be used to override the prompt. It is the same for all JSON messages pertaining to the same prompt. .. rubric:: Examples (reformatted, each object would be on exactly one line) .. highlight:: none Providing an invalid answer:: {"type": "question_prompt", "msgid": "BORG_CHECK_I_KNOW_WHAT_I_AM_DOING", "message": "... Type 'YES' if you understand this and want to continue: "} incorrect answer # input on stdin {"type": "question_invalid_answer", "msgid": "BORG_CHECK_I_KNOW_WHAT_I_AM_DOING", "is_prompt": false, "message": "Invalid answer, aborting."} Providing a false (negative) answer:: {"type": "question_prompt", "msgid": "BORG_CHECK_I_KNOW_WHAT_I_AM_DOING", "message": "... Type 'YES' if you understand this and want to continue: "} NO # input on stdin {"type": "question_accepted_false", "msgid": "BORG_CHECK_I_KNOW_WHAT_I_AM_DOING", "message": "Aborting.", "is_prompt": false} Providing a true (affirmative) answer:: {"type": "question_prompt", "msgid": "BORG_CHECK_I_KNOW_WHAT_I_AM_DOING", "message": "... Type 'YES' if you understand this and want to continue: "} YES # input on stdin # no further output, just like the prompt without --log-json Passphrase prompts ------------------ Passphrase prompts should be handled differently. Use the environment variables *BORG_PASSPHRASE* and *BORG_NEW_PASSPHRASE* (see :ref:`env_vars` for reference) to pass passphrases to Borg, don't use the interactive passphrase prompts. When setting a new passphrase (:ref:`borg_init`, :ref:`borg_key_change-passphrase`) normally Borg prompts whether it should display the passphrase. This can be suppressed by setting the environment variable *BORG_DISPLAY_PASSPHRASE* to *no*. When "confronted" with an unknown repository, where the application does not know whether the repository is encrypted, the following algorithm can be followed to detect encryption: 1. Set *BORG_PASSPHRASE* to gibberish (for example a freshly generated UUID4, which cannot possibly be the passphrase) 2. Invoke ``borg list repository ...`` 3. If this fails, due the repository being encrypted and the passphrase obviously being wrong, you'll get an error with the *PassphraseWrong* msgid. The repository is encrypted, for further access the application will need the passphrase. 4. If this does not fail, then the repository is not encrypted. Standard output --------------- *stdout* is different and more command-dependent than logging. Commands like :ref:`borg_info`, :ref:`borg_create` and :ref:`borg_list` implement a ``--json`` option which turns their regular output into a single JSON object. Some commands, like :ref:`borg_list` and :ref:`borg_diff`, can produce *a lot* of JSON. Since many JSON implementations don't support a streaming mode of operation, which is pretty much required to deal with this amount of JSON, these commands implement a ``--json-lines`` option which generates output in the `JSON lines `_ format, which is simply a number of JSON objects separated by new lines. Dates are formatted according to ISO 8601 in local time. No explicit time zone is specified *at this time* (subject to change). The equivalent strftime format string is '%Y-%m-%dT%H:%M:%S.%f', e.g. ``2017-08-07T12:27:20.123456``. The root object of '--json' output will contain at least a *repository* key with an object containing: id The ID of the repository, normally 64 hex characters location Canonicalized repository path, thus this may be different from what is specified on the command line last_modified Date when the repository was last modified by the Borg client The *encryption* key, if present, contains: mode Textual encryption mode name (same as :ref:`borg_init` ``--encryption`` names) keyfile Path to the local key file used for access. Depending on *mode* this key may be absent. The *cache* key, if present, contains: path Path to the local repository cache stats Object containing cache stats: total_chunks Number of chunks total_unique_chunks Number of unique chunks total_size Total uncompressed size of all chunks multiplied with their reference counts total_csize Total compressed and encrypted size of all chunks multiplied with their reference counts unique_size Uncompressed size of all chunks unique_csize Compressed and encrypted size of all chunks .. highlight: json Example *borg info* output:: { "cache": { "path": "/home/user/.cache/borg/0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23", "stats": { "total_chunks": 511533, "total_csize": 17948017540, "total_size": 22635749792, "total_unique_chunks": 54892, "unique_csize": 1920405405, "unique_size": 2449675468 } }, "encryption": { "mode": "repokey" }, "repository": { "id": "0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23", "last_modified": "2017-08-07T12:27:20.789123", "location": "/home/user/testrepo" }, "security_dir": "/home/user/.config/borg/security/0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23", "archives": [] } Archive formats +++++++++++++++ :ref:`borg_info` uses an extended format for archives, which is more expensive to retrieve, while :ref:`borg_list` uses a simpler format that is faster to retrieve. Either return archives in an array under the *archives* key, while :ref:`borg_create` returns a single archive object under the *archive* key. Both formats contain a *name* key with the archive name, the *id* key with the hexadecimal archive ID, and the *start* key with the start timestamp. *borg info* and *borg create* further have: end End timestamp duration Duration in seconds between start and end in seconds (float) stats Archive statistics (freshly calculated, this is what makes "info" more expensive) original_size Size of files and metadata before compression compressed_size Size after compression deduplicated_size Deduplicated size (against the current repository, not when the archive was created) nfiles Number of regular files in the archive limits Object describing the utilization of Borg limits max_archive_size Float between 0 and 1 describing how large this archive is relative to the maximum size allowed by Borg command_line Array of strings of the command line that created the archive The note about paths from above applies here as well. chunker_params The chunker parameters the archive has been created with. :ref:`borg_info` further has: hostname Hostname of the creating host username Name of the creating user comment Archive comment, if any Some keys/values are more expensive to compute than others (e.g. because it requires opening the archive, not just the manifest). To optimize for speed, `borg list repo` does not determine these values except when they are requested. The `--format` option is used for that (for normal mode as well as for `--json` mode), so, to have the comment included in the json output, you will need: :: borg list repo --format "{name}{comment}" --json` Example of a simple archive listing (``borg list --last 1 --json``):: { "archives": [ { "id": "80cd07219ad725b3c5f665c1dcf119435c4dee1647a560ecac30f8d40221a46a", "name": "host-system-backup-2017-02-27", "start": "2017-08-07T12:27:20.789123" } ], "encryption": { "mode": "repokey" }, "repository": { "id": "0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23", "last_modified": "2017-08-07T12:27:20.789123", "location": "/home/user/repository" } } The same archive with more information (``borg info --last 1 --json``):: { "archives": [ { "chunker_params": [ "buzhash", 13, 23, 16, 4095 ], "command_line": [ "/home/user/.local/bin/borg", "create", "/home/user/repository", "..." ], "comment": "", "duration": 5.641542, "end": "2017-02-27T12:27:20.789123", "hostname": "host", "id": "80cd07219ad725b3c5f665c1dcf119435c4dee1647a560ecac30f8d40221a46a", "limits": { "max_archive_size": 0.0001330855110409714 }, "name": "host-system-backup-2017-02-27", "start": "2017-02-27T12:27:20.789123", "stats": { "compressed_size": 1880961894, "deduplicated_size": 2791, "nfiles": 53669, "original_size": 2400471280 }, "username": "user" } ], "cache": { "path": "/home/user/.cache/borg/0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23", "stats": { "total_chunks": 511533, "total_csize": 17948017540, "total_size": 22635749792, "total_unique_chunks": 54892, "unique_csize": 1920405405, "unique_size": 2449675468 } }, "encryption": { "mode": "repokey" }, "repository": { "id": "0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23", "last_modified": "2017-08-07T12:27:20.789123", "location": "/home/user/repository" } } File listings +++++++++++++ Each archive item (file, directory, ...) is described by one object in the :ref:`borg_list` output. Refer to the *borg list* documentation for the available keys and their meaning. Example (excerpt) of ``borg list --json-lines``:: {"type": "d", "mode": "drwxr-xr-x", "user": "user", "group": "user", "uid": 1000, "gid": 1000, "path": "linux", "healthy": true, "source": "", "linktarget": "", "flags": null, "mtime": "2017-02-27T12:27:20.023407", "size": 0} {"type": "d", "mode": "drwxr-xr-x", "user": "user", "group": "user", "uid": 1000, "gid": 1000, "path": "linux/baz", "healthy": true, "source": "", "linktarget": "", "flags": null, "mtime": "2017-02-27T12:27:20.585407", "size": 0} Archive Differencing ++++++++++++++++++++ Each archive difference item (file contents, user/group/mode) output by :ref:`borg_diff` is represented by an *ItemDiff* object. The propertiese of an *ItemDiff* object are: path: The filename/path of the *Item* (file, directory, symlink). changes: A list of *Change* objects describing the changes made to the item in the two archives. For example, there will be two changes if the contents of a file are changed, and its ownership are changed. The *Change* object can contain a number of properties depending on the type of change that occurred. If a 'property' is not required for the type of change, it is not output. The possible properties of a *Change* object are: type: The **type** property is always present. It identifies the type of change and will be one of these values: - *modified* - file contents changed. - *added* - the file was added. - *removed* - the file was removed. - *added directory* - the directory was added. - *removed directory* - the directory was removed. - *added link* - the symlink was added. - *removed link* - the symlink was removed. - *changed link* - the symlink target was changed. - *mode* - the file/directory/link mode was changed. Note - this could indicate a change from a file/directory/link type to a different type (file/directory/link), such as -- a file is deleted and replaced with a directory of the same name. - *owner* - user and/or group ownership changed. size: If **type** == '*added*' or '*removed*', then **size** provides the size of the added or removed file. added: If **type** == '*modified*' and chunk ids can be compared, then **added** and **removed** indicate the amount of data 'added' and 'removed'. If chunk ids can not be compared, then **added** and **removed** properties are not provided and the only information available is that the file contents were modified. removed: See **added** property. old_mode: If **type** == '*mode*', then **old_mode** and **new_mode** provide the mode and permissions changes. new_mode: See **old_mode** property. old_user: If **type** == '*owner*', then **old_user**, **new_user**, **old_group** and **new_group** provide the user and group ownership changes. old_group: See **old_user** property. new_user: See **old_user** property. new_group: See **old_user** property. Example (excerpt) of ``borg diff --json-lines``:: {"path": "file1", "changes": [{"path": "file1", "changes": [{"type": "modified", "added": 17, "removed": 5}, {"type": "mode", "old_mode": "-rw-r--r--", "new_mode": "-rwxr-xr-x"}]}]} {"path": "file2", "changes": [{"type": "modified", "added": 135, "removed": 252}]} {"path": "file4", "changes": [{"type": "added", "size": 0}]} {"path": "file3", "changes": [{"type": "removed", "size": 0}]} .. _msgid: Message IDs ----------- Message IDs are strings that essentially give a log message or operation a name, without actually using the full text, since texts change more frequently. Message IDs are unambiguous and reduce the need to parse log messages. Assigned message IDs are: .. See scripts/errorlist.py; this is slightly edited. Errors Archive.AlreadyExists Archive {} already exists Archive.DoesNotExist Archive {} does not exist Archive.IncompatibleFilesystemEncodingError Failed to encode filename "{}" into file system encoding "{}". Consider configuring the LANG environment variable. Cache.CacheInitAbortedError Cache initialization aborted Cache.EncryptionMethodMismatch Repository encryption method changed since last access, refusing to continue Cache.RepositoryAccessAborted Repository access aborted Cache.RepositoryIDNotUnique Cache is newer than repository - do you have multiple, independently updated repos with same ID? Cache.RepositoryReplay Cache is newer than repository - this is either an attack or unsafe (multiple repos with same ID) Buffer.MemoryLimitExceeded Requested buffer size {} is above the limit of {}. ExtensionModuleError The Borg binary extension modules do not seem to be properly installed IntegrityError Data integrity error: {} NoManifestError Repository has no manifest. PlaceholderError Formatting Error: "{}".format({}): {}({}) KeyfileInvalidError Invalid key file for repository {} found in {}. KeyfileMismatchError Mismatch between repository {} and key file {}. KeyfileNotFoundError No key file for repository {} found in {}. PassphraseWrong passphrase supplied in BORG_PASSPHRASE is incorrect PasswordRetriesExceeded exceeded the maximum password retries RepoKeyNotFoundError No key entry found in the config of repository {}. UnsupportedManifestError Unsupported manifest envelope. A newer version is required to access this repository. UnsupportedPayloadError Unsupported payload type {}. A newer version is required to access this repository. NotABorgKeyFile This file is not a borg key backup, aborting. RepoIdMismatch This key backup seems to be for a different backup repository, aborting. UnencryptedRepo Keymanagement not available for unencrypted repositories. UnknownKeyType Keytype {0} is unknown. LockError Failed to acquire the lock {}. LockErrorT Failed to acquire the lock {}. ConnectionClosed Connection closed by remote host InvalidRPCMethod RPC method {} is not valid PathNotAllowed Repository path not allowed RemoteRepository.RPCServerOutdated Borg server is too old for {}. Required version {} UnexpectedRPCDataFormatFromClient Borg {}: Got unexpected RPC data format from client. UnexpectedRPCDataFormatFromServer Got unexpected RPC data format from server: {} Repository.AlreadyExists Repository {} already exists. Repository.CheckNeeded Inconsistency detected. Please run "borg check {}". Repository.DoesNotExist Repository {} does not exist. Repository.InsufficientFreeSpaceError Insufficient free space to complete transaction (required: {}, available: {}). Repository.InvalidRepository {} is not a valid repository. Check repo config. Repository.AtticRepository Attic repository detected. Please run "borg upgrade {}". Repository.ObjectNotFound Object with key {} not found in repository {}. Operations - cache.begin_transaction - cache.download_chunks, appears with ``borg create --no-cache-sync`` - cache.commit - cache.sync *info* is one string element, the name of the archive currently synced. - repository.compact_segments - repository.replay_segments - repository.check - check.verify_data - check.rebuild_manifest - check.rebuild_refcounts - extract *info* is one string element, the name of the path currently extracted. - extract.permissions - archive.delete - archive.calc_stats - prune - upgrade.convert_segments Prompts BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK For "Warning: Attempting to access a previously unknown unencrypted repository" BORG_RELOCATED_REPO_ACCESS_IS_OK For "Warning: The repository at location ... was previously located at ..." BORG_CHECK_I_KNOW_WHAT_I_AM_DOING For "This is a potentially dangerous function..." (check --repair) BORG_DELETE_I_KNOW_WHAT_I_AM_DOING For "You requested to completely DELETE the repository *including* all archives it contains:" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/internals/object-graph.png0000644000076500000240000116124114601576577020250 0ustar00twstaffPNG  IHDR (&[vsRGBgAMA a pHYs\F\FCAIDATx^-'p LFWLsd'7 'iתC&D ZTK$$A@ CO]]k~][׹v:Q``````````Jf```````````Y@pS;`````````(ŀPP. 0 0 0 0 0 0 0 0 0 03 (ζi 0 0 0 0 0 0 0 0 0 L7 + 0 0 0 0 0 0 0 0 0 0L 8jv 0 0 0 0 0 0 0 0 0 0P\\``````````fP,%m?|Ӏ`````````n@(W(``````````pf%§'N``````````BB 0 0 0 0 0 0 0 0 0 0 43 XJ:~ 0 0 0 0 0 0 0 0 0 00݀PP. 0 0 0 0 0 0 0 0 0 03 (JOO1 0 0 0 0 0 0 0 0 0@)rr``````````i@gtM```````````\\``````````fPW;c`````````R  2 0 0 0 0 0 0 0 0 0 00Ӏ,`)l 0 0 0 0 0 0 0 0 0 0tBB 0 0 0 0 0 0 0 0 0 0 43 (>=vj 0 0 0 0 0 0 0 0 0e```````````YR7 ``````````rr``````````i@gP"|z"\Ԏ`````````J1 + 0 0 0 0 0 0 0 0 0 0L 8o0 0 0 0 0 0 0 0 0 0  2 0 0 0 0 0 0 0 0 0 00Ӏ,DDک 0 0 0 0 0 0 0 0 0 b@(W(``````````pfKIg4``````````e```````````Y@pS;`````````(ŀPP. 0 0 0 0 0 0 0 0 0 03 (ζi 0 0 0 0 0 0 0 0 0 L7 + 0 0 0 0 0 0 0 0 0 0L 8jv 0 0 0 0 0 0 0 0 0 0P\\``````````fP,%m?|Ӏ`````````n@(W(``````````pf%§'N``````````BB 0 0 0 0 0 0 0 0 0 0 43 XJ:~ 0 0 0 0 0 0 0 0 0 00݀PP. 0 0 0 0 0 0 0 0 0 03 (JOO1 0 0 0 0 0 0 0 0 0@)rr``````````i@gtM```````````\\``````````fPW;c`````````R  2 0 0 0 0 0 0 0 0 0 00Ӏ,`)l 0 0 0 0 0 0 0 0 0 0tBB 0 0 0 0 0 0 0 0 0 0 43 (>=vj 0 0 0 0 0 0 0 0 0e```````````YR7 ``````````rr``````````i@gP"|z"\Ԏ`````````J1 + 0 0 0 0 0 0 0 0 0 0L 8o0 0 0 0 0 0 0 0 0 0  2 0 0 0 0 0 0 0 0 0 00Ӏ,DDک 0 0 0 0 0 0 0 0 0 b@(W(``````````pfKIg4``````````e```````````Y@pS;`````````(ŀPP. 0 0 0 0 0 0 0 0 0 03 (ζi 0 0 0 0 0 0 0 0 0 L7 + 0 0 0 0 0 0 0 0 0 0L 8jv 0 0 0 0 0 0 0 0 0 0P\\``````````fP,%m?|Ӏ`````````n@(W(``````````pf%§'N``````````BB 0 0 0 0 0 0 0 0 0 0 43 XJ:~ 0 0 0 0 0 0 0 0 0 00݀PP. 0 0 0 0 0 0 0 0 0 03 (JOO1 0 0 0 0 0 0 0 0 0@)rr``````````i@gtM```````````\\``````````fPW;c`````````R  2 0 0 0 0 0 0 0 0 0 00Ӏ,`)l 0 0 0 0 0 0 0 0 0 0tBB 0 0 0 0 0 0 0 0 0 0 43 (>=vj 0 0 0 0 0 0 0 0 0e```````````YR7 ``````````rr``````````i@gP"|z"\Ԏ`````````J1 + 0 0 0 0 0 0 0 0 0 0L 8o0 0 0 0 0 0 0 0 0 0  2 0 0 0 0 0 0 0 0 0 00Ӏ,DDک 0 0 0 0 0 0 0 0 0 b@(W(``````````pfKIg4``````````e```````````Y@pS;`````````(ŀPP. 0 0 0 0 0 0 0 0 0 03 (ζi 0 0 0 0 0 0 0 0 0 L7 + 0 0 0 0 0 0 0 0 0 0L 8jv 0 0 0 0 0 0 0 0 0 0P\\``````````fP,%m?|Ӏ`````````n@(W(``````````pf%§'N``````````BB 0 0 0 0 0 0 0 0 0 0 43 XJ:~ 0 0 0 0 0 0 0 0 0 00݀PP. 0 0 0 0 0 0 0 0 0 03 (JOO1 0 0 0 0 0 0 0 0 0@)rr``````````i@gtM```````````\\``````````fPW;c`````````R  2 0 0 0 0 0 0 0 0 0 00Ӏ,`)l 0 0 0 0 0 0 0 0 0 0tBB 0 0 0 0 0dӧOׯ_Q`6͛ߟwNj 0 0 0@nkV}````7|sv5```?L```4 !u{ 0 0 0 e ޽{G `X۷r=```4 ૮_ 0 0 0 0&6X1 0Pϟ z^- 0 0 0@!4Fv 0 0 0 g@(7'z ߀P{h! 0 0 0@c@(W(```` 庱 0r)j 0 0 0{m 0 0 0 0 c@(7^z c@(^z 0 0 0 k````H2 놲 0]) 0 0 0^nqk`0 0 0 0 0|za\ 0Przi\% 0 0 0PYP````` ɀP*0 ,o@(wr 0 0 0{HپY 0 0 0 0@>rq 0@9rq 0 0 0BfAa````$Bn({ 0kʩ2 0 0 e f^g 0 0 0 0ͧƅ^0-ƥ^2 0 0 ```` 庡 0r)j 0 0 0{m 0 0 0 0 c@(7^z c@(^z 0 0 0 k````H2 놲 0]) 0 0 0^nqk`0 0 0 0 0|za\ 0Przi\% 0 0 0PYP````` ɀP*0 ,o@(wr 0 0 0{HپY 0 0 0 0@>rq 0@9rq 0 0 0BfAa````$Bn({ 0kʩ2 0 0 e f^g 0 0 0 0ͧƅ^0-ƥ^2 0 0 ```` 庡 0r)j 0 0 0{m 0 0 0 0 c@(7^z c@(^z 0 0 0 k````H2 놲 0]) 0 0 0^nqk`0 0 0 0 0|za\ 0Przi\% 0 0 0PYP````` ɀP*0 ,o@(wr 0 0 0{HپY 0 0 0 0@>rq 0@9rq 0 0 0BfAa````$Bn({ 0kʩ2 0 0 e f^g 0 0 0 0ͧƅ^0-ƥ^2 0 0 ```` 庡 0r)j 0 0 0{m 0 0 0 0 c@(7^z c@(^z 0 0 0 k````H2 놲 0]) 0 0 0^nqk`0 0 0 0 0|za\ 0Przi\% 0 0 0PYP````` ɀP*0 ,o@(wr 0 0 0{HپY 0 0 0 0@>rq 0@9rq 0 0 0BfAa````$Bn({ 0kʩ2 0 0 e f^g 0 0 0 0ͧƅ^0-ƥ^2 0 0 ```` 庡 0r)j 0 0 0{m 0 0 0 0 c@(7^z c@(^z 0 0 0 k````H2 놲 0]) 0 0 0^nqk`0 0 0 0 0|za\ 0Przi\% 0 0 0PYP````` ɀP*0 ,o@(wr 0 0 0{HپY 0 0 0 0@>rq 0@9rq 0 0 0BfAa````$Bn({ 0kʩ2 0 0 e f^g 0 0 0 0ͧƅ^0-ƥ^2 0 0 ```` 庡 0r)j 0 0 0{m 0 0 0 0 c@(7^z c@(^z/_Ss޾}ǵ-ka] 0 0@Nr͂ 0 0 0 0IrmJ1 r)v_~] _s]g&]g:vlwPkf``N3 0 0 0 0@mnں9 0@]rKxaoV(ufuc2[`` 'N3 0 0 0 0@\7sm[xdR \ewfʽutرݱC՚`m 8 r" 0 0 0 0Iri:3uʭƷ~/aׯ_ L\bLXc+ 0 dIa````H2 vN7m 0P\Kl?s(ۧ8Vk`` /I75-~0 0 0 0 a@(=Y'w 0P\K7n75;WM``5  0 0 0 0 $uz hg6 ;\#Ԉ5& ``| $l|7zG}5jK \,``πPn}=7X߀P5XK3 ti 0 0P\AC[o%/zDm? 0 0 ׀Pq{g 0|{cMrզb`=z4Çӯ9=߃ߣ9c{``r6 g>m 0pTBծϮP~^`` %x)?gn~S~lHW3իWyt߿?؟xߏ?xz)3f^#=b```Bԙguf2 [Wo^€P.GK8 ``u d(i3rkjnW0"O !5{ܾkDv|Q0f{㶾; 0 0@r.ٷ}2 ^r;N``  2ճoFaXΣGÇG |u-]jjr6c%f٩kd_ }a`ՀP.ڴ]l2 {d}Bԝwug`H1 +@R1ch.fm~"P64ׯ{CG |JyQ{V( И8kW a$``r=_/ j@(\mڮ|m Fo``2@n/^P-7B}G |v&En>^[+ꤐ``R rk8aW/ԋ5\8``׀@ffL%Eo",aJ1+n; ){lgB2NRCR8׌'[`` r_{= m@(Y~yƄrq 0 0@9rr@y);ff=jsn}hrzӃz^s5Ga[9IKd`рP.9M\2 2|tö{Bלs5g`H5 I 3a^Ws,]Vbܘ>kGQͥYLMkAz 0 0rYIu0rkŕZ1\cX``׀PP.8|~bvQK!G9rߗhلmDKo``4BLf090Xr/K`` df4t]%7º[_Oۜ"PF]h:ʍtmc1wNvPnrej(Re>ttqzg`2 ˅c 0r)jZ\K7ng`#e`G?cg(wK/]؏):6Ԙ{(ѣG52%w 7%۷๙݉+ 0 0 1 ? 0m@(06k@(f```;;2Az~Agٳgo> Nglp/lor˔Pn__!qrPnfֻ1Q՝``5& ` ._SNմtBn1 0G6 Shlr䞳qr#(}}1!#rPn8n)RݱqcZ% 0 0ͷ7ƍ05 {wz\g1 0rr@W4n^5B>$o[_3uG n]1\G ~s\}Kk,S/``f@(wZxS7`Krp``\fƚzf`` d||;9׺+t68cƆ={lLi7jpPuI ~w3[4Snm4^[``g6 kl 5 X3^  0 0\\v4pPnlgj3 yoɜJKJ(7@nC(ww'Rj 0 0kek-[ l@(irՍ7uc`€@掁-lyHʝ3; nݫ.CGe9˫Pn# 0 0)r9Iq50 厫_Pq`0 0 k@(W(  b㏿. KΩ˥P/ FK>x no>1 0 0zb  0p|Bq[enks 0 0n@ 3!T:(W+O_(7@n)@ccz 0 01 {^Wz ǀPqze\U.rYŢ``\\v4:[߿xA,SiΌB>KS{4Sno+qi 0 0 L3 ;n 0%B|8F00րP.3cx=3 0 0v2w >Bk_w R aVS} +[Bo1\}%rs},G 0 0 00ƀP./cx-/ 0@ܴ:N i@(x0``5 +Bpٳg(rʍ=IB}c3::Jo``B>r[enksw|sBq 0 0@2w dX>x5Gr]3 G4Y^NaKwu3zwPnja9 0 0 0pTBծfr6 g>m[>r/ 0 0a@(W( lg2mׇܜTm5{~%l{Ϻ82 0 0כNx```9BՒKŀP.X3 0 d<"ۼ_ ExÇ< |5еnhk冫CgϞ*qPnT cǝ/{VOd``OBƃ 0kʩn@(ҍ?``rr@_x25H(C-lKoo_%{Pnߘto$``f@(٣2 rz1/By``s;2 H-w)1۵L1v̻9%u[.:NB 3ǶOosg`` \g3 b@(\,ڎX=N+b`3 +bҾM-751'ʍu0=x`i>f}Jc^Wɛ9 0 0rӟueTk=BՖ[e`k@ s@zd߿ip ҡܨ1nW95{]fr ?}A9OAܾqh2Sz3 0 0wBƅq 0kʩn@(ҍ?``rr@7[gϞ} hr i/շPnzjRy_Ρܾ@n˘zzD``(рP.%O\3r۠ϠPzf 0 0P \=R|,``̀P.󵙷믧9ż^_ͻ?Əh@(]&.``? dfA) zj0Dۘ3B]n޿j BG)7Rpٳg5 \vգPn31fǫ5C<14.S{; 0 0etoK&TޓӏPcP(}2)'B<Ѷ 0 ՀPP.C6onbo{#w0i^ڧƍ_ 6~^V2g[.Sf!V(7\ u 0 0%a=CMgmͷ7ƍj@(\m.6``3 fH5(80GpoF1kcC7>׵ l0:6IJbr} 1Z0Pnl_ kr2 0 0 2{4ٸ_D/G6Ҕ}Y>i~xrÝu۝Pg 0 0PTT25Ç3 ^zm CA47r@4ZKr>I?̵g S;w>}zˤ`ce2 0 a@(W^/}5|]ZflSڦ_zs]__:nCegvy}Ǭ,j ör7 0 00ƀ@f1Mڲ}7̜P?s 󸹟Z؏xXs>>r+𬯖C)O=t؆1˛;kr3K:˘m[n} ߼yS8g4``3 [}k0e\_oyM=εg?/5w7)~o)k6x}ǧ{,oɾ:rש+ 0 0n/Sapj nZ?)ׄ Æ>=W/n7Ǜ|m20ehȐ3 0 0}ur#l/^ Mq9LͬAqu1+$-T#4_/}ʍ0U-Γ ΡL(1$1|p丏Y|fKczǾ{i.oSkuB5CF 6VA,7!ܧO.2B7n8]]]^|yͷƚ0m 5+k C 0 0 a@(|w}B<\bڳrʍk(i,eyO[B,Ϻ鯆P9Fj|(^7'>whKеϝKm=g 1V}u_~Η}]wϟ?zϏOSիW],/ګ}'rՍ7u@mܸ::Myܼ&>Yx[J](cx_`1FAZn;<密|}5۔e|S½Krc7־fϱʭ2׉sA5al |댷cN^{SZcY3 ,i`j(W8%ZO 0 0}r˷q)۞b]kYr(;,O]i*B,foZ(5=l3^Z`cǖ5kuonݕH ~ƶ?zhr$c)|tLݧڅr8O1=1s|^ϔ@i>N߲66hNoߞ|}` YέhW``xr׳R(7\#Hе&+ȘC`yzrP. }9JvDps]ϕk͚nB k|cvJe`h)}v:`l3>x3F`R(wJ }=~(h _ɱ{u0 cڿoߞnܸx w˗/¹ di`PpnS?{``TB[ v`;evK"G?p"xvْR\xXlSn=B{|Ka cy>y}λـPp(75}3xpĔ`Ǭm MX~1ׄ)םX w]w:#;}c~<߷د#.sMu}m}2b} kr'XG= ,x 0 0Pr{یۡPn+5%XvXv1kL}1Ik:RݼmϯǬs;6u5Sd6>}t}j3<~xsrz{X++?``Bo\r}]ԙȆ]HKr/͊G ծ׵q޾&Zڶ/, /SƎPPg]OΣ2Ğ}ory/"+8@V ޺c`o@(=)ܮׄԇ!%17r HѨKx8zL(=6>5ea:c=Sf! m BzsYH5 N(7%gOyw,q=e{bϔmz3|j-x~. et]Q׭CB4=)uwvt)t8 w/_uL)ckƍھ^Y@.5ܸ7$R'QzzU(W8Sz 0 0=rf S>Hnr}Lسژh8 rlGW+PPh:RҺS:-94.^(9~N?Br#ܞ{{>N$7ԐR|(u.>HǏXW^N z/s^w^԰c#qޟu>7M?j8Tx]1,w̶vw9ϡ}BÎ}lqM2RNf) :ű>OD?ؾcՔcǥϕfS?v.^o;8=uw\3Dfan8ϹY\@y gqAO``4 [PnA,u>]z='7kl{+Ju}ȱaخ}6¢6x)9Pn`ju."~מ+ƃ.M"Бrl[w,ylKAc׺)ծq/^sPe|Wr v==7>s,cK,7/c5L C}"^J1]}&0<6Cs y_[ߘ/sҔOuU(7sJ 8ek Png \]l/c8t~is}c/  ==a.u\Yc-uιVdn֓zBsNCy})^\\㨤qd_xf`3 ^msqm?8jl}p|]Ѯ-ʍens3T=1.jσ}3GCj>[,k%}#US =]M5Rc,αW蚽?[3/5?7~f E]RsޡsEf7mFs.ˮeߌ)ǻvk#T؞"n)85]_Kc3%;fK.C?c|;S\Ck8U@}eYsŃO{]{bmZP>_n_Q-Rmz ̭ gA3.1e /߾j @.\\d``ˀPn.Ƅr锇*]T d8јj/rjfv7R;ـPQw5ӧqB®c! ۶Ln\mo7 0 0@r7:& 2^]k cߟL}!A7O$Pn׶ =Ϯ]~HT'^_(wo˗/;ys'C].q,I Nktm|9h:M 嶯9f'߇ޥP]gHct-̜3"NOC7>%L \G)rߢ}ڞ:uQ).lKv}}~]˻n*^ \8@mo7G@\e>?# 0 0 oxL(7< 2f雕*^CaC1R!C3FS .c{ej_}S=oN_.~Ǜ<9_oj\ݮ:ޏ?ݻgjk]`CSw{~^ vٿ9ӥl_H9^2Eܴ|*N9'zO5ok_Enfi~ ]ס)x}JrL@x;p ܔ@nԮ:5mh|Yhc1y)|=>/'u6/LC޳W,H^s/E(r?j{yfoc;>}ɍ?l_1z 0 0Pݏ BS@u= l=dfq`ܮ?Q;aqK_۳5cyBnׯ9E(7&'i駟:CKzBCr2 yokyG)wl68vۮ٩c^,)u,f_s`Pn̢u5kz_kSgwWƨPn13gy,B3M}NDmn15Qlno`n:,xlׯcC1a]WS(ww=nb2vk/l/}2gxQek S3mh!l~Ԁ6:.cb6ܿUo6;a`(ՀPnr')j+84JA99-5tMNcC>a:d.ZrWGۯC}a¸fPns^_|9uWeP }h썗fszhq>&un5V(ckyY{ӸiflϨ{ȧuc޵}VZ>:,};F}싱6狟C '_yc”]vor}W|celלhoW8oc</7kz )wW] 5nݺ mlG6ǣ%ok-K8;82 0 NQM t7N$b׽?yvob_Tӷ?{~=&naj 3vCYc}_dϟ;Y2#[15ؿv(7o}-8{? [wc^u:)wrSf9] qnE[)#T;';?gulfN=So:gl8>Ocݩ: @3[BMߖ<C4ڎb S;vDSPOߏ ]Oݖ׍N=g;aʗ>G A_o/<ω425)I%ݻwYܹ hߌe3{(Ww^7j 0 0pDB廝 ]Lķ47fc*5!q~8c_;d2]z~;7塱÷,?:k{N&EuV8=y_s+W{} ]?>g ;~Ύ r/֌)^/מu { MS7gJիW;zm~0YH"ngl˫cg}-б}ھE7~vi 4kP"5,X7Bf@T&7;xZj P01W꘳_l3 0 厫}M ͸7PM SC1K)X֖>cC)\O ُv hslڳTquW]kkp7)>\˥~^r  {e`ӀPn}YrL v=4/}֓0/ֳD-SE>M ؇t+3 ,ayoYr=jTBr~hf1rSÔCO_!qLlJ=מ.c;.vc; Sv؞յSnQڐ5(@nvJ2Y{N;KbOmxɔg`hR3g?XwM=˭c x )wwͦ7/f? ^ 0pn P0.M 0 0 t-ŜPnlQ]ȤIAKAvE3WϜ`vˮ奆Oؖ9zg ,y6 uCخ7.G}8uC1C3vR8/ozr~9&:%X {~-;A^ǵM;5)✷/p^3'%ij݋&H?y~W9ϱZ6}m̳:d@(wPn;cS™:s^۵1!)־u{_/hQ{6ڡ^^׫}Ek(LbyM; :ZmzR_;5TC 7qj2B}o^7r!ڔڵ{'m~!P]kS}s3 k, <|pP۷on6xm4W(WLO 0 0M 冷a!nPJyxd ƿ}(8'D:]czN(vmf1ǺYSfO/MF91[(82*BrB|糱.KnEj9&l655d;l:ۜk͠W)v^Cv/%9^Y׶=\'ȍlE;2LjA@eJOSR=v{{S7u~]_ ۟%}͹w,{u5-,y90.ӵC¸Ɲq 0 0B{Y,4Ǧrfujz>VY缷k .-sO2~Pzl -ZN6;1[(82*BˇrB|cSS|Fp)ϟ?eVϡmn_oD@.4mySž[ro)Ƽ&PnP(2@Pn=zumfꌻS)c^zm;k9ka5c9u?rt?u?uk|Y:ʀPr1?l:[O޽}9 c`P0n>=7``Hr:7'6zgt@+S] b?Rf(z:]3'K(k䩽8ᐡmjyh~cPzfr E).RHqxMO91)cxkr̟;:F4e0qы1w.)?CV5SnSد1ק}_Xn=&c]vդ}k}?_F#mMfC_nh9bpw\fe˗/of`'O:?|`]  0 0rwD(aOz: kƜ1Nyl%ePn13-q.[)Ǘ29u/cPn>0.(rCgn=KCa8m 2Fة E+9&(׷}XgxM:NsB],z΃Bc;ˈ ہ{]N k;gWH=֛}ׂQ؎'{j]ǀ[b.Kr_z՛Ihs)5 ?K}io9 ߞ! rl OW]kr3~/cx}3c|,]ƺ Cw~~TY2ZD۽ӧO;w]_'X:+lxWO`` ?8I}6eAƮXKr~ؐA{`94N.)(=alö{mͷz`@(7-j^74cvRC]CLُlCן g F@k{W1V\޶f)_kz׏Ͷ.u.u;d)׍c֕NGKr/]/ rf!NQJ/ڵI?з S/mhFǎ1}[}]K{lo/+ =4+p__XJw^|?Bf@̘.̽~˗;5`~ͷRq'뗪R\a\|) 0 0=,M}r>M =M}.#R.1;yMTĶY~\a9%y}_zo(T=t ǡ}rAX)nS^\='Y2s*mwƍ7oj@D8ݻws~g _q1{~RSl㟟)BK!qnxv K[*p7q^5͟ zƾ9pĿEmR[Xvlo{ͺPϚj06zM].Sﱟ]M{W?!s6jy_::7e;b)ǀ [:_> z>v|\ սc~֮ׄ1}4%v޽ӛ7o~w>j ]:!l?XMKr6¸Ƹ1 0 0rwT(7<5aP65͟ڏ9a9sBKl˜wϥ{b]M!5(y8<٧8=_z7ft 5y:P+DL-V{=5rYϏce1{ofl.zm2۷oO?).4kwLX>}F<ƝcMǚǡzضiBƛl@(׸f4kt}n_^-GW  2P?Ǘy]*z@.[IB׃K3txSꤎP0nFSx 0 0[ʭZ\7m)]2){լ{K~Y6mʶ_ˊ}e}O p-wήz\%%L]jϢMϻ[14Dlӹźc:-}/*^_(w%Du_T+6ף,ΜkTλ?]2 d}׌cC]Gi}3kăxp<slc+~iv(Wױ|mg```MB|˲bZ _}=ݾPqvG3`MffIc>BB l/f&60 w̍k׾5ڵ?sdM'YoE41;t,s,aWP0n}'e5?7 0 0p\Bqw 0@rq7e3WkY,l֔Q@;[3^o^| d4h{/'? DlNmx4?G3Byo5Kn+>|xzr``ҀPн Y~10 {>O\sh[~|gDmm;9+z_DO45wز 6u83|k*;eѣG'1%wk?7͏ 0 0@rٿ}2 Z,\[Bָ۶p*fٌg|1 x,G@1zflrͺ@6`E>`Y?rǟYrqzko  0 07 {z g@(7'z\Fs7je` dȬ}d(oxS}>LrŋB<Y|``ס 0\F=#mBL8.0 0 k(8wG H(wlW.͔7Kr 1 0 0p|Bq 0@~rq'e4wQ`ـ@NAKrcFFhqƌ?K-{_;{ާmL O(wcP/^/``883` B8ң 2I$ 0 0\\ 6t(7?̝ʍeLXvŋCb!xm߾s,ܷ?S%j?崃ұ:/*5흺x_]د^zչPg:_7S!9u^' 0 0 00ǀP.?sx/? 0@\c``\fƚzf`` d4HzPng_tl "؄;f߽oMt:}5OY^{W>E35AnL9/]Sk?T:v)/,lS7{f|Bs)μ``r5 f6m 0pdBٯmǯP>u]``R  2PBcCѡzƺf(7f\}BC:}j(7sAؔ5]}:wM}XGك#p<(y5:c`` G# b@(R,ۏ, nWk՚```̂c1x}yC\ ̩c£CM ^ {:RBrb{ifۗ]J(7j227?CzUr0R(7%ۄ;驞2 0 0}Ͽ3e-ƫi@(5}Y6_ 0 0<BB l`PnhR4<:ilw~):&L}afա_c<{) Rg rbbYg/ohFKXZqi^3Cԏ``2 WU_`} [r=[- 0 b@ @f-g{Pn_ 2C}ͤ k~7M /ڧx&C5±mX":(x|)7Ks{ިag~~``g@(w\R/`B8N  0 0\\ 6d(7¤]@mL}վx}_7fY;F#©sfnF(wͶsfcfH{7z 0 0@^r 0@r[mKo 0 00΀@fLa`(^Kr|3vPd:g[ ""rcqPnW`xjkSקS[u e``n@(׸0.`7 |M9U 2^q8 0 ـPP.X*q).>=?ΙwPhW5j5CeP;ԯ^]>LݾKܔٙk1 0 0rkŕZ1 e%Պױe`` _2 |Vʍ\O_x:c}F(wJ 76z[~׬c ƿOS؛N9v2 0 0r``yBהS5-݀P.3 0  2Pj/w}?i ÷ jWxvNm((F(wjxćҡk1K]{d```@(#82 2{4wB8``>2 |/]nt dz OY l:rcLSC} ln```irՍ7ucd@(efg`΀PP.X#ߏRv=c}onسP](7}[^ 0 0 0\pg1@r/ݸ[޸P5TM``2 9"\T-VzjT oCG(t-jٞx(W߾~tѣUө``g@(w\R/`B8N  0 0\\ 60'ۄ0cVׯ_ 6CSsg-yܮzvjɉG'z7 0 0 ۀPg 0|8u,w] Φz ƿ N yy"JkL<1 0 04 {̾o m@(7?\.stid`0 +@A+;M=@O {vZZޘiccg o_z}^s 0 0 \6 k# 0]ir/ݸc`l@ @ae9rf ~oax084ܰgɡܵCcz>OcoQ``ÀPn}6րP[K0 q  0 0P\\ 6C(ѣG³Csؘ{=K>x`wzf{1 0 0r``yBהS5-݀P.3 0 d<2L۾́5P;C1>2±(ZPe[1p~i̺v:# 0 0uʭƳ>3ݶ|w r9.}`J5 +b&ט5BAd^z5㽩B]AϘE7u__׷ׯ_.on3rcjRwc%Sx=sXA```BƂ 0kʩn@(ҍ?``27dm_`v2c)3r ;uߍܚDR#Pn_DŽv8*OSk"ԛ:P2Ǜ>g``>Bl8>0 ,o@(wretq`8\\667 iׁ/ȔO.֮0r /CaϘ1o1 {ʍvmӥg}kϗۧ:'q 0 0 08Bŗz1 $ʼnprn@(``| dn4 [Pn3£@/~G}Mmwn3PyM7?4cmi_ћXN'-xwcqn 0 0e6y€P.g[8 OS?``  2ZCMr(`{L1 aC_>jl06jtbb{z6OCNfS?``΀PvZ`z kZ2 R,%```y2!^jD(7f(M`cL)˟AulXFJsÞ9rccf15M5Z_ͶŲRS:``o@(w|8S3`!B {Frp\``׀PP. ulF+\0exϜ5Sӱ1nE7m>iײ=a```}Bטc5f3 [_ύs=k@(!```=2a^jÇ j_@2B]1ٳS~#?sc֛f"$ۆISS7zYK//Ŭ}Z.Qǔ~5!؎ؿئnN{>>3 0 0f@(7N< 0B⵼\ ``5 +"XF !s :{a``XڀP.SK<`5\8``\fƚzf`` dn{;9ֺkܘQoѮYr#٦m3 0 0ʭƵ^3ݮ\u)rY.Ų``J4 +Ff}Yoȶ=Knx߿/QJ<''2 0 0 0s 24א3 ݀Pqa\00րP.3cx=3 0 0v2@jW< v͒? n+B}Z3 0 02 {~_ ÀP1d==>~2 0 0@/7޿ nDիW/B={nj~}ܿz 0 0 0 a@(X0`7 |M9U r3NAY``Zxb```` 2)c8]5= ql_^o,0 0 `@(W"```` )^Mq1 f@(LڞM޻w/E?ut阓1G`q9f````$BnY^ 0)r9Iq5xqցܸnr]g&]g:91 0@i9f````$Bnv090X׳ ƶ{N(ufuϱ?_ 0 pD```` puSZ`efg& |ݻwrrub`j6tw````0 k, 0]) 0 0 0^r͂ 0 0 0 0IrF%do``@ڊb````\¸`XހP5TM```2 k````H2 F^7=(ـP.%o|3 0 0@mnV@ 0 0 0 0rp`€P.g[8```m ```` )wn3 0Pܺm|7 0 0 0P@```` ŀP.')N``qK```r6 k````H2 fw7m 0pTBծf`` g0 0 0 0 0P. 0]) 0 0 0^r͂ 0 0 0 0IrF%do``@ڊb````\¸`XހP5TM```2 k````H2 F^7=(ـP.%o|3 0 0@mnV@ 0 0 0 0rp`€P.g[8```m ```` )wn3 0Pܺm|7 0 0 0P@```` ŀP.')N``qK```r6 k````H2 fw7m 0pTBծf`` g0 0 0 0 0P. 0]) 0 0 0^r͂ 0 0 0 0IrF%do``@ڊb````\¸`XހP5TM```2 k````H2 F^7=(ـP.%o|3 0 0@mnV@ 0 0 0 0rp`€P.g[8```m ```` )wn3 0Pܺm|7 0 0 0P@```` ŀP.')N``qK```r6 k````H2 fw7m 0pTBծf`` g0 0 0 0 0P. 0]) 0 0 0^r͂ 0 0 0 0IrF%do``@ڊb````ŻwN_Ǐ_~ָOo߾~MO 1>}Bz+s)= 0 0 j@(6_ 0 0 0 0@>rq͛sΩgoݺuoO?I=pӧO/_?}=F_#P?ݻwu7n޽{zI}:~ wS?O```r  2 0 0 0 0@(۷{}=|P/c/roF5}uueLPK~S߃cr/K```.HD 0 0 0 0 0 {<1YqSC1Kg8o~5^k3Ck,G_kqS(8J' 0 0 0PL o`=0_```` GBr!%}ˈo&}/_"$doce}w|Ǭ7o\ׯ_;r}_jN```R fLJ#ƀg```XۀPqK6ϗu-Ν+?}t޸qCsϫ5]}>Ϋ 0 0 0ܝn8(1 0 0 0 =krF0ӧO~_駟Vk8߾}5K|5#r}Bԝwug``XÀP7he:00 0 0 0 le@(7k[7w9}Eo{̯_$ǏuFz@n5;}ݠS>r|WW```NBހ1  0 0 0 0ܼ}Fn_cɓ'B~+c7V%|vջw}c}מ|ÅrMe``` OBX?O/ 0 0 0 08B굵~웿>|ip/_ܧO}zuj_O_```: fx`s0껾3 0 0 0@r5]aB~+gYr|󍾮/_כ7obԶ}B~f 0 0 7 M58TC5d```2 W?GcgGh4£>?~[_/+o1 0 0]U_e```1 O݇ϻ…6޽{{_o޼)3cˏ١c Տ9^d``]AeP1 0 0 0 j@(7O>=6|"}xco޼C>K}]s?r|WW```NBt1 0 0 0 0pByZYjso}Vo}}QQGǏg^lPn1 0 0]&)TOd```2 W?q֭,B~›w^}{Օf|H(w1x 0 0 0{````fc ײ>0v\```rr````` @a2 \~3zzz 0 0 0@nCR7````5pƍ,dw^}}䉐_ 3rz' 0 0 0  0 0 0 0 ;wo~ZoAG7},ǏeǸz' 0 0 0n0 0 0 0 06?! ׮]͛7`__|{O? W\````1f. 0 0 0 0 0 O }v޸qC_o{_#;]z 0 0 0av7]`````)fsVǏ `֭[J_ 3 0 0 0zl_U[e````ތ?e]K}]ӧOwW_Wc@5US```#u````8ׯ_޽k~+sd˗/n>E;|m^Ϧڪ- 0 0 0 پR8TC5d````@;{ƍ}p۷oVϼlf]]kׯ_]qZw 2 0 0 j@( ````` ¸?ӧO_gݵk6`a7Kٮk;wucB̖{M~osWq) !7 0 0 0  0 0 0 0 0Ka?f!EMq7_~,p}֭S6lLog/Ջ```R ٞ&z 0 0 0 0@RøsמUUps{o[-竏kz)n!e```.BB 0 0 0 0 0@fqoi=Dxj7nD^uU8wާk```cp=б~ 0 0 0 0qϷɓ'ܹsޒlY˗/۷ug1m ߼y }cO8wܘt S/```87 6Ҁd````%ø޼ysܠOe5o߾=ݽ{wv_ÇO>} D޽{?pnqW```8 n44_ 0 0 0 0KX+{}_|9ş΍;#$87ܷT,c/fWo&sf=X}P0'͎{il P_```ӀPP. 0 0 0 0 0-¸]7c?>ݹs7w֭F1 0 0 077_````4Woc{u/;6_C!X]s|}v .;Vԓ``` r3,k`~2 0 0 0e-WYyuu53wޣ¹\:c``݀PP. 0 0 0 0 0a\ zH VsYʚ 0 0 0pntͷR 0 0 0 0 c@w^WZqLs# 0 0 0  2 0 0 0 0 = x岸g\g1 0 0 `nL 0 0 0 0>q;uer!A```=  2 0 0 0 0 㺱Ǎu 岙d粸3``` nOٞKm  0 0 0 0ݾ朏Pz~sׯ1j 0 0 0rr`````D¸`!Biui: n_s՜``` ٞxPZ3 0 0 0 f@LmP.CFp.{~```0 + 0 0 0 0 uc~[,S(--\Vpd1 0 0 bv\`````a\7s?u;rjg 2=e````In{ 0 0 0 0 ¸n/y~e 岼9ew````on{ 0 0 0 0u~K_(Mm<\6g}1 0 0 ,aK2 F````i@}3ކ&;\#Q#cx'```0 k````΀0?$e4¹Lf0 0 0 i Y_```` ŀ0.')NJxP.%8\Kmf``0 k````(ހ0n7=HPnz:fs7M```J7Ph````7 k|:>rٯžp.X3 0 00 k````(΀01nP{^r׫-yV87Ͼ/ 0 0 0Pn@ 0 0 0 0qBBcA8Zo```< ````o@7n|za\ }λ3 0 0 n7kowc```ـ0.5BƆ\cX`````KBfAa````u#}G\P1rDknp1/```1p 0 0 0 0@qq?BW=έƵ^3 0 0 a@(,( 0 0 0 0 0a\7~u 3GŶ #[8``` dP깞3 0 0 0 4qYppn=6```ˀPYP``````a\7=ˀPn^0>pn}=7```L~@@ 0 0 0 m@_(77uF8~ 0 0 0 ) 0 0 0 0 㺹CrS_sq 0 0 0@FlN( 0 0 0 0a@ǂc=F$[_ύs=g``2 k````4 [׍c o8,¹e_```^7= 0 0 0 00.4 {̾oM8z 0 0 m@(,( 0 0 0 0 07¸ev c㸬 OS?```z x  0 0 0 0 ˀ0n7=((Be¹ڸk```Lx 0 0 0 0ׄq˼~}ʭ}-ƣ>2 0 0````*6 [MaPn}6pn=6``(ˀ.?p1 0 0 0 00ƀ0./cxgƙ]2 ˇc 0 0 01  2 0 0 0 0Pacܸu]eh!Ӑpn}1^```1KE\ |```׀0n7Pn}7pn=6``8\\```` 6 {nRrYZʒmI87? 0 0 g h=s```h xPܺozn9 0 0 0\\```` 2 X7ekBmm0'G}```5KA\ z= 0 0 0 28\6 [wg``3 + 0 0 0 0 wsՍmـP.9mݮ\5 0 0 0@\ b```׀0n7>ŀP.')N'¹ڸk````_BB 0 0 0 0 Ȁ07TVe(VmVs7 0 0 g=p1@z 0 0 0 0e1ryk"[_ύs=g``1 + 0 0 0 0@qQꆴ:f@(L۟uL SW^Օ``׀.?p10z 0 0 0 0 ˀs 3ǏG8z 0 0 c@(W(```` s# fùP.󵙷˘]<# 0 0 0P\2zb ;^````@4 Ӓ,>O¹8s````BB 0 0 0 0 h@wn#5 kL ,a@8%YG 0 0 0P\v|R4 0 0 0 Հ01aL0\eέƹ3 0 0  2 0 0 0 0ai72V7W/ԋ4¹iuI```z xށz 0 0 0¸ 80\޶f]yέƹ3 0 0irr````Xр0nڍJ7tՉe .[O>Փn¹Ɔ 0 0 0W6````^¸޸ rCPCzzm\5 0 0 0pـPP. 0 0 0 0 uSMir0 amC}sq 0 0 0_ x`````z {^s4 e.mS=.sq 0 0 0P 2 0 0 0 0bqtvә rKTKzn9 0 0 n`;0 0 0 0uƭƷ~̀P.G3k{6+[v_e``4 kV````Fu f`@(#89έƹ3 0 0@m¹8s``8\30 0 0 0 m@ڣܬ.i@(%=YOe3WM``5P녭z 0 0 0¸ 80P\ko/[_ύs=g``\  ````(¸nz3v\޶f]b@8\,``@Q\@= 0 0 0 285j6 ; 0 0 k@(L8 0 0 0 06 F7x՟< E_րp[```ӵC?p@```׀0n7aB5Hǀpn=6``6 k&````CuSu rzӭ 29c``  s=g```ƀ0.  0n@(7V\έƹ3 0 0Vr̈́ 0 0 0 0a\7KYj=d@(<2 Z,-``50녩z 0 0 0¸ 80tBkǝ1Pzn9 0 0 e@(L8 0 0 0 0Yu3tVMryɻ}})¹,-erXb``^Y=p^z 0 0 0 0 ˀ ,g@(wZr g@8z 0 0K5 0 0 0 0q\frΝ_CՐ``j3ڊm`````?  g@(wr g@8z 0 0S  `````S¸nfN}0n@(7V\ e%Պױ 0 0@6}Z^````@΀PvZπpn}=7``R  `````U¸nVެ:VX΀PrR-`πp. 0 0 0@\```׀0n7 @ǀpn=6``  `````Q¸nJݔ{FX߀P5X`m@8 ````.@ 0 0 0 k@z@~rq' c@8^z 0 0mBfa```e@MG7` ?B83 [_ύs=g``Y\```׀0n7 #Hǀpn=6``r̈́ 0 0 0 00ʀ0n,3@rqG g@8z 0 0@}F=p> z 0 0 0 0ec@(82 s\``ǀPp````.ƭfz@9rq k@8z 0 0@}z0n~Pna)N #H``(߀nn1 0 0 0-fz@4[Yr4آ e@8~ 0 0 xVC7XO````I¸<-ɲxb`G-,0.՝f``yrr````Z?>G$;Qn2 a\i1E_(@{-gܕ?X``z T z3 0 0 x{ڵSLx4¸ ͬe4ϖkܴ>N 0¹Eׯ޽{w\}..``1 k6$```@<ʽyaf]憘y\7& =_k}aXoUԌ``{v&f```3xp<=l¸XRK@%}>Foޣf 0 0 0\h 0 0 0 0Px`{7rq\ㆠer@r-,y18R8|ϟ 溷Qս}v0 0pb```1jہcf\nB w3,=fKr-,:f1@mϒ\]~ݻl錙:ƌ>3 0 0@j 0 0 0u/{SNaܺiϺc /9͖kܼl[57Knswݯu80 0 0\!1 0 0 0@"h{ϖ+ 4780lfy-7Kuծ_d7 0 0{(۞ŵn```< ă١@n~rqa h r͒kl86l lo p,{ϟ?c9``v3ۊ]0oj 0 0 0Px r\a\cƱhg`g5Kqf 0P=ù)6tׯ_?{N0WC``] Ғ/F훛- 0 0 0 c 15gdžq 0p{Ζk\cc6r>玙%})>GLj; 0 0@r````H6Ď 5[05߀30^%w^߸W?V1_]]] Q/( 0 ـP" 0 0 0 0Px;%Yj\a\7|ж@-,y85ùSf=|`{E8v,c` 8u 0 0 0SK̖+Fc73 g`r͒^/e Ν:Kns]wӻws(G泒`nɧ ```(@KTCj3 0 0p\E=tN```0Z@rq]¯ep l5[Yr9t,b207;w«+\_H````d 7q```.Krc9C  le`r͒V520%,ׇϟ?````uEu] 0 0 0{Kr/͖+^έ=6lfɭۗ3Ƅs%~ݻwB2 0 0 j`Յ}fn0 0 0 0@uPl¸90{Xs\䲽m揁 sט%V})>O˜~7 0 0r````j0]}7onݺk y`kܺo 0%qʗ/9̺ 0 oI ```8\C[ˍI=bZ L-,G-~@r%#d;vf5&ksalf=F5}b 9Ns]xS| 0 02 ぱ uT#Yh>D.8rmc 0 0>}t/m}>g}f5& 17[Yr9kya5 aë+tLr$W 0 0@ye.``IG޽{ >``z m<5[%k-'ck\25ǔK8,xd,˸b``\z=ݻwn{ō='02 liov9n^+[.h% ` Gf\S_@l8e``  1@qB;~zy00 0x[h^rcܪ= 0W%0>`\ u1&]\i;m``4P\<鋾li/{uuF=``L |t֭rfuu 4% ` G% 5O'" 0 0#3}i;h&#H`XǏOPݻ~ƃy^8L;Z}~XzhyooI>J?c1˨k:ksK;ڟk>mCYm 0tBN`8B?| 03ws>z m>G/ٻ3>f0f_|]4Հr0o posGO8N`8B>leb9w~Ԁ@0uq{oB@rykǒ㗚3@͛7~ ;s{```Ņ@Y z$W`sx`=wY# 00̀P zP]fsgct-¸BװJO8:qg 0 ˀPYR`8B r_ 0 3~ 02yP]f_ݦ[{v [f_vd}10πkyO```@qa{ cX`ӀƃݿkV,1 N]̾b`:ŗz1 0XBfIe 0a0 ـٯmBeZ̾rܱ|`@(Lak $nI\\g3 0@ Xdd3mͷz3 04?a@(LTWm}e8rӫ15l}ck``uq 0 m@(, 0Pܲ?/ 051aL0r2du S׽-{~gθz^ 0 00@qaz STO`7a|0r2du S׽-{~gθz^ 0 00րPYR`8B> ~z=3 0G6&Gk-ŀPn=.w5-ӹk2Z1>[:<پ 0x96 lk@(wz 0 k}Ͽ3@-Ӂeq[_8j̸r [f_Z߀k{` 0e5K* g@(.'& 0_ xiL 0{8̾a2Ya`Buoaޮ+p3^| 0 5P\olޠa<B8S` xi| 0{8̾a2Ya`Buoaޮ+p3^| 0 5 kT(΀Pñ^  0 xv~K1 [ek)g rt̾pLd%y/<3 h0^EM?ݶ|7 0@s 0rtAu}uWc@(83\Ö17Zgz 0@r͒-ˉ2 0Wh o@(w1<.kXLVXǀP:u۫k2+犁q\댫_ 0c (πPny=5N`~h o@(w1<.kXLVXǀP:u۫k2+犁q\댫_ 0c %3 p쇡3 0pdh{dR iك2Zq~Y2-5#%psI 09(.cmr7 0<ܷ? 2xP]f_=Nƌ+װeue ٿƁ0 0P\2@qrrb 0 ՀƄ1ݿkV,1 N]̾b`:ŗz1 0XŅ 3 [^OS=e`߀ƇݿkV,1 N]̾b`:ŗz1 0XBfIe 0a0 ـٯmBeZ̾rܱ|`@(Lak $nI\\g3 0@ Xdd3mͷz3 04?a@(LTWm}e8rӫ15l}ck``uq 0 m@(, 0Pܲ?/ 051aL0r2du S׽-{~gθz^ 0 00@qaz STO`7a|0r2du S׽-{~gθz^ 0 00րPYR`8B> ~z=3 0G6&Gk-ŀPn=.w5-ӹk2Z1>[:<پ;uO;{16F10|nπP|mKc2뚓1uۈ#m 0S ƛޫ31P2?ƭ&k`Lh2ݾKeu +>1 Ln̺nb ̀NZ{`` ՀPUR`8G ?56߽{m}l+BVr>7|ôkbqϑH^+8Ӿ 00ĀPnNLTY!}kԞ< Q1luۉ㄁e ,۾j_`<>a !_߬ZMV0k[m[nL~}c"Z}0le@(ŀPnMTY\/  Y=Z,ـGgX@qa56tN6pPn V!9V2:綦|VʥV oSޛc(79l Vm0vޗuD}goLh27 Duu-X-ՀPnaˬk!ku.նb` BVIe 1 {nd|N=N.؏~~BUcͭ&987 lkϿg0 [ey[]؏*_ÖY^0پ0 0Px V}w#r}J䐶{M3N\}}nɱb5B+ڴ_l0K允e .Ӯ[{5Q]f]ve\10܀Pړ+c2'AN_ 0 ,k@(* 0P#rcEfx?3is _s.{#$l|1ofq``iBe3Q]f]>|nπP|mKc2뚓1uۈ#m 0S ƛޫ31P#rcҹWoX}P!~eKB˶e`&4ig3 3 [e\]؏*_ÖY^0پ0 0P V:UٝJ}QCZn5ma\Wu^e)B˶}gLhGڈ6 [1eu熁 ז94-9/1oX8F 0 00ŀPP. gਡܸ4sUq~OoĿvo\i^㏃>~8}jb_cǶi6>?vSBq vm==mۈok:FJ>;sƾnH(7ک~͚꾇f;p8N6)>6yUw/>Ѷz4V3s\}dh=|2!5hO+b\BeDuuu.VWc@(w?JWưe5ŀ2u~ 0 m0eU_bȡfh6BsCڬz2U(pj(7 A ;*p3==#lط>kǴmm# A 6X%> }6C\D}WuϘ!fyh pG : iF{3[1!-ロstWK7f_\&4U/K(ӀPnu5Q]f]ՕOR1luM1 0ck 0eʵJ* gȡW6ClF:Ɔr3mܮZs?$mnl۶q.۶h&r#:$֩u>mRڠ%nJrǴi{=\=CPtD?JͶZ⪫ߎD Fͪ:U_ 12'A ͼ0 [fMTYWeue`?rS~e [f]S x- loXgj 0@ [6XU!ʍ0g= li3V_sL(7€@Z{?m+6WuJmy6U1hgEuU[}~ۂǪ8cWm[6‚mv&,?K rSp乕mj]h0gʱ~ڶm+,Zկ}bno5!׆x}\(7j:% m/} JR+5 [fmMTYWbue`?rS~e [f]S x- loXgj 0@r8z(| r6/Vfm߻Šmyv*h{w׃q] o F"vcsέW˪vU\(-lzaf[wm'>\ȶmEӔX gՏGڬ3lm4OmVԡ+8vG[{un>RùmW^|vKIi3`Є>?e-&ˬ2BUJ2-)7`} 5``lŅ-Gփg\Mcus즆rl[oSZuxM3ض*lڂնlE/B}!3a!6%e*޵C pm}_ۂ] o[vHy9)9>RH[iad{~&4S+J(׀Pn5Q]f]ՕOR1luM1 0ck 0e(.l`W}=}\m-`lf-ț~m~ސP`OmS(;wl)16src{P_;TbnaPnj{ޮǬPi  W~|u);yyvYޣܶ 0rt`̺:o+1 Z+c2bk``{:@?P`6 kT(΀P/\۹Gk[X.% .LKٯ9nlRB:,c-5(d2%>kCM ={[[ۧ|!?)Fh фf^?ԃc-&ˬ2BUJ2-)7`} 5``lŅ-Bvjseڮ YJujx3)5PkRB@_L{>)ϭ^)u?,]1Im꘦}]S\7;M~/'T+=hm9̄f^?ԃc-&ˬ2BUJ2-)7`} 5``lBVIe rj#vao^߶.%pvR%SkM#g3K }nl/#jv,D(7/Bm\y誯C8 sw_RcSDW[kr- D[t{s#c~m#c{̻>0p BeDuuu^VWc@(w?JWưe5ŀ2u~ 0 m0eU_b@('`ph03%p ;Vcynٔ:g(yf޵ps!ZF{SeʍnxLٿPn}{ 湩a">5ڷ#SEkIm+˄f5ҏԈ Yce9Y]؏*_ÖY^0پ0 0P\2@qrp5*>n͠[[E8K [ (ESbαmef;T8V ePnrUm))xϹUdCpWj_j;Gƶ}W(w)#c{3w}arˬ2꼬 ǀP~jүaˬke l_@ `(ۀPUR`8B_^/e34~K 5×["ed6;)f;DPnϞ[ڇLk3`;d%)cYKmS WX1w>262ox;&4U/K(ӀPnu5Q]f]ՕOR1luM1 0ck 0e(.l`W}Uk-#Yrj}ϭZ\wN9%l@nl7%8&Y?f-rc;C?j(7VvmnkHwJm/'ͺ²}!!mzPn|n< YŹ#c~m#}mweBs5Ԍ W&ˬs2BUJ2-)7`} 5``lBVIe  Ŋc)Xٲs㑲_o<4^N.cy4Wjmt;S\/ʭcjs~^Jz]H)@hB3Gj@rˬ2Ꜭ ǀP~jүaˬke l_@ `(@qa<`;ĀPƬ8Uwܥa"eꆚ!ġ^l0jgoaPnFCalm+:$dҬGs^͡:uc,ҡ)ֺi>2}^PK^.~V k@(ښ.~ V)̺Zހ5Ԁ` Z%3 ~jjlE?5p;Vܔm3=4FoPt3+sPԕmLP!}ڏY{CGSi^_;Sضo #)vAN m@(.~ V)̺Zހ5Ԁ` l꫾C ;iV-mf܆1^F }o,-,~`i?m 6q]!+7mhmo>5)w`ܔ}j iժFa\xl0tPn48ؗ+wZm>ad9seBs?үԊr Y[eչX]؏*_ÖY^0پ0 0P\2@qr/\m?^M Y[2B]؟U蝻!i;6p7|iJیP\s{ڎ%ڰ/ Wfǿx: ͶcBU{Kl F]_a3̍mtr;&lz-J+t]u gV{*;&~]睶jF]Ȑ5W/bLBeDuuuVWc@(w?JWưe5ŀ2u~ 0 m0eU_b@(I3ڷjΚ+^Cp^'-ʍl;l JY_g_(wwm޷mgq=[}~k;޶n[(U=gVy xW}O=4תxne!ׅ>>2ٵKIi3ǽ }IR'6 [f}MTYWcue`?rS~e [f]S x- loXgj 0@rp V ]zص 6VW[iʬ}`xcWp8jٔPt6)_h[-]+vV3r F; 9|F[t\gl?̤IDATd;dybJ@~H(e>2)#C34ZWj@rˬ2\ ǀP~jүaˬke l_@ `(@qa<`;B!mS7VJm!ٝ V+1ۈvMsuss۶*lW=m^}Uf9뫬~~ [ɳ o{=#@^w??D;vs͐ϊ2۶>7փx݇^ڧ%ض?^K~[&4W3L(πPny5~j̺:+1 Z+c2bk``{:@?P`6 kT(B9%[GXp{sX2>#ld9ψ [|nl#eVڱ<}Sj%zvݘϏϊE2;՞cJk]ՖUͦ~ސo&14:xR}$FRy Mh]G}82l̺:/+1 Z+c2bk``{:@?P`6P\ز1pP` ,2BSj2Q]f]Sx= lg@(w_ҽ1lu]Ҍf ߦjS`ʵJ* g@(ׅ 0G2`B#ywj@(L&ˬk6 [f0-a ^z1 0?Ņ B5S 25) 0@LhOQ iDuu=yq[2ÖY9#0H+ 0[ʵJ* g@(u mr 0mevcBeeչK]؏*_ÖY^0پ0 0Px V}w\N8N`J1`BR,;l@(L&ˬ5ɣ-Ӽ1lu=rw: ݰg` wBVIe ~2 i&OszY<10΀Pvݛ2뚻;-?ÖYW.ue`_uU/K`؟xr۔c` g&4٧}(r˴n̺8-Pnaˬk  ~$w`- Z%3 낺6cʀ Mg122ܥ ǀP~jүaˬke l_@ `(@qa<`;ĀP.'Cx ' 00r){6 [_eu i̺x9޻cn3 0\2@qr]|s?F`4`B9=,g@(w\Duuݝ㎁_ aˬs2/:^ 0 @qa<PlnBLm1 0|ӾyBeZ7Q]f]r^r`@(Lưeֵsc`Hux?w; 0ruAj1 le&{[ٳ]ŀPnDuuuRWc@(w?JWưe5ŀ2u~ 0 m0eU_b@(!N`R dˎ=-ӯ2s}ghr4o [f]v~r݀{7lf݀PUR`8B._| 0s0Ӝ|O 3 ;rf̺q/rưeչK]ؗc}KR/`g0Cfj6\6bـ M>si<2.G9/9N~K0 [cc2Z91y$:ɻc`` BVIe 庠nqAM`2`B.{ b@(`̺:w+1 Z+c2bk``{:@?P`6P\ز1 '^  0@)Lh\e Duuƾ3y4Be7-G;?9^nX 3 n@(* 0P\/Q`9 iNO>'n{3Q]f]swgcBec2ܥ ˀξ 03P\!T35ۀP.SsyL1 l&9l۷~{ݻwzj 2Z97k\oK)[f͍aˬkhcX^Kmv 05u"k?XdXÀ Mp66",T72&-bL*[Duu㚖gy77cG(LưeuL=ưnt=ŏ 0@"&>HɀP.GXyg0^Mh?+ L(s2뺗f~rs6vYBe̺x~6eD1]o7 0@&M8pAGb\&`8zyꧢNӄ7|c^!Y xm6Qn]߽{kl=Yss Frٗ5߶;W.׶[5]q=Ʈ?㺻=lv}{pQ>]e2 0@f  z3 ݀ u@[Ф <_&4觾O%r˴cz5G9ǒKZTL2î[ׯ[[nv]CuXgvQ;2 0@\+0@qr]0 0p$&4?Nh* Xո&Or˴cz5567<\B]/Ҝr,2îW׮̫k>Xj ^ڷ>p5a` I_  0G2`Bs=oLhcBĴPzPnvLTWs+Gkpi9'PnW=z}iNW9|Pnva׫7|sv X]\Xj ^ڷ>p5a`r=E=&bp')wu|1v|KOP0~mqձrYlBs6}+ U<+? ^?xm-ӎuڷ}8oG.o5%TN_**[c/îgJvWGXyg@qa-6u^2 W=?"~"[egx?>P#M|yǥʄ:5h[!gg?7U#VJ#}qerukD:ukg5Je:PnOԳ~qQUO˹G(waשk|ps}|v~p0?`r=!~ivʷ/TԶ_|iPVv]xuɣ}`DŅJ,cra ̀PnZ{K(W(4SG(W(w!jBs]"ۜ촒 Menm,[fMT/[׮؍krn?]j5>>iv~׿V_~i_,=m7[ 0 ـPUR`8BǾ  +O]4l&,끠t_cem nK7Ql]۾R_ mj%ݥj]^]Be6]Bs%\2ưکX#w3 i0ޚg[:+yͳ.k\ܵe;BB{j?_Mho!~ڳ]z%8W9ñ_/';LCg$dTo?:勇cz\~GzH뛯VJNmg ݏsۋ}iY[x3~}]\9ֵ-z^SNQ?v+֯qNrg֯S)꘢klע~]_;✁g/Qݚn/;B˞f l]۾z۾ N1d|c 1l{jKSS :}.# 0 g@(* 0P.{ ,+/5ׄuh 4'b?h4U 'gccmcmo[˙k <6W\7mV9t23&&R>\/sn+<V;!as8zuv|]a6mj9sr;oiDrum;u^\9>I5G s׻8 p/pr4SVXn1~ǐj .wҌ1rum;u1eX2|1+No܏km-9mXgsqNu/ 0v &ms1Pz(?[#XU7uM}] bk\; 6Q߱Tзs}+(~a烾ur5`Bs)+ VkЌs{܄f\>+Kk]7B]U׍fv}8uYVmyOvމ{{Vq|՟{Զ\~ .s.޺&k53CΗtrS}Ͼ@nu}<ۮ՗Vf<)ܨQ|m<rnE˲5͐mn}}B˜1rum;u=^_a<~5y^#< ta=lXgso՟` Z%3pPndOsE!ٔϬ~CvM|$uR&vIio8Cc~B193Ky׶]}YNJ }m w'=S6ejտs,?8؄v ߴ]B4}6ٵ[} b 9ua0l|^[ȶZxϝۇC7匔/ᤆrڎ}wsfL/^Q^(ws֞LT/S׶ع/w}1"5,ZԵ`[ĬB(})ps?܃]gڶ}^Pnחsַkط2`Wx1ھ=6ԜGxP2.S׶UL+K1/5s[tX1;7<,s.C3 0xG;k -`'|f7޵rM|n5!Ut|ۤU_2jk !P[l*]մy}FKH8jexm֢ZsАv*0ګkuQPnx__n3[w}놬~5z_YJx>Ki2Q!]a!.C|r.u*|3\Qnj(7ڽoI!ŪDŽry׮kB/4y]\׵kw 52"ͮscߵ,:泞C rI 1Y߮>+]_zO~)'}xĿʝ<#ce6Nh#RMSCIư_]I5bcخ{D@߽M络Xgsq5oj 0r8Z(+ɡi{t]xKέo x_t}PnDɐm[5܃ ; ]1(v*Oj;jK=msӶR͐[rﮕ> =\mEݾɁhj E[s2t2uɾMhkNח¼MhLd ,/{/[ CC}̄fWvL })svmǔ?58r?R5Q=]?}sa0gm_4s=5Gjw9w7dl g yfRP_lj~ߗ WT>׵zs?W(wpake)ccv=z67mkSct9/}0Y\kz3 0xHk -[)s j<}ynrߐ1ؕrSۡiH8@mo]K۶@Gd㐕r듦>MePnuLQtըo{H(wl 0xDm[?wjs; }Ƚd0`Bs~Wm!縮 mB7S!QB]?fB}ukIȱƆz;r?Dum&BWq{,&9lv﻾v]Rߗ۝cBs}spLw=0z=LIʝ<cv:N%Va{1l׸ m$z3u3֙\\  0 Z%3pP l޹c32AqnVm[s16&\S3h1tmNR{k3$l]m\hrjv۟-CCA!{ߪ}ܶB_pW>m>=W6 4~ݶ69{U{59q/=:w˔wƆra50idb VCmikR#F(wsq~LT[ױ+w?Rn\(˱lvc_sZ}hfL(7}cgb {ʼn1um;=<ňs9` Ssu!חư].Wcܕ˹/0֙\[3 0x@m Jk 12y>fסum>97܏1ECCxc{Sڼl6_f`dDVsc6C}z[r)x.;vUؗds9i3@z>j Ogo39)+ I+ulRLO #]As+ğVۄPnW86eb+tXX6/vur=RWuJa\*Cetju9Wzm!PnR)m/d=&W~}}ʝ<c: ws|1e ]#a:z8^2s`.a38e` BVIe -;4xP]x~N8/CCCycnVʝȍhvoE[(95[c>eǼLynckзRls)s&)!mO gR==Ƹ1Q/`Bs^mIȾB%³]%c)!)2u15r]f=Z|G(wsq.LT[1ɺpG#ϛ81 lhKI_:+ގߡ/Cy';y8ưֵRF#1髽abu=Ɏce`a )k'T󾧆2eݶ^3p6H u6qehԿo~[}h> q75Pɽ6mȦ^ C1ռ yM #ŽcW["(T?LDlz7e_&ַ+ԷZ__Bs1dzvD_M\_|r8K׉)+Nٗ)+5XյU^V!;_[9cyvK}&v1_(r2h ;-v/? krގμkmԚ`m Z%3 {|"|dZ<ki>zh75zmEjm4P͔: ͇s<`O!禧PP6 ݄|m>ת+]^ߣ?-1K߹P[ruy_۷9ݓoKw%P|✜s3ΣR s_mWsx+}U(񥖾6ŪpOxz=m{;^(wpNNak׵Km@ci-ư]0:v0VܮcXյ2kh{RqP{r;v:_|1kkR3o2vlTXgsqF# 0 L3 kT(΀P|ܟ:79rcRy @(嶵KJȲgJsΛqn_RrܨQ 7`k@(wڍ}g] ԧ+䓺^_Zo 1_H=749-h{s[A^(wsqn6Q=O]}.]_.IIB) vxfc}=箱}Ⱶ/}Bssb ;_]ۮ*sפPc 9]3w/1֙\S]틺2 0xpK-b+Br#U]?ל%v,)16O׷]WʵRnrmSVj>qnZ(׽V{ۮ yJ CcZkULhvw ͮIsm j']3+04|";r]v}y8e<3wF(+S]Vk搟n_3w<}H#[cyڵxE^cR;Smj Kwk_8vP::sR_`7 A g@(ť-`vAn[]oBch0t>Q34 4C:K_Xk w쵴}C yݯ;:Ҏ 0 0e03Py/mH<,sCC͟JJ}xwD͐ ĜM mϑM_c.m?)5Hߔi>N\ܶjC.g%e>nC(=X{{{ ֻVZb23>3%V(7:zw{f6|Owr;Vޗ~^Mo=83Q=m𥮯VS%~1B y.4W-|u2ۻ|=ֱm'Wưu/5eIm/ca`1l@Ǟk{Wcy{}V{`1 kT(΀P HXdؕa !7}!oYkR*m}ڦ8͔ksRC})5K [)8n< m1gj6W>Sj9ƿ3οngPLLhvwPnDcۄu=Χ6ʝ=sizz]Z]6M]Ks/l 6C] gwrBbNkWr/<$g/sݻg1Vy*uK7a``x@L_ ʍmmqղa]DCHJpH8x!!k&,XpZ܆G6 @mmVfʍn^rxl8WXuwߛМnmR*&E{T ! +sM"V!֖mmuzc}iܴڋ/ڵ:`85d{t}u(eS`:_srs5e ;m_V:6:~a 5MY>sUe3\\ 0 Z%3pP`n|@kbl(7%(`t]￰uM 嶅XB 6C)^!KxRe+86q686 ]V(wm&s FYEz0mrrϭCU^_[МVokpȵ+2]۵I1)1yfМǚMyPn5JgZuޟgV2T);C( }ٮmǺ/k9N;jvz]aO5]>0۞XgxO 0 A>"ms3oOzum{phVs spB_06SMtvH(7&]-9jv9lۧ[V =|kun}mRծk <מV8w-ڶ9mN tE>g<-nۮBp _LhN=)\bB~zrm;JmϸیujFsۦ#uxA[L;'~&sUc&UvDzB]_Bm+1=j;˴~y[(71촺֥/ }M2/v/@a3\|t?` 6:V/MQB1 0i Jm!`h~gַogj(-7&q.!jڽm2(li>}ܶ炎Ѷhڌ ov=l!ڰy [ [!f~oGEݫm5W?OYOǺCMhN3v3t>|!f -V;K(맳T[[YűkBoQvʝކ9:4Q=]_JR^Bjm95FWP8u֔v躮OF:*t}\ڵW Rթ5aa7Oư:d%&!r7ư]񹟷L~L;َcg`arr`8OO׆a'ǜ_ ƄCת]}̮ j{h"XJ04ڹ&s?m?5K]ܪUt{QW࢚XZiN շDJ:\mV^_ൾ>3]?v4kvr F"W9cnz->3jsO^ko549X C)~uR]4cCC՗#έ4w* ͮPXLX#t R}^3\1rm!5X5&׵k%!+Բ-I> =6]_3n`}9mϓŚBCJ{PnuSÎkuf2N]Ϩm6;\k u/):yԡN^`دx0ک\׭/n Fu=jZrZ F/5 涅q㿝J Y4&]\Tsn}s2;v kSùԚ8V[%r'j(W8a~SEmP{-r㭯PRP^%u?׸`Րv_Ɵ< Q9kez|]P:wm}ƹI TUgn_%{Ful3M/;B9[2_׮ٗ9v=ŗIecBkajF|ty`5>G-`ӀPnf\yv.uٮ.CfPOenvW( [y6B;"q3cЯzLY$&΅b0!ang"\΍EJ5^[?f`&)ḆC'^W3tACRTX&Ԭ^f훜S_ ;?}U?>/祮DWz߭c}z o|5|7Pf 8Jls9|.2w*PnW(~SG]بeʽf=UӦܻG(w8g&յ+y7Tw|.4!e~2=ܹ2M+Ƣ#Uʝ~^ca׵u΋s_f  sܗ{,џrLciԓ` ӱXjmMʭDILvÉ3tă3wMO {aL81?f=cWh1ݬAqso> znhն=N){Ô$)Tu6﫟s?y>zܱ0ռo{?p79Z CK]7f/4W`m{TஉsP9reKY]P}ѩŘHoNSsw.ν&յk%9mfr&5/U8:? }S(w详L;f=N}{~\rrǝm ;q6X>k{}AйIg]"/y8#a~9t.5ޝf0¤}!!U_)m׎syn!j{M_:B=v\]Ǭ>C/;}Mj;Cư%j%ڹ"%po3\{]ퟺ2 0\2@qrȸW*KO25Q3`B3]KpWPDq^2AA!3-xnqU"BJC&5Qw}H?Nk=3Q^/.7]Aʮ1פsdž 3`B3ݚAЙ;0m5R%BJCB($XJyvo7sKU[nQ8!uV;S̕VM?& ZfPXPfe?n)7 Sڳ8l[-ulPsqu5Q=[\*Ky}n9۱Ͷt\wt}>󦼷Pqk꾱ީGski#;<cuMG>~r[:p:h ;֖cخu74J,c4 `1P\ehW'BZjܔUG`t&4ی3ms-Ӕ2:wy0vKưeuKSuی3m 0)ru!Lz-/ 0{7`B `@(L&ˬk Q i̺8-ŀ˥Xv,3 j0^ mX@X47zk[ښ` ̫z0pLBeDuuuVWc@(w?JWưe5ŀ2u~ 0 m@(W3Pz(ׅ / 0@̤zf߀PmSe5[-N{d [f]rxt:`6P\o:%ͿF1 00%ڒ-ӎ2:{ o@(w6_ù1lu]Îm|ukK.% 0 ʵJ* g@(M 0G2`B#ywj@(L&ˬk6 [f0-a ^z1 0?Ņ B5S 25) 0@LhOQ iDuu=yq[2ÖY9#0H+ 0[ʵJ* g@(u mr 0mevcBeeչK]؏*_ÖY^0پ0 0Px V}w\N8N`J1`BR,;l@(L&ˬ5ɣ-Ӽ1lu=rw: ݰg` wBVIe ~2 i&OszY<10΀Pvݛ2뚻;-?ÖYW.ue`_uU/K`؟xr۔c` g&4٧}(r˴n̺8-Pnaˬk  ~$w`- Z%3ÇӻwtNwanc7 00m` DuuuVWc@(w?JWưe5ŀ2u~ 0 m 0peS_=gয়~:=|0f3ĀǏ\\Μk`8ɻc=WBe4Q]f]s=/21luuSWeXg_ҿԋ`'îfɓ'7jB3BͿ֩5z5e`ۀ MC` n_%2뺄 ]]j [f]ve\1fX'^ 0 0j@(W(1P ̱B.Cg``LhgR rjRm;.b@(Lưeu/6`/ 0 0lx l݃Gj;wi'^ڗ` ̫z0pLBeDuuuVWc@(w?JWưe5ŀ2u~ 0 m@(71C!w{t֭/_urb`3Lhn{k ` DuuuVWc@(w?JWưe5ŀ2u~ 0 m䶀 dex";"dܲ/\cLx 0%0wɾ{1 [Ueu/rưe9K]ؗc}KR/`g 0@fj\.stidXʀ Msb`m'W&ˬ Wn@(>` [f]~r\̀{3ke؛\2@qr]v12 L1`B)~1 ;O;Duu͙ጁnBec2\ ˀξ 03P\!T35ۀPn~oo~?|͟mܱ>~_cᄏi81|7Z)_` [f]~r\̀{3ke؛\2@qr˿ ._Pn~oG7ߞJ Fskr? ׌ qƛvc9 Duu,FXրP_c2뺕'剁quƵoڍ`(.7N'a\Bֶ극~Zj^5Խg~%~7=sYsLhK iDuu=rgBe5-{>w&hX#w3 i@(* 0P/r˱'Zq-ebOcB=yj@(L&ˬk!krtm [f]K<9&VK6`wɾ 09(.Cm nk5([=\5eQ9cZdik _]j(?.z2?Bِ~f [f]kԞ| S B-`(ӀPUR`8G {!¯7ߜ?<1O @l/>Vl;7 z?{Pna}]?1ʭhֲ1^}~ck:I +3Ys:TSLZmΆ}BU$>뵫g{lo豞{]V-s3=| M~>rZ2.%-ӳ1lu-X=cΏ1r 0x[6m a(E$ṡf^ 6wߍ Eܱv^+mvxc#Z)ܾvESCVRPjOAaj-m?|]>CCA> tF]}0< qW},;T4؇ck_^ǽBiu0Ui{4 [[euQ i̺>a Vb~xdBe7Q]f]|rL̀Pnfaˬ/ã0> 0xK7)@ܶY"amao;ċl3@Wmk;1!=dU؏p^7S_1u>=_w6":heZvȪasr ժO}jKrvm!զ/u^H WsgV\y: 9v\4:%X>9go{Lh2Q Duu=91[2-ÖYR;ϣ0a(' 0[ʵJ* gPn=,as׶oZJ(zd[8` ط[8: 6W̬Bі?Z F~LY]AدfpHpbcpKs 6>B}f vk[8BY ^iy߷m+W\c8FY[0\5cvb{maޮk۷hW7  VլѹX2OZU6x[{ O 妄kӬEWm Vh Z)́Ϙ~dv/V'%-ӷ2Zȱ1[2MÖY? 0^q8 0 mݠS3_נzdʯ͕ur~3z.ܧXvhiZ9Sk[1\5C/5L96IYL 4=54hԶsW/m@S*ȐצRkU߇fJrڬu6rːy&?LрPnMTY#3{5 []c2u?}> 0Zr駟NO>=ŀo[qʝ7lkmѡߩsƚrmA֮cP4mN ʶXs])wNtԶLՔp CuLhݾKeu +>1 Ln̺nb ̀NZ{`` ՀPUR``V?~U 7&'?~Z쨡E/„?)a)A)!θMǔPnj`rHp0\-5Y϶UGm$R3CxH2M~mklkh(7/ԫsq^nH5k?&Z93 6`BM<H7 f{pf̺}d?-/ÖYW-ue`_uU/K`؟Yx>|ʽH GMٯ#r#4f`g΅ZCCc15x֐߹ 6_Wo 6m9w,)=rrюCS 97.q^RQ}AWN}x dk)[>-V{re̺ɠ}erưe+ 07Y`ʵJ* n }V(wFkH:;4DpPn ߽{54|)T}:=z:{֭Ӌ/V FK_)7U'#Aʶ?)s6mKZ)OoNc m縯6RσC)ܡΔrZ0w(7V ?ۂ}9}׽no 0kD?0Q]f]3Ya`B˴^aˬ֮l+ 뤵_ڋ`R θrej{2J6C}2 fVƚAĭWʝ~lӡ<50;dUI Ngj-qױ ŦlkhsSj~B>/6RkU?6"ҎCk^u^]Mh_0rDuu]Šde .Ӯ[{5-[}H3`^|i/`H5 +(9KVUnȉ?%8%x 2v;%95xCyh; mkn#%:eToC۸]">4rPUZۏ>=5 SԾ5ֶ별r 85&4զbHrn̺X޻2 ÖY׽o?G3`;^`6P\o= jJ15$Y)A))x~7Bgmw}s!α瘩X*7ë,ogdq^ʝҷYI<>%C7ʡf&4}r&ˬW=-ӫ1luӹž2+c@?``e fY0WPN]aѩr+gF 61=W(7?5i {WlKK_(!+D6WcuRܶt_V?j8&z^h;7W>V!af 7l[~z{% j W>+ 妵^|.{g?cPn̺:g+2`z_ 0 Z%3Pr(d F  Rn[8\3^Ȝ;۶bssJ dlE4]7`ma߮wymڌt"#|=n^_rPn[ / U}jBÔ՗6rus~m+) rr71=}eB[7 } &ˬV|&+ ,c@(wvګ1luڕs@cK{1 0@x : (9l U9}-z3xq,pqSy[0W}Vcsf \;w|=ݪ[e-﯇y7k﫷qv VAf"L w.ʍnۧALW5 ~7kp n|fPrmAx_sUv[_h;gXS>qy4Be7Q]f]v~rـPn~aˬ5#03 0kʵJ* gPn\ A*O[pn k+XcjoXʍ 4Ws.P9}WȴVm ˶6}lkjfmӶZܵlW6)gȊKr£ͺ;[=^9ZPmoTеss}yK]yabo؄r ͱ-gp4B/W&ˬko ڀPn̺:+2`z_ 0 ƃpL6Pz(7jPhBd3hJq,̀m}? -=0mSnu,x_ۊڡwxd:ٿc`` BVIe ![έ8jEUvb|mۆ^"Vd=w5j@r̺qum_~/ 0 Z%3 \BVU[09v 002-.s{]pl [f]d,10ހN1 0Ņ m@(ǮP?Lh lo@(w,LTY%LVXƀP2Wc2뺵+犁4:iŗb`TBVIe ^ ~?frS+JXπ ښkm]r˴a̺:+1 Z+c2bk``{:@?P`6P\ز1 '^O'Bn ,k˯e!rtb̺^ a@(7:aˬN|' ,kXgW2 0PUR`8B.nnp5 [nm[e`ێ;ms-Ӓ2:W9|0x ưeu K o; 0 1P\oA{@rˮ{~77?'nǯ 0`BހP5X.KX0eukưeukWiuڋ/ 0ry`8B.Cg``LhgR iDuu-8<2ÖY##%0$ώg` ˱rmo7 00m` DuuuVWc@(w?JWưe5ŀ2u~ 0 m@(* 0Pܲ/\nLԗ`/ ' ހP5X.KX0eukưeukWiuڋ/ 0 64 3S5@sja]uByem 0Q d('9-ӧ2ľ1rưeK]ؗc}KR/`g@(7PδTr>~xuV͟gJo`L8/0rDuu]Šde .Ӯ[{5-[}H3`^|i/`H5 +^|E lO0 0ffZ{`` Be2Q]f]8LVXƀP2Wc2뺵+犁4:iŗb`TTu 0kk/^'nf`H3`B3^ 02].K|&+ ,c@(wvګ1luڕs@cK{1 0@a*:5O?Y5xΝSԓ _^^fkmA``\&4˭~ ǀP~jүLTY^ݶo [f]sya`:˴+ڕ`*aǏ 2 0 H+-Ӧ2y~ /aˬs2/:^ 0 πPUR`8BP3`09v 002-.s{]pl [f]d,10ހN1 0Ņ m@( 0Lh lo@(w,LTY%LVXƀP2Wc2뺵+犁4:iŗb`TBVIe ^ `=0w~K1 [eeֵrtn [f]pNrdX<;`r4P\/FO:?] 0 lkϿg0 [ey[]؏*_ÖY^0پ0 0P\2@qr˾p1Q_`4`BS'``{B`~`̺.ag2ri׭ÖY׭]>W 0Ik/ 0 (.^0Pj) 0Lh lo@(w,LTY%LVXƀP2Wc2뺵+犁4:iŗb`TBVIe ^ `=0w~K1 [eeֵrtn [f]pNrdX<;`r4P\/FO:?] 0 lkϿg0 [ey[]؏*_ÖY^0پ0 0P\2@qr˾p1Q_`4`Bs>۷oOxÇ7={vz8>|8y溍?~|ݾ޽{w6fl7U2χ&ˬkڸvV׸V07X0\}/3]6~{O>F(w\wưe5WosWb<[a1|~cƖ6c8LnPɊrWڊ`09_ -ahF 0{7 ; md@(#yw3 Мn 0em2.-BXMh$f"+;M iCeuk&ˬ֮~mjV뾇0۷G=f|pָPvKM޽{79\oiP\rk-ݚ.[kq-q|Xa˗ajω߿_- Fs8y6-Ykv`f@(* 0PaJ 0@Lh1S~/4`g̬>gt˵Prm[euKSSܥabީNJC&B{n5](1lu]ܰĶ V׋X/밿ݱiX; 0 SaNکdB|۱ 04`B3D9Tn/^8dbrᗘ@!ص ߯^Bd.s3>i/euk&ˬ֮nǫ9ׇ*wY}hE(wX;=l>c2뺕ۍ1c{6Sujb`༁x 3P. 0 ɀ ޗ9ʶю4 ~>G:XrPjMTY=͛7EYT"mS3r<ÖY=^_cnu}6h8me؋oPSf`?rS+J`n6Pɚ14,Q#7I(7ϺLu`̺Nu߿5(X%UkBj-JLZ6!0_u_d1m̿sVr˼o&㧅e>G5^d_fѾq 6z{us2>ưǾM=/~cXpϨ_Rrvi#m 022`૮^v ^ `=xu6a\¯+&@mJWO k;=.` x  0XiE{yp5US`R d clnޗ/_r &!V3 0ϟ?m+t,J])7+6{Vwv9v ニ/-y}Vi3Pּu/"BN+OTd駟\c 2u zMNcC vtg`lB=aK7sߗ撟+,g 0 h)u"u??L`n,:5ox_b:&w@.c۷oy}͵9```MBB 0pm VoM>|ɓ]>ʽsm'8y-7 0 0ptY.JIKh[w 0~ |ӳgϮ]ݺu+k\K=P~mq1YX&n9O2 0 d trsxRC]t`@|1j{8꼅/t^cH504?RXuwmc0>khzX g=}``u {C(Oss3`` n[LշYzr6~ MƷcc nC~״X/K_>/o={X@b~N+! 0 0@y:ASl-hcm 0Wr->G}# 0[yδk"$e.Rm7soؗ;G26_z^uk:ư 0 0P\\(ǏOnJCxȮ0{èj%_u#Fc21&/^vXܶ{-uxӧ⯱[_7Ư j0s n`ӧO^ 0 0UT.y >[Ga/_f̍P/xub`u >_fUh//N1snڙ4kpUh(-WK|?]^^~,3dܴ~꼦` @MTҋ9"^Sz=3 0 0 ZA5+CD0r_KR/`i Bj۽{Vq}UXuc lmk-BCGX'6 c9ҾXX<αKo}Q[ 0 0j0^jCx 0 0 a +w޽Xf@}Ѫnvb+V{U֚ԌFx%Vٳ/•їo' lg`h7+"@!wx֮ڡѸZ^|~?LSg[=w–m[X}qMksm 0 a@(* 0 0 0EL}jJ"P_=wա,Ճׯ_ʍ:VaOpX)_( 0 0 Z````bԌ0쐟^2{rS^xH%Qso"]_ӧ\V:Q1m/V=c{a`]׸L =P{[GyqF9\1Pq_skmz 0 0 e`7O=Փ``R W+ׯ_b%֡m?WWW,C )E:VI/mׂASMy?C g@wZ[Wb_cݏ?N}Hk=CK[Vϝ֟`1208?, 0 0?B 0 0 0x  "<})B?؍_燗<>;B@?Ѷs!X Dxirkxjf%k@\KXq-_pnBIZKu_@6u3|P}}E.+[=wZK1sܮmq 0 0@^x#/ 0 0 0DO>_E={vc/V5^ӿm92 27f 00x§}6e`؋'Se``1j68jO>zf޽{ǡ`gg ހQ rVǏbqWF(7[Ümp 0 i  0 0xݻ7!ȇb./9׸Vlu mߖiۘcm@ݻw۷oGfVM՘`4n ```V5>x&V4Ok+V4u*GWWW:WzǗm+˘@ X2BƐƐƐ 0 05-v 0 0 3?xqqqƽs˗&K}6G} Pssvn lg@w^݀s޻aGro߾}/|}c`` d;8@ ````>}:=y&Sr)gώV?A.kl6H5 Lgfr2ĉp TKj``ʵ" 0 0 0"b>·nݺ >}TE-P+BWWW7up-m3b@/^\\|Kg!K\@v Ծ\w```-n 8ߟ^xqkxlE`(@c޹s&+FpH˯}5rsLƟXG\a\7:/? 0nÇ _O``Î#ɓ'O[_ַ]70 ƵԑS X=ǜ<g,qo_S? 0 0g``6z`0&bEF8y=n޿*><f]]yծ 0r3liC̍_YXz[>g``7`; 0 0 0pp1[nݬS?}p,իW?j^VU[z. @~hX D7¸1A"a]xm,jc X=1n- |zH׈XҢm 0 c$Jl@Z;kg``xݻ7Ab)7vOpIl<uޝb V-:{{o߾XX``p \x0u0 0 00wN1)]m8 ZׯXɓ'X nmls3 dn& oH?RxK;2 0 0P |``?J ޹sKAƄ dh B%Ϟ=Y3\Kf`;Vݮ t2ʍϟ=p,ίs 0 0\Lg`` 6aXiZ7&bu@,`,g)&m=ztch\.۶˶/ڗҀs } įo,.7wǹ`X߀w7 0 0 0@b:·nݺ =}(\O1͛Sn]NeZ68cP+^ʒ9MbLv|6 0 0 lk{:նJk`` xǏ&|<رXٳg7AX:w 80{1`\Vb~i5~ %B1 FeXUW``0`Bnr:N 0 0uݻwoV|𡟺73/@=zt߿zpnau>4Aj`q},9o|2pLV=fwu{so߾53̆``P [haPھ 0 0 g y< O`hkm?~|a{e-26`\}A_``M^^^``(ԀZ5a 0 0= ޹sK:y1?wnݺ ԫ+瀃k6ԑfڏ?@xei6Fڈ```oLԙa``vf SO< DNLx(wqNcXA[Ӿ%.%vL\3Ӝ|OM1f`n |0 0 0P;| j 0 0C p}U̘y73g@LVq~n/cnu_ՕXπskkQ ij]^^{ 0 0PE-G:Ng``dŋ;wܬa?13g b֭[gq=FtXf X=ǜ<ڗ}{x-~XmߵՏ``̀;w 0 0 0@޼ys{MÇX pǏoF-ˌnYtNWO``_z_ꕣWNѧ}`f{ƓpOí 0 0 {/u1w} Ĺ썕=zd> 3RzQO_ވ5Ê99m ˀsUo[4qyyYhsZY- 0 0@yr3 0 00Us˗&h2#=9BM5-nݺm<- Y#'#{ 04Vϝ~i?1>qGxg` |7 0 0 d` ~ɓ'_v(4D%ߺ<0UXmO[ym9H3 ,a\-gU2ߪ+ 0 0p\&3|s}yW^Y5`0+ӧOO~0O1ֻ9'|Jz.[Oeۗ_ 0rKxς˓O]B-``0Ԣx r[f`(/Nwܹ ]\\>~8XU 9(~*5{ŀgϞԙ W)^q c#ڱ4ŸϞ4+͉qc`g`I0C7jŶ| 0 L3͛Sl|uS]~ 1^|:-J[1 0p~@{co߾5ʕ``(ĀBRHx D0mm1 0p$޽;=x>+&^#ǚO1&U˚ kUa؛27wϟ?~Ntyyie^v`` YH! ]@=pp``"vqqJ/_to5-|؍0j,ٳb>F `€s9p.8xf}ٳ"ϊ<{e`("PDԗ} ?~Ep8gn0 0@O>}1_~2~{yTF}ׯ_=og``VhG>c◔"ۢ]lϦ 0 00΀ u!nnn̲:~ 0 k &R#|{֭P["B{ko/f>xy%Q76Kg 0zno&@w_sVٳׯO1nMkw`7`kQ_޾}k|䙭g 0 0;5p;-ukuTG`` O޽;ϯW{"^yK]ԅ D7ΙWWWǏ|YVsejh{`V o1qTZ]+Q``.BB 0 0 0@xqqq(s˗&A}d`hnp?9FpӧOΫ> 0z d! |ztS(0 0 #}E:2 0 0ؓ'OnBc1sK ׯO5+>+V{n5ڶMj_`j깬 uX~۷MK:`Xƀ87 0 0 0pX1*(aOZQ8le/s_F~޽ӻwL2;:2 0@F{1Ac_|1Q]^^/et~SBA 0 ,j V_|M+&1?n"Ekz=/^t[n9o;g;g3 0@k q^'&հf`jÌf.ȱ"]QW^ :1 00mBPtguYp~ݓIhxf(рs.tuuu=.іa6 0 0ifWh/uJn3!G9g7+,ֿ tqq{n>|xzק>f26B߿O>zP>bm`ǀsiGǏCk"1d ``a ˉ)xGiA3?'=37`8g͛7XI߈[r0սE 9I8*P6!ood``k'S\z# 0 01 k X@GTX 0@޽{w&'޽{溿K_b|8V[/vV_Uѝik) 0Kz.WK:g^]]]b+``J0Jr :< ϯvV|wa``/Y{ilO1f@7+տc̸&>czjsm 01 X=uս52 n 0 0k4ڞ 0 0 |鋕cB"cR. y9{)V˭V})V_[ÂmeA=ԃ`z~k߭[ꋉo߾5Ё| 0 0 (d`V{ko`` ?1Q[n>}zzW/5Q݇6ĵ FH׮ uy 0;4`}܃}\^^Wwy`o 7 0 0 0/^|)~@p@Pi;~1 Ço¹qڪ2 0p<V=^s/FbJ1 0 lm uP``3͛ݻwY9j]M/wN޽{7jUX_z%๓/*ZnUè_:Jp``w:o߾5_0``rݴ3 0 0@V"0WM4dC"kz܁ګXU7~Eٳg7n>```{Vݾ5V= 0 09YM + 0 05ÇA/_h8@N?n=5%V;V͍/{Xm99>'`1`mڝǏ۷o>yer 0 0PcW Ԁ`XONO< NM02i@wss)׿Xi=V˭¹qJ9>ǡ/0 0Q X=#گ~Y۷4xn 0 0ɸ8GL:fQ`` ޺u&,St98V>XEٳg7כعsa``?ZWjuuuu ҳ3sr 0 0Pc`:m` 0 0/^|r)~Oڨp9[هN="H2%鳙f`3`ښm:E(7h_>Tmݵ; 0 0@\\``X@ݻwoQ>o5}dCuۛ_;~~-UΣ> 0eznY?g|01.3 0 0&````q& b޽{(9PTuۀ.SsyOnݺYi*Vq: 0 0Ucz=0B9=6d`XK`@iLƟ`7狋qܹsz hp? Ak:u}ɂO𾡭 0@u c ۷oˆ%c7``J3`"D a &aDy|Ỵ㆚cM7& 3& Q8u=NKӯ{5|)eӇ``WF㘽``J4 $Exi5+xvLnH`r6[ӧOsqϙs[cp]p6#S> %[mw 0 0lL8%]^^x 0 0@%â.3@ծeݻwʍ^vW}` x_ ??NyI=cX'V~g EVsjd DXI.E@ct,lip.? +Pŵ֭[wQKkس `րsmϷKbryyY~`` (Hf14g`ֆ j?`>>|gܹs3b`]7ew["ʮ> 0 0P7`\r;'4ʍ/ ?L2 0  2 0 0 HzxCϟ{ ?G|/}p-mX1߿s-|_iqtd`zWNc|a˜\``?M!h`` Ov ӧ]9M/^^*q}%F'``x{ϯ$9?΅ 0 0 Ag``N:sMŠ8gh@,5\Aݻ'+T[/}Im```)Vek)[}gvϟ? 6y`8n```W޼yspQ2ÇOl|zV|kek¼b aO=ztk+ 0 0p@V525Xٛӕ`f>iu``d*g=Xt޽StK>fǦOoa Fpy£m.BU[n={f*<{e`z<*xuuu=/jFڈ``` alI`؏Xfe;w\©Z[+|kߨ"|a*`S_nlng>)`eǏϟ?{g_`` A60 0xsG-ZS U>|x/LLe`ط~9x޾}k1yNo 0 0 huc 0 0 DPo絫>}zvjMpiw޵ zVgϞc 0 00ߦGvzuuu=渼48s#wΥ 0 0 7 0 0 /]\\r/ rCO/T~u`_> 0^7>lay? 0 0`1 gL&^s`޼ys{M((g4ԶVՆeST\ |*Þy> 0 \z˔˃o߾5pNq]a`؀l\)+58g`]LÍ6u^{3޽{w޽{jf嵙ny5O4wJn[schm-```V]K|uuu=Ƹ40/ 0 0ظ %R5iZ˗/=p.u=e4+]\\|sٮ[J_;6\{F2A[n]_#+cKYS/l9`_z~1_|zlqmWW0 0 ll@6.Ёt3 m Ʒg0ɓ'7׭xsj' h=zts͎} 0 0{v]R۷o)< 0 0;2Çyw;<O3?K-ϟ??ݺugh_xarݵ; 0 0kc۷ob<1gcZRwug`i 0 0\be'O܄qzt<\gl.cK}.Kl@ԿsS6p< 0 00΀sǵ۞Udzj 0MZ``26'bOkV`qqzX2ƍpp1~޽/<~={ 0 0`V~qR(aa1m 0{2&̍8 0 0sM&VȋӀþ;@-2X%?&cgi-ﺩ0 0@N[x~XVٵb_Xc`~1 iR'*``޼ys{MÇ@2q\[W^]\bL4ϩ*,(n,ONRnu.t{/ݽc` X=w6i&~m#e:SWue`  2 0 0o rK⍁nq :V֏6o``R X=,/k1F/O8ڲz2 0$lvg` |*LsΝ˗/=0wxM90s}y Ĥ۷'nݺuS-ڑ`f7AFh2 0]|N 0 0Wݓ'On¸ybN{>.;O;` gqo+aU_t\-9o|2 0]S"לc~/b}Hԏ`H7 k-` CȊUO 6f 0Ј{~>ݻw]vҷ}\>#hD_3~dΟe9``dqWH``  0Im``~y=Н}FFpˮ 0LIbÇI2{& s.b`9 X=7_GWWWס{ n=S 0 0-4 df b4WʍUx03}06j@y"rqqqsNU'պZQS\.p38b!^|yw@={SC5<2 04`ܼ{۷oO <̫ι 0 c@/0ΑOP @< o,ho[ 0@O>}q>)V іimYj{ rPm61%/FHJV 0 0ViǏ?5Ioz)k o 0 0`|?{,G^``b!·՗*!vt!]8Kk+־4ˎe4שj>]>|xPw_`-G`H3`ܴW"?WИm{^sƱv 0 0G 0 0poڪ 챽s{]Jruuuz@sp6\ ~{XA+U#\^q-cyX3 0\*;犸]޹\]hcm 0 k ```, ĊoO'<߽{fXi.B5jhok+4VzԵj? Υh>q 0@PGkqB||y|֖ڒ`؇,: 0 ,i ~.9޿?ss#dvmϽg7!,k?~=E`b6ߋUkvֵc` V=_Pn|Z 0 0@B=ԃ`6Ajgp7 y}ާONO< ><`J.muۣ[- i Ǟ={vsh^-~۷Oqfؖ 0 ,g깧◺W"r -Tm 0 ߀P``63߿nLDpBj c D?O[M?xYo{n}cǏWݟ5ݶ j 0 oƽ秿9_u=poo߾um^ay\3 0fe`` k]c%-ï dsTrR 8[n5…mr L7+փem_ /1ݒ6Ԇ 0 0j(c?Sk= 0 0Q l8J;N'``@rӧ?mMb%8u̳/GP?/V5w zWWWG4Ϛ0ykhn߾}zV }k0y;Ua`l/ſ}n\uɿ{s}> 0 L3 ke<`` p8Zn5#[m;Vڴ܃5ܼ1w}}22+c=z)"D b%g˧z 00P_z1:3 0 00 W(``nrf8{}~@9.//+bu%kAz#zqm~mđ6b۷oO1)_>o/U ʁC߰2 0RVύER~} 0k)1x=}Խ$ 0 0g h8zF3iGm";׺V_x))|oH8n)s ͹j !ZA߱gϞ%#f`J6s~zg%ql΋ 0 0S 07;0>l}GR\0G~+VנAb[=PJ[1 d۷_p/@E %ԐAw_[(Ny`b`/B[>bq:O2 0 1hby 0⧅j"o{mlիW*Jk~y,¹}޻w޼Vm^3 0y۷Cl{:`8W c[r-r✨ 0 0xBVch ^ B{/`?$ G4Yt0 0@.Z=7inR/ 0 0#=f`X@e#l߯÷mE8J1\\M`|qvyyysm}m{W^ ,6q*j 箱\g>}}j``H'a`X@p,z{s߾NS%L ޽{V>ބ~ 0kÃn?;޹s{gl9` znܫ um|sW``rks``U0ǃޡp> @Lw|5S3`1o޼Y>x!_`2j 0 0@%``f7!qz__beޣ :pd͡``Uxë`csժ9?~<3X]՟`=1f'^``Pz\{}p#wj 0^xZݻ2s; 0 02ӧ޺u2}DjW`8\+1 0 j ~x?{\ i) 0}"pU>}Z}c_{}`Xs?}pݻ>\¬s O뜟vf``}n83 0 fכ=H~. ?2 0 crXь|Z0 0@IůM ^\\60\BI}ޱ1 0@Ӏ&7 0 0bCgPX׀̠`~1Mq? ```έfc~qbGԎ`X, E[h[{3 0@>|[ne͛7he{)V׽{s}ra` D0vl ```6mqOg|>K``Hg iWWWxP#d6 n{1ŋMXmm6Wpg1 @ m+dv 0 0 \o {g``4ҀF2`Vԋ< ÙJZS<02sgp9`>}tsNAz>9 0 0gϞeq/Lǯ{`c`Ƙ`sN7{f ?;\x]7 0xIV!5e.H``` ]'ٵ_2>2 0 `@0`L (+7!B>uǽGa?U_C! 0x}V1v})V'?jF 0 0wo޼~xH޽{ {d]`` g1n`f7MǏ{hr*}߾, {5H+:>`r5ӯ,``xT/"y/ǘZ1 0bX۷oOsF$ݻwOsj0Gگs.s1 00WW_~m"H??6d(@ׯ 4}{e``Pc6c~5}GA.//TwwzO=Ԏ@qw9Gdskb``޹s'{?ca^;1 0"غEkKs`4]ws-~_l7`(I ``z ăXaΠu뻄c` Gl܏q76%K2 0 _]/Wx>3 0 j` "_: 0 00p/<46yb 0xnm+706Ӗ 0 0pT{__OدHG}`kc}:: 0 lajaXӧO{}```#?ݻw7M O/^/͟VuGkC@{^}O}'ׯo./_ 0w򌸬g꩞ 0PܔԱ8@"Sm 0p$Ϟ=l|IUُk 0Se=; 02_?B1uﯞ1 4ӪsyuwnÛ ڂ ݿw'bU?'tf(Vr`h;C^/a޽{K[ JCQ۷o;o隻Dڕq/}1c87==qAį^k,=ݴ[n?gYjK?YoO$?_?"mnkym eq~'TM`B=)cpb=*{cw<8@㧟~:yɓ~s<ŌkLmfjZ(,?U(7&SƸlNX<7)Ȁ2kBeu=\sh_x iX̺:w=JzNZƗm_=a0ـP.9o|2@0A]NS3OBnH 6￿~h?]߿ukSe^rˬkJnrQgYf@(L:euo;MP|&R= # 0MP9!2n@(W7Pnx~STۓ'ON[=+ؾIg`-woBe5ww!r9k8YۀPnuʬhnBLm15\sXb  2u_"sٚb`Pnsb>~Tmcrό0v^ Ϋs2:ǖrҟme@(L:eչ\]n@(-2@)6q8)20ހPN1B͚}ӃYEBwy 2`zT2뺥)fj.B,e42=Y9bd Bm6b@(!NҀ &dNd 7 ;v ,g`Pn?>y޻wo[N,gEj5 .ӛPnu]`[ -e@(l\-ӏNu׽ rCa\.`  2u͂;-BMk?~<|S?93߿Z=ݻwt.\=0QykPnuҔm35\sXӀPnuʬ}g1\pg 1 '^  lit!["m'1ʝ~i?1{(Y?bǧj߫rbD2BeukWr9ÑhnBe2)sr[ۜ17ԀP.+Cx+ 0ӅLNovn@(wzr BM߿?]]]̭[N.';Ѧtm&4'[f]>?GKj W>r4dSf]wbkB nm2 ˆ 0\\``cBnrY4Pnmrr.Orˬs`@(; W:e9X]n@(-2@)6q8)20ހPN1B,Dj۵ .ӜPnu]`{-a@(%\L-ӐNu߽ 2AgˀP. 0@rr` YfӨP1뮿{LTiT(̺~> 1 '^r4gSf]>?GsejnS> 4% ,e@o0RN ǀP~j_Ց ~$u?MTV)J(̺Zr5 f6ױm Yc2|{7 r [[Vm`BB 0rTrS8ʲ,[V=O,ŀ2- YR;>r]_s5 [Mc2y~6ԀP.+Cx+kenms 8Z0(πPny5OմB1Duy5~*[f]յB1X(F?5)wB ݰ/װPnoՖJ1 + ll@(ME)7,BeSTR .ӲPnu-8S(?WBe4)GoC 2Ԋױ\6g{1@axz WSTMK0 q CyMTWBe9X]K0 q Cyr˫iSc2{7 r [[Vm`BB 0rTrS8ʲ,[V=O,ŀ2- YR;>r]_s5 [Mc2y~6ԀP.+Cx+kenms 8Z0(πPny5OմB1Duy5~*[f]յB1X(F?5)wB ݰ/װPnoՖJ1 + ll@(ME)7,BeSTR .ӲPnu-8S(?WBe4)GoC 2Ԋױ\6g{1@axz WSTMK0 q CyMTWBe9X]K0 q Cyr˫iSc2{7 r [[Vm`BB 0rTrS8ʲ,[V=O,ŀ2- YR;>r]_s5 [Mc2y~6ԀP.+Cx+kenms 8Z0(πPny5OմB1Duy5~*[f]յB1X(F?5)wB ݰ/װPnoՖJ1 + ll@(ME)7,BeSTR .ӲPnu-8S(?WBe4)GoC 2Ԋױ\6g{1@axz WSTMK0 q CyMTWBe9X]K0 q Cyr˫iSc2{7 r [[Vm`BB 0rTrS8ʲ,[V=O,ŀ2- YR;>r]_s5 [Mc2y~6ԀP.+Cx+kenms 8Z0(πPny5OմB1Duy5~*[f]յB1X(F?5)wB ݰ/װPnoՖJ1 + ll@(ME)7,BeSTR .ӲPnu-8S(?WBe4)GoC 2Ԋױ\6g{1@axz WSTMK0 Ҏ???ß6͵AmGGBe5'q_wr7ܼSBqOBe1)cRqO[B܌qz{Fr[{^``/rM3uӰy,BǪ7ͩU$k;=erˬ>{_]s~ߺ z"yL|%[c2:>|5%X;C_hǿ r7ďP.'Cx ' 0AAA`tBc0nD C}^C_{.>sRwmz5lBeus1B\__I%ggY(.O-ӛNu]ל{bZegjۡ9^so]-õPnuՑJ6 ke``cB_h|7_|36&㿕|1vln6s4 9U ?S~Ps9{{)t(*\i ٨D}5N>dr9;\aL Kڐ"K:}d;=0Z[?nΘ,"ʅ9vtڣ:ګtra+S ʅ `f1^e14"0Ei.M-Jp$(͗D^Nr]M|KQn}փ$Ts]뭾P{|$o@_n}(E:1חX~JLtK,A?K:WsÇ޷,>cӎ_5vus=ucuԦ1&nhvة1_yn,Dm9_;\߄; r @eVlk@Wc8.Qrn᱅Ϝv3szffvڊQ';r%MJ{*Z9v |Qn^Hmؖ1q|lfl-D}\(1S:},ENØr3ra|]Bcc[זNO.+<+0@WYW|F0(؉Z\*=?DqrKuDm>Dێ_E\ڬ[:G@_5DmZ6X;OrdN;~]pӧֿoR{ĘCG;5֦Î]<\"Ec E 0RI&|:cbx.:tlzM;!_ֶ/6VTMcQar"lTvVTSuK/Qn1'eLk5ޏ(Oc} vџ|Qn; ՘cmbr+D5s|` ƫ,Ƴ:0DGwZ;ZM(3' h//hB+~S?-mNu|]SBv{d^+:Br+Ǘ[?Fu mǯ6<}MJaQn;|mݧ|Qn*-irnbL9ugsvzhMCߒ~gnfMmcv'!mZu/^nsWIA ?$Op 00eQ.\@Q.Ieu;m3Y`(w}GQn~?p9'-\j-( JqR=lTvzh\ki[# @hQnZǨ^׿!9:КFmisRFJG;5KSn(J-%އ(~Jp;``MU\Mm0К(wʕN]׵b$:u_Dm(/6wtlTvZs۶ @h/L( ~jr 2&n/L(Ovך\o˜>^+viq(jcQ.,ᇴ0r @eZ媣ҩclsWome*D*r;6j;1lTDC?rS:jd4_O~wN߭-}NzKAyZ\}'Kry5c4rӡf۱ElT6YyDێ_[B{h*YǪrcXX{czyHԥڦxwtrrh2iǯr_Uuqk:(7Nxjq(z; [ѭ[~ŷ`ua`2DMIG?}OF9i?񏿖UCʯw|+=O|z #H+ʟ3Zhwe[+oC1{^qind [:|z86& DsY!-Lm۷ٮQQn;~ɷd(W=R_87fX}xuȷ[>FkqR{96`rی(Muk-QnjӺƢGXW}yB܆[qXk~hBsr%e9{Q~^ia`6*ji`>rEbaoKZ\`9uj\sgxt.[hQ ;ǞӢ10آƝ;IlatC1w=Ks֋jr9mD-QӒ\mIi;T"zlT寜vL mǯ[GCsC kN3+w[x|]B[ަkpZKiL|8Y hTvN;~]ko%k)El'Dl6Kڗ6e%ܱ]30[3(Q. Tf7Q:c9§I#h8s{y:$@ 3WE/["D$۟z9&΍\q 1w7k ;11]~DFsoNu6&+LjrkMs({|ɸZx~YB~[7(W~sCKG6&+uȢֺeےZ(\Z^ =0*r4ߡQ (ʕPІPA5)W`ޑ~:c߷\qPt)?ız@A]*O9ߞz96Y-rS_,ʝk ǧm0Fu~9rkMs(8rg uu Qv[r7(`ztԘ80[ 6&+u먢z6܊Z\pZ> %]006r @ez ^pաcb&%;q++,BS*Mt 4gXJ(|YX|(S\C6,<_ʓ)QRn1-o"mc"2(T㘘@N !瞍>;rkM%c%c}|ƽ?xyi/DrRKki]Ҽ8Wi94{D}iל5Vsi}7g V-1&.>gNB]|}Irlm;Fّz;Q/%@WYcy/ @\%~yb!.<'M8V:Yv.ZyeqpxuONEʒ+;<72E%[-nkrGFk?\W r5wlTۨEZ|KƣK6Uݞ< ז[-^(iDQy掹"c5^y@FdeN;~Qa]ZxLB[Ǝ[XkdνT|>רrLN(\D00PDKo_V .w6UDŽ9)%"Sd]K|rl"A顨<Aܱw劝mNgF6>T?_١mQnEBk==vX߳^ب۬Z|KƣK6U|{ZJ:S>(=$rS==C=ʂ(sv:(7º>|(-|}ɜ{(<{1o}DN ``TU х2Ы(׻d;'\":QaPd-νMeIhs6:fۥ[dD=$J0(~іoŒSck~iAmp>m٨ng9cD&lΘkxԻAx~,~̲(woO35&>4k[~ mm\z&砲w-E,zkKaW<^!]ní85_/w<|.Q./ E 0@(wƹ:56 HO5k]=/Au۱ȴsm.Fe?&@轹srۘl&--@8&k7,ug%63 rkM%Q(i$Qc7pm>_DmMVבDŭu%De&9Q.v\yDp&_`J0y Hܜh56С<-BZ8%b%\O}O.f_N42{uɢ\-K{<%sFN(wn 獊r(:q+&3566k3DuM1wwzܡ3'g <"m}Ii4ȡqʅ(sv(7pw-ZtNa]Zxl]B[֞kYks5fp&r`Mx7| `Q.\hܹΫƆ\EfԂg]4X:W/w@TX*;%O,Y4Tb$g<'O9)ƱK"=C%cߔ+6XOQݖr=(7)A1d<>gh>| Qn[vLm9HYcL|M[6)-6XOu Qn[mCmuCjs{sϭSc5x5_/KDr ;|n(bbN406r-NZc}.OkD?FI}Lpyl/wu|s_>YZ*!_-'jE9;6ZC 1Qt7aT==zFf-p(nqhhxԻAFcs]GB]1rh֞gcƘͥccs# ʌ( svYp%Ac%;-D%(w1q>Ζ>[ﭏri`_ \D00PDsHu56O ֺ(TtٹArs"?sB%F9yRԵtҔSN1AIƵk埍긾[=([2 ʍ[ʹOIՁQn\,3iǯr#/S-S喱<֚{KDrM|n(bbN406=r-\Uc=(}FYF,I(wN4kɿgiɢQnެSNѷ }}idS36׳mMnWX[Kƣޱޖ|%l;֩Grױki^3&Phi&z6&+uu±緎(7gߠy!ʵ٫&_9w+ܚ>ۧv'XW02(Q. TfGQ 1sn w(WkߩDyYh4s:ql5fIDATs"{nC QԞK?W?{Fu=ۯ=vZk3;59th:utQn11#ʍiSkdŻ|.㢜tr  ƫ,ƫ|M1Q{:Ŝ QnT;0&\ڛ.Y4DLJ$ԽSiK s(7F1/Do fl>Fum\urkM>o Qa῵WD9Y-UG\.r]T=(o5N\Oj)#R. +|0pDmb@<zD56glݥ<.֢\b̒E#;$Խ\ x!j%ߴW?q} mǯ6Gc%\RH> ʍcc$QMmi\?!˟ug1~znXe>ȭE2_o\~=)<ܐn`dQ.\@oCB#oɿusk smcS,&e>,>N7*r&g-voyDҺU"=7v%ڏ6<#mǯ6GcZeݺQn\+u ^K Qnfӎ_Gn.^eےZsX[)) OֺQ_ 0@!aClI{Li ><ͽ;{F!aj 1ee/Y4_ze?TZIrGC: b<߇Fu;UZ|KƣޱޡÆe[zp?(;ؓgs(7WMϥ-C'e-m_C?&޶pr%FVE5:TvQn[}hcA'xY'][̳QݖrvZkox;;&|+qCnuQn}ԃDǡyc9uQn ?uw r)35"FX_j7/[roKj׽sn+ekDp&_`J0(Q. TfQ)A5ZK bk'>QD?os-.{L#^h4g\!n͍U"1M{L؎@ ^=m4lTkD0]5G8V^l.cǞCۆG..Q'C\Z'!=vdɚt֭;r׵oI~kחxEv,]ra`-x/l b1^e1^)GhEP2yjmաccx;~1Z*O-,#ZD7F'MZhd 'O m_M{lUu?A{6~MZ|3׬7{z(w;/y4Q`g//鷫/r\3iǯr#oY{"vڦZ%+KDasvQ. -R E 0hQ!T >ױ I-sd,y,Ή}#M>i{s7'Jn Q).\[~QnEB9m鱺G}BQ?޾Qn;~Wsp`ܧ?iXMKܰ?o=6ڧDRjtӎjvi-6^mguh-^(|(Wuz;|F"k0;a `4*J;H@{ DjES?Mn;&Ի Ts}*߹J='Om!*26ΎA!!u(W<6 -ޛ e0dh>'jWr|~N:Dm'#r:8-ϩG;Sߤq,D܍>>R?6(WMrt8-\4$z6Y9bӎ_Gk5Fj]ZxLB[Ǝ[XkdeIܴOtlz;z}et0Dra`2D&"<pĔ JwJԜ!9atC:MS6%G[%F9zTTr<̥CƄeTQ)BCm|dm\|^6!vZktJy\h"|>>U;'BMZMk|>xbKΩ#{R|rhfӎ_Gs=e˺^z(w |(We%1^!C0k3(Q. Tf`KQ1g,mH{ekNa='Zh(2[Khfea%Wئ/:9s|w%F3@QnE9ufY}I@ڼ:FuZ Qn;~ePp\KhEF4(WuRsRՖ̭GkjQnm:udQ.n[d-gu%$m~}Co\՚/i_ٜz;.3rRVH+0@(`*3(W&Y;Am$i⬟USZg+U`0{|)Ok3}w͵P9Y=ߙ~7J;DEmʴ̍Re#wS{:kiĸҤ%lq-V;ڐumFKFu;mMDmUmvv9B O[uC}^ɾRxvYB~[:Ɨֱ|Fڟccb[jia|)660iǯjgk9}ZZ럵Ž$]^Cg(<՚/i_Oٝz[z}4p0xx` /4N0P\|]_fP-"k䰈(^s88ٚ6dQn~ݺ}{prj x'\-eQn 1ӯK;6ra6|1(6h`` :r @e2X>X 0zUl(wG-y-3Fu"ӯ-5&ra"2(O.׈my5 r ˖ ʅ-y[0a1^e1i@_ ˟Oˀ"Uk?(fØ6Qn~qB4d4&LD}r\On@ í3LeQn[^@(`*3(AE/ ʑ'?ɝEGVn8clj0Fu!ӯ5 K@ S}0UD}r\O6j2(jǷr6`3 TfQ.Wя~ rHD-(%eب_D}嶆dbQ.,@D%s> !OfaQ.XxYxْD%o| `< ƫ,84TvD}?s8%W_NڭA [w`}(OZ6@TfT6l"u+5~mD0:_[-Dra`2rT2y,(Ɇrg}r(O&QEۿE-s>bCaq\xM ʅͨl/؄H ƫ,ƣ2RaX |>('>(qcsبDۧ_@ =pLQn>U=eӧ_ik ʅ&2(_Ro- @/ ? TfQ.^cO~򓻯ʯ|M%r gmr YD06c< :}SI @ x$/8eQ.<& @tUEш ]p0>%c\da2`>Qn~2"DZ -:}uvV ʅX;feQ.Xyؚ6~36~v ߣ!@;c{"*j?p\qJܱYQ`O6ר7 r V 5:}uvZ ʅ⽰D0! E 0@Kgvg1_})M ʍ u 6r+m~Dpǔ?SS:}6\na/Èr- bb^@40gQvp``=g[Ŷ0gj"s(OFf\.ra%-@'oum߂5@ Wkp;rG`dQ.\\:5;z _^ŽΚ Q'_rm@ k[w`>yaӧ_-uga "r2" .\8- ƫ,Ƌ@ÀDv6`z}Qn~߄ ʅL>*>9bӧ_Ky5@ 5wDA0@(`*3(BQDcߣ3Fu"ӯw9 ʅNxNfQn1ӯ[|J3(J3`raK`b1^e1Z40r _\xwlT+KBۧ_- , DeQ.lFe|&>\O^@ í3LeQn[^@(`*3(AE/ ˈr'}(OPD;?*rdN~ڎ/xeQ.s5rank X@WYguSa?S)>Dpǔ?٨ϧr+m0~Dpǔ?SS:}6\na/Èr- \D00PD *zTPXFۗ?بeD}vr'ܱOQ@'ukv|[.ra%@ s[3``bbU=Eۧ_ik ʅ8 q(?2ӯup ~Fۯo`"ʅ ePˠr2|N"ٳg?X `iy>}2̒/بA{گ/_|ՖJѣyn^\\d{S=M}l#;_CD};KǸzeh<>asگ=yWc}և%mdNZÚWMW+'h =]}}{^Mh26}!>\'yODQ}.&ߗ~~vč!-&;曻c6?wiz#lT/!}ݯJ޻woVj#^s~pvBV;97NzNo5ftڵ=wl N"Y>%թSm.MCk#mb_5vXwn1ݻwz_5&VyɾYQ?@:w'vYQ_=R6D) /< /05@(`*3(@߄9~L?tl] C#[$2բ$vaC^g/Qg(~ƍvBS]˜X{t͍Z$іw #{QnZ[uۆrD''u>W4̝x+v{׫3QgۦcFlYuu xuvt{"sL<:הa/UQPz@ 7nH7k3(39BG.agrm(zlb2-k19+}rwopCNvDD:0=&F)VBOx,'.GڈrwJϥ4W4g.zcҾ}rlXOIΕ(֫J^cgD XϾ7~(.Q.t}00PDra`2rKwJ00(rᮓ= ]J@66z0\hI"7ب],[bLLm!jWD>t0p9rwJԵ hGMBg4\GqKa4#jnTu=oS J0Ӻ^n?9(w8]i]X4r_(w>׶r2 bb<*W \J.+<+[20(Wk,ܧ^v!RDr6ITzuj?ECڻ7Ȣ\1f;!Qז\[I|ӇxA1?'M^2;Z*RX bQn\<׹wjcVÌZ,|,egkku:R2- Q.uN# E 0@;vG@ Ge`DQ6#LYlrd78m%m9b|FԴݼys(QUZOޱ 7 sii$'X]D׎q᫩0|36rc[Fl5ֻj tkU;(^}m6nG;(zZN]؃e1^e1^xJ 2(_Rom (B̒|]yj+@s;%ZH]3e*BvyԍjZJ3(WUVdV[mNⶇhm$\tXzyJ.<7rsGmENY-^:E#r`u=zKj\Cwăaz`Q.\\= ((C&MBwxN疯spnY:CffkvwčjZK3(ƍژyYS6B]?y6^[;4#攭3ⷵvƛ_Ds,Li_N !,ND'ut3ΖjlXD5;x~{#rz-zM{iXzJ/6ۦQ::HZmxFiӖ3ºnXRiG2oUCu5Q.v)C!@!BX c3(wlSTFF -* ʗgk4OG쨺yezZK?(74+•߿= @6ǒ_D/V00>:\'""QkPwFecx`Qn.icmQ5^+/## @ E 0H;0Qx߿?ͭ&QF>>>/ĩvp=v+58(_yȻD"C"9Q=OG+[SDJ黾B_5_ U;#k)߯~!x?燶RCv<YZFADvg;%Q#¾6/(7diocS*i~}_<[uիݖQZu=z}~!iHuxT<OxxT~+ŷ Ļ1``2(ӧO&'60(1q[, ,m @m5&W~hDra`2Z@ @$twMuT{(}_F4/7o #Q_aF%bk .Zd$⻾˿{뭷BzĬWۨGGue99]۹ߣ}s:}]"V:)>xtP00002 d/^a#Iy:q#$VT/^Fg ڼ "w m'1C)2䭭~!/; {8;; ў޻wP"zAyУGBC,gڧ}Hگ D``XF@(Ŧ/ٳg![Mvn1...BBDT7Dؼ:Z$Tl'F\(t)y,b!zsֲ-WX+gѹTPz}0x1zC? bQn'ATp3zt҂p#\]lG{o]"ޏ.@ӧQ\~Da}hWml1xa"dSgumyȦ򤹌nZڏQ?:Kb(4mâkp<#i= `Q.\akO"oAa3kcLiWm4882ԧH>Rv Vd6"oں^m֭/ڗrQga` "t8:XWyT{Lݚ,ۈ(deu~|i={\" @-EgqFMVTADgabO|jrh*W=v${m\owQ9r`1-e >XW.Wu=:N"\1qʕ1/p/(Alu=zqX*NվOO%;Oؖ-l{ Ddϭ䷚)EΡ<H=y;OIU#NKѴ}(v~z3miM ƴk-Ν;Up`#H00@ͱ叿pS`s"\-Eͩیڪ9>L;|w00[1(da+'8@ F]IV#ZnkfRhiaaZQe...XqDo?z"}h^Y?R/' g@cC'Jn}߯QjDպkwӧ;? 7p5|7l#j)J_S㻰0@(`````xP溢46!lkܴޚ5_`;ڜl |5i,nwԚ_oguD?y3pM0nb ^BXC-!=z6S߳||C p 0  0zq$Qe lyYAZ j!nN[ $/m̀1JC'F52JQem(߭ ߣ4Lb@BJMkmt.M [Nr8HK3W//^uAnX=rD-Jպpt=} ߸qc6\GD0F;0Z4C4V]ZSA"?}ZD좱ƺmj1AX,l6 p``````€~W|޽{GȝNЂ;݉~Z%ɯm򋽰W\+-Q|Y[Jz_oB56g/䣟>IJR^V{?K|&5״ίkuf5!h]On߆e+?>Z>01 Bk?TmkQ=FN =Gѕ|%6MDM\)/50pY;-% ÞpVkvF:~hyٱE|2kgm4Ӛ3:;J}Kibouwu4J]02r 2EU-kP$R $bĴyH=G|Me׿&BZ dCS-4j3D9.x:A;;ZXw r7 ; U׻t(bTʭkC^hd'Ea(=O?76i,lq^Tt@cmxjT'5;-M_|/MкBZ}ćZ_Hcz#Z[A7:@5,ZJ09ڽ1;lޛoӱeֈSۤި%ujϡZ['Ƅ 0-3 h=-xiyI 4ztHw*ͳGml;위f?$Bv6a/&{$A/b X[5 _Hk:-ogwh9"\sP!#Xw:x[޾  0000-2lMWwyAH /6Ŧ[2 w;RH[r[Qy%)l:0ܞ' 3"sPu}[G+QD;s6*@ $B<8m@Kd<HHmdDu"_EaҶ-/׵5žk20pPt}RD5Nްgߚb@UG% @u]+c$պh9A.0J=9~>ub0.4t00002ӵO191 -ůAnF"YIs~~°^iu6m66КMk>5ֆFSH+[) "&'O)W+'_m@@R<Xm`5"+`J7 _Ete *Ֆk%/N޳ضF[ZU}:$)0φ @iX X R;wm#]ڿn!ZD:|#Ҷh2tpVu3 @ BMjjoR`````<:s-d/ɼJ(ٞڽyxC[jwj>?ZmIL!vn[0r4m߀-PRG.[ѢΉpu.u)oJs`%?``r9m 00000D͛I8NkMuUZu}?Lџ|M}PO>b7/_ܥCD˂-pj,n+,ߙF˭q;GW=݀Zu6E?*6+``.30 @ k$g5]7H~XǼNggglT8ҝ[)JϱcahFz=zD}]08?ܯh\v>%{Ν;{1^:X.emy T9@ 00`aɺv}Hw000&r9E 00000l\Ox$}-Anj 7޽ǴQE=Z bdK,#b-H;| a2 xKKY2iܬ[T9*eJeu eѣGAۮ~Zo- @lPsC>r3v-a7o\٬%|;ܸMp...6K}ǏB['m3z G)S\Ɖ[?gl˿`$ܩWOy0)7hKhՐn7``g@U)jÇY:2u~A},AEQ4|7c< ȍ$$E̱O){X3dݸ!tB~=oCc\†0E~:VqL¤f~pmG]ttEmڞy%','98 0000`e@ U (2lW5&|>DIƵ}t56ڮĆpK>}P"`oKyHQ5͉p5[[;#,Gs2"˳Ofǻ `2guĞ0000;wJ^ hDjkY>R­b2_ mp hqPDZY^#F6o9jQk5.]8g@Nsv;H000-r90000IȨEϟHuom$lIX0vv:~An̶2I5eŔHsnx~L'o޼I=f 2o͢sC$am: PݻgYF(y>@ (hښ waw?}?€l:mn7-KDD!׿?ZvIhrof*Ƚq˗à%E"kzdS"+9܊Y?";a 7=z8;8iJp/1E%3mad[|,hߗG|00Ѝhǁ_kC (Ai4Z)+"ץ$2'Za|_7#m~&vvv9"D27ʀGi\x[@|kW hq 4b"jKr%b.=}>(e#Eɕ/v6/;grcS_.=Is]Oӧ^\{ 05ύ7[ 9"|ME{.h)Xóa`=`Y{3|000PĦ<=yՆy׼KHQ /}i_> r-ŋ o(T:@*_pC!m ش& XE=i^HOpu^r[vy-Os8|_t0YY``3('*D M) fN.3L,޽9d~ro/W myS2Wvn1乿:O4ES=G,Tή0-a0:1pاz+պԨ1,cx،&Ix6l lǀu \9Ir S43E{|/~g O>|ܹCV:컂}ilZ+s+kUƣ3)$i|xƍW3i,~ZY{"Zn~@j?Nh)C;01(```` 4?)H]Ky{2|~ ~| {SCҦwpUw?u){ZXǧNm{i_)G|p IῖH}n"ׯ#uy[Tgk;u_竴GB:`c `>g000%HB\FLIF$sޕh3'R | H{} ?fS$W6{;bP:`׾G000Dp6ORE yd#mhUM^E"!FWh|!Cprْ6[tĹ؃:00@(```` ,́;wt[nE X ?ItK|LF:}Yy#BzN0"g5@މ, h|hjO܇ ;< /s ĝj_sJ"\6?ݶg{g[ l;")X~Lh6Ѕˀ6 ҵDh; r%$ºώ-'qEZwkyMgj-v˗/w޽{{ BP5m(%|6'•0n9{Q7R\sx?J2p}ypU+O00PUXX@ڸf6> Pdq'IKQT8#29ODNY)bm%o_tk ;u_d@Ԏ06#U\EeqIsEv|Sç5H'j|o= 1J򜆝 @ ڼfp\R.OWr~@$^iaf_#A|e) ҶPq|߿;6V_xAߨ?ڞ^H43-^EUۛ}ҿ:$7ߞQRh2*ck X c 3~0޴%zPTNrXG!|A Egj}dc BƑ0Lu_:ƟMGj?wWpsF[4~LwR lE}-3p0`!4 hW Iڄ/')""j(b.67e{E?P=!x\"ȥFf%q =2~@>O>  Gt)z4V-w\ߏ&RܫQۖ-Ğ00%.(YExɫM 7>|݇ب=8]NTHxXމ : ^S}Dy -&RD?mSbCRSn7!¥>hZG~1Cgp7|00@Kd<j Hؑ l-ՋblŨ ȅ~R6L$Ǐ郘;T;E(wNiw#``` &_```b@w(R.Fi}T=%Vi!mW[ԫ*-7vohggg؜}'ʀp%!a67 a'pAt ,hϞ=_t|#ҏ g?u @ `````sRO-鿙<>yFmLf7 !.߃ǚ $AπEMk)l˂x|[lw6>@C\'00e -E6D7n)R" :nG=9֛}BZa@Z3k%000 0F30Q2OWFg(\ٳg0b  \re?m36(@v^cha````\ظ}.47ef( ۉJ=ژ$*4bݻ'=ׅhe{ ؞\nos8P$(|E1BWa//QեthMIEyAl?Q \N[ WO8Cq&Ayh#m r!l>\[j /../7Ǧ>Hr3g蟁]@ֿI֭ZcR[  IcO"\m"Z.*jT> 00b`u @lhKH[ndQ7۫ϟ?sGמi!ȅ:"QEb]r}s c-%mߊ[ŮyX nLS'he st[`})# # XÇ_ 8$(\^&!"Jk9)DD-#,Ƴ!x>\O]o k]6k-EgLG]5cO>}uu=Qu _`@ekLvʕsD:Ь9ē'OXCan.hyXwv҆Նo=4t00001 iqH2j)]f~ 73rai7*}{vn߾ o6HKǁ sHs[-y7ҿpj6$)86(;H cݿGI+sO$TES%O?5uFE 0iLȶsvJŃv "E˥/c1n)l-䗺0000F0e - Y#b$FS\_ОXFQD) 7ў r>|8-^{ @K<|o_u|ݱ{dtCpV#Jޖ; OB"NEܗ ۗ?smRyg1(x0000EK f$ m?7$p?ggg,~m_ri&@E_9L;mخg4T;9=QGe ]1dkYOW-}okRCD RWK $q iؖޯi|C:(•(_ny!.a#A;)u?ŀs @0pH05 a``` -)2l־xmꤗ]խ}_ A"f)V<5h|| u=̀ƫ)Z&Q`52-ϹHIĄ5Ϯ ;b p 8@Nh+``1(Q. @=z(PSt?E%EQKm;­DrisG1lZ> 0'jn_sEܧk~}>4LcI!v}-t j G8" Gi0```` H`6j$<6׏'O鿱mlJ NK?z\IhMׯ3$.YxW4S.ETLS-20'Fo8"hԳ֘%3>\7k3?G00! Vbsl00u~Gbhy9cp^*b"a+z@ Mס#ֻꠑy_=b{lIPC0c՜W5T\=lvUlfeh0cec&?9 fQ.\E (2āڴaSrIQTN[E kLFU/Eɕ/5w ?κؿ$/_ܥݣ`aCI6Gz}\-|(z!e׷oߦ.S1p3㳑-j=ٛ ΰ 0ihҴ#y? @ tut "ZnQ7ݻl2 9GK۷Fۧ &;tܦ튠톭ը Hk>zvkkOs*ոi*p/$ʡӟT_%E˕Խ輒: c00`0Ƥ+Dk8<@DlɸYE׵{ThZ/36%|(R!@)/hYpCy*ޝ;wv?Ua }ϖl2Q'`k~~F```0_0000.t]{n޼b-Ǚ8M"!c(Sg*F~Z?~́ Ɓf _LQw: :u(T[J/Qրݻ4xF}-'xIf߿OMaF````Q("ۊɟFiA~pWnrNkĘD!ئ.opu}K |r' >myM"\V߫:4![s]ov$=-3pƍ}'Z.1y_Gۇ3K]a23 m-0x}bZSSV5H2l|Y߉ xO+_bq;C1Sw5`> w5ks"ׯtU"ឲ97%vhZ;lε$-0K4E&l`` &QLa``` $ɓ'"s<3oWlvHK'd~'D_E?...'qpM_p /ⷵvEM"\5J>_"#O)0@\Xuic]{6u{a/5`EslP2Dex}ͱ5a5'rz"E"N9{)z:mߧ3>J*#GSdyORGa>D˭>~3" .aFeQnMQ4000ڄՉDF9/mk&=?;Cک;rUo*\thIq|[<;ǴW\{MWi\w)J]ٲ67s߶- mzd  yČSY?x7/ @-ha```N2+ PR-~1'D1_raw$ڪ:I|> xx뭷mpht=p/)V;00@K텍 H>ݻ 00# )#h#џ(Ƈ֭[/}cu(\{[<*xaiO PWia` /_]vmߞ(wK3#•\c{QZ"">t}˙Jr%Ş a <}=k&sEia``ra```2 _k)Me&v>EO<ҖڟONY淛~k1rvvF@?j?ü7N"!A"0DbP"^}ףz"Yež]"Z.Fgmh:Ln|cg 000, ƀ"'6`O*$Lr"xM"C0cz/\Zwc(r{/y.;S^reMIU<9Sjߡ@>ZUWfɷaZmB/Q{e-1@````5O0ڠbrɁNoE>cL"9 )bSuAn>hmwf_.7}1W:vEDiWu>G{֭:;"{lA]iMh>v덁t 00 (hEW&U3?Ams ש`zz4Ial ,{Ƴ%ϮϮ7׷5Ev'C=NYJT8,g^r4_ͥ `c$9??7|k͏`2y :Г_Dǹ0^e+YYؖnkoޑBfȌ7D8, b@WjlBWWMA KMcx|>x`?>)";)30DF" S̉p%02g)= s*a5 Z.|n/ܜ&  @ A0000gSt\pwr7qIf,0\8*HW#̊Ǐ# =s|Ob EIW"V;Ј.O?u un' ΰWM 5qKss _ A0000`')ۊI6JlἭ{A. E:EGSE"y>'s |rek\^t Ç;w! R# "h> *B5ck;6.4Z.W=4fR?~u 00 j3|00D(6NVmNFr 2f$@dn=HQ۟?Xkd2gk#ژBe޽˵Ա^=e-WQQL-8p0y ] yFN ``` D=#Q롼("\%W>MgggpF|h/1P73F:!f!ﭓs"\]"3q1[MSMZ9v^ư]d`-: kkƻcu匙cz``` mg 001Hb>m( ~y_"S<퇋b"b+20JT50deh?~oth {=GͰ 3 Qڙ7o2^itڵk{NpmÇ~q}yul >xgzi{Z%Znk<3p001(5 ׀"H}2QXm5_\<=XX֜WslEɕX+c4i~V1C-o?S1ynp66`b2N```d?OuS>bNd`/U̲ 9uŋČZl8a%3wݷq\'z{rqk2hLxw/;J]캥tIc-n%-9[}h} &&r=[޼y k˽Ik׮yA^t}]\\/nR_cO3=0!Ӻ'zC)jzr{ZPr+ύO{=Ooܸy9{001 h,E1Cr[njwK-ڂ% h[*’oߞo{"ph"YsU鐑G=6'•/a|7v_}QO~yh!|gDˍ >ي݆5b|a``0.at* a@ 2ׯ_J䊌"prviG}E5oQyB -W\Sc4Ay#9?'1WN9V V7D>7}6.Ef.پ/k7rCܸH߀Y00@KEPB\ t(w{M"1l~hC\*Jaky'7׵qkKĕƚKEؾ?' H`C\ ۯΩտpRWcMN~![i\ư]2n/j``` "fH 00H7|$PN+Z.,ў@k#uu W}4W/o,k2/my@yMڞ̽9P/pcV=]~}_쿽96@ rqqAߥ@KG 0003Wn~:=O^Dkbk)n"ȵ ٳgD_mh*!=DWhI |rUmÜpuㆄDKHGG64=\۟-WAhfiTҡhTFl "&00001Rvƍ,|)GrYf"DP,b2rup*U? -g_l-?~o88͗u~~Nc #m.'NXq ݢyn:|vy.u1ݶ"Ck>g6EJ [΀ZizY?``e@0003)S YO\&-;$Wtlxc(l&t#Wo,Rv8WcwW\i,4pd~)IwRnk_?߼y>?ꈿ`;l0pa```S!HZt '0W91jwz=ϊ*wxw 7?vF춫[l_߄5HaA=G갞jMùԹCuN\ꄑ5e?-7?amϧg8"NE8@O00c3 Q"iEuU\=v]nqaEFW/1"qYmSU3v( Cjl_iB$.ցNIsX"ܱ{{X?i3>[@bmDz?r @g hNH _,{+6O(]ۥ^ecKd ȵ +Eund̚n" ^Ezd@ĤZ"4GK]-qbÇՑҶ}ԯQ Z.: 멯(Pa``pРԩ  Ȁ"8 :ú3mj[1ym:@21x| #i#>o߾k!ng#)u .us!E-2#U_y`xMf3ߚ q Zn_P/ -R>|z00r @G 8==PTMrp &v[1Zl~:lwԮ 6f!.j%ߒ]w0RD@ Z# 5vVmHSn[ϳqnVѼݻq䖾!Z.lߪ[?x=`J `Jg Zw ? @ 8֭['#im$u]o|aA.u~#JjwEȕ1wF)c;l⫱|qR:!b-+P<ܸqխ-ӿpbSӧOՋ6Է R6he!p  ӶUd\mxjJfX\El[϶Ix|rV,Eڲtx|]c.lC[0Pu`m˫{sDk~:lYG ķ.߂s@;@'1FEi  [T*4>000.NO>-Bi3VM]{-y|#ǽUK_NUqH*ŀDGNj/'.sQ8_"bD~E;o/F)mZcS7}۷U?GW|W miyWzGhk2ȷcGܘ~R l$-N000"````1$lH*no8_?9V}* _| >$mߏ7oD۰&)$׻鿚m)4nGO0(d/ʻthY=7nx.G )Rƺk}/ gN㛴M001k5uD{eud:2ϓ QA#mCʟSHڬ)*b~o[2͛p雾iyI[oQf@s76߃wrz h\  Dra```!>|_HQ-0~@XR#' pնp#v}GYwORYn2nvh[qwc)Iƈcp={j8\aIp#sJ ~lb %~ 000pD8 p ``f@As<|&bOѢ%A.mWηkHb}ԳtU"cml ̉p%̕@W۩mtSXt{JIaG)Sxe 1nH\xDra```yrf`2X3m޺u 4ŷ$ŶDVPYo|RmO^h{(nm2:ؙ ݣbk<m{SkkR\s6߃wX۷o* bE@tHkÐ&3Ն,izl,eJѐU\EJ,l[lem^6m?Q:J]jL$D{ȧ;iXԵuEb{.j4>X€ڋ4n/V%y:ܼg/N00%@o@Vm &*',2ȄPY.oS. r40"wlԡGQs}񡕁9*J'nn^!O_xA{|$ׯ?R7oބiffH}!rˍVV߿x`/C0P^:_̀6St -N\m" :--)gsbCI>ENr)bn<O7(B6aS$",M|޻w6+: m@^}=S-n ;rׁ˯a Mo0@ e@Z0&%(O,BիWwԧd q QSk{(("#fK!{y׶u-4^{cZ "=d= GSjաM5(>A}1 Z~>g=00PD8```` ?~=H ;pn>ydfN&,&T=@7_1|n %" 8Dվ/p@~?'½~NuȢv ԡ/^0dܚTT8߶~߮rak-xo=tHV\c{uLP;~mT~MKkEmHmV ,xv: w;svMjDG$֡3ećP"?nݺ*kM{]p-1(Ϫ-囼R` Dˍ~(@JrŻ `@Ioƀ67о>Mg~?Hb #oߦ #^&|]lvșȟu|gE$:~k 7O5]kԆpl̖엢OmE7ƍ; U9"HeОĵ}Pby?umtr={Fmtf)v;9q`00-,2 Ȗ30~x)}4bN"rcІ6|S4ß3<ӎ%pAv$C:$Co#SF}6}[hO`1 c9&r{bCl00" "\``d@LU#:ا:z}m&A*x~{=~x~q]_w55;`?GAݼyULuTN#&=ĠčE>l|5;Ǿ/ a@k7rmد6,k,XE01Pd`>(/  @i;T GԽuݾ}{!+E^ _ІE5J>|H須6pF>hG` G+D=!}^L޻w6v ݾ=_@=}aۗb@"{e0U)K00\````2\yzE#- 1c'6Oϩ7yCb #İφvvv)}R,W 5N@A(2 ;mk4`{rgG+ە+Wsrا00PD86gk:o @;틩}=!m" ډU_r؝߽{wYv }*>|cQQ\%xƍzv""]3.mƺS[ϟ?eߏ_(rmV ³g'G#V .. Tbk0OO"QļVb-$:(j1w/[oo...?Ʃ9N}ā9l؎ sDnGG[ޯR[F-T5vRǎ~;b;l3@\5Ї?``N'T6*~O00SBN):iÓM% mEHƬl賡nr?i?;"J~ʩW̗OuX!yr/sEf.u]9~=|~=?}a8D˥mFH]ć00  @Q \-ՠW^Iu8m]bPA׮]۷_/^` Б@:ouvyo5QwV\""Hc_t xr ;7*ej$Zn@y\ l\&00001"M<>nriI~휄m1mL[5۷! }9^~ss/D ?k ,R7``MR[sWwhEq_{g `dgؐKf+ׁJ$I~7ERӂe= ]϶p;m%PK_Oe_|ۗog?D:8o [͛D'pM#SkLEE̍WM?g-Wc~/=#:HO<|0}2#ؐ q^`M 9Uђ{=#]϶c6]FQW\"W>a;r1=Pԅ׋Rz pm~(햕M;Vvx>3F? @ a0001X s6L.=]zU".l$'|v: reGѮv1*ǏtijʷH>M"\V]g~CR'qc|]9t3SZ3@ DmׇԿ} j c? lF6uX3vh-t+,yf&״R["'Zx7v+Zo9?m-nx+oi7{#s"\uJ ORC>^kշ*rs|/ @h}z:?o߾?0| g 0u@[xc? @D}͛7 Zǚy^I4OpM{vb6*\ڪZT߿OU H;j{:vD ް[$9["0Fh`` ʥÅH%OH&T3YcV9Zl@/2vGxw-n蓑_E ǽU"\mZKv}4!…c1?=hJ@تLm]`n+% m2nx`ڦw `c"*E{3 y$ƥMVb ]-p.\߱Hmc>yOmӖNW"r ,w4{CZxέ6Mth@ `a@oܸqjT @NHBO{&},J޽˦GQmL_SK_^׋96ܹSODy{œF:t">z􈶎[:[I`)Zx9TYή0-d !Y8[3 _00.`T* HLH Jrt{\.ts2cT&fo6{QW Ҧ&%;br8,?sĔ"FJПGY-aTqKqv`S2pKwa`}4Wպ'^ 6 d 4F {z''!LB<)Q ϳTR` ;`"(8.-5ޑ 5;n"t*E۷[ZowD*ҷڼ{ч0f* YEVaX[5_tH2K00pD8La```D9=N0Iݞ$ZA 0o y{QQIR{yW,NaM&o"cNڊtqqA4BڈTDwK9\.?NJz. aƍD˅0<.o{Oyggg%[+c1``>À6"`֍$XV 1m l@ an%̀>c׫4&h.O<٩-}4T˼~ZKV8Ms"T)x,bhT%޳>KϞ=Ookx00r;䠒Sa```}~sMHmLbcl|C@Kπ]jsHyG} .kEY;;>GI4߈pz6#nՙH뱷  Z|;N_"0,'\x$\_j BM$\ T5LdJNdx'EDvf?~%E]^%Hc*s%"mĜW{D}JwnKTdytCN%Z -bg+\[޾ $,PL00006)6F^x"ƥUxT+u)iW"|^_H6\A[S)gAKK߹"\ 9:7:y;woǾ. ehuK:nOZ{y } mvm04t0000d@JKמ3h}5 u(W 0""~B Юbڵk{ g0E8o|}:,Զvb(B bhpAggg9/~v@};|00Pm\~H%GRFQJ9ޱ# ܴتv [~ƖD]a5H=et[;G+q"<"mk#3^r P؛ڟK"7r#S\X3xMaZyČGƣ@hdha``` L7#N*2v&?رj$*߁ ::=_LS)X>z"M>m˹"\Eʕ_'W~z뭷}Aoyv"^$n-a`<Asxm\y-޽K=u y'0D8t0000`d -ZHܦw# "!m0޺u يG߄5EZxM{ՇO={FD $:OvV(mQ_pc~2YUQ<EI:'"I.,b@M^k /-  @_ là WƟ`g@"t.pnt_0ڞOl~aɓ'nLQg30O6cq~~qɩ7]=wzEaJbhzv^Zi:L݁VHD˅V-ϵk_ Dt0000ɀNQl>an*,e"A9ۛ4A迩 Ν;C`DSvڪt-_):"/}}p=16)hʕ+o+mv1@\mX~L;00p2E8[8oPa`` 6VR!6ojf韴?bXc2O[ז5~8k6jTVM}UD5""{:_:Pi :bC7Qw``5 1;Z~|P7CVvK00r @NR!:A^[ ZNH&~+y&LFc cEFK]Z[|Ev9%WuBc \$8^G@L/H9Z/ u@ä```f5NRE^ޢ ܱ+v]ʀ"S$j%D^:;w SVC>iצ !EqHs^`^:'Ux"S+#umcz:fEW[%Z BD@k< }# @:;=MuU2dj`iE6RH߾Ʃ{?ӂՂSUnK ~s8j1y-BN^x:OP+ަ6@??.('J"h?w:Hjs|"Ě K^"ϩgDKU/?c0bߖ@bRDf=.^ߧ9ѩڵk~ʺfDf[j;5f{hĸDʅ/iR4RjmĨ&_~Pƻ%F~T16&^A S[o5XpP~?9]rD:|#xVi9&; l`8h):G3{.ӭh:`<&~b;l0hBD!?``` pjCK#nq=Z:2E[jf`ֲM Hh^P=O;0A.u^%G1̗~ۏooߋwu@ ws?om7=2n(p/GLBc"uUKhsuOcsn%y%sḥc~Lbi؉rVk|À5Zĸ! 7a<NЇNފfySk00?l000-0=%&Uɍ͹ׯĝFPf=(g@ +ȴ!a*8מXJm}?I<3UθN>Lm~jPf;ljl)) F{./鹒Fi$C"\q2: pUdJ PuY4&O>+yL:lAhڣHx,@N\cc f,}"]Tx͠z,͖pIO>(o$xxN=H[[Ol[a`zPVb&ksS/m7ykyK,JE)ݯul&6` Srֵd5 03V4Y5V8u+BK;Şeg% 4!YR@RA```04gүg#3o(Zf0E͵ʙc;[[QD )‚6MRYb% `;.Y96c]%mK%@Ey:i$\D۱e#&:СWL0641p9ZR1njz4KG{9Z#Vݩ}+Z$С 000S]+lPbJ( ebL7>ER|tEvmAm9Ra.|vgjm4f;EjCƂk۪*VWɾԻi9g9:'"\9;@:4>xMWk/\?:iڔ eC L~yQ.Xn>XsnP{ a, 4!±g0003?D?ąi]q }Tccw;|ç.ߗhC}{"ȭm>8@(-cz\k/K}l_*3"ؠa[XiGǏJDnռ؁%WNZY}<_a'WJQsm(6|Ƒ80 ; @\``` <2w- ğ5/HP[d_~+bx&Xᄹv`˿ s\Zo|5b*#ؿf;RDk#5 ߆ >X"..v6-ާ[RYsE8ژi-Ÿmm=ܷV>\z3BHzzߡ @xN F$TvXMK/||O?"AIۼmK컽}kEb:~~0A|QEԏ[Rl4Rc{ç{@ VW=Ҭ5M}h~ی3ve@sD5扈r4֟SulnC!~JؖE:F ~vn`zokm]~8 ۆ&⺿s}Dzea.mFG<_"n,B>>Uo"FKV `o 3ݏ?b>Q֭Pʛ&62PSK\݈.ڭj s@8b]p0p63WDT"ǀonT%X͵?PāO\DVt8 ~ 1}ݚrǁֆ6]1[r}s߄afجhk[I|n^7ij?ImH(h[c( {`.-6UZ@Z<{۴CIa@p{V|}VkGٰ5͞6uFZ0ϟ?Kl``}4Rt!yj3Fyva#3qį5ڶo"ʵ:о"D!iD{a`<lt5E%(_ )6 ܽ{wE> K+:[ ŕv7i?_KQ.uzH>?˪6f6HK!aM:1>f !#=7ZwlKkՁn/tQ kZwn3z(77k Vl֮{HlF: E 000-f<yAnZds<|Z P%_% j=iCY1ڤmuAҘPZ0mgZooY&qeo` .Zտr@%~i`;u:w ;kϰrm>cڷ]\\0`9>3]?ė1@H 000-gȧoG^PhͲ0Yׯ/6p&L1 q|X~kHss9ͤQ%ykp;GI?9aiz1Qu)ZC}o5k]yJt;i_:5Gꃱu jT7=sitԽ|& @m1I@%40tē^ߗ3'Ol຅ a.zzj%zl"!wt"/۪O{N+ߺuk==ۼR4Na`,?g^2<{5KOMy_36r <<̡i[(/mڲƭjU^7X"16΀"ÔTZ.E҂cI(<%jᅨd'Qƈ//ٴ!b.ˈ2oVHRCpf-Q.oD;f@![^e\"R_J޷.S[FFc)|뭷#6j3ڵkmk,|L{nm0n}Ǿ2 aexIuuqOz oL9F(w=?Sڱ2ۋ`?aE88Tl``za i!2CE"m^-1 !簁5MR[u!1{^"Eq[meIYE˯͹">"0ǀʭ1D>aWr0 @4b8?e1wamխjg 2 q2Gb?00#|naf4W${r21ņ>]6g N}z ۫C@Y#8!pN+a";ń>fzɸzuÀ[^𴱚_U4|{@yVS{ZsQ|[޷ش=BǏS8 `` 000pbɦ'W+rxU{h>ӂڇ>?zۃ08Zn9ސxj$[:!Pu%L>lW!/0 j8 N5%6e.M{ ~8yDgoU8VkѰ; @9Šppr9'cKl 00"X7'|^†Z´=lF3iql3R׊rGD(ƶ䷩DlԧUK\W\Xr>PR:%ι5֐u#cW(ˀO#}{yմu I&9P5600[D>MQt-'[AGmۏވsmć>Pm׶)L[+jeUKڶSFrdVD*JZkY鵮hm<ξ)0]t]@snEi,0Csk 9 5oU6. i"ʅ@ͫ-%1nMLHX JGF~zŋ;=_bS[:WʋƁ;J$N/-bh6!?0\&wih`rXƌ霏y\r9skܪy}}HYi``MBpM~(ɀ .!XHbd%0/q%=*ǓXT_]ރ07AlY HJw]O~ּ!!p5.>$f.=5e#@4qn~/r_ruy6)C o[pm&G>RJu/a"3(000!(+BxWWzU*b\&9'93|RuTAKDǀı$_؜oxfNNP1n Jnj̞]N|"GRc{>|Hǚm5ۑ;e]4Y{M ~v+yc6 @1ǀklhSZQkFab2l:;Ws_Eޓ-3s6}8dhԧfe_ZS)"Dxb-ՙRYWviɇ6\ `4+%E. iG~mQ` y|_x*uhg<~9>D88H``zb@Q¼,oĸq ȦqiczjcF*D\K|߿yi˵y-QHS-Qⴱ+aJW [Kn߾}0o"ZyKFAuwQ j݂}jQkg)nDM3߆-k@ [7Jܽ{!И d22 3 h]=\jCc=I92[1.mGkm=̬Ksm᩿WKVj%̃-ŀK6[%^=SJG;h֜W)H9(?LN圼L8@ q">8ei:R ΀;J,~w ʅG[k8w JokTrD bsom 9ug- 38W>9%}Y595ua#hx狇rczϖܪDl{asl02NL„ @)ͻ9?U1n횶AK.UyO<ׄ_=k/]t!Bx|Q)$w==̢=PPm}:XpMy>c#蟁-Ĺ: KY3HitȄ9Nu:a8dI;J\ƀV5o`k@KtDp mh12Zƞ01pem[ޜS'!.%6_-m)Jw mZyVd"C@XB$OcƮ>{b7蓁5ŹGe{Ic LAg>Pnx2[Gs2~t;ñى6 ,a gIaHKefG(q*͝;wp mC j-m ˷%[a6E,E과gg%V$APA"Vd2c?}2>sTUŧYQs=D~W ЁlU{TNlL=<A{;a'DDGŀw3b]/b4P{Q}9g҇%~}Q6gnCE%'mT <<=9tJ2זwD2jVVmpQW wڶ(m jKNߕy`LQKePwݎai_#= e[Վh/c7 @``` 덧 sY $+L*W (FnDݹLh]:{|3sJS۩(^zC_X;j3Z} @dsuH8rj-W8`v6|[b@/V;ל[4G_A=hP" G^000o^hK rqkEdĸpJǮ3hNDO_E_dnm;:py i[07|hۢ4ȗ+pݤ]E9Ŋ[077v0-^q㾺~ǏcC#' 0`d@W95c혂qA?z,~/% l8[ckb1n MYh7-.*^Qvif0iag`146S-㞥bNTd~?ɚ6 B\Dslgmi @8Weq;xBv+w"#ʍ?WlUMu?00Dra``00wMΈb4V4Sⓑmd#d\jھ|ٗ}Y NQ$z 'Œ'h{b=LEb[7_xA{Q{'&.oߡǮ@ s%¿{P0 m8o0/_@oԩ6|sV5niw101`l*@ _ bw9(@p{X4Kla"4&O,B\Xou[m$A-Pvgݖ# }6{coS\<r aݻmb}3۷o6q(3#10}W|k\mtHxE.62qIDr/GRdk%DY&'6ڷo;~8$U|Wi9Su:oa yU o}X=2i) Ta``0답ᮈ"q;0O q6a16˾#UqcQxFB܏+Wmͮ="SbhVQ#勼n TA~J\/G3_)n;xfH) #~'h.[C}Sy~?YWm?~;o_ӟn_w~?~ʛc1ݿwͿ7C.q'?Ƀ.C/.?gOOrl#~GvO|b~]CNw{~o]Ϳ7w~c~]տWwW_٥_y_Ki{Bmę?^W~.~o-M77߸K_.ta_kvK/}CvWW_+~N_~٫/ſx~߻K_ .~/y?ܟKٻ3w~wKإ6K.ۿ}~?'~MM?KooإD_]}W}.~Oh)h}~.m~˵zv|ENnd)6"{<>v+KMbk5i)R谊&9iߎ^^/(ZFS;|ee7c7,Gilø07s*Fc}mvFNs]R۝;6h9K4}=mk5ڤ/]/J?Nʯ_kߚSh [5tv;]㝮`;]'NkZO:7]kAOצkӵڥʐ~ZNs~.~u>J{Oki_Not`0kAL&{ӽGпi/D;;竟Kw}ݔn+=O M~ӽtojg5J[w¦{dӽ?._ /OQOuݯ7?N/N {=˴/s~|~/NH{W~].Wn}紧t>}=间@HG0!cV Ŝ.㘞CZ/iOrh\GKҔ{) S3zF|i"jrkCXDDG @NY뙹N܊FDX+VAnz>6{ @c 3r Q˝/+I+XɃmGw)[d@omK!J:6-X(ZVkS4ֲZ(ZVkS4ֲ4GakY-'?Xj#ӓKY'?Q(ЇG(QʰV>#vo-yDm(77 Zo^{D#y#͍QZ^IϏL$!%Rn8oxM; Q(m A{6rƲ3&j`3ʁU|X9 #])ہ+>( ʵhX{k~"=o-TVk^eDzZ֑}ֲZHXb-%ֲ4GDÈrQq =nDm(m 1! r)4D=lDr=}\Dn"pcܱ <ƺe,D)'VOr`,D)'VOJy|'֎d|KfxEbӶ߰p0mektw[j﷖߯m-H>kYھ]ֲZ5Że[oXjF(\Dn"p(QҠK܊DD \Drz:ADr= EQ.\7`(iYVBrza*X)J+VBrza*Xi455˶廭X-˱Ʒ,kF~|lc-ǥe-/-g-߲,e-H>kY}i_n>kY-X[ֲZ5;-e["0(QDr= E4rb s󝸕 Q.\Dr=m4\Dn"p(QUO4֍,`!J9=r`,x%`!J9=r`,xuOy.R\_quKVzEbӶ*Z߱p0meksZj(Z߰߯-k-H>kY]ֲZ{e[aXjE(\Dn"p(Q DD \Drz:[Dr= EQ.\7`(iYVBrza*X)J+VBrza*XvO}SGnWR+ACKV*cXZe*] #ZVk_5c-QRߴߗgX:ҸZVk_ߥi-/Zﱖ*SZjK{"p(QDr= E4rb s󝸕 Q.\Dr=m4\Dn"p(QUO4֍,`!J9=r`,x%`!J9=r`,x%͔RtՆE)ߒ|X9iK!Eb"oI,dkYNk-]%߷/[ֲ4[j+BZkY-}-Ƀ~IjE( \Dn"p(Q DD \Drz:[Dr= EQ.\7`(iYVBrza*X)J+VBrza*X)JS(޽{7ofGQʷ$V,}ڒ|EHkX[ #ZVkgZjkoeKV;#e}ֲZ%ZVK|K`)_i"0(QDr= E4rb s󝸕 Q.\Dr=m4\Dn"p(QUO4֍,`!J9=r`,x%`!J9=r`,x%WŹm߶K|Kaҧ-WኵOP%yp0meK|V;v|ZVk$oZjkoeKV;vٖ~ZVK4o[jkm"(QDr= E4qQ.r1(Q.\DQ.\7rzA(Íes*X'JFU|X9 -BH/YֲZ[}kYNk-]%߷/[ֲZe[}kY-ҼNo)]%G(\Dn"p(Q ݊p̭ wV2Dr"шrzA( \Dn,;V'?QX7(ʁUS4V(ʁUS4Vr Qʷ$V,}ڒ|EHAʧOP%yp0meK|V;v|ZVk$oZjkoeKV;vٖ~ZVK4o[jkm"(QDr= E4qQ.r1(Q.\DQ.\7rzA(Íes*X'JFU|X9 -R9=yp0me_EIc-RNO>eee"p(QDr= E4rb s󝸕 Q.\Dr=m4\Dn"p(QUO4֍,`!J9=r`,x%`!J9=r`,x%`!J9=r`<OTNO^,dkYWQXjӓkY'OQXjӓkY'OQXjћkY-7OQYjѓDr= EQ.\7rz!M\`oAKp E(QE( \Dn"pcܱ <ƺe,D)'VOr`,D)'VOr`,D)'V,}'?X8iɋlc-*JkY}rza-)JkY}rza-)JkY-}2za-)J:KY}2z(QDr= EQ.\7A[ĭdr"Ei"p(QDr=X6wO~ndY Qɇ`(iX Qɇ`(iX QɇKO4}Zrzb`$Xj=ZVk|Xj=yZVk|Xj=yZVK|XjyRVk| EQ.\7rzA( i{ \"cQ.\Dr=-\Dn"p(QUO4֍,`!J9=r`,x%`!J9=r`,x%`!J9=r`<OTNO^,dkYWQXjӓkY'OQXjӓkY'OQXjћkY-7OQYjѓDr= EQ.\7rz! ܭ'ܪ|'n%C(Q.\O(QDr= EƲc,x%u#*XRNO>X X X4O~"p`"ӓ #ZVkU4ֲZ(ZVkS4ֲZ(ZVkS4ֲZ(eZVKStZ(eQ.\7rzA( \DnHW/؛o)7r"El"p(QDr=X6wO~ndY Qɇ`(iX Qɇ`(iX QɇKO4}Zrzb`$Xj=ZVk|Xj=yZVk|Xj=yZVK|XjyRVk| EQ.\7rzA( in@8AV;q+\DrzhDr= EQ.\7`(iYVBrza*X)J+VBrza*X)J+VBrzaҧy)kX86ֲZ{G)'ֲZ{OG)'ֲZ{OG)7ֲZ{oG)'rzA( \Dn"pCz|(H@(Q.\Og(QDr= EƲc,x%u#*XRNO>X X X4O~"p`"ӓ #ZVkU4ֲZ(ZVkS4ֲZ(ZVkS4ֲZ(eZVKStZ(eQ.\7rzA( \DnH(w+ 2*8߉["E(F#EQ.\7rzlX DIcȲ ӓ+V'OQX9 ӓ+V'OQX9 ӓ+>͓Hi,XHH{|%>J9={%>J9={%>J{%>J=@( \Dn"p(Q DD \Drz:[Dr= EQ.\7`(iYVBrza*X)J+VBrza*X)J+VBrzaҧy)kX86ֲZ{G)'ֲZ{OG)'ֲZ{OG)7ֲZ{oG)'rzA( \Dn"pCD[1NUNJ(Q.\D6Q.\7rzA(Íes*X'JFU|X9 ͓Hi,XHH{|%>J9={%>J9={%>J{%>J=@( \Dn"p(Q ݊p̭ wV2Dr"шrzA( \Dn,;V'?QX7(ʁUS4V(ʁUS4V(ʁO'R >-R9=yp0me_EIc-RNO>eee"p(QDr= E4qQ.r1(Q.\DQ.\7rzA(Íes*X'JFU|X9 -R9=yp0me_EIc-RNO>eee"p(QDr= E4rb s󝸕 Q.\Dr=m4\Dn"p(QUO4֍,`!J9=r`,x%`!J9=r`,x%`!J9=r`<OTNO^,dkYWQXjӓkY'OQXjӓkY'OQXjћkY-7OQYjѓDr= EQ.\7rz!M\`oAKp E(QE( \Dn"pcܱ <ƺe,D)'VOr`,D)'VOr`,D)'V,}'?X8iɋlc-*JkY}rza-)JkY}rza-)JkY-}2za-)J:KY}2z(QDr= EQ.\7A[ĭdr"Ei"p(QDr=X6wO~ndY Qɇ`(iX Qɇ`(iX QɇKO4}Zrzb`$Xj=ZVk|Xj=yZVk|Xj=yZVK|XjyRVk| EQ.\7rzA( i{ \"cQ.\Dr=-\Dn"p(QUO4֍,`!J9=r`,x%`!J9=r`,x%`!J9=r`<OTNO^,dkYWQXjӓkY'OQXjӓkY'OQXjћkY-7OQYjѓDr= EQ.\7rz! ܭ'ܪ|'n%C(Q.\O(QDr= EƲc,x%u#*XRNO>X X X4O~"p`"ӓ #ZVkU4ֲZ(ZVkS4ֲZ(ZVkS4ֲZ(eZVKStZ(eQ.\7rzA( \DnHW/؛o)7r"El"p(QDr=X6wO~ndY Qɇ`(iX Qɇ`(iX QɇKO4}Zrzb`$Xj=ZVk|Xj=yZVk|Xj=yZVK|XjyRVk| EQ.\7rzA( in@8AV;q+\DrzhDr= EQ.\7`(iYVBrza*X)J+VBrza*X)J+VBrzaҧy)kX86ֲZ{G)'ֲZ{OG)'ֲZ{OG)7ֲZ{oG)'rzA( \Dn"pCz|(H@(Q.\Og(QDr= EƲc,x%u#*XRNO>X X X4O~"p`"ӓ #ZVkU4ֲZ(ZVkS4ֲZ(ZVkS4ֲZ(eZVKStZ(eQ.\7rzA( \DnH(w+ 2*8߉["E(F#EQ.\7rzlX DIcȲ ӓ+V'OQX9 ӓ+V'OQX9 ӓ+>͓Hi,XHH{|%>J9={%>J9={%>J{%>J=@( \Dn"p(Q DD \Drz:[Dr= EQ.\7`(iYVBrza*X)J+VBrza*X)J+VBrzaҧy)kX86ֲZ{G)'ֲZ{OG)'ֲZ{OG)7ֲZ{oG)'rzA( \Dn"pCD[1NUNJ(Q.\D6Q.\7rzA(Íes*X'JFU|X9 ͓Hi,XHH{|%>J9={%>J9={%>J{%>J=@( \Dn"p(Q ݊p̭ wV2Dr"шrzA( \Dn,;V'?QX7(ʁUS4V(ʁUS4V(ʁO'R >-R9=yp0me_EIc-RNO>eee"p(QDr= E4qQ.r1(Q.\DQ.\7rzA(Íes*X'JFU|X9 {l\gQ{ήqu]G_]PnA:Ͷ܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTLA@):or -(\]PnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*&ܶsSPn灂r -(\[P㛂r u|SPnAo|RbBRt:yPP`)% ):<(C}@N9$ОɅ`jC~TIDATNT+NN)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)EJSJ J}F7{78N܂r[P㛂r u|S1nq@榄 -(܂rktA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:\^pMASn -(܂rf[PnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*My ܔz7YA[Ps.(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7brym;7Nq((܂r umA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:r78 sSurd[PnA5܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTL./m禠)7[PnAN-(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7b ݔM }[PnA:r u|SPnAo -( YܡOJ ]ȢBN' ,89PP`!ERb(Ƀ4'Ӓt:RmVsC~Ƀj)%j>EJSJ J}F7{78N܂r[P㛂r u|S1ඝrk8[PnA:Ͷ܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTLA@):or -(\]PnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*&ܶsSPn灂r -(\[P㛂r u|SPnAo|RbBRt:yPP`)% ):<(C}@N9$ОɅ`jC~NT+NN)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)EJSJ J}F7{78N܂r[P㛂r u|S1nq@榄 -(܂rktA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:\^pMASn -(܂rf[PnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*My ܔz7YA[Ps.(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7brym;7Nq((܂r umA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:r78 sSurd[PnA5܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTL./m禠)7[PnAN-(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7b ݔM }[PnA:r u|SPnAo -( YܡOJ ]ȢBN' ,89PP`!ERb(Ƀ4'Ӓt:RmVsC~Ƀj)%j>EJSJ J}F7{78N܂r[P㛂r u|S1ඝrk8[PnA:Ͷ܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTLA@):or -(\]PnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*&ܶsSPn灂r -(\[P㛂r u|SPnAo|RbBRt:yPP`)% ):<(C}@N9$ОɅ`jC~NT+NN)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)EJSJ J}F7{78N܂r[P㛂r u|S1nq@榄 -(܂rktA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:\^pMASn -(܂rf[PnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*My ܔz7YA[Ps.(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7brym;7Nq((܂r umA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:r78 sSurd[PnA5܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTL./m禠)7[PnAN-(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7b ݔM }[PnA:r u|SPnAo -( YܡOJ ]ȢBN' ,89PP`!ERb(Ƀ4'Ӓt:RmVsC~Ƀj)%j>EJSJ J}F7{78N܂r[P㛂r u|S1ඝrk8[PnA:Ͷ܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTLA@):or -(\]PnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*&ܶsSPn灂r -(\[P㛂r u|SPnAo|RbBRt:yPP`)% ):<(C}@N9$ОɅ`jC~NT+NN)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)EJSJ J}F7{78N܂r[P㛂r u|S1nq@榄 -(܂rktA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:\^pMASn -(܂rf[PnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*My ܔz7YA[Ps.(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7brym;7Nq((܂r umA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:r78 sSurd[PnA5܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTL./m禠)7[PnAN-(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7b ݔM }[PnA:r u|SPnAo -( YܡOJ ]ȢBN' ,89PP`!ERb(Ƀ4'Ӓt:RmVsC~Ƀj)%j>EJSJ J}F7{78N܂r[P㛂r u|S1ඝrk8[PnA:Ͷ܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTLA@):or -(\]PnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*&ܶsSPn灂r -(\[P㛂r u|SPnAo|RbBRt:yPP`)% ):<(C}@N9$ОɅ`jC~NT+NN)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)EJSJ J}F7{78N܂r[P㛂r u|S1nq@榄 -(܂rktA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:\^pMASn -(܂rf[PnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*My ܔz7YA[Ps.(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7brym;7Nq((܂r umA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:r78 sSurd[PnA5܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTL./m禠)7[PnAN-(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7b ݔM }[PnA:r u|SPnAo -( YܡOJ ]ȢBN' ,89PP`!ERb(Ƀ4'Ӓt:RmVsC~Ƀj)%j>EJSJ J}F7{78N܂r[P㛂r u|S1ඝrk8[PnA:Ͷ܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTLA@):or -(\]PnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*&ܶsSPn灂r -(\[P㛂r u|SPnAo|RbBRt:yPP`)% ):<(C}@N9$ОɅ`jC~NT+NN)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)EJSJ J}F7{78N܂r[P㛂r u|S1nq@榄 -(܂rktA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:\^pMASn -(܂rf[PnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*My ܔz7YA[Ps.(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7brym;7Nq((܂r umA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:r78 sSurd[PnA5܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTL./m禠)7[PnAN-(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7b ݔM }[PnA:r u|SPnAo -( YܡOJ ]ȢBN' ,89PP`!ERb(Ƀ4'Ӓt:RmVsC~Ƀj)%j>EJSJ J}F7{78N܂r[P㛂r u|S1ඝrk8[PnA:Ͷ܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTLA@):o%Acsi 7t8@yꩧ?9:FC?y<u׿޹w;@}u_}'C?coo}Ǻ;{g{CG?c~:~su_)_7s1vm0>uoܜS ,Ly{nt饗Z>i>T:~o(h*flmHOJiF'Z{衇?mfsI?6P_Nb…ß5b7kmPkC?ck] Q(kv.gzn ʭr<0t=6&81F8ct\KM\R\EߘN_tiҬIG6)P&. ?|D>o +C!EoNp퐘cR\hկ~eiXw]tQOXϛݵ^kiXWn;3N:i͝Ir]wqW_=YmN?/y7~(W??uZpp bvZIwZW"ZT=Iw(04Pg0Ct] C}Xꃹ ]_җ}?>МkC}@zкoӖ^Ӗ^⃹j s&6T+Ck s\ن K JкV^uIs׽u݋_3ʽoXO?ݝviݻN\ {5tw- tk=IYgթNmR{w{Ī|s-t]wj#)7i@&պf b+KׅO9Ǻڨb1)P2%o#>e,DY)|4sN)p8 Z_zFo?{)M(.NrC`+hTx;ݮ/O}pԣs1`6\2շVAq6@|ouo8@Q!}3/r5TsNj)P9}ӟ KG_r' A)&}7`#<؃r5;Irł`vCni{Nh%;s-`|߇]X9X&;XPgL^ι sSP`aokC}0ѮRzt\bD|<'Xӆ%SОUm~?7ZiZm~P6mh_rmV%uG~Kۼ/r5^Y1So~{`{{yTϏ|#;=(m ?>Ovz><8F_q%Xϙj+rogڱZ;Lrr=ؿԯ4 m];Nr 6`r_] m(WL `ncAT6M\陊~P)4,W굻|S=P8Nq rk llcpn(w +-@;!Krǂq;6(WAlv CK)A0;}G.} ̽;-=e\Nrusu+_>۠ܡ?>Nrǀ_rr;Z4pUt,xJw^hZ\0w܆rPꃹጱy܆rо LYꃹ g]=w!6 <ڿ)B׆׆`l܆C}Xن U\ن K JкV^u sE@6۠\# 26ܱ۠`jrPwmP֤ƀ u]Ҡ1`ֿm)A:W.+g%ۇrǀvK)m/\7w,DYӺ5tZp;q@XA7Ŧ\͉q[(mcG\c4HruU WWrmPnc}(x&m\!9`Jj~pPKr0W&E@vvru0W 65J wvuw -} WS۬\3 e6l*>볊[>xX簠)7r^;ESp(W=o B: w!U%s۷V\ڹҤ]3qh/`vCShr>;s5Vlr3NrI}kAw}`0W' 9oc|@կn|4(~O%i]r!.ڤ@W7t㬍/|{ۆv8G&9:n){U(kbCvUo:j\ + CS->8Cy%ֆ`.8C*=>=?Qz(_Ssfsyu__ T/\ <֥x- wiW@nU6mSZz?JFZ 6)POQ6j mPxǰ Prd\1 ұ &S<\FsyֽOb2^C޳uLs{ Ȝ[p=j.(W޹hcNqOrWvW1[g &+UM4 ]rvWY);嶉w)w(T}YJƾRsus}(W; >*PnHAY~}(W5@qUW'> Շs=;SͥIr \Ga*o>۠\~*ύM +BAh#;s-`(up\AjhOw(pZm3T#({XЛ? ,ߤՆ`.8C5\#ň1qI}@⛴PFؿHO[zmhO[zm6ߤՆjfs\{Rmh_rmVI]}T+K њ>} AvTv ~_-4(W^xgj mިR\Fpq~pT0WW_7P<:U>)Pr=)Gs-ۇrxS\Fo}(h`qU(W|푫ǧ@ʋb۠\}?)(WP0WlDwq,7|(4\0׉^|pi稠sXI|rkj.S7")eDAǭwSE;5Uum=&CvU0$(MA혫: w,*'}ҷ!vCI q1o\ѷuc \}i'ضKud7vV$(WO)WPA`nf>}n&t$(W~{7\j] jWU{풫GݠO' ~c\*o$\qmWoڐŝ90TAA`A`/?YܡT9 hݮv=XdX9&Ն`N8Cusڑ]9Yw<hI> =%SО]%mhI J=͒6/6T+6K~PӺV^5ynӠ?~ϘIP<&m (WCZַKLR$(W0z9샹gׂZK袋3<)6&P.IP{uZ_::O} Nsvm`v ^ ԵO}(Z\{e+fBZۀQb\FC8h\age T$Agbc7w)45 z!-9֙VLq@y`n(Wo{O59hopr[NGs۷u4Zr $ۿi\DRU(w wC^gHФX?9W*ׇrAF5u^s]Ljg>3}]rX7UmҠv宂P/^:UmҠ\jjoPk_v75Pn&8UmҠ\t܇rW>; j*uPit< | 5+ YܡO pjnW>+x+ߤԆ`n8C\^5e/F( ,8I iN]7iK iK jĹ ojC~fs\{6K~P;uIQ/6DkFPҠ3 }ӛ0W}(W WWҠ\u ]sz_ۇr;;*7 mPo}?ho(풫Ǜoyos*ߤA6 ]뺏Fru}r < Um{$0Wl@mT)4(W9 rĽ^Lr_i1 sFܖaxr`ۣq\S]y ݔM }m=$wj ir:0?yCԵIrM0j2ɞMC)'ܤAki'[7P>Ikv"<\A |w>c@69&0W@nwqލ.o}SPrۇrK\}}(wʛ6Pr;3w~k^@᪚ c\MuЩBKt9,ljCyTJCcHvm6gFvj;NO{;GXp}PMAƺ)wn ojC}@z[M}@zkC|@{kC|,mnCvmV,qnCkC~fs\{.)>׆hݥM*+x{`^>-oy N8a t56U mP?ӆ' w3>PjUmz{QMѠ\NC"j чr_T(W9=rܩaVT(W5\ m풫!>PruY{p6Pn d7Jrр\= mC;j˿KHs&ʕVvb)ŝM.`(σv F!;Xaֆ`Spj|N}{SP`aL·]M:71b5 cr=P6. ׆׆`ӵY܆9Pߏ6/6T+cj K J$V^u6P< գ`6aƧ>YR\E7/½r5чroYiB`[ne* t^ŧBUM={*qhPSgF;E='(>Os&ʕV .So?7>zs  Ri\Vktpe=gC;gک;kn;uRiP;5g] ݤM|m=nFi(5՝rf\fds]p PՇ>)sf Pj^szt(yFl>ۍJr\$:ݣҡ\Mw/)??fM:]ݩqnTKwcC9jW++{,P`aaԆ0 W(8='Xפ>T9PlPmh=s1BK}@)|sX> =mf> =m!>=m!>8,anCT9POU%mh_rmV裡R6/6T+S0}T+K ѺKs%@6|[gO(Wѯ܋.ӎSg=\5N?Ov6P Zf\9Nrֺg97j5Or[m;7Mr:0w o(i( }.zr0 jGa@mIӜ|)Pn` rnXY(WjW}Hsؾo*{Ne7qYꠟKgvKr[s/&|(Wu'0WoHm6mZ߄oa-`m7asMԆ,P`a7]Â3T㶫&JͦkC}ppF3K_ȵ Sfӵ> =mʺF> =m!>=m!>8ڤmh7 JI~POY K Ju9yJkCf)Pnr_/:Nuϵ(W <ǹ_ \_{Hm6hg\<7^o Pn?W]gn=(Wuhl:k.U}0wLI;<곩l=[Qf@yBG|S^Trsǃ mǯ.;7n3AM>aݝQrsCц>F =OvF>0sB>|h?p׆ni!Pj<֍8כ9\ކ6n殍nkC`l O~qwm\{]Zp}3'o[mg׆~6?[7;N  շ8s;|+8묳g}͜Ptr){C7I8餓6N<Į~];N8a?7_e}86w=~R̜PvBTC }. ֆ, Ex6:?nm\gN陶N݉ K ޳Dюw7T,,6 iK iK iK .mh_rmVwmnCkCڼmH_hݵyڼo[6{6tֹ57{'mwؿ;S^sSO=uvi]u .͜Pڽz@['_кVzY|;w۵q%t;ծ#%_kAצkVmKm}KmKvЮmq{kNmrnZlzo5宮꺰nh-Rr~s_y:w-|ݺbZO׆Umh H5'5u|PN׸ CnuOʕWl͑[xw6E"P8EV)qq@fJa*{s(wu q76h#S-jCuhs$PjnbRC躡Gϟ $}h[7  ۆ ꃕ1d˽ۭVw߽?뮮 /~nκsB)m5~s?z~?wm\y]_~ye]ֵUj_? $ysBugOD}?ݵO~kD׆nѿin&o0Ff!.Nmr?uo}[v/hC?1Ʈ7 ];׾5yMƫ^W]x+6^wo~󛭺sB"a=*3t!V=+έ Y!kC|8xo\@!kC|`A덻9jC׆oՆ׆+-666!~!ZwmG6/}GڼmH_hݵyڼo[6/zы6^wm~sOkN(W*{{{~۽=vYo{;񎮍w]{ݝ ^wuBʷv_S5 k5/~]6هƍ7hfN(WtR_Yײۚ暽6ѣΏ2'+ 5 k5˶u6jP֛ԜP־>֫_^}кxͨ~]tN(Ŵ;e(*nf& u<@ r6EZÜL/(vʍҠM6¥Ar\M`Ƴ P>8(Wfuj P>;(׽qP\}ͶCZM66dqgp`,`!pkC|T8}@!B ֆ+XH\XXzmgK3f厩 ې~ks^u}tnC}D|CmH_zm]ͮens Pe]r*7 nun P n;+Hoۡ\wi]u=\+`^q@+M4(wŞ6¡PCws]r۷Pv]Єw}S~͜P>&씫on:շnvnNmr⊈rʭr?•t!'\SkC|@ A ( `a鵡 3 'F=?^1`pƘpf.pe6/6D܆V ܆׆hݵyڼo[6K)W&Nn喈ro6ĜPvM)Gj3'G낧sBZCL)mr]hP&7rs*f_Pn.0'ߺ0윣9\~.=N is;Q̜P_֍՟8ߏsG4[9\yg9pC4O?twϫí͜PɛOCc=ֵPFxnRA|'?)&}%Ws\u%.dQp0k^[u7dq K jz K 1smK ^Ӗ޿(p׆mhmj~ɵZiO;Zi_rmVwmnCkCڼmvmWs œ;c{?7[w֣~ϹS9z5#kYਆS ZC"kQ:Hk[кfN(WG[$k j]m(6tM{ &/^&T9dl`(D\7YuͭkYf5˦ fS}Ncs;6rwsCmk r[ןa Ow(06Î';X8lmc^ cr;X ,1O}@1y%RP`!AinN)qht >=)%`jC~r’kC}@%׆ K iKr> =m!>=m!>إP/7T+K JkC~P/6T+KrZI_zmV\rZP)({pm =6\rMA -y~KTҺ"XPnAS=%P\C+?S[P͂r u|SPnAo|RbBRt:yPP`)% ):<(C}@N9$ОɅ`jC~NT+NN)1T+):SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*My܂rEJSJ J}F7{78N܂r[P㛂r u|S1nq@榄 -(܂rktA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:\^pMASn m<oϻ~ZQ:4k:t.(NJ_=SG:F5ӟ6ڤC?~׵m<{^so~otmH}]{=<@wnWڸ{|3y׸O~򓮍뮻Ӹk5\ӵq7ڵ?߽;CG?c;?7??kC m|k_W]~vPog?YP=Cw6!Ϩ6FuukCw(6X[yFK_/<(ԏ9PP`aڸ́ n^-0jC}@zغv> =m!>=m!>tm4~o6T+ck K JTY܆%׆j%~l]{G~K|SdCZк]t _ܵq%tƥ^s=VmtZի?iw'v?9tL[[>ƭڭm.nmorMrۚԣ>:~nu<#]zs~sC W:O>㝜1Z֝׍~zo!mkz_uߣVSٻk>C׉fv.Ak4vqVC]6@yC)?H:~qT\K@"VӾ7:mBocmrӐ RTQ?ܖ&m:տ :d\y@H@Bt\7{챮 }Hp|*'}8Mᄏ뮻ǝwٵ_[7to}tj|tM{ 6.첮}{]fiFn8Ir_guVO|bo~N8S;SN9e|]'tR&ʽ _׾5yMƫ^W]x+6^wo~󛭺PioEQxF1dqg 9}{'Wڮ YܡsRjC} 8C?c&ޟ9>SBm6g5b밮ZsRjC}@zSŤoӖ^Ӗ^MfsߤԆjީMʼj~ɵZiwjԹ K JSyJkC&m^um/x_o}Lr ~s;o|c7i-ox[ڵ񶷽kxGƻ\AZK9s~Cj3ص/ Nm\/`o??6ꮍmh9Ir+Wh#67o§PZ x≮ }0D"+>}PY *O,'BmtL1$"mu9uq5beGgASnr\{pN(Wuc@CZ4(W5)\PrtZ4(Wmĝ=jK߰X(WtڤAW^?ruM+h@X(W7$A{ oxCc\zo:IrmM!^#;s/`htڌjC}@ZA.~P sSP`aɵ>97.M}@ꛤPFֿHO[zmhO[zm͒6S$Նj&iG~P,ynCkC~O6ZI_zmAP^2>h+P';}̪M{WtP_iP䎅r+~NmҠ\]ϴهqBXڤAұO@ $A[qd]4 ]L溛RiN(w uXT1GIF(Ȭ;]j-r^Anp.(T@\iXqkuJ@>L ?m6 wj(4 SC.̝)ܡ?:oHrvuJ(wvշӧN1<%;s.`>ۖrjC}@h0Cqw(B%׆`N8c0C'K}@SZmHO#uIXXzmhO[zm6ߤՆj&mG~Pߓ,}nCkC~O8ZI_zm6hoӆ6ra\ZSʽ{IP~SBKۀܩ\'c\nտr R!@{o3uaeE h\g*xM{)7sAs]d]r( Uם$$A~r r0҇rg8?|x?0w >x?􁽍OuC;?JoчrXnagڸ6ڮ -)Ν@@7t KnrQw^}|k_8s;|+P[(W7E7[N|;N8ao|/ҵkC?gƻrkS~B~x*VpBՓ,̹Vֿ7dq $PP`ahmZWSc=b? CsJ \pv:zXӆE%oӖ^Ӗ^9k 7iZiZm~P6mh_rmV%uG~s׽u]^x93ic=\=㻓N:io8S6N=ԮN;kCryu>uw]tQw󝮍~]!WD'9Z*u[n[OӮ rmi?%OS\˶u>JyJG];t]X7[(WZ^r'8d<3]J瞳kjzffb&G?|nl|SA:_>_^Kul 2R9sPa넝r*`´S3]K&c@5Jr $|1)P>\.cяMr@Q{CM {9ծ/6 PPܟ= [;+NU3Ɨv.St,P`ah>> Xh}hm8 CsN \p*${ ?8 , 16 Kj> =m!>=m!>60~okC~?60~ɵZiZm~PK꼏j%~!Zwin~{YC>gIPM}\ +m!(Wk Z{Z/y(W`m-cS\]܇~؆M 5>Oۛ&MU(W*+(w.sSE#p;:vsUPn9u m2ج~kgwn࠘(W7'c6U}R\}AP+M}(WF>B P9ܾgrs=e\o}PmMK)5EwZXIQ )Xꃹ"C:qXwbm3#Z9>SkC}@zڐM}@zkC|@{kC|0WmanCP$ֆj~hmanCkC~?62~ɵZIRyJkC&?(.;)P~5ArT:}|R\br;M/(Ww}>k0ױ Pֻ/\ ȝK/y(WDFfD\&@%67e6zyҪ븺uBǝm&ܔ:F (76PnXs*Ov'7|$@>{;$@~ !&χrtљpIr;iQ9zCwZX]dOr ;X16s;@XPg@x;I}@!I iCcR7iK iK \ن C}X!ٖ K Jl܆%׆j%~H]}T+K ѺKs(c݇rux<.U UW]Q(Wkr{]wE&}(7V{5ڇrS(W6(Wu9__M=) (_; ((vʍ@A_\rS|+շPn(WC)d\$Prd}xKH%@5픫o?sIrO<@ Q Pn+-Ԇ,lb#!;XzpFJm3VwJ+(07>x , MjmHORڿHO[zmhO[zm6mhPͶmh_rmVf[6/6T+CG>QLOCτ_CMr6(Wv"p  {0[TPnACu|^2dwRۂr Mr5Kx%@HsS>%@Ń>Snʇ(W7 =mH]tLj> =m!>=m!>60~okC~?62~ɵZiRm~PKj%~!Zwin^xm\m>+Hw{q\m sNZr ;j]<6frK;;!F@C,c -nNsWASn ,8IHr' Pl<&@CշMjvm`n (W79=0W7Rn%@\sM{\5uQ,̵ < , 5>P\P$ֆ`8#>ߤֆ!u1׆׆`lf}T+CS0~ɵZiRm~PKj%~!Zwinq_ oxCyC]կ }/y\+}}(W8ϝ {=썔 \qO>s1UZҠTP0hrz:2]ʕGi#9$$NR\պ?T^y2&U]NO=ԡE5Nr{{dusn>Wo}(;M\r%PW\Q(W9nj\}z1ydA>5b83F[bJOʓ , => $mXPg~snÆt(04P6.:.SОUm~?7ZiZm~P6mh_rmV%uG~4Ir}(=yޯ9ql 5SuQS\msWK|Iru}}(W_@=B3UʣA>lDm\Fy4v">M)WC Lr,#>E uvx> ʭr<04[PrS\5&a7(WuhIn)Pj @L)W7Bn@<>ha_oRo}[Pn&|k;/& Ѯˇ3j_0՟.z}C/6saڮiX I \pFx{\'uX , 96 K-6-6sfsojC~Oj K Jl܆%׆j%~h]R}T+K ѺKs(WROh7~;Umn5I,=6U-K%얛jMc1a7٠\/>>U-vMrxy0ʝVW 2Wϵ7\PF8N.oL/}/&A6|u.0& %A>}ݝNSJ]wݵwDPnvmR\檫Fn=(WO.'U.ѳ-$Iw\vmI,P`P gV^w>MZm3TOPojC}@zKb> =m!>=m!>6K~O|V{Rmh_rmVIm>~ɵZI'uIQ/6D.m\M}3P RP`&6s RP`î i.i׆׆`,ynC=MRmVim<~ɵZiY܆%׆j%%mG~4Ir屫z}&6W\q>+0W%}_Nu|+MmA?MrU]{キ;l07 Um~07 ]$(WK9l0Hrk܂Xs?O^oæoe8w}A\PnV_z-OѩM4 탹_~ ?7꒠6|ꩧw3td ц[$(W@4aAz7p7+zCκqцvMTպY]r{5}_kC7 4(& 9~GSS':?na'?ڸ: ճ ճ?ڤ@]v+(WN1kh"n_׺6կvַlߤ@&vdҗ]aHv YܙsCcnZ\OuQܥ^j׆,P`P g3v7c|sԎ>)6sUwnYן1!,H^i> =%SОYmh'I J=6/6T+6I>%׆j%%qG~K|SIQ;6 U.O|9쁹r5t.qwm\r%ݺ{}vU$(Wk Wvg?j[u͏htJjР\E 뽼nh'HyO%AZڪZ\S>{kyl AN]jZ wU|p֌)PƓ&`?WӠ\guܡA,ҜP4j45.(vʍ\PۮƬkcAP*;db&պ?pe Cn?O4(Wڇ>Dz$T7t#rچ~ĩM C~۸my]/u?yn M//׵ѿaֿn(W7u풫GLlO8Ӻ6N=NSN'|rI';Irqn6(}mk^󚮍WU]|+6^Wtm/7[uQ-՝V(,̵!x:{1; , =_> =of=G(04PP`ahm " =mh]R7iK iK \ن C}Xن K Jl܆%׆j%~h]R}T+K њ .Ů f!.ފ֠ںTphs6#smeZ\7)P@ܶvm`n{o@ n0 HzA+=]:v}e'\1%cXw* u=`Cn;ܡ;Pi(w FkjX٭gMZ];߸Kr7jx<UGXr&dP>8Irٹ_Vmvuw(W7TCڿ:+0WмS(W7`dqg sf Lw(0lj> XwJ zQP`ahN>X"X96 Kj> =m!>=m!>60~okC~?60~ɵZiZm~PK꼏j%~!Z6A/{ˬ{)P7٠\PKr;ncj(jtMA.@n庻@C\C\ennP[WL ʭr<4(wŞ6¥Av]NsBK)wN(W ; )w;wʝ]Ns.ONA@wʝpkC}D8)Bs96K3/GPP`aoWP6. K iK \ن C}Xن K Jl܆%׆j%~h]R}T+K њ<֝rrSPwʝʭrcn픻[;NʚQv-t|8 N𓷭Jr69\ijy4U=!p唝ru݃ۿ?sц[r~m6JR\=PxFgo?s's8Ť{wA|֍nk'?I׆~&Ikڣ[r /m| -}k_jukSrBC?Sֆ,̵us^zvmPg}9G}@9%ֆ`.8C˹|3G}@y%ֆuI-6-6sf6MbmVf6/6T+Ck s\uIQ/6DR6ԧ6 DNCuW㢋.Z;.⮍K.[74/pӔr7ܭzknm&ڹ۱);*EuG eGykC@vujS69h-'&ݴ!GwZaCw(pzzMꃹL<X2M?\pƦuy= , !R5͋9G|@{ZyRmVV5͉j~hQ'jJ<ZIOKrZI'y$K~whN)P|7y\ IC_+&Kr7yk@Cq)P&5}(wh{p)7P"uȹڐR>B!yQ6> =-Y܈hORmV=qܨVO~hQj>YrZI?k?J}G~NrZP)({pm =6\rMAgݒt)7[PnAΤ܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTX8 sOBi~܂r -(׹.[P㛂r u|SPnAo|RbBRt:yPP`)% ):<(C}@N9$ОɅ`jC~NT+NN)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7brym;7Nq((܂r umA:)(\7,P`'%.dQ`!ERb(Ƀ NN)1XHA}@zOR iI:\v6T+ιJZiOA~C~Ƀj)%j%>EJSJJ}F'r u|SPnAo -(MA:r78 sSurdsAyxt ?yz޿ M|sa(WӟhChz6:WP{ꩧ?9ur7|ɵ'Vn~׵1Tj)PnR=<ڸ6~_ums=]w}ws?7Mw7wmH;8rckkkڸ꫻6~wmG?~еxg\Ss98Ygյ/k_bƙgi|sgqFg>s?|ϧC?яv{{|Cw;=ykh\;wmo8c67wCrXwLOO 6dqg/}KErkCw(04PP`ahmtnzOCG}@9%ֆ Ck3gCjXXrmHOZM}@zkC|@{kC|0WmanCP$ֆj~hmanCkC~?6>%׆j%~h]R}T+K њ;A,n}FyhaVkHY$?gl\ds=mi  ?Cw?xvЇ6M {ZV70ڸ;6~_to}ïS$(AW^ye_޵qeumF[\rI_ܵqEuY& ?}'6N?N;kSO4N9q'wmtI{7$@17{ް?^w}kk^󚮍WU]|+6^Wtm/4tȩMm`G#;s-`|6Zu<C9%ֆ`.8կ~lqan , Mbm(06&q s07\ӆ%SОUm~?7ZiZm~PM⼏j~ɵZIZyJkC&m^um/x^fJro'}c7M-oyKFÆm]x׻ q)P@ڶ56>vm7h}豿C6 ꪫڤ@k֥?aF1mCe u$AM: 7o@ԇy۟:Ir}FS ڸ^NmRܣmPH0h";5xF1庞qYq:2]_;bOҠܥ;'Is( PZ7庻;׫v(w;ι3<%; AvZwÊ!;XӜp[%fNpŭ fN8Ch(04P6.׆׆`l܆I Jl܆%׆j~hmanCkC~?.>׆hM hz0n P PkvP +NN߽{ wWvK8NqXzw)P@Og)T[);J_g1W wҝQAS¶kg]XqS)PnR۠?=Viۦfj5WߴºS#Nm3Aɠ67r?я^Sͬo_HrRg}hF㗿zV{380ʻ ϒg4woۻ6B\=׾_pOI)wA샧Z}nm\ STo6}{\z , =_> Θg9ւ`~rJ , Mbm3qZ_9b9G}@9*2UmHOZM}@zkC|@{kC|0WmanCP$ֆj~hmanCkC~?60~ɵZIZyJkC&mP}Sn}Lrz'ܿO{>}C]'p vWp饗:ԿR~k Zchsм]w^5Ln)6)P u.ǟ'n܆vmr-{kG}s7Jrok;u bh[T`+fIP־mܦnmR܃vk<ҟkј>\k3y$6i޺)~|ZL^8 $_nrEy5UCprE0\wBrIr& 4:L'[E<r#[y?ޱϙաQv )PXwqGܓO>y>c3Aϛՠx} ڐŝ0F}{_uw ,mh> ZG sp(0V9jC}01>'74~P6TѿHOB瘣6 5qsԆ 6 sÐ瘣6T+CtMqL܆)ty9jC~?D$mhB瘣6T+C4Muj%~*Cgs/}KGJrH;SwG>=p@c4(w+(WCLrІB7|&(jns@cu6s+]c3C!^_}(wŋ能>o;VŐAޱM{N¦L@MQsLaym'8y-(vʍ@*7`@*;bMP 掙Dm & շmǂ6)mrU w9۶@Msj|UmrU գ?Om .bhw1!; M*/ Yܡ˜)kC} gSCXpν3umR sB.nG}@1ةkC}@z67ickC|@{6kC|^Ü~?4~PSmc? cةkC~O96ߏJc J=67JX4~s/掹-Pn\A .{mrG`vm`6Gq뢸mrE`)0Ws)mrUvCl \+V`LmU w n L R)Um -v50c,Fv9mo;}0wj۠\էj^o mo}Lk晻{}ml܋.hнMPj#0AzO כmrUO?}}<&6AMC;}ŝ զ!gnNUd8C\Xpr㦬 A2zB:n(MY\}cNY\}c⦬ jsXsǜ7vP߻ܸÜ~jt㦬 Jэ;̹ F7nP߻MٿVhtc Ѻr5mPvi탹_lܳ:k/n̽[\Q׭mr[m`t\զr5&(W5胹[\?_+ֱcŘ\\{mrey((ywiӡv̥0mMd4܉6B$hT;6(WC toCZ-WmrUnmo\AcmrUեymrU;n}_ۿ]mrUB4\ߐŝ%,`h'maFnmܜMUt8C5/&?4XsةjC}gd]SP`kLT> =m1So;UmhOoLT!>XBmknCsNU{WߘÚ~?F;UmV]}cknCnTZIwSߏOUu)s /PvG>]Fo۠\-W \pA?ѪͶA4roN`mrUij`#(f۠\iurw6(Wu{yi揩\"%jS`nu H-(7|!]s6B& u?n#m`>t9F(Wuh~av\~AcvF(W8=0NwF(+܇r[sQ6B nm0T56nP,P]0>1zC}@ߍ6K3V'KGXpjC}@z}cb7&vО6FߘةjC|܆1~LZioLamhoLZiύ= 1qSՆj%~1So1NUu)s?~}_oݛF(W~Wn#:{Pƍ7hf\զ{u.x\wmr9JrР1n#+VFm#kx2g^Q1 e49K*OV@6^rcUjV(WAHo+3CO;Mq[\գA5ש.@.@ Pn9(_U]r?[!;KY~WjFwSdq N>SLQꃥzsXsLQ%.M}@OӇ3P6F9ۿHOsuM7EmhOBSԆ`)9 97EmVjtcnCmlZiэ? qSԆj%~1So1NQuIs?,p[_PN>V(W^p.B<>{뭷Z(]rk(r4 -xչT̴2O{Xϥ@mKrSnAV] ]͋rF7 ]_;q;ߵMW;ߵ^PYYƗr">X ] ]߿ RB Rp uaPvXuS`a!>=m!>XJmcnCAZi?܆%׆jjss\êT>׆h]w-((~{+j_j[P 7NwmĐ]rXP }#. ѼHcMkb 7鷱UPn%@ʱ{M ]_}Aՙ@mNz/w+(wM?{IG}\wm)W~9|;7oqO?P_nfw}{U.=Z8okՆ,P`aB7~P,ΐGSjC}8CvУ{+Xpr3UmHOэSj7UmhOэ6KamviG~'܆1ةjC~wXshtc JmlTj%~F7~Kۼuۇr_YuN;m>=36?; ]^wrKAkj# عoN_\lNZCb lNb' =<-(\ZqX1q@a^7獛jSwU~} WvjÍƐxQڡyΩQ~ գ;6(WO>j\pꯚꛘz!C}rgx= AO()o}+otmpw!o-h>N{]%W`I'xVm ׾wιo h[u YI_P^]qJՒ,P`9Wcb A:NyiN(4&fP^z @nF;:>Ә)kC}@zn4Wߘ)kC|@{n씵!>HamhwϽ7emV]namhw5qSֆj܆{W7emV]}cT+c4Sֆh]r_wr:o++_wf۠\wG4*6Z[::H?;} ׅU"-a>zyVLOsQPnT(!wlcp.(wyѿ+r5r' IP&~M>?kr4$(W眎m)R0nS$(wDtmCv{r >TiAC7)tB0n]tQ~O@.<O4To~)K|3s8)P{"_կuCr }m]kt}Jr_DC  ܟLWmΜ nM-̸;W6dq oՆ`N8cLm7Xؔo \pzh:1fWn , MbmHOZؿHO[zmhO[zm͘>{⛴Po6 s\ن K JкV^{Rٽ%/^?h MrWkSІJN8amPWU6IP~?t_ꫯ!W)PqC{gy;P]Ơ6(W5$vl \]ԚACuӟ@bwxQk~Fh4(WR\I;Vu1@Y=ku=keƲO5qh̺ =LYz7\PFn1Mis@ruGuwS)P&D@׍Uw.g ਱a8*l@ 6)PnRCZ iCm;/~ѭm\=6)P=,V+_~ye]ֵ}kK.XrUozs9ݧ?'>ѵqwm觵8S; mmkkʻo|7 }׾v5xի^յWk۠ܟvm\֍E=EwZЍ)чڐ , )6s)|sJ}@I \p:{ KCqXWbmHOZM}@zkC|@{kC|0WmanCP$ֆj~hmanCkC~?60~ɵZIZyJkC&m㶡r5^җbCQ(W'ܿOozӛ[򖮍]o{ۺ6.zMtR\ng?ٮ}s]gyfŮ6(%WIrk-KQ؆vm?q׆ 6Ƣчr]rU(W@{[ԣVG#&)}@~զ;εF1)P~Ukm|'бFrP{Qm\1%G&w V1M+ {l;usךw<[;y`IP& mKr&$nj(W蘉e USC3@)\}8SmruSeLm<6 P?+|Lm\y k9=[=\ ~pl;zCw(0ω> XkXߌuZ C}X%cv9>P iCڿHO[zmhO[zm6mhP6mh_rmVf6/6T+C:ZI_zm*+ w=(W75|df()Pv)CKroɡ\m36 Jۘl3;f.t\wgmr6e)w.(w̵b ݤM|-ʝ*G*DZmr1@YCژoNk6B~elF(Wdַtf38cckPqi汵F(W ӗ^zڐŝ>ܟj{,P`a3  -؏uN(07S+SՆ` pj3fv>(07S+SՆuΘSОUm~?7ZiZm~P6mh_rmV%uG~s>ϣim+]cˎC}mrVmroI֤fEO=ԨPֿS>X WF(w*8HSngN ny)(vʍTw rU=A[)t >H?nGv&ca(Wl鍃~َ;ߏ)P~D?ע>WG']?~馞jS\sRܛoyԸkG=sA_}qw>$uQmS\:s>{uY]_6?):s|+~wLV(W y{DZi\;wm}4_vX}&@iEAwh!ZC?6ōV#3b.k0jCw(0W$ֆ`.8C73cu>P$ֆ`.8C~opݥ.(07> =mh]R7iK iK \ن C}Xن K Jl܆%׆j%~h]R}T+K њ<x{uX6>umz꩝Ou&պJ[WCZ7hC6wm|;4С#W-֥dp ]XaW_P]Ʀ)j׿~'x衇: ӆ{m躠g}v)P֥W\iК_jP}vYHkSqMrk8{}j@A? =6\rMAצ܃kCwviCC%(PP`aɵ>’kC}@%׆%ESОRmV%׆j~ɵZi_rmV\%ES/6D+KMA-({pm =6\rMAצj{y20ن)()h?{1(XY?!`uK>܂r[P㛂r u|Cw(C(Ƀ NN)1XHA}@' ,t> =')$N.T{\PߧtZiwrJZiOA~C~̓j%)%h>EGA:)(\7܂rTLq@ASn -(܂r&XPnAo -(MA:!;XpI YXHA}@' ,t>SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*My ܔz7YA[Ps.(\7܂rߐ ,8Ѕ, ,t>SJ Rt:yPP`)% ):<HOsI!>=-I .Նj9W)1T+):)1t! ):<(C}@NXprJ>BN'Ӝ|bhOKB|KZiwUJ J}N'{'NT+NN)1T+)BN' ,89PP`!EiN>I1%tr!>إP;*%j>EJSJ J}N'{'nT+nN)qD+)< -(MA:)(\7brym;7Nq^gG ?yWSNz-hhӆR\%m_?ݟ?;4چڨP\zyu(/ki<O<}nwm5T(߫ïqoWڸ{6?T;7nN7І+믿Ӎ!ǻN`5\]}?qƏ~];g}5}Gwy{_صqgvgѵ|f9?߽~ݱr{| }'px;ko}k1ӵ7{m ~v~??YC!;Zo}K_;cznmܜjC}@6}~o?' ,89)&6Xpks}l RP`aɵ> =ͭ7iK iK jĹ ojC~fs\{6 >%׆j%ޭKʼj%~!Z6/yK6^җvm|MBZ[ON:LJ>v/YG>_jt\jӟv'xbo{Cs1Z馛 /po{Um|_hz<\iu]VPz[n7p6T6bx뭷v=Uǐswv޺cƫuZ֧_[ԣ{muCqZKwHrGQ~m-|駟֍GUZ71H.ACǬzz\* }laVkCY$?g|&t=Yq=dAˠsCc.)6ѺM}@zkC|@{kC|,unC㛔P,ynCkC~Ok K J=Kڼj%~!Z6/zы6^wme/>c&BڠB77M-oyKFÆm]x;6w}o|Ozo|M=Fzo/qYgu9Ir~um?aF1LFChjҠ\iPP|H>? w4(WZ^l)`uC6 AbCaYcu^vM@ p:n1ANb7遂rk8 厽Яr7r鮸4u(WI}X\(W4$(Wyvt;շQ$AuSA& /1ow;Iru oxޘ IPاs;4ŝ0TI1dq 4P gg?n\/=G: $P g:L}@⛴PFc7iK iK ܵY܆{ꛤPߓ,}nCkC~Oj K J=K⼏j%~!Z6}rѦ8g}>;{UWY\hۯ.Nj^t|ڴ_:Ir{Яr:Ir׃)zonP|F7sܱVA:΄8 3(ᾡroNd!PuOI;jr|r ɨSrPp+@}uwLrvcj(m +8T7ruޤ@ַoN j\.Ir ,̹1NcJ,P`x96sSop%ojC}0bXP6.:.SОYmh'I J=6/6T+Ck s\uIQ/6Dk (W;ÒsԎM)W=z `&i\Z[hNj7IP^.5NڀƩM 5ն)\1NmR\寵9\LrU 庰|SAX(wNi{:S1b ʭrP$ֆ`.8c U1q\S , MbmHOZؿHO[zmhO[zm6mhP6mh_rmVf6/6T+C:ZI_zm]ۤ@wq/\7>3hACCumWĻcS\!us9{>/UKSN[(W^1m='?[W3 rW3fB8"]1?0UmRܶKn)w 9UmR\}Ce'ՇOF>Oy(W0nw2&ʽkvмkC}0jm]!QP`aH> =mH]tLj> =m!>=m!>60~okC~?60~ɵZiRm~PKj%~!Zwinq/r?яAC}7q)Pn%{UWj]T(W\w) އr'@& UuvJ$@M71fשn'c hX݁09>ߤֆ!u1׆׆`l܆I Jl܆%׆j~HmenCkC~?.>׆hݥM x bzn?z'B7|sDm\r<vֹJؔJ(wu33%s]_&@%a"Km[6x ܆qM@)6¹܄]runR$@dѣ$@Kn)W?P(W?Շr:6 PM7(M{'Cٚ.Ԇ,̵ȮCw(0'ֆ`8g? wКyZ9՟Vtc(0$P6.:&SОUm~?7ZiRm~PͶmh_rmV%yG~4Ir=}(Wۜ%@ZKFmhaNC;vm;jjhsjjmsNC;՚wMŎ׹Kr7S)rC=8V7} ʭr'4(7aG[ =IrS6w ʝ:߭q@fYPnAP~! vMO\)7C~Zr/xO%@QOzo}"j~[ߪrY Yܙk<Yܡ9Jbm3T>o|#:L}@I pFQR~ C|ZӆEǤoӖ^Ӗ^⃹j s&6T+Ck s\!ٖ K J$V^u6)P?r?EܷIrӚB)eDm\k)MuݶSܔ͗\բ픫xʆf PW$}'NCDsWPnws{+(vʍP.yrqP@ԓIB S2>(W7{3\]w607Hڜs9{Pn8Kr7=yO䄝r>Q{# #;s-`|K_zAmR}dq C5&ֆ`.8cM M}@I pPP`aokC}@zкoӖ^Ӗ^⃹j s&6T+Ck s\ن K JкV^u6)P}(WmR~PUW]Q(W_+֥뮈$@z_:FC PjO:W)i+M07 PjQPz8hxfιA=!]Qk'rPn?ʕzЯ(Wk&9zIrUIO1@Kr6a(ڇroC?齙 =#j~{PF. PjLwZPm s ,jC}0j@w-q,XꃹڑphM< CsO iCڿHO[zmhO[zm6ߤՆj~hmanCkC~?60~ɵZIZyJkC&ʕ=ė]v١Ory}(W5@[ײuMeS\Һw/Ʀ@$RrQ)TPnW3q@fm&n嶦&Aվu`nr$(aMrZH&A?xwg_~~Mr~_(W/kơ,̹/6Kw(@?$Ն`N8Cu/6K}@ꛤP g_SO}@kZmHO#uI-6-6sfsojC~Ok K J=6/6T+.>׆hݥM+};P$(W+(7MrUY[nP$A+ /6劙{=ܡ&sS\FO o!*鼵/@ASnr]MrU0VmҠ\զ/M|U+;&. ʕO|dr7iPn0+($ y{\/\\smC's WA\rɡ& Um Z@;<Yܙ{C7 =SKw(@L pB r)݇>|`]چ~LC7[׾{;o{[֮c9ko~sE1sB#_=ޭ Y! "]MSFrkCwθK-LT,,6 3>EFDzC|'kC|ڐM|ڐО܆׆hݵyې~yѺk>:!~>u}dnkmۼ%/xK_ڵw9'uzo{'7ir^sN8a|# _U9\=h͠ )vg?5DNm\p < >ה:u֖n_j[zlk^z.y۸[{͜P>/dmRkz{mͳ_#;#d)wႧsBꇎ!@mՇ6!YC }xmCX A> 탵Ɲwٵb׍]~އY6sBYh7B~vmhucqe}{]/k㢋.4 ~;ٿq Xe'_SnsO?N;kSO4q'wmtIvu|3' }[ҵmzl?I xk^?^Wumx+^ѵ|uA9Orpw%;еfS:~ YXXzm(<7 ^ `a^woX .6Xz ^6kC|@mH_zm]ѹ Kݵ K Ѻk>22jn~A~E/zQƋ_⮍eg9\zl4{{ umPn]zW'0׹7'+5n* z4W}W_*o jv_SjLzA5 ֭kYG}U6t~/S jӠ_M,[o(t߬DgN(WZhzcn@._;_\!^m\or?k8{пS *+Penr.OAAy?ULA@IZor>/宯&# { =6\rMAצ܃k Kp]"¦w]`` ⃂rٖK(B׆` jnrן[:!~>u}|swmnCkCڼܷٵy߶m A ?+0{n-(ܿ`nAGj[_&W IDATKASnܩ1N?>w;Nz!?#}kd 3=S9wج]jٍ>[~dn.s3tfdK='`?sλo?zιS>i?PpPLя~Եn]s+-DԹ瞻QsNF?A~Joooq]g~ǹ̹Svm?I?~Ǝ;dzl?Wvϙc.N]3N xr!;XSSErkCwP8C;Ny.#o(Pp=Sƹ;Pp K 3޿(5Q^2!~6/6D܆V]ې~6#s]m%/yIK_Ү ܛs\wtc>qwܿܿ?NG6K6s{w _=k Dmfqyum]\pAV̹S>mW[9נkS5ZV[c[7߼7nq뭷S9wn{ζ.oͶ_ >ݺ_+fΝrܪkۺZGnCNm)WϽs8p9 ʼnE8h8uQ;: to6ECw, N֥ ݤM|(w6¡;..sBK͜Pk3' .6sBK͜Pk3'ڐg,.t! K\ ,,6Xr];\Ӗ\N}@׆׆`jC~dP/6T+K JkC~P/.ʝj%~!Zi_rmr\>' .6sBK͜Pk3' .6E"PkSgx^ ʭr’kC}@%׆%ץ#C >=m!s] J}C~P/6T+K պK>%{#jn>웂rMAצ܃kSP)({pm nnhKv%8 sW _:[Pn{dC!yc -(fA:)(\7dq N>)1\BN' ,89PP`!ERbv Π> =-yОG|KZiO9NT+NN)1T+):SJ Rt:yPC|@{ZN'] JsRbVSt:yP;9PߧtZiwrJZIOA~G~ɣ܂r[P㛂r u|SPnAo*My܂rEJSJ J}F7{78N܂r[P㛂r u|S1nq@榄ɦrxuiϫ)<\ϫk6 4a_=jI5ӟ7?%PYzǯ>sϡyg'|roC:nPN7yw{7nN7І<}]{zo?yw뭷ybk>4{=t]vYwh|tg}${CG?{Aǯ>W^ٝtI'>[nc]}{_wq !zț/}Kݫ_c.7mzy~6 }l/:έ.Cw(:PP`a6o5깩(PP`a67:&_znю?SP`aɵ> =myYR> =m!>=m!>[%V76W?iw'vu_׻s 7}Z3Hlʕo㎽ܧvvޚ<6Z:C=4~חF絟~u_ם<;7 j^Q]?emo c[>Z\Z39q#` g@b26 6Fuq{9lxn"}ݩY~⿆\p 禠)7c/jbߚMV1E &6 lz߉/tOmŝ fk[5.%;XXmI>gKS{_{A}ߌ~(}i>gzЯu/(}i> =m:M}@zkC|@{kC|06<~>]~P܆%׆j%~j%~!Z6/zы:^Q6 ~^t}kޓkrux;1ϽI(W@Ogu޺roqsoUP.k @\=cMB͝wu]{CSEϣ aPnxI^zњz`&\t.ۘ$D'AcMBb'Fuz,MY1P*5~|s8 LZ& ʛz)B >ޛz>;fI(W9k"ۀܱuu掁77 Or8(^W} lPvmMBڡU@nrMʣGk}݃u65 Սj?z/?؇rudo ʕ?ؠs=މ z?sfSPm nc^{ECsmkl ʝ ]*P5%;c0VDZy*P5%;X翴P3Vw5{ל\{!wr}WݜTHe/y/)w3E}w}Y9wrg/r%0ދ;{mg{9wܭ' %:K3H﹧^R+k!wpWyKEJrR%2cV)דrHr/:-TyJ}K;o)>X.k=?^ܹܽ#-RSlbn~`[9tܺ;,B̻fxYwLKʝ&-R}|1k0뾜;喵Kʝ~!w?ORȹaQ8{I/{˞#ol=?_i܏tZ^RMIn~Vlly%OXiqrds {{rƲ}l)TXXqm(qSg 7}/u7 7G\iד#oiG_iG_ym?>kzoC׆fy䵡Yɼ?Yɼ?ڐGxoONzZR''?rǞ|}*~>;H'|uH#/o,z/)w0!w/R/5{;ރ{!wrp>-w|;=*n=ܭ%㬥Ogi{I7OɽI G7ߏHrg)*>^'d6.=nr,LJi"^7jp^RHrr&r7{IZʽϵ!w9-7}Kʝ>R~V֛>{IsJ"o'S;@mph/)[[JxNk^W=só!C)+_ʸg)C>C܏gr烽u_d)h I70n{_ܬMl ܡr6TXs/g=?nS<׆r7E^Sw_PaarĵQ7̴ δ ]kxoCѬt_{:64+ކ# J7J׆d={//|*厘-o/)wXNɝ_E|OO9=k/){{#ɤLf9-w{L^RZrg1Yusx"|8{I)#΁:ʷ_ܑܽ)wϪ{ܭ!厳rmxVcOrRR.kyIg)wSrd) >V:=3bAHߦIEP·;;o&)>~vni.odr燃Eʝk,Rٗmr~^frZϛ!i#.R>9v>)~;_R{Nkf]ܹ=u-)Zx6bH>!<5>YK`ds'xsg",*,,Y6Tθpϑ/ K# 39ֿ7ssjn162Ӗ{qM9 3kC83kC8Hy}4+ކ# J5kCyMy!Y/xS1/yY^Rg~g>gsF[N)7Ork;1י_)wٳK^RG-R}3)o});Cʝ}w>[3re=ܑ?g=)wɾ]/Rn<RqJnJ,bnCfY`sYlrO]@½mO')>uB l">ċQN%Y\Ko5%EM%Y6k)7=]u)wzsiEM(CNCEʝ]͙|<~;RȵCʝ߄]K#&[3B"厘6RZʝo͙|H__prE}K^Ih"~}\x{H{ N@8ks#;TXX%r7<]yR27 Q׆r7R27 Q׆r@fr:)d}mt}m\ƹk.fhV: G^kzoC׆f%f%kCyP{-~G}TY^R^Rg}gE}k)w>3&!gLx+^-~$HC oxS)OipN#.{Hk)w“xn͈;;{k)7u]W^7˴{H{n߬"n4rXKև)>ןS'LWUIu Ms&'^7i}7$k1ܺ!cM07ofrM-]v)wdTCt-n!rr>rYu-㟻C}TʝMŤOR3Z}[=RK_ҧR^O6}kJsZd['6K#,'!kҏܹo9)w)wUz1wNU6y{r2X*-͝dRڹkVvds 8G]A"g\JZ#~-s&Rpa4}|XA"g\J'k:_:Paaum(d-yo)Rr@f׆p@g׆p͵hV: G^kzoC׆f%f%kC͗BʝLOOCRȺ>)w)~,^Rf)w=yR)ڌJ_7RnR;kJ=kZ\OʭcR^rD)7Y=N}h)wG"IqRCKp)7=srZʝBn8)SS<ƓroJc<)R9ƥNC9#:)G3Rr@9#]TPaAH׆r@fZܔ2ӎ6:ӎ6dm.,t}4+G~GyMm?Ьt_{:64+G4+G_k)>NDI-iۿ?'܇r;I-|6{RIÖ܇rH1{SMr'-hzRjzce2G\#HD<{Cq}Rn6UʝZcrIJϝ#o߾]:)d(isJrR}E??F){{sRȏDkܯ;-oz"9ڌ@HA6wP;XQixJ9CXLZE9CZi% \j~SL;L;y)!y}4+ކ# J5kCyMy!Yrϴ_9)79J{랞{zRRwQyR/οsT6QʝY;w6JJ 7%ŀR'1pD)7}޷;}JNf淘R.CʝǙ7z&v!㬥\R|R??|rܗ) o-ck8)OON#onϕ|PC}qRixrMo:q3!·3~OA\VZ}[R_?^D}||{|Rn;{\iz:3r==yY{wof-Rr{ӯX;>AoE]㕮1aR^ {qzd /7OMy;}vF$ZȝMj})7}|n4ob "Qt]yO !Λmrsk>)I {HZʝu:7gu)w~OR{H??znR;rᩔ;bگE6#p~~C>C\=R>1q6͙|ZxM3s sh>ĩxds KKz\jm(q7}<>um(q>9ϧEArC>qm(1sS-;r}RnzYyK%l"%MIOyP9˯Q}uB?~ۥܛn,t&R=cKw=7#k)7};y|M=G\K${I.II[z͞7H{IArRu_c9z)w̟Y7MY9;'o؏X8{HG~GJ__D*N?f>P\Nɝ+2~=󒗼䩘YgL_Hqr'MA+^?=#?ܡ׆r7>W7)мIr@# 3f}n [^ٖ_PaaqĵGߔ2ӎ6:ӎ6tm G~GyMm?Ьt_{:64+G4+G_m>?;rw}wEM%]z;~PSr)wmr==)w9$Y5#ZIJ;,'ο|rJ;!Nٻ\ܹDyJ==3"cgz^')w^R k17<RCʝssʯsytmrﵔ{b|872r; ]ܹ#.W}W]\!/sܷ-%FrZHs]OO*.'f/)?#)M!wc͝tcr{K [$lPaa}o6Tθy܏YK|zy-+SpsĵrK7f *,,qm(d_8)d}mt}m[汿hV: G^kzoC׆f%f%kC!>J/x Arz*~G~b|FٟOO)w~~Kr.厔;׷}۷Rmygkr)o|c6{I?O9c|ҿzLgYKsN^Rk)>9hi}B^ӵKnR}rAHf/)wdٵ;ϧSf%厣qS̽71ksSPRRefyu 72TH/-^E6!w0ro;-7^7aހOȝEPS]N]E"/k4r?ߞﻗ;5oR8?RK]ؿsi[_[|eHyۤ٭.\T!еJ[)^"nH'^R2to&x.wl=Z];y(;P0u=E̝k~X031c-.Rߞ^r~~i;?Y~r ;?Po;b|h4$= y_ӛ'M)w~SFgSqr뛾雞Ooxȫ#.}=9-g~g6͞RsZZӋ_'arYqkNȝk"7{O)pd.*Fm=6dsg?pϘ{J7|7<9-wrߏ:}~i\ַ#.ל;+^Mk;k׾)s\{N~Q٫wN!K?SʝǞ=s)w'{$T.=9;x[>Sʝj-)q=NzάYroJto:)Ξsgmwuׇw͞Rpsq'az%kwZʝuuS]zM1wKƽz~%qOwZ%a@Ik'[^}PYʝo!κEdI[<-̛7nqDv2"X׸a~LR׿wo-?,bMwo}M[ʝ~֛R|mbሁMw}R|k;ΟYrkNk>4OT;kA~'~t:"䮥ܑ|͞R:?Zr}7~i>D>3K5\窾4M)wN޺6{JOO)wܹ>?ii9vh>uZg/zыNMʝ㶮͞R:kdFy]=x:͝2䙼ܶ.g|__ ܡm(kC9*gZ%.-f][_k(TX `14.nZs {zPa&7GZi=4)d}mt}mXކ#Y鼿6tymhV: G^o f-/Rpe_eF1wxOs_eOOxrݔr?s?4'nfo)wzo?#~~ieoaPfgg5|0E]K??i]Ͻy?)^\'51{JW/k5xǓkdZKsfvO)wogmsG]=Of fvt->|gε㭥ɵumr1wܛ@;gsN[03i};bad3C"&^Bȝ[]>bZ]>@׿;\IrϽy٤'r".Rs-OKkroJ#]Bȝ>r1GyT|K'ܹ<]'뱮;}*,~\BΘKps !wyM~fCMֆrUΘlv}~Ppem(du_2)d}mt}mZѬt_{:64+ކ# J7J׆dm~oHYȝ{R똽'|ݙ'r`޳kYۤKR<Ȑ#:ߔr9@ZۤKR< \m}E^oʸ7K=$'LX2|/O,&.v/!-c#ݻrϽem\mlp@ٶ@RR}_ :/!.> ܔr/ioNu7o~}^Y缹LnJ}F`>5%NoZHS;zNsfr=o{۞|б#r7?}<5--OOɝ̶H0xNs!k~x>TON>'9_R5_Zl>kΥ60࿴,7%ܡݛ\JΘ5#q)kzoC׆fy䵡Yɼ?Yɼ?ڐmFʝC.CIӜ\ٟOi;̳桤|i藐rG_ [!N]{s9w~w.R6sx>)w;r)tz|)wYyvEʝ}Kx K{Voo>#)w~t <.zRw?AKܑq/).R0ܾo<}7}97r~Lo%.fN_?sO}ы^twiCIK97–?~qK6w.0wh9k3k;k|inum(3u6}>}ڑ/K# pӺ6Knsor?)TXx7kC9 3Y<[7̴ δ >汼nZ׆fy,m?Ьt?km{:64+Z}4+G_<05{9sOܗOj݈pz<8t_;2 {Irz{H)wY٫}ًJN'{}+rgȹ>KNwW9r=;=ξ.ȹ;2f>J]?azsݛR8<wn)N<{{Iv6Et~ֹ9!IKK795*׽4Hkfy8oͮO17 R n?1N2u R`\{N˵9aw>oCK랆@dZY9aw>桥I{|6\__ZR}k@<Ͻzyh)wk?>ɇs}'>>E_Eyh)wk6G [Dn濏4_^Z\C6wcce2N|އ^CzPCXofΈbfT~(TX ܴ rz> r3/-=Q@i[id]k7̴ δ >m'ܴ J=YkCyOm?Ьdޓui|Gy!YM5{ ?? xskN^%^A!sA]u2U׿?ӧf_ .=͉,r8 ijwCK{9k3j^Fb^Y~5 RzmFvrc}M\u?-{b8X7_j9]XK?sx8B.u.-h2y2ߧv O)imܦu^ܶir֦Im[&)mmܻRj}{W)?z͝xjm(MPSθ:^Pp>PSx*,%PL#}4(iG_iG_5 JY鼿k܇ZP6ds K9%z|A9CRp{PP.2.C~iM83= \Ьt?=4+[{z̶iV:4+mcӬdoiV2:'YCE}5Rʽ{oA)Qʽ{mr^ܻF)Q&S>{ ݳ:!Sd**zR30?zskd~9߳krr6rrnrl\pF6wpRC9BKΤZj(TXhəA9BSK ̴i2ҞZtdL \Ьtާf!cJ}WCJ}Cƴ:|[zYɼWC-J}Cܻ?UUMWJJ 7JJ 7Jw 7\\Oʭc@)W)W)W)7JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘpIpIpIpceS2Y¯\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUMF/q/r=)\\\d**&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5J{1P'd})***&JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcM/R'1 [\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUMF)w/̽8O2\\\5Z)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrn۽Q:rrrrapppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(@WpIF+*&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5c7J[ǀRRRRn2lrrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ܽ2 >ɔrrrrh\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUM|lF)דrPUUUMRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&XuB^}'RRRRnpppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(zRnJJJJɰUUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄krbN+$SUUUM^rrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ƚ^_\Oʭc@)W)W)W)7JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcR^ {qzdJJJJkRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&X >{Iu (***&V)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrnQ݋:!s>NL)W)W)W)7yVUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄kz}vor=)\\\d**&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5J{1P'd})***&JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcM/R'1 [\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUMF)w/̽8O2\\\5Z)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrn۽Q:rrrrapppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(@WpIF+*&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5c7J[ǀRRRRn2lrrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ܽ2 >ɔrrrrh\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUM|lF)דrPUUUMRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&XuB^}'RRRRnpppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(zRnJJJJɰUUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄krbN+$SUUUM^rrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ƚ^_\Oʭc@)W)W)W)7JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcR^ {qzdJJJJkRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&X >{Iu (***&V)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrnQ݋:!s>NL)W)W)W)7yVUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄kz}vor=)\\\d**&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5J{1P'd})***&JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcM/R'1 [\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUMF)w/̽8O2\\\5Z)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrn۽Q:rrrrapppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(@WpIF+*&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5c7J[ǀRRRRn2lrrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ܽ2 >ɔrrrrh\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUM|lF)דrPUUUMRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&XuB^}'RRRRnpppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(zRnJJJJɰUUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄krbN+$SUUUM^rrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ƚ^_\Oʭc@)W)W)W)7JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcR^ {qzdJJJJkRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&X >{Iu (***&V)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrnQ݋:!s>NL)W)W)W)7yVUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄kz}vor=)\\\d**&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5J{1P'd})***&JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcM/R'1 [\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUMF)w/̽8O2\\\5Z)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrn۽Q:rrrrapppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(@WpIF+*&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5c7J[ǀRRRRn2lrrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ܽ2 >ɔrrrrh\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUM|lF)דrPUUUMRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&XuB^}'RRRRnpppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(zRnJJJJɰUUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄krbN+$SUUUM^rrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ƚ^_\Oʭc@)W)W)W)7JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcR^ {qzdJJJJkRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&X >{Iu (***&V)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrnQ݋:!s>NL)W)W)W)7yVUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄kz}vor=)\\\d**&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5J{1P'd})***&JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcM/R'1 [\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUMF)w/̽8O2\\\5Z)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrn۽Q:rrrrapppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(@WpIF+*&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5c7J[ǀRRRRn2lrrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ܽ2 >ɔrrrrh\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUM|lF)דrPUUUMRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&XuB^}'RRRRnpppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(zRnJJJJɰUUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄krbN+$SUUUM^rrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ƚ^_\Oʭc@)W)W)W)7JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcR^ {qzdJJJJkRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&X >{Iu (***&V)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrnQ݋:!s>NL)W)W)W)7yVUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄kz}vor=)\\\d**&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5J{1P'd})***&JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcM/R'1 [\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUMF)w/̽8O2\\\5Z)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrn۽Q:rrrrapppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(@WpIF+*&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5c7J[ǀRRRRn2lrrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ܽ2 >ɔrrrrh\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUM|lF)דrPUUUMRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&XuB^}'RRRRnpppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(zRnJJJJɰUUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄krbN+$SUUUM^rrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ƚ^_\Oʭc@)W)W)W)7JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcR^ {qzdJJJJkRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&X >{Iu (***&V)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrnQ݋:!s>NL)W)W)W)7yVUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄kz}vor=)\\\d**&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5J{1P'd})***&JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcM/R'1 [\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUMF)w/̽8O2\\\5Z)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrn۽Q:rrrrapppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(@WpIF+*&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5c7J[ǀRRRRn2lrrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ܽ2 >ɔrrrrh\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUM|lF)דrPUUUMRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&XuB^}'RRRRnpppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(zRnJJJJɰUUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄krbN+$SUUUM^rrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ƚ^_\Oʭc@)W)W)W)7JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcR^ {qzdJJJJkRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&X >{Iu (***&V)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrnQ݋:!s>NL)W)W)W)7yVUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄kz}vor=)\\\d**&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5J{1P'd})***&JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcM/R'1 [\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUMF)w/̽8O2\\\5Z)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrn۽Q:rrrrapppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(@WpIF+*&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5c7J[ǀRRRRn2lrrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ܽ2 >ɔrrrrh\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUM|lF)דrPUUUMRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&XuB^}'RRRRnpppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(zRnJJJJɰUUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄krbN+$SUUUM^rrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ƚ^_\Oʭc@)W)W)W)7JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcR^ {qzdJJJJkRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&X >{Iu (***&V)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrnQ݋:!s>NL)W)W)W)7yVUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄kz}vor=)\\\d**&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5J{1P'd})***&JJ 7JJ 7JJ 7ds I?-5t# -9>(TXHzjPa%g IO-5*,LTC83)g ֆf>W-54+-9>hV:ZjhV:[r&}Ьt'=Ьd޷dLYɼO{j#YoɘppppcM/R'1 [\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUMF)w/̽8O2\\\5Z)W)7F)W)7F)W)7lPa!駥ndQa%g IO-5*,LPa!驥r@I2Ӓ~jt5Lz!\Ьt'f%gJ}SK J}KΤi4+iO-u$+->rrnrrnrrnrrn۽Q:rrrrapppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(@WpIF+*&(*&(*&ܐ*,$Ѝ,*,LPa!驥r@I*,$=PВ3r@fZOS δI/kZ^Ьt޷LYOzjYoəAyRCyߒ1f%>d%c҇RRnRRnRRnRRn5c7J[ǀRRRRn2lrrnrrnrrn~ZjFZr&}PRC9BKΤZj(TXhəA9 3-駩p@gZSΤ5 J}rZjhV:[r&}Ьt'=Ьt޷LYOzjYɼoɘAyRGyߒ1C)W)7F)W)7F)W)7F)W)7ܽ2 >ɔrrrrh\܄\܄\܄CEI*,$=PВ3r@Zr&}PLKi!Й֔3ppMkCyܫI4+IO-54+-9>hV:ZjhV2[2}Ьdާ=ԑt޷dLPUMQUMQUMQUM|lF)דrPUUUMRRnRRnRRn ܡBOK ȢBKΤZj(TXhəA9BSK -9>(d%4LkʙB8YOUK J}KΤI4+IO-54+->hV2ӞZHV:[2&}(*&(*&(*&(*&XuB^}'RRRRnpppC6wRC7В3r@Zr&}PRC9BKΤiI?M5:Ӛr&imhV:{RCyߒ3f>驥f%gJ}SK J}Kƴ:IJJ 7JJ 7JJ 7JJ 7(zRnJJJJɰUUMQUMQUM!;TXHiYTXhəA9BSK -9>(TXHzjPa%g̴iM9^״64+ɽjYoəAyRCyߒ3f>驥f%%cJ}SKJ}KƤ\܄\܄\܄\܄krbN+$|{k, .}]:gmf O55oض\KK懒PY^|?8{O5>5yO;s?óf~N?/|g]?tZ??8-|(qt5}v?pZ__?i>@;OS7k:|и\ۿ}77^qzwZywߝn=yiz}~מ^zZ__9-//ӿI|4 p'zͺ/ϜxiF ^m{\o}[O5\oy[N78-כri7~N?z_~<7}>S?׽?O{z}~i{\}Z8v}i۾4lZ.k_r}˷|i雾\xZ_r/N5_5?O/ſ~6}?O//yz8-l61Zecj",f9_99Obf",|ȇ|ȣ_e}6YEX?2O5:gmsf&9k|ёsn 7:r,¼N5K֙?2ӎ}osS6}5m}}osM{kzGkzX۬?k\|.9~~˾N_\_K9w>?~:î?]f,ww9m#?k^lz9?O7 roN5g>??Yկ~ןYz`kXA&{wk,,//n^Ktyo=_ux>^z}^z}^ֲ5޶#[߽rrMsqyNc̚/Ox>]m{{^9{{;v͞?9^gt+ݟ^Y{w}۾uZ;˸ XǹxqnMe\f~=kippR׉Hcu}ޛg\Oʭc }ޣ/Vzss:F,yHwq}֛L?7_`pPϕrg gC]?` [vhztxXI{C:95?&?G]p8Yt-?x׻uZAr?(Y>LY29k3Ṗ:爠??yZe"]爮?çZ@O׿\IyZtFʝYYɰ:_לZ˜__{Zm\ˇW|W<<-]v苾\__pZoF]i^җk>Pܵ)S>S^ItZOO<-'|'k6c?cO11q>Ns}G~#>#Nr}~ifjCkrZizFʝ#fHr-?uYk3ó!&e̓scWYk3Ï%9[g̣sc79m ?}g&G^?i}rg5/<9cugrן?]>ן/?S^ּ z3gr}ڧ}i^3>3N˵|~>ݟ|7ϯ?_M^{ =zbX=~ȿW477?,9k3{Rkz%M{MH{S=^Z^{-mr졭鑑k-H&E2'#\{}`zo6y:]zO.ygws>BZb^o/}]=ZRPJp|b2(e#"s?9Wʥ|}L]$51|9Džɺ8w gK[:!nu/=Ͻ>g(5sj}47o غJ缑|p3g凒|oo=)w\nw۾p>8wmo?rs?4|p6Ox'is*955sZ4|R q0sIusCs3Y:7scPn=lfݧ{su  Root EntryRoot EntryF@̣pVisioDocument 4SummaryInformation( DocumentSummaryInformation8  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~VisioInformation" ՜.+,D՜.+,0 `ht    Zeichenblatt-1SZeichenblatt-2SZeiger (1-D)2SByte oder VariableK Rechteck VaFunktion / Subroutinemm Umgekehrte geschweifte KlammerDesigneffekte.1Dynamischer Verbinder KTextanmerkungrb ZeichenbltterMaster-Shapes0|_PID_LINKBASE_VPID_ALTERNATENAMES _TemplateIDATC010498411031Oh+'0 X`lx  Microsoft Visio@`p      !"#$%Visio (TM) Drawing 4LZ3cR}@}XAwH}@}lAwH}A<u+ !fffMMM333Iya͋V׻ę5@@@FFFG}oƒUJ:DT5I[1hXT@. .Ub##/am0U\?dK[&/~&b%2~ ! oLh $c)P?#~)~,,J & "& ,i 4&?C"/.?L?\.?o A  },,,'p/%O6$X6Q (}j?k)?"   *I3#oC=S?#A:;A;dUdU@;*dUdUdUAOpNOD`;dUdUdUdUdUdUdUzUbS)R~;4OL%=Qip;RRRJggRph=Qj .@]E' %p3Ԣ~|bFp#| | | ~ui??p9[!i fTd',50fZOlO~C.ߐE#PQc:  A ,U345&6&9Eaaj6 8d7?!%3 URg L-ak@9$KtiQHeuDDT(!cp;/paM-uh\`(+AT %5+H>>5a%h34ovJuX0Hp(!MmcP=?`(z?84?D0pA D7!U/i 4?Q`_1_JD;a3(/@}_L/^/L%a$G[aY %//// ??.??R?d?v???V[l_ru@+y_w-uaDd/r*&oo*o_Qv4DOOO\U$/@__o/DA/Gj:d2(joG2O5j7@oo ooo?&s2;IO[Odd 6 S( $b4@SyYkO(Or&̗T4RDP@+&pqprg-.UEd \UhřϽϏ____"LwnU2r?GXDeTeP贁Nkcc#E_挿¿{%ܶ/o Mv{&pqo s'9K@]o߁3呧G(x,+1fߣSBuD'l*|l$6&w-n!1q;qHVN`amF"%ȓz䥍 / /@RdvMIySe-T"bbbb$T$bb~9bIbyb~9b3c'쩠? 4<}J~>?!OXOpuOGK59&O@_Ǿٹ(n4Y--`e7"At4"H$ @Y,b'@ F7A-48E7 ;t4"H$ @Y,b'@ D@A-8E#7 ;U!U"#$%t4"H$ @Y,b'@ z-7 P&'t4"H$ @Y,b'@ l@A>- `7"AU(+,t4"H$ @Y,b'@ =-Q7 PU/0U1234U5678U9:;?@UABCDUEFGHUIJKLUMNOPUQRSTUUVWXUYZ[\U]^_`Uabcdt47"H$ @Y,b'@ rC-7EW AUvefgUhijkUlmnoUpqrsUtuwxUyz{|U}~t4"H$ @Y,b'@ _C-7E At4"H$ @Y,b'@ AjA-7Ex7 ;z23456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd*t4^"H$ @Y,b'@ 6C-7E A;X4@H@RZgIR@4RZMR@QZ-MR@F@TQZIR@[F@t7aPR@8PR@8MR@49RRH<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(4E}ElRZREQZREQZREEPZREE7RET8RE8REl9 RUlUU&'/0 e4 UFD# Th(/TYTYBBUF~?x<F BP(?4| P?+ O @HZUBH??6*!v 07IB`Software,SpM icPeK ,>bjM k.E ,ZU"g]" e^ %* #uo a? /75"Auf da]sBlt zieUhnudUnzrVGrbgWvo)wiDtypBv7V,Qm&.zWg 2XnZ2a&Hrblt.HD" #  B>TL{&LC?:k2h>#8b #3 ABKAUF~^?FbX?FO~?F]P6 $A >7u`?-7au`b?A@a7u Su;`b]u  -"2>UF`0 \.A'1o','y/%-'-f/$>$M&d2TFKQ"{Q{,;K^?e;KuUKuU,"#>#2N_Nk?<@ a,JU3 AKC2 C>3 X>#AKASG;/`Vis_81.chm!04B70HHA`HABȋ4%4& 3D6 :L)%nX_7 aTAQQA2(SEA EM)V7C&L@nҪ@eAPQdR }fPstlPQgRKBB_b))%gN_5u_V( b2(6bT,@KA5"l'U;hmtibil#2(A4>$-69{[3bk3(3u$>L{x6c|qHdbgHUK s FI~ƹOWEF3#KpB U&a`o@$7o_PUFD# Th(/TYTYBBUF~?x<F BP(?4P?+ O @HZUBH??!t 07{B`8Byte,BiC ,NK bU lE"KY"i;nG DaC"h!TA pG SoUfC wm rE"S{ d!cPe ,>bjE kC  e^| 7  3u ,7=OasNDas Shp}eaufudZicenbl]t) ze.HD   3 B>Tp[/Lp?as&;.h> ,b MJ=UF~?FV*JR?F\.?FM&d2n?Q6 U AMA333u` ? !+5ChMW]u$g>5 L@X!$5 /`Vis_81.chm!#4E"67M `_!b-"#"'?9h:AiA 9 $''2ǥqE0&!$E0&!qP \@6%A]Dc]3V:?Ka!Db1OCK%YEiO{OL@ODJv%YKOO"NBSB53^@M^U_g_y_Rg@14ot'2TALd"2(Ech,@!3qlf1U1 m3 =M!l+TM9M]a6 `($T 6D UF~@xTiC;'h>4bP9 EUF~?L&d2?F\.?Q6 U AM| ;;u` ?*#)3=KU_euo>5 L@X!#$5 /`Vis_Sba.chm!#57_ 2#`$g!-*#*'?9  %&/7 aJ=?Iy+#&Z&P?B#2N贁wNk?@J'q ,A MVq6q4!*#ekab1; ?Eq6 I-BCb:@e2C1%>> OO1EA5~%5]NGxOC9AOI%\O`JOOrn_gPo|'TAFd" (?cp,@&!iA%9 -$b]'/2q`&!gE0f*laUmHE?KlT(]9^]-Ma6 `r (QJE-Os~%_s%/\h}Mf!>zbcrA bb Hsr ~L)uEW/>Fį#,KG$JB CS]Ba,`]@Loj]CPUFD# Th$/T YTYBBUF~?x<F B_P(?3P?Y ȅH?BT ?2~B 03B`T9>R4@Xflh>, bE=UJAUF~?F#H$??Q6 AEDA33333#300 0 0 0u` W?!+5CMWaku@#u>U5 _L@V"JXh#!$5 /`Vis_81.chm!#4"3 `!k(_%n>J-h)_##' ?9q!0!!g}PVh#@>J5A,aG"bCV"1_%>?O!O3G!>H%;iO{J1BCB6CL,155OOL=A_YM+^|>!C_U_g_yXXE+____yXX3;?&oOZe 1O3uniSoooUo3(2Oo.oORo3H_oYo`bO&I5rn7I[oEg 7TRA?"(8F,@!$0l#1U#H3 ?k!b\(Tm9)xB >T*a$`o@|ooPUFDfP h VTB UYuU??Fxha T,aaUQJ\UF BP(?@?F~?$-?P nL#l]dV^ ] $g]Q"U"Y*"]>"~#<?&#A&Br&C"u` ?PYu"bl""& M&u"u&u"u"u"u" ,u" ,u"4,u"H,66t#\"u"///$u)ar;21'u `Fxu21yB}BAABA"!^hO$,@ZLupO耑 @Ca4R#qt{u*gvXWcZufvvWuTj*uX{u ]ʤ *aGIJKMNOIQ"R$QTܝQVWXYC3333.3>3N3^3n3~33333333@33Vf4b@Vqf0φЬ uZ1[]^s@333'373Gc ƢEt&EAxÎ;BA}@c"3"3."c Ӣp x/3,S/e ~/iRfSIA62ab ZaLxVIǪ *:Jzךת'OgEa@braĠqAQJ`FWO@r ЀiC}F@`I@)@AEBsZeȓ3U ,puӐe(p 4>{ScNV:fcfR:bWKqP?@pA3VUbQw Sio@(h#FtϿF@ qRfa+_i&7=akVQhaRkQclQohaR2brˡF@#I~rW{ 3~TE}O bqO cy`I@Ё`sVRڀfK@ڀbooOlw wKuK`1s*8zK@JqyozI@ eoo /uB s!xx7SlP4;walPe,0kNq @Jq.-8r JqM9-t '-E1*dbt+r sQJQLŌH{M0tb;{O,=CƁS=ԯןƋM2'Kx~{עamzD1-?F|ǓnJ\˟ݟITjU˫ˬ7F:-˫YkIՎ_TF ?BP(!RQ!4 Iz{7 F.!`}~k QhaqY.!(SFxfb@&7! kRka},1ff",1~,1ff ,1WVHf312E1kaB12lQ12a(712,1HfWV3UYkaO} mlQYBaYO(B,1WVHf3ka lQ"(;" FHfWV3Y"]#kaS#" q#lQ]#"]#S#"QoDcjWV3,1HfEFWVCaHf``5d 䰈qGA8rL5k0BAK6 k0L5k5L5|0V!CBL5 = |5QQ5&t?U: =|5vŦ547 P@QzqE4HQ7b&qn24Dp)CTU+0F2o$0jjSRjaRD9Q#eM1sB=yIZR1s^lZLV_4pQL׌HH.5Oa 5.. ^fCmnfv3p` T]`avelShift`$xa_d@a3p`OΠ`fs`m!*_a@@ VV%Sr V:67sGr:2!l7s[r 27.1i$3s 1T$y@a@dvkQXU.q.'2rqty!,0}v`n$qqt7v-x8cʁEl@baa%]1]1!H$rI*J!1LLLDŽkq+MԄ .NAOAAsQRe.S"T/.rq.qqAWVqqFYpRBZ}/b[.)\r].QLQ_1(A`ٓza(.44.;[Rx a;`"[TBf"< 1W`B`sh]`cu g`f&+o]`m`t`l`g`l*?`0qH`.:jx +\T`x`bdvq_v!L5~*ϫ16DP`)_R`Ht%k` 7DVhzڠ2xQBuݿ)/\BanAM_0qx2Ϛ0AP*(O`a`*4dFxgy2?Wʷշۯ-K08&4Fb[mD{ǿDD!s 1ϱPGYϋ(6ѲĢϩȩ5hA`# &AsDEy3 UĻɯAv^2`OnwAQ9)(ĽBT0fAvH`Uta)Yp!3EWLnew@)ncbā Av9H`ER#t6Hl ~)99:'ϟoP$tm-At!BT)ங'7İDZEBs&1)/I4+r/o/Av(* t/% *Pa#/?4Vir4F:?@^?mAiCOOOO6O=nO Z\9?K8/ `v GAAtPT&xJb=rb+ Trt*P@?B#o54trptb T mkqőԁw`3P?squ{tcF~ϿF@ Y%ukp{`?u %,Vrq @V4`+AszSPsT3 )5>%j{ိqi!stgq xŕ2htbrQ? !3'󿂱/0 c{[яJar<`Ѡ 5 %*5}9ᙓ @r^rlA$Q2U lQQ؉uyhU+424r@1Ryɒ^rx@7.?@hzi?wԐH}߈7c6z#o/|UD`a1H_\"p1@&WrBdeKTv7.!ԟ@V) `aGo,o>oPobo1qyo>TooODowQS+F搞R6);=[vrrQv>LUF+q StFUevS߅x$~&qޗ݅ԯ1).UaO|tǢKT*ϰd4)'ߕr|<ߒM2/WkNȏ%rQ|evʭVlߟU[#Իg.gM1xweۿoo1CBSCS/e/w/S~eB]v KcVFO/GXiPdU:Ǽ ڄE-,/wb/cƻ͟E/?'?*&)BOZlcƄdUO7c*&tG);AEOb1ҿ)M_Cb.ϛ]!_R8Vb=x](' _ ݂?z-sT_Na2s/@.g(O]Sa5 _;;L7pwrra2wcCB|WV.gWi gU1gtO@|{럘p  D g,l;;.o@o*aa Oo0Ґjo2kJΆb`b~Mk 2(s.®%)nd\S?l$`-@K ay!*t1K쐴^VQ2CIUdUpς1aanOa7P7߸y!X16BòJa@a WE1!UަQb@72Ce$6N ¸ջVcIpb)L(aO[Ne4 (ga# f8 \ 2d T!bu-(;M_qڋgֿ`4@?F\oA`0ΉjM09 Q(3b8ye/˸?FƣE?Fp5Ys?F5&!Q@Ή( 0_Bԃa2U0*s3?ܵ0Ԃ2 k+ݓ0]'%Qn?Fѡ?FW^bd%?F?Ԋu-Q/c,DJ}?5^I,QIƥ,$~//'% ?F*^Y?F[E?F|{T?c,|гY,vݥ,-?C6*~??b'%OZ`?Fa)P?Fů$_?FPvϟ'͚?c, h"lxp_Qҏ,mV}b!)/L FAOSO+~'%`0DLJ?FNqVUO:Oc,~jt7A`,ףp= ,7d__Kh'% QuU?F`4pF?FLAD?X@t]_c,io?JY8FZ4? qۄV$N@a__'%X/~%l3!?FeY?FJ~ӗ oc,z6>LZ4kw#awivQooj'%'-q?FmUa`2#+?F(%oc,Y7 }j GfZ4 ~,ClL^!UFV{WE?FcJΦc  )?H},""3\ \ -YVqjǻ )1̶ Ѡn<δ m|ѠaQaCSVGh4cfUpi$5,)֝F3L&AP"iЧ Z3*p#q8 |`rXӗze2ZՊ@ #5.?\zi{B  ȧ#ޯ&&JJ\BMV㖿t侜+Ģz`rl%ǨΩ)1@8"4߀Hߒƿ./1*Tύrˤ]c-=( @=5-2-9#.@"~C Ȓ#@ 7?D`Ofrsetx %q,kڡv hE 5ɞ)3U+KGAb TrKvlԐhi"//ׅDzW280//?(?< ????QcOwOO)O7(QOأTx7HU' Xߞ^Ba5a8A88F4b./#Dn0B O`]@eŜkol7]j(PUFDfP h-VTYYU?"H$ @?Y,b'@?xTT;͑U@ !@ү&P6 lAu` u(Jݩ c?EDe34 D   J ũ2qL lvF BP(?L0 `LineTray sp r{ ncyz۶&` v%P t e n&$`u v%W{ igTyt&a` v%_Rouy dw")g&A0'2A{Gzm?>5Q?A^n"`Fw lo0~/+n"j?|?+5/!4`S:Ta ow?<*+` 9?& ` 9S5@y0eK $KXOf@s{ 'B $KYtO 6`9UM@gy i@i04Ai '. `%9Dw AcNul>k$Uhh2Q2QEXT/J> 09([_q_[_o!otgU__o_oTEooo,o1A>Te\Ls c!2TCt1MU$U<"itjt AktE*Q*QURmtQ!!otU p qU r s-3"!!uGRvTlwaExn11Uz{Y|})~ARɄ22քeㄐqJ"T c 14.m"?<%Rpsf0M8@H8 MEJUU8!r5GrK&HZ&H͟@G+G3"?GERml&Jlmyome>mGG-GAUV>6HԂyϿHႻFSP6HuANqe (=MQ_'0Uhv$on!3` LDnFxB'3` A@iBݞ"4` C m@bAxB}lewL9WIFE/HU}= 6p9yE49dFY#dMdZB `z]@et`|o+De[~]:aG7]PUFDf h-TYYU?~@x]@L'} V6lX-1u. Bj2u2Z2j2#u9r#ULH/MZ1+B#AD5 60`Vis_SE.cTm!#20AD%`>Copy<0wigTt ?DU1f@M)@c<0o+@'ofdBUArX@Aad@iV@n3@ WAl@ef@R@6AtBvBb@hB@lBAJ@=## AoG?9/#n"E&444 7I#$@HB59_lj#"SV6U8*l>(Uhn E /J$&9"(Ln eg5Le#pheo'T !! Js~o/|R12'"U7A%O3W_7xr5(-6 `F2 xqu\J@#:0b)&`AAs)@E T@xd@Di e i=M]UJh#HT0#3 UEJ\}|U@f?@3!?F~?FL&d2ٺ(1?P Ǜd#pa HkaQ"U$"Y8"Mx#BU<&?&@&A&C"u` ?F]o"fpy" & G&o"o&o"o"o"o",o",o".,o"B, 6 6n#V"o"///$u)ar;21'u `Fxq21uBAy B AA21"!XhQ$,@pup`Ca$Rou@+u*LfXQc* IRg fbY-H- M ZU ZZZZUZZZZUZZZZUZZZZya-yyIvJvqyyMvNvOvMyEQvRvQyTvTQyVvWvXvYv3* j|%5EUeuu 4 蕺!BZ)CT@x@U @n@o@a@%iEPn*Љ4J1[v}y]v^vc@x3+"" qؗAU2Aym@xcv$"" zp$E1u/w3,#? N?$hVW9AB[bSq20S hV1Aqׁסױ!1A7Ig?abrXqkiqruׂ6rS@6'_@rp9SR2MV@׌aV @aU-ܕ?XrcU:uJed*ɮ᧤N@q37sfjBA'b6bjB'#KPRQʾqjeArQ c@(3FA{aٿA3b!&»F_ v7_M%;f!xjB!s(a?xjB,2brrP#VFI%r'#7 3%Р$UM_ }uj_2 ׂP5GׂWsZ2bQwvPQrT_@|wu‹ׂPc?P 5uR܉͏Cgyc(` q(`e80{A @9-܎ Ʉ '؏bAP42Dr \sya@Q_X俋NZ]b6 O6͒z{ # Z{u£@%ЊׅqmϕDR1oL^c,ߛѯgZ $j%{|ב R{iۄ);qσ⥞occL&d21b}aN/9K!KG'!cpK1a81YNSFxBD $e0Bof$z:cRTrDBʩya#HuzR j"^vc ` T-pavelS/hit`RHq/tq `OfYsfps}1 oa@@VVSrlV F"1+-27i41Tb4_qt_;aXUZ'2rq_!n0^Spn4"1PT_sE<raa-A-A1Hc^IpJ}bL;M 2NQOQ1\Q\QQؔcWRS򔆮TqqW&o$Y@RZMb[Z\g]tpapa_TAQQAbpJvq;Ϥ+bܤoa pQ+d0AW`BfpsĠh-posugpf&+o-pmbptdp^lbpgfp<XR*R?`0ȁfHT` jd0+9TfpxrpbASv_vPQE~ 1;F`a`пҡ<Rfp%tknpr !3EWj|Ϸ2HaB^׺/9Bmqn*<0NHBew0yQ̀~`ϿOdpahp+=#bRdﷰ4z:$ߢ B<R ai*rp iisXmTZdj `F1H<zՕ{xMOai?ޯ!_` tD.Jr5<}= ` K* `[Z l v 3mAxD{z{.gMbeᒎcQɥģ"Ĕ,/ħ<w{) ѫcĔpĽ} ѮmJ5N13_MsSspy`ID?S/c}ba K"eb@o> )עa2¢ `DVh%# UD kmjBt2ߧ߹$c褣%!rr[Rf`}aTy7[C3DnUbe|b;ruv`Up0;nr`1:븁X%`?$2$N1} Y4Brb@qAa-aa;qa `//5?*?cT Z "!0ЏQƽP׳M+Q ܒ*QDb)QX\T׳a_uu`bېRIu}ܒQ (l\O~?hzP-îLޯ)ّ `{Cݕ2YǕT `ƲG . !ё1ɕHᝢ WGMĪ ?Ɓ%Qx?ˆt/$Ͳt ڔ,ş$O4  ,o>oPobotoobo1*BBZВՑ7zBoaa^屓aG9gl6ҩ$xrNPƯدk)D _& 3EWi{ߍߟ߱ /THZld Z_VjhRdv=X|w$%F / >Pbt~44FJ3x<T+///(/aNO:QN9O:////./??'?9?K?]?o???1???? OJEOcUQOOOOOOIYvotuoacVh͟y_T_ j &m toB)j ooQvwёʮhg_#F K?F&?x?0l}G{,:`@"   -?%aEr%Õ))EyXR} o`д$#?ęxQIr/xUt,bwXUl0i1ewvv֔tQ=Q)R}h4FƶwGYs?CpYՕY 4e%{QHvsLQgŘ{)tUbzf^ȯfܓ֔% E:a`*q>^TbtxoMdǏ@هT/f/˖{OtC]c VqǐI֒FV –nUF?vn qsɿ *Ys?F>?F:Xc?FMڪ﫰|JܡtQq&A=QngiC21MwZ漣A$rqg0N܏tE˔F*CUy ,Xjw=Oas߅<`ߦ7 {RC*OoNkrfŜo-?QKe_? Obweoz}&U.V@U4kW5wR<k 7 fwdůWdvw.ϒ,u6^.ڌ@(]/5Uk@ k@Dw,|@q K0/'LK9'Cm0ϾSBs2ّ5)\ls?1D@ q>Aw9#uQ y~qҰq٪$E)U5G1r9{ a`pM>AkVRbדL!.UEG!ܱ'kWH٤*Jn}viIpU'LG(A^|{Օ4߭9w7;Օ f!+S2)kt/'م"Nj$6HZl~P`4@?FEO jM󎣈PuY Qᦤ8%/˸?F#eֻ?Fp5Ys?FwtÊG?( 0O(LпDJգ<}?5^ITLQIjL$?~OOI5 ?F;?F[E?F3Ϛ)O(L|гY<ߍTLvߜjL-C6*C_U_'5OZ`?F3$i?Fů$_?Fir8_(L h"lxHOj?FeY?F܇]}(Lz6>19lTkw#N<vuqRd/%5'-q?F0 B,?F2g#+ׄ0]/٨(LYЍj@ T jLCl#15v?3FV{WE?FD~ΏJNk(@(+ WIHg}jLzB՟$q$D*P!)=6?zH7j A^ 3=< 2Aġa1aS~h4'+5.ϼ7O9H%F_p8Ɋ( b-SlJg&ǯٯ>qA>%7IWt?&t:NO/ag#mҳ31>ũ!bX,!.@-R^rC&Aϵπ!EWiSh:Mu^9a.^;g?&h&8!AOϓ{EEEW{>h"R7"(P!1('b!)1jUP-29Z3ˡ#@$ 1d>"wCCZ3Q%p1bؒC!p2@&7DZ3`O}f70seto?uܰA0&2,?>?P:v72p&1"E$A1oٿA32ÿ&A>+5R{w,0';)w-eV-jUCU+ a ?%A'; WTrvt0lghin0w8M=Bb7OOO;J!HR?+vOOOOO__????(?q>_]?u sEtjqttHD: #  h  T0]]9 # B 2AUAɿAhY?ٿA,c2ֹƿP6 Au` ?A@j$u#J.9axu`u]`bu`+u >ڍU@@M&d2}34"ÿDC'>"M-w'>"D(p A "'$"&H' O#&5 $s&]$2JD=#D> D6#U`l"_# #1?9#H1 uNWzIs;2 8J#2zGz3Fx$2[Ru(/YB DU_V?\.?/B:o:2A7P`BackgGPou_ndCM`lM`HQz@@bZ}H D?h1GA #X8P6d3662Q1CUBF4i7o117;y0~1Mr)Srm>de}mspTt "qq"1 1381812tMItBB Eb'b$4ANu13[h2AuBH1BʜA!ʩ¶jÄ[QЄT݄zrf>!$0 %3To1/u/u?8XxAC`IWsSwSwRs%o.@0RgPf KNC`teI`\ai!nn3uZR%d DM`kO`mtiM`5HQ-2HZv#`NC`o>!8V@MKsEŭAد3dVrbRaPags0bjkTykp~1])S搹qO`zfY`u^s;cTͱnA²oivlPA$*q#8@q#5$rQQR*E?eNRעQcBdg:b`OTndGPuNEb;StRaaGPdB[Ÿ-uijd[„6k~]QRƀ@gP@ A>LEs?e\/dϩs Aee"g 9bY`dt2D-Ÿpsr- Sqv߈;`aݔ֑#pS~>"ƒE*ȯ:b` Txk GPp}1Բydc j]aaAe(Se1dSrʴ &e%\g#d ɿRf);M_=7DßӁ„{81,[/O^kxÅi-ùƁ3 uI[Uu`  *L%LBW?AP)Es̲aٳTe"/)#We"ɧ??? ?:OOB&c_ //Y`6?H?Wis??]CJ_ oAUCGP?e`(__! m2oDoVohozooo0 siԛOo oW}_oOOOcV¯]WץR=Oas-](]uќoo~Gm#oTw {Ɔ҆΃S 6:0Bw)s͟ߟ'9ρJca:smQm Krаe/Ȯя- voP* V²#5VUo"/%Wo"O _._@_.o*o/**u(!S[mJ5li@eu+arw% UR°l;Q@nt9'G} !M @o#1Ϡt+o_1OCLRʿ.@=(*jMD`sprkND߂ϚByiϹV" ?5G&8߭\n߀޲5F/=$2&"4FXjj"j"/ɨu*FXjv_?q????9 ???Qc0OBOTOfOxOOO8B+DO!O=`Rotyfb)]O( !1idBm!?ȅKbB;rbrc`R@*@;H(iPB_)/BE!1v])g%oks{1tpnN;GWewyuPz%doaqm Bqo2;a2wtSkmQb@nvUaTtnsb bdds°bjTq3o: rrcjKbT!E!222?kl;!30kC2m2  aZZ!*kψC\\E&&BGO ='`C²!cRf Ot9d_D@qA,@"hi."4 b bA' Y! Pk?zz!!_lDZU!!'7!ƕP d>"p}9+6@,1X8d:UPH1/0rb4,1)*AA/_"_o & #hQQ,0#` B scriftunkg,1 `CWalPoPtQg1E}u+iH5u1! 7 @@j&@@q{`?@rbk?de?s)gUA$ &?$%vr;#xAZ%RiLE" 2 0"U$2 eur! |u[]4 t&&(s~rzlH1AUH1?iiu5U A2z"ry"!rByǤo VTPQayl p.-LPnPʡTn/k?Pup r 3Qiߢ PuPws 4EPtIkrJPo?PlΣV?PrPRʧIE zmr󢴳 5D b9TB*nPnߤz w 6VO_LA!0jA_(S_e_{W'6&!3A(P!|/2g1 D1y 3 H1D1oE7@q;E{e|lE1@@~!@Fg#?F"??QkPyA`Q27W(ATl 1<25l =clYmfxAj`PL!@@[g?+@@1#_  +??@*o4?PD}p\z-uru`u0`bu ,0@@"4o@a/T("q6y /B#u2#Q*"0#ᝯ2,`6@@ߊn`P @$Rxww &;01O5l^3);hK(a~"f''?銞dYկd#j_ubsUTK0a?DbB`@_bzI/!*9`P"3\o"҂3q55"1I7zrHQQr9:z:a:a9aaR=R>rrqdQhQrq(Qr q2aatQmO\A[|RlqlqⴋrT,@rLQux҅t0`q^qspC&Av"rBpsx^֔u~*k sa4azuNu ca@bֵf7sa9@xU܅-rQ9PCaLJY„f6D/@r*PPV#ROj&TP@Yn`@Q.%Z$yaUBaXfpUu!`4q х4s q, to! ТZhQi5,Qrc3LP@j*(B<@b@t^?$@o.a#@PIKr3[b3KlqJj/ \Ca/O Yn`5PQYYCZOǐ2p:F>pKBrOgOq/@LKHKEKːYCJ=Aұ)VZn` OzOOOOE^Z___*_<_NXQ3h|GCA|epPKd @A[-B^ AYsjT '_*%kŽ_Zoc ss&qg1]JS|([w-v`DbKOb=~h>fkviKleN{>f8{gyjPsKϝ̏|vTXc[עHAmeZfDo1ock2DN&xݟ^p*wwjo>f?ʗTٛg>f,GY^4Fܢhn??Qa$%<}Ac)#]KAt^ FA1?0_P_b_k?@ܟ9Zm(͖pB?@qK:?@W@ή8W?@S^HssZ*m(5=*/P#&?@!W/?@?@F?D'A&a?@0?@vj6??@^ŴBI!O&J6/iлH$?@ T9_3`?f\Θm({E=T@m`mЁ?@l6͊`?$Zm/~&0=ficUo@@K^9`lخgA%=,^?@tA XP@@z(fkT?@Ô1fe=j.ƀ?@P3{@@5 I]ͻ?@+ӟ wu=~@h{?@j"~#HXt$'=)&3I{(?;3 BP 6Oc= }…lO'ƯدH6[0 O۶f_@3l~ !H<@)>PbtφX?<)H\t('A)_qUO}Oash8\@?x<@fb@t^ OH3q$y)AᵅO(Ld?x<ǣsxqM~^ACi#Sq"T#25(3 #A/'2U:H'm!D0B 4"!` TextwCo orz`` LainFn 3` Asa&!4` mpP  Aj({"AFn(?A!3~yZ[O PA4wq 'HFdAT(q_q3.gq+-뵅h.((zR28y3yPU)^BPԇOOOOOOO__)_;_M___q_______1i/{/^X" 6q'AS6m#p Pya d4//*/E$B ?' `?FM%# %?)Q.(gMACoooo|+>,",x>Pbt}R,>bMt=ζȏڏG"fFXj|xU,įFX*@pԯ8G(ma" AS$4!̱ q 2aaJYĆcy"ȵ08̱u){`gl6ߛ @@L"@@Hh4?;C¹?FG[r;1֔'u`Fx8<"\YP"Q!cZ"[!y!!2..Arq´"T,@-@<إN`-9-L7Cz`.WAoubҟ Ab;sż.v̟6 ˟W'?̵4!Z)4TB`xAp`n>@tzj7X3#|گ2п6=#X)Zܽ?-qry ~ZAu`u`6@rbÞ?5@hȩ.p@Őc)a&M/_)s6})7Up`uR!cA*qK2-[!*U)aYӣ#Հ@+("euF@X,@@cP?$ 9/fPtwߡ2wy#wI`rSbK3`q a\~a c©Fǐ⫣  Hː5<Nas i!3 w%<;㐐/?/e u' @N2 -< N6£D1 5s1 .h fx sN!P $4b ?O0A&GWA///&6?+//O*`׹m(D11CJ/"ǗNO`O//??M'X'jY K"K(;G"JK>1?/KK__O]?o?_"o??MR&vum]pɍ0cKdoe񠬁lOe񑡡ojFxKk@[?vuS‚@Rmw@|mƁ NGy}Aog6oo?M@]f?qoooooo\ke?__O\Z ՁX9/ӘC_U_KXF_}6Z?Fe?2”,zNt/DF`0 &F1l aPdl/BjZԘl-U%{ 酼N[bzf^5OHƏ؏bo2oBbd?Ffl6&FF?FuWuZ}1cԚ\ ϥRsBTЯ*oo`GÊ ݢ{Կ̗ޟM2.RkO֨ϸ"4FX<ߺ̱ 2U9MSiO@]ўyd!4q<|a|aSeOZAkQw9+OO!@ Ai1/C.</bsPbOo@nZA">Wi{;^5˩?@FmtĹf~:G?@?@;:y?@A^}/9*[Jȏ :g}= A?@I l?@C"<%0Md 8#(m?@'`sI@DϜͰMUh>OmHaoW?@&N?@?@+%U OmHg6D~)cK?9?@_1n`Um,H_mHﷃP,P 5b?@|XYm˗_mHOgOw^ G/6/?@$!?@d%FV^_mH+5gf ?@qK"`:= ?@6NZ`4olI5gN@S:3ί?@foQ̄oȡ4$1qY?t@  O=rbK{!v '炏ÏՏ" ?V N x4(1(<1> =Ÿ?8Aį߬X,@@cPx114<1U??D$yίU@`0 Q/Si ^1Ts] <kQ'2qiq GQ! Q#`n%bC6a i@bpИCa zq`%`>qW,@Q"acqB@>q݁oӂ_=@`{k*#pq95"+aq@uo}5ub lҊq徶#Ž#UU)*/023456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdTUUUUUt4l"H$ @X,b'@ 7PGcC-T8E A;X  !"#$%23456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd*UUUUUUUUUUUUUUUUUUUU                          ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @* A B C t4"H$ @Y,b'@ C-D8E An@SZRR@SZPRH<(H<(nELSZRERZRUz`8MU?R; @3L&@ LFDTyB tuh$/T *UF"H$ @FY,b'@Fx@AǁBCůq !]2 H  #-N!-|1` Ba0UshSitzn `Ca lozC",>1R V*a0riE dֲn^Ͳ ` CڠnE etڠHAʝ1zL)vGͬ!?EHPb1\BB>C>d~  N"H uHLy]1#b7@=AK P $#N"=OasN"%~ Z$$$$$$$-$A$UUU(Q 4/4dB|# "$1)&~C`SN"Cg-}#V}}}U } } } }U X%W&N#3 &r0IQo*ƒ%̀S!+A{Gz۔?_%Q? 2` S ]ao-z@"//` FܰPRtmױn&S袝u`` %.` ?1"@6@6@1u+ A[cWnm'?C41`%TSaispr"W2c#w2}??<#1`)?T;" +Smyx # ` )WXOcf`em+H 7KYFOЫ `)Dܰ1i%`J)Mgibi[@!K_#eZeW2za"Fw2 `Lܲe?<V@\rXbTb }TaܰҺ@z-V ^V N?V_V]rXbY~[%~[1fe*e`ƨa˃]bzec>e{l-C#)$⦮ vP rBIqdœIecQש3G~cPaess@` t_rlPu-`u.`qߝx~$ gD)ob~eũRőe{n e\2U]?qÀ?A?G beZ ґRVܰsPRRD@Y\m#E0ѐ3ޒe!|:ul|a|a "Tѥ1111ob3b11:rQQyd>7$JH8Wr9d:qы$ҡ=>_«ino@+r,qٴH T,@Wroe0WܑѰC@1ouzBqe[A-hrB0s:u)f9;e-- 8X@'ZGQ$Y-IGb́veɡvIbvJbvKbvQe!vMbAvNbavOb́vqeɡvq!evRbv!eTb!eAVbaWb́Xb̡YbءIL 9  '4%H^)qT x* @ nY0o @I$pХ)˶UaeةeD]b3^bS_bs )vae hO`dhcb "h p=a;õ / J/,$ƶe4?Q-<rZQmarUrRU@T?@rϳH`f3z6]@|YOa@Xa>5qƶq#ϡ3 Q3-=M]m}Ab tUHPuJPed@Wwa<01ȳ`aUarA 4S,@:Qɳ;@a3SzVbR(JF@h4F@A̵Ի`O2`<0tC0#`I_0rp 3_0X,qrz? ql? pPrF0arѣqucDR*fH0b@o5o?Ylrmgq{eR{rcjH0EaijF0 o@HoZolo~oelz^oo xqDSڜgaƜekt @a{-bm~ 1arZt 'i{ŋzÏՏb sQA}&!r8P{=DdbkO^ y 'q 5G8S'|{R"x_01{am`MzqD{1ݏ!,>PbPt믪jh  "O ',̯b6:_R@Nt?A'M_1VwR1w[RtU"QYA` !PR}Kt2a'Q~aLA3SqFxyoLs!B_=@@`{R[n?D"XtT[u}qɒxSyqEtOF tX>>BH3IJ*9LD*MQ N^OkammsQR0qQ`?TI UaaIWӄaXY턒ZR[5r\[r]!3Q3Q%_;eUU@ I`V!]ad[[;|B yqudP ȬD}|XP`BM`sh`Vcupg`f&o`mRI`tK` lI`gM`T;b,,,b?`0 brrX@Ӧğ֘XP+TM`xY`b#<5yrv!#{-~L@FX1hB@}LRM`ҡt kU` ίBT0X2h@gy/&Ban˯׿YY*Xk(yoρϯOK`aO`CbW4h$5@O߸ mܥ7ƿ i*Y` Fipi W-kB@9K]ﳦz npI`ab "W9hO@/a߰SV lO`sY`pdKmepW18h$\@ )}ބ,n W6hit`"&ys` F^pQ b# gv]ϥ UkŠl6/H/ g/P3/ #ꃂ@ٷ///,QGwŠVc?"?4? U?@Cm?W c??/0! (=)O#O EO:S]O KʝcOOO/±O?O_ ?_@cW_ B͏___%S Rd_o#oEo$s]oWxk/oo/OErd _)A QĂOO_±uo{  / q/$S@rbޏ{) \1aasWU1n" 0LŠȃП{ *A2+_AA4e-SK⺱p߰߾Яb̳ 9?QD;MD|s%pѿ鿔 cz_!S?ϿXP#S A*s 0&hϙ͢u3+_S7f\2`Ownqߡt:QEϦQ_fPUmQ+=NfyZ>PLmns?": _rf,PRt<ݏ:YP=Ϗ$Zt mptm|Ù?K_3:C5)BymsalnPyqI4_X /fR۲th/3+:P3arL4/,_p// /S;%n p\) ɂAMmtR"?:/+  Qwb$>bU (lQ4gUU/GA qG@Nt?Gw?aQDd =psAG /p8  ͐|љ͐kљ͐Zљ͐Iљ ͐ ͝+sA/@Rdv*/N/r/ZO/~O&/OJ/\/n//// _/D_/h_?"?4?F?P)G 26er fGm0'z(MH1Ct;Pt]t24jtqwt=tatJGt<@ GUt Gt GɭtPttGtYBqA_M{w2nws`MS`IDTSSt)}#5EJ\d Katgo0`innqapdY%d Dku`tiur0$Np0r9KC bPrmqW*aAaXAg A0`fkTyp_CDnObj0;G0`u7p;SA}rxn zANqMrzqNqqqq.qa M#ǠETc%])deKrl/v// ?u.d?!K/D,>P?p<$e'O#KOY# d R)wO'9@RpP>"8r FΏ E[}4Sgi[!oCsN ;E2naGwyZPJZda耨!m`*$aolZVpt`t[ik Et bpFiD&Nebws@QKBqR6rlt a0RU q }"@6qNqLQhquqΕhPU+s3r5p5PyAHu$AmA TywBT8s@ev@LP`<bu$wǣΑM vB4tl6f?rq$ Nt?"y0u veΕ2 uxAA   ^RawQLQbwUeLQEu'}a;sQGv or~tx c?[ЖǖkTqx30 ?HsuЋa ,gl`U+B{ |UykUyZUyIUy dGv 'UyTz ASssa'}8s jB{ R= ?nTz T???w"1P ߼Gs/AH˭F !?F:Y?7q?0MQ@"Bs  -w?^%D1rkbob pwkbroQocIXm@ O$?j1y!Bua%YT*JȗFl}o`o~ SA uj*B@AkQHOO.!1Gb2xMrvoUfbzf^CߏKo)Fs 4kQB{w%%_7_PI_[_m_rQqVT__?y4__oneONd7wv^o]& rFH|bb\qakIuWUF|> !dQopf|D?F:ɴgKݰCO&͐")ďA4TP\Z{Bs¦!Frƀո32҇|X"AecRvvN"ob!\lpf՜oofN`)ơrT䰳WX!˯___3EWCǿp^npog5NjooZUB| !tPoEE{~ wO%7yA{T$ nƆ&؏P /2/ 5/24moEZ' n574F8?m!ݯ4FXO&O;CF2|sOOOE)/G;`O@V=c9W3?Ml$ݮ*׆%zoN6W'@{ؽooj=gn)NMlb ;as]bt}rrD)_ lPwP PDW,"\FF9_K_5QQ Z_0$w+x[__wQ;x=v"3c9q),o\C?wP-@V Q*!VܐNʢaA=NI`oU{1QWyRCit ?@%:?@j U"p?@1 nИc˷}?y( 0 Bsa2_U0*s#?ܵ r" k?+ݓ h#u2ng;?@dAc?@6WK@D?@h6FϪN\nDJ }?5^IQIư$~x2ّ?@1?@'/܎w?@t^/n|гY vݰ-C6*//m20Aa?@6I?@ ׼?@Uk/n h"lx p_QҚmV}b/L FL?^?62.?@5X6?@;CMLť?n~jt 7A`ףp= 7dO!OVX2h8r?@Ds\_lL@_JThOnio JY86e$ qۏFN@aOO'2 ?@m܉9pgXvg?@&AhE+_~nz6> ~g g8 d +j 4! z_< ^m a{\a/SԤL@4@q`{t@c&ፎF/bXAП␂e#9 .qCPbtď֎F$D"ؕ@@!J9ȦgFetMx|ӗ 1CCUyMԻӞAﮧ*$ĭĿl~A&|Ҙǟٙ4!ӿ1-yqqσϕϐѯ!)<$5_"x}֔hnߖ8!( !%P8 29á#s1Ҭ, tCu*cTþ@Օ7JD`Of}set ׾?upUlr Ah4F@A_ >+  |s1,v呂s1s5%Us+q : ыAm Tr@U-hPooooo yKs؅Yk}Jt .xVlT¤Ujʏ܆MqR*A@mrEgJtkIugbRMqLzi.\krifIⲷ))6pq/odTr'@z3s1NTfф1FDZѱ%Z j9²*^W#ѧ؈@a@|A҆vBU$~Q%̀l[A{Gz?xտQ@ F& lhGpH@J` !N\owS^` PBjr@E|A`#n|vnyM\nC4`xR S@yRE&a7 ` X; Y%t0. K`D& vcBAE6Qb`M g@'! u#V"`5OS@TB# umNYzsh t"~&1bϠH@KKKGUGGG GTK G Gr 1d34&32zz҄bCF BP(( LBeR@dB/Cl32`Il sDaP(24X_@YmBkҿ@q?@a [??vêCuLYMp@bB"p@0!uYdQ$^"AR#uAs)4¬QS5_)? 1>x %?%6ϑ?\.ް?A@P?C1a` B}gQJ`l sEkheQhWhSX>o47Q@DrFB%BmaloZeUXpTIQDhR11‚ $ RҲDD7IDdDDArD2TrdT> Ta-Tfa:T|GTTT"!؂Lف0{T-rˆT&lÕTS!AAůTLBƼT2T4TdbB%_7EEـOFы`Iw'E%G0FAool$o+G_ 3+ Ktg2iSLo^l…uvooo+G%F1 D0)#nY@o`jRՓ1a m40&P@*H 04NA#^^l K}Cŗ}u6rAbI"|r3V$bH-sObjYT{pF])SYqGTzzlGs;qcCaFnAJoziS^d8EAAA Qd"E ( !b 8p>J7zBWWxwo2`OCKցd0uz2;dd|DEI"P@ _ @#>@HHEs8RՂrH(%B~4˟sUAZeeygE&b1Xy%A.@=~h̠$LUH0ABA4˿ ݵE%o&` mTWxEk0pG^d!`jƳΪ3 gݠ!aAaQΣB4ZE5%(7I+G11mSẁϻai5r$ԯW#c RWf(:LXa̠nN$B QBQBqQBA2A-!SB0QB=QBJQBWQBdQ;~QBQBQAQBQBQ`5qQ-?.D;` `ߢ8$DRA r7>W9 a`Easarƃuj|7Bo4//BR5/`+RP,K`PbWAټlQ@A7SPӬ/(//[!f -?!?3?E?W?i?{?CTBPi䒥rw"/(WjL\nOO'O9OKO]Ow"lb-෭_S-qEza??? 4PGf-`ϑ(\o8SSJR_XtVVV|Sw#,fOoo&o[phPo\otooooooo}Qؘ3auM%FuR)KreP?8_(n__vOOrOrqi wuVoO_"_ӏ +"o:L qEzD!4FQr^r5f?Jlej1r7% Rl;:>nt&_5XߏQ㇢Ћ|A?1C%-?cuzDa-KsM_qσϕϧπσ9 ӯrp0BFؔʟܟ$(YPr<!3EW ɷ<ض*@ J+!FEb paҊh@6@BP(d?TuL` Ӛ޶?u:t `[tқLuᎄ8?@px<޿ ϛ?:e[?3Lq[mB>ajO_̕a6z1 !3"lWmVq;PKVP vR]b $lHbNcz1qiHihTD ēct!paߞǓiu (MUT6x4%ipKvo?hE(AiELuiEo쁃@@\.G:EPg:z^@@:p8o@"pa @z31"CpSD Lu,XM&d2?EWiB 7Tp.`2JObxEtlhߕoz1 cy!RRy!he@);Mj>IOB$O6OHDc1?=c%eO?d2v#d!?}A!OYkOObtovEQ_uM4%4wjPv`Q0Ta=d,@>|Ta??oaqPTѿ*b=蓦 dwd`pvnoq__yU{6`*Fʽv\n2B˺vl$A 6QŽ6%ޭ>ߩߣ({B˖-tBőuBż>cuuj "Ϻv$66&Fpm0*ϴ0.Žbi由12PPs% rXq'2qR!TPش`!'L㶹51B %ճ t@a1aAW!`2a֕{%qۿy@SQ&s}"xH 4?@xMQQ@8bBcAWlBmA2 1 옞[T/,@g-)gg̝qCpqou>-ȵp:Tgӂג7¸ `ܿ \$w@(ZdF@@)J9|I*KT@= H@hH! G @GH"I'"JG"Kg"ȡ"M"N"O"2_q'2RG2qg2e2Q2V2W2XBY'BgQ?JR  lL lLUlLlLlLlLUlLlLlLlLUlLlLlLlLlLlL)S7 4{)Xn)VnTpxp `npopř  =I.[aa[Ybyb"]b^b_b)~$?.p@@r=`rc@rgY)$a^1Mi`r }[$s$-ۡZ/Srj,}re@ʏ@r܃/m@=Pp@F sݑ[ݑ[CGSGGGGGGGGG#W3WCWSY+尜_qUPu\ed_,g )Ġ2񅥣ɢI7 @!IcS[H7'!ԋPm2P񹀢#PIrIc 3 bѠ / c` /אV~ϼ㷿ː*!jF <ϾпJnπxѺ14LJ1eNH @!-Ž ! ,ql-߲;9K cs!I#ݜܗbbObҁ؂*Ւa| "עmD1vSejQ!3na +j, Yp//0B/x "@* u@A9Hh 83 I2@>4qLj͠K/8f?ޠ8J@S,wcYB>j-F3?1f)ѨɒX;n?Fu& Ԃ`@AaQkJ@Oj5ЈG0Y_ _2]A­UPV PUUUPAa4U 1m USe_?]j 1meUv^Seq g PBeh$gRTd6`P+;h pZs"Fns[vsFs6ɓr}Xssrr}Asr}%AsAs}6ӓ2v6,ZvJA6x%A6A,6ݓvA܂ṽAA#66v:A0^vNA:|:A@0v?:yvU6#6XC)C0PT^E#$ܦ r"Ɂ\)<^ iAi$ ?23@ٗ??=PsDb5T;pAr6RCA CD8j1` TavelShi"@t<@[R篱1`O@fsͰڽEXa\/4AV6A]ayv6yÉXyÝhD})B@Ah+o!_=<@@`/{1aQAun"T(7&CE7رz Ei;FvBQQ2!HAIAJ%AiAiAALAM 2AN2qO$1aa7QQj0RѰ T,pQU9AWW3BWSBX$`RYm)BZzqI[\]aAA_Q**aɰADݱAaaA;n q:}A,nA,7TaW`BͰsP@hֳugfoJmɰt˰lɰQgͰK$*µ`0B<@Ӥ&8DV+VfTͰxٰbnv@1U~ aQ!vW1!<@!3iRͰRt/kհ yPN`r2<@ 3M/fBnKWi{RS q//AO˰aϰ4/F/X/j/b/d3/4H<@/8 /J,7fF Uiٰ iiE?i?ZC?Q@ǹ???3ti "Gnpɰ!a.O@O _O8SDwO9ۯO`/O3 qiVUlϰsٰd=Kme__=_*cU_18H_8_.i_oo!/osGo6RԂo_o6` k_oo sYmew/% UkEu6_0Y ЃYm:7I[X3iGFwE ֳ Տij3E8+q (p) şݟYm);5i~Bv~ ׯYmAz*!33%O R~d ſݿ173#5i% \n!r]Ċφ ϒYm DG+VBZddv߈ AߌYmJOPtSir/!b)&^p l YmwZ܁A034QRWREn @A@LE @H>P q`T 2 DhO6-OK%B:?0?>Pb eL}Ym*__TD+As );Q>#(iYm?##O UA@s&h/D-h"'b/3 //K܂/`On/!/ODiaC+7[/&= L?^?p?K RU ~??=?C?&=aO+O=OOOaALnoOO;OqSO&=O__XKC1RR-t@_R_>v_Gc_&= ^t!t mOto+o;Lo'sdo&=%oo,RdB"s l:Ro?F4 |_K+24*t u-*faD#s"r̄h JD@VhFu9S OR! IpMt} t:΃C#/('܊a 5rЖ # * h=>UO%gv`nlM!khDywROHwsßцޟ*$Ch[*&GKSKtUTpxֲGrlOw+*-&@rĀ4@ՊG5 )!A~s%pWBPNA:!bbe.z t@NEڰWOKUSUHP RЪRڰI SQ/qDK rp8!'CTEmѶa}=ILӯՖz@bX,@x<˾?ךԔ"um` F_u` ?Qp>B .4 s"rBAs"h`A sB`zc}sԖ^lz$UA CC։U/ ѹ{@wD~b`!~0!~PmР ߲mГe@x M|QMkQMZQMIQ MAe Mg`Y s!+*x<[g{ YD0FR agP)!{s!= M ^ opH=?Q `r* :%UM^oR$R r>E Q% CUgy -Quo??;?_q#OGOkO/%/7/ I/[*'@V#@/pmњ@@=g/y//////// ??-???Q?Ou?O?_????OOo;Oo_O OOOOOvO_7_I_[_m_[/____oo,o>oPobotoooo0oTox(:L^ڟ"$HįlƏ__X2 @`oqr8o\mW>qڨєĻң$ĐҤi3Ľ|T 8 E R\_#lyq_ղ~N'`b_`IDבҒ}ߵѠ K.egr߱wa+=% Djkuwqtbijs)$ҹsמ&3wwpщ| WZr{RaA'4 AefbkdTykpCDbnObjp;GupNp;A r nh.ONѰżWuL`!Nb6)/QRY@)׭Zsnd()G8VqH!Q.Q;Qbwr\` Engp_schbfJ7#!zp!A!bl pdptC5/G/BR&?89HQUM)))hCUgY"`# 3U#+#%b*'[K]a6ѓa6)???w5? OO.O@OROC)TG/$rI"Hr=Y$QI"h/?0W("///OOO __Gr=obm?nP)vOOO55SdcT:pxdk@O !}SZ5oGisQWeI#-R-_\)oooJ9o{Srb!?@Rdv@j 3aJsӡuy#)U5Kjrpl9o]oPotծq֬u)t3ŧ˟# aR/!` / ?)[r5X_u0r$OERJgqiP&oRs;E!nTnGbwbyPdnah(mTwojlV(RtktPi"kRtb(Q!!޵bn/sHj l|0+U͢a8̡NtPpbU+sRwtb r5:OWŠ%T@{%O̠` 9uPCscN _꟒ 4Hџh4?.@FW}Ѥޏx&A N2ߍ kW̡n{̡(ŧhÉb.Wأվ?PG>A?!xNӃ:xIx~:P(\kAy,ityhyIRU |kZ*I !!* 'O <RҡJ1 |  |Uz qF BP(V>rJ1zߌߞ߰1 x%)@Q 2DVhz 1 J\nFjZ./|"NӇ'/*/{_H=_δAtEo:s#ϑ)v7տ, j%p4UFfl6IcęJ?F\.?FuWu?Fm{^_9pqB-ѹ07{"'&4-FU\租 /j=/ a/^2^2?H r74N:uP¡Z/ "Uϟ?r/ΰFOD0q'9Ko& lgfRCGo޾D`O0? Pb_P6 >?ώRc@/o?1A%Bkb׀tnbe߷߈`A Sg-튞Xo`4vҎ);wUBmvf\oHg+H_Z_l_~__]_ __b&o8m1Ö5}HmkP;gqq@((?+"Ͼ.t'u'/K—)M?19tѡl?~4v6=??9O+O=NXO,͕NGۆw$NX#_ D,h˯\ گ0Wy Er9be\2?dU@, q=C;,?7Rv& puuӤU 1Q|QA-F\q+1u'tѡtաPuQvE$ !1T1; j"…////z./W?@4'zC%FcpRLF? (Q$/B٥4LFGOgNRgOmk\I|OK2u19ajR1 h2Da`^5˩?@$]PYjMN; SQNQ mù~:G?@zDֳ?@;:y?@hKߔ\1YPxR@Yٍ( 0ilBie_a2U0*sieA?ܵN_A k+ݓNգŲe}= A?@?@C"<%?@.>P0~olDJil}?5^I|QI0|$~FXزe8#(m?@xMÖ`DϜq;Ϲl|гYilߍ|vߜ0|-C6* 8aoW?@'|N?@?@Vbl h"lxilp_Q|mV}baq3y/L F̏ޏe6D~)*Ce?@_1XwW%l~jtil7A`|׿p= 0|?7dڏ֨e揷P אNi?@|XYc$m0lioilJY8x"t q8tN@aRdHeOw^ #?@$!?@o =cͫlz6>~aliF~"tkw#~v;*Eef ?@[:= ?@ּ*8nlY߷ʖj\pҦ"t 0|Cl!׿鿦aU[e5A,GeY]x0| 3U+wѮ8']jZ @A^[ Tr0v:`l_hi4`=hmFr(gUgkj'0RrXY]ȱsC|n %7OoF6[_)o;j#oҏYfe^9b@l@q`?@a W[eerC2uL)ܯ@b8An@(bu)@@r(r,ru,=y8q紥,ȨTev?\.? ,Y^`BKc!gKe.C X3GG"HR ςՂqamsqasp 7Tɡ4žBAAr"$hҳuҴI;⶜·s"ÔДݔL,arrsqC҉+y8`EqBA_*lqayȆQQɡ_oTqa<QϏL;c`:ׅՇXo¬@ԯۇ)p! K"eAiru&8Jۇ.` D@n]fI%Cʭfb@ڈ NKKB-XGͯε8k_x RXӉ-rbx"V!V"bRdԝs0bj VTj0p]#)S qzQls;c0zѪ!nAo*iAߛU!Ѣ_ČB#52B@e_s`CO0Qіd0u*;2~1d2d4BB,m-lbH@)p@\蛕es%2DBӟ!GrvՂ/#sA bee)@g b27CՁ >@p<qMQsА RwuܬJGֿ` T@x k0p@"q4I aSqaa\]QMQ$󛕴uۇ%1#S0|1ѕYk}sďEZDBƑ!APҨ+#g R@f "5B/?^?ѓ=.;H 9IQbo|碅!ބ#`%@/R!# :j/|.ӓ/Β$U//3O1IQCg2&?+`i%Es2~saZ0]?o??_,_>_P_b_tW__BFo1OCOےոOOO3?%__+PR!SZo|)ueS`Ys_\n ! }sֿ +i$B_l&sb1}g'roxWR DoVoho@ŏ׏ @1}1}!*$POasG}//Ao f&|)!H TPf'sCį֯ w $/6HZl~QDHaǵAS/yKre߮@ yϋBTCv$AVb"2beOwEg2a¬  'VDW?ҟoo߹on]urJu`OrJ!*!$߰V#7PW7Wew>y@0PŊ%dBa<0Cm@oB;b2aBw4tN0sk82ab@191GA1‘ 1aT?1 brdtsDЇbj1bk)ipbͲ&TAPǵȷ*ϲi1AOa'2t!)0ǵ鍋qx@ ȶ "mC "CnFxDY1^3` AF0tt&xA4` RIm@O1xu61aޢ?18)x"q$qAPǵɴ8Ec'1'1ǒѨ(L}$q0qGEo R`RUnBcog,Oαwk(;bH`jx@,@2B鿇x@B@–oD_=x@@`{1ŋl$q UAFu$q-ſ>xBp9x@+x@@x@@p@@\.?@O:q$G)qs5,D<]~?S[?/qqjO#Bi8djJ NyH'r/q/o KO @@h@Lt:w@rp6 8`@z@ 2#p%!g\I\np% 10/.E/W$w$RAMfϧϹyHn\T1\?6_H\LQLQ1 UfxG_YX_T)TE *gU>Pbt?oX, j//b3S$R|2,f58$R////// ??.?@?R?d?v????? 2*OܿyO`Nj/MjOOO$?/__+_a_s__qg__/Mg___k/!o3oW|LeeOoN:Oeup_0Tj R}__o o Voho_&[loZэoNLJLj oo(MoПTvD_n+=^pʯܯ+hzJ|X/zO<y"H$@@btӭ9);M_q O//%/7///O?a?///?!?3?E?zi?{?ď?????J OOJEOWOiO:ύOO,_~b[U!_3_siTU?_o&_ϐ,o>oto2oozzu D&Jinȏڏ"Z QRӊUo@X'V%[$ǀ?[AO+gF Ϝ]a<ؕkG$O,Wf'U@X, @@4FO@?gD7P -DT!T_ԝ_(SWE}aAh@@WELt:@)p%%Hs@z3BOOp*/W_/_A^Н56VV!3zFAM9iO{OC(nPͿq߃܇чD9oKo]oߔ߉T!mV%?! /Y ??.?@?R?d?v??????U@ ??O!O3O*Vj@@6`E9hUagݔOJ H;POLU7E_S7_!p]Uqj_|__>X_VφVϏ&ooonoo¯okou Q_B[T+/əA>"o!G$f!r`0 ?@N@8P S+gQ?&oT*JR?@KնWBwӚ$&[rod_C$<ߴ'9ՀDuO` &?jϒc\o2"c?FX?o AQU'yɯۯ#5eQSHeQxQ8COuk ID Po6 nte_r 5r/ؓƿ!~ūV%C$V%D6 r*coyP;!!G$;!)Ar߄?@BP(o@@@XH??QS)ӒS5WV {=J{"=B{{9ԧ섅K(#OC0P贁Nk{Dbƪa'聍uB:i/_b z_"Γ™3=Oabt1*?D>27e>?u?n*A ,0BTa98+@,@I\TzM?C?#q n) /Ken(H7>T8U5'^A_S_('[Q__'U<_P_Qe^ro&ox_H[omo߸'[ooZYؕ'U?|>ooZY_o<V_~ToW~_WUdm (BWkseiߴ _;e=唻X'I2SS{e!$M$Dה5GeSeFl/On@#eSXX/`BW"p1A¦r֦`By //-/?/Q/׿u/////??T?;?M?_?a?????OO%O7OIO[Od$O呴OOOOO_`-2_Qc¯b_t___0B_ox:oLoooooPcu4 `֯2WiiA/㷣Ǐُ!3E1Rd.1r`?̟ޟ0BT&p@CUgysfFtFuF 1CUgy ???cu3E߫Ͻϓo)loM_ߨ/ߕ߹.1}.1);M,oqsb/O嶏W/MooOa%@mGH㔯i{Oȅ^;"ܟr/6/H/~///// ///? ;2/?A7 O?=s????? oo1oO͑ ` O2ODOVOb B+_r,vhOOOO_ _2_D_V_h_z_______߹@oRo"oooHooo!*<`r}  *N`P?Ϩrhʃk/=/0B//x ??ҟR?d??>/b?-ʥ۾O/O'EWoO{OOÿMOO )//G_ʃ_N]oρϓϥ 3a0_T|$ F\C s%?8s?h|Al\EVEU@@ @@q@ӒwPTQ#Mxx4ST*J@@t:N ~^?@x@ )pl$ @z`@~:ʥJ\eDffH!MI5!3[n$5 @t*..5@z);lTab= |oa4o{oooo+=Oas32G-yޞ?NtzUp_.R>Pu`p@s(mbmb@xď֏dᘥ3"Ehrߟ̟r߄&{?QP{5|jᜯү,>Pb3ɿ~ANiU0:LX, `>qp*c@bu3ghߞ߰Vz j.@?-jr6HDrKCY!AqYT >ieP_aE}zI[m?򟣗ٟv0S~wxqs~w|u{Hϯ᯺CietBPhmXXGW'2qRVغC!kD4}A^$7Z/l'z/ Ϟ/////$_6_pH_.?BK? ]?o???>yK??OO$O6OHOZOlO~OOOOOYOO_ϛV_h_z_8o___>__ oo@oRodovooooonoo /Aew"Cg+Os/ߏ?/@c/'96/K߉ ?//_߉?/n?fxT/?2OhOzO>Pbtο8ÃQ1%jU $ FS:Oq?_lv cDL1E9ɽ5U@U0@@@ @otىgP}gk7ZI%ԝDhWA~a @@tpn@@x@ )Bp*>A{w;@z3ur!{~A+VJUV38vM %*tm,9%z%9 a]ϴ23"#,@!3Tat-B?#?aqzDV]åAKAE>1ŚEv[?O7!>m;?M?dj??r,r?>1aE??1*N\.BOTO*OOOph;OO9h5@_ _9ǥh;jOo_1^O_7 1B_TPy?_7 5N,o>o-iO(hXQg_'2UbMȊ-!o-gƀhz;btKq}߸/,6HnQYk}hB)! C?Qcut/;M_ql%bI[_/!/3/E/W/Kqv//Kq////?[o,?oP?t?)ߘ???q95OO`OrO_9O_O_ 8_J__߹Y__o"oXojoooooooo 0Bvk!Oatk!lg 2z^CUgyw фˏݏI *<N`r̟ޟ?/8J\?ȯگM4F|jO3_OĿֿ 0BTU`rRmUϪϼώOEi:o^oAS{ Bfd]>bf폪5ˏ3}:Lş$N19KN1Vzo /'i2L&-/?/Q/c/u///?'Wҵ/////?!?3?E?W?i?{??????? OO/OAOSOeOwOOOO}_O ___+_=_?a_u______ooB0oBohozoo*ooo q.Rv:%Im*#r矈̏3/E//J\/?ڟS?e?"4FXj|Ī>үmAO(:L^p=nvɿۿ#5ϯPzR}%Qcuχϙϫ);M_qߧ߹(_7I[3EWi{ނmAmA(L^}?? 1Uy6Z~ -/Q/?$V/h/Ͽ῞/)//Cq.?@?ov??߾????OO*O%7I[mq()׉F# 2DVhzݯ .Rdv珚|////voPbt_SُL^pr/E֏$6HQbtNQO7O<ѯ`3" Cb!oobe}H/0/!3f/x/i{Ϯ///?,?P?At?eߘ?߼??O(OLO^OpOOOOOOOOQ_TQ$_[H_Z_l_~_____W?l6f_ oo1oCoUogok7v݃ooדoo,>Pbt(L^pv܏p6/'?l/Ɵ؟ Q,>FMQdv/9]O*ON_r5_G[y_6ϝ_Z_~_ oX_Qo2ߙoVZ_zo_)߿o'_q.@vE*<ŁJnObX!3EWi{'W#2ߩ V+*/uѶ2(:L^p//$/6/H/Z/l////r?/?? ?2?4oV?τό??????? OQŁ$O6OŁ\OnOO8ϤOOOO"_F_RdϷ_FP__+__(o:opoo7Iooo\6Z~W4 2DVhzOFƏ؇!/ .@RdvKPT*Jϟ)~0/lf^HZxȯگ"4FXj|]_cOĿֿk_ g?BTf$ߊϮEooPo>Pbt߆ߘߪկ!}!$6oZlOs ͋#Gk(Lޏpן{g$lE-?/ /2/@D/V/h/z//.G/'K/}/??*?SOaQloϢϴϷ 2r\.CUgyߋߝ߯)pk$%7I[m!3EWi{o6/ASU?w+XQFXQ~@O_ /D/h/Ͽ//P?/;/_?@?9Oψ?O??I[Oߣ`OrOߨOO3O_i{8_J_\_n_@_____ZI_Wo,o>oPobotoooSog@ oo'9KIfx,>Pbt3/Ώ(=L^p.џʟ?&?HZl~Ư3M,>/bt_#_G_k_(Ϗ_Lp__oCogo$:_ol~oo_o?YoDV_ .@RdvJQ"4OXjG"#q{a-͏tgV+/nvhDi2L^p//$/H/Z/l/~/////۟//??2?o?h?z??o?M?? OO.O@OROdO~OOVOOOO_/4_SX_ |__Obd(oK_oP:oLoooI[oo$Hl!Ei Dhzԏ K 2 1@;?dvП}@ );M_qK@j?k䟯ïկwF!3EWi{ÿտ2 /*oSew?ϭϿ} O=߸oasoߩ߻' 14F@ 1l~|o/=/Ï2Vhz/Ovi{0fԟ_?ޏϯ>\wM_/(/ݿL/^/p////////6L ?7*?%N?`?r??????7^OO$O6OHOZOlO4d~8OOsOOO __0_B_T_f_x______oo>oPobo߆oooho o:(^0-VhzLԏ'//@/d/;ʟܟC?g?$?H?l?"?OدcO $?DOhO?OO/;_ q__@RdvψϚϬϾM׏ao8J\n߀ߒߤ_/|> ߀!3EWp?VAms'9K]o@#5JYk};?3Ugya/a&/8/J/n//_/_///S?w4?X??|?Ϡ??+?OOs0O_FϻxOOOϫK__eߓP_b___#__oo(o:oLo^opojNog+o]ooo .@dvw3f hm2 2QVhzԏ ./RdvП֯ǑLp`_jb #5GYk} p//)/;/M/_/q//////x/??%?7?I?[?m????sO?O?O!O3O5WOkߍOOOOOOO _8Ǒ&_8_Ǒ^_p__ ߦ__׏_og$oHoloo0oo?oc hݏ~);@R/ЏI/[/*OQd? ??Dhϟt_@d˯]/ﯬ!?Ÿ7/mPbϼG?k?(?L?p?OܯCOgOOH^?O __ؿ?Oc_ 2}O_hzϩ?_)o;o .@Rdv߈WߺCqu"4FX?|~jZ ?q'9K]o#5Gk} 1UyOV //-/?/Q/c/u//Cq//`Cq///"?4?SoX?wo|???+?O OO0OTOxOߜO_'OYoo,_>_t_x___GooEߏLo^ooooooo$6zxGYtxd*6L&;M_q~! Ϣԏ .@RdvП?*P]xvψ8Oo<ߣo`ooH3W{81o ASXj㏠o+as0BTfx  Y{$6HZl~Ko|> //0/B/ .{COƶg/y//////// ??-???Q?c?u?)4߫???$?OO)O>MO_O_/_OOO<_O__7_I_m_____\{__{o$o6oZoloooo?oc Dh;͏_2dv7/Q/Pbo:N٩//eoI1/C/U/g/y//////// ??-???Q?u????????O)O;OO_OOOOOOO`_%_7_I_[_m___M__jM__o,o>o]boooo5oY:^ 1cy6H~i /Ə؏#Q/ O/Vh//Ÿԟ .@^N`1nOȯگ"4?bGYk} ?W2?bͿ߿'9K]oϓϥϷϚo#5GYk}ߏOߕ81CUWy -Z1HZ1B/?"FjџR=aB;/ͯ/K]/b/t///5/?k}:?L?^?p??????:_?7 O.O@OROdOvOOOUOGxOO__)_;_M_٤Jgi_{________ oo/oAoSoeowoo6ooooo+@Oas1)K]oɏ-.@dvϾ%?I?m?*?Nr??!OޯEO׿iO&!q///z??1o6?UoZ?~?Ϣ? ?-?OOu2OVOzOs_O7oM __R_V=__%__#m*oߥobo&5Y}^to1oy6H~o ?Q 2DVhz buD dxA o7YG7E$-Wi7Gtx<?@vn?#9edQYҒ-Qu!$ W]gur!r$ ')%ȸE#%MEQ!"// 9&#bdz@1@#eϫ31%*?iI4wu1K?p?O0O/EM`_gm@gsWXA W2tI+WowsћM.߳MՇv%iIM2GՇQSrKK`2cZl~ƯدIr&=Ogyya}3E-Ŀ־Q&'9K]oϥϷ#5ߐ}ߔ!=&t/7%/i,|/[m///G-PEho/AhfxȊtI yw0(-DT! T8Xa]\C3(/3lBF]L" S31 ^LTzT[NRPLTh&_ASeqe;1Dk9Kq!怣 _gqd|g1&a7A>q???7I OO0OBOtѢ恞|@@p@v@@4F@@xsszu0n&e:Vv;Vv<Vv=Vv>V` S S-AVfABVUCV0id Q}EVFV2 S Q͆IVJVKV LVMV1N VEOVY SfmQVRVfSVTV0 SіVV3WVXV CYV!o[VfI\V]]Vfq^V_V0 SaVOBrvvvvvvvvv--AAUUii}}͆͆  11EEYYmmіі  !!8FI]]qq@,___O]Si-B=cPy;4&e(mwX':d nd t mm1=mmJmmmաmMQm*mm!m"Tq%>aon2q|ohqUqdq5uqdqsqdqdqds1hA=eqdU hiqah1h!uqdh1!h1hၠG!h-h 1h5h19hE=hYաhqEhqhMhQhUhGSof呠]hah eh!ihImh]QhqqhyhAh@qdNh]*`P`Y`q``q``e``q`Y```֤` d``A`,`U``ik`d`Y```0I,*`*`*`*`*`*`*` *` ,*` *` *`1 *` *`E*`*`Y*`,*`q*`*`*`*`*`*`*`,*`*`*`G*`*` !, "#!&'I()]*+,q,-./01ୡ23f̓T}ZdmSmxh*xA PumX-Q_x<?@vn+?_tgupufx r!կ--!tܡaut"}v1}-a8>26q|Co3zDxyPbz@Qh@01_0P贁#NkjEw)lS0U!%ESix%~U/x6譡_DӤ۩0s!8b 9b\wxpݧqğ {u#5w r22O|rU*&00goooo:%I ^a~s~Ê({o vo-PBWi{ÏՏ /ASewүќP䙊\NrQɯcCB&8VS\kj|ՇEſ׿鿓ITkQgauj~|`jt!CUg89YBi0jT1D &0U0DDx ϙ̛ѿRA'/@iߠ߲?!eoi>L !E{D0+{DDGA0TY)[mSew3iw[1u-OGHaL80G0%7I[m&γXHR%R&]/*,s/ϩ/'R/$uPTofxϊϜ,> )y4?@p8@)pЁFM z3h@F X;p:xhM&d2?peH1̟.E2kj nnLeہd݈^RTg  @bpἅyDЉa* ˜fln@#ƀ@ nFmOBpBV._A_'98zOODh56ffV/h/z/1/C/Ooaoso//!//2}!!v/`Lq?~ooooooȯooZLq "LqTqwMmӿ!3CRdv؈pr@K#{f5 J|O>xy- 2p6"߫ߑKџ㟛<ЮV$6H7I擯$ۯ 5G%ewx 7¿( .@RdvψCAܧϹWNC"H$ĈxX?ޫ O߷J?~omFFh4߀UpPlXA&-/@/&8Vyn66Ugy0BN?`?r?u1My_wKA}O;M_q (3 2D~{{1ƆJ^T(B8[0 sk"pCDfV/CoUo7ؖfoo{//EXP__TV//dhEkvjwgFRB????????KAO!O`vA9/B?BpmnMe]sagRf_To 5Rl BpmnMesagRf__To Eti#y  BpmnDirect o&Textan}mrkug.49*Text AUnoain.490msvWarwnOPesoal5IfZeigr (1-D) Pointer(1-D)6Funkti]o /Sbrune.426Functi]o /sbrune.426Funkti]o /Sbrune.436Functi]o /sbrune.43$Zeigr (1-D).4&Pointer (1-D). 4$Zeigr (1-D).46&Pointer (1-D).46$Zeigr (1-D).5&Pointer (1-D). 5$Zeigr (1-D).52&Pointer (1-D).52$Zeigr (1-D).53&Pointer (1-D).536Funkti]o /Sbrune.546Functi]o /sbrune.546Funkti]o /Sbrune.5!6Functi]o /sbrune.5!$Zeigr (1-D).56&Pointer (1-D).56$Zeigr (1-D).57&Pointer (1-D).576Funkti]o /Sbrune.586Functi]o /sbrune.586Funkti]o /Sbrune.596Functi]o /sbrune.596Funkti]o /Sbrune.06Functi]o /sbrune.06Funkti]o /Sbrune.16Functi]o /sbrune.16Funkti]o /Sbrune.26Functi]o /sbrune.26Funkti]o /Sbrune.36Functi]o /sbrune.36Funkti]o /Sbrune.46Functi]o /sbrune.46Funkti]o /Sbrune.56Functi]o /sbrune.56Funkti]o /Sbrune.6Functi]o /sbrune.6Funkti]o /Sbrune.76Functi]o /sbrune.76Funkti]o /Sbrune.86Functi]o /sbrune.86Funkti]o /Sbrune.96Functi]o /sbrune.96Funkti]o /Sbrune.706Functi]o /sbrune.706Funkti]o /Sbrune.716Functi]o /sbrune.716Funkti]o /Sbrune.726Functi]o /sbrune.726Funkti]o /Sbrune.736Functi]o /sbrune.736Funkti]o /Sbrune.746Functi]o /sbrune.746Funkti]o /Sbrune.756Functi]o /sbrune.756Funkti]o /Sbrune.76Functi]o /sbrune.76Funkti]o /Sbrune.7!6Functi]o /sbrune.7!6Funkti]o /Sbrune.786Functi]o /sbrune.786Funkti]o /Sbrune.796Functi]o /sbrune.796Funkti]o /Sbrune.806Functi]o /sbrune.806Funkti]o /Sbrune.816Functi]o /sbrune.816Funkti]o /Sbrune.826Functi]o /sbrune.826Funkti]o /Sbrune.836Functi]o /sbrune.836Funkti]o /Sbrune.846Functi]o /sbrune.846Funkti]o /Sbrune.856Functi]o /sbrune.856Funkti]o /Sbrune.86Functi]o /sbrune.86Funkti]o /Sbrune.876Functi]o /sbrune.876Funkti]o /Sbrune.8!6Functi]o /sbrune.8!6Funkti]o /Sbrune.896Functi]o /sbrune.89&Zeigr (1-Dw).13(Pointer 1-D).136Funkti]o /Sbrune.916Functi]o /sbrune.916Funkti]o /Sbrune.926Functi]o /sbrune.926Funkti]o /Sbrune.936Functi]o /sbrune.936Funkti]o /Sbrune.946Functi]o /sbrune.946Funkti]o /Sbrune.956Functi]o /sbrune.956Funkti]o /Sbrune.96Functi]o /sbrune.96Funkti]o /Sbrune.976Functi]o /sbrune.97&Zeigr (1-Dw).14(Pointer 1-D).14&Zeigr (1-Dw).15(Pointer 1-D).15&Zeigr (1-Dw).16(Pointer 1-D).16&Zeigr (1-Dw).17(Pointer 1-D).17&Zeigr (1-Dw).18(Pointer 1-D).18&Zeigr (1-Dw).19(Pointer 1-D).19&Zeigr (1-Dw).20(Pointer 1-D).20&Zeigr (1-D).2(Pointer 1-_D).2 (DD3[GG3\%G3T$G3TE3HhG335G3D35G3,=#G3,`!G335G335G335G3D3 5G3`U(G3[}G3%G3Խ&G3TE3$HhG3, *G3345G3`i+G3T`(G3d1G3`.G3%G3LQP@G3dQPUG3TmE3<}G3XG3'G3`)G335G3345G3D3i5G335G335G3,/G37/G3`fA3j/G34`.G3`A3`A3"G3DHhG3;G3K!G33l9G3`)G37G3L3G3`8*G3b2G3t`-G3dHhG3HhG3T"G3,$G3XT@E3HhNG3lG3`*G3$G3G3TE3ԼG3`)G3`H'G3[oG3[G3[G3[G3_PG3[G3[G3[G3[/G3ؐGG3aG3 {G3<G3G3G3G3ؑG3G3 1G3<KG3X3e&G3XG3T\3 G3HhG3HhG3IhG3$Ih G3,\3<G3\3ZG3[3x&G3DIhG3[3&G3dIhG3[3"G3T[3" G34B,G3n*G3t.G3D38G34/G3`-+G3܉X.G3?G338G35G3329G3lk4G340G32G33;G3D3<9G33u8G3/G33:G3Ĉ2G3H/G3w2G3T2G33=G37G3,[3O%G3[3t'G3G3G30G3ļ)G3Z3( G3`HA3`LA3`PA3`TA3`XA3`\A3``A3`dA3`hA3`lA3`pA3`tA3`xA3`|A3`A3 `A3\`A3l`A3t`A3d`A3|`A3`A3Z3#G3Z3%G3IhG3L[G3TZ3&G3$?)G3Th)G3G3ؒG3,Z3$G3佳.G3-G3D1G3$uIG3$]PG3D39G3GG3Z3a(G3^3&G334G3(G3t 1G3d<=G3ĖyEG3^3$G3D.G338G3H>G3=G3DCG3>ZG3$`]G3$JG3<1G38KG35G3<G3-G3!GG3$h3G3t6G3T^3$G3t)G3+G3̆I2G3,^3{"G3^3$G335G3D35G33+5G33`5G3]3(G3Ծ,G3]3(G3,G3]3=(G3 `e,G3T]3(G3`,G3,]3(G3` ,G3395G3D3n5G335G335G3]3 (G3亳5,G3\3a(G3,G335G3D35G335G33T5G334G3D34G334G33%4G33Y4G3D34G334G334G33)4G3D3]4G334G334G333G3D3,3G33_4G334G334G3D34G33/4G33c4G335G3D35G335G3P65G3DPk5G3P5G3?5G3> 5G3>?5G3D>t5G3>5G3=5G3=3G3D=F3G3=y5G3<5G3<5G3D<5G3D M5G3 5G3 5G3 5G3D3! 5G33V 5G33 5G3P 5G3P 5G3DP* 5G3P_ 5G3P 5G3P 5G3DP 5G3P3 3G3Pf 3G3P 5G3DP 5G3P 5G3P8 5G3Pm 5G3DP 5G3 )G3 ,G3P, 5G3Pa 5G3P 5G3DP 5G3P5G3P55G3Pj5G3DP5G3P5G3P 5G3P>3G3DPq3G3P5G3P5G3)G3Ի7,G3c)G3D,G3d)G3t,G3` )G3`6,G3b)G3h3,G3t`)G34,G3t )G3D5,G3a)G3,G  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABC]>rFxml:spaceprsrv]rFxmlns:vdhtp/%scea .i+ro of.+o/i iA/20*c6%et/ \n]rFxmlns:v14htp/'sUcea .i-ro oUf.-o/GUf=e'v=s=]o'200'Eet1 lnU.U-t4 "H$ @Y,b'@T?/$C6E 0+ AU %t4 ̧[`0 C-`m7"AJ@?0LR@SZo06RH<(H<(JE?1RESZ1R{č  g"4FXo 8)i(N1j@(Mw]y5hGB C Qz`X> BvZR$[Ox!{)_V'l]~P1d4sP2T! ײ.B="dnG zK&D+Qd/?$f0W*'"D\y 1~%!././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/internals/security.rst0000644000076500000240000005022314601576577017572 0ustar00twstaff.. somewhat surprisingly the "bash" highlighter gives nice results with the pseudo-code notation used in the "Encryption" section. .. highlight:: bash ======== Security ======== .. _borgcrypto: Cryptography in Borg ==================== .. _attack_model: Attack model ------------ The attack model of Borg is that the environment of the client process (e.g. ``borg create``) is trusted and the repository (server) is not. The attacker has any and all access to the repository, including interactive manipulation (man-in-the-middle) for remote repositories. Furthermore the client environment is assumed to be persistent across attacks (practically this means that the security database cannot be deleted between attacks). Under these circumstances Borg guarantees that the attacker cannot 1. modify the data of any archive without the client detecting the change 2. rename, remove or add an archive without the client detecting the change 3. recover plain-text data 4. recover definite (heuristics based on access patterns are possible) structural information such as the object graph (which archives refer to what chunks) The attacker can always impose a denial of service per definition (he could forbid connections to the repository, or delete it entirely). When the above attack model is extended to include multiple clients independently updating the same repository, then Borg fails to provide confidentiality (i.e. guarantees 3) and 4) do not apply any more). .. _security_structural_auth: Structural Authentication ------------------------- Borg is fundamentally based on an object graph structure (see :ref:`internals`), where the root object is called the manifest. Borg follows the `Horton principle`_, which states that not only the message must be authenticated, but also its meaning (often expressed through context), because every object used is referenced by a parent object through its object ID up to the manifest. The object ID in Borg is a MAC of the object's plaintext, therefore this ensures that an attacker cannot change the context of an object without forging the MAC. In other words, the object ID itself only authenticates the plaintext of the object and not its context or meaning. The latter is established by a different object referring to an object ID, thereby assigning a particular meaning to an object. For example, an archive item contains a list of object IDs that represent packed file metadata. On their own it's not clear that these objects would represent what they do, but by the archive item referring to them in a particular part of its own data structure assigns this meaning. This results in a directed acyclic graph of authentication from the manifest to the data chunks of individual files. .. _tam_description: .. rubric:: Authenticating the manifest Since the manifest has a fixed ID (000...000) the aforementioned authentication does not apply to it, indeed, cannot apply to it; it is impossible to authenticate the root node of a DAG through its edges, since the root node has no incoming edges. With the scheme as described so far an attacker could easily replace the manifest, therefore Borg includes a tertiary authentication mechanism (TAM) that is applied to the manifest since version 1.0.9 (see :ref:`tam_vuln`). TAM works by deriving a separate key through HKDF_ from the other encryption and authentication keys and calculating the HMAC of the metadata to authenticate [#]_:: # RANDOM(n) returns n random bytes salt = RANDOM(64) ikm = id_key || enc_key || enc_hmac_key # *context* depends on the operation, for manifest authentication it is # the ASCII string "borg-metadata-authentication-manifest". tam_key = HKDF-SHA-512(ikm, salt, context) # *data* is a dict-like structure data[hmac] = zeroes packed = pack(data) data[hmac] = HMAC(tam_key, packed) packed_authenticated = pack(data) Since an attacker cannot gain access to this key and also cannot make the client authenticate arbitrary data using this mechanism, the attacker is unable to forge the authentication. This effectively 'anchors' the manifest to the key, which is controlled by the client, thereby anchoring the entire DAG, making it impossible for an attacker to add, remove or modify any part of the DAG without Borg being able to detect the tampering. Note that when using BORG_PASSPHRASE the attacker cannot swap the *entire* repository against a new repository with e.g. repokey mode and no passphrase, because Borg will abort access when BORG_PASSPHRASE is incorrect. However, interactively a user might not notice this kind of attack immediately, if she assumes that the reason for the absent passphrase prompt is a set BORG_PASSPHRASE. See issue :issue:`2169` for details. .. [#] The reason why the authentication tag is stored in the packed data itself is that older Borg versions can still read the manifest this way, while a changed layout would have broken compatibility. .. _security_encryption: Encryption ---------- Encryption is currently based on the Encrypt-then-MAC construction, which is generally seen as the most robust way to create an authenticated encryption scheme from encryption and message authentication primitives. Every operation (encryption, MAC / authentication, chunk ID derivation) uses independent, random keys generated by `os.urandom`_ [#]_. Borg does not support unauthenticated encryption -- only authenticated encryption schemes are supported. No unauthenticated encryption schemes will be added in the future. Depending on the chosen mode (see :ref:`borg_init`) different primitives are used: - The actual encryption is currently always AES-256 in CTR mode. The counter is added in plaintext, since it is needed for decryption, and is also tracked locally on the client to avoid counter reuse. - The authentication primitive is either HMAC-SHA-256 or BLAKE2b-256 in a keyed mode. Both HMAC-SHA-256 and BLAKE2b have undergone extensive cryptanalysis and have proven secure against known attacks. The known vulnerability of SHA-256 against length extension attacks does not apply to HMAC-SHA-256. The authentication primitive should be chosen based upon SHA hardware support: all AMD Ryzen, Intel 10th+ generation mobile and Intel 11th+ generation desktop processors, Apple M1+ and most current ARM64 architectures support SHA extensions and are likely to perform best with HMAC-SHA-256. 64-bit CPUs without SHA extensions are likely to perform best with BLAKE2b. - The primitive used for authentication is always the same primitive that is used for deriving the chunk ID, but they are always used with independent keys. Encryption:: id = AUTHENTICATOR(id_key, data) compressed = compress(data) iv = reserve_iv() encrypted = AES-256-CTR(enc_key, 8-null-bytes || iv, compressed) authenticated = type-byte || AUTHENTICATOR(enc_hmac_key, encrypted) || iv || encrypted Decryption:: # Given: input *authenticated* data, possibly a *chunk-id* to assert type-byte, mac, iv, encrypted = SPLIT(authenticated) ASSERT(type-byte is correct) ASSERT( CONSTANT-TIME-COMPARISON( mac, AUTHENTICATOR(enc_hmac_key, encrypted) ) ) decrypted = AES-256-CTR(enc_key, 8-null-bytes || iv, encrypted) decompressed = decompress(decrypted) ASSERT( CONSTANT-TIME-COMPARISON( chunk-id, AUTHENTICATOR(id_key, decompressed) ) ) The client needs to track which counter values have been used, since encrypting a chunk requires a starting counter value and no two chunks may have overlapping counter ranges (otherwise the bitwise XOR of the overlapping plaintexts is revealed). The client does not directly track the counter value, because it changes often (with each encrypted chunk), instead it commits a "reservation" to the security database and the repository by taking the current counter value and adding 4 GiB / 16 bytes (the block size) to the counter. Thus the client only needs to commit a new reservation every few gigabytes of encrypted data. This mechanism also avoids reusing counter values in case the client crashes or the connection to the repository is severed, since any reservation would have been committed to both the security database and the repository before any data is encrypted. Borg uses its standard mechanism (SaveFile) to ensure that reservations are durable (on most hardware / storage systems), therefore a crash of the client's host would not impact tracking of reservations. However, this design is not infallible, and requires synchronization between clients, which is handled through the repository. Therefore in a multiple-client scenario a repository can trick a client into reusing counter values by ignoring counter reservations and replaying the manifest (which will fail if the client has seen a more recent manifest or has a more recent nonce reservation). If the repository is untrusted, but a trusted synchronization channel exists between clients, the security database could be synchronized between them over said trusted channel. This is not part of Borg's functionality. .. [#] Using the :ref:`borg key migrate-to-repokey ` command a user can convert repositories created using Attic in "passphrase" mode to "repokey" mode. In this case the keys were directly derived from the user's passphrase at some point using PBKDF2. Borg does not support "passphrase" mode otherwise any more. .. _key_encryption: Offline key security -------------------- Borg cannot secure the key material while it is running, because the keys are needed in plain to decrypt/encrypt repository objects. For offline storage of the encryption keys they are encrypted with a user-chosen passphrase. A 256 bit key encryption key (KEK) is derived from the passphrase using PBKDF2-HMAC-SHA256 with a random 256 bit salt which is then used to Encrypt-*and*-MAC (unlike the Encrypt-*then*-MAC approach used otherwise) a packed representation of the keys with AES-256-CTR with a constant initialization vector of 0. A HMAC-SHA256 of the plaintext is generated using the same KEK and is stored alongside the ciphertext, which is converted to base64 in its entirety. This base64 blob (commonly referred to as *keyblob*) is then stored in the key file or in the repository config (keyfile and repokey modes respectively). This scheme, and specifically the use of a constant IV with the CTR mode, is secure because an identical passphrase will result in a different derived KEK for every key encryption due to the salt. The use of Encrypt-and-MAC instead of Encrypt-then-MAC is seen as uncritical (but not ideal) here, since it is combined with AES-CTR mode, which is not vulnerable to padding attacks. .. seealso:: Refer to the :ref:`key_files` section for details on the format. Refer to issue :issue:`747` for suggested improvements of the encryption scheme and password-based key derivation. Implementations used -------------------- We do not implement cryptographic primitives ourselves, but rely on widely used libraries providing them: - AES-CTR and HMAC-SHA-256 from OpenSSL 1.0 / 1.1 are used, which is also linked into the static binaries we provide. We think this is not an additional risk, since we don't ever use OpenSSL's networking, TLS or X.509 code, but only their primitives implemented in libcrypto. - SHA-256, SHA-512 and BLAKE2b from Python's hashlib_ standard library module are used. Borg requires a Python built with OpenSSL support (due to PBKDF2), therefore these functions are delegated to OpenSSL by Python. - HMAC, PBKDF2 and a constant-time comparison from Python's hmac_ standard library module is used. While the HMAC implementation is written in Python, the PBKDF2 implementation is provided by OpenSSL. The constant-time comparison (``compare_digest``) is written in C and part of Python. Implemented cryptographic constructions are: - Encrypt-then-MAC based on AES-256-CTR and either HMAC-SHA-256 or keyed BLAKE2b256 as described above under Encryption_. - Encrypt-and-MAC based on AES-256-CTR and HMAC-SHA-256 as described above under `Offline key security`_. - HKDF_-SHA-512 .. _Horton principle: https://en.wikipedia.org/wiki/Horton_Principle .. _HKDF: https://tools.ietf.org/html/rfc5869 .. _length extension: https://en.wikipedia.org/wiki/Length_extension_attack .. _hashlib: https://docs.python.org/3/library/hashlib.html .. _hmac: https://docs.python.org/3/library/hmac.html .. _os.urandom: https://docs.python.org/3/library/os.html#os.urandom Remote RPC protocol security ============================ .. note:: This section could be further expanded / detailed. The RPC protocol is fundamentally based on msgpack'd messages exchanged over an encrypted SSH channel (the system's SSH client is used for this by piping data from/to it). This means that the authorization and transport security properties are inherited from SSH and the configuration of the SSH client and the SSH server -- Borg RPC does not contain *any* networking code. Networking is done by the SSH client running in a separate process, Borg only communicates over the standard pipes (stdout, stderr and stdin) with this process. This also means that Borg doesn't have to directly use a SSH client (or SSH at all). For example, ``sudo`` or ``qrexec`` could be used as an intermediary. By using the system's SSH client and not implementing a (cryptographic) network protocol Borg sidesteps many security issues that would normally impact distributing statically linked / standalone binaries. The remainder of this section will focus on the security of the RPC protocol within Borg. The assumed worst-case a server can inflict to a client is a denial of repository service. The situation where a server can create a general DoS on the client should be avoided, but might be possible by e.g. forcing the client to allocate large amounts of memory to decode large messages (or messages that merely indicate a large amount of data follows). The RPC protocol code uses a limited msgpack Unpacker to prohibit this. We believe that other kinds of attacks, especially critical vulnerabilities like remote code execution are inhibited by the design of the protocol: 1. The server cannot send requests to the client on its own accord, it only can send responses. This avoids "unexpected inversion of control" issues. 2. msgpack serialization does not allow embedding or referencing code that is automatically executed. Incoming messages are unpacked by the msgpack unpacker into native Python data structures (like tuples and dictionaries), which are then passed to the rest of the program. Additional verification of the correct form of the responses could be implemented. 3. Remote errors are presented in two forms: 1. A simple plain-text *stderr* channel. A prefix string indicates the kind of message (e.g. WARNING, INFO, ERROR), which is used to suppress it according to the log level selected in the client. A server can send arbitrary log messages, which may confuse a user. However, log messages are only processed when server requests are in progress, therefore the server cannot interfere / confuse with security critical dialogue like the password prompt. 2. Server-side exceptions passed over the main data channel. These follow the general pattern of server-sent responses and are sent instead of response data for a request. The msgpack implementation used (msgpack-python) has a good security track record, a large test suite and no issues found by fuzzing. It is based on the msgpack-c implementation, sharing the unpacking engine and some support code. msgpack-c has a good track record as well. Some issues [#]_ in the past were located in code not included in msgpack-python. Borg does not use msgpack-c. .. [#] - `MessagePack fuzzing `_ - `Fixed integer overflow and EXT size problem `_ - `Fixed array and map size overflow `_ Using OpenSSL ============= Borg uses the OpenSSL library for most cryptography (see `Implementations used`_ above). OpenSSL is bundled with static releases, thus the bundled copy is not updated with system updates. OpenSSL is a large and complex piece of software and has had its share of vulnerabilities, however, it is important to note that Borg links against ``libcrypto`` **not** ``libssl``. libcrypto is the low-level cryptography part of OpenSSL, while libssl implements TLS and related protocols. The latter is not used by Borg (cf. `Remote RPC protocol security`_, Borg itself does not implement any network access) and historically contained most vulnerabilities, especially critical ones. The static binaries released by the project contain neither libssl nor the Python ssl/_ssl modules. Compression and Encryption ========================== Combining encryption with compression can be insecure in some contexts (e.g. online protocols). There was some discussion about this in `github issue #1040`_ and for Borg some developers concluded this is no problem at all, some concluded this is hard and extremely slow to exploit and thus no problem in practice. No matter what, there is always the option not to use compression if you are worried about this. .. _github issue #1040: https://github.com/borgbackup/borg/issues/1040 Fingerprinting ============== Stored chunk sizes ------------------ A borg repository does not hide the size of the chunks it stores (size information is needed to operate the repository). The chunks stored in the repo are the (compressed, encrypted and authenticated) output of the chunker. The sizes of these stored chunks are influenced by the compression, encryption and authentication. buzhash chunker +++++++++++++++ The buzhash chunker chunks according to the input data, the chunker's parameters and the secret chunker seed (which all influence the chunk boundary positions). Small files below some specific threshold (default: 512 KiB) result in only one chunk (identical content / size as the original file), bigger files result in multiple chunks. fixed chunker +++++++++++++ This chunker yields fixed sized chunks, with optional support of a differently sized header chunk. The last chunk is not required to have the full block size and is determined by the input file size. Within our attack model, an attacker possessing a specific set of files which he assumes that the victim also possesses (and backups into the repository) could try a brute force fingerprinting attack based on the chunk sizes in the repository to prove his assumption. To make this more difficult, borg has an ``obfuscate`` pseudo compressor, that will take the output of the normal compression step and tries to obfuscate the size of that output. Of course, it can only **add** to the size, not reduce it. Thus, the optional usage of this mechanism comes at a cost: it will make your repository larger (ranging from a few percent larger [cheap] to ridiculously larger [expensive], depending on the algorithm/params you wisely choose). The output of the compressed-size obfuscation step will then be encrypted and authenticated, as usual. Of course, using that obfuscation would not make any sense without encryption. Thus, the additional data added by the obfuscator are just 0x00 bytes, which is good enough because after encryption it will look like random anyway. To summarize, this is making size-based fingerprinting difficult: - user-selectable chunker algorithm (and parametrization) - for the buzhash chunker: secret, random per-repo chunker seed - user-selectable compression algorithm (and level) - optional ``obfuscate`` pseudo compressor with different choices of algorithm and parameters Stored chunk proximity ---------------------- Borg does not try to obfuscate order / proximity of files it discovers by recursing through the filesystem. For performance reasons, we sort directory contents in file inode order (not in file name alphabetical order), so order fingerprinting is not useful for an attacker. But, when new files are close to each other (when looking at recursion / scanning order), the resulting chunks will be also stored close to each other in the resulting repository segment file(s). This might leak additional information for the chunk size fingerprinting attack (see above). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/internals/structure.png0000644000076500000240000061173214601576577017747 0ustar00twstaffPNG  IHDR 3UsRGBgAMA a pHYs\F\FCAIDATx^5I/ϟbs L4H6##HO2a'"'2 D0LdDO sf۶z7:ݫWU{{U5``````````A=aE`````````:P44U[e````````c`````````\৤6S˜````````g@XOX`````````s%M%MVm`````````)`````````l@;)M0f````````c`````````\`I~ISU[````````xa=a=`````````:P~JjS;%`````````~`````````l@;XҴ_TmՖ````````b@XOX`````````sN c`````````a=a=`````````:P44U[e````````c`````````\৤6S˜````````g@XOX`````````s%M%MVm`````````)`````````l@;)M0f````````c`````````\`I~ISU[````````xa=a=`````````:P~JjS;%`````````~`````````l@;XҴ_TmՖ````````b@XOX`````````sN c`````````a=a=`````````:P44U[e````````c`````````\৤6S˜````````g@XOX`````````s%M%MVm`````````)`````````l@;)M0f````````c`````````\`I~ISU[````````xa=a=`````````:P~JjS;%`````````~`````````l@;XҴ_TmՖ````````b@XOX`````````sN c`````````a=a=`````````:P44U[e````````c`````````\৤6S˜````````g@XOX`````````s%M%MVm`````````)`````````l@;)M0f````````c`````````\`I~ISU[````````xa=a=`````````:P~JjS;%`````````~`````````l@;XҴ_TmՖ````````b@XOX`````````sN c`````````a=a=`````````:P44U[e````````c`````````\৤6S˜````````g@XOX`````````s%M%MVm`````````)`````````l@;)M0f````````c`````````\`I~ISU[````````xa=a=`````````:P~JjS;%`````````~`````````l@;XҴ_TmՖ````````b@XOX`````````sN c`````````a=a=`````````:P44U[e````````c`````````\৤6S˜````````g@XOX`````````s%M%MVm`````````)`````````l@;)M0f````````c`````````\`I~ISU[````````xa=a=`````````:P~JjS;%`````````~`````````l@;XҴ_TmՖ````````b@XOX`````````sN c`````````a=a=`````````:P44U[e````````c`````````\৤6S˜````````g@XOX`````````s%M%MVm`````````)`````````l@;)M0f````````c`````````\`I~ISU[````````xa=a=`````````:P~JjS;%`````````~`````````l@;XҴ_TmՖ````````b@XOX`````````sN c`````````a=a=`````````:P44U[e````````c``````K }~g_uZ ``m7< 0 0 0 0 0 0G '?y͟~o/? . 0 <#} `````` D$?|Og Q7 0 0i?X{om1 0 0 0 0 0CO̼ܿZ1 0p?zi/Q`````xwWZ0_>sn<'`2zY~O>ď0 0 0 0 0 0c |_>GpïߓMzSo~vd>\1 0@aߨNaȀ[:mB 0 0 0 0 0D{o^D:f`5  {l 0 0 0 0 0#{=ǿկy,BGs\3 L|*g`````xa״?ۏ~ٵz<Sǃvx`q  10շ 0 0 0 0 00a=ţrӟ}[ڵ7>L`=1E`` \ހ+9&w 0 0 0 0 0- q(Bw?C{_* 0 0.=x?_`Gq 0 0 0 0 0S [bwݪ^8^YxR'`````g@X~}q`3_Xy`` vbD- 0 0 0 0 0@\?_j`/~]` z``S rVo3a:N 0 0 0 0 0 π?\]ϣp{qm`g0*Ah_ 0 0 0 0 0 a=x7WO~C0 0 7 7oa 0 0 0 0 0 1 ק{zַg杻y`}P0 0 0 0 0 0n@Xi=F~_m> >ikKg~b`|)gb8:1x=C 0 0 0 0 0Wcaw xߴ%3?1 0Qz31!````` ^<6l~Z' 0 0szz hDog````xa=v{wO~"瞙 0 0@wʹ}fe=# 0 0 0 0 0 <Հ=?X }ss޽s``f>: 0 0 0 0 0c_jX3 0 t?o_VG+w3 0 0 0 0c6_>Ca=7;g`~#;G`````1 ٯW_zn7 0 \n\| #vDΉk`````5 F?=:ۜ1 0)z&F v> 0 0 0 0 0O5 nz\pe\1 0@a=a=4P;Ń`````.XaQX+`5 5`Pz&ډ0 0 0 0 0 ŀ=, qÕ}r 0@D`{`` xOZ7 0p||6zVAXL;`xa9M`8>YzcE_0pak?a=Ff`Y jYYYФhk`B?ok'V7&mߨz2 bX/VY^ w~ 0pQƁe>e@XsW=\'W 0 0Pk@XOX Nw@ȫ}4yyҵ4a=aFyb`Zn<aL@*xQmk!cd1}j}qzkPX+`ahecz"'g|<}|h? W ulJ裏h{fGP'"SHdB{5*QU˔1o~_ӱޑg}O3 WW/%WV'ԉ`k@Xo3z}II}\X?~&H,C n2޷K}^-YʸFkǔy 曘sc>}1u9Gո__}yΫ+ꕼ;W`a=a=4`ҽ狼oM.Ws9{О%}P_8+R5b pcֻ+c}zNW=N a=Ǹ㪇+`j j Բv"=3h֋Z|'Ufb(r> W ƿz%w>/-W({U~7l@XϘ1&>b`Zzz hv"(Zw=2^F/΃W7JaTpF @뀰m ݳRM~Ͼ5گf@X^s 0 0AZVۆorP#42fɘv 3*ajzss =zwe߳Rɣp_=O 1_c՘`6 'L~ۓYa0(7s~e JX/w|kyg^MX}??1fm7 0ˀÖW=\'W 0 0Pk@Pkg"l(OK;x o{Hk?q_O7Vm߾oD;ro-VtL?wc"?|ZhbjobޏadWq21ks>DZ^7Wߚ{˰^f%39gh.QsDz?[ܵ}U`'w~=%&7^P\;̙qu-:5]z%=8^j~:jj\5v\{6 0^X+`5 'j'ۻxb)a闺 SL7<ܑ Ss~%VbqCim_s~Jn dSҺ&޿ZD޽{cX(WY5?%%7~K3w(ii--MZJ0Y1u朖Ch93VQϵgsba=z z\pe\1 0@AZV3NgfgjaҀM.x=<vڲu5UPSwGW)Y&D Ֆ=m˅7v*1s[7Src> і17KB[ǿKMclI=u|f9/jg(˖aeMA}N[d. 9E{J !gY3w*5fr)2Ek߹,:z:.y5j 磒k'KB[m8uܚGͫ6ݯZS3/k@X}wq'՝}j 0 a=a=4`I\_s_ nMٵGnR/o&mY 땶mސ֍JSҮ65ҺX*i&քz{‡.{qP@^[hiȱRoYӘ'!V3^u92Laa#m?ޚӦ=q-kayӳwt%s-/%b#ןy-J*jOM1>e=JB^8ʽlH`\j_1k3])vz8o1^{a=zٲ_``ƀրA-+j&2aԪ$Ztذ ;Mڕrawk!*.x]g)}D|\ lz:rۗ\۶o7t31EJVߚ6˵#Vڎ)v#t_5m=o7 i&82sږܪ~9/e58ue|L\϶ լP[[1Ǫb-WA+9\hlqQ2?;zL5l==*7/54նzrL Nzv[rz|*8{ac8z|m]^NY o@Xo>b qu;`ЀdZrAk!ٶzW͍Tmrae0a \?WxZ._qq: 󔎁 A$SmKP Dl"uc0-j^蓵TJH+qonkJr}E?]ܪV;J~ze̜ܒ3KWo[ׂюܼYZx+ ~s>%c6R:2sϵ8+nDM[=}.߳1E/b óW=\'W 0 0Pk@XOX Nwް^ܬmkrݺU)Ydx%R8?^Z|q#>w&t3Gikrs@#IFV]s; mvKZXd5!~s/ Դt~<iέ8Vy}8G뻽mӜ9:q]2J|IJ+[Xɍ=tܼܣXKSۯ57^[7- Omq%+{d$R>aӏ>[bjm*'UW 0 0ee=AD`{fF1h] knҖ\MI.UBZXoO8ajb?`Ti\8so(*Vvz]M^{_MPo`Y>1tb[#_tkue[r>UfjqڒjG} sfame1k*SH:8261 B֬9K޽Һݮ#~^֫yroSϽ/G /\[_#zr}::wu1|z;Ca=Rd`  10M>Z=n- Q:wdUڴ^IbnuޮS7{&@͞} n< V4[r}!w#v?[rOKiߴi.|URL.w#hS~I[RKB{o6;sfq8?THd%\mRK{v#;#jUb`ZZg"l(ZhR7ZL=&kstY^֋}-ϫw!|N+8\xuf^MM{Z {tցJxGjq4X[ӣY /wX/ Lȭwź3zGGz:ZzN~qmWك)O+ƱWWsL```i@XOX MjWXo 핆1r7Wj~{WQJGoBzΞG'(96G"9ysjޛ{eKj;:Ou[uqoER:{Μkũ>Kl绌sͭXR+Ḿ5_ɭ7(c/1 sWZ:ٮ쳊\%QԉאW 0 0pZgr ^a ֋An'mp\ɍі-{V\nF lwt-GnyrCG˚%=5fKKF\+"7Rڟ۵ 8=g{뱽֚Ju^~Z񣡲Tx?u=T?׎{ [-jԣ=yӾ^_)9c䘶) ?g\WSy?Z1߀^ca=^ѽs`c`@&M>iQe1VOjX{7x]G4A#76w-k&kGǽ[tTJW;Jzq}Ęz̵\m@:Z_ 5[c"* z~i򱼥!'onFh'JG^^azz\J.kʞrt^, 6GV:߷wΞ3{r+n=(=k|mP{#ckVlyS0r]]^ֶǜckxZ}:5zr^{|ۯ}+W?G+ƱWWsL```i@XOX MjuX/ZdzaνoYah(65 ޖ\ M}wt{Xz?#E$L0-ŵP2۞ܖsfϰ^B U&x~~rXtz?M_{WܚjcTVخ=aZkYXޟݻjǛ' w qe|0 0w0 5`Pz&;L.zr+$N$Zx۳QPJOsD;guܪT[na=':\mBXo{?j.-BzSȠ4f^+G뗚7Z͋޽[}t2"S2yr=_r=gFy^5kJ93w.Z]r+`VRsF}eiM@oV-{q%cvg˔Wԉ{N_=i\ q$+ 0c`@&q'm}޷=zQ\,ז|VAF{Z(~W"ѱG9TMÎt1X8bnqclk YYon!0kIQn߳{A{>Z=;)+o:爫]bnhY׎X[sT~<1k9>6o箳q1Ǖn+"??յN <π LX'8FN`߀րA-+?pM^a~s7zr+m ֋{WY87U;&R7|Fz~J݄uio _f^+{6Fem1OsΉ&=g Yg]vg^{o駟&mOݮi[ޗz=u>nVi_[m}}:oW( ZNԋ֓/)Ǖ 0  .rWhLNa܍VzR7'VlI+Mjc0Aϛw07'<>T[RN$s?VJWM;5kǘ=Rk\l_[9-mأ%cթ֦K#wu=z_=mpw#Sg=7G{Ӛ~Ӿzo>hy Wګz1p/zQƇWXv,3 0lZAiR 7FmXW;zRbxqCxq5+gޏW5=&5>2[kGXH]jjV=C¹`Nj;{Μ#3}gwgJ];}ŹS8̭,{k[ckz=UXSAɒ}߸-euW/b^a=F7WkZΞ3{r=cra|GZqJm%Վxzm*mޏBc{Ş9gK{ڗΑU 8/=7Wԉ{N_=i\ q$+ 00eeqX2nWX/RU0r[Výn2z9:,a|X/w}ϪhG{zLξ޺%ߞHjgϙz=Ÿ%˭ZSz}!`r+-߳eg~n=r=]$,5}שW1 wi\q5g3 05 'Lϝݱ֋o[+^sFَ/ o '8΅z>rd{XzaGF7Zr׋ii;bΌ6 )؞PRi_ =zϔ_XS+^BC8z]{ mU}fm9%}lmu<;[^ ݯOF'z\9p 0 j ԲU2@^nUέ']ћ)`X:+ptnŊqzϾQfk.F67G^jK`Um w.{V::K* >z>H/sfef~>=W;-~e|G[skZɹɽwAc<~zy-rU[=˽7ν]Il;o_utk9fu w])[j׸Lzyͧ1Ֆ&7uӜs=n5z!Mg@X_m_٭W߹ 0}  10}&Y}qn_n ι{ny#vcOUYGZ57d%n^;#e ymҕahO+kXFh]#VrsrM$棘s?%u3G8^sf.Hg5wS{Z[Y.svX-5s~΍3=9c k+J[ל /xdZ} $sw%c?WqZoׅy/ >xmɗxbe-c_5cf~--jj~?3&z a=zO``րրA-+j'33Va čҰLn%G ԍ)dR*WHd-иCGľJn,[)*є>^7#e ymz%a8loRVx_q#ac6*o=C3[y,{Ұjsy6j[{\*ST_ QA>QSR.cmn+;{dYx?{Υ媸{5>b;㱇Ga=zO``րրA-+j'334{BclDL1R*60nm֋c]a/?[Ҿ D[G4q45##M9:19VY{kj| +m͑%uS[SBSkm ۦקpgΙz[clg]ߒꗽddmy sA{rY-k󹼦krdIM뻨]飌K{BQb]gFҰ^}- o6 1{/OS{O 륮 g]ԻgO 0Pk@XZ3% qU60 0@ozz ha.Nw5vHX/n}$^M8*7}3z<8SHF%ᓭ1:׆76-M `HkS^;j%n PT5mahM7kR5,mQG{֜yFX/|}^9jA}ufX/w9Ur}}Q3sf-}oV|_zZ*&J-ǾoEyr9.SK5۶>acUa=zO``րրA-+j'33ǝ)7J#J [9nmK`|>f-#kzڍԿdG雑L`!u߬ Ըke%@ܚ?=u92W emRtcd:Ιg""T U\9VVK')rq\Q4w7@SzJU9$RZiqG7y{eX/λS?-zю#y{j^M%lZ"W`rc$n^ a=>{㪇+`j  10ډ.#AJ=n>JoB.7nJS`h{!oͧF-W~$s_tiL, R(}mwޛPX*G%hc圱t]?k1[Z\:j5p:ޔlZԪ\ 3 *5q\{岯#R-w^%Vwnڀi.]=ǭ# #:ڥ8i{-MkдiYzsO[RIͧ[5}8ΙsyTPuל9_l?7կoWTs0U1cR_Se?=Vu9Zt 845O׎Vѭ!jף[5ma!7VGGכ;CGytNp}Ip&~ 7 wk 2.``;c`@w\E YlF8GX7Ԋpfj ךF2{=v}+ث=OonR?ciٖxjiڗW=\'W 0 0Pk@PkLaZqYn/QC^izJVTOcR^Muf㹧Ԫz޾1Gڷ`9gs+z,4ߏW=\'W 0 0Pk@XOX Nw`ZP]ܠrx̓՟x<b_ xt:kw4k;6w1"O r~gCx>Ùcp4z0+UW 0 0ee=AD`{f`@R+?8= 0?U􄡍W߲ ^H]ڧJ r•}[7R_2wU/4 7f^=^ڠ3 0a@Pk n< 0@jiŦڛscnR۲3J"P\]2w᫘srU#68z+l_3¸GԛT{ʀ{= qÕ}r 0)#g"l  1ޞ^#G9kS 3Z]O}W-姟~Ū#rMzuwZlU:{Ԍq ۷W[a=1 0d@XozI8@ܜU!r "&gu<^@ju= 83 0p加7Q lc 0p'z<(UW 0 c`@]<`E$V~ ҟ}@X,z_N 0t-Z۾Xecaa=zO``րրA-+j'3 0 0 0 0 0pz,(UW 0 c`@]<`````b@X>b`ZZg"l  0 0 0 0 0] âW=\'W 0 0Pk@XOX Nw`````a={X㪇+`j j Բv"=3 0 0 0 0 0w1 bz\pe\1 0@a=a=4P;Ń`````.XaQX+`5 5`Pz&ډ0 0 0 0 0 ŀ=, qÕ}r 0@D`{`````cEa=zO``րրA-+j'3 0 0 0 0 0pz,(UW 0 c`@]<`````b@X>b`ZZg"l  0 0 0 0 0] âW=\'W 0 0Pk@XOX Nw`````a={X㪇+`j j Բv"=3 0 0 0 0 0w1 bz\pe\1 0@a=a=4P;Ń`````.XaQX+`5 5`Pz&ډ0 0 0 0 0 ŀ=, qÕ}r 0@D`{`````cEa=zO``րրA-+j'3 0 0 0 0 0pz,(UW 0 c`@]<`````b@X>b`ZZg"l  0 0 0 0 0] âW=\'W 0 0Pk@XOX Nw`````a={X㪇+`j j Բv"=3 0 0 0 0 0w1 bz\pe\1 0@a=a=4P;Ń`````.XaQX+`5 5`Pz&ډ0 0 0 0 0 ŀ=, qÕ}r 0@D`{`````cEa=zO``րրA-+j'3 0 0 0 0 0pz,(UW 0 c`@]<`````b@X>b`ZZկ? 0 0 0 0 0 0_W>O>S6ԑGud`c# 0 0 0 0 0 0Tac]usWXMq 0 f@Xo?rzVճ  0 0 0 0 0 <[Po{XWhsJ 0 L 0 0 0 0 0 0Cs\H`tߡ`3 0 0 0 0 0 0pakϿ3 0_-)``````ڀ 0 0pCC@g``````Zz֟g`'b 0 0 0 0 0 0 a=``|6``````2 U=`c`````` I.( 0 oP`m0``````ր޵_``/  0 0 0 0 0 0 m@XO@@@`tߡ`3 0 0 0 0 0 0pakϿ3 0VT 1``````^ 0 0px?>MLx 0 0 0 0 0 0Wc*{ 0  1 0 0 0 0 0 0$````7w(6 0 0 0 0 0 0 \k@X 0 0|K``````6 '  0 oP`m0``````ր޵_``+ * 0 0 0 0 0 0/`@XO@@@`|w &<`````` w=e`````````hzn J0 0w00;X t``````5 wmW``Kz% 0 0 0 0 0 0C````7w(6 0 0 0 0 0 0 \k@X 0 0}C``````0 '  0 ?>^;@ 0 0 0 0 0 0Uػʞ 0szz 0 0 0 0 0 0 00a=7%``;M : 0 0 0 0 0 0ֻ? 0 0a=ߒb``````  0 0w00;X t``````5 wmW``z! 0 0 0 0 0 0 ```/i ``````*z]eqc`a=a=``````ڀ 0 0 ``````k ][՟`ҀoI1 0 0 0 0 0 0``;M : 0 0 0 0 0 0ֻ? 0 0`e=AEc``````  0 0w0д 0 0 0 0 0 0pa=1 0܀ 0 0 0 0 0 0 m@XMrA ``~}k 0 0 0 0 0 0? 0 |i@XϷ````````hz 0 0 ``````k ][՟`o1 0 0 0 0 0 0``; qh`c`````ʀ{Ws\```n@XOX``````6 & 0 0pCC@g``````Zz֟g`4 [R 0 0 0 0 0 0 00a=``~}k 0 0 0 0 0 0? 0 XYOP7``````xz 0 0 4m01 0 0 0 0 0 \e@X9.{ 0 007 ' 0 0 0 0 0 0Cs\P`tߡ`3 0 0 0 0 0 0pakϿ3 0_-)``````ڀ 0 0pCC@g``````Zz֟g`'b 0 0 0 0 0 0 a=``|6``````2 U=`c`````` I.( 0 oP`m0``````ր޵_``/  -oo? 0 0 0 0 0 08_XnhA;zjɥZ2 0  /0? 0 0 0 0 0 0XuՍ]a73g 0 00a~_WM9Z `````6||ހ*fU=`)M&q````` a 7G7 0 <@7:ֻ} 0 0 0 0 0>zz݄2F``;Iz&;L.! 0 0 0 0 0zqa==# 0 0pa=a=4p.R 0 0 0 0 0= qÕW=\'W 0 0Pk@PkLa````a={X㪇+`j  10ډ. 0 0 0 0 0w1 bz\pe\1 0@AZV3Ng`````.XaQX+`5 'j'ۻx0 0 0 0 0 ŀ=, qÕ}r 0 jYYDP;؞`````cEa=zO``րl 0 0 0 0 0pz,(UW 0 0ee=AD`{f`````b@X>b`Zzz hv" 0 0 0 0 0] âW=\'W 0 0Pk@PkLa````a={X㪇+`j  10ډ. 0 0 0 0 0w1 bz\pe\1 0@AZV3Ng`````.XaQX+`5 'j'ۻx0 0 0 0 0 ŀ=, qÕ}r 0 jYYDP;؞`````cEa=zO``րl 0 0 0 0 0pz,(UW 0 0ee=AD`{f`````b@X>b`Zzz hv" 0 0 0 0 0] âW=\'W 0 0Pk@PkLa````a={X㪇+`j  10ډ. 0 0 0 0 0w1 bz\pe\1 0@AZV3Ng`````.XaQX+`5 'j'ۻx0 0 0 0 0 ŀ=, qÕ}r 0 jYYDP;؞`````cEa=zO``րl 0 0 0 0 0pz,(UW 0 0ee=AD`{f`````b@X>b`Zzz hv" 0 0 0 0 0] âW=\'W 0 0Pk@PkLa````a={X㪇+`j  10ډ. 0 0 0 0 0w1 bz\pe\1 0@AZV3Ng`U /y~sw9 0 0S |_gO>{?~Enֻ#jd΍o`c@Xo:3M` D0>o~ӍӍoc1j 0w0?޳|;}h}|8 w 0 ,  !ng`5yP/0}JlQ}I_ `8׾z_*6{gs8ֻO_4.ɳs`c`@&NN1 ,WՋV~m.^x57ϵ>=Z`ڷ?G}s?~}-[3@|fE7j{Gz\p)UW 0 0ee=AD`癉@ܼ󣿟L1p@jU>Kl()pNaw##=x|<0QH]}qnzuosߗmQ,;}Ս qe0 0w0 '0hC\Y~`k'n"+@mԍ+BӜ,p^*0$4tx΍!+aS+[aW;ةµ#>ӏ+-?7vZ ׊Z ׊+b`0ee~dw0 ͷPBPH&#yu.}?=Xtk.4tb\1p%$ [@OuX7|ץ>(iਫ਼ή^]*^YxR'``AZV;hLJ{auݻwC'F=ڞ -C==>˭&l˭V4ke}wtJTNVa=D-ڜzokcvՒW 0 0pzz h6ȥzq:BA?$Yɭ=yE# lj{Gn=w>Z $=V֋t^q\ݦT苚Eյ|#;U"w4*631e ׊Z ׊+b`0ee~dw0 ն-5J_z#nnfccI=z|c.i՛͘c ,%}}\SzLa=.K.o%Exe^a=Fb`ZZg"l,3zXUdOs",O𕺩#hԊ{H\+XYm`V׎|2>``;c`@w\EGXo\`/>ԯz >ɁԜ7["7[y-KkI߿n߷CZKX֦^uԾc|xx3{X㪇+`j j Բv" V'O =˵yH0"Xyy~;οbeL l5E߿ny>Զ{xsVuWYē:1 0}  10g߉ Ź=x'|7"1ǿ3zqc87 ?=jzvjE/NJeWuYv# ܩ|n~rsM|ż6M)GڐtNkJX?z^sgnorLGVJڶ9ZӾkrזJ\Ƽst]ՇblOX %}?]SH͵ytmYpkȓzg-\\[\}'Ok'5z}Fjta=zX㪇+`j j Բv"녇p\2(LA.nOn)=97uTH(sMݥ5~:ҳ_8εuRbZ/=f8HSZ'5? ~kbu|^)q7{xݲBfiojڑr[guxP:nKw)0}@8kK{eY: qSn/hs_[}d'%+QM^"z}{蜯wJjf׍21mk8ث/^Q?=3W<۸kSGz\ 0 0  10;L."wFXomu-$*o%/v~XnsMɹE}hǑZ9սו۝q=ΧלT = Zs[I?"0SHߒ|+Jy${B[mYG]kRG|\Tz~RrNcv5)8KD> ]--W~<ק\\8=ӦhGB*5u %op}PtkéՆf+jc%w/'_j> WzޯZ8z}tz\n1 03 j Բ3IR?5pFX/ږ$xPz&\t[+R$Rzܚ@^+wl{ \=Ko}=7sXSq"lc5a}~_ܣpKr6.JP#A嵀kK1չ6 v_qi8Hhޏs4]ͅ`jؾG-ka\s17TբoQֻV.,u&#mv,W;ǾjC1Jkۮ[{6w7꺱^H+WϐR+ޫ1pasjz5Ηy`i@XOX p9ꗳzՋnTl=z-~ٺṷח\IvC8?=k~n[ "2s,+:N5][ao>g/ڳ?[3%Z<=+M׍2[c0zwԉח}.{wWnaRX+`5 5`Pz&ډ2sVX/* M+uՆ&Go/Ŵ*Jj!7ضb|^7:L}h[*os7s)[ci5g̓>ߺmL߭{NlL}4smͅl -]Ah(foU汭ksѱ;5|o?'5Dro+U2mM0 bI>~5?L@vrI0:5F\ Kw6wằ^tϴ*pVJecr]WoR 1àW=\'W 0 0Pk@XOX N3nQ l{,<=GڞUrS9'Y[9nm󖺉2W걈=>1sr庑{/S[;fE3OGGo5^38zTz\jy 0  10&:>=V &5o}3عm[Zt{۞!\{cg/w͢=ȳum;KkAsk35S]rU*\Ky9s|瞹] [Ԙ>s8$5J1ZɬUz0Zmj^=Rs홷eqO87FGq'w]Y.׍~^_Zjom|p㵇ia=zO``րրA-+j'?L[}/FV޶cշ8߳&~n`@.:yq(=fT?˞uMh(y&##mɭJXjd#p2vGoTZ>w[G1b`Zzz hv".)WkqcMVc V*޶5UߵשvD+~jY٪saJb~j(ܖ7ύ=a’nwopcyq.DXs\Tk|G9%5i>7-~cS}rkL_6ڻcXN׍RKU f9Ҿu>okaDX;`xa=a=4`r~և{.r7;"7'g1#-oh.7Ru^zg)g/*LInu:VJ8Y!Y=zJo:?\ nuiy{wPAm\=8kuٻ"Н~OmVv._۾ڴ蟞8:\ eu-iwE}Vjs}4pq?a? ^.s~SW*իo 0 0pZwe *s%k)I7KZu=hϰ U| eQ5c臵>x7ZT]"5[+~㔚O98r~qTh8#T7}^usu^n\$+q[ZHP7u}( MMm_9ez-_*hw M 5_)َ{JXG6|Xyq5|{޽c!( 0 <ۀ01?{b3zrxmdA74O&z.X7fgo oY~eSP>)=GծZ&]#K6uXm#׽z%sT7}B%7ܷfȫzO.8r[Dy\T5pGZ,;Z=uG紖}%W˾kXo^bnZݰoQ;t8:}!7g!׹*.ߧ>nzkz\ 0 0 j Բ6sxFX/wCikew%ohMy,p7KmZ;ﳏjI?&ڜ]ҶuT{z8 Zj*TPjeڮy^i" _MhsQ\^s!qNm9z.ZQ˾BXo]][!dM߶4pFq&s=5zϾ7cas*zΓu`m@XOX x=3zPVx$J E}ZKWG˴m>s^Sr3Ԥy^zeꚵsQkxkK甚qhmZ} $Ǭոe?1?ӵ Ƶ4pq?{aVc4%~AU]g|zl/UW 0 ֻ9m/g"ka {>5aVKdM7Kmݜ+ ۴k%>8S[b|.W+ -'(=s@V0`c[/5waئy^Z}TV@JkK9遭}wG} zVb阮kֻ{ؖ}*4Z&9ߪ>nzm?QX+c`a=a=4pE]zւz%+m9"f}QJL|i;Դ6 3մLmjuS<{tXڶB"ķu7a}9򈻖mKS'U=a^JvG]-/+؊yhmJq6G}Hռd~n]ǖ}^0V>踟׽e앎%Wsާz鋑ƅW#yv.<3 0\Zi2w%z܌йKB)-[XmZQB=wh{ֻwv"/.]m.O?[r4 HFJcjGY͞csZ"\jp{'RQh>7egXSzq??;07OZǞ1zܵ|v k@Xzoa=^źd`{c`@&{OGep$>8ȦGHք[ZRm S}չgXmݠ}\Q":8*d\q:R7sast^"В }YlrsV \#7+3WzǨ{WES{7-o~[r$(5/l9~״Z}ne8Ιab꿫zq]iob /_[^z-/!w?{jbOǽSX/mq->|*Kcߵ!9q5cΦۿz'a=We``Z75˔7i͙PE R4rA=ayUIͭ2չgX.mY>/90>Z@p+l㜣>[7q[s/rZgյ/e6 XŸݳy [mny{ƜAI*_;zQlӵ`߽sZ,ws'wٚ˶nė\sm(k=K^575-X;rީe >[a\jNn~).Ψyw f-6uuŸcX/>=^k-Ź1t>宛}z^3;_Wt,+w3 0p?zz hd{ɶe}h[˷zG{K.ᅚQ\)ǛnËV`%0[WޥlM7cuvg/̭Mfw m_ ,v5Yi5_H.Ѫ5-opku.<b,O圷J95GWUsԆ㗆ZB1GNu5]k ӵ,[AlֱM9~[i=>e+56kS˾KXo;qmX_.<v.hawmz-u4V;v;׵Ktuv{^;k+w>z8n[`3 5`Pz T>-\{W)I7cq%nυa^z#s^`2vN7ߧcnwi#?%goFڭs k厵u_ߴ99^ץ[>r7{3v5֌ݵOF뭅tidϮJ^ZsH%JIL jҾoQVlzZXs]5=MԺe)W2vB!ܵt^:UX/`Y0%qOۻ\QzkpZS? ֻO_4.ɳs`c`@&N%}H՚`N\;Bt[`!|I0bY>XUhM@:&]^+*uS?}ݭmZ̃%D_j{Ni(mrm^3"ز֘J{[HJ>승$W:~Zjp#AkU-CW՜֫[͇s[s-ޑ`d͜9JX7]7J\%g!kcȵz޲Sw_W]wok_SN>8```@PkL.w\Ö7?jnt|ahkڭP֍ 0=n練ޝ^ZXm>^3,7tY|YhgpqJvkTlB%sV~s*زծ#7JHkΞ֮5;v cV;[֦\ܮV?5axO3ї[unW\SbFk쒪_ͼ?ko^omr?!#֦׮=t9t\BT;SxJ`~mږI6z\՚=3 0 0À1Y.BHq㌛%!F=qaanuö&6TOA3]GŶG̞}8f )@+]b)*ƞ48NpQY˛5sđ8vò/bۚOm[9d+:Y瘪92snݖ8˕]{sQ// GRkyIߥ)y?2[{8}YɪzSkjj2umjϷ紞}_{=ч17m-~i4T{P+{M;u* uVZ^z}3?dz-}Z_Xr%`ߋgD[\S+'-mi ۥq~{5,MZ>^%ޗV3-=7j9RcqOԋ?Ӗ}Sz.5Ǟ3̙ߚh˹eivk83̹pV_ꚽF͹t1Ѻ6spL g-E:9z]Iii}A˿o|uŜ;ͷ5ʭ*2GDZc޷GV <#uZ1pajJz%Εw`k@XOX t;} kcݹ} 0@jh}Z}{n 8TX+`5 5`Pz&ډ0 j (\p>w6 0RE(/j@nUWrocEHf8>;Ka=Q[xd`u  ᬰhg` gL 0k ޻w,.<^|y.{b;R_7Ʊ/`@Xu^ qӗ} 0Q;z& v0 ԍ? ghf Jfոa=5.zcR˽_+z,4ߏW=\'W 0 0Pk@XOX Nw`@jE7Oݽc^@*C|ͥ+}iN հa=Z8ZCX+`5 5`Pz&ډ0 n ҉Gq?{x@b^ǣ=?-Wsg{M81 @ZUM]{O |?z\pe\1 0@a=a=4P;Ń` +8wH6:\?Q^V0]kO?[sY|^os/ai{5a=&{㪇+`j Umm_z5L`@ꆟhƶ>f  1̿fϱ9iGz:;M#7hYcqaMX0xj4·i`i@XGw/x7G 071y#s2~` g@X  0pw2#Gy>w}ΗzTz\jy 0  !޳IS1 0pL?jn[}ko^H`LMߵe 1π^ھ[a=^ٿs`c`@&LB_0 0 0 0 0uWYē:1 0} j Բ^AcRR_`````WWv++w3 0pzz h${IV_ `````΀^]*^YxR'``AZV;hLJ 0 0 0 0 0@?zjnzeΝ`c@XOX d3 } 0 0 0 0 ֫_e+O 0 5 5`Pz}II}`````g@X_m_٭W߹ 0}  10}&Y}/`````:zu⫬^zeuI`` jYY1)/ 0 0 0 0 0 +;w`a=a=4`$/ 0 0 0 0 0Pg@X^|KXN< 0 0׀րA-+4&%e`````a~}ez\? 01 'Ld````` Ջz Չ'ub`0eeƤ 0 0 0 0 0 3 ׯVXWg`>ЀI> 0 0 0 0 0@azUV/a:N 0 0@_ZwИԗ`````~ q; 0 ǀ0g````3 WW/%WV'ԉ`k@Pk2 0 0 0 0 0π^ھ[a=^ٿs`c`@&LB_0 0 0 0 0uWYē:1 0} j Բ^AcRR_`````WWv++w3 0pzz h${IV_ `````΀^]*^YxR'``AZV;hLJ 0 0 0 0 0@?zjnzeΝ`c@XOX d3 } 0 0 0 0 ֫_e+O 0 5 5`Pz}II}`````g@X_m_٭W߹ 0}  10}&Y}/`````:zu⫬^zeuI`` jYY1)/ 0 0 0 0 0 +;w`a=a=4`$/ 0 0 0 0 0Pg@X^|KXN< 0 0׀րA-+4&%e`````a~}ez\? 01 7pX_C?j 0 0 0 0 01|>xOqO>7մ}!3 0 b@Xo?~_~G5``````~'7}n =e`#o=rV! 0 0 0 0 0 g?֕7$ҟc 0a=0 0 0 0 0 0 0 ````7w(6 0 0 0 0 0 0 \k@X 0 0|K``````6 '  0 oP`m0``````ր޵_``+ * 0 0 0 0 0 0/`@XO@@@`|w &<`````` w=e`````````hzn J0 0w00;X t``````5 wmW``Kz% 0 0 0 0 0 0C````7w(6 0 0 0 0 0 0 \k@X 0 0}C``````0 '  0 ?>^;@ 0 0 0 0 0 0Uػʞ 0szz 0 0 0 0 0 0 00a=7%``;M : 0 0 0 0 0 0ֻ? 0 0a=ߒb``````  0 0w00;X t``````5 wmW``z! 0 0 0 0 0 0 ```/i ``````*z]eqc`a=a=``````ڀ 0 0 ``````k ][՟`ҀoI1 0 0 0 0 0 0``;M : 0 0 0 0 0 0ֻ? 0 0`e=AEc``````  0 0w0д 0 0 0 0 0 0pa=1 0܀ 0 0 0 0 0 0 m@XMrA ``~}k 0 0 0 0 0 0? 0 |i@XϷ````````hz 0 0 ``````k ][՟`o1 0 0 0 0 0 0``; qh`c`````ʀ{Ws\```n@XOX``````6 & 0 0pCC@g``````Zz֟g`4 [R 0 0 0 0 0 0 00a=``~}k 0 0 0 0 0 0? 0 XYOP7``````xz 0 0 4m01 0 0 0 0 0 \e@X9.{ 0 007 ' 0 0 0 0 0 0Cs\P`tߡ`3 0 0 0 0 0 0pakϿ3 0_-)``````ڀ 0 0pCC@g``````Zz֟g`'b 0 0 0 0 0 0 a=``|6``````2 U=`c`````` I.( 0 oP`m0``````ր޵_``/  0 0 0 0 0 0 m@XO@@@`tߡ`3 0 0 0 0 0 0pakϿ3 0VT 1``````^ 0 0px?>MLx 0 0 0 0 0 0Wc*{ 0  1 0 0 0 0 0 0$````7w(6 0 0 0 0 0 0 \k@X 0 0|K``````6 '  0 oP`m0``````ր޵_``+ * 0 0 0 0 0 0/`@XO@@@`|w &<`````` w=e`````````hzn J0 0w00;X t``````5 wmW``Kz~Ko? 0 0 0 0 0 08?q M5hg@X]-TK```a/~Ԁ`````kwހ+U}`F4 79m}_}7h5D`````^W/s|'zǺꆫWs\```n@Xo?r#v5`````F6zo4)gytn|3 0;_X>0 0 0 0 0 0 ou }߰{ 0 0@w;末9 0 0 0 0 0m 뵩#\?oak{nлn_޾ۿ61 Ѐt_HzF0 0 0 0 0 0n@X1F>ͰޟgA-mq_Ŀx~ֻo7`````a=Ο>Ͱ{A-_@R?Gaw|*_ۑ6y5Ws2ƀo6 0 0 0 0 0 Հes1bnpt ,EP磏>z>S}y5O>ywOZ/-aRXGz8]o5mk( 0pzIֻ39#`````^c?\]]/B^mS\jc??k/w{ kⵥsÑ^smƆmՕ2 7a=U 0 0 0 0 0pԀCG ^}o5a^Ƕ2׹S>oK=u1GJO?GwԖ^z-`Vkm~J8NN01kyj  10ډ. 0 0 0 0 0w1 l&2 GԧgXO>ywȭb=0tgQ8MS;޽{W܆En:cD[?G=TfhX/5-B4&aD[knk>6i`#5ĿymQ5:x Z$`````F4 Y?(H`{OE*K B6oծ޿E0@:{"TuURwN@\m?63IKE[JDd%yml=i@X7g0ags,`````zc~?}_/ }H륂JG8b* lmu\zѦ^6ܵuKCW5uɭ 1ȶw EЮZ߅ SaymSxIr#s?^c@X7g(agr<`````zc_巾?O!3j˭747h zm[ *ukAGn}t㱯W;J귚 sahS\{"֧[Rڱy5^gc`@&gOO1 0 0 0 0+l?O{'?U:_k֋Ֆ)/rdž>A.p%+M!˭ dru\5pT5qgOݷTmֱJ~Ze%;쮽Ra#V&~"Zoo[aet5:rYY@ҟc 0 0 0 0 0pĀ?Gyxm+=xczMy^.gu0\" BTTmr] ʭN[$L+9VJzoInsuX/gs/}ph8`\K1뼏a-zz h`k 0 0 0 0 0] y?ܖ52o۷]y4Ԋz =J7D֚گJ<ة0aM0* cyco U8,UY- Ms]$WqW_GX>8a@PkL=& ````` z,u>?MXm{+o~?o߫iͅk˜9#gkKSr+}Lfn%mJC]K~Jd|\ꚥT w`XMȰ8;KTHy(CkagIo+4Xklaɀ0ɛ`````j@X+cm#pW_<'{+zڿE@&3Qnu#a>KBUkYql<j}aW[kUa#JS-1֫sߣQk޿0@AZV3Nc`````nxVo[`ol^>0ۑǁNV[PV3* .1u^ zsjI.W\Pnk\v56Sl˙ch1 0 0 0 0 4 SKOGxPk? eϵL)yZPHj@S6%']zܣ᰽MTPn+twEϽw?\?ݸRKрրA-+뙬F 0 0 0 0 0k{~~x޾7 Y)u\&C%?%- 1#eSaXlm+x*>bŻ: m9Nez=–]g &mtd````HNszy@VHhocAAJyg^G[M-Wj;rrq iq0@0ee= 0 0 0 0 0S {߾o4]U/|{ n'| M}Z.VBږ;rA,ϯj[(%p؞F[2JVȵa=zS&mpc````c@X=nZ&Vg ~\$j];^MZwXe(E0՞[.W#be5I=rk嬖<v-z߭ǔ15AZV3a:a9/````` w߿~<6ţXS+ſE,uhyT8.ڳ66fN"pc"ڨm. Y2ߞ9amE=k}'yfXoqԭǍh1.탣;c`@wlE`````Ā'%Nzl׿wݷGoFG{Gg^)VKbuT[Ǹ[X/T"D?]O?S!R[؍`-gw oKV,l=n3'0ee=&m``!y|h[ @XԎ`΀uezޠ^+}By E,=c?B^RKB]\X~)ȵ$]k̼aoGXτvG% 0 LAP1>[7XH!YM47F_Aplc`Ӏޘzѷ*^[zezVhlm 爵cs빀awf;c=σR}!czՀ넣]. 0 7=b"0>d@ js\^7f``j 1Sk]#s]z[[*^x=C'r3>\آ65{rle?]ŶAZV3g@|hAp3FF#t^ʷJ5r:``Ooz_~ߜHzZM.a\;u%7\u4k[|1xc|0=#7q=wO[s˒M/9a}'_s1퓫  10'+" 0Co⃟+c ʒ 0w5 Y6?E/m|o}ejrqzr ]{\GՖ/y@.A~Sꕜ]}96=a8^Vk']k̼AZV3gka7[KB{V2fF3pN[|} 0 0 ݿFG7Cz~zg:לG^3X5.׾Tp$Tu`UIh,ծԪlO0|t-O{~RsksnmՀur^. 0f`+6&⃡*~*ߕxtVbƇn|(j* 0 0<`oo\'O֋sq)sK}!H,ϒG 1Ϩ̧BnSߵznX_BpGhoX/X,qr}JX/>Ooy}  :emz&#~`a 8@&aGޣ; 0 0 a=}x_*qa\koU޽{Wb-4Xbh,w#T[J6ObɱS֋sRzx:%64 w˞9߷I,k 0} ć}Ƈ.Gv#}_]Օ``^݀1{ W Ձm}-zZ[qduܓ/j?VO}W^a{WZ~"3a˱{3VRLgGhzTP.;Y{ZD}JXG>w;?3 'Luz 0 >D0w^ͻ 0 0 0z om~ӟ ]p-v V'Ck!0MX(}|UeX/Yh,ws_(^KH@&=`a9ڿkjS#ah5 F-Rrw j{uwIwQ€o2{>+ܽ?c 0 3d޼~vVm````z,?q3?/Ql;H˽&z)Sh/A'BazV[8X+[W,Ѱ^\k[/y/Pzap)6S=&Dʭxkc`@&ךf20Ʋ?' 0 0 a={ڏF[1z{JW[Q/ m}RV{~h#H٣}t45 k_zQzyrdZ~J j Բ_2m  03/>LcŇOcߕ<\{M|xzɼ=QcMMoV.ϭʄ4}жTmoY!GL9}G?|Xۯ8eM.޳kzO͹\9,8>7=Ͷ} 0s =0>ՠ^<"{gO86XS>~+h[eT_ *6=vbTr{ >hE5+ n]z[cy<^}  10ɝ`g}rU n$5{>,/j.LJ.[B~Ju 1>]/ eƾ[j2_I[־ǯ5\oTG M;mɱq>Rvh[8|Zk~o 0 À=a?٪z/VhzwKRcT|tj XnLV_Br},93gq>5=GzSm5f֋zԄm}}Y㼮5A!V{]k0 0pFIpZwx~Б &ϭW|;~xm_~ oT!yiߖ*s/s}yGKSwyW-{{Vm}B_0 0:^׿/6zӟ;tHVt[IGVZ &BhyE'FϚZ'7]R|oo;<9dTEs^CG(cDVv-ehKVI!ԞVA4 'L9w <@|H$UpM\X/>d۶rU#u-75Aip$WZc][fCkO18N˰)(zd=x60 0m>wwn@%OX +2 h@PkLV#NVΉk` B)%޵ DmdĵVm&֋r23rV[;kk`ap &}`x\hcG-y"L1Z`.L6/eP'զxtyYZ(RTX/#/b%j/:n*cNh79 QT䱶kc[rs^{zg'ηŇw9K/yz\-;~k>g 0 c@Xo۸ [k?5_|۹iqSk BIܢ\l? 0  10'MB 0ra+{diX)n-PVY My`*qs tskUhSe+V:=%mY m=84[E~ o?8-zw%b-Y=K~}ƕQ`׎|>ՠ^o^a.%<^Õ~ 0Ap[YoA``Nr+rzZJV: ڹw%9-?<,Y/AV4 W2Z _ LՄۖK-jW(8JzMI͖ۤj8GzwWC{,y( 0 0zAq?i38}2}GJ>˝O3X'm2~`Zc`@' sa`cGRG,*]myPI-֫ }W\JQRMPo+WҞuQ*վԹKY9ֻ^W;w.1aNU_`6 7v^5~cռ{Wqѧ!/|wL` Zg2b2qL`oa+TE@+2ZI '֫onuhW*hY r- ƭ="H)զ˒fjNI suS2ua햏͍]b6jv 0 0 @y׿fXq;}->?ߕ3)Ϻ\7m3`c`@G&uQa`|w SZ[]okegRvBJ[jOI}s Zs蕳s$wDZ[ٜ8xˤߟTs5g``|zx+Yje y vײ7 At[Y$Js``w ߒ=Zォz בёaop\Ga`i@X" lxm^mݻ495`` e@Pk7 V3M 0 0,w }j^[<4:\ַG[Bnջx/>O|qr3Z=.u+nF2 0^{wa=zaeď?GޖbPG`aX3 'L&~`e ˞aoGd  صWiG@.pG}5VR^+3sǰޱz=wdyf``lzcUWX=4"egzO>wm7g0 2 5`Pz&^ 0@?ܝzXυR{VS˵eHks ChK.8'XѮg1Oiֻzlr|Mڪ- 0m@X㪗-e`j  10I. 0 @W|;e0n+2ZٮeZy67nz̰޻wzQضdAe<~n9ZZo{\~``5 fz\6f1 0@AZV3Km8a`^rᡭzc*Sђ!d2s$ht䵹Z?ae-rkzc[u.(;_mƩ~qz%smu-`q ӗwz\ɣ 0c`@&םgk VAґ2 1-Wk94:\VZG["hk6u­ѱ8-zWZe}`ր޵տWv^l3 0,ZAh_ 0 0bT)Dg˅>+'{:0f ="7i~gom8[++Z8f`KX+`5 'j'ۻx0 0p~n**4-C%+3Zz([*t=ް^%wl'W6WmԒ`4 _z\pe\1 0@AZV3Ng`{H]": ߹w[]KXLMR _%Vfo{7wDZڞ^\Q 0gc9a=zO``րl 0= |'GF`b%#ya~KMXohOI8u`jSz{mמyMc=a֨Ք`0 ]wz\pe\1 0@AZV3Ng`HM[Z_l0~wYUz%Tsu`jjG} hjLrAǬ=ްvk{z'o`x-zgoa=β81 0a=a=4`73 0\޽ˮAX]k  ]]/[<+bTJ:053bi6x{7ǽnmOX׿^c~``owoz\ݨ1 0k0ee&i 0r+E+W{s Q}jlG >$ja^.UJ_|~Z\<aA=m}|.`wVW\+ E('R[M@ԑY;櫅N{m{k:0[-<ԆnYljk{W7ouX``z_z#weIX/"m>O}%(u#P|޳?!c`5|GNA.V拕Z؊JWN{.809ec_%Ա"7EM$DZzLQA)pZ{o꫒ u-#8jlO|:Y;:q``กx_%aWs6``s  10z7 0U"+iŊ^˟3=EsǎDcq=-wݕ* /K˺> ї]b*0RF=;]m\V`l@ `@$B! 9#:2JskЛbs}.bDfky9-GuZ圌ӧ:xøgNe,?kSìmmѶ=kf~0 ``6 fsp&0 `@ P80 ` #3 0 `X%>މp5'0 `gq&,:V0 2z_e0 `u c>XW{ 0qxd6΄Zb=\ª~b0UfX̿0 `Xz[4s>s0 3^ L9| `03@5bG0 `a@e{*wƍ04XPi,8Dߚ^k1 `@ 륱N:a0 `` -~Wsb=\ƈs `@ ˰^ sF0 `z0 `08zlcAyp=q ` a Cu^/x`03@9ǹ90 `@ 륱NRcV1 `ȓPZ V{Ge<0 ` /ZOi=1 `00;b=秺޿z~5}Q?,? zpb.1 `Ŗ0 `@ r_-[(0X[N"{?-;~|B83<>q/)\Ĥ~0 `` *pn80 `b=\WNtO&&KZ'IIc{ʉ(srz.?&;0 `e;,`_߻w-b70 `05j8#olw|l?þBBBӪǧzqGZCi]!sҵ$0 `0 `0 e`1s`}~cM3XM71>=;`sV5 ^ `0 `0 `MbT7cGA~ŷ^){@SMXXŊ`0 `0 `2P'֓4?F/|MW_7nwg9柣b=uc~\ba 0 `0 `XOj!@T47u>Jh o\(~ovu8qH91*UXυɢ.L|0 `0 `0 `` \=$%7b=є~Y?eG)KZe.ĬY_x?La0 `0 `(z=̄Z<^~k(&UGCbd#S0 `0 `0(>REs 7iXN:ۜ?GB2!ocbU_T^+.v7jGyGVlllD.us `0 `0 `j=E`k}}]~T~X~>".?ǟ1l\8" b(ݟ;X/® /D̙90 `0 `0E3Ou??=5Gğ7oIoC'hPY.U}0 `0 `0 `00,֓#Dv2``ˎ>?=!ZG}"j2QMT#X0 `0 `0 $XO*ղp[[[B5B5B1Be_|~~Hp̑Ah 0 `0 `@){hCĬOʟbbb^ef_Ǐ߳K/_,j"CV;K.] 1 `0 `0 ``z!VfD(X)# Y@w}oc~_Y.e"s1Υ0 `0 ``e};mzXXX'Ihd17ze|W֫r4*^Ѹ3]m'sҥK}#0 `0 `0 @)ۻwov]Hpu5?]6b=c_ dBB^/#_wqGVlllDo׺@0 `0 `0 `` TB^4c|]={u|/G?o~GlZ 2ӯz{~ YB;n'֫ 0 `0 `00>1v*w}s_XoWv!˗5 Il>-b=B<z]3 `0 `0 `@7I瑿ZMe0NTes~"7ſ=gy ]뭆Pi!ě*멬}ĵn\4Y `0 `0 `ADiy϶;lßJy?!!M|~|kw~̑t^Η{>*W=,di's0 `0 `0*Xό B~MWm~a')}yٿ~R#OXL0] `0 `0 `(z>@}7l`Gg}>ʟWƿoB%Osק+]~RF8p `S4IDAT0 `0 `Hz1Lk+&U~,'>g+Utr\ T/`0 `0 `0P;"!4F/|MW]~,'>gU 5TKRãSn[~0 `0 `0 `0pbm!GjOĮٳBЪu|/g+_opRXGzpގ7~C/s `0 `0 `@ ĴBAPPPW!pX_>Zy sկa/\:[M=ׅ,K+~~_20 `0 `0 $?tGeU(4 D"ߦު>l~ Y_oRקXoz2, CF?bg%bN 0 `0 `0 `` ǔ%Xlmm5>7VQlQK5n~xEJ'9 N94VCW_10'GI50 `0 `00JC=T䔿!Ǣ?|,o`vv zG ׊ ?6 MXo6.U#0 `0 `0 `` b=UjQ!HS g޶PgW}?fWd@|?~_Y勵 BsTd-e90 `0 `0 ̆z9ĨEDXo6.U#0 `0 `0 `` b{11&ͯ _E(F(!\p]z~?}.v= Ԏ/;X[[+666ba ܜc0 `0 `1P% y7۳"UogͿ>}yw5B!bJiw b0 `0 `0 ,X/ͼ޿^v׊vʶ! 5.d>k9 9kX/๋j7oUsK? `0 `0 `a^P+ߩ}?j#~,>O;{db.*Ꝭm=X<.G'0 `0 `0 `` bvC"J5?5?m_8}~_Yz @ZFXe".K|0 `0 `0 `` V)Sj9Bb=볜a[?BY^)tW֫:=__q/s0 `0 `0 ,RwޑVHp)\\}B!ᯮb>5OK]6BkO&b/q|kkkFtq.l0 `0 `0 `*޶>T%칯lU}}5<} Ȅ~>}nTw9xq=:QɷU \aO|X/"BBH0 `0 `0 ,X/uRrs~b]n:g|ꏖkv@LP/ BU[쥁K `0 `0 `b!S6z,Mex!b_-0ݧ_ݎ?y׮VB[SQ w*멬}ĵˢ]Ks `0 `0 ``> $[[[C/1@Ŀd. 8sd!] +XοzBKBډs9żb0 `0 `@){ Z~)+ֳ~iGsv>So_;ׯ=Ok B/[/KXo.c10 `0 `0 ``> bBjD/6?7X/$ҭbׇ}\cSWh ȗ+_WֻplmE^͸= ډs9żb0 `0 ``e|o Yߔc_[Ymߊ?o{Kp1O?~^W֋/'B8}oz(pc1 `0 `0 `aݻw$G:w:7m#3_%O}>+~U6+>JDܞw ۋbcc#*ty0j^1 `0 `0 `3P%#t;5o}E`k}}!hL%f% ɱ`l6B:^.*;uhP/ ̋Q![ߑ$~?J C `0 `0 `71^O_uCOo[_뛳~ E녊zt[1~!y!^Gy_xa0 `0 `_hM~kމ6bL]w'A?_"[}7MT֋>z^'ץ0 `0 `0 ̊A!Ȭ [[[ ]!ӎY7!>b?Fs?oމ3GBg_}m^NBdV"K0 `0 `0hJރ}-MLR)+ BO4u~TH>gPGu!nGG).v'; ˹=l+Nd'>s0 `0 `00+J_RC;-3Dx3Xg_=,/_P/g}_<;otmP14ۉ\B` 0 `0 `zd)1rOu}|mz3ym_]e>mb=߉3/{ێhP/vb='8y0 `0 `0Y1P;9sUf~o#WӮP/4d~O]!*|#]>N}9[}ZLW(]P=K^hƗzX[[+666ax0 `0 `0 `` TRrs"UkGC-?oU|SÿƿZ*]\wpQ; ?vED,~.0 `0 `0 `g &[Ne?W+۵놬˞w~ZZ!Z |5B9w_薰>;zzPrhWYo.[)0 `0 `0 ``1 B4M]6b6?7 V|v|]vE=k+~Ks̑,777USY/kIH20 `0 ` yoW/$lmm5<wD;fc_P.{Zk'zg' CBCQRj'֛剹0 `a{^q-¿]0 ` b| M,QR)+-Ђ'o|2 a*ٿX|RqTꝋ&snqꝏ?vb=<.x0 ` ѬNo?/? ƿ^ _^s9qN'c2Ԙp `/u£IzDog~?ᱷUq_6`1 `@^ b{'SNy6b=볼ac77koNXOٵ&"mC8 K?OmllHN{.wܱ0PT Rb=%+_+KbOY[Q/ab9]L0 ty<]n Gjxg-<6VY82 `2B~'ղݟݰ˞w~Zߜ7kX:G}Ub zzg zz}shWYϥK `00.UbP=D6'ON,w^*7Ϧa^lf0 `@6,%{~kSY/f#u>/_|_YwWz.-^{QJѾuX/MT `00Ub Г(,́yrc`0 `]d`P:g__HcQ󳵵U[Yo}}}oQ|q!'>yiugl;h^>v`/vb=+]\'\b^ׇL>z!!0 `(z_{=^:x"[ i,n~m ?JAħƧUЯ=Ovgz ^bz.Luas `ȇb|֒]גXl0 ` Jޗ/Xf$ѫ ]qz9wZwf߹Vɿz+9wP'؋Suz\8V0 tb=,vyXbb 0 ̞z9o YYVXoYr0 w⹕^`_Y/v9Lg= b)|/vb_ l10 sgX3n|0 ` 'J޽{Gr8)osmzgy>rooߎ<>-^3(] s؈ s: ( `00 ۷o_}]!asM7AU~ys|[b6[nU9|flyl3mWfὛ~wu-68aa|Zyڬ]<Ü y oui^a̡_.9bLc6Bg_8*PŔۃ}Roc$֋Pq3]da0 O-G^;{=N<9SX @ ١5ALVfMe?c},ۧ1HkUp?œ 6ϛu5-*^5W]'> }~ f8͜ ?~QcX51>#&zП&4swV `0?1*wROuwӵbn>aG_[_[C|O]eR֧X/灆{Pb*Q2c00k-֋}XhݍaT4A|$&X7=G?~ԏ#~$vM']<14j}A$k$"0\I`aA `07b=Bo;Qߦ^̷_yBut&/KP5w^Kǟz''ˢ}ssSe=v00 ]*5M#؛V<Q'h kV'k#i[/Qk+N9=TP/jz +k ` tUW'Vk6b9k _MZU$&qU5mUE\XؘX/ªuuqOV9H׺ϙEe fk k%oM (*^]_x$= `0'XR䔿/K(zY||/߹g9_ G).]=Ꝏ^䇟~ҙAٞPLt)y9ɺb00O(֫zk @N\7H6nAM~}~lַF\W) ׬{З*AcC3ִO< ߴb ݅TCߚĬuByu}ibMz `VR>UfO/:5O}Uo+:{Vwٶз[-KXo.-\RYo `f@z" jU% £TU.#=PlP4X/VŮJ8dUgU#pޫͣSE55oZa]oӾg1afNX=bbM*5UBlG0 `j1p}qDmX_yOΧm9Ŀ+ƿz9ӂPop|zCn6n .vbպpIe1 `U WPUq08&S'ꕟSa|讍N7P/J|8xUq<{V=mE>M#~ӧ sx `0P%=c|{"Uk׮/_?7񝯾3}!Uz]wBv{g@P?z <0 `yڈ5I> ̪OmMW܏#x*8B*bc֦/}Z5&tfb}6Y%֛T,qM.c0V!#>DZ7=BІ17BQLvBzgzB*9J'UK]e/*\FYc `f@zVIYU4ʹ@N &خzkc{=YcXoݭvxbL[mkp `@Xo;Q~&Tצ^?ow'׮rh37MT֋>e. `Xo jF6kY{xh߯{MLcVyTka&CY3'}tXIW57ʁzϴc0Z f%ڪ>rf* -a?G)k'ٞ0ЃOΧ]`do}^r|z'μT+{ٶC8|Ѹ)(E0 ` ?ezUj}Wb7gzg1ךb0 V֋?`"7TV<]S=c_~k{OJYzW֋t^8acB=6ۉ\ެ1005ϬkT֛u\szM7EB_GXx㚇X/ymmmWǫv^z3uk0IAmm30 `h@) gyK9ƢϵYUo[;ϲէw}Wx/v"Rn⫏ RnWi. _+s `@ +Mļzo }nyahw]zڬ>X0 ``V Tr|w޷lU}ڵ+!˗5) _Jƾ{VIF7$VLԚ""Y]xN0 ^b;s"]X*]xq؟X/za-Kv'%km:`0(bb7MY+ֻ_̶!̿{#>Qs.4Cw⹗)b1sO^W=86? uas\Na0 zq,ETULS-nbPAo=;gU X}OR5IDlM}=mku Xĺv`0hzorOu}|~^î坿֗Orׯ]=8pG=މ ^ϿP/^:1`9onn^׋Py.0 `00oQ ޯI9ث:hc~c{[:n}Jlޛ֏Xy^|vs `fX/M\O~ڪ>r>sw>/&b/ f 4rׯ8sdZ\E&Rk'֛K `*0,Z9M"֋U n喱j03W}vo{U=.nMR5$UqM[u057U] ½aW^ZW%D馛Z)'5si '}yq[6u771a0+X+z9oʱJmzg;ў[4u~;O?S^+Ԝi zNx3/Vrv af; \/h 01@=1SMض.RWUխDd]۱ .^?JԦ { j c}b|k6c0QJ^,s~%TF|]SJ!W/D2?N|ǁVR_g/9D~⅗Ꝍ/>~gccUE `0bm#DYIPݻ+b0,q S%֛Dw-DmmeҪs년cL7)gMĀvuX0 ``Q TRrs_V׮]7d?r3?g}|G '¦OEz_@zH?=buQ>Dž0 `)2@w*ٸp %h^(/&w̃׉!,U1>i*c[]lüL᪱3[)-}-0 ` obbU爛z?OԊk }t}|wZߺBD> ut#R֧Xo߭rٞ#?nh߮^ޗ./0 ``X*WUsTmkcN\5U++\GƘl뵙:^[Q\ٷ13V]<`Gfb=qeq{ 0im 'ҍoӦ^[?uB+?]vEߩK?=w,777USY/i.G5 `@ T ԂiگMZ{q]UUς*DzS9 j 6 ©H٬4סNU ƙIz9X.mF |U_\:4bu} s0دr,a7u#ۿc'^ `X/$sT%Zo1z#L?G ?uB͜]v̑mC E DPDz.TrX0 @]5_mEM,Z6Xʯ⾪9؛:1ZgĄdUbj~Wտ wl-cbK&y.^:y0 ` JW/ڄTNo\''u;o/EV{dbsS Bۃ? KXeL1ƀc `b¬&4uֳM+ } )m#kz{[۩[e?K[!d#b>=TwM-k+klo![_g0 `X%Jgx{m7kc~Gg}˟vOZ"K pYV_Yϟ}$~}ooodz֧ zF:ϽZqj'"i~?گ1dˏ/?50 ` _kRf~_!Y OYzٰ(l FW1=6MOn3&q\ 8ּ\i3A[qض_8*a.~׉g0 ` }zwo~lU}uCC|͗/k~SʙEOaԯEzP+<@ڊz6^D& kh 1 `l ꂀo'}O^/4n?s .v\,g0 @xnQLMzip0k `00oz}^D f0?m*ϯ5B_k~E"w@Q_Q/}a,777USY/y_xt0 ` 7btX` 0 `k yoW/lmmV[__-N?*]7N9 aZ^^#wjz)XŊ`0 tP=/VU鍊n&Y?bn$kw!0 `(z?p_R~&Q>Y't>Q]ѐ}t>VA=g=Bۃzзkz@Y0 `Ho1*۷o*ݪtc0 `` bO/ sOu}|O~ӮWKM~o5 Bi_:w4 ?'˵cS}ޱSm'K#A*m0 `@]x,,gɓ0 `X-VrVbh)Ÿ>`D C߬~?Omll4 0 `g T ;xLzޭ!0 `1v"Uk Y5_̯MY__7_P**FzzzBD$~\Z{l|9n&땑^%50 `]e`߾}Ѫzo}[g* 'c]&0 `>1^S]_}Zۯkî坿7g}r֯4 =@:J(NWshWY .0 `2b;3X' `0 ta^.S]_z:ȟvO =Aٷh>ɿOkɳ/?y=B{noOjt|珿YonnR3*HBL `0g cpCջoB07tgQ֙c0 ` Oz9o|S]Vme1K1! cyW7fi~ę#Pl+ Bó7zy9ɺb0 `0 `*0P꽽B gH/WH^\ӿ?+VA=?sq쑨-y^^XW `0 `0 `90P%K=}Kw]n:g|e'Ͼ>Jpkz){~oP/oED9\P6 `0 `0 `ȝXo;秺;>Z/^v}}/u|T[eBiu!{z %c=TΠ]e=5_1 `0 `0 X/ S6T֋?':\#?sM+b0ЇRP/?s#hTYOe#]^{yem-0 `0 `0 b%S5綶j+뭯hcOqt?S'̙kGAc0g8>uuG$NB& 20 `0 `0cmm^&!Xb?X/aCBRC|쏲o;[.:#k.*~GA?Ϲ=}kE/vbջpYe1 `0 `0 ` J_~O O?X?M_'MwiNU;xZ'O}b; \.d0 `0 `VRBw)槺b=|?:ӯo[l9ӂPop|znx|P/@^zwqʚc0 `0 `@. b{r\Mdzۈo|_)7[}ZLW?b'MċBO?yPH_d[X[[+666n.0 `0 `0 `1P%=c|}qV :e~o|ꋺb'μ>J} RnAzED.l򻰱0 `0 `@~ zI=?T+{UmO>Q]}eGOޱӇ<~W?sx䙭"<86BehH6]XSk `0 `0 ` ?zorOu}|sy篭_Gk7ysiM"j / Ro ~O훛*멬G `0 `0 `HA^H撿*bs[[[G}'ruB0|#W>rׯuҶ^^urP/vb_ZS `0 `0 `XJ}'K'?3>B/O$/n~Y?'s?ӕӢ*[;{d}.^xmy{cۉ\جʅqb0 `0 `@~ bO| b)#ֳ>OSW:>swo]2-w}Z9~e';gʶRRP/vb.&\6YS `0 `0 `XVֻ'DlVk0O3G}f~̋Yӆwkgϟ=z^S BDۉ\جʅqb0 `0 `@~ b{r\IT'zۈo|BА1?uV WO,;=Sk 5gZ]?]\XSk `0 `0 `0P%=c|}a]E`]g?|27e}~|C/fOEz/ӿ ^ۏ9\{zB""\.)Å0 `0 `0 @LC&Tz_Xmo߲v}}/93o|Gtkv@Y+KCBF;ab=bh̜/o$0 `0 `0<hD?ݟm!cs?V ]?>Vq~튠Sz=o־Oz҉Yonna0 `0 `d`PS&h'?mmmՊGrg.ۧCÿrׯ8sd{R@kz9;}0*D ]+KX/0 `0 `VRw_*3姺>qz'iԿ2MVA=ڙ'BWN+ꅟ~G 빼Yc=0 `0 `0<(zw%#`)#1Uwf}sקW{kzgwՂb{)_}/s `0 `0 `@2 \{Z*U R &b\Y<79ӂPop|kgt|VﱃPN*g=6?0 `0 `0 `2o~s$r&y6b=볼ac77koӿV`ڛBۏ>MOwBvGף%~ve(> > >VGQ|GQ|f`}C^S]vXaY>ǿ圿7>}ƾ_9|*]\_ nʾO :!'>j3`}DFjqG6V_} 5DK{qr%OZ^A͟Ճm$|To4` ??Vv?#? m*?ƿ/??_+b'P#E}4n_űSϿOi7?GE > >GQ|GQ|g䧢Bz룏^^`=?>?"ϷN9xij~tK6b=\t|zǟ}cn~>Q|GQ|GQ|f`'GGG+cv~//g9_>N=sNJZ^OV]4ND>GU'GG1A|aT2n|G'(:._B"offfff6-r֧W{7zζ}{W B= c~6Z>7}QuPY#?#A|ćvXOPTiOI<>m[wu|zFg>=3YPA~#f~OFGA}>?C"GcA|A|'>ʟ______~V߭MxW~ډH=7ݬm|To` ??/GG1A|a4_LquU~ݰzCS^]ği/ѓ* ɥOߍ Rn;7ZOFGA}>zA=?'ϸ"???????`y}jz"+?M~~m9rbOGEŞg{BOz>:s#Q|A|b(>(>3````````_tce=ⷫ+?O;pC_jz??O>Fh~Qw>Q}Pd}!~?#?D > >Ljz#>Gği~Qe*wډ3G'AEs|hk7?`(>nGQ|GQ|?????????ɀQwG/eݱ_ Ag?O=Ovܫ}P Y=\?'{B}vG%"A|A|M$(>(> -?G'ESdͿnmkzcOŅz;m|ߣ}cE > >Ηܯ_r~'O:vV|||||||||ǟZty^eW*z~}BC}葿=(i7?u1|u>:?.G%A|A|MOۈO'+*~www_էްnBg0oo> ^H̏ >7b}QuPHG 5A|ap=*ʯnxqMELuOOOOy?Ce_FzaBCyhi7?u |Q>:?G]t >0*ě6>~w׊Wִoooooof9_!{ؓ=zOV(nPFϵ|FGciA|A|F/ZGQ|GQ|}'+72@? ~ˏ׫y]vEw'~c|+z_k7?Q>QoGG1v~A|a4->(>rVXo}}}$G$~-~r_AV;qxE=?ߑEv?FP|GQ|Gq ug4X_d|s.:#k-7 rn?ZvSwь|:>`U:!GX"R|A|ćQʸq| x pYV_YV+?V!@~i7?u)|m>:?*G] >0$>ʟNWi~߿'O BU֋?͞P,ۃR+n~)>`}TDE?#?Ƅ > > &mzl?d/}dO aZ\D~P{Bi7?`H#?c)> >è@|GQ|GqXh*=խE_t,iAwߍ8FP/mC$c~.|T_IJ>GE?GXL|A|Ə_B-W_W?爟Ϝk;z/o*XIW,|#+6k7?`э6?C/ GG1 > >V$GQ|?z!%~_E@iMrapu^[.^$A=>? > > Q|GQ|a?ڪ>#o濗￝v2$}Z(W;q|V\_U֫r_ >:`}#?#hA|a4%>88X/p?3 cg7Ȁ"_T/rѧ{db-(>  snSŗP !v?"#?#K >0GQ|GQ|*,z0XOs;?O+ׯԏ(^9WćmW/;=f>?AQ|GQ|av2`ddԽQ[e{[3'dClaÇBW}{.ZRo?ľkEHn~>fA|A|!//_q~!'OܟtXn>-I/|~"noJOAGP}>L?C\?#?1! > >;6,ʯ~???????fv*gVKXVq4OP/>-n#|>`Uy!~?#?D > >)[+{GğڊSor+gG RnJߨhk7?uGuk>* ???#?G >0Y6c?_AB\_";z(̡7Jq/Bn~}?H#?c(> >h"F|GQ|ǭz#9"[o[:Çc?)+"|*3i?c|GEv?FP|GQ|Gq ug4X_d|*sWv;Z^?_cPOh>Q`}D?O#?c,)> >èPe8XOPt\D~m[O+ׯ֗jzlC\+n~6R>7}QuU#?#A|a4;I|l+֓??NؿۿۿۿOJYzW֋?7=ޣY|=n~>FGc-A|A|F(>(>B3eڈ,>秇ײէݰn-*O?8cɧ,~-(>?# > >C 8_:__p~EgCB$'Oܟ?뮻놷_Qu/y߿xb_F-ޯߣBOvG/Ҵ| 1A|A|ꉏ(>(> :?okz?Oߎگ>}}} }O2?_qw_BO<~~D|TT`" 9GG1&tA|aB3_^`_+4/?/=;]v+'{^?mO6#d(~:GE > >#; >2VmeQ[%~_?> uCP/O>^)z7OFGA}>?CGc > >d qbqL> 2o iUЯb#=޻,^QO>to#"|t|UA|a(>(>Ïn??????`9H%x i=Bm{?>:`}TdE?#?TpxǶb=CS_9?OsRy'֧]ޠ_Y/vx"_/O?}Ƨ}0(GQ|GQ|f`'GGG_~ia>'dli1cB;ľݽW!K3= v?/GG1A|aT">(>8,?X*vGWˌ[ N>d*݈Xē7=ZQ/S/~gTыcA|A|FΏWΗܯ_r8?:?:?:?:?:?:?vZ+/rUY#؜|GE[O-R~ϵ|GA|a4->(>8̀AW?zoW_Wv2!iM" /|Az/B} B>M|T$`ꢅ_#?c,Q%> >uakkV>R#E|_E|qʉcmɧ(/7{^&y:^Q@o?-NGv?!nc䢆G8w~^y\ېPsUЯb+GGOWz?Ϲs_6QG}c,(> >èE|GQ|Gqݿ?ȿ?u%6XO/K!>O9ǧ[YV_Y ~9؇k/ҵ>:`}Td_::p?S#tahh8XOP{ޜI9ӂPop|k_=O?yozOf~goFيxn~>FGcuA|A|F/rGQ|GQ|sY|_?iUO}էݰn aO>T?D(> ?O]w]׮]PTX~޵YeUMWU6hO >_=vN;EA&}{G?ſKk7? } >d ѯ_??f} p~{wcRX++++++W-~w#~ݻw9gU;gD(Ij|wXXfBƗw~Zߜ7#B9OSקX3O=*Kԁg'n ڿɿ.| `0 ?¹'My~{WO WigZgz `0c=?vev~vo!~|枿6 Fl9>+wީE3}Cř ^w|,~~bgmN 0 `@x׻_{;q9oc:DwUO1 `a q?T|33綶j+뭯H'6?=G+ro;'ÿOߝ=|h)zP ^{kkX;Vs{pOmBډ?,1g0 `)20,|C6SM}-0 `RYoV _Gg}˟~fsPȉϼKwO+ׯw[Pzvo~FNm?SASm'֛%0 `HA^痫1Ǿ#SY+W0 `0О>U'ݲVᣬ8gZm!3sgo;o{6k}ڰWY7m7l'kw9b0 `Rf]7d{)/:H#w0 `RwޑV4S]͟vO+_}!c~ QmV-۩?{+gID OQ+K}؈ 1]0 `@ b_Wٞozy0XG `06 Tr|w?"Uk|_󛲾oT;T[%ڨP3ž|8|,rhb='B6 ^ 0 `e\7mMQke0 `&a &!{~7׊/ߒuy篭Yo|{>[UC_iP/(7a0 u~"+MyA';I0 X/0Ʒ|^etNcB~Bc+G+ އ=͡[3*aDV훛z+uɔ%un0 ̒*[8r)Xv0 `@xw~ڪ뭯H'*~_?}vɾw6AV;{ж~@mz*zyg17zg>ɞP`zݾdp d}0 `f@L59oˬ` 0 @)ֻwgy+|~mE7ffWAv 'O4rn1srZa0 LX/ F.~0 `@ byoeSha։̜r#3$$$vwBi~;Gm;}`;^.0 ` r=ߔsw]^{mGX10 `șzq~1ڊ|濴ek~s{b^ g~=)5g}Z_7W֋t?PoSo|oɿfE/ϼ\E%J >)P/>$Ft|.2Ȱ0 X7u}XPP0 ``E1ٿ)[U_>!˗59o|/}z!K}Zn`zoنDV;b_$u0 T10XY>ߔG 6 `0V!{jNXۿ:y~d| 7} ?:[_!/x )1BH%K( `0P2P'`_ep)/ pa0 b7,bίm*U u2:_o/eZ! SH|#S>rׯ]9|_^ﱍh|ٗ^//ޝEz*y0 ` 0P%`|F(i:I[km1 `j00(|3ȱ-.?U[Yo}}}$h}>QdF[Tl!8fSrЧ^g_sӟo(]V_YG~X+̶=l䞿 ۉV%0 `-MϾbmmkhV0 `1p=OXg)_x槯`CD~P>-ǷvA̡ ?E;~Ho#/{l|)w1ɚb0 (z׼ ٞo%s7oX7|90 `XJ޽{Gr8)oMX_OΧmyoͿ5gli1}]¿~"n;v n?ObmllDp7FV0 `@){d{)o?z10 `*^oKlتuY5_̯Yo|;_}QghY0WڈX̋gTG'"G^*^R/Rt|9~XO 70 `Vz[6_ \ \ `0 @L&[WAXe?=?j|y翭Y߁o|;Y#>]+KK//oF;ψV 12*(k}1 `h@|S^T뱉66580 `y00,|3̩-XYRh;$[ QW_SH +4|_"^}+a蛛zz*`0 U/HJ[G `06 r< ΁-N(U+[__y*YJ/?%&^sׯ= 8{t#I#;W{lhs580 `3v~氪zXC `0 bbVu5qz?)}2VBv]S'_X!{1 XυG  0 `Ha[盒QbYk0 `8 bZ $X`^-d~֏.BL]]eǗ֧]_Uԫ}A{``;Kq.=/0 ` ]loJ>ZqF rl0 `0b|~1ڊ|濴ek~s{{/þ7O0v@Po`|zN_\P/~BDۉ\l0 `X J5o|C۟+bޥ'60 `@)ۻwH+CX_OΧ./ڤz~li1]?/}v"RnЃ_ t)؈ 1W o.0 `VRo=ߔ7*ش10 l3P%=c|]7\u_ߔ/~%c߽zPٗ"|ND玾\zE_TPq0 ``ZYٞo#%0%1 `071^S]|kzuy篭Yo|{>k;xaZ^A1j79\}Qc}/0 `fN.r8ߔzc0 b7,bίTY?|-,+2#n׮9\p"|Vˠ[W ~~o̢}ssSebju.0 `*[Nrz/0 `Xz9oY6綶j+뭯H'Ϳ%= f;}<[m;@42h?߫W 5BBzsarZc0f &`_t%t~fz:_g1 `j1P,7%Ϲߺ:qz'~D!d9oͿ~=wkN*^غWQ泌-<-^EBډV%0 `-M7rY{ `f`߾}E60/X0 9߶˯#bsY>yUh'}/ʾsקW;Ź+zʗm?o-ء>vb|/(\>Y[ `0Al_gbmmk%{/`*2pM7??U crgJe{U'ݲVJo >}o=viA7[tTs؃_'ԋ?:6RRqKXEM5Ƈq `0@)ֻo|}J ^Hq}$_ozӛ-R>-^lpOD۾HAn?rKjccCreL.CW"x]b0 X e{)oz7 9Y~^,9Ol ݸS`wy*їW{51[cqJ{~替w]7ܐu_ߜ7%czsUPZk{l"RopFƗC$s9r 0 ``< >W2t\tшx +OsY#VgeK^6zcC|J:ek\2吿=?V+ֻ:aG_[_뛳>|W#^x nhq"O|#z 0 `+@-\p)/:$n$!  .j^bÿo,UaUUV^񓁙Y<7P?M.10zo1Ŝ_7V֫CbN¾/Xj!>~J?ЇzCs[ MbH̺%y50 @ќ22b=6cb(WT *aأbwmkbb: s~: s,S SJGmW3/|}oWAvKg/zB-< ǎG=ޱ !_zzݹPpc-0 `-M9zl-M#+?9{TKk3^ɓJ{UUK W2oC޷{-hb}/n*38oBu8=*gn ;w)]V_Y3O+*΅mpXwYﱃaz.:.:c0 -˕?~/k::*E&{R:J7#Lgfާݚ8Oz9TֻYoڞr?-k|mz>^dַ/dw ;dO+u~z^@o m'sEa0 l3Py=ߔ?=zDzf% ܅s*tMzX"^}-ͳy'b=t(z 9w$zOumz7u>u~_&}֑o~#[}ZL/|{k'"~۵.O|ύ  d t.0 ``Re{)o*῍X/TЋ8"^;&<}ĴK6>kXypd@X/7/|lU}ᆬ/_//~~(bg\k#BzE ~o0 ` 00XY/v~3߮${YR  ? m`l?ͤ} c o߾jn3ks,^0bF\i/܅Y${} [8Yc>.&ĞTeE_>$p:O^_-kMbb7ߺ:?Z o{k%?=?j|y翭 |Ϝk;xA]OWXLث=wzx*X0 `X,upCXRY{!Dxnx"=jY8c|Cn hӗ纱^m>|M”0㎩ l  bgJ_ >i{Џa۫Zgm|,c-0Z\9 _qd}RF 3|,9o~a^naΌo16B/坟֗CgN޿_";'3{c4‰@|/MVKn\Y `2sO.r~Z[.f&['kdYx&`Щwf%B^MYo3{1KA$H `f-ue AZLƆ?ofؼխݬlNIiX swɉl1i[{mUӪǙO2?VU]Z46|kRbica~mlkSr< h|;nmmV[__ZŭOT}m:>Nwmc7=ʥ]m^y{Ejz\8V0 `0v~UJJHP'VcZG4 H&:Ƥ³hJ4q0sJD9/oKִbc|ߵD}Wߎ=}䨯`[pXq$c}Z_70KFz};,A('>pGz Qt0 ``5(z׼ٞo%ُX7l~f%֫#+E">n5~T<.2&ڨOUe>Tշ#S>i375uBݻwDŽJan* M=+^iu R%j[oym[NUg B:#Q[_HUkcO9i#H-Vy8b|Z6B֢c4lgǗ ΝGϵRr)?毖=6b=|?]6>~So|omZ~VE/bq[w,zzœ'^/?K/% 0 `@){?g?D|SϞ|'zzbizARW o?W%nGR'k>M¢6Ķlӧjq۪_m>x k^s+j(߯JFV5' zm*o ]c(T+S f-֛%66b0GUš즮=|vv1w608+*W%m*!eV*]zq;\6EXoyS\W7K!{}פvs~mSYR;#[ W_Sq|_"ֻpxq/ī*-~+?AO1EX777USYOe= `0`J5o^E%g) 5nqT\RX4P/if9³:ci, b:sb0J$j TիC+mWE넃GN"OPn|1޴B:FVYKk$֛45 ǩI$蘆xw~ڪ>W>[Ɨw~ZQ!4U;(wÇ'3<[T>8*E꩞|h MX/KD 0 ``Rbp|C>ƱY{LS-wb[mBb>DpS%УYʬzLZqVbih \ҳ`gac}cFX'Nqk'QUٳaUuk&^4˴>ʎgi#K׶dy)+ֳ>y/tO;;>/K}¿~=wkϝ+^jz9@x Rk'K%0 `hX/ {hkuKL*I+aVk.D1ӬU9Ϊn#\XoZV6J76ϰ u"\ϙXwՍ/&.m[ i|U]'16ikbEOm:T=~jZ;n^^WYoV&j_Gg}˟Hǭu-*d YV_Y̋z϶=׍6^.0 `8 r=ߔ䣏^{mG81qeD _AS'D 5@2ZM5Xi¢I_1^'`Vj[m.U#'e'ޤkW%̉TYx}h|4s2iYT9qPO۸9:Qls Fg%ڛv쳮7meXoJVlˮ?[?7X[**fyZ"#ſ '|iB[|\4qDo"~PDO*_d/vb.&\6YS `0Xk|g?~zDG}ńMU{ȤJoɬ* W[#;'|#[qMM W=ޱlb:SUM2[U6 5 Msl鑜]MֺleLL8}Zqܬz]\y0ZAcɤضz]gR㉔7&TF|]S< 7ߚ?y$[}ZLS,E;~tcK[?Obmll4^"úZW `0 b7}|OUcv=hJ DG}I$4I{rm, k&JY*iMG$b^ɓc"ξc1ku|a`}^ be,~l &yowf5OM\jo?uuzovlU}ڕu 7-ʾCeiU^?aP/>H?VJ}b='+NtFҽ@v0x<WY?ߔGbm2ӈxJ4N%Ef)Xr8NObPAi~F(8طEd6U~ָ ͣe_y$txjꠁqě)Tl,O#+V%/mݣc}'/oB~'ղVw/beϿ;?m}omL's5=rU9vzɏz)YF;vpdM1 `b ض/?ߔ}zlTlUݻU+B꫍P/k.TlN\JaM?Ъ*͊}ڌ+sZ^#3_"ֻ c>UL=<KOˢ}ssSe=T0 `Xl9o b=6iz3*4i%65mXME&ML+ U%@G\6 ۞v.ߟM9,Iܷo_ֺ:b[g q9Z̃~X\ge'X/Dm>Э;ڪ HYTEo| 7*wÇJxG\T>8^eh 9ťډһxpYd0 `&a &` 皜7뱑662 KV \P|̺/9YRWՏIRm9zݸ"UkۿiZm?a,|]Xĕ`^?;- .<JHT-zM{Rgy)+ֳ>y/t!L;;/K}¿~=wkϟ/Aq{?> KX/ Q0 ` 00,`|CqݬDi[J F2X۾ve£[cB家Q*f"zrcdU}F WX˶+gՏI`wfiS{9TLt>&Wz]\y0[ :O+z}B o$ەDmg/%|;7}f;=}^r|z玼ԫ8~~GmtLq'z3 Tۉ\Ls w `a`PEJtڥVEv1j]}lbY6KƲzA|kLZR>/fm2JTUBYY,m*`cBcGP\7'_Ui7z]\Y3:N0^go{zc|ߵ79_V7ӧ֧ ._8g BAz(pc1 `5d{ o~ozs܇Y}l"*S]Y6-֫ԉBEx۾nVbyjSI-nUæ*lm9fiSsDku W|߼U&Xk1kF죊ݪYTCf6c)֋;'ղF|ooyoͿMO
8~ӈ-]YMl q{~L*DS\ϛXo1ubb8|nl*FFO>ĘX/M緮wuX=aQK;m_]'>c9v$}ArgI*s!rZb0 1P'`'28ߔ!zl?@J~Uڌkf)Be*qSǩYWxqqU՜FSUmmwLukfYY/.x*V6f5TI{Uf>)׮BIOUԫ(-AɧKSzf!Ҿ TQqq 0 `J.@S9T%BO6>obi\UU3᱃mE[a bзGSUj}1U´зk[  V`5k4xeA4@-gSB:^)kFUcraU_{]0oU6FU=Ɇ%֛T$\Z,KW%~Tinb7ί[[[]_[ŭOso]2]vmc^5S6j'R%0 `b &`_d$t~Ez젍/XJpVLSUm)IR9.7h\QMُꄐeUAW% ߦ0UXAҴU6c_pϮ[W4 ޷ \٧GA7JRA7+{ :^`jm;3/|}oWAv.z**ꅟgOd=S_Q/vbոp)e1 `l7%zxof- Y%h#HAAM]_s*gM:'MX/ܠ0$]WjGaU7Cx)Ehu4}7Yo9vUhЭ8b=BW*ezRխN,>]>CׯwNѷ' rm?{8}ghﱃ~yzuizc0e`PEHɟ=֯tFo`u`p!֫#V%iy[UlN]uIV4 B_fBe*lom*q1us5ƓGYM,6ҺS$g^ƩPFV Ee*g>\eD6v5yTYoڞr?-k|mz>^h~YI+/e_YӆwzQZxO Rngzy9ɺb0 3pEw5ٞoʋ~z@CW'F{Z!CSU V{eH,Zzӊºd㊵YY XCqڊ=šʦ}4pv>&U=v2_ } OwV*M"t |Ʋbb&r[jWk/Xbo$)6b=|/%9GYJ/ٟ>-^􇒆'^EŹϞ-N?6gBo~kcc#:>ua70 `ȗRwypn '3b=bFbLX4c%cBإmu!r >.7g#Ybbij1dqsmi[`춂s{91rٸ SjX&68+!lG϶{m+΃MB(:):< 8ci29K>MII`b2KYq}z 7wߗTw:e~o|:oߩ*?rѽ-{Ap{?s!vb=I$|.0 .bl>ߔ|/W  50|}\{ Έ} q+] h843{csW5}9m0 u ?V޿*vq~+ s}dsZ{-> %i<֪l{ V}~S0Zr]K^lp60kQ3VWq9mwYT^>1&|OՊv]t/Fo>0k;=g-t#}R{e~Us)ϥ0 `:[8t!  `]`X }6H@0V1sEg-֦zoFߺ:ms0֗ii7s^z 65B3/%[lrT2|c3@6Fأ=Lc/b7sc|;nmm?w}}$Gj}>UB7ȾCDžkgچf[LP/Z Btډ\Ls w `a &`۾LXV@ ;g'[xo^xZ){_|S2Wg}_*mGu}C_;~[ځ{*^x8uAs{ptYp `0 9_Fz4e13@Ѯ3x#b`+$[jb2=\IL^k|.a_3;-fSwO{}|zUԫxyks'E/ .%Np^$[0 `[ R=\ٓ^{|cXz ٛvc=bvk}>z_*nYk+[VQV3k}﶐ h>}o=>mHׯ;zBG&~A{t IF `cXk=/:zǾ-b=✥s|2_93@dlrKwy'T{Rߔ|ﴉ?/y `2@IBųa^[Vb8̛X/M緮ogVr?UoOܯ t޾3֯H] {Po%~\PYObe0 `J0P'`|S߈#0@6knƨX/U㜝vfӢ^4?I0EFO?-d_ 6o?+}^ K BD777U֓^˯]~Ks `H*["KEPdon@n a:7O<Lr< w~ڪ ,n}}m[msҝ_UԫP$~|_%|yՅ0 `61[8t)$6|V7MŠ@)[1 smՊw:e?ǿo{Kէz;uуmʇ_--t ŹB'I 'VbIbO `@)ֻo|mO9Hc0 Ub|k~替Z޻/_?7񝯾3ө _9z/5Rn xosb_NRܼw ``eA6#2`0 ``> zDb_n~_w~g~ .>` O>c޶P/Q,&~ 0vs9żb0 t:A7MY1Xu0 ``~ r; c|9moq5|b쯶"jFW} 2/9/ܿe_" sGzoxW7کonnA`0 Up"pd60 `]c`Pf8{jzݻwByꞌ>._Bz**~18hLXJ.V0 ``> l\dXo> M `0EJC_{ M9繞ߺ8}sb~xGyE>sO'E?[ߎosWڥKŹ~Ź^JP/vb=+]\'\b030,`_&v~5'֛=?lҜb0 tR_*^n{׻U+{[2*| /VWHǎCH>Gz9^_ޥ3{BroOD/ȺXl's &0 `-˙oJfzbmmk7l͖-i>1 `z_*s5ўf)kb_s9cN?:\nEƉh.cB~kcc#*\10 `XJ5o|cEm|^6Fc0mz6p~f;zhcUPu/T3!>?ha]'lB}݈XP/}G~XO 70 `Vz5NE#%0%1 `071^No]6V{׻Ey#ʾ&t#t:O(_;;x/*Q!Z- Ǐ/U,t9%K( `0P2P'` 7X|0 `00,|3̲裏6 k_}Ǿ!2b?gGEP|+/~*s^BF33mnnUT\LŔ0 T1P%`"X0 `X/ !U絾bwx#!!J䏣?g+FLȝPCۋ![]EFDCŽډV唵0 `@L?h't~rz:_g1 `j1PzkYov$23<-{|ϟ/w6 g{ Y|\~}>&+_;p]k/+ K'O CIXo.-\RYo `0 ¹&M7rY{ `0z b/ 9Bx>VB_G`|_|r7_o] n_֯wܹހc0ٶ~Pxb#~]Ņ*k `VA[痒ݦ?ZqFϯl`5m[w `0'W+=_%i{i:iBEB+R_ix_`iCW#=̦ȉpd]1 `~?!֋$Q~0 ` GJ޽{GrX)o$b'9zhG6︃PP/_r_3 5BG6!/l_\:Y_1/;n؈4r< ) `0Ud]s=/{3*ڸ10UeJWjp~&=V l'=|GaS֧U"1PRţq{? mlCqG|>FDT;R0\cNa0 "1^NoߡCoH/pKKɓ'G?s!jb{;u}ZuJ^,.?z_:uVCz.oVƘq `*2P'` 99oʿx'chƌ{ `Va^nu5ίᑷrK7y,["_s?wu<~l~X/Q^#K"ޥ BD777USYOe= `0`JֿHHRUqazի6nc0 r<ߌ$3;c|Цh\qc0Uc`XsX}}/1 `XeJރDbnA}|'}+QϾ*腅آ2'[<M._8_h//=z3g6YKc?0 `m,οخO>Y^{mpX9 `0W-nx|>bb뷊P/hO93_X5Lh!">eSן~esMd빨0 `z DO>X_s?s޽{} `0 d//aOh|zwAQYeYB_IņӆOD:z{r*NDؕ0 `ȅ+bkɮbxF_|9Ǘ90 `0o~sGCE<+L[E8?N(&4!4!4!4?}?$>-PAx) $oXH/*%)m0 ``^EAK? /?uGk7?`u>O'w 2:D i~~ij;ݿ+o!ۊoY~|HJ|ޓV~A]*C"=rh?{_wܡ>0 `*00ܑp~|>[_HE/O/|_xdm,bҁo.n_;2}}Y뇒ቩ׮ D*멬0 ` 0P%sіw/%$R$$$$"|__B U9~xϻJ?X|(Ο?ߏ?~/i!]g?Xw l"1s ډTY*2ƈs `0Pz]B痨I/:͏>Q(`}D?ԧ+_x*=O<ο?oI''S՛^{/N.W~9szfI\K\c0 bpeU~~}_Eƿo9?{ɀ&oO~Y|Е.Ky^x1.%$ʈV#1+o1 `ze.\H['G(; >0zw*>BrUb_z.w|!&N'i+q `0 .%z~"1?u/>7`#^X? yGGqA|\ u}PGL(P.k7Zn;bcc#]ĭu0 ` &MJ|޿o;w.>`UdxED\i~-ɷ_h0lj"jc0j V֫J|sY녘T<`?yX_[{Q/>CeE?k !~OS??W)TC|B(鷫'IjIj<0 `@X[OEa;㫻GE9`}T%أ_A|a'ݯ/}QwWzM/\LY*멬}&0\HqL.]ԻwQ?Ȁ*W??????8cÇk@+UhsuOXO2:dqa0 d &:$:7 =7wU7M|ķ:`}-md$>>=wP G}Cbzؒ0 `X/kzov&}o?GH3#Cco> 霯z[֯K m1<ڦv:~bF$vbHJ[g `0A޶P/Kɱ[?@c~H>j}Q%F+q?#>c1߯W9cQ4u3MdIJc0 b=i.\H[EZ+a> %WU|_WBBG?>F|*>v]_6ExF+ tO};bcc#*Ĕ]ĭu0 ` |?&|>|_7f~P!~z#?c~jW""&~Roۉ"ϗ?Ym10 ``e?Wk}k?gՉ8>poa)~2կj$%0 *b=Y\4hvEFAA?#?Ɗ >C!S^*[^Bf677USY/kb^0 `@V T\ĺ|KFFFM|$%%SM4__'#?0>_ѷٷSXOeU,c0 ``\tvEE > >OGQ|GQ|dz֯dk6YmD$W-Im0 ``UHEB>f* 0O?+ ;οjjjjjjj|"\GzI6sE/d&0 ;z.o~d Ћ_ ______ 888|0`hs!֓^$b0Ue`Pb:!!G%$$$$$o'_~/ ~iif& OP]Ey!c\PFXۼbmm؈'*b ?s `0Q+*Eη.]TV7BxxxPC]?E;=?wI&w4o[' `^׬>؇NL+> >j(>_eT֓ W2b 0 tZ^(/FEF.;ux ?3?Jȿes>hdggggggggy umhmnnz r b0 b=7W$~OKKKKKKKjѽ $8{PYu!^Qz^>c=0 `@\N#?cl"> >((>(>#>4 4id[0 `[ zoi߆mY?3 #?cA|A|F+UMR>{B?틟bn%P%0 `99=G#`죊!?#?Fϼx}ĎWI|ħS"ڤRЯ,:('?b=y%/0 `@!s ^Mv~Ǘ> ¿$W5o?D!~ )~B[8 ^0 ``^ ,.b]ĺЍЏQ"A"aqVԲ???!` 6n$7︣X[[+666~3V_u `00J"E0a.exE'...." 89999992 >رQ /HZe=[Ub 0 tF^xxxxxx%$$:h''B1B1 'Ot?9ɝA*^0 ``^ Ԋ8߹r4닠DDDDDDD WCs '֓ W2b 0 tJDDDDDDDDDDB-CwUUqw?p7t62Ymd@ж0 `b *s|'%%M0%"Ut~CH^fI[FsC'|p3Ɔo `0pb=0 `0 `0 `HܕFG5:7_2_0 `2@Tob0 `0 `1pI:G 0 ``b=v.0 `0 `0 `H5b]+bִ{0 `j70 `0 `0 +^左50 `1@ 0 `0 `0t #@ p8ake0 `b=LÏ0 `0 `0 `` je(RYoFio `0e1@es `0 `0 `b=b= dg834g 0 m3n1 `0 `0 ``U P͸ `1@Ucx1 `0 `0 `)3@G H)黠 `3@~pe0 `0 `0 `X6Z TXX|>1 `b [<Tɻ[7c4va0 ̓b=b= d<0 `@w Z k `01@7̝C7|Xݻwɍɏb01pL|60 ` [\\c0 ``q -nsl]e/T{ $1 `RXʇ8bʦ~a0 ̖b'>'0 `^7!5{x'mם?`O^0 ,`jK"[ `00>z0 `^רvtb}}`/Ӻ:b0 ` _2Tk0 `dX|0 `rdXpoy[{qA7g]b0 2d`\G0 `Hb4׍Y7 `0 3@Foۍb뮻?H'O&W `B?lڍow8SY<Haޔ}4ۇ92G02@ᦓXoFio `0e1@es `00O5 B~m= `002d1 `j0@̞30 `1@y0wV~XOL0 ̝<6޳C,0 `X Vcٳu0 `X50?Ԋnfb=ys `00wL{Ob= 8c0 `X#c0 `92@ypVwXOr^0 ̝<6ޓX0 ` ? `0< s]mT]w]:90 `;seoW=!v7fc0 Uޘq `gX/5^ommIˍc03,c3g9θa0 l3@0 `rdXXW{ 0q P1J30 ` M\7f0 `0Pj\yO\a00.zzȐq 0 ` M\7f0 `0@b990 `eP+Czg `0 :g `0j y0Oyp=q ` a Cu^/x`04 Ks؛u0 `@=zld6Byp=q ` je(RY#x=f0 `@ 륹nͺaX /b3\2wl1Y0@Yp4zWW0 2d`\G0 `Hb4׍Y7 `g`޽//>Л0gXo9]`0.0@zK>0 ̟b1104سgψXo&.iͺ51@&F&i'$`00k2"(f(0 `@7 溰 `` - ؁5aX?SzWW0 2d`\G0 `Hb4׍Y7 `gXoscs ̓b=|̓/b=\̓++ `eP+Cz1 `dX/uco  ֛cdX_XW{ 0q #@ ^0 `i2@溱7 `` ql1y2@yEyp=q ` je(RY#x=f0 `@ 륹nͺa?zcc `` k|j\yO\a00.zzȐq 0 ` M\7f0 ̟b1100O5p5'0 `B Z*q: 0 `Hb4׍Y7 `gXoscs ̓b=|̓/b=\̓++ `eXX20#z0 ^ެ03@79Ʊ90 `2@PKe=`\G0 `i2@溱7 `` ql1y2@yEyp=q ` a Cu^/x`04 Ks؛u00?86< <"<0 ``\2j30 ` M\7f0 ̟b1100O5p5'0 `b=b= d `0&zi{n@J ŷ"ʯ}Ǭ?g3<32cǎt3ŗ`93@yMyp=q ` je(RY#x=f0 `@ 륹nͺa]g 曋__ uA7ɘ9mzQ90|}$c;d M6ox7b=\ `00!]p. a03@79Ʊ9VᄏQ8s5i;w/4#Q8cZ M>w;b=\ `02jǹt1 ` ֛c@ 7xmm-szqMU!}0V>Xz؛{zWW0 2d`\G0 `Hb4׍Y7 ` U+j+?&v -szJ {ls M?8Cb=\ `02jǹt1 ` ֛c}0/رcE zߘX/b"0ν{AB?۶*c$00p5p5'0 `b=b= d `0&zi{n@:^-઄}x9ubq>WG$V=wz N)0 `2@PKe=`\G0 `i2@溱7 tX&$cX녾*zMX_l`3@6azWW0 2d`\G0 `Hb4׍Y7 `k E}NX/T;w\1V=R*<{*1@yNyp=q ` je(RY#x=f0 `@ 륹nͺa]c`Q"E}NXmE5wwumF(0 `2@G x `dX/uco Ǽ~[ߚmQS%֛DWU};~ֵȅb=,σeb=\̓++ `eP+Cz1 `dX/uco 14¶-sz= Ldy#0 `]`XXqyWx^(PĈ"ct B E "CPJl@( ؉ vhhX]5] tWy9_W1С-L.! 0 7 ׾9 0p_[5.Vpݟ`׺N^V[ZG=ˀ^_j+E`m@Pkl@ޱ,j 0 0@[zm_G2Zn7˗uT.=z惹v֛oW 0 0zz th` 6x1 0 c1 @7raCo׺{>YՀ-l q•sr 0: jYYDP;8```Y7M``?mIhoj{׸qq]l2Pf@Xx'aK1 0m  1Сf SW`ؚa=&fR{d`bռMη_cuQ[?_hm@XƄj9b`ZZX8z&[sM``ֻ~s`(^|ShtX-oNߴ:ǣpv m= a=z^xf``ЀIyک 0 Ƌcya9b? E־s{7 lǀvjӸ'g`k@Pà; Mj 0 0Pc@X/Vm׈qa=װ1p;z qճo7 01 '@LJ```a=~^~``XA/^}K#Z%nc@X6޻wa=z7g``: jYYo$N 0 0K 1Ԑ3s N~_֋ͽsm{; lÀ6x7i`i@XOX 9!1 0@a=fj8X??.]k+vιkZw.a qus`o@Pà?Lj 0 0a=p1\/_JXoua`QƃWv?L3 0Ozz th Yԍ`j 1Sk0?\%7:ziݹxbGp.G`ؾAZV39 0 08ZÑsp?Ï wka_s]:q/zc{3 2 V [z\p\1 0@aY>^XDpms 0 0pzw; d7?EߋC{z߯~9yk]gq8u/<р- q•sr 0@Dx```Y7M``Kb%!pMҦzw^:zזƗ|z| qe|0 0[0 aPz&-L.! 0 0ހ^>X3@Ct硽Xm/V^THox_nUk]GXϘ}̺?Əb@Xօj9b`Zzz thv"p 0 0Ozn 0%=Vsau-/mᑁw```@PàL.[\C``a}̱>f =V Ŋ{StSa@XouxjofY`4 '@L}Nꪮ 0 0pi@X  0vl^|r2wk^'\67{$>8B;?z SFE:vZ€Vʬg2d1 07 w>\3 0 0ހ^>>ca}ʅu"({b#=?vz?1_nqlk; cC& P?'`ػa=nXf`H W^r.^|yvy {qٳV<Çe?uNa=a)#~C-ԢZgrarr3 0#} 0 0 f{gzp5'w/B`Uۆ'|r@Q('wT{FuNzz thddj 0)z\``р-\ al -9͵A 0 aPzɝ``QgY``f@Xj^z| v-l;rzzq!ЀI|W`l@X=v~``rh1?՚apo'{~?Ghg: gǙ> ݀V¬g31 0 À1l<3 0 0 1¼>\Oa=[C uNa=akYss a=a=:4`73 00 w: 0 0p4z̷0/WkbV%a |+2}^q'ORf :?ww],o.,냸ӧO֖9u᜹~kОZ{Ͻ?j4uqZq8on_^{nrlar޹ߖ5o]'篟>4 5 >F``u ӏsNa@XOX M 0 01 Ƴ:3 0 ̀- Ֆz灶Tfn'BB M[cϗ9ޒUs%s>wt: juԲޱI]``;Nkf`8a=[xۇkVKcxOIP/X%ۆurA8Wj1+clU5rϞ=[rjk%x`gNx2K=EͶ˭8b~^26re֬\yʍ%lt}<#թ: 1Сt?Z%[IDAT 0  0 0@nZXo7U.pTXo*Y"<U}S+쭽X.HAx^0X0m迸N }M:/k,? ;Qg=c+]XtNύ\J|1vdzԨs-ZgXkp``mv}a```ayx a\'occ&Վ]qV>˅ApXm(Lcằn ?qմ%]gcO# مҹ=׆hG:犿[sҶˍƹOm+=էZĘL_֮8YrNyN4f@XOX M 0 01 Ƴ:3 0 ̀- a\ njђPɶ9˹TjBAc+mV~* ȍmZhvmɭVȒc[׎@TLƼ]2JHnMrL= p^x:0ee=D 0 0p zǨ 0 0G3 | zpfXol)cS%A\PdTsN4n D.7]Mjjjtj+ZwS8O@IPohGwjݩqS㮴&cN\׎6wϝn_cCs&a`g@Xo53Ԍ``iz}Q} -[ThhXJV$kg.U{omeKMszضù~ C.`Wb}c[Kdɩ>eNSs``z}xTG``~n@XϘh1&jͰ^J5)oz-Vڟu*<6we\ t R̵mͶĹRFzc :)7w צT8pIw7uRsZg``a@Xu6ՙ``hoa^Xor#l+c͵ǰ^l]Z]p* ypKIkqƂ^ᾩ |SD/Yq>Lì0``c;Fguf`8a=[ۇ\H./"xEfI@5zsW'6l MzTsr2- [jW+<[XoN}]p銓-ι:NZwAlVo``[{^``z6 w zp$pV ks77me,ښ{9VuӖk/_֢)aϗD/]qrj>eNSs``z}xTG``~n@XϘh1&aa~I-nn+T-51' 6-^Ysjzs[J]Xdrt>ڪAZV3lu.6``u ۟|O``a@Xoum<Õ: z-XOKsV[#4' Qn/%`iԤT-nzKzQұƊ%5wyR黭cC[`Î`hk@Xm``a{ޅJXo~rن_n*8ennnkcͶ, Kh!WdKNm[s;~|ݭ juԲ3 0 \ǀug 0 0u ][XoZj_s9Ag5~ere˭pSיۜ\w*45-[%/{a5\#:_nxz>]N@Dx```֋ 0 0 kNկ~}ź 5 VZkҭ:gkra8גٖX]mI[jߛZͭ&{X/J17ed'k}r>eNS: jYY~ 0 0Їí:# 0 0 €>! E)BRsF.06|ځaXu5&\P4 7 ͩ.aszq[rcmn2uu*@zm5uv>wЀ[}՗`z,``` LF``u ӏ^:rk!1Cϯ4dehXmO}!\(-DZ`%:ߣutm+k›'qޱ-/jiK!5s5#cAa|,YqGk0ee=ɵ'c`a;``؟MŋPQza(d rzѶfKҰjr5}6xY^Cn;˶u_SmYd n%!胥c%_awckjd yogշ  1С{81 00 : 0 0 TX?ֻfG 5V%ۿSSXɬ/ - <={o4)bw*5<4mx:"vKa5j]p> t>>1$u-9[XrN``mv}a``ewljvۚ-7xv,ZV[#FX/5l%a[#퉕jۓZ}/Jp%[_3jqZXoZ/#̍ae¥>om@XOX 8É`؆amxP````=z4{w6Yη ܱywwS+y_";Ц-]n]Crֵelj Rŵ>wJ;&!̩U ѯQ%}pޖ8_5s;owKan?/D]Z뒶L[]嘜j㳈:Sε M9S 0 06 m.Ƌ0 0 04ApEfXXe0uͩ~[zΥAԸVy_ϸk8~2cC&ζտ 0 0z,nŢv 0 0{0ŋɰ.3ߟ2YMXt5=ȓ]Xχ^f``yo``8}=zHGg^3'$W{/  藖"ZWj 0 0Ā?Kx/? 0 0 0pDwww{M>3 _.~^zfb`@G{^j7 0 0@aK1 0 0 ?x2/|wߧ3?@^j | `"g  0 0_zqv 0 0 0 DduǏ^z%Ɵ{5ɓ5^IGnc@XzL&1 0 0pmz]ۜ1 0 0zӧO0:>Ü VՋS@_^ uRX>Xf` # 0 0 0<b^lu[τ1:Nؙ7v~;ϟ'W{pĉwo,j/ 0 0<z7 0 0 0={V֋@1o0q ɰ^8 W:Lx N 0 0/z^ 0 0 0 |GŁǏ3:~T5ْ/_`` ח\K```ӽ{zC;};Ws±W`V:(gh5a8/[ 0 0 mƇz0 0 06 Ǐ{Xq/^b6~ 0kcCkO 0 lӀ6b  0 0 0_^햸S[޿6wre`ހCV/~yW;``zxq,/ 0 0 0@XѣG[Ne:A?'`рqrO 0 0 ```W^^IXemjf,W`؃AZV3aFN``!````gVz̙w``ЀIګ= 02 wz 0 0 ~ {}ُͽ~g`nk@PVֻ2``kcZ\5``8ѣUze/:c={60 0Po@Xz``h@X=fn````"PwՃz^6{`؆a? mcpԁ``a=Zs~```bXoW{a=8qϞ 0 3 Aay$``f@Xٽ^f````^zu?``QgY```>}Z{p| 0 (>/ XZUsQs``Yw]```u |UAXIٳgBz]; 0 0Pm >E mF- 0 πjn9 0 0 0t޽o[[Rw 0 0luX?[5\ 0 0 ǀy````;"+ 0 0pzCXä 0 0p zǨ 0 0 0@}QQP/}|^߫[mƖ~կ 0 uRXϤgc`8acxVg````}{뭷&_?}wz~j\?>e``ڀ^, 0 0@8{``naޛ ַ/^[sMs 0 a?\ &x``c;Fguf``5_Nbս>LPRuǓԟ 0 ^ 0i/``cwv]``GM}AG;61 0@Cmg`8acxVg````=_dP/Vo:XZo,K} 0<z~֛7L"```oݛYe``nm G0?w_}ukoo+1Ӈ~xz7O><=}9˗_[5`Jƛc[0 "gr 2 0 c1 0 0 0З-pݻw0v'|S)N/~zr'O"eL5^0y6~o{o0Ǥ%jen1ݻ_װ>h a8^}``2 Z%``8Ϊz;n41ov2 <.B62yo ^s QTwj{g|@a M֫&  0 0Wzծv 0 00ٳɰދ/-6i"7`y5cGamXmns[\[ ۰pi@Xo6Ta=}!g`؇a}xR'````ѰO^UJ5ιS}fGcXAӧj؞YXψm^/\&W:]z~3es3 0 G```hX>XicFc*txE.Vߋ0Ho9^%8Fx&G3-7/1 Y/3u:ܚ͹NτЀIIZ-Ւ` a```(7p޽Ѱg}&\ͦzTt65\S ה穰^s8¥[r>ﭳ[0f%2g>{ bTXo骔9_/A }\kpXYo[l:2 0 \c¼ 0 0 0Pf 9}뭷F__|F;mFV+ ФƳgFWڳuex:"w½Vߌ^MC{a;=޳GXo#8|zkq.~``O+b``nk/dX믿 fi3[ؖ[r~mb }+W}cUT0xn 8Fhz[a󕰞0nwUa`5 '``z6?q2w im{ܖZk4ͅz}Hm5.^gfܾ̚6|.A |\Lvkr>``mf]ua``g? 6֬Qjv5\w(9FRlFXok\zOG^szz thfp# 0 0_zqv 0 0 0u L>|0/!QSǏًڄp2Ϧ5^Y=.=s~ޚH딚:MYv\2hjzokg}CC^οs}Yv\:%Lz|kԝ`8#?`կ~``8nЀߝPsa>BKssx. XxHvDHc>1>R>-І{zW 1С㲉Q?`؋#bU?N//} 0 0@?~V_w>\'>s[^K;Ο \w*A#h17<2ci@$LVSTJcc[ƪq% lsۺssKMyg*kThhEnLԭk.5K9!;iSd`=tNS``ZzԚ)9^XoۮlE[Rcn "aQ ŵp]Ix?%qY!PᘒPss- .9gcMZpx.06ޟ MK\on@8uy#u|m^j#׿k\TX/~>u+ƹB׹9$<DZl%aPzD1 0 ɀ{򪭼2 0 cJqzv LHjL*Wm9Ј:Y%MJ:FjuWM|Ku9K9֋>{N*e!T;J mKglu{=$A-ʭV2K\/ukI?[lkz55BV87=^^iojj 0 0800 0 0Уa=[۶\ZaFS+"jC`>ws+5_\ojl55͵vyrZrαYiPohW.@65{ ei(nK{ڋcohnEñNjasTݹZ6ƶ-Yƶc~ncC&ζտ 0 0z,nŢv 0 0a=4KXoۮn]*47\WP*Os;cmHkJV9 ծbHoo/۱$XwͰ^I=;ygKm SkvלZ켟r[N[S[ic0ՏS!vSώQ8;\uΎ%aPzu``j@Xݽnv`` b۶zK6s;S,B XT(a% AKa0-f*8l[qI0m[;;_J˺9&;wv8'5_G}`X׀޺ɧd```Qƃ޶] >sVK3VCTpj0T|e9OeVV;Ju+qS֩ZoxjԒp?8l[;8Gf}RҗsKMXoj͝kyaɼ_Z_6LF 0 0a=ֲ<,1 0 lɀ-< mխzV-B#նr֥Smgz1f8ga9uO, :]s)k|Xi4W q*XwsWOKӆZԠ&lXҿkRu*xcm8]s>Xӿ޶?Ea=a=:4PPt'h5R#`2 Ȕ?g```mVXoۮnK"2xk[r+,厏{ZBi5A?WM+) F˜εv(gn`(uܖScF;cE^d[\pnje\H4h7o8_md~͝3U.w)3Kn˰T?yg,AZV֫&} 0 0Gzѭ6s 0 00e@X)#s~.mWJvsLgnPE86Ԗ-n-c&SQLR}"anX/Yl sK+퍍2껹[N{`6%\?ޜq>w> mŜ} 1СkM  0 րm 0 0@zm^jlukM.JB\i5ܪs)c˭7ԨU\r!wIϯ_SRKι Zszm!8-yԹ[WjkM]ug``a}|Dzw[1-Bg-F8%zTu[r\X/6Թ[paն{]}ks6/9VV֋WIkj8G+5Y:h.Z-/5d@sZa5\K5eKU,l%'@L'E}```OxݓWm``Ԁ+Vj۾.YoecKʚ`.'sע?- Smdy9KZ6؋KVpjVK% cS[Z[#c[O3k6Œ{y]aNYbZwA\0 02 z_ 0 0@a~⩮r!m$9*]ilesiVKw" [oN?EĵѾ59Rcw?VaV+u:-٦9huV -a~jk]a.iHXOX 4 W`e@Xo[0>ԃ``u ӏ<*fҭw6~+ Ѭ=OhcɊc%F^K.\c^#yksץ]kaĵz@-L9XYoSJ5%juԲޱq@w  0 0@ 0 0G7 g zp 7www?X>r`yr*xS폭,SᏩhS5p\3F ko :'0t1TklԮisV05:uїEjzcݒܠ}?"[CVjj 0 02 Dž`` qµ>\jXYGncs{EqS!k5m0krRX^c-kT`yĥ=Ln֋{j^r E/=X:1x  0 0zN 0 0 ֫/KX)HZ۝Zrhk\& *yOsW[ ծhY~_BX/׆[׶"}_k)uܖڵ}[F0KmOsW{='֨eM-י͵uyO[yKPà$ 0 0 1w0 0 0@ʀ-ansaۓᵜp]ej6 )^Yc0]M\UIbi+c&s>s+`=&k/KUX/o*x76g؎uN(缏kCiS%"?Sl鳲VA-+4h 0 0@;z[} 0 0 ݮ{v/_Wƨ Gm" 3geq2N[4, ש_K2v2U-zq%9JC9;Ms*9VzJ[m^idĽl-;fbXJc.烒kV"΃S[߰_g%cmC%}ᘺYzz thDX7/ 0{5 ^j7 0 0ch1Gv5a1 __[ \Sm3;?۵pZ#QƜ - ʙʭV\#h@T.5lڷ$_cA"$3'.4#=k/}5* 7 }Xj~QSZ.[\`f9> NyŸZQ{~FcͽV}Ep_:۵d^_̕+MF&>9sxg.a=a=:4`>b` q܃c1 0 0a=&Z zExR9algO>nW5Zsl5_vV'@mfr+t=0~枳}p[rz~j=޺5vlSR0eec bz3 05 wj 0 0гa=[㪅ڰ^68'۷4ږx^ڱj[*k``Ѐ v: 0 6 Xkc _c3EO[oSK?M-[KanNX>jjOLny~z7R}d{=Vsk:L\O\q``?   0У_g?{?XGd~b%Y'^a=Z㪅=3(MCn5BKu>}Ub#5۵6 '@=3SxWs5g`[UC=`u zwGwB?߳ǀ^?Ҹ㪅Ga=Z9Saoy>8VςZZg"  _^KW) 0a=¸`z4 u ֫/KXx'a⫿ʅ殮'$W{aONzeX1|>0W7Wk*'@Lt&5⻤(~VQ|a4C{7=%G7AZV3Im"~spST.{F鹿[/f {sRjֶfW2пakl1 рGtgcBXjjoLm;|o?(#olGG~3՟w{sGЀI_|@ <>^cӧYzL]Ӣk  0Уa={tힸf΀^]U_zeS]? _}W|G4ث.ǏZwlv}YO3-0O<,t|n@Xލ?8acxWw87 C9AXjjn\$U+wϊKzz thdd@Z&zxO,>cd8kڐc!  0Уa={tힸf΀^]U_zeS]? _h{ޣ.;5>V: jYYXؤwsaҺc}O◅Xe/^sg[7 a='+74}}ww/mq&5WJXoyr``0 ǂр=vO\3Pg@X*/a~I?1 0m  1СgۉFXoho.W5ߵ|^Ka>j+1 w~]3m _~/ - q•sr 0: jYYDP;8~f 2ɩ l@G[cV\=hi@X/a@XouxjofY`4 '@LLkS*$L?fPKa=T9݋a=VbU;Yezxq,/ i@XϺz quk  0 AZV3MX3.ꫯN߇~O/_I(0Bq['}r~O>䧿o=^Svv K Q[+>߭S_z>/~?88Wk5Ygckc9a=ZrN``ր݇a7؉p^;:z7~E'Oto'SlMk="91wqL]wvp%׎ ]cqT py)ZD{\kxCSm/=˟8{_Hm]amQ׿>oCP,~,:aM~lxnn}}8z愹漏#㾅{a=ZrN``րVA-+j'oẵW֋T@/̚m|#(akrk_#8wLͺ~Mh1e2.59K|jksFK5xqjpg[ȱ};j#z_- D,x.eӟuzޱƱ9j8sy7G5 ~ z\p\1 0@a=a=:4P;8~-bŴSqzsÃѾT={ qڗ}[;.'p%+Մ"pȜ^.6-sV^ CX/jwnER7{*,= QcFXu6ՙ D& M]#~~댅b@pޒ{zr8s ' a=cX㪅+`j juԲv"pvͬ֋0Y*3'68Jmg:mW0֋P՜0vhwo/}T[s)WQ`MX/ں$@x%FJB5u6Ԏ\X4w֋{^s0U8fom[;N``-j[.7l ԱwSuޒ؛;_얄!k?s2h?7g0 1¼W-\9'W 0 0Pk@XOX Nc͐N.(Tg. 5jrm z6űlІWy,4U EDۢWJCbb?Z^|AuyaÂϏUǂzϱ=F?D /v6˭d Q}斵-pkώ>;X;Vof7l,>ZIXZɅr!D-^6Ehv,ZpՄZg8o\X+`5 aPz&ډ5VX/b ɅLBCrA0Td%[ڎJ\.d[8f,(VRK4u[+T{l7{~W2ŗzc+3S`n 1y9 vQKzL`bԖ۽?5fZPt6GsrkV˭MX< -ƆW-\9'W 0 0Pk@XOX NciX/"PTE8Y#s*TXo |\,lQj#W=OdZ@ڜ㦮{딆b56sapp悃SAӟ&W3K<;cya=ZrN``րVA-+j'oL.wkcxo.4_z#뜭3s6VT[^l-c} [ l.=Mlir,76{\o{>u^?sDm^56՘ VǍm;ӵS֫4}nI_;;B%0jzy'rz.U W 0 cCZE.%E(^uOvk]]-'sCPlLԶUjC} d^zǹ շ$7]Kֽ-p;ɎW 8T6HVbZי 땮w~O@cJ;V)my=K,:#㾅{a=ZrN``րVA-+j'o̚a 6w^3޼gom[;N`` ]i׺Ц*tszq\Hn* wn;-#7ՖZ9cea=ZrN``ր݇ZaaԊoKi\\Ѯk.r0Xj\ knp_箪jsQߒ pZc+ Md8CS>'Fm00 Hֶ$P2u댅nAԊvS\ȯ,sٗsb{Հ- q•sr 0: jYYDP;8~f EhB 7f+"ܔ5bȭ֗kK. 6g{k=V\/kI_ $9,~;Tm[jY:K1}ƨM룎ƣ:2U"ﵮM]knX/w^n5Xqon cqn{g`_U/a=bU;Ye`oz+^߃vKִ\?<^q|n {-Њp]AKj5\.^\+ 1N.ek/ۧ[ ўܘu1glےSZ5{?;XXX6՛RRa%ϗ̆k^:[ 7ymE[ q(7 WW\^y_q``vЀIݤy]-~VZ֜Z/;MZ"UʚSR;Naj59?of-uVw&q2nj ǖu\?_-W3L`_ɭ^C{>[u^^OX_{9a=[㪅+`j juԲv"pv, Ex' ] cMj~~^*έ.%-Sa8oI`|`d{EXɭr8xl֋67i|6z}xTGX@j%!wj{sys}z; c@XZni\ q%# 05 '@LLKCaJ_玮֛FXPVMXoܢmKV-qKWBȭ okNr[f`7֋sD|qmY#,Vʪ E56SX5Ī }\_36w’̅8BqT=Sc2R-p̨u>lQGQ``mC{Kvmua=cb1|L1?zƙW{p2 0пa=a=:4`g^ JmE[ޒFXM>RXon˗ꈥ5uXoU#;S5/7~R\6kcyF6j):[5ĶS- Ž}a=cjcJdzGr-ՑW`خAZV3M5zc땶gV鵦kі-r j{ϱFfk$u1Ӛ ajkT{V~yv mƅZ0 UKal5ZGO-{? 0Уa=[㪅+`j  1Сډ}x֋VRڱ5bH<6mlIZq-VXz-KZS\7BL5l-m֬^wkX]S׹ \w{͘u: ~d`liK^Hx[Gzkxu,/G7 g z\p\1 0@AZV3N߮HUJ Ml-Ml_*4vO>MJÏcs ~ka\2V\pyc ]^ng0o 9^mWs\`` +ͽzګ k_(Xoa]X+`5 '@j'oVX/j:WJjjoS57@kKZ%olKV{ne#B~ͭ48>$v^Č| :>wf_͌35c-ĥkun[|Sܜ -=O-x 0 U Wz\p\1 0@AZV3N߮5zJfO{  g嶴O׮WKb%<&uS-n-7'lYW`s }VjS-l6Ǭ1n; a׿kXMnscuƸ9};_s= ػa=[㪅+`j  1Сډ}x֋:/ ڲv5saX6wwww}EP1"ǩ{ s - ~jXjd^.:7ùKz9lTf,:v9VmUo[``Mkrms99+RpݜժSa>a=c{ͱ\<5f\)AsBQa//u*arA]j xJK^.WcUrc=-TmڵèE4sm+#k,XB<@m&g@X  @"A?OUw" t_{+ak_[<he)C~Ƒ ¿W-\9'W 0 0Pk@PàLkf^.HTP.J.;0TIXoi_q\ +7xm:\c#EOɊ8W{r+ѕ`^.( 9FG{ٳCx21ڋ6Ӎꫯ&l\;76.z3aQa=. 0p\:|79TP/Γ ]:[ E9^Yyj~N'wqlW{CX+`5 '@j'o" j{ R;Uq?ߏJ,dkv!Ove*6 + jhoަjFXo*8X5pMmj|u(Ema> Fm a` X-a+.[}Ru]S}>q#LZWX 1py@Xͷf`: jYYτg@^.(R;m[j][/ƂhS]=J{S?yiP$Uz%j  jRMqX ֘y>k)Ϻok=_]om{l9alsV=3 XY΅j9b`Zzz thv"pv-zQ6%[^b%˕R[֦l[Ζn{~xv%c$?ڗPePpI5u_ce5U֋smi[[b+MVX:J;f 9fmYw]``00EkIp/Bd% ]:}qrk?uR}9;ױs4z̷0/U W 0 0ee=ADIVZ6;Ҝտ=sb ֮\V+mSa=k_#Zaql'[3[(+앴ts$|՚"kq~l.Ɯ1M޷Sa~ji\% 5a7U+Ex6vD?uK(k%XXX;ֽcya=ZrN``րGlAxE6\o|Kk\Sf\w%aq0]%ڱ~?Gў诒U,uKqKhDtVkQ:ڛsfk~>Z⽦l9ƎU˔a=. 0"`[?[w~-U.Ƕ^a۱׺7s9z5 v z\p\1 0@AZV3NgfOjz{/m5aj5]6盁v-``ֻ]^X}7`؏a=a=:4`$V֫3Ύga c?v]_``z5 v z\p\1 0@AZV3NgfOxݓk5K[^ cl2pzw; 0 0m ߣ7 0 lˀ0nkUu!nG>|^ y׹a>qcud``cBX+`5˻v"p<3{2 랼^̻\Y/ʵ:%3 w^3 0 0΀^=[a=߽ 0v  1Сv&YXz)ӧO&O<};a} 0 0@ڀblq•sr 0|xV3NgfOxݓk55&bk\5Gno@X50Ԁ`` ߧ8```@XOX larVjek}yrU~[Xg\Ke`2 Ȕ9?j`6Kijbڒa=mya2![Uc@X:̳~f``G-QO`ضa=a=:4`ī>#b7xg/[UccV= 12ea`أa=n[㪅+`j juԲv"p<3{2ɓy@)?Q~X'd@XXsgz3 0 ŀ- q•sr 0|yz&ډW_}uۮܚ^|i,tynM##w}_e`8a=[㪅+`j u宰v"p<3 0 0> nƛ1 0 00n@Xi1F={vz뭷wy9M[XrNs 0 ,1 Na=’I{a`c@Xo?2Ԋ``rz}Uy_gcaG u1R>Fb`::)wc 0 06 > 0 0- qG}4{:޴%4G1 0z~3),```?S+J``(7 WW\ǏGzӟ 0 00π0!Λ~c`cvofY``z8=旿hX/è=Ye`5 aPz&ډ0 04 Ϻo 0 0a=cd1rww7{뭷N}^ߛmO 0 0Ԁ^:L K'g`؇a}xR'`` _O^x!G`CuIK3 0 ʀ{1 0 4 ھ>Ѱ޽{N^{ӵ-9`Ca 1 00 :O 0 0@ak"wѰ/wƆ`h@Xz&-N6% 0 0aS} 0 0 ݾ=>lr &{v/ 0 0]zz thIWmԆ`4 Ӛ'``cq-wwwL{a=ߗΔ`\dS˙' 0 0wz ݰ3 0 02 ZsӧO'z/}g 0 \.ևjMXo8G 0 0=c``KzL1/jyݛ 6k\9e`(1 a2TX/`ؿa8TC``^7 g\,^:Ŋyo+ȍc^e`(5 '@J'yX0 06  0 0@ڀtl|A}z']) 0 0pUW_˙' 0 0wz ݰ3 0 02 ŒO?- =zHP+g`n\{~9+' 0 0 1w0 0 0kϞ=+ Ūz/^``\_bkl0 0 À1l<3 0 0 XYZ^:}A&``` z&DLkL 0 0}zۯqF 0 0 ֫#;NnqP/pߑ̽c 0 0  1С&U}``x7``ZX+vwwwo{w^qP12 0 0 aPz&sr 0 lπjb  0 0 -~g޽[ůxBPF{̉ 0 e@Xz} Rz2 0 a~```Gz\_U|P}^ߋ8's  0}C^߃֤ 0 0a= 0 0 h@X︮ӳgN]ӧOO?>ūVڋ"XqO+j 0 ׀0)wRV;c`zxq,/ 0 0{1 w<{P^*o|P```36ӐvZYx6r 0 o@Xo>T2 0 0p{z5AbTnŊ|/} 0 ls=_KXXlٽ 0 8bXɓ'/_z``:6OO_W?{キzP?>z_.`؜5/][އC``=8RX__K0 0 0pzwY-ѣӋ/|);p```6۰=|16 ׭.``8RX/\N>```]K_z<8}w޿;k} 0@z~hk3XLB```kZk9``Xnӧzly! 04 Wam60 0 m@XM9S 0 0 0pmwwwؾ*z{gϞ^```_:/AhT/``ؙka``- {?=~oS|_ZA5P[̋ 0 0p z~3y\cp ``ֻ} 5``````Rzz thtp 0 0oz~ 0 0 0 0 0 ˀVA-+k՛`8a޸W{`````g@XOX 71 0zq=0 0 0 0 0 0pZwd 0 06 ܵ͹s 0 0 0 0 0 7 '@L'E}```OxݓWm`````: jYYv 0 0pzźd`````zz th=x2 0#} 0 0 0 0 0[1 aPz&L0" 0 0ր^W2 0 0 0 0 0sy0 05 7j 0 0 0 0 0: jYY@73 0 À1l<3 0 0 0 0 0}cC&>&huTG`2 Ȕ?g`````0ee 0Z0 0 4 WK_ 0 0 0 0 0cC&u'J?```ܪMb````` juԲd 0 0p zǨ 0 0 0 0 0 a@XOX Q```ʀ#SF`````c@Pà3Lvj 0 0Ҁ_-}97_ 0 0 0 0 0 k@XOX (ם(d`cs6M`````^7 aPz&; 0 01 Ƴ:3 0 0 0 0 0Їa=a=:4`cVGud`)zLsF`````؎AZV3٩ 0 0@Kz||1 0 0 0 0 0a=a=:4`\wԟ`j@Xͭ.6`````x݀VA-+Lv 0 00 w: 0 0 0 0 0@Ѐ ZՑ` 12ea``````;: jYYo;d 0 0- җs 0 0 0 0 0Ѐr݉RO`تa=6jSd`````uZg31 0 À1l<3 0 0 0 0 0}cC&>&huTG`2 Ȕ?g`````0ee 0Z0 0 4 WK_ 0 0 0 0 0cC&u'J?```ܪMb````` juԲd 0 0p zǨ 0 0 0 0 0 a@XOX Q```ʀ#SF`````c@Pà3Lvj 0 0Ҁ_-}97_ 0 0 0 0 0 k@XOX (ם(d`cs6M`````^7 aPz&; 0 01 Ƴ:3 0 0 0 0 0Їa=a=:4`cVGud`)zLsF`````؎AZV3٩ 0 0@Kz||1 0 0 0 0 0a=a=:4`\wԟ`j@Xͭ.6`````x݀VA-+Lv 0 00 w: 0 0 0 0 0@Ѐ ZՑ` 12ea``````;: jYYo;d 0 0- җs 0 0 0 0 0Ѐr݉RO`تa=6jSd`````uZg31 0 À1l<3 0 0 0 0 0}cC&>&huTG`2 Ȕ?g`````0ee 0Z0 0 4 WK_ 0 0 0 0 0cC&u'J?```ܪMb````` juԲd 0 0p zǨ 0 0 0 0 0 a@XOX Q```ʀ#SF`````c@Pà3Lvj 0 0Ҁ_-}97_ 0 0 0 0 0 k@XOX (ם(d`cs6M`````^7 aPz&; 0 01 Ƴ:3 0 0 zŋo[ 0 0@_:/ 5' 0 0@΀```{wNok?~|裏N_|)B}=ܳ{0v``ccC&cN 0 πjn9 0 0 fo9ݻw/K~xp?L3 0пAZV՘`` -//ϟ{```ᄈzZa=> ]_x9&```:*gbĤ 3 0e??x /} 0 0 _N{ew, ὏?J{~wyLՙ`ƀ^ZL5cya`kHa^ 6H-}vX```?2cemwڽ;}t{;v:Ԕ7X`l@Xz&=OJ/ 0 0Pna/\:=ָ/J_1 0 DRX8֟>}ZXoFo} 0 lt_2[ߣq~mmYb`m@Xo1~ԇ``y^zѣ{ޗ_~) z߇3 02[XXn͟ 0 \πk} 0 0 ]:Ox\``Ҁ^Qa=d 0 0p zǨ 0 0G3 wlދ/~z}gNjB|}^߉mntǞ_`~ uTXjUK``1z|#``р)ױ~zgbNj{2^``}cC&}M^ 0 00׀;sx; 0 0[6 nVh/V: 0 0ҀVA-+4ZN 0 0z۩q 0 0 g@Xo_}zwVڋu{g 1 05 '@L۝tFm```Mz<ɹxb`؊a=k->}8wSW{ s 0 0AZV39198G 0 0 mFƑ1 0 0Po@X8;矟ݻW{葰^ߑ`؃a? |0h# 0 0rzC} 0 0 m&{'-n^Ϟ={ҽXN 05 Pah3 02 wz 0 0pz/?q2+ wjGl1 03 '@LTg`k\7``kcm kd`޽{^`we/e`fjZCsrfe~I?1 0 ݀{7 3 0 q[ViVXϗ3g`fjZCsr&WO<'``cxa``a=Z%;z9 0 0p5WP_bkL0 0 À1l<3 0 0 XY5?zhru/{9 0 0pWȚk4a>H1 0 `@X``. 1_N>Sa=_ޜ`\d5 tq``z0 q 0 0z b^?``EZv'Zc``QgY``fz̯m?\]N`;g`h~?L;/hz}đ>b`=8v3 0 XYɰ^lۺ: 0 0^Pa= 0 0p zǨ 0 0G3`e=6իӽ{F{~^ߛmO 0 0Ԁ^:L K'g`؇a}xR'`` _e_r4Sa75>Ƈ~O 0 \π^:7LV``?``a=ZzFz^ߛ(`Xb@Xz&% 0 ǀ~je\ 0 0裏Fz^ߛ#cD_+`c@Xz<&) 0 0pkz ڠ3 0 0€W-\ga+zw:޴%4G1 0z~3),```?S+J``(7 WW\ճgτ:^(J_1 0 z~(2ɩ 0 0@kz61 0 €w-}z~/Šs``a? 4ZN 0 0z۩q 0 0 g@Xo})Ǖ 0 l r 0 0@{zc} 0 0 ]Ϗ\X#8w3 0}ZgrT#``5 q#```klaRX+`5 '@j'{x0 04 Ϻo 0 0a=c㪅+`j juԲv"p<3 0 0> nƛ1 0 0 a9`x݀0y1 00 w:ܳ'O~_x`8+¿W-\9'W 0 0Pk@Pv"p<3 0 0> nƛ1wo7+N' 0 ۀޱj qʖ 05@$X ```[;NOzƂ 0@ʀ-a=ZrN``րVA-+j'3 0 Ӏ>f7 g< 0 1py@XkYs```̀0``c;Fguـ=vo|3 XYo~q;a=``` : jYY䲅E8d`kckۿ_`c]a=ZrN``ր 0 Ӏ>f7 g< 0 qbn㪅+`j juԲv"p<3 0 0> nƛ1 ǀy`qzH1"U W 0 cC=<``}g݌7uc@X 0z \ֻ~s`` juԲd 0 0p zǨ= .=vo|3 XYo~q;a=``` &mc`kckۿ_`c]a=ZrN``րVA-+j'3 0 Ӏ>f7 g< 0 qbn㪅+`j  1Сډ 0 0> nƛ1 ǀy`qzH1"U W 0 0ee=ADxf``}g݌7uc`yX﫯: [㿿q;/_\?j}^k5]uk|g{r͑~}/Ϻm} qu( 00 wHm5Ȅ1x[q^~``O+b mt=y_ow>lONovі q?Υ\|4twww{NG_zΕhLJ~8zUBkhe 鋱]3fǡ}\opY:;ssWmm ݦ{.U 0 À0cV'ub`Zj^Raq\ֺk_ksm^ӧOv_T~2R}9]kX3@*4tr[_FixEi,$U  F`*Wwl;֪Ya/֋kj`Sn6^Ƕ6܆|sqբݗu) ׌z kF;oRέ *˅jt9̗l<8e0LƽosտK Oz\p\1 0@a=a=:4P;8Ã`اa}xS7ndvS[l9*3pcb Qcra՜\&8~R_BnV06x }?)Wa.jqTwrZ6>ei@Xouxja`0 aPz} N:2 0 Lcdʈ3uSaMȆ{̝d8G* 8ݏyc[5[6'Ҁ_-| q•sr 0 coy\?``z\k $ՆVC+ iV+y累\["`U^mih_*TΑYr@Ym0.ږJ7 iOl;gTtZϹ~ /٠_k\ U W 0 cC=<``}g݌7ucƶ-]Q?s+nMrḱw;gIn,77Mwm+նT]? f`?S=+a=U[ye`_Z5- 0 0a= @*j=sQ7 ~rzzCX+`؂a=a=:4E<``1Zr|{V+ jEl*P"4]#0d[ג;&u!dן @\-HSu kA̗5k;?ǨAKz|%U W 0 0ee=ADxf``}g݌7ucZrA\0v%m-F.[su\ݝR>_1j-) qʍ^YosK4gaj;ޚXknk@X߫a=zf``_Ѐx_z 0 5 \;V 륶i-zϩPTz-LKWKZ*%&S!\]rԖsnjc㼏TS~J 鷩\ce`a}}(Ǖq 0 lVA-+\0h 0 0@{zc}@[-I[nhùTljVahW.ٳw.pupT7%efkn^9j 8]a-mt4 Ӛs q•sr 0@Dx```Y7MA="T+ x؊rE+pxEil{kwyo*V3εt'υ*B% LOɽzkS۟|g`]z'ٟz\ 0 0 juԲe 6p 0 7 ׾9 5"mpVj^7hKZP#Wڛ IYmesШu鱗Ʌr[SSm;_a=6%T S 0 1 '@L! 0 πjf?7"T m Fr*PTX/1mW{h3ka8mIR[u*8K/eJX3qc;F=յ͹s 0 02 aPz ``QgY{6"rǯk. C׹z{6ՏqݱۺwlԖj16RmH7S!qSB[X.Wj#_M0NڂCm`6s=׎ 0 ˀ޾e|hH \V+ "5 V;eƟ"5{ V{j-tjo=/7[qjɖܞ l߀kq$j3 0 g@XOX TM`` e@X s{7"Zll5ܪ_O<{ k{k+ ˗/F_jX]bl9y8s5kұ.`@Xj9b`ZZ ѵïqLp1 07 wj20pTj[M -on5iK6˵ߛmC.?ܬk]>X.Rc]6W?w zܵp'U W 0 cC=<``}g݌7ucr۔FgN,w`Q*H!%[qqΒu[R}3bj}nk"xRtK}nvNy,}룎[z\mͤ0 01 juԲ1I\``;^͍s5@.\^n ӱ-pDцX!s":gs ŒSựX?]˭(SAֹ!\g4Z>#f@Xj9b`Zzz thv"p 0 0Ozn L7ធbTfu\p08ٳl[J%|=x=_"W}a}hw k~:ORBSG9֋ws9/=U>aI-m ݦ{.U 0 ÀVA-+c$Չ```a=~nm`lem9c\[#@ W77h[٦čQj;6+aXr>'{A:Dߔr+-n{o#_r~Qhk@is& 5{盳uR9@;zn:? 01 '@LۙdB-``a=Zrna26bÊonʅmtkל W1֖ڀS^4=ޜ[yhoC7-]0p׈kE`vXojšku[sTmcƛ^MwI0vEjƄc=Khc@XMݫWG```Ѐ v: 0 6 XkcXkϞ=;E)Izi28v+hpZkXUßq\۰]_ՒFEІ*lsW,7ccjq%ׯAXAܗ}c#T0Z7W!}Ycͩx62pzyh/ 0 lӀVA-+msՅ```mzLmbOZ EiiVqڧSuUW87 C9AX+`5 '@j'{x0 04 Ϻo6ڶ6vK!RC b~㪅+`j juԲv"p<3 0 0> nƛ1 Bu[a=~`@Xkz]ӛk 09zz thog`a@Xu6ՙHn^|K 0Џa~jq)Ֆ}mE۳7Ϙ`5 omo9n?`Zg``a@Xu6ՙoO-k#`E.'|S rsߪzNsF`Ԁ+VjƋcya`V@ y=``mV=`-xI2d7"l]BzEom' 0/z^ƗW{2 0зAZV{К՗```0 ǂ1ގ,|Ϟ=kl9\kcjmSq>a=ZrN``ր^&g"  0 0Ozn 0pK! u4Hz j9b`Zzcv"p<3 0 0> nƛ1 <][Fӧz\jz,j9b`ZzÚv"p<3 0 0> nƛ1 |WxE?O>_|)#nѢ6#` qb.㪅+`j ulz&ډ0 04 Ϻo 0 0a=c㪅+`j  1Сډ 0 0> nƛ1 0 0 a9`x݀VA-+Lv 0 00 w: 0 0p4Vcya=ZrN``ր 0 Ӏ>f 0 0cDX+`5 aPz&ډ0 04 Ϻo 0 0ןs``uzz thd 0 À1l<3 0 0 XYj9b`ZZg"  0 0Ozn 0 0 3FZa=ZrN``ր 0 Ӏ>f 0 0z \ֻ~s`` juԲd 0 0p zǨ 0 0G3`e=[㪅+`j  1Сډ 0 0> nƛ1 0 00n@Xi1Fj9b`ZZg"  0 0Ozn 0 0 1py@X}ι>g`^7 '@Lvx 0 01 Ƴ:3 0 ̀oa^X+`5 aPz&ډ0 04 Ϻo 0 0a=c㪅+`j  1Сډ 0 0> nƛ1 0 0 a9`x݀VA-+Lv 0 00 w: 0 0p4Vcya=ZrN``ր 0 Ӏ>f 0 0cDX+`5 aPz&ډ0 04 Ϻo 0 0ןs``uzz thd 0 À1l<3 0 0 XYj9b`ZZg"  0 0Ozn 0 0 3FZa=ZrN``ր 0 Ӏ>f 0 0z \ֻ~s`` juԲd 0 0p zǨ 0 0G3`e=[㪅+`j  1Сډ 0 0> nƛ1 0 00n@Xi1Fj9b`ZZg"  0 0Ozn 0 0 1py@X}ι>g`^7 '@Lvx 0 01 Ƴ:3 0 ̀oa^X+`5 aPz&ډ0 04 Ϻo 0 0a=c㪅+`j  1Сډ 0 0> 1ϟ{``:67~_[K[tM\2 0p<ZwlVs``֋/ 0 00 wu[Ol9/[ 0 0Pc@XOX L``kHapK0 0 0п߬;վ+c` NXY$w0 0 0Pfha=.\' 0 0 n@Xϸ0.o}/~W{t?- h@XGɪ=q 0 n@Xϸ0.``` Lo=_-L8a?t o ՜`8acxWw```_~yB[>g @wֳmja w5 '@j'{x0 04 Ϻo 0 0 \_=~,j *zaXmW_b,+ۼiܓ=z&R+c`طa}S?```z_|/7 Ƈ~xz7zŪb+_|n._æ۟QxگW{}}lOCX+z&=MB+ 0 00߀N1 0 03;6."x>/J}jZ:[jL<8w8y6V@[SFsm26^& d`[?```X~_?~,ZVN ],BB|.ß |Ga&Nح 2>1>թN"ײgSY-s=#jz£<^{<f HZrN``^M5a``k/ }MC/|V劐Oװf} 5>zmR# [nss I9z,q a=a=:4`Vc5f`0 ǁ```WN z7, Elř Rֈ 땏ɶ* )uK1r_7}D=8m'l k6Ik/u me=H+[ 0 0-z۪ 0 0 0 {}7#75vՒj8*LǦM(zqӺraFήyai[7>;FlUX4[361 00 : 0 0 g&z=j~au3cwE@h{g?c( ϗa]6Vv ×ί)'@n= 0 0pzg3 0 0 0Џ~2[o˫`O[Vj0a~jZZc^ȭw{p^OX[[0ee=˭'g`a3``?{wN^ػwć~Wyn\*]nkR+zZ%KQaרq$a=Kc`OzƂ 0 0 0Po/޽{g9ӧ77fX/Η  ّƺgo.'@n= 0 0pzg3 0 0 0ПvSO?T` ߥQϟbخ3Kɓ'h˗/Wik{%;Ç*k^/ϽF:Cs혣-56,5ڞ E8w9֋XYSrSǔ8*jvަ=SYhgjE[ky~k1[QK >לoJ>O{m/k=Ok_P .Yиچ\!```Q'I```Oߟ E/\%AA.8q@R>bPIj%غ%[^^'}ޮKSC;W'lZ%kVJ5JLwjϭd{NZ!ƒyi{q-1Z҇Sc5d|ݭ 7+#3w[sĘ-zD^oa u_9?16Yi_ m KSF{zc[;FXOX lmў#  0}룎ƣ:2 0 01g"ǪS:Eh X#82v[ޜՑ.4_Rh0JsJKϞ= q\)ug8Z ]mpݚұyy@=\ E✥hvSө;Ά6S2WQK̍\8tDdnb,qx, (uKs0eev(bz2 0 a~```Xf+}W^\ w\;$5Pi#6 WmC'"j}6S+Zؗ ͕q0+ \PԸXkr.o=ŵkCrca/y\m,̤¥S|ҹ&ּ,uZ3gOi$WӶIi%ϓ%s+'@0h/H1 0Rz -5 1 0 0 7Nq`ŋ{ _ ME `-"ssA #4xmYRV*]H:BVKV+Lduϰ ЯSۇ\ss.WYJ3Fh+ޒiǚa\׆" 9h󹧩mHk ˅J`'S㩠/q0oM-52V8I͝-z[KBrsVb, åj1՞1$cu,\3ϙ϶A$o]\+]A``:C6͖'7C▊xa~)rM``00 0 0 k vݛVw9=~w@Fi`/Z Ƥ7(h SU@sqKBn}5w9HJ0ca4uAۖ oa xnkq_s $gi rlGcZl =el>Lcn9~Ű^.X2yl>j<ɅeBۗͅB>K<]oéՆZrm#؞ږwyco) &uq^n{ީj}.t[,Yۢm`߀^56՘``o;{իWz+}G;"hrctkl*"4wnŶ3sͅ9J温Uki`dnX(Nlk.S[kkeX/7~V̭wMyl^^.VX/է2z^\xvvgZa>NMm~if]K``€``` |{ԋ_lcjb]JVF jJ ~%/y*lz6Dvަ\+k*z~\ HqvUU26K{j8wn̔_lZҩ:;'DZfK\oa=a=:4IF[SS` `>```X@,[.}ݿ^oOmukz]yr@ڵz ќGJ5 cck/aTMyeIX/ږۂyjE[ŸѮAZ-0"H BqlJ{{MBX:nlIySo.[yRW{:NPà}V`` _cX```裏f{g}vnk#r\ u|]$|ᛸ~., '-ٲ00kٯSATIZZs-iҰ^\;uurydVA5V֫ @wm-!ԒPtSz=J-M͹WInK'S}ǟ b+0zU W ``J qR10 0 0"hWރNbt^`n5<ڑ 4j5k+jWK,L3"UYܰ˚m:Wjة-ιk䮙4\+Veˍ=wc[]z-1'BCmJ{esv ZmE.`Z%1UU,[[_XOX jBqm2 0@q 0 0 0@{/^ ݻwg ԯ}.82Ʌ>֬qer!C[2^*dZd[Q" O1>JE[jeXoΖ٥}mᴩs-לn<^K-^5\UpdsAZVֻ=~m`ؿa8TC```b?$Pյ68QiYk+\pt:׭rA-b[]{1Z+VT+]e8[[.{OJb5 8ͩkZbA "5?5t$-eT{`7 c5f``x闿eqP/UvXokCb Wj_k;( 2մ$ƖHɪY5RrzK%}䘵BaPbilv,VA6cۄGcc07 V۷%^n.u=ܘ)y.\+[a-Z_>qh?c 0 06 mƃ:0 0 04_[Xy/U}9"W][+L* Rem%\^njy_OK[je9.%S\'XV[Kb5$^d6k=Ora!htq;g$ZTԉ`> YWU]```b{MofKq5k_>N8VJ"|Qz͵ub"s,VZ?l9 F- هkrp\UlSc5晒a꼩Mrmjb_Sj^풱wḬ`9P2לöx.AZV/[ 0 0Џa~ji\% 0 0 0p]dP/V/:~*Q/0vU*J"ZW.vƖzJ_%iyrչޜ^nq 'ǹ9-s="uiX/77͒UR}[%YyIM}sCsm}zz th`6]f`QgY```u Jy%ju>?srh c!ke;ŽcvsAȩ9ֻfa܊öcZJ[i歒sVt W!rsԼ!XXnjҰ^.\QnE9+ ֞;j}mxAZV[-`mSk``]_```y}ɰޣGC1M&J*CGP$2) JBes\{v@X{K\$M%ԸsFF[KjX/7S[bY;p[լ4abN`,A8ojc+ )cAy+{Y[-ejzS|wVa9!rGD{K8&|I.{b^5-cC-& ~o 0 0Ҁ_-}97_ 0 0 0У/AXu믿. 8G׾Cc"7!E8`*43"TIbL+q?8oגRM8vኚd c׉*Y+~Tծ`հ^j%Vm?7P5v\Z.@ßT0*pyَ Լ3g}ޞc瘚k6=v! 2ױZ]t qQ省9o^n%h_Y?U۸X&/۵zcas_5Y{xVA-+y6r 0 ,7 9ԇ 0 0 0 |駓a_PߡVʅGj$s>΃(?e+ҶOͥ`XDZ-Y ma}՚5~"۾4X4%V3UT|'JWK4ן 2wֻ\ydKFXol!,~G%˅Ԣ亷K5!w#w_*ub`g@Xo{51NԄ``ضǏݻwu, Ԅ􆕑1{S!Ԭt핐JS+IqIoVPm1 WIɱ-zqݔҭlSusy|I3hֶ%m[2ZsW裢p_#P1)5EXolƸ,(sKi= M%۠>V*}96VcmS6Fحڗ+ T- 9v{W]'KMy JØp juԲyk; 0 0@q 0 0 0>ɰޗ_~)wOn9lW24lAeh$AjkʸvM0?S[bz/CjۼjkL's/sJWx- Kg%A8T,k犩鏘wf\srm.Q9Ϙ>y[)w+NV?ֻ[urNub`Xj@X!``8X5olUիW̓QG؊1WR'+˵%'k%*݂rν3\מqVjpKIK<,omX/5P氵\Oy:v5};8XzD{91 YckZ<FY1:{?FXOX }b~8 0 +' 0 0 0@/9{|oSH6gQ϶=jzt:}je/k^woeO8{t͜0 07 j 0 0 \Gz}hj VJmƪ'{]W_Fulٷ4zw; 0 0 1wms 0 0 hP//|,sH:%Ȳgj/{jwl #>֦k܀k> y0 0 00 w: 0 0 ,7e2 _& jR!O{vNXosg~-cE_uֻM``m@Xks=````"獽;_(,0W}̹ ˷~k#w1l=Ը~;D8QXσd 0 0p zǨ 0 0 0rdXիWPY`X>6ÇYkZg4\?7;čLv>0 00 w: 0 0 ,70{/}{DH>s -.lWƄ-p5n1n3w~]3 0 \j^``z1// ; /\My9M [Y/B,y̱ga¸}-"t]b5QIקFfPݦy 0 060Rq@``cz|'8wznz̥ |W_[ϸ?os vީ߳bOz}:&a=>F`` ?魷 0 0 0Ѝ~J})ǀ7Xπz}2ǩ`&v.HV+ 0 0 0 0 0$sSkr>8'Oq-x\G bezGc````دanNX-6>`8َ[X8# 0 0 0 0 0 z\]˚ 0cN믦&r5e````8a=[X㪅+`j  1Сډ 0 0 0 0 0[1 b z\p\1 0@AZV3Ng``````+XlaQX+`5 '@j'{x0 0 0 0 0 lŀ-, q•sr 0: jYYDP;8`````cEa=ZrN``ր 0 0 0 0 0z,(U W 0 0ee=ADxf`````b@Xj9b`Zzz thv"p 0 0 0 0 0V ¢W-\9'W 0 0Pk@PàLa````؊a=[X㪅+`j  1Сډ 0 0 0 0 0[1 b z\p\1 0@AZV3Ng``````+XlaQX+`5 '@j'{x0 0 0 0 0 lŀ-, q•sr 0: jYYDP;8`````cEa=ZrN``ր 0 0 0 0 0z,(U W 0 0ee=ADxf`````b@Xj9b`Zzz thv"p 0 0 0 0 0V ¢W-\9'W 0 0Pk@PàLa````؊a=[X㪅+`j  1Сډ 0 0 0 0 0[1 b z\p\1 0@AZV3Ng``````+XlaQX+`5 '@j'{x0 0 0 0 0 lŀ-, q•sr 0: jYYDP;8`````cEa=ZrN``ր 0 0 0 0 0z,(U W 0 0ee=ADxf`````b@Xj9b`Zzz thv"p 0 0 0 0 0V ¢W-\9'W 0 0Pk@PàLa````؊a=[X㪅+`j  1Сډ 0 0 0 0 0[1 b z\p\1 0@AZV3Ng``````+XlaQX+`5 '@j'{x0 0 0 0 0 lŀ-, q•sr 0: jYYDP;8`````cEa=ZrN``ր 0 0 0 0 0z,(U W 0 0ee=ADxf`````b@Xj9b`Zzz thv"p 0 0 0 0 0V ¢W-\9'W 0 0Pk@PàLa````؊a=[X㪅+`j  1Сډ 0ƱmmP23 ##@P# #QDFF ":S_zwOj^````ŀXńEW W`Z C-OsTa````ŀXńEW W`zb=ֻx0 0 0 0 0 b@b¢X+{r 0UB'90 0 0 0 0 b@b¢X+{r 0Ub= T]<`````F1 c1aQU•=b`VP˓Ճzf`````F1 c1aQU•=b`X. 0 0 0 0 0뱘(*ʞ\1 0@ՀPazA`=3 0 0 0 0 0뱘(*ʞ\1 0@ՀXO@CՃz`````Q XLXqpeO``j@0d=A `````Q XLXqpeO``j@'cA` 0 0 0 0 0(z,&,J'W 0 0P5 jjyzX  0 0 0 0 0(z,&,J'W 0 0P5 1@ Ń``````b=z\%\ٓ+`j5 'c93\``Xм̋````ǀXoעb3Oܫ+{f2 0q/C,Cd`````gb=&>'cf#ss޽s`` n 0mA=?#̈`````1 |]ӧ'C͍Xy``8{v}2 0 0 0 0 Xx2֛7?}ok?``~6 kX 0 0 0 0 0 K===}2[BLvd?g 0 ,a@X``````a@ݒvF钎\b` ~9 :  0 0 0 0 06 cn)sٓO՛ߋ3]ʑ}I 0 0^b=Ēxb````z-m~bVPҥ y 0K5)sP,}P؏)`````?e@ֶtrrl7W[ك[``] z 44˛ ````` \'rz_ 0 1 -rz͉'sb``````<b^C{\^^>?{h?`8lbeXߔU 0 0 0 0 01;w V(kxw`H5*sp3 0 0 0 0 02 c륶nnnbyK( 0 0K z 44s1a````ط/18?bZ{ 0 0^ 勾/f2+`````2 0~Ho͛i݊ܜw`؋|?'K3 0 0 0 0 fv?W3 0{3/\mvWf 0 0 0 0 0Xzc#?nnnjsOnğ< 0 0p<z KQ^k`````nzL/_LWWW/fS?~5/8`o@CX5f````j@wzwqz.O礟i`׀XO@C=v^;````8vb{Mo߾-Y ;==tgy`6 jjyo:ׇ`````_cNsT\xWW4Gkf` z 440sqd````qx޼ÃP=0A`` dW^M3`````8D?[7o,띝M! 01 ka۷oׯzbE`````8HwUX?^"֛0Gf`?a; 0 0 0 0 0 0 0{1Ç?Yo.//v+w/~E D h 0d\````````/V[{1{L1U< 0|!``````f`~2=ao^O6tww'toN fj 0hh`````````03+`````````5 1 0 0 0 0 0 0 0 0 06`C 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0ӞIDAT 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 06`+b 0 0 0 0 0 0 0 0 0b= 0 0 0 0 0 0 0 0 0a"V 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 067kKu`IIENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/internals/structure.vsd0000644000076500000240000030300014601576577017741 0ustar00twstaffࡱ> Root EntryRoot EntryFP7pVisioDocument pSummaryInformation( DocumentSummaryInformation8x  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~VisioInformation" ՜.+,D՜.+,HP X`hp x 8 Zeichenblatt-1SZeichenblatt-2SByte oder VariableK Rechteck Va Umgekehrte geschweifte KlammerDesigneffekte.1Dynamischer Verbinder KTextanmerkungrb ZeichenbltterMaster-Shapes0|_PID_LINKBASE_VPID_ALTERNATENAMES _TemplateIDATC010498411031Oh+'08@P\l mabemabeMicrosoft Visio@`7pVisio (TM) Drawing pR@ovRm:mAw:m:mAw:m0A<u8 !fffMMM333Iya͋V׻ę5@@@FFFG}PFٖ֠~dյ91UJ:DT5I[1hXT@. .Ub##/am0U\?dK[&/~&b%2~ ! oLh $c)P?#~)~,,J & "& ,i 4&?C"/.?L?\.?o A  },,,'p/%O6$X6Q (}j?k)?"   *I3#oC=S?#A:;A;dUdU@;*dUdUdUAOpNOD`;dUdUdUdUdUdUdUzUbS)R~;4OL%=Qip;RRRJggRph=Qj .@]E' %p3Ԣ~|bFp#| | | ~ui??p9[!i fTd',50fZOlO~C.ߐE#PQc:  A ,U345&6&9Eaaj6 8d7?!%3 URg L-ak@9$KtiQHeuDDT(!cp;/paM-uh\`(+AT %5+H>>5a%h34ovJuX0Hp(!MmcP=?`(z?84?D0pA D7!U/i 4?Q`_1_JD;a3(/@}_L/^/L%a$G[aY %//// ??.??R?d?v???V[l_ru@+y_w-uaDd/r*&oo*o_Qv4DOOO\U$/@__o/DA/Gj:d2(joG2O5j7@oo ooo?&s2;IO[Odd 6 S( $b4@SyYkO(Or&̗T4EDP7H+&g-.UEd \Uˏݏ____"LwU2 r?߱GXDeTePNkcc#ER¿{%ܶ/o Mv{&l|$6&w-!a!!!1Lpqq;PasqKwHVNT`F[ȓ !z䥶1 @Rdv%Iy -bbbbbbIbybb쩓DMv4,}J(̽~ .?Z//puR/MrGK9&/@:?LyǾٹ  /r p/9cߓAdUe?w?O2{OOOW _u// ??S?_,_?2Y?????oO@$F=OK]sOOfOOO"e 3C _#5@_R_d^uc,aYo-o@CgG$t$#hoIlZFoBQ`Ѡ1`ConectrWigtz70aϧAQ` Ed`cl&K`Pten´99K@q^Wc8Ii߹l‰ݣA͝>†p&U5Ruig;(X3a ŀ&U=HTaWsprnyC™ͺIJrBgt24`.SQȕ0xPt4"H$ @Y,b'@ @ A-?E7 ;Ut4"H$ @Y,b'@ [: C-pG*7 At4"H$ @Y,b'@ T7A-$p7 ;t4"H$ @Y,b'@ TA-$w7 ;U!U"#$%t4"H$ @Y,b'@ T4[iC-dp*7 A&'t4"H$ @Y,6@ ,AA>-`7"AU(+,t4"H$ @Y,6@ <\4C-pH*7 AU/0U1234U5678U9:;?@UABCDUEFGHUIJKLUMNOPUQRSTUUVWXUYZ[\U]^_`Uabcdt47"H$ @Y,b'@ `C-pV AUvefgUhijkUlmnoUpqrsUtuwxUyz{|U}~t4"H$ @Y,b'@ [_C-p At4"H$ @Y,b'@ lTiA-pw7 ;z23456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd*t4^"H$ @Y,b'@ `6C-q_ A;X4@` MR@`QNR@dkPMR@kPMR@d`NR@`IR@D`RNR@DlP`PR@lPPR@$mPMR@mPQRH<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(4E`{RE<`REkPRE lPRE`RE`RERE|lPRElPRE\mPREmPRUR/0 e UFD# Th(/TYTYBBUF~?x<F BP(?4P?+ O @HZUBH??!t 07{B`8Byte,BiC ,NK bU lE"KY"i;nG DaC"h!TA pG SoUfC wm rE"S{ d!cPe ,>bjE kC  e^| 7  3u ,7=OasNDas Shp}eaufudZicenbl]t) ze.HD   3 B>Tp[/Lp?as&;.h> ,b MJ=UF~?FV*JR?F\.?FM&d2n?Q6 U AMA333u` ? !+5ChMW]u$g>5 L@X!$5 /`Vis_81.chm!#4E"67M `_!b-"#"'?9h:AiA 9 $''2ǥqE0&!$E0&!qP \@6%A]Dc]3V:?Ka!Db1OCK%YEiO{OL@ODJv%YKOO"NBSB53^@M^U_g_y_Rg@14ot'2TALd"2(Ech,@!3qlf1U1 m3 =M!l+TM9M]a6 `($T 6D UF~@xTiC;'h>4bP9 EUF~?L&d2?F\.?Q6 U AM| ;;u` ?*#)3=KU_euo>5 L@X!#$5 /`Vis_Sba.chm!#57_ 2#`$g!-*#*'?9  %&/7 aJ=?Iy+#&Z&P?B#2N贁wNk?@J'q ,A MVq6q4!*#ekab1; ?Eq6 I-BCb:@e2C1%>> OO1EA5~%5]NGxOC9AOI%\O`JOOrn_gPo|'TAFd" (?cp,@&!iA%9 -$b]'/2q`&!gE0f*laUmHE?KlT(]9^]-Ma6 `r (QJE-Os~%_s%/\h}Mf!>zbcrA bb HUr ~L)uE/>F[*#DJB U6bbt'`xo@<QzoCPUFDfP h VTB UYuU??Fxha T,aaUQJ\UF BP(?@?F~?$-?P nL#l]dV^ ] $g]Q"U"Y*"]>"~#<?&#A&Br&C"u` ?PYu"bl""& M&u"u&u"u"u"u" ,u" ,u"4,u"H,66t#\"u"///$u)ar;21'u `Fxu21yB}BAABA"!^hO$,@ZLupO耑 @Ca4R#qt{u*gvXWcZufvvWuTj*uX{u ]ʤ *aGIJKMNOIQ"R$QTܝQVWXYC3333.3>3N3^3n3~33333333@33Vf4b@Vqf0φЬ uZ1[]^s@333'373Gc ƢEt&EAxÎ;BA}@c"3"3."c Ӣp x/3,S/e ~/iRfSIA62ab ZaLxVIǪ *:Jzךת'OgEa@braĠqAQJ`FWO@r ЀiC}F@`I@)@AEBsZeȓ3U ,puӐe(p 4>{ScNV:fcfR:bWKqP?@pA3VUbQw Sio@(h#FtϿF@ qRfa+_i&7=akVQhaRkQclQohaR2brˡF@#I~rW{ 3~TE}O bqO cy`I@Ё`sVRڀfK@ڀbooOlw wKuK`1s*8zK@JqyozI@ eoo /uB s!xx7SlP4;walPe,0kNq @Jq.-8r JqM9-t '-E1*dbt+r sQJQLŌH{M0tb;{O,=CƁS=ԯןƋM2'Kx~{עamzD1-?F|ǓnJ\˟ݟITjU˫ˬ7F:-˫YkIՎ_TF ?BP(!RQ!4 Iz{7 F.!`}~k QhaqY.!(SFxfb@&7! kRka},1ff",1~,1ff ,1WVHf312E1kaB12lQ12a(712,1HfWV3UYkaO} mlQYBaYO(B,1WVHf3ka lQ"(;" FHfWV3Y"]#kaS#" q#lQ]#"]#S#"QoDcjWV3,1HfEFWVCaHf``5d 䰈qGA8rL5k0BAK6 k0L5k5L5|0V!CBL5 = |5QQ5&t?U: =|5vŦ547 P@QzqE4HQ7b&qn24Dp)CTU+0F2o$0jjSRjaRD9Q#eM1sB=yIZR1s^lZLV_4pQL׌HH.5Oa 5.. ^fCmnfv3p` T]`avelShift`$xa_d@a3p`OΠ`fs`m!*_a@@ VV%Sr V:67sGr:2!l7s[r 27.1i$3s 1T$y@a@dvkQXU.q.'2rqty!,0}v`n$qqt7v-x8cʁEl@baa%]1]1!H$rI*J!1LLLDŽkq+MԄ .NAOAAsQRe.S"T/.rq.qqAWVqqFYpRBZ}/b[.)\r].QLQ_1(A`ٓza(.44.;[Rx a;`"[TBf"< 1W`B`sh]`cu g`f&+o]`m`t`l`g`l*?`0qH`.:jx +\T`x`bdvq_v!L5~*ϫ16DP`)_R`Ht%k` 7DVhzڠ2xQBuݿ)/\BanAM_0qx2Ϛ0AP*(O`a`*4dFxgy2?Wʷշۯ-K08&4Fb[mD{ǿDD!s 1ϱPGYϋ(6ѲĢϩȩ5hA`# &AsDEy3 UĻɯAv^2`OnwAQ9)(ĽBT0fAvH`Uta)Yp!3EWLnew@)ncbā Av9H`ER#t6Hl ~)99:'ϟoP$tm-At!BT)ங'7İDZEBs&1)/I4+r/o/Av(* t/% *Pa#/?4Vir4F:?@^?mAiCOOOO6O=nO Z\9?K8/ `v GAAtPT&xJb=rb+ Trt*P@?B#o54trptb T mkqőԁw`3P?squ{tcF~ϿF@ Y%ukp{`?u %,Vrq @V4`+AszSPsT3 )5>%j{ိqi!stgq xŕ2htbrQ? !3'󿂱/0 c{[яJar<`Ѡ 5 %*5}9ᙓ @r^rlA$Q2U lQQ؉uyhU+424r@1Ryɒ^rx@7.?@hzi?wԐH}߈7c6z#o/|UD`a1H_\"p1@&WrBdeKTv7.!ԟ@V) `aGo,o>oPobo1qyo>TooODowQS+F搞R6);=[vrrQv>LUF+q StFUevS߅x$~&qޗ݅ԯ1).UaO|tǢKT*ϰd4)'ߕr|<ߒM2/WkNȏ%rQ|evʭVlߟU[#Իg.gM1xweۿoo1CBSCS/e/w/S~eB]v KcVFO/GXiPdU:Ǽ ڄE-,/wb/cƻ͟E/?'?*&)BOZlcƄdUO7c*&tG);AEOb1ҿ)M_Cb.ϛ]!_R8Vb=x](' _ ݂?z-sT_Na2s/@.g(O]Sa5 _;;L7pwrra2wcCB|WV.gWi gU1gtO@|{럘p  D g,l;;.o@o*aa Oo0Ґjo2kJΆb`b~Mk 2(s.®%)nd\S?l$`-@K ay!*t1K쐴^VQ2CIUdUpς1aanOa7P7߸y!X16BòJa@a WE1!UަQb@72Ce$6N ¸ջVcIpb)L(aO[Ne4 (ga# f8 \ 2d T!bu-(;M_qڋgֿ`4@?F\oA`0ΉjM09 Q(3b8ye/˸?FƣE?Fp5Ys?F5&!Q@Ή( 0_Bԃa2U0*s3?ܵ0Ԃ2 k+ݓ0]'%Qn?Fѡ?FW^bd%?F?Ԋu-Q/c,DJ}?5^I,QIƥ,$~//'% ?F*^Y?F[E?F|{T?c,|гY,vݥ,-?C6*~??b'%OZ`?Fa)P?Fů$_?FPvϟ'͚?c, h"lxp_Qҏ,mV}b!)/L FAOSO+~'%`0DLJ?FNqVUO:Oc,~jt7A`,ףp= ,7d__Kh'% QuU?F`4pF?FLAD?X@t]_c,io?JY8FZ4? qۄV$N@a__'%X/~%l3!?FeY?FJ~ӗ oc,z6>LZ4kw#awivQooj'%'-q?FmUa`2#+?F(%oc,Y7 }j GfZ4 ~,ClL^!UFV{WE?FcJΦc  )?H},""3\ \ -YVqjǻ )1̶ Ѡn<δ m|ѠaQaCSVGh4cfUpi$5,)֝F3L&AP"iЧ Z3*p#q8 |`rXӗze2ZՊ@ #5.?\zi{B  ȧ#ޯ&&JJ\BMV㖿t侜+Ģz`rl%ǨΩ)1@8"4߀Hߒƿ./1*Tύrˤ]c-=( @=5-2-9#.@"~C Ȓ#@ 7?D`Ofrsetx %q,kڡv hE 5ɞ)3U+KGAb TrKvlԐhi"//ׅDzW280//?(?< ????QcOwOO)O7(QOأTx7HUI' Xߞ^Ba5a8_A88F[K"/}#ĠzT݆B _`$@eŴko\7hoj(PUFDfP h-VTYYU?"H$ @?Y,b'@?xTT;͑U@ !@ү&P6 lAu` u(Jݩ c?EDe34 D   J ũ2qL lvF BP(?L0 `LineTray sp r{ ncyz۶&` v%P t e n&$`u v%W{ igTyt&a` v%_Rouy dw")g&A0'2A{Gzm?>5Q?A^n"`Fw lo0~/+n"j?|?+5/!4`S:Ta ow?<*+` 9?& ` 9S5@y0eK $KXOf@s{ 'B $KYtO 6`9UM@gy i@i04Ai '. `%9Dw AcNul>k$Uhh2Q2QEXT/J> 09([_q_[_o!otgU__o_oTEooo,o1A>Te\Ls c!2TCt1MU$U<"itjt AktE*Q*QURmtQ!!otU p qU r s-3"!!uGRvTlwaExn11Uz{Y|})~ARɄ22քeㄐqJ"T c 14.m"?<%Rpsf0M8@H8 MEJUU8!r5GrK&HZ&H͟@G+G3"?GERml&Jlmyome>mGG-GAUV>6HԂyϿHႻFSP6HuANqe (=MQ_'0Uhv$on!3` LDnFxB'3` A@iBݞ"4` C m@bAxB}lewL9WIFE/HU= _6p9yEW49dF[L#dJhMB `]@eko+׈]:aG[NhPUFDf h-TYYU?~@x]@L'} V6lX-1u. Bj2u2Z2j2#u9r#ULH/MZ1+B#AD5 60`Vis_SE.cTm!#20AD%`>Copy<0wigTt ?DU1f@M)@c<0o+@'ofdBUArX@Aad@iV@n3@ WAl@ef@R@6AtBvBb@hB@lBAJ@=## AoG?9/#n"E&444 7I#$@HB59_lj#"SV6U8*l>(Uhn E /J$&9"(Ln eg5Le#pheo'T !! Js~o/|R12'"U7A%O3W_7xr5(-6 `F2 xqu\J@#:0b)&`AAs)@E T@xd@Di e i=M]UJh#HT0#3 UEJ\}|U@f?@3!?F~?FL&d2ٺ(1?P Ǜd#pa HkaQ"U$"Y8"Mx#BU<&?&@&A&C"u` ?F]o"fpy" & G&o"o&o"o"o"o",o",o".,o"B, 6 6n#V"o"///$u)ar;21'u `Fxq21uBAy B AA21"!XhQ$,@pup`Ca$Rou@+u*LfXQc* IRg fbY-H- M ZU ZZZZUZZZZUZZZZUZZZZya-yyIvJvqyyMvNvOvMyEQvRvQyTvTQyVvWvXvYv3* j|%5EUeuu 4 蕺!BZ)CT@x@U @n@o@a@%iEPn*Љ4J1[v}y]v^vc@x3+"" qؗAU2Aym@xcv$"" zp$E1u/w3,#? N?$hVW9AB[bSq20S hV1Aqׁסױ!1A7Ig?abrXqkiqruׂ6rS@6'_@rp9SR2MV@׌aV @aU-ܕ?XrcU:uJed*ɮ᧤N@q37sfjBA'b6bjB'#KPRQʾqjeArQ c@(3FA{aٿA3b!&»F_ v7_M%;f!xjB!s(a?xjB,2brrP#VFI%r'#7 3%Р$UM_ }uj_2 ׂP5GׂWsZ2bQwvPQrT_@|wu‹ׂPc?P 5uR܉͏Cgyc(` q(`e80{A @9-܎ Ʉ '؏bAP42Dr \sya@Q_X俋NZ]b6 O6͒z{ # Z{u£@%ЊׅqmϕDR1oL^c,ߛѯgZ $j%{|ב R{iۄ);qσ⥞occL&d21b}aN/9K!KG'!cpK1a81YNSFxBD $e0Bof$z:cRTrDBʩya#HuzR j"^vc ` T-pavelS/hit`RHq/tq `OfYsfps}1 oa@@VVSrlV F"1+-27i41Tb4_qt_;aXUZ'2rq_!n0^Spn4"1PT_sE<raa-A-A1Hc^IpJ}bL;M 2NQOQ1\Q\QQؔcWRS򔆮TqqW&o$Y@RZMb[Z\g]tpapa_TAQQAbpJvq;Ϥ+bܤoa pQ+d0AW`BfpsĠh-posugpf&+o-pmbptdp^lbpgfp<XR*R?`0ȁfHT` jd0+9TfpxrpbASv_vPQE~ 1;F`a`пҡ<Rfp%tknpr !3EWj|Ϸ2HaB^׺/9Bmqn*<0NHBew0yQ̀~`ϿOdpahp+=#bRdﷰ4z:$ߢ B<R ai*rp iisXmTZdj `F1H<zՕ{xMOai?ޯ!_` tD.Jr5<}= ` K* `[Z l v 3mAxD{z{.gMbeᒎcQɥģ"Ĕ,/ħ<w{) ѫcĔpĽ} ѮmJ5N13_MsSspy`ID?S/c}ba K"eb@o> )עa2¢ `DVh%# UD kmjBt2ߧ߹$c褣%!rr[Rf`}aTy7[C3DnUbe|b;ruv`Up0;nr`1:븁X%`?$2$N1} Y4Brb@qAa-aa;qa `//5?*?cT Z "!0ЏQƽP׳M+Q ܒ*QDb)QX\T׳a_uu`bېRIu}ܒQ (l\O~?hzP-îLޯ)ّ `{Cݕ2YǕT `ƲG . !ё1ɕHᝢ WGMĪ ?Ɓ%Qx?ˆt/$Ͳt ڔ,ş$O4  ,o>oPobotoobo1*BBZВՑ7zBoaa^屓aG9gl6ҩ$xrNPƯدk)D _& 3EWi{ߍߟ߱ /THZld Z_VjhRdv=X|w$%F / >Pbt~44FJ3x<T+///(/aNO:QN9O:////./??'?9?K?]?o???1???? OJEOcUQOOOOOOIYvotuoacVh͟y_T_ j &m toB)j ooQvwёʮhg_#F K?F&?x?0l}G{,:`@"   -?%aEr%Õ))EyXR} o`д$#?ęxQIr/xUt,bwXUl0i1ewvv֔tQ=Q)R}h4FƶwGYs?CpYՕY 4e%{QHvsLQgŘ{)tUbzf^ȯfܓ֔% E:a`*q>^TbtxoMdǏ@هT/f/˖{OtC]c VqǐI֒FV –nUF?vn qsɿ *Ys?F>?F:Xc?FMڪ﫰|JܡtQq&A=QngiC21MwZ漣A$rqg0N܏tE˔F*CUy ,Xjw=Oas߅<`ߦ7 {RC*OoNkrfŜo-?QKe_? Obweoz}&U.V@U4kW5wR<k 7 fwdůWdvw.ϒ,u6^.ڌ@(]/5Uk@ k@Dw,|@q K0/'LK9'Cm0ϾSBs2ّ5)\ls?1D@ q>Aw9#uQ y~qҰq٪$E)U5G1r9{ a`pM>AkVRbדL!.UEG!ܱ'kWH٤*Jn}viIpU'LG(A^|{Օ4߭9w7;Օ f!+S2)kt/'م"Nj$6HZl~P`4@?FEO jM󎣈PuY Qᦤ8%/˸?F#eֻ?Fp5Ys?FwtÊG?( 0O(LпDJգ<}?5^ITLQIjL$?~OOI5 ?F;?F[E?F3Ϛ)O(L|гY<ߍTLvߜjL-C6*C_U_'5OZ`?F3$i?Fů$_?Fir8_(L h"lxHOj?FeY?F܇]}(Lz6>19lTkw#N<vuqRd/%5'-q?F0 B,?F2g#+ׄ0]/٨(LYЍj@ T jLCl#15v?3FV{WE?FD~ΏJNk(@(+ WIHg}jLzB՟$q$D*P!)=6?zH7j A^ 3=< 2Aġa1aS~h4'+5.ϼ7O9H%F_p8Ɋ( b-SlJg&ǯٯ>qA>%7IWt?&t:NO/ag#mҳ31>ũ!bX,!.@-R^rC&Aϵπ!EWiSh:Mu^9a.^;g?&h&8!AOϓ{EEEW{>h"R7"(P!1('b!)1jUP-29Z3ˡ#@$ 1d>"wCCZ3Q%p1bؒC!p2@&7DZ3`O}f70seto?uܰA0&2,?>?P:v72p&1"E$A1oٿA32ÿ&A>+5R{w,0';)w-eV-jUCU+ a ?%A'; WTrvt0lghin0w8M=Bb7OOO;J!HR?+vOOOOO__????(?q>_]?u sEtjqttHD: #  h  T0]]9 # B 2AUAɿAhY?ٿA,c2ֹƿP6 Au` ?A@j$u#J.9axu`u]`bu`+u >ڍU@@M&d2}34"ÿDC'>"M-w'>"D(p A "'$"&H' O#&5 $s&]$2JD=#D> D6#U`l"_# #1?9#H1 uNWzIs;2 8J#2zGz3Fx$2[Ru(/YB DU_V?\.?/B:o:2A7P`BackgGPou_ndCM`lM`HQz@@bZ}H D?h1GA #X8P6d3662Q1CUBF4i7o117;y0~1Mr)Srm>de}mspTt "qq"1 1381812tMItBB Eb'b$4ANu13[h2AuBH1BʜA!ʩ¶jÄ[QЄT݄zrf>!$0 %3To1/u/u?8XxAC`IWsSwSwRs%o.@0RgPf KNC`teI`\ai!nn3uZR%d DM`kO`mtiM`5HQ-2HZv#`NC`o>!8V@MKsEŭAد3dVrbRaPags0bjkTykp~1])S搹qO`zfY`u^s;cTͱnA²oivlPA$*q#8@q#5$rQQR*E?eNRעQcBdg:b`OTndGPuNEb;StRaaGPdB[Ÿ-uijd[„6k~]QRƀ@gP@ A>LEs?e\/dϩs Aee"g 9bY`dt2D-Ÿpsr- Sqv߈;`aݔ֑#pS~>"ƒE*ȯ:b` Txk GPp}1Բydc j]aaAe(Se1dSrʴ &e%\g#d ɿRf);M_=7DßӁ„{81,[/O^kxÅi-ùƁ3 uI[Uu`  *L%LBW?AP)Es̲aٳTe"/)#We"ɧ??? ?:OOB&c_ //Y`6?H?Wis??]CJ_ oAUCGP?e`(__! m2oDoVohozooo0 siԛOo oW}_oOOOcV¯]WץR=Oas-](]uќoo~Gm#oTw {Ɔ҆΃S 6:0Bw)s͟ߟ'9ρJca:smQm Krаe/Ȯя- voP* V²#5VUo"/%Wo"O _._@_.o*o/**u(!S[mJ5li@eu+arw% UR°l;Q@nt9'G} !M @o#1Ϡt+o_1OCLRʿ.@=(*jMD`sprkND߂ϚByiϹV" ?5G&8߭\n߀޲5F/=$2&"4FXjj"j"/ɨu*FXjv_?q????9 ???Qc0OBOTOfOxOOO8B+DO!O=`Rotyfb)]O( !1idBm!?ȅKbB;rbrc`R@*@;H(iPB_)/BE!1v])g%oks{1tpnN;GWewyuPz%doaqm Bqo2;a2wtSkmQb@nvUaTtnsb bdds°bjTq3o: rrcjKbT!E!222?kl;!30kC2m2  aZZ!*kψC\\E&&BGO ='`C²!cRf Ot9d_D@qA,@"hi."4 b bA' Y! Pk?zz!!_lDZU!!'7!ƕP d>"p}9+6@,1X8d:PjH1/U0r4,,1)*AA/_"_o & #QMQ,0#` B scriftungm,1 `Cal PoPtQg1`E}u+iH5u1! 7 @@j&@@q`o?@rb?d-e?s)gUA$ ?$%vr;#xAZ%RiLPE" 2 0"U$2 ur! |u[]4 t&&s~rzlH1AUH1?iiu5UAA2zry$"!rByǤo VPQayl p-LPnPʡn/k?Pp rN 3Qiߢu PuPw 4EPtTIkrPo?PlΣV?PrPTRʧIEz!mr󢴳W 5D b9BTBnPnߤz 6VOLA !0jA_S_e_ŕ{W'&!3AP!|/2g1 D1y 3 H1D1oE7q;Eȧ{e|lE1@@~!@Fg#?F֌"??QkPyA`Q27W˚ATl 1<25l =cl@YmfxAj`PL!@@[g?+@@1#_  +??@*o4?PD}p\z׈-uru`u0`bcu ,0@@"o@a/ ("q6y B%#u2#*"0#ᝯ2,`6@@Ên`P @$Rxww &;01O5l^3);hK(a~"f''ׄ?[銞<<h.?l"dYկd#j_u bsUTK0a?DbB`@_bz I/!*98`P"3\ o"҂3q@551I7zrQQr 9:z:a:a9aaR=R>rrqdQhQrq(Qr q2aa0tQmO\[|RlqlqⴋrT,@rLQux҅t0q^qs pC&Av"rBpsx^֔u~*k sa4azuNu ca@bֵf7sa9ՐxU܅ŔrQ9PCaLJY„fD/@rF*PPV#Oj&TP@Yn`@Q.%Z$yaUBafp Uu!4q х4s q, Tto! ТZhQZi5,Qrc3WLP@*(B<@b@t^?A$@o.a#@PkIKr3b3KlqJj/ â\Ca/O Yn`PQFYYCOǐ2p:F>pKBrOgOq/LKHHKEKːYCJ=Aұ)VZn` OzOOOOE^Z___*_<_NXQ3|GCA|epPKd @A[-B^ cAY£jNT '_%k…Ž_Zoc ss&qg1]JSr|([w-v`DbKOb=~h>fkviKleN{>f8{gyjPsKϝ̏|vRTXc[HAmeZfDo1ock2DN&xݟ^pP*wwjo>f?ʗTٛg>f,GY^4Fܢhn??Qa%<}dAc)#]K2At^FA1?0_P_b_k؏?@ܑ9Zm(͖pB?@qK:?@W@ή8W?@S^H?ssZ*m(5=*/P#&?@!W/?@?@FD'A&a?@0?@vj6??@^ŴBI!O&J6/iлH$?@ T9_3`?f\Θm({E=T@m`m?@l6͊`$Zǂm/&0=ficUo@@K^9`lخgA%=,^?@tA XP@@z(fkT?@Ô1fe=j.ƀ?@P3{@@5 I]ͻ?@+ӟ wu=~@h{?@j"S~#HXt$'=)&3I{(?;3 BP6Oc= }…lO'ƯدH6?[0 O۶f_@3l~ !H<@)>Pbtφ X?y3yGPU)^BPԇOOOOOOO__)_;_M___q_______1i/{/^X" 6q'AS6m#p P:yad4//*/E$B ?' `?FM%:# %?Q.(%,",x>Pbt}R,>bMt=ζȏڏG"fFXj|ϲxU,įFX*pԯ8GB(ma AS4!̱ Dq 2aaJYPcy08̱u) {`gl6 @@L"@@Hh45?;C?FwG[r;1'u`Fx8<"\YP"Q!cZ"[!y!!02..ȣrq´"T,@-@<إN-9-L7Cz`WAoubҟ Ab;sż@.v̟6 ˟W'?̵A4!)4TB`xAp`n>@tzj7X3#|گ2п6=#@X)ܽ+?-ryN ~Au`u`@rb?5@ȩ.p@Őc)a&M/_)s6})7Up`uR!cA*qK@2-[!Ue)aYգҺ#@+("euF@X,@@cP?$9/f$Ptw2wy#wI`rSbK3`8q \~a, c֩hǐ⣰  ˩5< Nas i !3 w%;G/?/e u @N2 -< N6£1 h5s1.h fx sN!P 4b ?O0A&GWA///&6?+//O*`׹mD11CJ /"ǗNO`O//??M'X'jY K"K(;G"JK>1?/KK__O]?o?_"o??MR& vum]p0cKoeLlOe񑡡ojFxk@?vKuS@Rmw@|PmƁNaGy}A<"po Bpp9Lp A? vEqLJ PAdrHXLJ,HwX!kmieT~A#5@?MVl5^l6qBV0IPo oomDu׿߉Еe n`vc~ae` TmavlShi"ptпome`eOpfҳr$ ienюB@0wo0oO!~_=@`{U$erdT~?L&d2?qnSU^P^ e رT|E?U  ŕHfc IsJ026q6q|qLԣWM 2N&O@Qԗ`R`ԇAT&qUuVrIW)rX6KYCZPr[]8~~^]w2Ѡ%2_䨁@Ldq2јqq-;X |pnԛq'VUAAU{S@X,@@c?P?]2vÑrHTn?O#@2pcr2ql{ԑ١U^@dإʼnU~K/MEp2mԌq $SqU£rIdҥqҦ~ҧҨ(ҩ 5JJҫOI\ҭiҮvүҰD {VuPqM/|qf 1 q/$r89UƀdK/Eb].J?/ ћqXՈ!2яՁ=7&LMP5,Ku/?+/UF BP(m???*?og6oo?M@]f?qoooooo\ke?__O\Z ՁX9/ӘC_U_KXF_}6Z?Fe#?2”,zNt/DF`0 &F1l aPdl/BjZԘl-U%{ 酼N[b?zf^5OHƏ؏bo2oBbd?Ffl6&FF?FuWu~Z}1Ԛ\ ϥRsBTЯ*oo`GÊϮݢ${Կ̗ޟM2.RkO֨ϸ"4FX<ߺ̱ 2U9MSiO]yd!4q<|a|aSeOZAkQw9+OO!@ Ai1/C.</bsPbOo@nZA">Wi{;^5˩?@Fmtf~:G?@?@;:y?@A^}/9*[Jȏ :g}= A?@I l?@C"<%0Md 8#(m?@'`sI@DͰMUh>OmHaoW?@&N?@?@?+%U OmHg6D~)cK?9?@_1n`Um,H_mH揷P,P 5b?@|XYm˗_mHOgOw^ G/6/?@$!?@d%FV^_mH+5gf 밷?@qK"`:= ?@6NZ`4olI5gN@S:3ί?@f?ōo ȡ4$1qY?t@ O=$rb K{ӞvD '炏ÏՏ"?V N x4(1(<1>=ŸD?8Aį߬X,@@cPx114<1U??$yίU@?`0 Q/TSi^1ATs]<kQ'/2qqGaQ! Q#`Qn%bC6a i@bpCa zq`%>q,W,@Q"c,qB@>q݁oӂ_=@`{k*#upq95"+aq@uo5ubO lҊq徶#Ž#UU)*/023456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdTUUUUUt4l"H$ @X,b'@ >cC-$qJ A;X:+,234567J89;U<=>?U@ABCUDEFGUHIJKULMNOUPQRSUTUVWUXYZ[U\]^_U`abcUdUUUUt4N"H$ @Y,b'@  C-dw A;Xn@QTRR@jPRRH<(H<(nE$Q}RE,kPRU`8z{%@&d2@D" a@ LFDTyB Фuh$T UF"H$ @FY,b'@Fx"D"uTN)O  1̙/2// UetTE<$KUijklopqrs )G]1i4Q(q> i4Hu:Di4-q>)1e8#>@%1e8;-1e8>D11e8>51e8>D91e8>=1e8>DA1e8>M1e8>DQ1e8>U5i4:5P ,6AWAxAA,AAA"lQ>Q__ oo=1 O!"  9 #aa 1` B scr,0f ungn` `Ca80lB<0u""afoo`c V r;b,0nd;rk^2r ` C<0n`e`t<0Jr11u1}uy q0qyu @@{^k!@@p8@@< U^?%/7 1>/?X%i/!#.o}2bvuTQE z  @"1#0P贁Nk[T2gc= A [[sѤ1 (~&!ߟ 4" Qz91wUpQ U/tQHzJ-p-(4"b) 9}Q 2ǡWԯ|%}Q:`Se*:&毈ƿ*)2į1Sri0Z`c-1 :&QX#'_2q$"q1~  }u14! -AÞ|o@@ZT-H$@@ZVy6!2Џ(DPVhzل~N`rH )5 L^%/ϾSevv%+=ӑ1IYk}R$dTPd+))zԼn@Nӟ?@<" ?FhK% V#y6淚h&a廒Mat!ڑ1 r%t"&'s&'%81tR?+d+PF +(ŔbR`+ A`c`n`bqlaQz<< ґ1yGYk}AyCV"M&_+_zo\V%_____(OL^vvC1}O/1'<&/JRJ)r\)@sn.//C1#v//8,8n;?_?q<_4#1??;#bzy6;B9ޟOEOWOiO{OOOFOOOuo_Bo/_|d\_n_*___uXoo:booop'ooooo 2ПVhzzN%0uèzԱy 0BTfxߜҏ,>Pbtc_ïԱ }4$Imկy4[;:2G0@'ര1',G9<H/ł #7!|@@`0!@Į~s\ Lr#&'vӲ (w.&'@sa#va# ϐߜ/e $D /A/*F]!ܱ-7/?4!@J;'?4ԱJ7),>Pbtx.cgCrd1*F+FBF6/H(FFFFFFF///r_//??(?|9AJ?}8Ak?}8CWܱAؿ갚1??'9KϠ]oρϴzD@ u iDss(-DT! ~FԠF&1 7ߚtV$r6qۜrdK_]__jaӒcQ` AcentConorz0BA= dvAco+sAh vϟ)7uN`U+˶ACoUogoyooooozo@ }D}X4FXʴFԴFĶ!jŜv X/ew0Bʏ܏x"4FXjώ@M tA,$6ﱯl~ZXo ۯL*N`r곁ɿۿ#n@_BP(L_Ei^pypπϔjzFtFՠ <ݚwN!1Cu/gyߋߝ߯ -?Qcu.]j*` MFEdpO5OyLyOOAm4O#O_KOokRG6x5 551$  oj Jd@ @@9N!@@S( Bp&od2?d?d{?)81fff?Sbfffffd/r, 1:(1*?<9eAs7|3Ik1 6PYa#ar.lH!PeaasQuH i8eT5borg]sprvp @Ho=4EW8@@Vjx?@Md䚕?QNQd*=W$//$w?>?P?Rb=s=5(lHPbt@%LI.l /h.Rq/ * I I x$"Im8h@/d,yLoJL@@Vj5\ jZ?@ x??[r;I[1I'Iu`F` CO25vAvA ` AcnpfODV`ETnpny6VOO3o1`9OK2 ;SyIe6* ` 9X+OfehZ v[Y_~ `9Dit,G6`9UMgii['?qo?@FP(?oEELn O95 u`?uph@mf fW uFi` zeRu|a2 ;C `ze*_K"C ze?mmob4ɓKϟs #z^p#a{ ݥ鯊ϥ\~"vT pApAN3ۄ@o1o1uaaQ Q7"B"9)x2:6@bra=>jՒ2G"b`bPŔ a~"'"@6%Nށ'!m'#~1 !CdpA(qQ pᷴ'#Nآm xҮ P*JR?@klkF@@u.*x 40%B%pQx5V@eδ翐ॣu@RO4a{craS`1bb@r#ӹg@l.@ hŐMQeɠ=% 1 U+puRlMs*@jށȘQȁ5rl>j$ʯܩ$Itm2m9#mIrjb}38b \ q7߹g ubkbWgz `z"!;aNa]k$bt{awlo'9K@oO:ZaGFdwM (ui&w?CG@RF`0ΘZ򟟎}y̌.O$ 1CUgyJԈ>_a&v_ǜ"ٖ.nRkٖ}"ϡQmfW7+M_qyRoݿiA[*&[?RSߜπPϯ{l$>eQQQQߴߞѺ1AJ)zo.?XUoy p ՟oRON@1&q_*5w?@c&?@1S9S_O$$׀a+/߼@ n/ VЖQ*JRe@_R_d_v__K/w:Nٿ\f@{<o.o@oRo doѿ_Zv9_oo$6qo}oZvR얧4X $(!#ҎWM @y<@4xҮ@PR4Xb4)ـe5؏;/l%#ROT*G.>i q!Tq‰╁‹4q'_2q꠼4ڋ!0†"` TextCoror zې0` La=inFAn=J3` Asaa&4` @mpr:dj ({ҧ}FuP(?!3. jϩuu аBEgCri9h qHɴ1TEL .$$KL‚a̱/q wѥ?Q@a)q}hGдOGј(/ߋL -d2L@@b@3~߾g~[r}; 1 k' u`FH?u8uq_'!b0ց!ِJz@V4uq<@&10ABCdwy@xRb8-դ&M #zx,wcUK>V) $(JJČTıSq$ !~ ~ 惥3*4upVp/o.̀A{Gz?Q? ` uSnaow?'9` UFlP[t9rJSu?P`` u[` 2VIN=-1 Acx`u)TGsaG'cyI"/+/djqjqO0S 񐒿0q40s"tgWWޢ5T,@74?qc;a`񊁄 QVC[yAjia,pLs┴x f MZ[?7@Њ3@Bu@u]a?qPbHwV[d3`LsFx/P/b/t//+U//?;WцQ-aagԑT-qA }??p8Q1 J?F2}~&q#?RkȆR_vjTk-z^E|?@U?el?@ ]{ ҵ?@iLyp&Ӝ4V]Y-tA?@e"9c?@ZOFIvPPtq?@:S[?@Qn?@3jvP?-qC0qp2Z`?@#I e?@t:s[O, ?@bַ(?@6\,k?@?z@>?@BT!0/T51e$ݧJA3V5@ ?hhU 6-vQ*$Qk/////UPt5weB+sliMy?????."?FrUM?6OKO]OoOOO?MeOF_i(ݧl\\_n__ϴ@x<@sn(ֿ@MZ[@आQp\5oYop8gyoF= ǤoloNVQi50Q ݢT0QPi u?u޵c0QW'2KqGz!'ۿ0Of"ݡ` TextCoϠorz` L}ainFn3` Aspa&ݡ4;` mpϠWj%{/NFҰ(@[?N!3gh\NǟuE-޵Srv /%Rpہt*yH&\WT5L.W1pVas1)etR4Q ԡ-!s1Q!QQaWhx-&kᴡiWkL Qd2L@@*"= @eN q[ar;1'uv&`Ft?ud_ࣄ)bZ!ෲ.@V1d 1<1.TQABCd&"2y1 dҧs3Uhb@kX!1DKJnذA1h0lhlKlhhhh h h h h B{FF 1Cc{Cb34WQd@/o.ƒ̀\ pA{Gz?hQk? ^` S۠a5'ow_` Fpl(UPtnSbu `` ` 2@@㫁- Ac\eĂ0LK`T߁saac(9`P Sy*a ` XOfQs(LJ: @ YO^H `DpcÁ6a`Mgi{i\𹃰 {W1X?F?@O@@xqyД@Le廒5b up`T?upM @m±h Qh q@s1F5` DRudgӶ `DDQQ4WqXwKP.i#{KRӠQQ0+̰@a6E/b apz%At-8kH1\ k"@TQb::4r34Nb99"ii74AA 94B:D4Rm"PP+='D>4D bAAcQmnwxb,,0!QCQDԢ;xHTT,@BᑀAu10Q H1CǁiOpC?tXb _ X,?B@a*Rr@[U@dU07ArA2G& OD24G*OC BxèE2N4_EŸK_F :ko}hWPèEBU?uwoD dooZèEm% 'A+ܹJEc!rm~ܶ@rSsv@ưV @uYH/yj|ɨEM,ԡUuU6_=D!4_xp1*qȢHㅭ5r6AlR4r__hQ$lVϨWLBv tˏ2ИpA#I1r\bGP31~ \p ưѣA!lR*[0r[0Œ'>Eq W|ư r */I'[.U:ߝN(F U"/ H(%%t'6O?qہ// {K/ +6Fd4)?//&M(p?/.3k'CP~/ ?-G)<'O?q?? OO1OCEROvO+fFV4t__OgE/-L F/8_=fF56FG?ok=_0}g?oo)o;oMo_oCOUOo3| g%oN% XuWfJ_\TfFlV_$E_'/MUl~hٮDf"{C]R|{h:[i\hY;èpʯܯqˁ5w?@ Ѥovԩ?@_1eX{?@#>RؘR_vj,uk-޾bz^-E|?@UtWel?@ ]{ ?@iL@&Ӝ4߆=Y-tA?@e"9c?@ZOF~uI PA?@:S[a ?@Qn?@3j -qװhu0q`P2Z`?@#I e?@t:i Dŀu[O, ?@bַ(?@6\,k?@?zNŀu'@>?@?BT!؝ $JUϢk@ 88%vFQI*m!/؟d @.@R†80t7'V WeBo90 .oo$&Œ/;X-ҏHo$&gq/d(JoUm///X!@x<@>m_ @X,d,M!U/Ϣ??p8,?OQOL/0B>|/0BD>/;B>/.B>09B>N0-B>{02B>0;B4>08B> 1IBi1JBTheDoc"Gestur Fom a FhrungsliieGuideSTR Normal&msvNoAWutCn 7ecPage-1"Zeichnblat -1visVerion*Byte oudrVai bl&Byte o]rvai7bl0Byte oudrVai bl. 1,Byte o]rvaiwbl.1"Zeichnblat -2Page-2EinfachBasicRe_chtkRectan7gl0Byte oudrVai bl.2,Byte o]rvaibl.202Byte oudrVa]i bl.31.Byte o]rvaibl.231Flus normaFlow NrmaBUmgekhrt sucwif_Klamr Revrs bac6msvPreiwIconCopT Pa g&msvStrucueTyp 4msvSDTarge_tIn eusc io 4msvSDCaloutNHi'ght(msvSDCalou]ttye2msvSDCalou]ttyen *msvSha_peCtgorisOrientaioHi_deLaruAtachToSide"Re_sizW_thTxSideLewadrBginLeadrE n(WHBoxInterUscin IsEndnteri oExtens7ioInsetSideMpont&fnMidp}ontOfse&CaloutuSyeR oCStyle1CStyle2CStyle3CStyle4CStyle5CStyle6CStyle7CStyle8CStyle9CStyle10CStyle 1CStyle12CStyle13CStyle14CStyle15CStyle16CStyle17CStyle18CStyle19CStyle20$Orient]aioR oOrientTopOrientWBotmOrient5LfOrientRghOrientAuoLeadrRotLeadrH inLewadrC7nt$LeadrMipontsuAsociaton$Design}fekt.1visUSETypemsvTheeInfomsvTheeyp(msvTheeLatinFo t(msvTheeAia_nFo t,msvTheeCoplxFnt6msvTheeLinTra sprncy,msvTheeLinPate r *msvTheeLin]Wigt.msvTheeLin_Rou dg@msvTheeCon _ectrwra sprny6msvTheeCon ectrWPatr 4msvTheeCon ectr]Wigt8msvTheeCon ectruRu di g2msvTheeCon ectrBgi .msvTheeCon ectrE d0msvTheeCon ectrE d2:msvTheeCon ectr]Bgi S z6msvTheeCon ectrE dSi z6msvTheeFil Tranprncy,msvTheeFil Patern:msvTheeSadWowr np rncy0msvTheeSad_owP tern,msvTheeSadowty l0msvTheeSadowXOfst0msvTheeSadowYOfst<msvTheeSadowM g_nifc 5to4msvTheeSadowDircton"msvTheeCol r$msvTheeEfe7ctVerbin dCo}nectr0Dynamische_r Vb7nd(Dynamic onetrTextPoWsiin2Byte oudrVa]i bl.3.Byte o]rvaibl.2 32Byte oudrVa]i bl. 3.Byte o]rvaibl.2 32Byte oudrVa]i bl.34.Byte o]rvaibl.2342Byte oudrVa]i bl.35.Byte o]rvaibl.2352Byte oudrVa]i bl.36.Byte o]rvaibl.2362Byte oudrVa]i bl.37.Byte o]rvaibl.2372Byte oudrVa]i bl.38.Byte o]rvaibl.238JUmgekhrt sucwif_Klamr.239(Revrs bac.239JUmgekhrt sucwif_Klamr.240(Revrs bac.240JUmgekhrt sucwif_Klamr.241(Revrs bac.241 Textan}mrkug$Text AUnoainuAtribuesBpmnId"BpmnCategoris(BpmnDocueta in&BpmnArtifa]cTyeBpmnNaeBpmnStae"BpmnPruoetis,BpmnPruoetis_Nae,BpmnPruoetis_Ty.BpmnPruoetis_Va7luLBpmnPruoetis_Valu_EUxr s oBdyTBpmnPruoetis_Valu_EUxr s o]Lnga9e:BpmnPruoeti_s_Cr_ela oBpmnText$BpmnCatego_ryRf$BpmnEleetTye6BpmnConectigOb]jTye(BpmnCoditT ye4BpmnCoditExres  RBpmnCoditExres _BdyZBpmnCoditExres _Lagu;g"BpmnMe]sagRf,BpmnMe]sagR_f_Nm8BpmnMe]sagRf_PruoetiBpmnMesagRf_Proeti_5NmBpmnMesagRf_Proeti_7TyDBpmnMe]sagRf_Pruoetiw_VlubBpmnMe]sagRf_Pruoetiw_Vlu__ExrinodyjBpmnMe]sagRf_Pruoetiw_Vlu__Exr]inLn uPBpmnMe]sagRwf_roeti_uCrel o2BpmnMe]sagRf_Fro RBpmnMe]sagRf_Fro Prtic+pn.)TyeBpmnMesagRf_Froj Rl FBpmnMe]sagRwf_ro Eti'y .BpmnMe]sagRf_To NBpmnMe]sagRf_To Prtic'pn%Tye>BpmnMe]sagRf_To 5Rl BpmnMesagRf__To Eti#y  BpmnDirect o&Textan}mrkug.49*Text AUnoain.490msvWarwnOPesoal5If3hS3G3T3%G33$G3XT3E3T3G3`4A3`4A3`4A3`4A3Tg#4A3M'4A3a+4A3 a/4A334(G3hS[4G3p4%G3a4A3M4A3T4G3a4A3$a4A344+G3d4(G3܂51G3F5.G3Ԉt5%G3hS5G3iS5G3lT5E3K35G3K35G3M6A3M 6A3,a6A34a6A3G3 >"G3,B> G3b>,G3D>*G3t>.G3<>8G3?/G3M?+G3|x?.G3T3??G3<?8G3@5G3=R@9G3@4G3$@0G3\@2G3D=!A;G3=\A9G3=A8G3A/G3>A:G36B2G34hB/G3̆B2G3B2G3D>B=G3<8C7G3ToC%G3C'G3M3CG3M3CG3tC0G3dD)G3HD G3hD0G3D0G3D2G3TD0G3*E2G3Ĉ\E0G3E2G34E0G3lE2G3 F0G3܉PF2G3F0G3LF2G3F0G33GDG3XG)G3 3GDG3G)G3T 3GDG32H)G3Ta[HA3\a_HA3ԋcH#G3H%G3THG3jSHG3,H&G3$I)G3T+I)G3N3TIG3 N3mIG3TI$G3I.G3I-G3J1G3 38JIG3dJJPG3>J9G3rK4G3K(G3K1G3JK=G3$JG3D!3IM=G3!3MCG3MZG3#N]G3JNJG3,N1G3JNKG3!3FO5G34"3{O<G3dO-G3DJOGG3"3+P3G3"3^P6G3P$G3DP)G3tP+G3 Q2G3da>QA3laBQA3taFQA3|aJQA3aNQA3aRQA3aVQA3aZQA3a^QA3abQA3afQA3ajQA3anQA3arQA3avQA3azQA3a~QA3aQA3aQA3aQA3bQA3 bQA3bQA3bQA3$bQA3,bQA34bQA3RA3lcBRA3tcFRA3|cJRA3cNRA3cRRA3cVRA3cZRA3c^RA3cbRA3cfRA3cjRA3cnRA3crRA  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     ]>rFxml:spaceprsrv]rFxmlns:vdhtp/%scea .i+ro of.+o/i iA/20*c6%et/ \n]rFxmlns:v14htp/'sUcea .i-ro oUf.-o/GUf=e'v=s=]o'200'Eet1 lnU&U-.t4 "H$ @Y,b'@  l!}C-dq+7 AU %t4 [l C>-D`7"AJ@ Q5lMR@Ql7RH<(H<(JEQmREQnR{dm]  g"4FXo 8)jJ(N1j@(yV4](\Ϡh^H }B`Z P`u~āz`S3 BvR̿R$TO!1ҡΥ)nPW'<^E+~1trd}51P24vR\$PjB="dikzK&DWGY ?pl*'"Dqn~%!././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/internals.rst0000644000076500000240000000321514601576577015722 0ustar00twstaff.. include:: global.rst.inc .. _internals: Internals ========= The internals chapter describes and analyses most of the inner workings of Borg. Borg uses a low-level, key-value store, the :ref:`repository`, and implements a more complex data structure on top of it, which is made up of the :ref:`manifest `, :ref:`archives `, :ref:`items ` and data :ref:`chunks`. Each repository can hold multiple :ref:`archives `, which represent individual backups that contain a full archive of the files specified when the backup was performed. Deduplication is performed globally across all data in the repository (multiple backups and even multiple hosts), both on data and file metadata, using :ref:`chunks` created by the chunker using the Buzhash_ algorithm ("buzhash" chunker) or a simpler fixed blocksize algorithm ("fixed" chunker). To actually perform the repository-wide deduplication, a hash of each chunk is checked against the :ref:`chunks cache `, which is a hash-table of all chunks that already exist. .. figure:: internals/structure.png :figwidth: 100% :width: 100% Layers in Borg. On the very top commands are implemented, using a data access layer provided by the Archive and Item classes. The "key" object provides both compression and authenticated encryption used by the data access layer. The "key" object represents the sole trust boundary in Borg. The lowest layer is the repository, either accessed directly (Repository) or remotely (RemoteRepository). .. toctree:: :caption: Internals contents internals/security internals/data-structures internals/frontends ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/introduction.rst0000644000076500000240000000035014601576577016441 0ustar00twstaffIntroduction ============ .. this shim is here to fix the structure in the PDF rendering. without this stub, the elements in the toctree of index.rst show up a level below the README file included .. include:: ../README.rst ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1711734324.119211 borgbackup-1.2.8/docs/man/0000755000076500000240000000000014601577064013733 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-benchmark-crud.10000644000076500000240000000671214601576577017647 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-BENCHMARK-CRUD" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-benchmark-crud \- Benchmark Create, Read, Update, Delete for archives. .SH SYNOPSIS .sp borg [common options] benchmark crud [options] REPOSITORY PATH .SH DESCRIPTION .sp This command benchmarks borg CRUD (create, read, update, delete) operations. .sp It creates input data below the given PATH and backups this data into the given REPO. The REPO must already exist (it could be a fresh empty repo or an existing repo, the command will create / read / update / delete some archives named borg\-benchmark\-crud* there. .sp Make sure you have free space there, you\(aqll need about 1GB each (+ overhead). .sp If your repository is encrypted and borg needs a passphrase to unlock the key, use: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C BORG_PASSPHRASE=mysecret borg benchmark crud REPO PATH .ft P .fi .UNINDENT .UNINDENT .sp Measurements are done with different input file sizes and counts. The file contents are very artificial (either all zero or all random), thus the measurement results do not necessarily reflect performance with real data. Also, due to the kind of content used, no compression is used in these benchmarks. .INDENT 0.0 .TP .B C\- == borg create (1st archive creation, no compression, do not use files cache) C\-Z\- == all\-zero files. full dedup, this is primarily measuring reader/chunker/hasher. C\-R\- == random files. no dedup, measuring throughput through all processing stages. .TP .B R\- == borg extract (extract archive, dry\-run, do everything, but do not write files to disk) R\-Z\- == all zero files. Measuring heavily duplicated files. R\-R\- == random files. No duplication here, measuring throughput through all processing stages, except writing to disk. .TP .B U\- == borg create (2nd archive creation of unchanged input files, measure files cache speed) The throughput value is kind of virtual here, it does not actually read the file. U\-Z\- == needs to check the 2 all\-zero chunks\(aq existence in the repo. U\-R\- == needs to check existence of a lot of different chunks in the repo. .TP .B D\- == borg delete archive (delete last remaining archive, measure deletion + compaction) D\-Z\- == few chunks to delete / few segments to compact/remove. D\-R\- == many chunks to delete / many segments to compact/remove. .UNINDENT .sp Please note that there might be quite some variance in these measurements. Try multiple measurements and having a otherwise idle machine (and network, if you use it). .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY repository to use for benchmark (must exist) .TP .B PATH path were to create benchmark input data .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-benchmark.10000644000076500000240000000203414601576577016705 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-BENCHMARK" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-benchmark \- benchmark command .SH SYNOPSIS .nf borg [common options] benchmark crud ... .fi .sp .SH DESCRIPTION .sp These commands do various benchmarks. .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-benchmark\-crud(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-break-lock.10000644000076500000240000000255114601576577016771 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-BREAK-LOCK" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-break-lock \- Break the repository lock (e.g. in case it was left by a dead borg. .SH SYNOPSIS .sp borg [common options] break\-lock [options] [REPOSITORY] .SH DESCRIPTION .sp This command breaks the repository and cache locks. Please use carefully and only while no borg process (on any machine) is trying to access the Cache or the Repository. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY repository for which to break the locks .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-change-passphrase.10000644000076500000240000000303314601576577020347 0ustar00twstaff.\" Man page generated from reStructuredText. . .TH BORG-CHANGE-PASSPHRASE 1 "2017-11-25" "" "borg backup tool" .SH NAME borg-change-passphrase \- Change repository key file passphrase . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp borg [common options] change\-passphrase [options] [REPOSITORY] .SH DESCRIPTION .sp The key files used for repository encryption are optionally passphrase protected. This command can be used to change this passphrase. .sp Please note that this command only changes the passphrase, but not any secret protected by it (like e.g. encryption/MAC keys or chunker seed). Thus, changing the passphrase after passphrase and borg key got compromised does not protect future (nor past) backups to the same repository. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .sp REPOSITORY .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-check.10000644000076500000240000002164514601576577016041 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-CHECK" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-check \- Check repository consistency .SH SYNOPSIS .sp borg [common options] check [options] [REPOSITORY_OR_ARCHIVE] .SH DESCRIPTION .sp The check command verifies the consistency of a repository and its archives. It consists of two major steps: .INDENT 0.0 .IP 1. 3 Checking the consistency of the repository itself. This includes checking the segment magic headers, and both the metadata and data of all objects in the segments. The read data is checked by size and CRC. Bit rot and other types of accidental damage can be detected this way. Running the repository check can be split into multiple partial checks using \fB\-\-max\-duration\fP\&. When checking a remote repository, please note that the checks run on the server and do not cause significant network traffic. .IP 2. 3 Checking consistency and correctness of the archive metadata and optionally archive data (requires \fB\-\-verify\-data\fP). This includes ensuring that the repository manifest exists, the archive metadata chunk is present, and that all chunks referencing files (items) in the archive exist. This requires reading archive and file metadata, but not data. To cryptographically verify the file (content) data integrity pass \fB\-\-verify\-data\fP, but keep in mind that this requires reading all data and is hence very time consuming. When checking archives of a remote repository, archive checks run on the client machine because they require decrypting data and therefore the encryption key. .UNINDENT .sp Both steps can also be run independently. Pass \fB\-\-repository\-only\fP to run the repository checks only, or pass \fB\-\-archives\-only\fP to run the archive checks only. .sp The \fB\-\-max\-duration\fP option can be used to split a long\-running repository check into multiple partial checks. After the given number of seconds the check is interrupted. The next partial check will continue where the previous one stopped, until the full repository has been checked. Assuming a complete check would take 7 hours, then running a daily check with \fB\-\-max\-duration=3600\fP (1 hour) would result in one full repository check per week. Doing a full repository check aborts any previous partial check; the next partial check will restart from the beginning. With partial repository checks you can run neither archive checks, nor enable repair mode. Consequently, if you want to use \fB\-\-max\-duration\fP you must also pass \fB\-\-repository\-only\fP, and must not pass \fB\-\-archives\-only\fP, nor \fB\-\-repair\fP\&. .sp \fBWarning:\fP Please note that partial repository checks (i.e. running it with \fB\-\-max\-duration\fP) can only perform non\-cryptographic checksum checks on the segment files. A full repository check (i.e. without \fB\-\-max\-duration\fP) can also do a repository index check. Enabling partial repository checks excepts archive checks for the same reason. Therefore partial checks may be useful with very large repositories only where a full check would take too long. .sp The \fB\-\-verify\-data\fP option will perform a full integrity verification (as opposed to checking the CRC32 of the segment) of data, which means reading the data from the repository, decrypting and decompressing it. It is a complete cryptographic verification and hence very time consuming, but will detect any accidental and malicious corruption. Tamper\-resistance is only guaranteed for encrypted repositories against attackers without access to the keys. You can not use \fB\-\-verify\-data\fP with \fB\-\-repository\-only\fP\&. .SS About repair mode .sp The check command is a readonly task by default. If any corruption is found, Borg will report the issue and proceed with checking. To actually repair the issues found, pass \fB\-\-repair\fP\&. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 \fB\-\-repair\fP is a \fBPOTENTIALLY DANGEROUS FEATURE\fP and might lead to data loss! This does not just include data that was previously lost anyway, but might include more data for kinds of corruption it is not capable of dealing with. \fBBE VERY CAREFUL!\fP .UNINDENT .UNINDENT .sp Pursuant to the previous warning it is also highly recommended to test the reliability of the hardware running Borg with stress testing software. This especially includes storage and memory testers. Unreliable hardware might lead to additional data loss. .sp It is highly recommended to create a backup of your repository before running in repair mode (i.e. running it with \fB\-\-repair\fP). .sp Repair mode will attempt to fix any corruptions found. Fixing corruptions does not mean recovering lost data: Borg can not magically restore data lost due to e.g. a hardware failure. Repairing a repository means sacrificing some data for the sake of the repository as a whole and the remaining data. Hence it is, by definition, a potentially lossy task. .sp In practice, repair mode hooks into both the repository and archive checks: .INDENT 0.0 .IP 1. 3 When checking the repository\(aqs consistency, repair mode will try to recover as many objects from segments with integrity errors as possible, and ensure that the index is consistent with the data stored in the segments. .IP 2. 3 When checking the consistency and correctness of archives, repair mode might remove whole archives from the manifest if their archive metadata chunk is corrupt or lost. On a chunk level (i.e. the contents of files), repair mode will replace corrupt or lost chunks with a same\-size replacement chunk of zeroes. If a previously zeroed chunk reappears, repair mode will restore this lost chunk using the new chunk. Lastly, repair mode will also delete orphaned chunks (e.g. caused by read errors while creating the archive). .UNINDENT .sp Most steps taken by repair mode have a one\-time effect on the repository, like removing a lost archive from the repository. However, replacing a corrupt or lost chunk with an all\-zero replacement will have an ongoing effect on the repository: When attempting to extract a file referencing an all\-zero chunk, the \fBextract\fP command will distinctly warn about it. The FUSE filesystem created by the \fBmount\fP command will reject reading such a \(dqzero\-patched\(dq file unless a special mount option is given. .sp As mentioned earlier, Borg might be able to \(dqheal\(dq a \(dqzero\-patched\(dq file in repair mode, if all its previously lost chunks reappear (e.g. via a later backup). This is achieved by Borg not only keeping track of the all\-zero replacement chunks, but also by keeping metadata about the lost chunks. In repair mode Borg will check whether a previously lost chunk reappeared and will replace the all\-zero replacement chunk by the reappeared chunk. If all lost chunks of a \(dqzero\-patched\(dq file reappear, this effectively \(dqheals\(dq the file. Consequently, if lost chunks were repaired earlier, it is advised to run \fB\-\-repair\fP a second time after creating some new backups. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY_OR_ARCHIVE repository or archive to check consistency of .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-repository\-only only perform repository checks .TP .B \-\-archives\-only only perform archives checks .TP .B \-\-verify\-data perform cryptographic archive data integrity verification (conflicts with \fB\-\-repository\-only\fP) .TP .B \-\-repair attempt to repair any inconsistencies found .TP .B \-\-save\-space work slower, but using less space .TP .BI \-\-max\-duration \ SECONDS do only a partial repo check for max. SECONDS seconds (Default: unlimited) .UNINDENT .SS Archive filters .INDENT 0.0 .TP .BI \-P \ PREFIX\fR,\fB \ \-\-prefix \ PREFIX only consider archive names starting with this prefix. (deprecated) .TP .BI \-a \ GLOB\fR,\fB \ \-\-glob\-archives \ GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see \(dqborg help patterns\(dq. .TP .BI \-\-sort\-by \ KEYS Comma\-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp .TP .BI \-\-first \ N consider first N archives after other filters were applied .TP .BI \-\-last \ N consider last N archives after other filters were applied .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-common.10000644000076500000240000000542414601576577016251 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-COMMON" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-common \- Common options of Borg commands .SH SYNOPSIS .INDENT 0.0 .TP .B \-h\fP,\fB \-\-help show this help message and exit .TP .B \-\-critical work on log level CRITICAL .TP .B \-\-error work on log level ERROR .TP .B \-\-warning work on log level WARNING (default) .TP .B \-\-info\fP,\fB \-v\fP,\fB \-\-verbose work on log level INFO .TP .B \-\-debug enable debug output, work on log level DEBUG .TP .BI \-\-debug\-topic \ TOPIC enable TOPIC debugging (can be specified multiple times). The logger path is borg.debug. if TOPIC is not fully qualified. .TP .B \-p\fP,\fB \-\-progress show progress information .TP .B \-\-iec format using IEC units (1KiB = 1024B) .TP .B \-\-log\-json Output one JSON object per log line instead of formatted text. .TP .BI \-\-lock\-wait \ SECONDS wait at most SECONDS for acquiring a repository/cache lock (default: 1). .TP .B \-\-bypass\-lock Bypass locking mechanism .TP .B \-\-show\-version show/log the borg version .TP .B \-\-show\-rc show/log the return code (rc) .TP .BI \-\-umask \ M set umask to M (local only, default: 0077) .TP .BI \-\-remote\-path \ PATH use PATH as borg executable on the remote (default: \(dqborg\(dq) .TP .BI \-\-remote\-ratelimit \ RATE deprecated, use \fB\-\-upload\-ratelimit\fP instead .TP .BI \-\-upload\-ratelimit \ RATE set network upload rate limit in kiByte/s (default: 0=unlimited) .TP .BI \-\-remote\-buffer \ UPLOAD_BUFFER deprecated, use \fB\-\-upload\-buffer\fP instead .TP .BI \-\-upload\-buffer \ UPLOAD_BUFFER set network upload buffer size in MiB. (default: 0=no buffer) .TP .B \-\-consider\-part\-files treat part files like normal files (e.g. to list/extract them) .TP .BI \-\-debug\-profile \ FILE Write execution profile in Borg format into FILE. For local use a Python\-compatible file can be generated by suffixing FILE with \(dq.pyprof\(dq. .TP .BI \-\-rsh \ RSH Use this command to connect to the \(aqborg serve\(aq process (default: \(aqssh\(aq) .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-compact.10000644000076500000240000000534514601576577016411 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-COMPACT" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-compact \- compact segment files in the repository .SH SYNOPSIS .sp borg [common options] compact [options] [REPOSITORY] .SH DESCRIPTION .sp This command frees repository space by compacting segments. .sp Use this regularly to avoid running out of space \- you do not need to use this after each borg command though. It is especially useful after deleting archives, because only compaction will really free repository space. .sp borg compact does not need a key, so it is possible to invoke it from the client or also from the server. .sp Depending on the amount of segments that need compaction, it may take a while, so consider using the \fB\-\-progress\fP option. .sp A segment is compacted if the amount of saved space is above the percentage value given by the \fB\-\-threshold\fP option. If omitted, a threshold of 10% is used. When using \fB\-\-verbose\fP, borg will output an estimate of the freed space. .sp After upgrading borg (server) to 1.2+, you can use \fBborg compact \-\-cleanup\-commits\fP to clean up the numerous 17byte commit\-only segments that borg 1.1 did not clean up due to a bug. It is enough to do that once per repository. After cleaning up the commits, borg will also do a normal compaction. .sp See \fIseparate_compaction\fP in Additional Notes for more details. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY repository to compact .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-cleanup\-commits cleanup commit\-only 17\-byte segment files .TP .BI \-\-threshold \ PERCENT set minimum threshold for saved space in PERCENT (Default: 10) .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # compact segments and free repo disk space $ borg compact /path/to/repo # same as above plus clean up 17byte commit\-only segments $ borg compact \-\-cleanup\-commits /path/to/repo .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-compression.10000644000076500000240000001130014601576577017310 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-COMPRESSION" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-compression \- Details regarding compression .SH DESCRIPTION .sp It is no problem to mix different compression methods in one repo, deduplication is done on the source data chunks (not on the compressed or encrypted data). .sp If some specific chunk was once compressed and stored into the repo, creating another backup that also uses this chunk will not change the stored chunk. So if you use different compression specs for the backups, whichever stores a chunk first determines its compression. See also borg recreate. .sp Compression is lz4 by default. If you want something else, you have to specify what you want. .sp Valid compression specifiers are: .INDENT 0.0 .TP .B none Do not compress. .TP .B lz4 Use lz4 compression. Very high speed, very low compression. (default) .TP .B zstd[,L] Use zstd (\(dqzstandard\(dq) compression, a modern wide\-range algorithm. If you do not explicitly give the compression level L (ranging from 1 to 22), it will use level 3. Archives compressed with zstd are not compatible with borg < 1.1.4. .TP .B zlib[,L] Use zlib (\(dqgz\(dq) compression. Medium speed, medium compression. If you do not explicitly give the compression level L (ranging from 0 to 9), it will use level 6. Giving level 0 (means \(dqno compression\(dq, but still has zlib protocol overhead) is usually pointless, you better use \(dqnone\(dq compression. .TP .B lzma[,L] Use lzma (\(dqxz\(dq) compression. Low speed, high compression. If you do not explicitly give the compression level L (ranging from 0 to 9), it will use level 6. Giving levels above 6 is pointless and counterproductive because it does not compress better due to the buffer size used by borg \- but it wastes lots of CPU cycles and RAM. .TP .B auto,C[,L] Use a built\-in heuristic to decide per chunk whether to compress or not. The heuristic tries with lz4 whether the data is compressible. For incompressible data, it will not use compression (uses \(dqnone\(dq). For compressible data, it uses the given C[,L] compression \- with C[,L] being any valid compression specifier. .TP .B obfuscate,SPEC,C[,L] Use compressed\-size obfuscation to make fingerprinting attacks based on the observable stored chunk size more difficult. Note: .INDENT 7.0 .IP \(bu 2 You must combine this with encryption, or it won\(aqt make any sense. .IP \(bu 2 Your repo size will be bigger, of course. .IP \(bu 2 A chunk is limited by the constant \fBMAX_DATA_SIZE\fP (cur. ~20MiB). .UNINDENT .sp The SPEC value determines how the size obfuscation works: .sp \fIRelative random reciprocal size variation\fP (multiplicative) .sp Size will increase by a factor, relative to the compressed data size. Smaller factors are used often, larger factors rarely. .sp Available factors: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C 1: 0.01 .. 100 2: 0.1 .. 1,000 3: 1 .. 10,000 4: 10 .. 100,000 5: 100 .. 1,000,000 6: 1,000 .. 10,000,000 .ft P .fi .UNINDENT .UNINDENT .sp Example probabilities for SPEC \fB1\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C 90 % 0.01 .. 0.1 9 % 0.1 .. 1 0.9 % 1 .. 10 0.09% 10 .. 100 .ft P .fi .UNINDENT .UNINDENT .sp \fIRandomly sized padding up to the given size\fP (additive) .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C 110: 1kiB (2 ^ (SPEC \- 100)) \&... 120: 1MiB \&... 123: 8MiB (max.) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C borg create \-\-compression lz4 REPO::ARCHIVE data borg create \-\-compression zstd REPO::ARCHIVE data borg create \-\-compression zstd,10 REPO::ARCHIVE data borg create \-\-compression zlib REPO::ARCHIVE data borg create \-\-compression zlib,1 REPO::ARCHIVE data borg create \-\-compression auto,lzma,6 REPO::ARCHIVE data borg create \-\-compression auto,lzma ... borg create \-\-compression obfuscate,110,none ... borg create \-\-compression obfuscate,3,auto,zstd,10 ... borg create \-\-compression obfuscate,2,zstd,6 ... .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-config.10000644000076500000240000000533514601576577016227 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-CONFIG" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-config \- get, set, and delete values in a repository or cache config file .SH SYNOPSIS .sp borg [common options] config [options] [REPOSITORY] [NAME] [VALUE] .SH DESCRIPTION .sp This command gets and sets options in a local repository or cache config file. For security reasons, this command only works on local repositories. .sp To delete a config value entirely, use \fB\-\-delete\fP\&. To list the values of the configuration file or the default values, use \fB\-\-list\fP\&. To get and existing key, pass only the key name. To set a key, pass both the key name and the new value. Keys can be specified in the format \(dqsection.name\(dq or simply \(dqname\(dq; the section will default to \(dqrepository\(dq and \(dqcache\(dq for the repo and cache configs, respectively. .sp By default, borg config manipulates the repository config file. Using \fB\-\-cache\fP edits the repository cache\(aqs config file instead. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY repository to configure .TP .B NAME name of config key .TP .B VALUE new value for key .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-c\fP,\fB \-\-cache get and set values from the repo cache .TP .B \-d\fP,\fB \-\-delete delete the key from the config file .TP .B \-l\fP,\fB \-\-list list the configuration of the repo .UNINDENT .SH EXAMPLES .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The repository & cache config files are some of the only directly manipulable parts of a repository that aren\(aqt versioned or backed up, so be careful when making changes! .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # find cache directory $ cd ~/.cache/borg/$(borg config /path/to/repo id) # reserve some space $ borg config /path/to/repo additional_free_space 2G # make a repo append\-only $ borg config /path/to/repo append_only 1 .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-create.10000644000076500000240000004526714601576577016235 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-CREATE" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-create \- Create new archive .SH SYNOPSIS .sp borg [common options] create [options] ARCHIVE [PATH...] .SH DESCRIPTION .sp This command creates a backup archive containing all files found while recursively traversing all paths specified. Paths are added to the archive as they are given, that means if relative paths are desired, the command has to be run from the correct directory. .sp When giving \(aq\-\(aq as path, borg will read data from standard input and create a file \(aqstdin\(aq in the created archive from that data. In some cases it\(aqs more appropriate to use \-\-content\-from\-command, however. See section \fIReading from stdin\fP below for details. .sp The archive will consume almost no disk space for files or parts of files that have already been stored in other archives. .sp The archive name needs to be unique. It must not end in \(aq.checkpoint\(aq or \(aq.checkpoint.N\(aq (with N being a number), because these names are used for checkpoints and treated in special ways. .sp In the archive name, you may use the following placeholders: {now}, {utcnow}, {fqdn}, {hostname}, {user} and some others. .sp Backup speed is increased by not reprocessing files that are already part of existing archives and weren\(aqt modified. The detection of unmodified files is done by comparing multiple file metadata values with previous values kept in the files cache. .sp This comparison can operate in different modes as given by \fB\-\-files\-cache\fP: .INDENT 0.0 .IP \(bu 2 ctime,size,inode (default) .IP \(bu 2 mtime,size,inode (default behaviour of borg versions older than 1.1.0rc4) .IP \(bu 2 ctime,size (ignore the inode number) .IP \(bu 2 mtime,size (ignore the inode number) .IP \(bu 2 rechunk,ctime (all files are considered modified \- rechunk, cache ctime) .IP \(bu 2 rechunk,mtime (all files are considered modified \- rechunk, cache mtime) .IP \(bu 2 disabled (disable the files cache, all files considered modified \- rechunk) .UNINDENT .sp inode number: better safety, but often unstable on network filesystems .sp Normally, detecting file modifications will take inode information into consideration to improve the reliability of file change detection. This is problematic for files located on sshfs and similar network file systems which do not provide stable inode numbers, such files will always be considered modified. You can use modes without \fIinode\fP in this case to improve performance, but reliability of change detection might be reduced. .sp ctime vs. mtime: safety vs. speed .INDENT 0.0 .IP \(bu 2 ctime is a rather safe way to detect changes to a file (metadata and contents) as it can not be set from userspace. But, a metadata\-only change will already update the ctime, so there might be some unnecessary chunking/hashing even without content changes. Some filesystems do not support ctime (change time). E.g. doing a chown or chmod to a file will change its ctime. .IP \(bu 2 mtime usually works and only updates if file contents were changed. But mtime can be arbitrarily set from userspace, e.g. to set mtime back to the same value it had before a content change happened. This can be used maliciously as well as well\-meant, but in both cases mtime based cache modes can be problematic. .UNINDENT .sp The mount points of filesystems or filesystem snapshots should be the same for every creation of a new archive to ensure fast operation. This is because the file cache that is used to determine changed files quickly uses absolute filenames. If this is not possible, consider creating a bind mount to a stable location. .sp The \fB\-\-progress\fP option shows (from left to right) Original, Compressed and Deduplicated (O, C and D, respectively), then the Number of files (N) processed so far, followed by the currently processed path. .sp When using \fB\-\-stats\fP, you will get some statistics about how much data was added \- the \(dqThis Archive\(dq deduplicated size there is most interesting as that is how much your repository will grow. Please note that the \(dqAll archives\(dq stats refer to the state after creation. Also, the \fB\-\-stats\fP and \fB\-\-dry\-run\fP options are mutually exclusive because the data is not actually compressed and deduplicated during a dry run. .sp For more help on include/exclude patterns, see the \fIborg_patterns\fP command output. .sp For more help on placeholders, see the \fIborg_placeholders\fP command output. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B ARCHIVE name of archive to create (must be also a valid directory name) .TP .B PATH paths to archive .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-n\fP,\fB \-\-dry\-run do not create a backup archive .TP .B \-s\fP,\fB \-\-stats print statistics for the created archive .TP .B \-\-list output verbose list of items (files, dirs, ...) .TP .BI \-\-filter \ STATUSCHARS only display items with the given status characters (see description) .TP .B \-\-json output stats as JSON. Implies \fB\-\-stats\fP\&. .TP .B \-\-no\-cache\-sync experimental: do not synchronize the cache. Implies not using the files cache. .TP .BI \-\-stdin\-name \ NAME use NAME in archive for stdin data (default: \(aqstdin\(aq) .TP .BI \-\-stdin\-user \ USER set user USER in archive for stdin data (default: \(aqroot\(aq) .TP .BI \-\-stdin\-group \ GROUP set group GROUP in archive for stdin data (default: \(aqwheel\(aq) .TP .BI \-\-stdin\-mode \ M set mode to M in archive for stdin data (default: 0660) .TP .B \-\-content\-from\-command interpret PATH as command and store its stdout. See also section Reading from stdin below. .TP .B \-\-paths\-from\-stdin read DELIM\-separated list of paths to backup from stdin. All control is external: it will back up all files given \- no more, no less. .TP .B \-\-paths\-from\-command interpret PATH as command and treat its output as \fB\-\-paths\-from\-stdin\fP .TP .BI \-\-paths\-delimiter \ DELIM set path delimiter for \fB\-\-paths\-from\-stdin\fP and \fB\-\-paths\-from\-command\fP (default: \fB\en\fP) .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .TP .B \-\-exclude\-caches exclude directories that contain a CACHEDIR.TAG file (\fI\%http://www.bford.info/cachedir/spec.html\fP) .TP .BI \-\-exclude\-if\-present \ NAME exclude directories that are tagged by containing a filesystem object with the given NAME .TP .B \-\-keep\-exclude\-tags if tag objects are specified with \fB\-\-exclude\-if\-present\fP, don\(aqt omit the tag objects themselves from the backup archive .TP .B \-\-exclude\-nodump exclude files flagged NODUMP .UNINDENT .SS Filesystem options .INDENT 0.0 .TP .B \-x\fP,\fB \-\-one\-file\-system stay in the same file system and do not store mount points of other file systems \- this might behave different from your expectations, see the description below. .TP .B \-\-numeric\-owner deprecated, use \fB\-\-numeric\-ids\fP instead .TP .B \-\-numeric\-ids only store numeric user and group identifiers .TP .B \-\-noatime do not store atime into archive .TP .B \-\-atime do store atime into archive .TP .B \-\-noctime do not store ctime into archive .TP .B \-\-nobirthtime do not store birthtime (creation date) into archive .TP .B \-\-nobsdflags deprecated, use \fB\-\-noflags\fP instead .TP .B \-\-noflags do not read and store flags (e.g. NODUMP, IMMUTABLE) into archive .TP .B \-\-noacls do not read and store ACLs into archive .TP .B \-\-noxattrs do not read and store xattrs into archive .TP .B \-\-sparse detect sparse holes in input (supported only by fixed chunker) .TP .BI \-\-files\-cache \ MODE operate files cache in MODE. default: ctime,size,inode .TP .B \-\-read\-special open and read block and char device files as well as FIFOs as if they were regular files. Also follows symlinks pointing to these kinds of files. .UNINDENT .SS Archive options .INDENT 0.0 .TP .BI \-\-comment \ COMMENT add a comment text to the archive .TP .BI \-\-timestamp \ TIMESTAMP manually specify the archive creation date/time (UTC, yyyy\-mm\-ddThh:mm:ss format). Alternatively, give a reference file/directory. .TP .BI \-c \ SECONDS\fR,\fB \ \-\-checkpoint\-interval \ SECONDS write checkpoint every SECONDS seconds (Default: 1800) .TP .BI \-\-chunker\-params \ PARAMS specify the chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, HASH_MASK_BITS, HASH_WINDOW_SIZE). default: buzhash,19,23,21,4095 .TP .BI \-C \ COMPRESSION\fR,\fB \ \-\-compression \ COMPRESSION select compression algorithm, see the output of the \(dqborg help compression\(dq command for details. .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Backup ~/Documents into an archive named \(dqmy\-documents\(dq $ borg create /path/to/repo::my\-documents ~/Documents # same, but list all files as we process them $ borg create \-\-list /path/to/repo::my\-documents ~/Documents # Backup ~/Documents and ~/src but exclude pyc files $ borg create /path/to/repo::my\-files \e ~/Documents \e ~/src \e \-\-exclude \(aq*.pyc\(aq # Backup home directories excluding image thumbnails (i.e. only # /home//.thumbnails is excluded, not /home/*/*/.thumbnails etc.) $ borg create /path/to/repo::my\-files /home \e \-\-exclude \(aqsh:home/*/.thumbnails\(aq # Backup the root filesystem into an archive named \(dqroot\-YYYY\-MM\-DD\(dq # use zlib compression (good, but slow) \- default is lz4 (fast, low compression ratio) $ borg create \-C zlib,6 \-\-one\-file\-system /path/to/repo::root\-{now:%Y\-%m\-%d} / # Backup onto a remote host (\(dqpush\(dq style) via ssh to port 2222, # logging in as user \(dqborg\(dq and storing into /path/to/repo $ borg create ssh://borg@backup.example.org:2222/path/to/repo::{fqdn}\-root\-{now} / # Backup a remote host locally (\(dqpull\(dq style) using sshfs $ mkdir sshfs\-mount $ sshfs root@example.com:/ sshfs\-mount $ cd sshfs\-mount $ borg create /path/to/repo::example.com\-root\-{now:%Y\-%m\-%d} . $ cd .. $ fusermount \-u sshfs\-mount # Make a big effort in fine granular deduplication (big chunk management # overhead, needs a lot of RAM and disk space, see formula in internals # docs \- same parameters as borg < 1.0 or attic): $ borg create \-\-chunker\-params buzhash,10,23,16,4095 /path/to/repo::small /smallstuff # Backup a raw device (must not be active/in use/mounted at that time) $ borg create \-\-read\-special \-\-chunker\-params fixed,4194304 /path/to/repo::my\-sdx /dev/sdX # Backup a sparse disk image (must not be active/in use/mounted at that time) $ borg create \-\-sparse \-\-chunker\-params fixed,4194304 /path/to/repo::my\-disk my\-disk.raw # No compression (none) $ borg create \-\-compression none /path/to/repo::arch ~ # Super fast, low compression (lz4, default) $ borg create /path/to/repo::arch ~ # Less fast, higher compression (zlib, N = 0..9) $ borg create \-\-compression zlib,N /path/to/repo::arch ~ # Even slower, even higher compression (lzma, N = 0..9) $ borg create \-\-compression lzma,N /path/to/repo::arch ~ # Only compress compressible data with lzma,N (N = 0..9) $ borg create \-\-compression auto,lzma,N /path/to/repo::arch ~ # Use short hostname, user name and current time in archive name $ borg create /path/to/repo::{hostname}\-{user}\-{now} ~ # Similar, use the same datetime format that is default as of borg 1.1 $ borg create /path/to/repo::{hostname}\-{user}\-{now:%Y\-%m\-%dT%H:%M:%S} ~ # As above, but add nanoseconds $ borg create /path/to/repo::{hostname}\-{user}\-{now:%Y\-%m\-%dT%H:%M:%S.%f} ~ # Backing up relative paths by moving into the correct directory first $ cd /home/user/Documents # The root directory of the archive will be \(dqprojectA\(dq $ borg create /path/to/repo::daily\-projectA\-{now:%Y\-%m\-%d} projectA # Use external command to determine files to archive # Use \-\-paths\-from\-stdin with find to only backup files less than 1MB in size $ find ~ \-size \-1000k | borg create \-\-paths\-from\-stdin /path/to/repo::small\-files\-only # Use \-\-paths\-from\-command with find to only backup files from a given user $ borg create \-\-paths\-from\-command /path/to/repo::joes\-files \-\- find /srv/samba/shared \-user joe # Use \-\-paths\-from\-stdin with \-\-paths\-delimiter (for example, for filenames with newlines in them) $ find ~ \-size \-1000k \-print0 | borg create \e \-\-paths\-from\-stdin \e \-\-paths\-delimiter \(dq\e0\(dq \e /path/to/repo::smallfiles\-handle\-newline .ft P .fi .UNINDENT .UNINDENT .SH NOTES .sp The \fB\-\-exclude\fP patterns are not like tar. In tar \fB\-\-exclude\fP .bundler/gems will exclude foo/.bundler/gems. In borg it will not, you need to use \fB\-\-exclude\fP \(aq*/.bundler/gems\(aq to get the same effect. .sp In addition to using \fB\-\-exclude\fP patterns, it is possible to use \fB\-\-exclude\-if\-present\fP to specify the name of a filesystem object (e.g. a file or folder name) which, when contained within another folder, will prevent the containing folder from being backed up. By default, the containing folder and all of its contents will be omitted from the backup. If, however, you wish to only include the objects specified by \fB\-\-exclude\-if\-present\fP in your backup, and not include any other contents of the containing folder, this can be enabled through using the \fB\-\-keep\-exclude\-tags\fP option. .sp The \fB\-x\fP or \fB\-\-one\-file\-system\fP option excludes directories, that are mountpoints (and everything in them). It detects mountpoints by comparing the device number from the output of \fBstat()\fP of the directory and its parent directory. Specifically, it excludes directories for which \fBstat()\fP reports a device number different from the device number of their parent. In general: be aware that there are directories with device number different from their parent, which the kernel does not consider a mountpoint and also the other way around. Linux examples for this are bind mounts (possibly same device number, but always a mountpoint) and ALL subvolumes of a btrfs (different device number from parent but not necessarily a mountpoint). macOS examples are the apfs mounts of a typical macOS installation. Therefore, when using \fB\-\-one\-file\-system\fP, you should double\-check that the backup works as intended. .SS Item flags .sp \fB\-\-list\fP outputs a list of all files, directories and other file system items it considered (no matter whether they had content changes or not). For each item, it prefixes a single\-letter flag that indicates type and/or status of the item. .sp If you are interested only in a subset of that output, you can give e.g. \fB\-\-filter=AME\fP and it will only show regular files with A, M or E status (see below). .sp A uppercase character represents the status of a regular file relative to the \(dqfiles\(dq cache (not relative to the repo \-\- this is an issue if the files cache is not used). Metadata is stored in any case and for \(aqA\(aq and \(aqM\(aq also new data chunks are stored. For \(aqU\(aq all data chunks refer to already existing chunks. .INDENT 0.0 .IP \(bu 2 \(aqA\(aq = regular file, added (see also \fIa_status_oddity\fP in the FAQ) .IP \(bu 2 \(aqM\(aq = regular file, modified .IP \(bu 2 \(aqU\(aq = regular file, unchanged .IP \(bu 2 \(aqC\(aq = regular file, it changed while we backed it up .IP \(bu 2 \(aqE\(aq = regular file, an error happened while accessing/reading \fIthis\fP file .UNINDENT .sp A lowercase character means a file type other than a regular file, borg usually just stores their metadata: .INDENT 0.0 .IP \(bu 2 \(aqd\(aq = directory .IP \(bu 2 \(aqb\(aq = block device .IP \(bu 2 \(aqc\(aq = char device .IP \(bu 2 \(aqh\(aq = regular file, hardlink (to already seen inodes) .IP \(bu 2 \(aqs\(aq = symlink .IP \(bu 2 \(aqf\(aq = fifo .UNINDENT .sp Other flags used include: .INDENT 0.0 .IP \(bu 2 \(aqi\(aq = backup data was read from standard input (stdin) .IP \(bu 2 \(aq\-\(aq = dry run, item was \fInot\fP backed up .IP \(bu 2 \(aqx\(aq = excluded, item was \fInot\fP backed up .IP \(bu 2 \(aq?\(aq = missing status code (if you see this, please file a bug report!) .UNINDENT .SS Reading from stdin .sp There are two methods to read from stdin. Either specify \fB\-\fP as path and pipe directly to borg: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C backup\-vm \-\-id myvm \-\-stdout | borg create REPO::ARCHIVE \- .ft P .fi .UNINDENT .UNINDENT .sp Or use \fB\-\-content\-from\-command\fP to have Borg manage the execution of the command and piping. If you do so, the first PATH argument is interpreted as command to execute and any further arguments are treated as arguments to the command: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C borg create \-\-content\-from\-command REPO::ARCHIVE \-\- backup\-vm \-\-id myvm \-\-stdout .ft P .fi .UNINDENT .UNINDENT .sp \fB\-\-\fP is used to ensure \fB\-\-id\fP and \fB\-\-stdout\fP are \fBnot\fP considered arguments to \fBborg\fP but rather \fBbackup\-vm\fP\&. .sp The difference between the two approaches is that piping to borg creates an archive even if the command piping to borg exits with a failure. In this case, \fBone can end up with truncated output being backed up\fP\&. Using \fB\-\-content\-from\-command\fP, in contrast, borg is guaranteed to fail without creating an archive should the command fail. The command is considered failed when it returned a non\-zero exit code. .sp Reading from stdin yields just a stream of data without file metadata associated with it, and the files cache is not needed at all. So it is safe to disable it via \fB\-\-files\-cache disabled\fP and speed up backup creation a bit. .sp By default, the content read from stdin is stored in a file called \(aqstdin\(aq. Use \fB\-\-stdin\-name\fP to change the name. .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-delete(1)\fP, \fIborg\-prune(1)\fP, \fIborg\-check(1)\fP, \fIborg\-patterns(1)\fP, \fIborg\-placeholders(1)\fP, \fIborg\-compression(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-delete.10000644000076500000240000001150714601576577016222 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-DELETE" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-delete \- Delete an existing repository or archives .SH SYNOPSIS .sp borg [common options] delete [options] [REPOSITORY_OR_ARCHIVE] [ARCHIVE...] .SH DESCRIPTION .sp This command deletes an archive from the repository or the complete repository. .sp Important: When deleting archives, repository disk space is \fBnot\fP freed until you run \fBborg compact\fP\&. .sp When you delete a complete repository, the security info and local cache for it (if any) are also deleted. Alternatively, you can delete just the local cache with the \fB\-\-cache\-only\fP option, or keep the security info with the \fB\-\-keep\-security\-info\fP option. .sp When in doubt, use \fB\-\-dry\-run \-\-list\fP to see what would be deleted. .sp When using \fB\-\-stats\fP, you will get some statistics about how much data was deleted \- the \(dqDeleted data\(dq deduplicated size there is most interesting as that is how much your repository will shrink. Please note that the \(dqAll archives\(dq stats refer to the state after deletion. .sp You can delete multiple archives by specifying a shell pattern to match multiple archives using the \fB\-\-glob\-archives GLOB\fP option (for more info on these patterns, see \fIborg_patterns\fP). .sp To avoid accidentally deleting archives, especially when using glob patterns, it might be helpful to use the \fB\-\-dry\-run\fP to test out the command without actually making any changes to the repository. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY_OR_ARCHIVE repository or archive to delete .TP .B ARCHIVE archives to delete .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-n\fP,\fB \-\-dry\-run do not change repository .TP .B \-\-list output verbose list of archives .TP .B \-s\fP,\fB \-\-stats print statistics for the deleted archive .TP .B \-\-cache\-only delete only the local cache for the given repository .TP .B \-\-force force deletion of corrupted archives, use \fB\-\-force \-\-force\fP in case \fB\-\-force\fP does not work. .TP .B \-\-keep\-security\-info keep the local security info when deleting a repository .TP .B \-\-save\-space work slower, but using less space .TP .BI \-c \ SECONDS\fR,\fB \ \-\-checkpoint\-interval \ SECONDS write checkpoint every SECONDS seconds (Default: 1800) .UNINDENT .SS Archive filters .INDENT 0.0 .TP .BI \-P \ PREFIX\fR,\fB \ \-\-prefix \ PREFIX only consider archive names starting with this prefix. (deprecated) .TP .BI \-a \ GLOB\fR,\fB \ \-\-glob\-archives \ GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see \(dqborg help patterns\(dq. .TP .BI \-\-sort\-by \ KEYS Comma\-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp .TP .BI \-\-first \ N consider first N archives after other filters were applied .TP .BI \-\-last \ N consider last N archives after other filters were applied .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # delete a single backup archive: $ borg delete /path/to/repo::Monday # actually free disk space: $ borg compact /path/to/repo # delete all archives whose names begin with the machine\(aqs hostname followed by \(dq\-\(dq $ borg delete \-\-glob\-archives \(aq{hostname}\-*\(aq /path/to/repo # delete all archives whose names contain \(dq\-2012\-\(dq $ borg delete \-\-glob\-archives \(aq*\-2012\-*\(aq /path/to/repo # see what would be deleted if delete was run without \-\-dry\-run $ borg delete \-\-list \-\-dry\-run \-a \(aq*\-May\-*\(aq /path/to/repo # delete the whole repository and the related local cache: $ borg delete /path/to/repo You requested to completely DELETE the repository *including* all archives it contains: repo Mon, 2016\-02\-15 19:26:54 root\-2016\-02\-15 Mon, 2016\-02\-15 19:36:29 newname Mon, 2016\-02\-15 19:50:19 Type \(aqYES\(aq if you understand this and want to continue: YES .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-compact(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-diff.10000644000076500000240000001054614601576577015672 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-DIFF" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-diff \- Diff contents of two archives .SH SYNOPSIS .sp borg [common options] diff [options] REPO::ARCHIVE1 ARCHIVE2 [PATH...] .SH DESCRIPTION .sp This command finds differences (file contents, user/group/mode) between archives. .sp A repository location and an archive name must be specified for REPO::ARCHIVE1. ARCHIVE2 is just another archive name in same repository (no repository location allowed). .sp For archives created with Borg 1.1 or newer diff automatically detects whether the archives are created with the same chunker params. If so, only chunk IDs are compared, which is very fast. .sp For archives prior to Borg 1.1 chunk contents are compared by default. If you did not create the archives with different chunker params, pass \fB\-\-same\-chunker\-params\fP\&. Note that the chunker params changed from Borg 0.xx to 1.0. .sp For more help on include/exclude patterns, see the \fIborg_patterns\fP command output. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPO::ARCHIVE1 repository location and ARCHIVE1 name .TP .B ARCHIVE2 ARCHIVE2 name (no repository location allowed) .TP .B PATH paths of items inside the archives to compare; patterns are supported .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-numeric\-owner deprecated, use \fB\-\-numeric\-ids\fP instead .TP .B \-\-numeric\-ids only consider numeric user and group identifiers .TP .B \-\-same\-chunker\-params Override check of chunker parameters. .TP .B \-\-sort Sort the output lines by file path. .TP .B \-\-content\-only Only compare differences in content (exclude metadata differences) .TP .B \-\-json\-lines Format output as JSON Lines. .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ borg init \-e=none testrepo $ mkdir testdir $ cd testdir $ echo asdf > file1 $ dd if=/dev/urandom bs=1M count=4 > file2 $ touch file3 $ borg create ../testrepo::archive1 . $ chmod a+x file1 $ echo \(dqsomething\(dq >> file2 $ borg create ../testrepo::archive2 . $ echo \(dqtesting 123\(dq >> file1 $ rm file3 $ touch file4 $ borg create ../testrepo::archive3 . $ cd .. $ borg diff testrepo::archive1 archive2 [\-rw\-r\-\-r\-\- \-> \-rwxr\-xr\-x] file1 +135 B \-252 B file2 $ borg diff testrepo::archive2 archive3 +17 B \-5 B file1 added 0 B file4 removed 0 B file3 $ borg diff testrepo::archive1 archive3 +17 B \-5 B [\-rw\-r\-\-r\-\- \-> \-rwxr\-xr\-x] file1 +135 B \-252 B file2 added 0 B file4 removed 0 B file3 $ borg diff \-\-json\-lines testrepo::archive1 archive3 {\(dqpath\(dq: \(dqfile1\(dq, \(dqchanges\(dq: [{\(dqtype\(dq: \(dqmodified\(dq, \(dqadded\(dq: 17, \(dqremoved\(dq: 5}, {\(dqtype\(dq: \(dqmode\(dq, \(dqold_mode\(dq: \(dq\-rw\-r\-\-r\-\-\(dq, \(dqnew_mode\(dq: \(dq\-rwxr\-xr\-x\(dq}]} {\(dqpath\(dq: \(dqfile2\(dq, \(dqchanges\(dq: [{\(dqtype\(dq: \(dqmodified\(dq, \(dqadded\(dq: 135, \(dqremoved\(dq: 252}]} {\(dqpath\(dq: \(dqfile4\(dq, \(dqchanges\(dq: [{\(dqtype\(dq: \(dqadded\(dq, \(dqsize\(dq: 0}]} {\(dqpath\(dq: \(dqfile3\(dq, \(dqchanges\(dq: [{\(dqtype\(dq: \(dqremoved\(dq, \(dqsize\(dq: 0}] .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-export-tar.10000644000076500000240000001025314601576577017062 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-EXPORT-TAR" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-export-tar \- Export archive contents as a tarball .SH SYNOPSIS .sp borg [common options] export\-tar [options] ARCHIVE FILE [PATH...] .SH DESCRIPTION .sp This command creates a tarball from an archive. .sp When giving \(aq\-\(aq as the output FILE, Borg will write a tar stream to standard output. .sp By default (\fB\-\-tar\-filter=auto\fP) Borg will detect whether the FILE should be compressed based on its file extension and pipe the tarball through an appropriate filter before writing it to FILE: .INDENT 0.0 .IP \(bu 2 \&.tar.gz or .tgz: gzip .IP \(bu 2 \&.tar.bz2 or .tbz: bzip2 .IP \(bu 2 \&.tar.xz or .txz: xz .IP \(bu 2 \&.tar.zstd or .tar.zst: zstd .IP \(bu 2 \&.tar.lz4: lz4 .UNINDENT .sp Alternatively, a \fB\-\-tar\-filter\fP program may be explicitly specified. It should read the uncompressed tar stream from stdin and write a compressed/filtered tar stream to stdout. .sp The generated tarball uses the GNU tar format. .sp export\-tar is a lossy conversion: BSD flags, ACLs, extended attributes (xattrs), atime and ctime are not exported. Timestamp resolution is limited to whole seconds, not the nanosecond resolution otherwise supported by Borg. .sp A \fB\-\-sparse\fP option (as found in borg extract) is not supported. .sp By default the entire archive is extracted but a subset of files and directories can be selected by passing a list of \fBPATHs\fP as arguments. The file selection can further be restricted by using the \fB\-\-exclude\fP option. .sp For more help on include/exclude patterns, see the \fIborg_patterns\fP command output. .sp \fB\-\-progress\fP can be slower than no progress display, since it makes one additional pass over the archive metadata. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B ARCHIVE archive to export .TP .B FILE output tar file. \(dq\-\(dq to write to stdout instead. .TP .B PATH paths to extract; patterns are supported .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-tar\-filter filter program to pipe data through .TP .B \-\-list output verbose list of items (files, dirs, ...) .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .TP .BI \-\-strip\-components \ NUMBER Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # export as uncompressed tar $ borg export\-tar /path/to/repo::Monday Monday.tar # exclude some types, compress using gzip $ borg export\-tar /path/to/repo::Monday Monday.tar.gz \-\-exclude \(aq*.so\(aq # use higher compression level with gzip $ borg export\-tar \-\-tar\-filter=\(dqgzip \-9\(dq testrepo::linux Monday.tar.gz # export a tar, but instead of storing it on disk, # upload it to a remote site using curl. $ borg export\-tar /path/to/repo::Monday \- | curl \-\-data\-binary @\- https://somewhere/to/POST # remote extraction via \(dqtarpipe\(dq $ borg export\-tar /path/to/repo::Monday \- | ssh somewhere \(dqcd extracted; tar x\(dq .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-extract.10000644000076500000240000001036714601576577016435 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-EXTRACT" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-extract \- Extract archive contents .SH SYNOPSIS .sp borg [common options] extract [options] ARCHIVE [PATH...] .SH DESCRIPTION .sp This command extracts the contents of an archive. By default the entire archive is extracted but a subset of files and directories can be selected by passing a list of \fBPATHs\fP as arguments. The file selection can further be restricted by using the \fB\-\-exclude\fP option. .sp For more help on include/exclude patterns, see the \fIborg_patterns\fP command output. .sp By using \fB\-\-dry\-run\fP, you can do all extraction steps except actually writing the output data: reading metadata and data chunks from the repo, checking the hash/hmac, decrypting, decompressing. .sp \fB\-\-progress\fP can be slower than no progress display, since it makes one additional pass over the archive metadata. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Currently, extract always writes into the current working directory (\(dq.\(dq), so make sure you \fBcd\fP to the right place before calling \fBborg extract\fP\&. .sp When parent directories are not extracted (because of using file/directory selection or any other reason), borg can not restore parent directories\(aq metadata, e.g. owner, group, permission, etc. .UNINDENT .UNINDENT .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B ARCHIVE archive to extract .TP .B PATH paths to extract; patterns are supported .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-list output verbose list of items (files, dirs, ...) .TP .B \-n\fP,\fB \-\-dry\-run do not actually change any files .TP .B \-\-numeric\-owner deprecated, use \fB\-\-numeric\-ids\fP instead .TP .B \-\-numeric\-ids only obey numeric user and group identifiers .TP .B \-\-nobsdflags deprecated, use \fB\-\-noflags\fP instead .TP .B \-\-noflags do not extract/set flags (e.g. NODUMP, IMMUTABLE) .TP .B \-\-noacls do not extract/set ACLs .TP .B \-\-noxattrs do not extract/set xattrs .TP .B \-\-stdout write all extracted data to stdout .TP .B \-\-sparse create holes in output sparse file from all\-zero chunks .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .TP .BI \-\-strip\-components \ NUMBER Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Extract entire archive $ borg extract /path/to/repo::my\-files # Extract entire archive and list files while processing $ borg extract \-\-list /path/to/repo::my\-files # Verify whether an archive could be successfully extracted, but do not write files to disk $ borg extract \-\-dry\-run /path/to/repo::my\-files # Extract the \(dqsrc\(dq directory $ borg extract /path/to/repo::my\-files home/USERNAME/src # Extract the \(dqsrc\(dq directory but exclude object files $ borg extract /path/to/repo::my\-files home/USERNAME/src \-\-exclude \(aq*.o\(aq # Restore a raw device (must not be active/in use/mounted at that time) $ borg extract \-\-stdout /path/to/repo::my\-sdx | dd of=/dev/sdx bs=10M .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-mount(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-import-tar.10000644000076500000240000000751614601576577017063 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-IMPORT-TAR" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-import-tar \- Create a backup archive from a tarball .SH SYNOPSIS .sp borg [common options] import\-tar [options] ARCHIVE TARFILE .SH DESCRIPTION .sp This command creates a backup archive from a tarball. .sp When giving \(aq\-\(aq as path, Borg will read a tar stream from standard input. .sp By default (\-\-tar\-filter=auto) Borg will detect whether the file is compressed based on its file extension and pipe the file through an appropriate filter: .INDENT 0.0 .IP \(bu 2 \&.tar.gz or .tgz: gzip \-d .IP \(bu 2 \&.tar.bz2 or .tbz: bzip2 \-d .IP \(bu 2 \&.tar.xz or .txz: xz \-d .IP \(bu 2 \&.tar.zstd or .tar.zst: zstd \-d .IP \(bu 2 \&.tar.lz4: lz4 \-d .UNINDENT .sp Alternatively, a \-\-tar\-filter program may be explicitly specified. It should read compressed data from stdin and output an uncompressed tar stream on stdout. .sp Most documentation of borg create applies. Note that this command does not support excluding files. .sp import\-tar is a lossy conversion: BSD flags, ACLs, extended attributes (xattrs), atime and ctime are not exported. Timestamp resolution is limited to whole seconds, not the nanosecond resolution otherwise supported by Borg. .sp A \fB\-\-sparse\fP option (as found in borg create) is not supported. .sp import\-tar reads POSIX.1\-1988 (ustar), POSIX.1\-2001 (pax), GNU tar, UNIX V7 tar and SunOS tar with extended attributes. .sp To import multiple tarballs into a single archive, they can be simply concatenated (e.g. using \(dqcat\(dq) into a single file, and imported with an \fB\-\-ignore\-zeros\fP option to skip through the stop markers between them. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B ARCHIVE name of archive to create (must be also a valid directory name) .TP .B TARFILE input tar file. \(dq\-\(dq to read from stdin instead. .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-tar\-filter filter program to pipe data through .TP .B \-s\fP,\fB \-\-stats print statistics for the created archive .TP .B \-\-list output verbose list of items (files, dirs, ...) .TP .BI \-\-filter \ STATUSCHARS only display items with the given status characters .TP .B \-\-json output stats as JSON (implies \-\-stats) .TP .B \-\-ignore\-zeros ignore zero\-filled blocks in the input tarball .UNINDENT .SS Archive options .INDENT 0.0 .TP .BI \-\-comment \ COMMENT add a comment text to the archive .TP .BI \-\-timestamp \ TIMESTAMP manually specify the archive creation date/time (UTC, yyyy\-mm\-ddThh:mm:ss format). alternatively, give a reference file/directory. .TP .BI \-c \ SECONDS\fR,\fB \ \-\-checkpoint\-interval \ SECONDS write checkpoint every SECONDS seconds (Default: 1800) .TP .BI \-\-chunker\-params \ PARAMS specify the chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, HASH_MASK_BITS, HASH_WINDOW_SIZE). default: buzhash,19,23,21,4095 .TP .BI \-C \ COMPRESSION\fR,\fB \ \-\-compression \ COMPRESSION select compression algorithm, see the output of the \(dqborg help compression\(dq command for details. .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-info.10000644000076500000240000001272114601576577015712 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-INFO" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-info \- Show archive details such as disk space used .SH SYNOPSIS .sp borg [common options] info [options] [REPOSITORY_OR_ARCHIVE] .SH DESCRIPTION .sp This command displays detailed information about the specified archive or repository. .sp Please note that the deduplicated sizes of the individual archives do not add up to the deduplicated size of the repository (\(dqall archives\(dq), because the two are meaning different things: .sp This archive / deduplicated size = amount of data stored ONLY for this archive = unique chunks of this archive. All archives / deduplicated size = amount of data stored in the repo = all chunks in the repository. .sp Borg archives can only contain a limited amount of file metadata. The size of an archive relative to this limit depends on a number of factors, mainly the number of files, the lengths of paths and other metadata stored for files. This is shown as \fIutilization of maximum supported archive size\fP\&. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY_OR_ARCHIVE repository or archive to display information about .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-json format output as JSON .UNINDENT .SS Archive filters .INDENT 0.0 .TP .BI \-P \ PREFIX\fR,\fB \ \-\-prefix \ PREFIX only consider archive names starting with this prefix. (deprecated) .TP .BI \-a \ GLOB\fR,\fB \ \-\-glob\-archives \ GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see \(dqborg help patterns\(dq. .TP .BI \-\-sort\-by \ KEYS Comma\-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp .TP .BI \-\-first \ N consider first N archives after other filters were applied .TP .BI \-\-last \ N consider last N archives after other filters were applied .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ borg info /path/to/repo::2017\-06\-29T11:00\-srv Archive name: 2017\-06\-29T11:00\-srv Archive fingerprint: b2f1beac2bd553b34e06358afa45a3c1689320d39163890c5bbbd49125f00fe5 Comment: Hostname: myhostname Username: root Time (start): Thu, 2017\-06\-29 11:03:07 Time (end): Thu, 2017\-06\-29 11:03:13 Duration: 5.66 seconds Number of files: 17037 Command line: /usr/sbin/borg create /path/to/repo::2017\-06\-29T11:00\-srv /srv Utilization of max. archive size: 0% \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Original size Compressed size Deduplicated size This archive: 12.53 GB 12.49 GB 1.62 kB All archives: 121.82 TB 112.41 TB 215.42 GB Unique chunks Total chunks Chunk index: 1015213 626934122 $ borg info /path/to/repo \-\-last 1 Archive name: 2017\-06\-29T11:00\-srv Archive fingerprint: b2f1beac2bd553b34e06358afa45a3c1689320d39163890c5bbbd49125f00fe5 Comment: Hostname: myhostname Username: root Time (start): Thu, 2017\-06\-29 11:03:07 Time (end): Thu, 2017\-06\-29 11:03:13 Duration: 5.66 seconds Number of files: 17037 Command line: /usr/sbin/borg create /path/to/repo::2017\-06\-29T11:00\-srv /srv Utilization of max. archive size: 0% \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Original size Compressed size Deduplicated size This archive: 12.53 GB 12.49 GB 1.62 kB All archives: 121.82 TB 112.41 TB 215.42 GB Unique chunks Total chunks Chunk index: 1015213 626934122 $ borg info /path/to/repo Repository ID: d857ce5788c51272c61535062e89eac4e8ef5a884ffbe976e0af9d8765dedfa5 Location: /path/to/repo Encrypted: Yes (repokey) Cache: /root/.cache/borg/d857ce5788c51272c61535062e89eac4e8ef5a884ffbe976e0af9d8765dedfa5 Security dir: /root/.config/borg/security/d857ce5788c51272c61535062e89eac4e8ef5a884ffbe976e0af9d8765dedfa5 \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Original size Compressed size Deduplicated size All archives: 121.82 TB 112.41 TB 215.42 GB Unique chunks Total chunks Chunk index: 1015213 626934122 .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-list(1)\fP, \fIborg\-diff(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-init.10000644000076500000240000002134414601576577015723 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-INIT" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-init \- Initialize an empty repository .SH SYNOPSIS .sp borg [common options] init [options] [REPOSITORY] .SH DESCRIPTION .sp This command initializes an empty repository. A repository is a filesystem directory containing the deduplicated data from zero or more archives. .SS Encryption mode TLDR .sp The encryption mode can only be configured when creating a new repository \- you can neither configure it on a per\-archive basis nor change the encryption mode of an existing repository. .sp Use \fBrepokey\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C borg init \-\-encryption repokey /path/to/repo .ft P .fi .UNINDENT .UNINDENT .sp Or \fBrepokey\-blake2\fP depending on which is faster on your client machines (see below): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C borg init \-\-encryption repokey\-blake2 /path/to/repo .ft P .fi .UNINDENT .UNINDENT .sp Borg will: .INDENT 0.0 .IP 1. 3 Ask you to come up with a passphrase. .IP 2. 3 Create a borg key (which contains 3 random secrets. See \fIkey_files\fP). .IP 3. 3 Encrypt the key with your passphrase. .IP 4. 3 Store the encrypted borg key inside the repository directory (in the repo config). This is why it is essential to use a secure passphrase. .IP 5. 3 Encrypt and sign your backups to prevent anyone from reading or forging them unless they have the key and know the passphrase. Make sure to keep a backup of your key \fBoutside\fP the repository \- do not lock yourself out by \(dqleaving your keys inside your car\(dq (see \fIborg_key_export\fP). For remote backups the encryption is done locally \- the remote machine never sees your passphrase, your unencrypted key or your unencrypted files. Chunking and id generation are also based on your key to improve your privacy. .IP 6. 3 Use the key when extracting files to decrypt them and to verify that the contents of the backups have not been accidentally or maliciously altered. .UNINDENT .SS Picking a passphrase .sp Make sure you use a good passphrase. Not too short, not too simple. The real encryption / decryption key is encrypted with / locked by your passphrase. If an attacker gets your key, he can\(aqt unlock and use it without knowing the passphrase. .sp Be careful with special or non\-ascii characters in your passphrase: .INDENT 0.0 .IP \(bu 2 Borg processes the passphrase as unicode (and encodes it as utf\-8), so it does not have problems dealing with even the strangest characters. .IP \(bu 2 BUT: that does not necessarily apply to your OS / VM / keyboard configuration. .UNINDENT .sp So better use a long passphrase made from simple ascii chars than one that includes non\-ascii stuff or characters that are hard/impossible to enter on a different keyboard layout. .sp You can change your passphrase for existing repos at any time, it won\(aqt affect the encryption/decryption key or other secrets. .SS More encryption modes .sp Only use \fB\-\-encryption none\fP if you are OK with anyone who has access to your repository being able to read your backups and tamper with their contents without you noticing. .sp If you want \(dqpassphrase and having\-the\-key\(dq security, use \fB\-\-encryption keyfile\fP\&. The key will be stored in your home directory (in \fB~/.config/borg/keys\fP). .sp If you do \fBnot\fP want to encrypt the contents of your backups, but still want to detect malicious tampering use \fB\-\-encryption authenticated\fP\&. To normally work with \fBauthenticated\fP repos, you will need the passphrase, but there is an emergency workaround, see \fBBORG_WORKAROUNDS=authenticated_no_key\fP docs. .sp If \fBBLAKE2b\fP is faster than \fBSHA\-256\fP on your hardware, use \fB\-\-encryption authenticated\-blake2\fP, \fB\-\-encryption repokey\-blake2\fP or \fB\-\-encryption keyfile\-blake2\fP\&. Note: for remote backups the hashing is done on your local machine. .\" nanorst: inline-fill . .TS center; |l|l|l|l|. _ T{ Hash/MAC T} T{ Not encrypted no auth T} T{ Not encrypted, but authenticated T} T{ Encrypted (AEAD w/ AES) and authenticated T} _ T{ SHA\-256 T} T{ none T} T{ \fIauthenticated\fP T} T{ repokey keyfile T} _ T{ BLAKE2b T} T{ n/a T} T{ \fIauthenticated\-blake2\fP T} T{ \fIrepokey\-blake2\fP \fIkeyfile\-blake2\fP T} _ .TE .\" nanorst: inline-replace . .sp Modes \fImarked like this\fP in the above table are new in Borg 1.1 and are not backwards\-compatible with Borg 1.0.x. .sp On modern Intel/AMD CPUs (except very cheap ones), AES is usually hardware\-accelerated. BLAKE2b is faster than SHA256 on Intel/AMD 64\-bit CPUs (except AMD Ryzen and future CPUs with SHA extensions), which makes \fIauthenticated\-blake2\fP faster than \fInone\fP and \fIauthenticated\fP\&. .sp On modern ARM CPUs, NEON provides hardware acceleration for SHA256 making it faster than BLAKE2b\-256 there. NEON accelerates AES as well. .sp Hardware acceleration is always used automatically when available. .sp \fIrepokey\fP and \fIkeyfile\fP use AES\-CTR\-256 for encryption and HMAC\-SHA256 for authentication in an encrypt\-then\-MAC (EtM) construction. The chunk ID hash is HMAC\-SHA256 as well (with a separate key). These modes are compatible with Borg 1.0.x. .sp \fIrepokey\-blake2\fP and \fIkeyfile\-blake2\fP are also authenticated encryption modes, but use BLAKE2b\-256 instead of HMAC\-SHA256 for authentication. The chunk ID hash is a keyed BLAKE2b\-256 hash. These modes are new and \fInot\fP compatible with Borg 1.0.x. .sp \fIauthenticated\fP mode uses no encryption, but authenticates repository contents through the same HMAC\-SHA256 hash as the \fIrepokey\fP and \fIkeyfile\fP modes (it uses it as the chunk ID hash). The key is stored like \fIrepokey\fP\&. This mode is new and \fInot\fP compatible with Borg 1.0.x. .sp \fIauthenticated\-blake2\fP is like \fIauthenticated\fP, but uses the keyed BLAKE2b\-256 hash from the other blake2 modes. This mode is new and \fInot\fP compatible with Borg 1.0.x. .sp \fInone\fP mode uses no encryption and no authentication. It uses SHA256 as chunk ID hash. This mode is not recommended, you should rather consider using an authenticated or authenticated/encrypted mode. This mode has possible denial\-of\-service issues when running \fBborg create\fP on contents controlled by an attacker. Use it only for new repositories where no encryption is wanted \fBand\fP when compatibility with 1.0.x is important. If compatibility with 1.0.x is not important, use \fIauthenticated\-blake2\fP or \fIauthenticated\fP instead. This mode is compatible with Borg 1.0.x. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY repository to create .UNINDENT .SS optional arguments .INDENT 0.0 .TP .BI \-e \ MODE\fR,\fB \ \-\-encryption \ MODE select encryption key mode \fB(required)\fP .TP .B \-\-append\-only create an append\-only mode repository. Note that this only affects the low level structure of the repository, and running \fIdelete\fP or \fIprune\fP will still be allowed. See \fIappend_only_mode\fP in Additional Notes for more details. .TP .BI \-\-storage\-quota \ QUOTA Set storage quota of the new repository (e.g. 5G, 1.5T). Default: no quota. .TP .B \-\-make\-parent\-dirs create the parent directories of the repository directory, if they are missing. .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Local repository, repokey encryption, BLAKE2b (often faster, since Borg 1.1) $ borg init \-\-encryption=repokey\-blake2 /path/to/repo # Local repository (no encryption) $ borg init \-\-encryption=none /path/to/repo # Remote repository (accesses a remote borg via ssh) # repokey: stores the (encrypted) key into /config $ borg init \-\-encryption=repokey\-blake2 user@hostname:backup # Remote repository (accesses a remote borg via ssh) # keyfile: stores the (encrypted) key into ~/.config/borg/keys/ $ borg init \-\-encryption=keyfile user@hostname:backup .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-create(1)\fP, \fIborg\-delete(1)\fP, \fIborg\-check(1)\fP, \fIborg\-list(1)\fP, \fIborg\-key\-import(1)\fP, \fIborg\-key\-export(1)\fP, \fIborg\-key\-change\-passphrase(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-key-change-passphrase.10000644000076500000240000000564614601576577021151 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-KEY-CHANGE-PASSPHRASE" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-key-change-passphrase \- Change repository key file passphrase .SH SYNOPSIS .sp borg [common options] key change\-passphrase [options] [REPOSITORY] .SH DESCRIPTION .sp The key files used for repository encryption are optionally passphrase protected. This command can be used to change this passphrase. .sp Please note that this command only changes the passphrase, but not any secret protected by it (like e.g. encryption/MAC keys or chunker seed). Thus, changing the passphrase after passphrase and borg key got compromised does not protect future (nor past) backups to the same repository. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .sp REPOSITORY .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Create a key file protected repository $ borg init \-\-encryption=keyfile \-v /path/to/repo Initializing repository at \(dq/path/to/repo\(dq Enter new passphrase: Enter same passphrase again: Remember your passphrase. Your data will be inaccessible without it. Key in \(dq/root/.config/borg/keys/mnt_backup\(dq created. Keep this key safe. Your data will be inaccessible without it. Synchronizing chunks cache... Archives: 0, w/ cached Idx: 0, w/ outdated Idx: 0, w/o cached Idx: 0. Done. # Change key file passphrase $ borg key change\-passphrase \-v /path/to/repo Enter passphrase for key /root/.config/borg/keys/mnt_backup: Enter new passphrase: Enter same passphrase again: Remember your passphrase. Your data will be inaccessible without it. Key updated # Import a previously\-exported key into the specified # key file (creating or overwriting the output key) # (keyfile repositories only) $ BORG_KEY_FILE=/path/to/output\-key borg key import /path/to/repo /path/to/exported .ft P .fi .UNINDENT .UNINDENT .sp Fully automated using environment variables: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ BORG_NEW_PASSPHRASE=old borg init \-e=repokey repo # now \(dqold\(dq is the current passphrase. $ BORG_PASSPHRASE=old BORG_NEW_PASSPHRASE=new borg key change\-passphrase repo # now \(dqnew\(dq is the current passphrase. .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-key-export.10000644000076500000240000000612514601576577017067 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-KEY-EXPORT" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-key-export \- Export the repository key for backup .SH SYNOPSIS .sp borg [common options] key export [options] [REPOSITORY] [PATH] .SH DESCRIPTION .sp If repository encryption is used, the repository is inaccessible without the key. This command allows one to backup this essential key. Note that the backup produced does not include the passphrase itself (i.e. the exported key stays encrypted). In order to regain access to a repository, one needs both the exported key and the original passphrase. .sp There are three backup formats. The normal backup format is suitable for digital storage as a file. The \fB\-\-paper\fP backup format is optimized for printing and typing in while importing, with per line checks to reduce problems with manual input. The \fB\-\-qr\-html\fP creates a printable HTML template with a QR code and a copy of the \fB\-\-paper\fP\-formatted key. .sp For repositories using keyfile encryption the key is saved locally on the system that is capable of doing backups. To guard against loss of this key, the key needs to be backed up independently of the main data backup. .sp For repositories using the repokey encryption the key is saved in the repository in the config file. A backup is thus not strictly needed, but guards against the repository becoming inaccessible if the file is damaged for some reason. .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C borg key export /path/to/repo > encrypted\-key\-backup borg key export \-\-paper /path/to/repo > encrypted\-key\-backup.txt borg key export \-\-qr\-html /path/to/repo > encrypted\-key\-backup.html # Or pass the output file as an argument instead of redirecting stdout: borg key export /path/to/repo encrypted\-key\-backup borg key export \-\-paper /path/to/repo encrypted\-key\-backup.txt borg key export \-\-qr\-html /path/to/repo encrypted\-key\-backup.html .ft P .fi .UNINDENT .UNINDENT .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .sp REPOSITORY .INDENT 0.0 .TP .B PATH where to store the backup .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-paper Create an export suitable for printing and later type\-in .TP .B \-\-qr\-html Create an html file suitable for printing and later type\-in or qr scan .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-key\-import(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-key-import.10000644000076500000240000000426114601576577017057 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-KEY-IMPORT" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-key-import \- Import the repository key from backup .SH SYNOPSIS .sp borg [common options] key import [options] [REPOSITORY] [PATH] .SH DESCRIPTION .sp This command restores a key previously backed up with the export command. .sp If the \fB\-\-paper\fP option is given, the import will be an interactive process in which each line is checked for plausibility before proceeding to the next line. For this format PATH must not be given. .sp For repositories using keyfile encryption, the key file which \fBborg key import\fP writes to depends on several factors. If the \fBBORG_KEY_FILE\fP environment variable is set and non\-empty, \fBborg key import\fP creates or overwrites that file named by \fB$BORG_KEY_FILE\fP\&. Otherwise, \fBborg key import\fP searches in the \fB$BORG_KEYS_DIR\fP directory for a key file associated with the repository. If a key file is found in \fB$BORG_KEYS_DIR\fP, \fBborg key import\fP overwrites it; otherwise, \fBborg key import\fP creates a new key file in \fB$BORG_KEYS_DIR\fP\&. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .sp REPOSITORY .INDENT 0.0 .TP .B PATH path to the backup (\(aq\-\(aq to read from stdin) .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-paper interactively import from a backup done with \fB\-\-paper\fP .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-key\-export(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-key-migrate-to-repokey.10000644000076500000240000000364314601576577021274 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-KEY-MIGRATE-TO-REPOKEY" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-key-migrate-to-repokey \- Migrate passphrase -> repokey .SH SYNOPSIS .sp borg [common options] key migrate\-to\-repokey [options] [REPOSITORY] .SH DESCRIPTION .sp This command migrates a repository from passphrase mode (removed in Borg 1.0) to repokey mode. .sp You will be first asked for the repository passphrase (to open it in passphrase mode). This is the same passphrase as you used to use for this repo before 1.0. .sp It will then derive the different secrets from this passphrase. .sp Then you will be asked for a new passphrase (twice, for safety). This passphrase will be used to protect the repokey (which contains these same secrets in encrypted form). You may use the same passphrase as you used to use, but you may also use a different one. .sp After migrating to repokey mode, you can change the passphrase at any time. But please note: the secrets will always stay the same and they could always be derived from your (old) passphrase\-mode passphrase. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .sp REPOSITORY .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-key.10000644000076500000240000000233714601576577015551 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-KEY" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-key \- Manage a keyfile or repokey of a repository .SH SYNOPSIS .nf borg [common options] key export ... borg [common options] key import ... borg [common options] key change\-passphrase ... borg [common options] key migrate\-to\-repokey ... .fi .sp .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-key\-export(1)\fP, \fIborg\-key\-import(1)\fP, \fIborg\-key\-change\-passphrase(1)\fP, \fIborg\-key\-migrate\-to\-repokey(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-list.10000644000076500000240000002264314601576577015736 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-LIST" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-list \- List archive or repository contents .SH SYNOPSIS .sp borg [common options] list [options] [REPOSITORY_OR_ARCHIVE] [PATH...] .SH DESCRIPTION .sp This command lists the contents of a repository or an archive. .sp For more help on include/exclude patterns, see the \fIborg_patterns\fP command output. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY_OR_ARCHIVE repository or archive to list contents of .TP .B PATH paths to list; patterns are supported .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-consider\-checkpoints Show checkpoint archives in the repository contents list (default: hidden). .TP .B \-\-short only print file/directory names, nothing else .TP .BI \-\-format \ FORMAT specify format for file or archive listing (default for files: \(dq{mode} {user:6} {group:6} {size:8} {mtime} {path}{extra}{NL}\(dq; for archives: \(dq{archive:<36} {time} [{id}]{NL}\(dq) .TP .B \-\-json Only valid for listing repository contents. Format output as JSON. The form of \fB\-\-format\fP is ignored, but keys used in it are added to the JSON output. Some keys are always present. Note: JSON can only represent text. A \(dqbarchive\(dq key is therefore not available. .TP .B \-\-json\-lines Only valid for listing archive contents. Format output as JSON Lines. The form of \fB\-\-format\fP is ignored, but keys used in it are added to the JSON output. Some keys are always present. Note: JSON can only represent text. A \(dqbpath\(dq key is therefore not available. .UNINDENT .SS Archive filters .INDENT 0.0 .TP .BI \-P \ PREFIX\fR,\fB \ \-\-prefix \ PREFIX only consider archive names starting with this prefix. (deprecated) .TP .BI \-a \ GLOB\fR,\fB \ \-\-glob\-archives \ GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see \(dqborg help patterns\(dq. .TP .BI \-\-sort\-by \ KEYS Comma\-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp .TP .BI \-\-first \ N consider first N archives after other filters were applied .TP .BI \-\-last \ N consider last N archives after other filters were applied .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ borg list /path/to/repo Monday Mon, 2016\-02\-15 19:15:11 repo Mon, 2016\-02\-15 19:26:54 root\-2016\-02\-15 Mon, 2016\-02\-15 19:36:29 newname Mon, 2016\-02\-15 19:50:19 \&... $ borg list /path/to/repo::root\-2016\-02\-15 drwxr\-xr\-x root root 0 Mon, 2016\-02\-15 17:44:27 . drwxrwxr\-x root root 0 Mon, 2016\-02\-15 19:04:49 bin \-rwxr\-xr\-x root root 1029624 Thu, 2014\-11\-13 00:08:51 bin/bash lrwxrwxrwx root root 0 Fri, 2015\-03\-27 20:24:26 bin/bzcmp \-> bzdiff \-rwxr\-xr\-x root root 2140 Fri, 2015\-03\-27 20:24:22 bin/bzdiff \&... $ borg list /path/to/repo::root\-2016\-02\-15 \-\-pattern \(dq\- bin/ba*\(dq drwxr\-xr\-x root root 0 Mon, 2016\-02\-15 17:44:27 . drwxrwxr\-x root root 0 Mon, 2016\-02\-15 19:04:49 bin lrwxrwxrwx root root 0 Fri, 2015\-03\-27 20:24:26 bin/bzcmp \-> bzdiff \-rwxr\-xr\-x root root 2140 Fri, 2015\-03\-27 20:24:22 bin/bzdiff \&... $ borg list /path/to/repo::archiveA \-\-format=\(dq{mode} {user:6} {group:6} {size:8d} {isomtime} {path}{extra}{NEWLINE}\(dq drwxrwxr\-x user user 0 Sun, 2015\-02\-01 11:00:00 . drwxrwxr\-x user user 0 Sun, 2015\-02\-01 11:00:00 code drwxrwxr\-x user user 0 Sun, 2015\-02\-01 11:00:00 code/myproject \-rw\-rw\-r\-\- user user 1416192 Sun, 2015\-02\-01 11:00:00 code/myproject/file.ext \-rw\-rw\-r\-\- user user 1416192 Sun, 2015\-02\-01 11:00:00 code/myproject/file.text \&... $ borg list /path/to/repo/::archiveA \-\-pattern \(aq+ re:\e.ext$\(aq \-\-pattern \(aq\- re:^.*$\(aq \-rw\-rw\-r\-\- user user 1416192 Sun, 2015\-02\-01 11:00:00 code/myproject/file.ext \&... $ borg list /path/to/repo/::archiveA \-\-pattern \(aq+ re:.ext$\(aq \-\-pattern \(aq\- re:^.*$\(aq \-rw\-rw\-r\-\- user user 1416192 Sun, 2015\-02\-01 11:00:00 code/myproject/file.ext \-rw\-rw\-r\-\- user user 1416192 Sun, 2015\-02\-01 11:00:00 code/myproject/file.text \&... .ft P .fi .UNINDENT .UNINDENT .SH NOTES .SS The FORMAT specifier syntax .sp The \fB\-\-format\fP option uses python\(aqs \fI\%format string syntax\fP\&. .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ borg list \-\-format \(aq{archive}{NL}\(aq /path/to/repo ArchiveFoo ArchiveBar \&... # {VAR:NUMBER} \- pad to NUMBER columns. # Strings are left\-aligned, numbers are right\-aligned. # Note: time columns except \(ga\(gaisomtime\(ga\(ga, \(ga\(gaisoctime\(ga\(ga and \(ga\(gaisoatime\(ga\(ga cannot be padded. $ borg list \-\-format \(aq{archive:36} {time} [{id}]{NL}\(aq /path/to/repo ArchiveFoo Thu, 2021\-12\-09 10:22:28 [0b8e9a312bef3f2f6e2d0fc110c196827786c15eba0188738e81697a7fa3b274] $ borg list \-\-format \(aq{mode} {user:6} {group:6} {size:8} {mtime} {path}{extra}{NL}\(aq /path/to/repo::ArchiveFoo \-rw\-rw\-r\-\- user user 1024 Thu, 2021\-12\-09 10:22:17 file\-foo \&... # {VAR:NUMBER} \- pad to NUMBER columns right\-aligned. $ borg list \-\-format \(aq{mode} {user:>6} {group:>6} {size:<8} {mtime} {path}{extra}{NL}\(aq /path/to/repo::ArchiveFoo \-rw\-rw\-r\-\- user user 1024 Thu, 2021\-12\-09 10:22:17 file\-foo \&... .ft P .fi .UNINDENT .UNINDENT .sp The following keys are always available: .INDENT 0.0 .IP \(bu 2 NEWLINE: OS dependent line separator .IP \(bu 2 NL: alias of NEWLINE .IP \(bu 2 NUL: NUL character for creating print0 / xargs \-0 like output, see barchive and bpath keys below .IP \(bu 2 SPACE .IP \(bu 2 TAB .IP \(bu 2 CR .IP \(bu 2 LF .UNINDENT .sp Keys available only when listing archives in a repository: .INDENT 0.0 .IP \(bu 2 archive: archive name interpreted as text (might be missing non\-text characters, see barchive) .IP \(bu 2 name: alias of \(dqarchive\(dq .IP \(bu 2 barchive: verbatim archive name, can contain any character except NUL .IP \(bu 2 comment: archive comment interpreted as text (might be missing non\-text characters, see bcomment) .IP \(bu 2 bcomment: verbatim archive comment, can contain any character except NUL .IP \(bu 2 id: internal ID of the archive .IP \(bu 2 tam: TAM authentication state of this archive .IP \(bu 2 start: time (start) of creation of the archive .IP \(bu 2 time: alias of \(dqstart\(dq .IP \(bu 2 end: time (end) of creation of the archive .IP \(bu 2 command_line: command line which was used to create the archive .IP \(bu 2 hostname: hostname of host on which this archive was created .IP \(bu 2 username: username of user who created this archive .UNINDENT .sp Keys available only when listing files in an archive: .INDENT 0.0 .IP \(bu 2 type .IP \(bu 2 mode .IP \(bu 2 uid .IP \(bu 2 gid .IP \(bu 2 user .IP \(bu 2 group .IP \(bu 2 path: path interpreted as text (might be missing non\-text characters, see bpath) .IP \(bu 2 bpath: verbatim POSIX path, can contain any character except NUL .IP \(bu 2 source: link target for links (identical to linktarget) .IP \(bu 2 linktarget .IP \(bu 2 flags .IP \(bu 2 size .IP \(bu 2 csize: compressed size .IP \(bu 2 dsize: deduplicated size .IP \(bu 2 dcsize: deduplicated compressed size .IP \(bu 2 num_chunks: number of chunks in this file .IP \(bu 2 unique_chunks: number of unique chunks in this file .IP \(bu 2 mtime .IP \(bu 2 ctime .IP \(bu 2 atime .IP \(bu 2 isomtime .IP \(bu 2 isoctime .IP \(bu 2 isoatime .IP \(bu 2 blake2b .IP \(bu 2 blake2s .IP \(bu 2 md5 .IP \(bu 2 sha1 .IP \(bu 2 sha224 .IP \(bu 2 sha256 .IP \(bu 2 sha384 .IP \(bu 2 sha3_224 .IP \(bu 2 sha3_256 .IP \(bu 2 sha3_384 .IP \(bu 2 sha3_512 .IP \(bu 2 sha512 .IP \(bu 2 xxh64: XXH64 checksum of this file (note: this is NOT a cryptographic hash!) .IP \(bu 2 archiveid .IP \(bu 2 archivename .IP \(bu 2 extra: prepends {source} with \(dq \-> \(dq for soft links and \(dq link to \(dq for hard links .IP \(bu 2 health: either \(dqhealthy\(dq (file ok) or \(dqbroken\(dq (if file has all\-zero replacement chunks) .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-info(1)\fP, \fIborg\-diff(1)\fP, \fIborg\-prune(1)\fP, \fIborg\-patterns(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-mount.10000644000076500000240000001322414601576577016120 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-MOUNT" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-mount \- Mount archive or an entire repository as a FUSE filesystem .SH SYNOPSIS .sp borg [common options] mount [options] REPOSITORY_OR_ARCHIVE MOUNTPOINT [PATH...] .SH DESCRIPTION .sp This command mounts an archive as a FUSE filesystem. This can be useful for browsing an archive or restoring individual files. Unless the \fB\-\-foreground\fP option is given the command will run in the background until the filesystem is \fBumounted\fP\&. .sp The command \fBborgfs\fP provides a wrapper for \fBborg mount\fP\&. This can also be used in fstab entries: \fB/path/to/repo /mnt/point fuse.borgfs defaults,noauto 0 0\fP .sp To allow a regular user to use fstab entries, add the \fBuser\fP option: \fB/path/to/repo /mnt/point fuse.borgfs defaults,noauto,user 0 0\fP .sp For FUSE configuration and mount options, see the mount.fuse(8) manual page. .sp Borg\(aqs default behavior is to use the archived user and group names of each file and map them to the system\(aqs respective user and group ids. Alternatively, using \fBnumeric\-ids\fP will instead use the archived user and group ids without any mapping. .sp The \fBuid\fP and \fBgid\fP mount options (implemented by Borg) can be used to override the user and group ids of all files (i.e., \fBborg mount \-o uid=1000,gid=1000\fP). .sp The man page references \fBuser_id\fP and \fBgroup_id\fP mount options (implemented by fuse) which specify the user and group id of the mount owner (aka, the user who does the mounting). It is set automatically by libfuse (or the filesystem if libfuse is not used). However, you should not specify these manually. Unlike the \fBuid\fP and \fBgid\fP mount options which affect all files, \fBuser_id\fP and \fBgroup_id\fP affect the user and group id of the mounted (base) directory. .sp Additional mount options supported by borg: .INDENT 0.0 .IP \(bu 2 \fBversions\fP: when used with a repository mount, this gives a merged, versioned view of the files in the archives. EXPERIMENTAL, layout may change in future. .IP \(bu 2 \fBallow_damaged_files\fP: by default damaged files (where missing chunks were replaced with runs of zeros by \fBborg check \-\-repair\fP) are not readable and return EIO (I/O error). Set this option to read such files. .IP \(bu 2 \fBignore_permissions\fP: for security reasons the \fBdefault_permissions\fP mount option is internally enforced by borg. \fBignore_permissions\fP can be given to not enforce \fBdefault_permissions\fP\&. .UNINDENT .sp The BORG_MOUNT_DATA_CACHE_ENTRIES environment variable is meant for advanced users to tweak the performance. It sets the number of cached data chunks; additional memory usage can be up to ~8 MiB times this number. The default is the number of CPU cores. .sp When the daemonized process receives a signal or crashes, it does not unmount. Unmounting in these cases could cause an active rsync or similar process to unintentionally delete data. .sp When running in the foreground ^C/SIGINT unmounts cleanly, but other signals or crashes do not. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY_OR_ARCHIVE repository or archive to mount .TP .B MOUNTPOINT where to mount filesystem .TP .B PATH paths to extract; patterns are supported .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-consider\-checkpoints Show checkpoint archives in the repository contents list (default: hidden). .TP .B \-f\fP,\fB \-\-foreground stay in foreground, do not daemonize .TP .B \-o Extra mount options .TP .B \-\-numeric\-owner deprecated, use \fB\-\-numeric\-ids\fP instead .TP .B \-\-numeric\-ids use numeric user and group identifiers from archive(s) .UNINDENT .SS Archive filters .INDENT 0.0 .TP .BI \-P \ PREFIX\fR,\fB \ \-\-prefix \ PREFIX only consider archive names starting with this prefix. (deprecated) .TP .BI \-a \ GLOB\fR,\fB \ \-\-glob\-archives \ GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see \(dqborg help patterns\(dq. .TP .BI \-\-sort\-by \ KEYS Comma\-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp .TP .BI \-\-first \ N consider first N archives after other filters were applied .TP .BI \-\-last \ N consider last N archives after other filters were applied .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .TP .BI \-\-strip\-components \ NUMBER Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-umount(1)\fP, \fIborg\-extract(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-patterns.10000644000076500000240000002776514601576577016635 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-PATTERNS" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-patterns \- Details regarding patterns .SH DESCRIPTION .sp The path/filenames used as input for the pattern matching start from the currently active recursion root. You usually give the recursion root(s) when invoking borg and these can be either relative or absolute paths. .sp Starting with Borg 1.2, paths that are matched against patterns always appear relative. If you give \fB/absolute/\fP as root, the paths going into the matcher will start with \fBabsolute/\fP\&. If you give \fB\&../../relative\fP as root, the paths will be normalized as \fBrelative/\fP\&. .sp A directory exclusion pattern can end either with or without a slash (\(aq/\(aq). If it ends with a slash, such as \fIsome/path/\fP, the directory will be included but not its content. If it does not end with a slash, such as \fIsome/path\fP, both the directory and content will be excluded. .sp Borg supports different pattern styles. To define a non\-default style for a specific pattern, prefix it with two characters followed by a colon \(aq:\(aq (i.e. \fBfm:path/*\fP, \fBsh:path/**\fP). .INDENT 0.0 .TP .B \fI\%Fnmatch\fP, selector \fIfm:\fP This is the default style for \fB\-\-exclude\fP and \fB\-\-exclude\-from\fP\&. These patterns use a variant of shell pattern syntax, with \(aq*\(aq matching any number of characters, \(aq?\(aq matching any single character, \(aq[...]\(aq matching any single character specified, including ranges, and \(aq[!...]\(aq matching any character not specified. For the purpose of these patterns, the path separator (backslash for Windows and \(aq/\(aq on other systems) is not treated specially. Wrap meta\-characters in brackets for a literal match (i.e. \fI[?]\fP to match the literal character \fI?\fP). For a path to match a pattern, the full path must match, or it must match from the start of the full path to just before a path separator. Except for the root path, paths will never end in the path separator when matching is attempted. Thus, if a given pattern ends in a path separator, a \(aq*\(aq is appended before matching is attempted. A leading path separator is always removed. .TP .B Shell\-style patterns, selector \fIsh:\fP This is the default style for \fB\-\-pattern\fP and \fB\-\-patterns\-from\fP\&. Like fnmatch patterns these are similar to shell patterns. The difference is that the pattern may include \fI**/\fP for matching zero or more directory levels, \fI*\fP for matching zero or more arbitrary characters with the exception of any path separator. A leading path separator is always removed. .TP .B Regular expressions, selector \fIre:\fP Regular expressions similar to those found in Perl are supported. Unlike shell patterns regular expressions are not required to match the full path and any substring match is sufficient. It is strongly recommended to anchor patterns to the start (\(aq^\(aq), to the end (\(aq$\(aq) or both. Path separators (backslash for Windows and \(aq/\(aq on other systems) in paths are always normalized to a forward slash (\(aq/\(aq) before applying a pattern. The regular expression syntax is described in the \fI\%Python documentation for the re module\fP\&. .TP .B Path prefix, selector \fIpp:\fP This pattern style is useful to match whole sub\-directories. The pattern \fIpp:root/somedir\fP matches \fIroot/somedir\fP and everything therein. A leading path separator is always removed. .TP .B Path full\-match, selector \fIpf:\fP This pattern style is (only) useful to match full paths. This is kind of a pseudo pattern as it can not have any variable or unspecified parts \- the full path must be given. \fIpf:root/file.ext\fP matches \fIroot/file.ext\fP only. A leading path separator is always removed. .sp Implementation note: this is implemented via very time\-efficient O(1) hashtable lookups (this means you can have huge amounts of such patterns without impacting performance much). Due to that, this kind of pattern does not respect any context or order. If you use such a pattern to include a file, it will always be included (if the directory recursion encounters it). Other include/exclude patterns that would normally match will be ignored. Same logic applies for exclude. .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 \fIre:\fP, \fIsh:\fP and \fIfm:\fP patterns are all implemented on top of the Python SRE engine. It is very easy to formulate patterns for each of these types which requires an inordinate amount of time to match paths. If untrusted users are able to supply patterns, ensure they cannot supply \fIre:\fP patterns. Further, ensure that \fIsh:\fP and \fIfm:\fP patterns only contain a handful of wildcards at most. .UNINDENT .UNINDENT .sp Exclusions can be passed via the command line option \fB\-\-exclude\fP\&. When used from within a shell, the patterns should be quoted to protect them from expansion. .sp The \fB\-\-exclude\-from\fP option permits loading exclusion patterns from a text file with one pattern per line. Lines empty or starting with the number sign (\(aq#\(aq) after removing whitespace on both ends are ignored. The optional style selector prefix is also supported for patterns loaded from a file. Due to whitespace removal, paths with whitespace at the beginning or end can only be excluded using regular expressions. .sp To test your exclusion patterns without performing an actual backup you can run \fBborg create \-\-list \-\-dry\-run ...\fP\&. .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Exclude \(aq/home/user/file.o\(aq but not \(aq/home/user/file.odt\(aq: $ borg create \-e \(aq*.o\(aq backup / # Exclude \(aq/home/user/junk\(aq and \(aq/home/user/subdir/junk\(aq but # not \(aq/home/user/importantjunk\(aq or \(aq/etc/junk\(aq: $ borg create \-e \(aqhome/*/junk\(aq backup / # Exclude the contents of \(aq/home/user/cache\(aq but not the directory itself: $ borg create \-e home/user/cache/ backup / # The file \(aq/home/user/cache/important\(aq is *not* backed up: $ borg create \-e home/user/cache/ backup / /home/user/cache/important # The contents of directories in \(aq/home\(aq are not backed up when their name # ends in \(aq.tmp\(aq $ borg create \-\-exclude \(aqre:^home/[^/]+\e.tmp/\(aq backup / # Load exclusions from file $ cat >exclude.txt <\(dq, where char is \(dqH\(dq, \(dqd\(dq, \(dqw\(dq, \(dqm\(dq, \(dqy\(dq. For example, \fB\-\-keep\-within 2d\fP means to keep all archives that were created within the past 48 hours. \(dq1m\(dq is taken to mean \(dq31d\(dq. The archives kept with this option do not count towards the totals specified by any other options. .sp A good procedure is to thin out more and more the older your backups get. As an example, \fB\-\-keep\-daily 7\fP means to keep the latest backup on each day, up to 7 most recent days with backups (days without backups do not count). The rules are applied from secondly to yearly, and backups selected by previous rules do not count towards those of later rules. The time that each backup starts is used for pruning purposes. Dates and times are interpreted in the local timezone, and weeks go from Monday to Sunday. Specifying a negative number of archives to keep means that there is no limit. As of borg 1.2.0, borg will retain the oldest archive if any of the secondly, minutely, hourly, daily, weekly, monthly, or yearly rules was not otherwise able to meet its retention target. This enables the first chronological archive to continue aging until it is replaced by a newer archive that meets the retention criteria. .sp The \fB\-\-keep\-last N\fP option is doing the same as \fB\-\-keep\-secondly N\fP (and it will keep the last N archives under the assumption that you do not create more than one backup archive in the same second). .sp When using \fB\-\-stats\fP, you will get some statistics about how much data was deleted \- the \(dqDeleted data\(dq deduplicated size there is most interesting as that is how much your repository will shrink. Please note that the \(dqAll archives\(dq stats refer to the state after pruning. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY repository to prune .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-n\fP,\fB \-\-dry\-run do not change repository .TP .B \-\-force force pruning of corrupted archives, use \fB\-\-force \-\-force\fP in case \fB\-\-force\fP does not work. .TP .B \-s\fP,\fB \-\-stats print statistics for the deleted archive .TP .B \-\-list output verbose list of archives it keeps/prunes .TP .BI \-\-keep\-within \ INTERVAL keep all archives within this time interval .TP .B \-\-keep\-last\fP,\fB \-\-keep\-secondly number of secondly archives to keep .TP .B \-\-keep\-minutely number of minutely archives to keep .TP .B \-H\fP,\fB \-\-keep\-hourly number of hourly archives to keep .TP .B \-d\fP,\fB \-\-keep\-daily number of daily archives to keep .TP .B \-w\fP,\fB \-\-keep\-weekly number of weekly archives to keep .TP .B \-m\fP,\fB \-\-keep\-monthly number of monthly archives to keep .TP .B \-y\fP,\fB \-\-keep\-yearly number of yearly archives to keep .TP .B \-\-save\-space work slower, but using less space .TP .BI \-c \ SECONDS\fR,\fB \ \-\-checkpoint\-interval \ SECONDS write checkpoint every SECONDS seconds (Default: 1800) .UNINDENT .SS Archive filters .INDENT 0.0 .TP .BI \-P \ PREFIX\fR,\fB \ \-\-prefix \ PREFIX only consider archive names starting with this prefix. (deprecated) .TP .BI \-a \ GLOB\fR,\fB \ \-\-glob\-archives \ GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see \(dqborg help patterns\(dq. .UNINDENT .SH EXAMPLES .sp Be careful, prune is a potentially dangerous command, it will remove backup archives. .sp The default of prune is to apply to \fBall archives in the repository\fP unless you restrict its operation to a subset of the archives using \fB\-\-glob\-archives\fP\&. When using \fB\-\-glob\-archives\fP, be careful to choose a good matching pattern \- e.g. do not use \(dqfoo*\(dq if you do not also want to match \(dqfoobar\(dq. .sp It is strongly recommended to always run \fBprune \-v \-\-list \-\-dry\-run ...\fP first so you will see what it would do without it actually doing anything. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Keep 7 end of day and 4 additional end of week archives. # Do a dry\-run without actually deleting anything. $ borg prune \-v \-\-list \-\-dry\-run \-\-keep\-daily=7 \-\-keep\-weekly=4 /path/to/repo # Same as above but only apply to archive names starting with the hostname # of the machine followed by a \(dq\-\(dq character: $ borg prune \-v \-\-list \-\-keep\-daily=7 \-\-keep\-weekly=4 \-\-glob\-archives=\(aq{hostname}\-*\(aq /path/to/repo # actually free disk space: $ borg compact /path/to/repo # Keep 7 end of day, 4 additional end of week archives, # and an end of month archive for every month: $ borg prune \-v \-\-list \-\-keep\-daily=7 \-\-keep\-weekly=4 \-\-keep\-monthly=\-1 /path/to/repo # Keep all backups in the last 10 days, 4 additional end of week archives, # and an end of month archive for every month: $ borg prune \-v \-\-list \-\-keep\-within=10d \-\-keep\-weekly=4 \-\-keep\-monthly=\-1 /path/to/repo .ft P .fi .UNINDENT .UNINDENT .sp There is also a visualized prune example in \fBdocs/misc/prune\-example.txt\fP\&. .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-compact(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-recreate.10000644000076500000240000001747014601576577016557 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-RECREATE" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-recreate \- Re-create archives .SH SYNOPSIS .sp borg [common options] recreate [options] [REPOSITORY_OR_ARCHIVE] [PATH...] .SH DESCRIPTION .sp Recreate the contents of existing archives. .sp recreate is a potentially dangerous function and might lead to data loss (if used wrongly). BE VERY CAREFUL! .sp Important: Repository disk space is \fBnot\fP freed until you run \fBborg compact\fP\&. .sp \fB\-\-exclude\fP, \fB\-\-exclude\-from\fP, \fB\-\-exclude\-if\-present\fP, \fB\-\-keep\-exclude\-tags\fP and PATH have the exact same semantics as in \(dqborg create\(dq, but they only check for files in the archives and not in the local file system. If PATHs are specified, the resulting archives will only contain files from these PATHs. .sp Note that all paths in an archive are relative, therefore absolute patterns/paths will \fInot\fP match (\fB\-\-exclude\fP, \fB\-\-exclude\-from\fP, PATHs). .sp \fB\-\-recompress\fP allows one to change the compression of existing data in archives. Due to how Borg stores compressed size information this might display incorrect information for archives that were not recreated at the same time. There is no risk of data loss by this. .sp \fB\-\-chunker\-params\fP will re\-chunk all files in the archive, this can be used to have upgraded Borg 0.xx or Attic archives deduplicate with Borg 1.x archives. .sp \fBUSE WITH CAUTION.\fP Depending on the PATHs and patterns given, recreate can be used to permanently delete files from archives. When in doubt, use \fB\-\-dry\-run \-\-verbose \-\-list\fP to see how patterns/PATHS are interpreted. See \fIlist_item_flags\fP in \fBborg create\fP for details. .sp The archive being recreated is only removed after the operation completes. The archive that is built during the operation exists at the same time at \(dq.recreate\(dq. The new archive will have a different archive ID. .sp With \fB\-\-target\fP the original archive is not replaced, instead a new archive is created. .sp When rechunking (or recompressing), space usage can be substantial \- expect at least the entire deduplicated size of the archives using the previous chunker (or compression) params. .sp If you recently ran borg check \-\-repair and it had to fix lost chunks with all\-zero replacement chunks, please first run another backup for the same data and re\-run borg check \-\-repair afterwards to heal any archives that had lost chunks which are still generated from the input data. .sp Important: running borg recreate to re\-chunk will remove the chunks_healthy metadata of all items with replacement chunks, so healing will not be possible any more after re\-chunking (it is also unlikely it would ever work: due to the change of chunking parameters, the missing chunk likely will never be seen again even if you still have the data that produced it). .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY_OR_ARCHIVE repository or archive to recreate .TP .B PATH paths to recreate; patterns are supported .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-list output verbose list of items (files, dirs, ...) .TP .BI \-\-filter \ STATUSCHARS only display items with the given status characters (listed in borg create \-\-help) .TP .B \-n\fP,\fB \-\-dry\-run do not change anything .TP .B \-s\fP,\fB \-\-stats print statistics at end .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .TP .B \-\-exclude\-caches exclude directories that contain a CACHEDIR.TAG file (\fI\%http://www.bford.info/cachedir/spec.html\fP) .TP .BI \-\-exclude\-if\-present \ NAME exclude directories that are tagged by containing a filesystem object with the given NAME .TP .B \-\-keep\-exclude\-tags if tag objects are specified with \fB\-\-exclude\-if\-present\fP, don\(aqt omit the tag objects themselves from the backup archive .UNINDENT .SS Archive options .INDENT 0.0 .TP .BI \-\-target \ TARGET create a new archive with the name ARCHIVE, do not replace existing archive (only applies for a single archive) .TP .BI \-c \ SECONDS\fR,\fB \ \-\-checkpoint\-interval \ SECONDS write checkpoint every SECONDS seconds (Default: 1800) .TP .BI \-\-comment \ COMMENT add a comment text to the archive .TP .BI \-\-timestamp \ TIMESTAMP manually specify the archive creation date/time (UTC, yyyy\-mm\-ddThh:mm:ss format). alternatively, give a reference file/directory. .TP .BI \-C \ COMPRESSION\fR,\fB \ \-\-compression \ COMPRESSION select compression algorithm, see the output of the \(dqborg help compression\(dq command for details. .TP .BI \-\-recompress \ MODE recompress data chunks according to \fIMODE\fP and \fB\-\-compression\fP\&. Possible modes are \fIif\-different\fP: recompress if current compression is with a different compression algorithm (the level is not considered); \fIalways\fP: recompress even if current compression is with the same compression algorithm (use this to change the compression level); and \fInever\fP: do not recompress (use this option to explicitly prevent recompression). If no MODE is given, \fIif\-different\fP will be used. Not passing \-\-recompress is equivalent to \(dq\-\-recompress never\(dq. .TP .BI \-\-chunker\-params \ PARAMS rechunk using given chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, HASH_MASK_BITS, HASH_WINDOW_SIZE) or \fIdefault\fP to use the chunker defaults. default: do not rechunk .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Make old (Attic / Borg 0.xx) archives deduplicate with Borg 1.x archives. # Archives created with Borg 1.1+ and the default chunker params are skipped # (archive ID stays the same). $ borg recreate /mnt/backup \-\-chunker\-params default \-\-progress # Create a backup with little but fast compression $ borg create /mnt/backup::archive /some/files \-\-compression lz4 # Then compress it \- this might take longer, but the backup has already completed, # so no inconsistencies from a long\-running backup job. $ borg recreate /mnt/backup::archive \-\-recompress \-\-compression zlib,9 # Remove unwanted files from all archives in a repository. # Note the relative path for the \-\-exclude option \- archives only contain relative paths. $ borg recreate /mnt/backup \-\-exclude home/icke/Pictures/drunk_photos # Change archive comment $ borg create \-\-comment \(dqThis is a comment\(dq /mnt/backup::archivename ~ $ borg info /mnt/backup::archivename Name: archivename Fingerprint: ... Comment: This is a comment \&... $ borg recreate \-\-comment \(dqThis is a better comment\(dq /mnt/backup::archivename $ borg info /mnt/backup::archivename Name: archivename Fingerprint: ... Comment: This is a better comment \&... .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-patterns(1)\fP, \fIborg\-placeholders(1)\fP, \fIborg\-compression(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-rename.10000644000076500000240000000313714601576577016227 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-RENAME" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-rename \- Rename an existing archive .SH SYNOPSIS .sp borg [common options] rename [options] ARCHIVE NEWNAME .SH DESCRIPTION .sp This command renames an archive in the repository. .sp This results in a different archive ID. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B ARCHIVE archive to rename .TP .B NEWNAME the new archive name to use .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ borg create /path/to/repo::archivename ~ $ borg list /path/to/repo archivename Mon, 2016\-02\-15 19:50:19 $ borg rename /path/to/repo::archivename newname $ borg list /path/to/repo newname Mon, 2016\-02\-15 19:50:19 .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-serve.10000644000076500000240000001554414601576577016111 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-SERVE" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-serve \- Start in server mode. This command is usually not used manually. .SH SYNOPSIS .sp borg [common options] serve [options] .SH DESCRIPTION .sp This command starts a repository server process. This command is usually not used manually. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS optional arguments .INDENT 0.0 .TP .BI \-\-restrict\-to\-path \ PATH restrict repository access to PATH. Can be specified multiple times to allow the client access to several directories. Access to all sub\-directories is granted implicitly; PATH doesn\(aqt need to directly point to a repository. .TP .BI \-\-restrict\-to\-repository \ PATH restrict repository access. Only the repository located at PATH (no sub\-directories are considered) is accessible. Can be specified multiple times to allow the client access to several repositories. Unlike \fB\-\-restrict\-to\-path\fP sub\-directories are not accessible; PATH needs to directly point at a repository location. PATH may be an empty directory or the last element of PATH may not exist, in which case the client may initialize a repository there. .TP .B \-\-append\-only only allow appending to repository segment files. Note that this only affects the low level structure of the repository, and running \fIdelete\fP or \fIprune\fP will still be allowed. See \fIappend_only_mode\fP in Additional Notes for more details. .TP .BI \-\-storage\-quota \ QUOTA Override storage quota of the repository (e.g. 5G, 1.5T). When a new repository is initialized, sets the storage quota on the new repository as well. Default: no quota. .UNINDENT .SH EXAMPLES .sp \fBborg serve\fP has special support for ssh forced commands (see \fBauthorized_keys\fP example below): if the environment variable SSH_ORIGINAL_COMMAND is set it will ignore some options given on the command line and use the values from the variable instead. This only applies to a carefully controlled allowlist of safe options. This list currently contains: .INDENT 0.0 .IP \(bu 2 Options that control the log level and debug topics printed such as \fB\-\-verbose\fP, \fB\-\-info\fP, \fB\-\-debug\fP, \fB\-\-debug\-topic\fP, etc. .IP \(bu 2 \fB\-\-lock\-wait\fP to allow the client to control how long to wait before giving up and aborting the operation when another process is holding a lock. .UNINDENT .sp Environment variables (such as BORG_XXX) contained in the original command sent by the client are \fInot\fP interpreted, but ignored. If BORG_XXX environment variables should be set on the \fBborg serve\fP side, then these must be set in system\-specific locations like \fB/etc/environment\fP or in the forced command itself (example below). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Allow an SSH keypair to only run borg, and only have access to /path/to/repo. # Use key options to disable unneeded and potentially dangerous SSH functionality. # This will help to secure an automated remote backup system. $ cat ~/.ssh/authorized_keys command=\(dqborg serve \-\-restrict\-to\-path /path/to/repo\(dq,restrict ssh\-rsa AAAAB3[...] # Set a BORG_XXX environment variable on the \(dqborg serve\(dq side $ cat ~/.ssh/authorized_keys command=\(dqexport BORG_XXX=value; borg serve [...]\(dq,restrict ssh\-rsa [...] .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The examples above use the \fBrestrict\fP directive. This does automatically block potential dangerous ssh features, even when they are added in a future update. Thus, this option should be preferred. .sp If you\(aqre using openssh\-server < 7.2, however, you have to explicitly specify the ssh features to restrict and cannot simply use the restrict option as it has been introduced in v7.2. We recommend to use \fBno\-port\-forwarding,no\-X11\-forwarding,no\-pty,no\-agent\-forwarding,no\-user\-rc\fP in this case. .UNINDENT .UNINDENT .sp Details about sshd usage: \fI\%sshd(8)\fP .SS SSH Configuration .sp \fBborg serve\fP\(aqs pipes (\fBstdin\fP/\fBstdout\fP/\fBstderr\fP) are connected to the \fBsshd\fP process on the server side. In the event that the SSH connection between \fBborg serve\fP and the client is disconnected or stuck abnormally (for example, due to a network outage), it can take a long time for \fBsshd\fP to notice the client is disconnected. In the meantime, \fBsshd\fP continues running, and as a result so does the \fBborg serve\fP process holding the lock on the repository. This can cause subsequent \fBborg\fP operations on the remote repository to fail with the error: \fBFailed to create/acquire the lock\fP\&. .sp In order to avoid this, it is recommended to perform the following additional SSH configuration: .sp Either in the client side\(aqs \fB~/.ssh/config\fP file, or in the client\(aqs \fB/etc/ssh/ssh_config\fP file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Host backupserver ServerAliveInterval 10 ServerAliveCountMax 30 .ft P .fi .UNINDENT .UNINDENT .sp Replacing \fBbackupserver\fP with the hostname, FQDN or IP address of the borg server. .sp This will cause the client to send a keepalive to the server every 10 seconds. If 30 consecutive keepalives are sent without a response (a time of 300 seconds), the ssh client process will be terminated, causing the borg process to terminate gracefully. .sp On the server side\(aqs \fBsshd\fP configuration file (typically \fB/etc/ssh/sshd_config\fP): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ClientAliveInterval 10 ClientAliveCountMax 30 .ft P .fi .UNINDENT .UNINDENT .sp This will cause the server to send a keep alive to the client every 10 seconds. If 30 consecutive keepalives are sent without a response (a time of 300 seconds), the server\(aqs sshd process will be terminated, causing the \fBborg serve\fP process to terminate gracefully and release the lock on the repository. .sp If you then run borg commands with \fB\-\-lock\-wait 600\fP, this gives sufficient time for the borg serve processes to terminate after the SSH connection is torn down after the 300 second wait for the keepalives to fail. .sp You may, of course, modify the timeout values demonstrated above to values that suit your environment and use case. .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-umount.10000644000076500000240000000715314601576577016311 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-UMOUNT" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-umount \- un-mount the FUSE filesystem .SH SYNOPSIS .sp borg [common options] umount [options] MOUNTPOINT .SH DESCRIPTION .sp This command un\-mounts a FUSE filesystem that was mounted with \fBborg mount\fP\&. .sp This is a convenience wrapper that just calls the platform\-specific shell command \- usually this is either umount or fusermount \-u. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B MOUNTPOINT mountpoint of the filesystem to umount .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Mounting the repository shows all archives. # Archives are loaded lazily, expect some delay when navigating to an archive # for the first time. $ borg mount /path/to/repo /tmp/mymountpoint $ ls /tmp/mymountpoint root\-2016\-02\-14 root\-2016\-02\-15 $ borg umount /tmp/mymountpoint # Mounting a specific archive is possible as well. $ borg mount /path/to/repo::root\-2016\-02\-15 /tmp/mymountpoint $ ls /tmp/mymountpoint bin boot etc home lib lib64 lost+found media mnt opt root sbin srv tmp usr var $ borg umount /tmp/mymountpoint # The \(dqversions view\(dq merges all archives in the repository # and provides a versioned view on files. $ borg mount \-o versions /path/to/repo /tmp/mymountpoint $ ls \-l /tmp/mymountpoint/home/user/doc.txt/ total 24 \-rw\-rw\-r\-\- 1 user group 12357 Aug 26 21:19 doc.cda00bc9.txt \-rw\-rw\-r\-\- 1 user group 12204 Aug 26 21:04 doc.fa760f28.txt $ borg umount /tmp/mymountpoint # Archive filters are supported. # These are especially handy for the \(dqversions view\(dq, # which does not support lazy processing of archives. $ borg mount \-o versions \-\-glob\-archives \(aq*\-my\-home\(aq \-\-last 10 /path/to/repo /tmp/mymountpoint # Exclusion options are supported. # These can speed up mounting and lower memory needs significantly. $ borg mount /path/to/repo /tmp/mymountpoint only/that/path $ borg mount \-\-exclude \(aq...\(aq /path/to/repo /tmp/mymountpoint .ft P .fi .UNINDENT .UNINDENT .SS borgfs .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ echo \(aq/mnt/backup /tmp/myrepo fuse.borgfs defaults,noauto 0 0\(aq >> /etc/fstab $ echo \(aq/mnt/backup::root\-2016\-02\-15 /tmp/myarchive fuse.borgfs defaults,noauto 0 0\(aq >> /etc/fstab $ mount /tmp/myrepo $ mount /tmp/myarchive $ ls /tmp/myrepo root\-2016\-02\-01 root\-2016\-02\-2015 $ ls /tmp/myarchive bin boot etc home lib lib64 lost+found media mnt opt root sbin srv tmp usr var .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 \fBborgfs\fP will be automatically provided if you used a distribution package, \fBpip\fP or \fBsetup.py\fP to install Borg. Users of the standalone binary will have to manually create a symlink (see \fIpyinstaller\-binary\fP). .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-mount(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-upgrade.10000644000076500000240000001561614601576577016414 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-UPGRADE" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-upgrade \- upgrade a repository from a previous version .SH SYNOPSIS .sp borg [common options] upgrade [options] [REPOSITORY] .SH DESCRIPTION .sp Upgrade an existing, local Borg repository. .SS When you do not need borg upgrade .sp Not every change requires that you run \fBborg upgrade\fP\&. .sp You do \fBnot\fP need to run it when: .INDENT 0.0 .IP \(bu 2 moving your repository to a different place .IP \(bu 2 upgrading to another point release (like 1.0.x to 1.0.y), except when noted otherwise in the changelog .IP \(bu 2 upgrading from 1.0.x to 1.1.x, except when noted otherwise in the changelog .UNINDENT .SS Borg 1.x.y upgrades .sp Archive TAM authentication: .sp Use \fBborg upgrade \-\-archives\-tam REPO\fP to add archive TAMs to all archives that are not TAM authenticated yet. This is a convenient method to just trust all archives present \- if an archive does not have TAM authentication yet, a TAM will be added. Archives created by old borg versions < 1.0.9 do not have TAMs. Archives created by newer borg version should have TAMs already. If you have a high risk environment, you should not just run this, but first verify that the archives are authentic and not malicious (== have good content, have a good timestamp). Borg 1.2.5+ needs all archives to be TAM authenticated for safety reasons. .sp This upgrade needs to be done once per repository. .sp Manifest TAM authentication: .sp Use \fBborg upgrade \-\-tam REPO\fP to require manifest authentication introduced with Borg 1.0.9 to address security issues. This means that modifying the repository after doing this with a version prior to 1.0.9 will raise a validation error, so only perform this upgrade after updating all clients using the repository to 1.0.9 or newer. .sp This upgrade should be done on each client for safety reasons. .sp If a repository is accidentally modified with a pre\-1.0.9 client after this upgrade, use \fBborg upgrade \-\-tam \-\-force REPO\fP to remedy it. .sp If you routinely do this you might not want to enable this upgrade (which will leave you exposed to the security issue). You can reverse the upgrade by issuing \fBborg upgrade \-\-disable\-tam REPO\fP\&. .sp See \fI\%https://borgbackup.readthedocs.io/en/stable/changes.html#pre\-1\-0\-9\-manifest\-spoofing\-vulnerability\fP for details. .SS Attic and Borg 0.xx to Borg 1.x .sp This currently supports converting an Attic repository to Borg and also helps with converting Borg 0.xx to 1.0. .sp Currently, only LOCAL repositories can be upgraded (issue #465). .sp Please note that \fBborg create\fP (since 1.0.0) uses bigger chunks by default than old borg or attic did, so the new chunks won\(aqt deduplicate with the old chunks in the upgraded repository. See \fB\-\-chunker\-params\fP option of \fBborg create\fP and \fBborg recreate\fP\&. .sp \fBborg upgrade\fP will change the magic strings in the repository\(aqs segments to match the new Borg magic strings. The keyfiles found in $ATTIC_KEYS_DIR or ~/.attic/keys/ will also be converted and copied to $BORG_KEYS_DIR or ~/.config/borg/keys. .sp The cache files are converted, from $ATTIC_CACHE_DIR or ~/.cache/attic to $BORG_CACHE_DIR or ~/.cache/borg, but the cache layout between Borg and Attic changed, so it is possible the first backup after the conversion takes longer than expected due to the cache resync. .sp Upgrade should be able to resume if interrupted, although it will still iterate over all segments. If you want to start from scratch, use \fIborg delete\fP over the copied repository to make sure the cache files are also removed: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C borg delete borg .ft P .fi .UNINDENT .UNINDENT .sp Unless \fB\-\-inplace\fP is specified, the upgrade process first creates a backup copy of the repository, in REPOSITORY.before\-upgrade\-DATETIME, using hardlinks. This requires that the repository and its parent directory reside on same filesystem so the hardlink copy can work. This takes longer than in place upgrades, but is much safer and gives progress information (as opposed to \fBcp \-al\fP). Once you are satisfied with the conversion, you can safely destroy the backup copy. .sp WARNING: Running the upgrade in place will make the current copy unusable with older version, with no way of going back to previous versions. This can PERMANENTLY DAMAGE YOUR REPOSITORY! Attic CAN NOT READ BORG REPOSITORIES, as the magic strings have changed. You have been warned. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY path to the repository to be upgraded .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-n\fP,\fB \-\-dry\-run do not change repository .TP .B \-\-inplace rewrite repository in place, with no chance of going back to older versions of the repository. .TP .B \-\-force Force upgrade .TP .B \-\-tam Enable manifest authentication (in key and cache) (Borg 1.0.9 and later). .TP .B \-\-check\-tam check manifest authentication (in key and cache). .TP .B \-\-disable\-tam Disable manifest authentication (in key and cache). .TP .B \-\-check\-archives\-tam check TAM authentication for all archives. .TP .B \-\-archives\-tam add TAM authentication for all archives. .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Upgrade the borg repository to the most recent version. $ borg upgrade \-v /path/to/repo making a hardlink copy in /path/to/repo.before\-upgrade\-2016\-02\-15\-20:51:55 opening attic repository with borg and converting no key file found for repository converting repo index /path/to/repo/index.0 converting 1 segments... converting borg 0.xx to borg current no key file found for repository .ft P .fi .UNINDENT .UNINDENT .SS Upgrading a passphrase encrypted attic repo .sp attic offered a \(dqpassphrase\(dq encryption mode, but this was removed in borg 1.0 and replaced by the \(dqrepokey\(dq mode (which stores the passphrase\-protected encryption key into the repository config). .sp Thus, to upgrade a \(dqpassphrase\(dq attic repo to a \(dqrepokey\(dq borg repo, 2 steps are needed, in this order: .INDENT 0.0 .IP \(bu 2 borg upgrade repo .IP \(bu 2 borg key migrate\-to\-repokey repo .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg-with-lock.10000644000076500000240000000412014601576577016652 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-WITH-LOCK" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg-with-lock \- run a user specified command with the repository lock held .SH SYNOPSIS .sp borg [common options] with\-lock [options] REPOSITORY COMMAND [ARGS...] .SH DESCRIPTION .sp This command runs a user\-specified command while locking the repository. For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ borg with\-lock /mnt/borgrepo rsync \-av /mnt/borgrepo /somewhere/else/borgrepo .ft P .fi .UNINDENT .UNINDENT .sp It will first try to acquire the lock (make sure that no other operation is running in the repo), then execute the given command as a subprocess and wait for its termination, release the lock and return the user command\(aqs return code as borg\(aqs return code. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 If you copy a repository with the lock held, the lock will be present in the copy. Thus, before using borg on the copy from a different host, you need to use \(dqborg break\-lock\(dq on the copied repository, because Borg is cautious and does not automatically remove stale locks made by a different host. .UNINDENT .UNINDENT .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY repository to lock .TP .B COMMAND command to run .TP .B ARGS command arguments .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borg.10000644000076500000240000010032414601576577014756 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG" 1 "2024-03-29" "" "borg backup tool" .SH NAME borg \- deduplicating and encrypting backup tool .SH SYNOPSIS .sp borg [common options] [options] [arguments] .SH DESCRIPTION .\" we don't include the README.rst here since we want to keep this terse. . .sp BorgBackup (short: Borg) is a deduplicating backup program. Optionally, it supports compression and authenticated encryption. .sp The main goal of Borg is to provide an efficient and secure way to backup data. The data deduplication technique used makes Borg suitable for daily backups since only changes are stored. The authenticated encryption technique makes it suitable for backups to not fully trusted targets. .sp Borg stores a set of files in an \fIarchive\fP\&. A \fIrepository\fP is a collection of \fIarchives\fP\&. The format of repositories is Borg\-specific. Borg does not distinguish archives from each other in any way other than their name, it does not matter when or where archives were created (e.g. different hosts). .SH EXAMPLES .SS A step\-by\-step example .INDENT 0.0 .IP 1. 3 Before a backup can be made a repository has to be initialized: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C $ borg init \-\-encryption=repokey /path/to/repo .ft P .fi .UNINDENT .UNINDENT .IP 2. 3 Backup the \fB~/src\fP and \fB~/Documents\fP directories into an archive called \fIMonday\fP: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C $ borg create /path/to/repo::Monday ~/src ~/Documents .ft P .fi .UNINDENT .UNINDENT .IP 3. 3 The next day create a new archive called \fITuesday\fP: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C $ borg create \-\-stats /path/to/repo::Tuesday ~/src ~/Documents .ft P .fi .UNINDENT .UNINDENT .sp This backup will be a lot quicker and a lot smaller since only new never before seen data is stored. The \fB\-\-stats\fP option causes Borg to output statistics about the newly created archive such as the amount of unique data (not shared with other archives): .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Archive name: Tuesday Archive fingerprint: bd31004d58f51ea06ff735d2e5ac49376901b21d58035f8fb05dbf866566e3c2 Time (start): Tue, 2016\-02\-16 18:15:11 Time (end): Tue, 2016\-02\-16 18:15:11 Duration: 0.19 seconds Number of files: 127 \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Original size Compressed size Deduplicated size This archive: 4.16 MB 4.17 MB 26.78 kB All archives: 8.33 MB 8.34 MB 4.19 MB Unique chunks Total chunks Chunk index: 132 261 \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- .ft P .fi .UNINDENT .UNINDENT .IP 4. 3 List all archives in the repository: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C $ borg list /path/to/repo Monday Mon, 2016\-02\-15 19:14:44 Tuesday Tue, 2016\-02\-16 19:15:11 .ft P .fi .UNINDENT .UNINDENT .IP 5. 3 List the contents of the \fIMonday\fP archive: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C $ borg list /path/to/repo::Monday drwxr\-xr\-x user group 0 Mon, 2016\-02\-15 18:22:30 home/user/Documents \-rw\-r\-\-r\-\- user group 7961 Mon, 2016\-02\-15 18:22:30 home/user/Documents/Important.doc \&... .ft P .fi .UNINDENT .UNINDENT .IP 6. 3 Restore the \fIMonday\fP archive by extracting the files relative to the current directory: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C $ borg extract /path/to/repo::Monday .ft P .fi .UNINDENT .UNINDENT .IP 7. 3 Delete the \fIMonday\fP archive (please note that this does \fBnot\fP free repo disk space): .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C $ borg delete /path/to/repo::Monday .ft P .fi .UNINDENT .UNINDENT .IP 8. 3 Recover disk space by compacting the segment files in the repo: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C $ borg compact /path/to/repo .ft P .fi .UNINDENT .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Borg is quiet by default (it works on WARNING log level). You can use options like \fB\-\-progress\fP or \fB\-\-list\fP to get specific reports during command execution. You can also add the \fB\-v\fP (or \fB\-\-verbose\fP or \fB\-\-info\fP) option to adjust the log level to INFO to get other informational messages. .UNINDENT .UNINDENT .SH NOTES .SS Positional Arguments and Options: Order matters .sp Borg only supports taking options (\fB\-s\fP and \fB\-\-progress\fP in the example) to the left or right of all positional arguments (\fBrepo::archive\fP and \fBpath\fP in the example), but not in between them: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C borg create \-s \-\-progress repo::archive path # good and preferred borg create repo::archive path \-s \-\-progress # also works borg create \-s repo::archive path \-\-progress # works, but ugly borg create repo::archive \-s \-\-progress path # BAD .ft P .fi .UNINDENT .UNINDENT .sp This is due to a problem in the argparse module: \fI\%https://bugs.python.org/issue15112\fP .SS Repository URLs .sp \fBLocal filesystem\fP (or locally mounted network filesystem): .sp \fB/path/to/repo\fP \- filesystem path to repo directory, absolute path .sp \fBpath/to/repo\fP \- filesystem path to repo directory, relative path .sp Also, stuff like \fB~/path/to/repo\fP or \fB~other/path/to/repo\fP works (this is expanded by your shell). .sp Note: you may also prepend a \fBfile://\fP to a filesystem path to get URL style. .sp \fBRemote repositories\fP accessed via ssh \fI\%user@host\fP: .sp \fBuser@host:/path/to/repo\fP \- remote repo, absolute path .sp \fBssh://user@host:port/path/to/repo\fP \- same, alternative syntax, port can be given .sp \fBRemote repositories with relative paths\fP can be given using this syntax: .sp \fBuser@host:path/to/repo\fP \- path relative to current directory .sp \fBuser@host:~/path/to/repo\fP \- path relative to user\(aqs home directory .sp \fBuser@host:~other/path/to/repo\fP \- path relative to other\(aqs home directory .sp Note: giving \fBuser@host:/./path/to/repo\fP or \fBuser@host:/~/path/to/repo\fP or \fBuser@host:/~other/path/to/repo\fP is also supported, but not required here. .sp \fBRemote repositories with relative paths, alternative syntax with port\fP: .sp \fBssh://user@host:port/./path/to/repo\fP \- path relative to current directory .sp \fBssh://user@host:port/~/path/to/repo\fP \- path relative to user\(aqs home directory .sp \fBssh://user@host:port/~other/path/to/repo\fP \- path relative to other\(aqs home directory .sp If you frequently need the same repo URL, it is a good idea to set the \fBBORG_REPO\fP environment variable to set a default for the repo URL: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C export BORG_REPO=\(aqssh://user@host:port/path/to/repo\(aq .ft P .fi .UNINDENT .UNINDENT .sp Then just leave away the repo URL if only a repo URL is needed and you want to use the default \- it will be read from BORG_REPO then. .sp Use \fB::\fP syntax to give the repo URL when syntax requires giving a positional argument for the repo (e.g. \fBborg mount :: /mnt\fP). .SS Repository / Archive Locations .sp Many commands want either a repository (just give the repo URL, see above) or an archive location, which is a repo URL followed by \fB::archive_name\fP\&. .sp Archive names must not contain the \fB/\fP (slash) character. For simplicity, maybe also avoid blanks or other characters that have special meaning on the shell or in a filesystem (borg mount will use the archive name as directory name). .sp If you have set BORG_REPO (see above) and an archive location is needed, use \fB::archive_name\fP \- the repo URL part is then read from BORG_REPO. .SS Logging .sp Borg writes all log output to stderr by default. But please note that something showing up on stderr does \fInot\fP indicate an error condition just because it is on stderr. Please check the log levels of the messages and the return code of borg for determining error, warning or success conditions. .sp If you want to capture the log output to a file, just redirect it: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C borg create repo::archive myfiles 2>> logfile .ft P .fi .UNINDENT .UNINDENT .sp Custom logging configurations can be implemented via BORG_LOGGING_CONF. .sp The log level of the builtin logging configuration defaults to WARNING. This is because we want Borg to be mostly silent and only output warnings, errors and critical messages, unless output has been requested by supplying an option that implies output (e.g. \fB\-\-list\fP or \fB\-\-progress\fP). .sp Log levels: DEBUG < INFO < WARNING < ERROR < CRITICAL .sp Use \fB\-\-debug\fP to set DEBUG log level \- to get debug, info, warning, error and critical level output. .sp Use \fB\-\-info\fP (or \fB\-v\fP or \fB\-\-verbose\fP) to set INFO log level \- to get info, warning, error and critical level output. .sp Use \fB\-\-warning\fP (default) to set WARNING log level \- to get warning, error and critical level output. .sp Use \fB\-\-error\fP to set ERROR log level \- to get error and critical level output. .sp Use \fB\-\-critical\fP to set CRITICAL log level \- to get critical level output. .sp While you can set misc. log levels, do not expect that every command will give different output on different log levels \- it\(aqs just a possibility. .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 Options \fB\-\-critical\fP and \fB\-\-error\fP are provided for completeness, their usage is not recommended as you might miss important information. .UNINDENT .UNINDENT .SS Return codes .sp Borg can exit with the following return codes (rc): .TS center; |l|l|. _ T{ Return code T} T{ Meaning T} _ T{ 0 T} T{ success (logged as INFO) T} _ T{ 1 T} T{ warning (operation reached its normal end, but there were warnings \-\- you should check the log, logged as WARNING) T} _ T{ 2 T} T{ error (like a fatal error, a local or remote exception, the operation did not reach its normal end, logged as ERROR) T} _ T{ 128+N T} T{ killed by signal N (e.g. 137 == kill \-9) T} _ .TE .sp If you use \fB\-\-show\-rc\fP, the return code is also logged at the indicated level as the last log entry. .SS Environment Variables .sp Borg uses some environment variables for automation: .INDENT 0.0 .TP .B General: .INDENT 7.0 .TP .B BORG_REPO When set, use the value to give the default repository location. If a command needs an archive parameter, you can abbreviate as \fB::archive\fP\&. If a command needs a repository parameter, you can either leave it away or abbreviate as \fB::\fP, if a positional parameter is required. .TP .B BORG_PASSPHRASE When set, use the value to answer the passphrase question for encrypted repositories. It is used when a passphrase is needed to access an encrypted repo as well as when a new passphrase should be initially set when initializing an encrypted repo. See also BORG_NEW_PASSPHRASE. .TP .B BORG_PASSCOMMAND When set, use the standard output of the command (trailing newlines are stripped) to answer the passphrase question for encrypted repositories. It is used when a passphrase is needed to access an encrypted repo as well as when a new passphrase should be initially set when initializing an encrypted repo. Note that the command is executed without a shell. So variables, like \fB$HOME\fP will work, but \fB~\fP won\(aqt. If BORG_PASSPHRASE is also set, it takes precedence. See also BORG_NEW_PASSPHRASE. .TP .B BORG_PASSPHRASE_FD When set, specifies a file descriptor to read a passphrase from. Programs starting borg may choose to open an anonymous pipe and use it to pass a passphrase. This is safer than passing via BORG_PASSPHRASE, because on some systems (e.g. Linux) environment can be examined by other processes. If BORG_PASSPHRASE or BORG_PASSCOMMAND are also set, they take precedence. .TP .B BORG_NEW_PASSPHRASE When set, use the value to answer the passphrase question when a \fBnew\fP passphrase is asked for. This variable is checked first. If it is not set, BORG_PASSPHRASE and BORG_PASSCOMMAND will also be checked. Main usecase for this is to fully automate \fBborg change\-passphrase\fP\&. .TP .B BORG_DISPLAY_PASSPHRASE When set, use the value to answer the \(dqdisplay the passphrase for verification\(dq question when defining a new passphrase for encrypted repositories. .TP .B BORG_HOST_ID Borg usually computes a host id from the FQDN plus the results of \fBuuid.getnode()\fP (which usually returns a unique id based on the MAC address of the network interface. Except if that MAC happens to be all\-zero \- in that case it returns a random value, which is not what we want (because it kills automatic stale lock removal). So, if you have a all\-zero MAC address or other reasons to better externally control the host id, just set this environment variable to a unique value. If all your FQDNs are unique, you can just use the FQDN. If not, use \fI\%fqdn@uniqueid\fP\&. .TP .B BORG_LOGGING_CONF When set, use the given filename as \fI\%INI\fP\-style logging configuration. A basic example conf can be found at \fBdocs/misc/logging.conf\fP\&. .TP .B BORG_RSH When set, use this command instead of \fBssh\fP\&. This can be used to specify ssh options, such as a custom identity file \fBssh \-i /path/to/private/key\fP\&. See \fBman ssh\fP for other options. Using the \fB\-\-rsh CMD\fP commandline option overrides the environment variable. .TP .B BORG_REMOTE_PATH When set, use the given path as borg executable on the remote (defaults to \(dqborg\(dq if unset). Using \fB\-\-remote\-path PATH\fP commandline option overrides the environment variable. .TP .B BORG_FILES_CACHE_SUFFIX When set to a value at least one character long, instructs borg to use a specifically named (based on the suffix) alternative files cache. This can be used to avoid loading and saving cache entries for backup sources other than the current sources. .TP .B BORG_FILES_CACHE_TTL When set to a numeric value, this determines the maximum \(dqtime to live\(dq for the files cache entries (default: 20). The files cache is used to quickly determine whether a file is unchanged. The FAQ explains this more detailed in: \fIalways_chunking\fP .TP .B BORG_SHOW_SYSINFO When set to no (default: yes), system information (like OS, Python version, ...) in exceptions is not shown. Please only use for good reasons as it makes issues harder to analyze. .TP .B BORG_FUSE_IMPL Choose the lowlevel FUSE implementation borg shall use for \fBborg mount\fP\&. This is a comma\-separated list of implementation names, they are tried in the given order, e.g.: .INDENT 7.0 .IP \(bu 2 \fBpyfuse3,llfuse\fP: default, first try to load pyfuse3, then try to load llfuse. .IP \(bu 2 \fBllfuse,pyfuse3\fP: first try to load llfuse, then try to load pyfuse3. .IP \(bu 2 \fBpyfuse3\fP: only try to load pyfuse3 .IP \(bu 2 \fBllfuse\fP: only try to load llfuse .IP \(bu 2 \fBnone\fP: do not try to load an implementation .UNINDENT .TP .B BORG_SELFTEST This can be used to influence borg\(aqs builtin self\-tests. The default is to execute the tests at the beginning of each borg command invocation. .sp BORG_SELFTEST=disabled can be used to switch off the tests and rather save some time. Disabling is not recommended for normal borg users, but large scale borg storage providers can use this to optimize production servers after at least doing a one\-time test borg (with selftests not disabled) when installing or upgrading machines / OS / borg. .TP .B BORG_WORKAROUNDS A list of comma separated strings that trigger workarounds in borg, e.g. to work around bugs in other software. .sp Currently known strings are: .INDENT 7.0 .TP .B basesyncfile Use the more simple BaseSyncFile code to avoid issues with sync_file_range. You might need this to run borg on WSL (Windows Subsystem for Linux) or in systemd.nspawn containers on some architectures (e.g. ARM). Using this does not affect data safety, but might result in a more bursty write to disk behaviour (not continuously streaming to disk). .TP .B retry_erofs Retry opening a file without O_NOATIME if opening a file with O_NOATIME caused EROFS. You will need this to make archives from volume shadow copies in WSL1 (Windows Subsystem for Linux 1). .TP .B authenticated_no_key Work around a lost passphrase or key for an \fBauthenticated\fP mode repository (these are only authenticated, but not encrypted). If the key is missing in the repository config, add \fBkey = anything\fP there. .sp This workaround is \fBonly\fP for emergencies and \fBonly\fP to extract data from an affected repository (read\-only access): .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C BORG_WORKAROUNDS=authenticated_no_key borg extract repo::archive .ft P .fi .UNINDENT .UNINDENT .sp After you have extracted all data you need, you MUST delete the repository: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C BORG_WORKAROUNDS=authenticated_no_key borg delete repo .ft P .fi .UNINDENT .UNINDENT .sp Now you can init a fresh repo. Make sure you do not use the workaround any more. .TP .B ignore_invalid_archive_tam Work around invalid archive TAMs created by borg < 1.2.5, see #7791\&. .sp This workaround likely needs to get used only once when following the upgrade instructions for CVE\-2023\-36811, see \fIarchives_tam_vuln\fP\&. .sp In normal production operations, this workaround should never be used. .UNINDENT .UNINDENT .TP .B Some automatic \(dqanswerers\(dq (if set, they automatically answer confirmation questions): .INDENT 7.0 .TP .B BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no (or =yes) For \(dqWarning: Attempting to access a previously unknown unencrypted repository\(dq .TP .B BORG_RELOCATED_REPO_ACCESS_IS_OK=no (or =yes) For \(dqWarning: The repository at location ... was previously located at ...\(dq .TP .B BORG_CHECK_I_KNOW_WHAT_I_AM_DOING=NO (or =YES) For \(dqThis is a potentially dangerous function...\(dq (check \-\-repair) .TP .B BORG_DELETE_I_KNOW_WHAT_I_AM_DOING=NO (or =YES) For \(dqYou requested to completely DELETE the repository \fIincluding\fP all archives it contains:\(dq .UNINDENT .sp Note: answers are case sensitive. setting an invalid answer value might either give the default answer or ask you interactively, depending on whether retries are allowed (they by default are allowed). So please test your scripts interactively before making them a non\-interactive script. .UNINDENT .INDENT 0.0 .TP .B Directories and files: .INDENT 7.0 .TP .B BORG_BASE_DIR Defaults to \fB$HOME\fP or \fB~$USER\fP or \fB~\fP (in that order). If you want to move all borg\-specific folders to a custom path at once, all you need to do is to modify \fBBORG_BASE_DIR\fP: the other paths for cache, config etc. will adapt accordingly (assuming you didn\(aqt set them to a different custom value). .TP .B BORG_CACHE_DIR Defaults to \fB$BORG_BASE_DIR/.cache/borg\fP\&. If \fBBORG_BASE_DIR\fP is not explicitly set while \fI\%XDG env var\fP \fBXDG_CACHE_HOME\fP is set, then \fB$XDG_CACHE_HOME/borg\fP is being used instead. This directory contains the local cache and might need a lot of space for dealing with big repositories. Make sure you\(aqre aware of the associated security aspects of the cache location: \fIcache_security\fP .TP .B BORG_CONFIG_DIR Defaults to \fB$BORG_BASE_DIR/.config/borg\fP\&. If \fBBORG_BASE_DIR\fP is not explicitly set while \fI\%XDG env var\fP \fBXDG_CONFIG_HOME\fP is set, then \fB$XDG_CONFIG_HOME/borg\fP is being used instead. This directory contains all borg configuration directories, see the FAQ for a security advisory about the data in this directory: \fIhome_config_borg\fP .TP .B BORG_SECURITY_DIR Defaults to \fB$BORG_CONFIG_DIR/security\fP\&. This directory contains information borg uses to track its usage of NONCES (\(dqnumbers used once\(dq \- usually in encryption context) and other security relevant data. .TP .B BORG_KEYS_DIR Defaults to \fB$BORG_CONFIG_DIR/keys\fP\&. This directory contains keys for encrypted repositories. .TP .B BORG_KEY_FILE When set, use the given path as repository key file. Please note that this is only for rather special applications that externally fully manage the key files: .INDENT 7.0 .IP \(bu 2 this setting only applies to the keyfile modes (not to the repokey modes). .IP \(bu 2 using a full, absolute path to the key file is recommended. .IP \(bu 2 all directories in the given path must exist. .IP \(bu 2 this setting forces borg to use the key file at the given location. .IP \(bu 2 the key file must either exist (for most commands) or will be created (\fBborg init\fP). .IP \(bu 2 you need to give a different path for different repositories. .IP \(bu 2 you need to point to the correct key file matching the repository the command will operate on. .UNINDENT .TP .B TMPDIR This is where temporary files are stored (might need a lot of temporary space for some operations), see \fI\%tempfile\fP for details. .UNINDENT .TP .B Building: .INDENT 7.0 .TP .B BORG_OPENSSL_PREFIX Adds given OpenSSL header file directory to the default locations (setup.py). .TP .B BORG_LIBLZ4_PREFIX Adds given prefix directory to the default locations. If a \(aqinclude/lz4.h\(aq is found Borg will be linked against the system liblz4 instead of a bundled implementation. (setup.py) .TP .B BORG_LIBZSTD_PREFIX Adds given prefix directory to the default locations. If a \(aqinclude/zstd.h\(aq is found Borg will be linked against the system libzstd instead of a bundled implementation. (setup.py) .UNINDENT .UNINDENT .sp Please note: .INDENT 0.0 .IP \(bu 2 Be very careful when using the \(dqyes\(dq sayers, the warnings with prompt exist for your / your data\(aqs security/safety. .IP \(bu 2 Also be very careful when putting your passphrase into a script, make sure it has appropriate file permissions (e.g. mode 600, root:root). .UNINDENT .SS File systems .sp We strongly recommend against using Borg (or any other database\-like software) on non\-journaling file systems like FAT, since it is not possible to assume any consistency in case of power failures (or a sudden disconnect of an external drive or similar failures). .sp While Borg uses a data store that is resilient against these failures when used on journaling file systems, it is not possible to guarantee this with some hardware \-\- independent of the software used. We don\(aqt know a list of affected hardware. .sp If you are suspicious whether your Borg repository is still consistent and readable after one of the failures mentioned above occurred, run \fBborg check \-\-verify\-data\fP to make sure it is consistent. Requirements for Borg repository file systems .INDENT 0.0 .IP \(bu 2 Long file names .IP \(bu 2 At least three directory levels with short names .IP \(bu 2 Typically, file sizes up to a few hundred MB. Large repositories may require large files (>2 GB). .IP \(bu 2 Up to 1000 files per directory. .IP \(bu 2 rename(2) / MoveFile(Ex) should work as specified, i.e. on the same file system it should be a move (not a copy) operation, and in case of a directory it should fail if the destination exists and is not an empty directory, since this is used for locking. .IP \(bu 2 Hardlinks are needed for \fIborg_upgrade\fP (if \fB\-\-inplace\fP option is not used). Also hardlinks are used for more safe and secure file updating (e.g. of the repo config file), but the code tries to work also if hardlinks are not supported. .UNINDENT .SS Units .sp To display quantities, Borg takes care of respecting the usual conventions of scale. Disk sizes are displayed in \fI\%decimal\fP, using powers of ten (so \fBkB\fP means 1000 bytes). For memory usage, \fI\%binary prefixes\fP are used, and are indicated using the \fI\%IEC binary prefixes\fP, using powers of two (so \fBKiB\fP means 1024 bytes). .SS Date and Time .sp We format date and time conforming to ISO\-8601, that is: YYYY\-MM\-DD and HH:MM:SS (24h clock). .sp For more information about that, see: \fI\%https://xkcd.com/1179/\fP .sp Unless otherwise noted, we display local date and time. Internally, we store and process date and time as UTC. .SS Resource Usage .sp Borg might use a lot of resources depending on the size of the data set it is dealing with. .sp If one uses Borg in a client/server way (with a ssh: repository), the resource usage occurs in part on the client and in another part on the server. .sp If one uses Borg as a single process (with a filesystem repo), all the resource usage occurs in that one process, so just add up client + server to get the approximate resource usage. .INDENT 0.0 .TP .B CPU client: .INDENT 7.0 .IP \(bu 2 \fBborg create:\fP does chunking, hashing, compression, crypto (high CPU usage) .IP \(bu 2 \fBchunks cache sync:\fP quite heavy on CPU, doing lots of hashtable operations. .IP \(bu 2 \fBborg extract:\fP crypto, decompression (medium to high CPU usage) .IP \(bu 2 \fBborg check:\fP similar to extract, but depends on options given. .IP \(bu 2 \fBborg prune / borg delete archive:\fP low to medium CPU usage .IP \(bu 2 \fBborg delete repo:\fP done on the server .UNINDENT .sp It won\(aqt go beyond 100% of 1 core as the code is currently single\-threaded. Especially higher zlib and lzma compression levels use significant amounts of CPU cycles. Crypto might be cheap on the CPU (if hardware accelerated) or expensive (if not). .TP .B CPU server: It usually doesn\(aqt need much CPU, it just deals with the key/value store (repository) and uses the repository index for that. .sp borg check: the repository check computes the checksums of all chunks (medium CPU usage) borg delete repo: low CPU usage .TP .B CPU (only for client/server operation): When using borg in a client/server way with a \fI\%ssh:\-type\fP repo, the ssh processes used for the transport layer will need some CPU on the client and on the server due to the crypto they are doing \- esp. if you are pumping big amounts of data. .TP .B Memory (RAM) client: The chunks index and the files index are read into memory for performance reasons. Might need big amounts of memory (see below). Compression, esp. lzma compression with high levels might need substantial amounts of memory. .TP .B Memory (RAM) server: The server process will load the repository index into memory. Might need considerable amounts of memory, but less than on the client (see below). .TP .B Chunks index (client only): Proportional to the amount of data chunks in your repo. Lots of chunks in your repo imply a big chunks index. It is possible to tweak the chunker params (see create options). .TP .B Files index (client only): Proportional to the amount of files in your last backups. Can be switched off (see create options), but next backup might be much slower if you do. The speed benefit of using the files cache is proportional to file size. .TP .B Repository index (server only): Proportional to the amount of data chunks in your repo. Lots of chunks in your repo imply a big repository index. It is possible to tweak the chunker params (see create options) to influence the amount of chunks being created. .TP .B Temporary files (client): Reading data and metadata from a FUSE mounted repository will consume up to the size of all deduplicated, small chunks in the repository. Big chunks won\(aqt be locally cached. .TP .B Temporary files (server): A non\-trivial amount of data will be stored on the remote temp directory for each client that connects to it. For some remotes, this can fill the default temporary directory at /tmp. This can be remediated by ensuring the $TMPDIR, $TEMP, or $TMP environment variable is properly set for the sshd process. For some OSes, this can be done just by setting the correct value in the \&.bashrc (or equivalent login config file for other shells), however in other cases it may be necessary to first enable \fBPermitUserEnvironment yes\fP in your \fBsshd_config\fP file, then add \fBenvironment=\(dqTMPDIR=/my/big/tmpdir\(dq\fP at the start of the public key to be used in the \fBauthorized_hosts\fP file. .TP .B Cache files (client only): Contains the chunks index and files index (plus a collection of single\- archive chunk indexes which might need huge amounts of disk space, depending on archive count and size \- see FAQ about how to reduce). .TP .B Network (only for client/server operation): If your repository is remote, all deduplicated (and optionally compressed/ encrypted) data of course has to go over the connection (\fBssh://\fP repo url). If you use a locally mounted network filesystem, additionally some copy operations used for transaction support also go over the connection. If you backup multiple sources to one target repository, additional traffic happens for cache resynchronization. .UNINDENT .SS Support for file metadata .sp Besides regular file and directory structures, Borg can preserve .INDENT 0.0 .IP \(bu 2 symlinks (stored as symlink, the symlink is not followed) .IP \(bu 2 special files: .INDENT 2.0 .IP \(bu 2 character and block device files (restored via mknod) .IP \(bu 2 FIFOs (\(dqnamed pipes\(dq) .IP \(bu 2 special file \fIcontents\fP can be backed up in \fB\-\-read\-special\fP mode. By default the metadata to create them with mknod(2), mkfifo(2) etc. is stored. .UNINDENT .IP \(bu 2 hardlinked regular files, devices, FIFOs (considering all items in the same archive) .IP \(bu 2 timestamps in nanosecond precision: mtime, atime, ctime .IP \(bu 2 other timestamps: birthtime (on platforms supporting it) .IP \(bu 2 permissions: .INDENT 2.0 .IP \(bu 2 IDs of owning user and owning group .IP \(bu 2 names of owning user and owning group (if the IDs can be resolved) .IP \(bu 2 Unix Mode/Permissions (u/g/o permissions, suid, sgid, sticky) .UNINDENT .UNINDENT .sp On some platforms additional features are supported: .\" Yes/No's are grouped by reason/mechanism/reference. . .TS center; |l|l|l|l|. _ T{ Platform T} T{ ACLs [5] T} T{ xattr [6] T} T{ Flags [7] T} _ T{ Linux T} T{ Yes T} T{ Yes T} T{ Yes [1] T} _ T{ macOS T} T{ Yes T} T{ Yes T} T{ Yes (all) T} _ T{ FreeBSD T} T{ Yes T} T{ Yes T} T{ Yes (all) T} _ T{ OpenBSD T} T{ n/a T} T{ n/a T} T{ Yes (all) T} _ T{ NetBSD T} T{ n/a T} T{ No [2] T} T{ Yes (all) T} _ T{ Solaris and derivatives T} T{ No [3] T} T{ No [3] T} T{ n/a T} _ T{ Windows (cygwin) T} T{ No [4] T} T{ No T} T{ No T} _ .TE .sp Other Unix\-like operating systems may work as well, but have not been tested at all. .sp Note that most of the platform\-dependent features also depend on the file system. For example, ntfs\-3g on Linux isn\(aqt able to convey NTFS ACLs. .IP [1] 5 Only \(dqnodump\(dq, \(dqimmutable\(dq, \(dqcompressed\(dq and \(dqappend\(dq are supported. Feature request #618 for more flags. .IP [2] 5 Feature request #1332 .IP [3] 5 Feature request #1337 .IP [4] 5 Cygwin tries to map NTFS ACLs to permissions with varying degrees of success. .IP [5] 5 The native access control list mechanism of the OS. This normally limits access to non\-native ACLs. For example, NTFS ACLs aren\(aqt completely accessible on Linux with ntfs\-3g. .IP [6] 5 extended attributes; key\-value pairs attached to a file, mainly used by the OS. This includes resource forks on Mac OS X. .IP [7] 5 aka \fIBSD flags\fP\&. The Linux set of flags [1] is portable across platforms. The BSDs define additional flags. .SH SEE ALSO .sp \fIborg\-common(1)\fP for common command line options .sp \fIborg\-init(1)\fP, \fIborg\-create(1)\fP, \fIborg\-mount(1)\fP, \fIborg\-extract(1)\fP, \fIborg\-list(1)\fP, \fIborg\-info(1)\fP, \fIborg\-delete(1)\fP, \fIborg\-prune(1)\fP, \fIborg\-recreate(1)\fP .sp \fIborg\-compression(1)\fP, \fIborg\-patterns(1)\fP, \fIborg\-placeholders(1)\fP .INDENT 0.0 .IP \(bu 2 Main web site \fI\%https://www.borgbackup.org/\fP .IP \(bu 2 Releases \fI\%https://github.com/borgbackup/borg/releases\fP .IP \(bu 2 Changelog \fI\%https://github.com/borgbackup/borg/blob/master/docs/changes.rst\fP .IP \(bu 2 GitHub \fI\%https://github.com/borgbackup/borg\fP .IP \(bu 2 Security contact \fI\%https://borgbackup.readthedocs.io/en/latest/support.html#security\-contact\fP .UNINDENT .SH AUTHOR The Borg Collective orphan: .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man/borgfs.10000644000076500000240000001320514601576577015310 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORGFS" 1 "2024-03-29" "" "borg backup tool" .SH NAME borgfs \- Mount archive or an entire repository as a FUSE filesystem .SH SYNOPSIS .sp borgfs [options] REPOSITORY_OR_ARCHIVE MOUNTPOINT [PATH...] .SH DESCRIPTION .sp This command mounts an archive as a FUSE filesystem. This can be useful for browsing an archive or restoring individual files. Unless the \fB\-\-foreground\fP option is given the command will run in the background until the filesystem is \fBumounted\fP\&. .sp The command \fBborgfs\fP provides a wrapper for \fBborg mount\fP\&. This can also be used in fstab entries: \fB/path/to/repo /mnt/point fuse.borgfs defaults,noauto 0 0\fP .sp To allow a regular user to use fstab entries, add the \fBuser\fP option: \fB/path/to/repo /mnt/point fuse.borgfs defaults,noauto,user 0 0\fP .sp For FUSE configuration and mount options, see the mount.fuse(8) manual page. .sp Borg\(aqs default behavior is to use the archived user and group names of each file and map them to the system\(aqs respective user and group ids. Alternatively, using \fBnumeric\-ids\fP will instead use the archived user and group ids without any mapping. .sp The \fBuid\fP and \fBgid\fP mount options (implemented by Borg) can be used to override the user and group ids of all files (i.e., \fBborg mount \-o uid=1000,gid=1000\fP). .sp The man page references \fBuser_id\fP and \fBgroup_id\fP mount options (implemented by fuse) which specify the user and group id of the mount owner (aka, the user who does the mounting). It is set automatically by libfuse (or the filesystem if libfuse is not used). However, you should not specify these manually. Unlike the \fBuid\fP and \fBgid\fP mount options which affect all files, \fBuser_id\fP and \fBgroup_id\fP affect the user and group id of the mounted (base) directory. .sp Additional mount options supported by borg: .INDENT 0.0 .IP \(bu 2 \fBversions\fP: when used with a repository mount, this gives a merged, versioned view of the files in the archives. EXPERIMENTAL, layout may change in future. .IP \(bu 2 \fBallow_damaged_files\fP: by default damaged files (where missing chunks were replaced with runs of zeros by \fBborg check \-\-repair\fP) are not readable and return EIO (I/O error). Set this option to read such files. .IP \(bu 2 \fBignore_permissions\fP: for security reasons the \fBdefault_permissions\fP mount option is internally enforced by borg. \fBignore_permissions\fP can be given to not enforce \fBdefault_permissions\fP\&. .UNINDENT .sp The BORG_MOUNT_DATA_CACHE_ENTRIES environment variable is meant for advanced users to tweak the performance. It sets the number of cached data chunks; additional memory usage can be up to ~8 MiB times this number. The default is the number of CPU cores. .sp When the daemonized process receives a signal or crashes, it does not unmount. Unmounting in these cases could cause an active rsync or similar process to unintentionally delete data. .sp When running in the foreground ^C/SIGINT unmounts cleanly, but other signals or crashes do not. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B REPOSITORY_OR_ARCHIVE repository or archive to mount .TP .B MOUNTPOINT where to mount filesystem .TP .B PATH paths to extract; patterns are supported .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-V\fP,\fB \-\-version show version number and exit .TP .B \-\-consider\-checkpoints Show checkpoint archives in the repository contents list (default: hidden). .TP .B \-f\fP,\fB \-\-foreground stay in foreground, do not daemonize .TP .B \-o Extra mount options .TP .B \-\-numeric\-owner deprecated, use \fB\-\-numeric\-ids\fP instead .TP .B \-\-numeric\-ids use numeric user and group identifiers from archive(s) .UNINDENT .SS Archive filters .INDENT 0.0 .TP .BI \-P \ PREFIX\fR,\fB \ \-\-prefix \ PREFIX only consider archive names starting with this prefix. (deprecated) .TP .BI \-a \ GLOB\fR,\fB \ \-\-glob\-archives \ GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see \(dqborg help patterns\(dq. .TP .BI \-\-sort\-by \ KEYS Comma\-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp .TP .BI \-\-first \ N consider first N archives after other filters were applied .TP .BI \-\-last \ N consider last N archives after other filters were applied .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .TP .BI \-\-strip\-components \ NUMBER Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/man_intro.rst0000644000076500000240000000317314601576577015714 0ustar00twstaff:orphan: SYNOPSIS -------- borg [common options] [options] [arguments] DESCRIPTION ----------- .. we don't include the README.rst here since we want to keep this terse. BorgBackup (short: Borg) is a deduplicating backup program. Optionally, it supports compression and authenticated encryption. The main goal of Borg is to provide an efficient and secure way to backup data. The data deduplication technique used makes Borg suitable for daily backups since only changes are stored. The authenticated encryption technique makes it suitable for backups to not fully trusted targets. Borg stores a set of files in an *archive*. A *repository* is a collection of *archives*. The format of repositories is Borg-specific. Borg does not distinguish archives from each other in any way other than their name, it does not matter when or where archives were created (e.g. different hosts). EXAMPLES -------- A step-by-step example ~~~~~~~~~~~~~~~~~~~~~~ .. include:: quickstart_example.rst.inc NOTES ----- .. include:: usage_general.rst.inc SEE ALSO -------- `borg-common(1)` for common command line options `borg-init(1)`, `borg-create(1)`, `borg-mount(1)`, `borg-extract(1)`, `borg-list(1)`, `borg-info(1)`, `borg-delete(1)`, `borg-prune(1)`, `borg-recreate(1)` `borg-compression(1)`, `borg-patterns(1)`, `borg-placeholders(1)` * Main web site https://www.borgbackup.org/ * Releases https://github.com/borgbackup/borg/releases * Changelog https://github.com/borgbackup/borg/blob/master/docs/changes.rst * GitHub https://github.com/borgbackup/borg * Security contact https://borgbackup.readthedocs.io/en/latest/support.html#security-contact ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1215193 borgbackup-1.2.8/docs/misc/0000755000076500000240000000000014601577064014113 5ustar00twstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1251752 borgbackup-1.2.8/docs/misc/asciinema/0000755000076500000240000000000014601577064016044 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/asciinema/README0000644000076500000240000000026514601576577016737 0ustar00twstaffDo NOT run the examples without isolation (e.g Vagrant) or this code may make undesirable changes to your host. Running `vagrant up` in this directory will update the screencasts. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/asciinema/Vagrantfile0000644000076500000240000000622714601576577020250 0ustar00twstaffVagrant.configure("2") do |config| config.vm.box = "debian/bullseye64" config.vm.provision "install dependencies", type: "shell", inline: <<-SHELL apt-get update apt-get install -y wget expect gpg asciinema ssh adduser fuse mkdir -p /wallpaper wget \ --user-agent="borgbackup demo screencast" \ --input-file=/vagrant/sample-wallpapers.txt \ --directory-prefix=/wallpaper SHELL config.vm.provision "record install", type: "shell", inline: <<-SHELL gpg --recv-keys "6D5B EF9A DD20 7580 5747 B70F 9F88 FB52 FAF7 B393" asciinema rec -c 'expect /vagrant/install.tcl' --overwrite /vagrant/install.json < /dev/null SHELL config.vm.provision "record basic usage", type: "shell", inline: <<-SHELL # `rm` below allows quick re-exec via: # vagrant vagrant provision --provision-with "record basic usage" # this is useful when testing changes rm -r /media/backup/borgdemo || true rm -r ~/.ssh/ || true rm -r Wallpaper || true deluser --remove-home borgdemo || true # In case we have skipped "record install" if [ ! -e /usr/local/bin/borg ] ; then wget https://github.com/borgbackup/borg/releases/download/1.2.1/borg-linux64 install --owner root --group root --mode 755 borg-linux64 /usr/local/bin/borg fi mkdir -p /media/backup/borgdemo mkdir Wallpaper cp -r /wallpaper Wallpaper/bigcollection cp /wallpaper/Trapper_cabin.jpg Wallpaper/deer.jpg adduser --disabled-password borgdemo echo '127.0.0.1 remoteserver.example' >> /etc/hosts ssh-keygen -f ~/.ssh/id_rsa -N '' ssh-keyscan remoteserver.example > ~/.ssh/known_hosts runuser -u borgdemo mkdir ~borgdemo/.ssh runuser -u borgdemo tee ~borgdemo/.ssh/authorized_keys < ~/.ssh/id_rsa.pub asciinema rec -c 'expect /vagrant/basic.tcl' --overwrite /vagrant/basic.json < /dev/null SHELL config.vm.provision "record advanced usage", type: "shell", inline: <<-SHELL rm -r /media/backup/borgdemo || true rm -r Wallpaper || true # In case we have skipped "record install" if [ ! -e /usr/local/bin/borg ] ; then wget https://github.com/borgbackup/borg/releases/download/1.2.1/borg-linux64 install --owner root --group root --mode 755 borg-linux64 /usr/local/bin/borg fi mkdir -p /media/backup/borgdemo mkdir Wallpaper cp -r /wallpaper Wallpaper/bigcollection cp /wallpaper/Trapper_cabin.jpg Wallpaper/deer.jpg mkdir -p ~/Downloads/big dd if=/dev/zero of=loopbackfile.img bs=100M count=4 losetup /dev/loop0 loopbackfile.img # Make it look as if the adv. usage screencast was recorded after basic usage export BORG_PASSPHRASE='1234' borg init --encryption=repokey /media/backup/borgdemo borg create --compression lz4 /media/backup/borgdemo::backup1 Wallpaper echo "new nice file" > Wallpaper/newfile.txt borg create --compression lz4 /media/backup/borgdemo::backup2 Wallpaper mv Wallpaper/bigcollection Wallpaper/bigcollection_NEW borg create --compression lz4 /media/backup/borgdemo::backup3 Wallpaper unset BORG_PASSPHRASE asciinema rec -c 'expect /vagrant/advanced.tcl' --overwrite /vagrant/advanced.json < /dev/null SHELL end ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/asciinema/advanced.json0000644000076500000240000053726014601576577020531 0ustar00twstaff{"version": 2, "width": 80, "height": 24, "timestamp": 1657143034, "env": {"SHELL": "/bin/bash", "TERM": "vt100"}} [0.768648, "o", "$ #"] [0.819047, "o", " "] [0.842504, "o", "F"] [0.878977, "o", "o"] [0.899264, "o", "r"] [0.943053, "o", " "] [0.978278, "o", "t"] [0.987772, "o", "h"] [1.031408, "o", "e"] [1.056081, "o", " "] [1.086997, "o", "p"] [1.152571, "o", "r"] [1.209285, "o", "o"] [1.255887, "o", " "] [1.26543, "o", "u"] [1.274899, "o", "s"] [1.315128, "o", "e"] [1.40852, "o", "r"] [1.431039, "o", "s"] [1.447279, "o", ","] [1.519005, "o", " "] [1.575052, "o", "h"] [1.58459, "o", "e"] [1.594101, "o", "r"] [1.606992, "o", "e"] [1.748322, "o", " "] [1.87594, "o", "a"] [1.945731, "o", "r"] [2.000125, "o", "e"] [2.024668, "o", " "] [2.203154, "o", "s"] [2.263049, "o", "o"] [2.309367, "o", "m"] [2.33112, "o", "e"] [2.352861, "o", " "] [2.490625, "o", "a"] [2.551042, "o", "d"] [2.583045, "o", "v"] [2.690816, "o", "a"] [2.700252, "o", "n"] [2.759018, "o", "c"] [2.768529, "o", "e"] [2.808619, "o", "d"] [2.855074, "o", " "] [2.884555, "o", "f"] [2.914707, "o", "e"] [2.929256, "o", "a"] [3.129633, "o", "t"] [3.138965, "o", "u"] [3.184356, "o", "r"] [3.311044, "o", "e"] [3.396778, "o", "s"] [3.406265, "o", " "] [3.415762, "o", "o"] [3.471532, "o", "f"] [3.675, "o", " "] [3.757453, "o", "b"] [3.801838, "o", "o"] [3.838019, "o", "r"] [3.85161, "o", "g"] [4.018913, "o", ","] [4.052867, "o", " "] [4.179986, "o", "s"] [4.190396, "o", "o"] [4.372854, "o", " "] [4.481366, "o", "y"] [4.490884, "o", "o"] [4.511015, "o", "u"] [4.555555, "o", " "] [4.630529, "o", "c"] [4.640007, "o", "a"] [4.724636, "o", "n"] [4.910983, "o", " "] [4.938589, "o", "i"] [5.095055, "o", "m"] [5.107011, "o", "p"] [5.214923, "o", "r"] [5.226529, "o", "e"] [5.315333, "o", "s"] [5.471155, "o", "s"] [5.555004, "o", " "] [5.622492, "o", "y"] [5.661749, "o", "o"] [5.707342, "o", "u"] [5.787235, "o", "r"] [5.811207, "o", " "] [5.857214, "o", "f"] [5.88323, "o", "r"] [5.964393, "o", "i"] [5.97392, "o", "e"] [6.021398, "o", "n"] [6.044981, "o", "d"] [6.069829, "o", "s"] [6.106061, "o", "."] [6.147432, "o", " "] [6.351264, "o", ";"] [6.361522, "o", ")"] [6.392543, "o", "\r\n"] [6.39289, "o", "$ "] [6.392956, "o", "#"] [6.412913, "o", " "] [6.425579, "o", "N"] [6.435148, "o", "o"] [6.444729, "o", "t"] [6.539217, "o", "e"] [6.632652, "o", ":"] [6.642205, "o", " "] [6.697711, "o", "T"] [6.715209, "o", "h"] [6.7247, "o", "i"] [6.759216, "o", "s"] [6.95973, "o", " "] [7.008323, "o", "s"] [7.019203, "o", "c"] [7.057184, "o", "r"] [7.257712, "o", "e"] [7.338241, "o", "e"] [7.40562, "o", "n"] [7.43121, "o", "c"] [7.572713, "o", "a"] [7.612116, "o", "s"] [7.626855, "o", "t"] [7.66643, "o", " "] [7.727205, "o", "w"] [7.777758, "o", "a"] [7.827163, "o", "s"] [7.858017, "o", " "] [7.907525, "o", "m"] [7.952253, "o", "a"] [8.014485, "o", "d"] [8.0731, "o", "e"] [8.273661, "o", " "] [8.336051, "o", "w"] [8.439208, "o", "i"] [8.448307, "o", "t"] [8.470845, "o", "h"] [8.499491, "o", " "] [8.614968, "o", "b"] [8.816062, "o", "o"] [8.832931, "o", "r"] [8.991667, "o", "g"] [9.021108, "o", " "] [9.039006, "o", "1"] [9.126149, "o", "."] [9.140746, "o", "2"] [9.298244, "o", "."] [9.319537, "o", "1"] [9.49402, "o", " "] [9.533047, "o", "–"] [9.594869, "o", " "] [9.667117, "o", "o"] [9.761801, "o", "l"] [9.923825, "o", "d"] [10.02222, "o", "e"] [10.062303, "o", "r"] [10.15542, "o", " "] [10.1914, "o", "o"] [10.235464, "o", "r"] [10.263389, "o", " "] [10.272554, "o", "n"] [10.282136, "o", "e"] [10.339477, "o", "w"] [10.54022, "o", "e"] [10.615052, "o", "r"] [10.654864, "o", " "] [10.735194, "o", "b"] [10.744688, "o", "o"] [10.786952, "o", "r"] [10.81902, "o", "g"] [11.010552, "o", " "] [11.105492, "o", "v"] [11.133374, "o", "e"] [11.142911, "o", "r"] [11.25048, "o", "s"] [11.327051, "o", "i"] [11.349735, "o", "o"] [11.406042, "o", "n"] [11.415592, "o", "s"] [11.436237, "o", " "] [11.635499, "o", "m"] [11.723838, "o", "a"] [11.788201, "o", "y"] [11.879904, "o", " "] [11.958149, "o", "b"] [12.001343, "o", "e"] [12.126177, "o", "h"] [12.135508, "o", "a"] [12.230989, "o", "v"] [12.264373, "o", "e"] [12.280025, "o", " "] [12.410209, "o", "d"] [12.450914, "o", "i"] [12.539827, "o", "f"] [12.598996, "o", "f"] [12.608261, "o", "e"] [12.617685, "o", "r"] [12.632187, "o", "e"] [12.790989, "o", "n"] [12.826976, "o", "t"] [12.89162, "o", "l"] [12.975071, "o", "y"] [12.986557, "o", "."] [12.999146, "o", "\r\n"] [12.999751, "o", "$ \r\n$ #"] [13.060331, "o", " "] [13.069688, "o", "F"] [13.099036, "o", "i"] [13.185258, "o", "r"] [13.198792, "o", "s"] [13.209188, "o", "t"] [13.263526, "o", " "] [13.329055, "o", "o"] [13.407171, "o", "f"] [13.608135, "o", " "] [13.626983, "o", "a"] [13.688315, "o", "l"] [13.697764, "o", "l"] [13.715004, "o", ","] [13.892359, "o", " "] [13.901819, "o", "w"] [13.998468, "o", "e"] [14.032986, "o", " "] [14.087043, "o", "c"] [14.125877, "o", "a"] [14.151009, "o", "n"] [14.235155, "o", " "] [14.313567, "o", "u"] [14.335062, "o", "s"] [14.344804, "o", "e"] [14.379354, "o", " "] [14.509748, "o", "s"] [14.652207, "o", "e"] [14.680643, "o", "v"] [14.728477, "o", "e"] [14.788854, "o", "r"] [14.90724, "o", "a"] [14.961741, "o", "l"] [15.162007, "o", " "] [15.227757, "o", "e"] [15.282232, "o", "n"] [15.291691, "o", "v"] [15.417971, "o", "i"] [15.443234, "o", "r"] [15.555707, "o", "o"] [15.611088, "o", "n"] [15.672888, "o", "m"] [15.696164, "o", "e"] [15.778922, "o", "n"] [15.829436, "o", "t"] [15.871693, "o", " "] [15.922998, "o", "v"] [15.957608, "o", "a"] [15.9844, "o", "r"] [16.001013, "o", "i"] [16.082567, "o", "a"] [16.124326, "o", "b"] [16.219488, "o", "l"] [16.242184, "o", "e"] [16.367007, "o", "s"] [16.482485, "o", " "] [16.535034, "o", "f"] [16.582985, "o", "o"] [16.608749, "o", "r"] [16.805253, "o", " "] [16.817763, "o", "b"] [16.827346, "o", "o"] [16.902962, "o", "r"] [17.010552, "o", "g"] [17.019948, "o", "."] [17.035642, "o", "\r\n$ #"] [17.045132, "o", " "] [17.055624, "o", "E"] [17.182999, "o", "."] [17.25914, "o", "g"] [17.454545, "o", "."] [17.464707, "o", " "] [17.474183, "o", "w"] [17.503023, "o", "e"] [17.68359, "o", " "] [17.698943, "o", "d"] [17.791032, "o", "o"] [17.801167, "o", " "] [17.831033, "o", "n"] [17.842921, "o", "o"] [17.959138, "o", "t"] [17.96848, "o", " "] [18.145872, "o", "w"] [18.161406, "o", "a"] [18.292528, "o", "n"] [18.320058, "o", "t"] [18.437598, "o", " "] [18.45628, "o", "t"] [18.541798, "o", "o"] [18.62301, "o", " "] [18.661202, "o", "t"] [18.786572, "o", "y"] [18.796032, "o", "p"] [18.807045, "o", "e"] [18.928807, "o", " "] [18.950521, "o", "i"] [18.987044, "o", "n"] [19.075581, "o", " "] [19.0942, "o", "o"] [19.106641, "o", "u"] [19.130251, "o", "r"] [19.330682, "o", " "] [19.341078, "o", "r"] [19.358696, "o", "e"] [19.368058, "o", "p"] [19.377522, "o", "o"] [19.419039, "o", " "] [19.608443, "o", "p"] [19.617959, "o", "a"] [19.694452, "o", "t"] [19.794913, "o", "h"] [19.952455, "o", " "] [20.035038, "o", "a"] [20.083042, "o", "n"] [20.171973, "o", "d"] [20.243033, "o", " "] [20.254983, "o", "p"] [20.291367, "o", "a"] [20.3727, "o", "s"] [20.466977, "o", "s"] [20.558116, "o", "w"] [20.567538, "o", "o"] [20.607054, "o", "r"] [20.666378, "o", "d"] [20.678977, "o", " "] [20.690985, "o", "a"] [20.702998, "o", "g"] [20.744796, "o", "a"] [20.790992, "o", "i"] [20.800385, "o", "n"] [20.881913, "o", " "] [20.950979, "o", "a"] [21.040524, "o", "n"] [21.11104, "o", "d"] [21.275424, "o", " "] [21.463012, "o", "a"] [21.503013, "o", "g"] [21.512508, "o", "a"] [21.57893, "o", "i"] [21.606392, "o", "n"] [21.725122, "o", "…"] [21.769809, "o", "\r\n$ e"] [21.803359, "o", "x"] [21.815009, "o", "p"] [21.824309, "o", "o"] [21.89458, "o", "r"] [21.906961, "o", "t"] [21.948228, "o", " "] [21.967744, "o", "B"] [21.977193, "o", "O"] [22.12507, "o", "R"] [22.154999, "o", "G"] [22.202465, "o", "_"] [22.253869, "o", "R"] [22.269407, "o", "E"] [22.294996, "o", "P"] [22.327029, "o", "O"] [22.416502, "o", "="] [22.617292, "o", "'"] [22.662825, "o", "/"] [22.67222, "o", "m"] [22.707279, "o", "e"] [22.734538, "o", "d"] [22.790948, "o", "i"] [22.810998, "o", "a"] [22.829427, "o", "/"] [23.031009, "o", "b"] [23.043435, "o", "a"] [23.155688, "o", "c"] [23.182287, "o", "k"] [23.303009, "o", "u"] [23.335461, "o", "p"] [23.434866, "o", "/"] [23.444346, "o", "b"] [23.499795, "o", "o"] [23.531013, "o", "r"] [23.661517, "o", "g"] [23.715646, "o", "d"] [23.755832, "o", "e"] [23.765272, "o", "m"] [23.802991, "o", "o"] [23.872678, "o", "'"] [23.903462, "o", "\r\n"] [23.903965, "o", "$ e"] [23.913483, "o", "x"] [23.947622, "o", "p"] [23.964186, "o", "o"] [24.011565, "o", "r"] [24.031009, "o", "t"] [24.040478, "o", " "] [24.225827, "o", "B"] [24.241378, "o", "O"] [24.279005, "o", "R"] [24.28844, "o", "G"] [24.410795, "o", "_"] [24.457036, "o", "P"] [24.478998, "o", "A"] [24.537176, "o", "S"] [24.622476, "o", "S"] [24.63584, "o", "P"] [24.645319, "o", "H"] [24.679837, "o", "R"] [24.713232, "o", "A"] [24.747347, "o", "S"] [24.802772, "o", "E"] [24.939031, "o", "="] [25.063414, "o", "'"] [25.085883, "o", "1"] [25.131182, "o", "2"] [25.146789, "o", "3"] [25.17097, "o", "4"] [25.181406, "o", "'"] [25.255167, "o", "\r\n$ #"] [25.455604, "o", " "] [25.515004, "o", "P"] [25.524401, "o", "r"] [25.533815, "o", "o"] [25.551117, "o", "b"] [25.563604, "o", "l"] [25.652944, "o", "e"] [25.662389, "o", "m"] [25.69001, "o", " "] [25.712501, "o", "s"] [25.724796, "o", "o"] [25.874974, "o", "l"] [25.941505, "o", "v"] [25.982985, "o", "e"] [26.010991, "o", "d"] [26.050291, "o", ","] [26.093324, "o", " "] [26.111054, "o", "b"] [26.198394, "o", "o"] [26.286561, "o", "r"] [26.326204, "o", "g"] [26.356889, "o", " "] [26.430982, "o", "w"] [26.44645, "o", "i"] [26.472184, "o", "l"] [26.494997, "o", "l"] [26.530965, "o", " "] [26.607747, "o", "u"] [26.63889, "o", "s"] [26.728184, "o", "e"] [26.746538, "o", " "] [26.776367, "o", "t"] [26.812555, "o", "h"] [26.821996, "o", "i"] [26.838955, "o", "s"] [26.927322, "o", " "] [26.936757, "o", "a"] [26.946151, "o", "u"] [26.995406, "o", "t"] [27.011962, "o", "o"] [27.024437, "o", "m"] [27.117245, "o", "a"] [27.126966, "o", "t"] [27.279335, "o", "i"] [27.288775, "o", "c"] [27.314968, "o", "a"] [27.334924, "o", "l"] [27.371544, "o", "l"] [27.431022, "o", "y"] [27.631831, "o", "…"] [27.65508, "o", " "] [27.668634, "o", ":"] [27.678135, "o", ")"] [27.687675, "o", "\r\n"] [27.688086, "o", "$ #"] [27.70553, "o", " "] [27.736874, "o", "W"] [27.778417, "o", "e"] [27.787855, "o", "'"] [27.847064, "o", "l"] [27.872775, "o", "l"] [27.882286, "o", " "] [28.006994, "o", "u"] [28.131631, "o", "s"] [28.225535, "o", "e"] [28.246953, "o", " "] [28.262315, "o", "t"] [28.370427, "o", "h"] [28.384875, "o", "i"] [28.502982, "o", "s"] [28.703165, "o", " "] [28.7349, "o", "r"] [28.770977, "o", "i"] [28.832334, "o", "g"] [28.842777, "o", "h"] [28.874993, "o", "t"] [28.888544, "o", " "] [28.902979, "o", "a"] [29.076391, "o", "w"] [29.120581, "o", "a"] [29.225466, "o", "y"] [29.235041, "o", "…"] [29.283084, "o", "\r\n"] [29.283646, "o", "$ \r\n"] [29.284034, "o", "$ #"] [29.383424, "o", "#"] [29.392808, "o", " "] [29.420544, "o", "A"] [29.4309, "o", "D"] [29.440364, "o", "V"] [29.449873, "o", "A"] [29.495251, "o", "N"] [29.639413, "o", "C"] [29.64894, "o", "E"] [29.66553, "o", "D"] [29.746904, "o", " "] [29.758356, "o", "C"] [29.798779, "o", "R"] [29.830964, "o", "E"] [29.843403, "o", "A"] [29.858925, "o", "T"] [29.868203, "o", "I"] [29.899006, "o", "O"] [29.965264, "o", "N"] [30.162763, "o", " "] [30.191018, "o", "#"] [30.201373, "o", "#"] [30.272056, "o", "\r\n"] [30.272614, "o", "$ \r\n"] [30.272946, "o", "$ #"] [30.293589, "o", " "] [30.33099, "o", "W"] [30.340457, "o", "e"] [30.542977, "o", " "] [30.560565, "o", "c"] [30.572165, "o", "a"] [30.614991, "o", "n"] [30.663027, "o", " "] [30.71139, "o", "a"] [30.783952, "o", "l"] [30.801481, "o", "s"] [30.839011, "o", "o"] [30.852338, "o", " "] [30.861725, "o", "u"] [30.900896, "o", "s"] [30.927412, "o", "e"] [31.014982, "o", " "] [31.055047, "o", "s"] [31.223032, "o", "o"] [31.302948, "o", "m"] [31.339396, "o", "e"] [31.539842, "o", " "] [31.676113, "o", "p"] [31.857625, "o", "l"] [31.916215, "o", "a"] [31.9574, "o", "c"] [31.981896, "o", "e"] [32.088152, "o", "h"] [32.107751, "o", "o"] [32.188877, "o", "l"] [32.272549, "o", "d"] [32.301495, "o", "e"] [32.321059, "o", "r"] [32.408185, "o", "s"] [32.422845, "o", " "] [32.451011, "o", "i"] [32.529593, "o", "n"] [32.593158, "o", " "] [32.632624, "o", "o"] [32.642958, "o", "u"] [32.65631, "o", "r"] [32.838845, "o", " "] [32.898746, "o", "a"] [32.922951, "o", "r"] [32.971394, "o", "c"] [32.982984, "o", "h"] [33.022383, "o", "i"] [33.032879, "o", "v"] [33.051246, "o", "e"] [33.084787, "o", " "] [33.26713, "o", "n"] [33.353287, "o", "a"] [33.366472, "o", "m"] [33.422971, "o", "e"] [33.581748, "o", "…"] [33.713408, "o", "\r\n"] [33.713891, "o", "$ b"] [33.867002, "o", "o"] [33.91104, "o", "r"] [33.932041, "o", "g"] [34.075293, "o", " "] [34.084734, "o", "c"] [34.121825, "o", "r"] [34.219301, "o", "e"] [34.288629, "o", "a"] [34.343252, "o", "t"] [34.357744, "o", "e"] [34.435105, "o", " "] [34.444706, "o", "-"] [34.52727, "o", "-"] [34.564482, "o", "s"] [34.574039, "o", "t"] [34.599311, "o", "a"] [34.755245, "o", "t"] [34.771806, "o", "s"] [34.78124, "o", " "] [34.851131, "o", "-"] [34.939052, "o", "-"] [34.951559, "o", "p"] [34.961005, "o", "r"] [34.97031, "o", "o"] [35.12819, "o", "g"] [35.228805, "o", "r"] [35.251403, "o", "e"] [35.276778, "o", "s"] [35.307296, "o", "s"] [35.316668, "o", " "] [35.343066, "o", "-"] [35.367632, "o", "-"] [35.415702, "o", "c"] [35.452671, "o", "o"] [35.574231, "o", "m"] [35.650388, "o", "p"] [35.659845, "o", "r"] [35.735001, "o", "e"] [35.752448, "o", "s"] [35.787031, "o", "s"] [35.796485, "o", "i"] [35.843758, "o", "o"] [35.870949, "o", "n"] [35.958837, "o", " "] [35.970931, "o", "l"] [36.010975, "o", "z"] [36.211339, "o", "4"] [36.220814, "o", " "] [36.303384, "o", ":"] [36.312833, "o", ":"] [36.392376, "o", "{"] [36.442668, "o", "u"] [36.64288, "o", "s"] [36.690147, "o", "e"] [36.72678, "o", "r"] [36.927231, "o", "}"] [36.963512, "o", "-"] [37.061838, "o", "{"] [37.083465, "o", "n"] [37.14301, "o", "o"] [37.260484, "o", "w"] [37.33903, "o", "}"] [37.428115, "o", " "] [37.44573, "o", "W"] [37.455225, "o", "a"] [37.526439, "o", "l"] [37.535647, "o", "l"] [37.545066, "o", "p"] [37.561417, "o", "a"] [37.570742, "o", "p"] [37.615839, "o", "e"] [37.633475, "o", "r"] [37.77723, "o", "\r\n"] [38.728182, "o", "0 B O 0 B C 0 B D 0 N Wallpaper \r"] [38.729483, "o", "Initializing cache transaction: Reading config \r"] [38.73014, "o", "Initializing cache transaction: Reading chunks \r"] [38.73084, "o", "Initializing cache transaction: Reading files \r"] [38.731497, "o", " \r"] [38.740067, "o", " \r"] [38.75526, "o", "Saving files cache \r"] [38.756708, "o", "Saving chunks cache \r"] [38.757468, "o", "Saving cache config \r"] [38.759071, "o", " \r"] [38.761562, "o", "------------------------------------------------------------------------------"] [38.761744, "o", "\r\r\n"] [38.761989, "o", "Repository: /media/backup/borgdemo"] [38.762153, "o", "\r\r\n"] [38.762386, "o", "Archive name: root-2022-07-06T21:31:12"] [38.762674, "o", "\r\r\n"] [38.762747, "o", "Archive fingerprint: 280fe9e3d92e2e61f04f0ef98de32b4d0a797ec9e3b0b29ddb2b0946c4a0236b"] [38.762885, "o", "\r\r\n"] [38.763095, "o", "Time (start): Wed, 2022-07-06 21:31:12"] [38.763338, "o", "\r\r\n"] [38.763535, "o", "Time (end): Wed, 2022-07-06 21:31:12"] [38.763775, "o", "\r\r\n"] [38.763962, "o", "Duration: 0.02 seconds"] [38.764202, "o", "\r\r\n"] [38.764463, "o", "Number of files: 32\r\r\n"] [38.764682, "o", "Utilization of max. archive size: 0%"] [38.764874, "o", "\r\r\n"] [38.76508, "o", "------------------------------------------------------------------------------"] [38.765384, "o", "\r\r\n Original size Compressed size Deduplicated size"] [38.765551, "o", "\r\r\n"] [38.765757, "o", "This archive: 401.15 MB 399.73 MB 542 B"] [38.76595, "o", "\r\r\n"] [38.766157, "o", "All archives: 1.60 GB 1.60 GB 399.57 MB"] [38.766321, "o", "\r\r\n"] [38.766523, "o", "\r\r\n"] [38.766796, "o", " Unique chunks Total chunks"] [38.767025, "o", "\r\r\n"] [38.767238, "o", "Chunk index: 182 711"] [38.767399, "o", "\r\r\n"] [38.767602, "o", "------------------------------------------------------------------------------"] [38.767957, "o", "\r\r\n"] [38.815857, "o", "$ #"] [38.923502, "o", " "] [39.027, "o", "N"] [39.043393, "o", "o"] [39.095046, "o", "t"] [39.154595, "o", "i"] [39.167017, "o", "c"] [39.219356, "o", "e"] [39.25099, "o", " "] [39.284613, "o", "t"] [39.336021, "o", "h"] [39.439378, "o", "e"] [39.448858, "o", " "] [39.471101, "o", "b"] [39.480542, "o", "a"] [39.530899, "o", "c"] [39.564044, "o", "k"] [39.606444, "o", "u"] [39.645591, "o", "p"] [39.65497, "o", " "] [39.681416, "o", "n"] [39.772835, "o", "a"] [39.782316, "o", "m"] [39.910022, "o", "e"] [40.02652, "o", "."] [40.199614, "o", "\r\n$ \r\n$ #"] [40.243003, "o", " "] [40.259433, "o", "A"] [40.282203, "o", "n"] [40.299793, "o", "d"] [40.500115, "o", " "] [40.513573, "o", "w"] [40.555786, "o", "e"] [40.640486, "o", " "] [40.700229, "o", "c"] [40.728957, "o", "a"] [40.929419, "o", "n"] [41.12993, "o", " "] [41.256333, "o", "p"] [41.306777, "o", "u"] [41.352942, "o", "t"] [41.471912, "o", " "] [41.545122, "o", "c"] [41.609493, "o", "o"] [41.630075, "o", "m"] [41.645445, "o", "p"] [41.662961, "o", "l"] [41.714345, "o", "e"] [41.72384, "o", "t"] [41.759022, "o", "e"] [41.773394, "o", "l"] [41.782732, "o", "y"] [41.833383, "o", " "] [41.895001, "o", "d"] [41.904433, "o", "i"] [41.926981, "o", "f"] [41.946971, "o", "f"] [41.979006, "o", "e"] [42.03056, "o", "r"] [42.087944, "o", "e"] [42.123277, "o", "n"] [42.143987, "o", "t"] [42.260474, "o", " "] [42.26997, "o", "d"] [42.391003, "o", "a"] [42.415766, "o", "t"] [42.432303, "o", "a"] [42.63498, "o", ","] [42.644225, "o", " "] [42.827016, "o", "w"] [42.845569, "o", "i"] [42.908977, "o", "t"] [42.964603, "o", "h"] [42.974094, "o", " "] [42.98695, "o", "d"] [43.063018, "o", "i"] [43.072434, "o", "f"] [43.107612, "o", "f"] [43.135052, "o", "e"] [43.14441, "o", "r"] [43.214774, "o", "e"] [43.270106, "o", "n"] [43.27948, "o", "t"] [43.294946, "o", " "] [43.319007, "o", "b"] [43.494978, "o", "a"] [43.511473, "o", "c"] [43.570849, "o", "k"] [43.58022, "o", "u"] [43.639716, "o", "p"] [43.654954, "o", " "] [43.715076, "o", "s"] [43.724678, "o", "e"] [43.815095, "o", "t"] [43.854662, "o", "t"] [43.916752, "o", "i"] [43.929157, "o", "n"] [44.019065, "o", "g"] [44.066708, "o", "s"] [44.078303, "o", ","] [44.159135, "o", " "] [44.216838, "o", "i"] [44.307253, "o", "n"] [44.334797, "o", " "] [44.372032, "o", "o"] [44.381394, "o", "u"] [44.390774, "o", "r"] [44.442241, "o", " "] [44.463309, "o", "b"] [44.473591, "o", "a"] [44.49526, "o", "c"] [44.50448, "o", "k"] [44.602902, "o", "u"] [44.631293, "o", "p"] [44.8315, "o", "."] [44.841097, "o", " "] [44.850531, "o", "I"] [44.866947, "o", "t"] [44.930496, "o", " "] [44.951657, "o", "w"] [45.01905, "o", "i"] [45.067479, "o", "l"] [45.076954, "o", "l"] [45.227241, "o", " "] [45.239817, "o", "b"] [45.26112, "o", "e"] [45.290566, "o", " "] [45.303406, "o", "d"] [45.312421, "o", "e"] [45.363008, "o", "d"] [45.382612, "o", "u"] [45.454977, "o", "p"] [45.474444, "o", "l"] [45.483814, "o", "i"] [45.686687, "o", "c"] [45.734982, "o", "a"] [45.909772, "o", "t"] [46.110364, "o", "e"] [46.119831, "o", "d"] [46.154955, "o", ","] [46.31846, "o", " "] [46.342976, "o", "a"] [46.365482, "o", "n"] [46.451798, "o", "y"] [46.493003, "o", "w"] [46.502493, "o", "a"] [46.548, "o", "y"] [46.618954, "o", ":"] [46.645599, "o", "\r\n"] [46.646017, "o", "$ b"] [46.737468, "o", "o"] [46.862935, "o", "r"] [46.875431, "o", "g"] [46.979005, "o", " "] [47.038989, "o", "c"] [47.071029, "o", "r"] [47.271454, "o", "e"] [47.423014, "o", "a"] [47.478504, "o", "t"] [47.623367, "o", "e"] [47.667711, "o", " "] [47.868353, "o", "-"] [47.927624, "o", "-"] [47.987025, "o", "s"] [48.115443, "o", "t"] [48.146002, "o", "a"] [48.164393, "o", "t"] [48.275588, "o", "s"] [48.33613, "o", " "] [48.345597, "o", "-"] [48.50091, "o", "-"] [48.511465, "o", "p"] [48.524985, "o", "r"] [48.534442, "o", "o"] [48.543773, "o", "g"] [48.624086, "o", "r"] [48.636604, "o", "e"] [48.662972, "o", "s"] [48.672252, "o", "s"] [48.723547, "o", " "] [48.923935, "o", "-"] [49.043023, "o", "-"] [49.062581, "o", "c"] [49.075033, "o", "o"] [49.095001, "o", "m"] [49.174991, "o", "p"] [49.231004, "o", "r"] [49.240489, "o", "e"] [49.274596, "o", "s"] [49.29506, "o", "s"] [49.375731, "o", "i"] [49.474879, "o", "o"] [49.484391, "o", "n"] [49.551834, "o", " "] [49.591234, "o", "z"] [49.600649, "o", "l"] [49.746253, "o", "i"] [49.795017, "o", "b"] [49.913498, "o", ","] [49.922915, "o", "6"] [49.991003, "o", " "] [50.043696, "o", "-"] [50.05315, "o", "-"] [50.119, "o", "e"] [50.187581, "o", "x"] [50.199004, "o", "c"] [50.216452, "o", "l"] [50.386885, "o", "u"] [50.403456, "o", "d"] [50.427008, "o", "e"] [50.47501, "o", " "] [50.490128, "o", "~"] [50.499528, "o", "/"] [50.557048, "o", "D"] [50.589507, "o", "o"] [50.614943, "o", "w"] [50.646175, "o", "n"] [50.772655, "o", "l"] [50.788527, "o", "o"] [50.96277, "o", "a"] [50.972183, "o", "d"] [50.981668, "o", "s"] [51.159808, "o", "/"] [51.169272, "o", "b"] [51.18588, "o", "i"] [51.195007, "o", "g"] [51.395579, "o", " "] [51.598979, "o", ":"] [51.637314, "o", ":"] [51.687702, "o", "{"] [51.819, "o", "u"] [51.828341, "o", "s"] [51.844681, "o", "e"] [51.854996, "o", "r"] [51.931009, "o", "}"] [51.940417, "o", "-"] [52.082583, "o", "{"] [52.168797, "o", "n"] [52.24168, "o", "o"] [52.442577, "o", "w"] [52.464135, "o", "}"] [52.473611, "o", " "] [52.483012, "o", "~"] [52.510975, "o", "/"] [52.555894, "o", "D"] [52.571294, "o", "o"] [52.593995, "o", "w"] [52.680131, "o", "n"] [52.690653, "o", "l"] [52.733071, "o", "o"] [52.77442, "o", "a"] [52.97502, "o", "d"] [52.999044, "o", "s"] [53.019388, "o", "\r\n"] [53.963521, "o", "0 B O 0 B C 0 B D 0 N root/Downloads \r"] [53.964284, "o", " \r"] [53.964921, "o", "Initializing cache transaction: Reading config \r"] [53.965567, "o", "Initializing cache transaction: Reading chunks \r"] [53.966217, "o", "Initializing cache transaction: Reading files \r"] [53.96692, "o", " \r"] [53.982311, "o", "Saving files cache \r"] [53.983834, "o", "Saving chunks cache \r"] [53.984584, "o", "Saving cache config \r"] [53.986341, "o", " \r"] [53.989445, "o", "------------------------------------------------------------------------------"] [53.989754, "o", "\r\r\n"] [53.990138, "o", "Repository: /media/backup/borgdemo"] [53.990438, "o", "\r\r\n"] [53.990824, "o", "Archive name: root-2022-07-06T21:31:28"] [53.991138, "o", "\r\r\n"] [53.991518, "o", "Archive fingerprint: d292c218f6c32423a9de2e3fbdf51c8ee865dc83ac78f62624216ebdae7ca55e"] [53.991813, "o", "\r\r\n"] [53.992184, "o", "Time (start): Wed, 2022-07-06 21:31:28"] [53.992478, "o", "\r\r\n"] [53.992851, "o", "Time (end): Wed, 2022-07-06 21:31:28"] [53.99314, "o", "\r\r\n"] [53.993512, "o", "Duration: 0.01 seconds"] [53.993812, "o", "\r\r\n"] [53.99419, "o", "Number of files: 0"] [53.994479, "o", "\r\r\n"] [53.994893, "o", "Utilization of max. archive size: 0%"] [53.995216, "o", "\r\r\n"] [53.99559, "o", "------------------------------------------------------------------------------"] [53.995881, "o", "\r\r\n"] [53.996319, "o", " Original size Compressed size Deduplicated size"] [53.99661, "o", "\r\r\n"] [53.996989, "o", "This archive: 576 B 549 B 549 B"] [53.997279, "o", "\r\r\n"] [53.99766, "o", "All archives: 1.60 GB 1.60 GB 399.57 MB"] [53.997951, "o", "\r\r\n"] [53.998329, "o", "\r\r\n"] [53.998748, "o", " Unique chunks Total chunks"] [53.999041, "o", "\r\r\n"] [53.999436, "o", "Chunk index: 184 713"] [53.999731, "o", "\r\r\n"] [54.000115, "o", "------------------------------------------------------------------------------"] [54.000409, "o", "\r\r\n"] [54.049234, "o", "$ \r\n"] [54.049753, "o", "$ #"] [54.068119, "o", " "] [54.07753, "o", "O"] [54.086991, "o", "r"] [54.18121, "o", " "] [54.24303, "o", "l"] [54.342987, "o", "e"] [54.371024, "o", "t"] [54.513575, "o", "'"] [54.569014, "o", "s"] [54.769663, "o", " "] [54.847339, "o", "b"] [54.872046, "o", "a"] [54.942801, "o", "c"] [54.962407, "o", "k"] [54.985108, "o", "u"] [55.129751, "o", "p"] [55.191004, "o", " "] [55.200452, "o", "a"] [55.401184, "o", " "] [55.485684, "o", "d"] [55.515004, "o", "e"] [55.533494, "o", "v"] [55.585263, "o", "i"] [55.608998, "o", "c"] [55.661538, "o", "e"] [55.684062, "o", " "] [55.706779, "o", "v"] [55.718956, "o", "i"] [55.741305, "o", "a"] [55.774956, "o", " "] [55.784238, "o", "S"] [55.81504, "o", "T"] [55.834466, "o", "D"] [55.843769, "o", "I"] [55.930083, "o", "N"] [56.09109, "o", "."] [56.25442, "o", "\r\n$ s"] [56.27403, "o", "u"] [56.32435, "o", "d"] [56.333722, "o", "o"] [56.535107, "o", " "] [56.611262, "o", "d"] [56.669831, "o", "d"] [56.695163, "o", " "] [56.822363, "o", "i"] [56.848108, "o", "f"] [56.876998, "o", "="] [56.886385, "o", "/"] [56.90777, "o", "d"] [56.922055, "o", "e"] [56.956174, "o", "v"] [57.06045, "o", "/"] [57.114878, "o", "l"] [57.166361, "o", "o"] [57.187001, "o", "o"] [57.237174, "o", "p"] [57.246641, "o", "0"] [57.256116, "o", " "] [57.329709, "o", "b"] [57.339104, "o", "s"] [57.353578, "o", "="] [57.551207, "o", "1"] [57.562974, "o", "0"] [57.584427, "o", "M"] [57.751007, "o", " "] [57.884754, "o", "|"] [57.894982, "o", " "] [57.978933, "o", "b"] [57.993342, "o", "o"] [58.02676, "o", "r"] [58.198989, "o", "g"] [58.399364, "o", " "] [58.414833, "o", "c"] [58.442568, "o", "r"] [58.454953, "o", "e"] [58.46519, "o", "a"] [58.55284, "o", "t"] [58.593169, "o", "e"] [58.624302, "o", " "] [58.765138, "o", "-"] [58.847729, "o", "-"] [58.886836, "o", "p"] [58.931277, "o", "r"] [58.944868, "o", "o"] [59.098028, "o", "g"] [59.1514, "o", "r"] [59.25589, "o", "e"] [59.423182, "o", "s"] [59.48791, "o", "s"] [59.591304, "o", " "] [59.666137, "o", "-"] [59.735141, "o", "-"] [59.764108, "o", "s"] [59.883644, "o", "t"] [59.984319, "o", "a"] [59.995915, "o", "t"] [60.087205, "o", "s"] [60.288136, "o", " "] [60.297505, "o", ":"] [60.398272, "o", ":"] [60.409597, "o", "s"] [60.478088, "o", "p"] [60.555086, "o", "e"] [60.587356, "o", "c"] [60.596772, "o", "i"] [60.650928, "o", "a"] [60.683017, "o", "l"] [60.75472, "o", "b"] [60.767219, "o", "a"] [60.80299, "o", "c"] [60.863321, "o", "k"] [60.942935, "o", "u"] [61.021254, "o", "p"] [61.030747, "o", " "] [61.040178, "o", "-"] [61.111171, "o", "\r\n"] [62.112352, "o", "Initializing cache transaction: Reading config \r"] [62.112873, "o", "Initializing cache transaction: Reading chunks \r"] [62.113327, "o", "Initializing cache transaction: Reading files \r"] [62.113941, "o", " \r"] [62.125566, "o", "8.39 MB O 32.95 kB C 32.95 kB D 0 N stdin \r"] [62.360058, "o", "67.11 MB O 263.60 kB C 32.95 kB D 0 N stdin \r"] [62.576735, "o", "125.83 MB O 494.25 kB C 32.95 kB D 0 N stdin \r"] [62.800828, "o", "184.55 MB O 724.90 kB C 32.95 kB D 0 N stdin \r"] [63.001059, "o", "234.88 MB O 922.60 kB C 32.95 kB D 0 N stdin \r"] [63.212902, "o", "285.21 MB O 1.12 MB C 32.95 kB D 0 N stdin \r"] [63.433125, "o", "335.54 MB O 1.32 MB C 32.95 kB D 0 N stdin \r"] [63.636129, "o", "385.88 MB O 1.52 MB C 32.95 kB D 0 N stdin \r"] [63.787663, "o", "40+0 records in\r\r\n40+0 records out\r\r\n419430400 bytes (419 MB, 400 MiB) copied, 2.66153 s, 158 MB/s\r\r\n"] [63.795318, "o", " \r"] [63.815459, "o", "Saving files cache \r"] [63.817002, "o", "Saving chunks cache \r"] [63.817775, "o", "Saving cache config \r"] [63.820066, "o", " \r"] [63.829239, "o", "------------------------------------------------------------------------------\r\r\nRepository: /media/backup/borgdemo\r\r\nArchive name: specialbackup\r\r\nArchive fingerprint: 05e096b07e9031a5d8527f2a4014717c22d9d9005554c6867b608d0f0f670561\r\r\nTime (start): Wed, 2022-07-06 21:31:36\r\r\nTime (end): Wed, 2022-07-06 21:31:38\r\r\n"] [63.829424, "o", "Duration: 1.74 seconds\r\r\n"] [63.830396, "o", "Number of files: 1\r\r\nUtilization of max. archive size: 0%\r\r\n------------------------------------------------------------------------------\r\r\n Original size Compressed size Deduplicated size\r\r\nThis archive: 419.43 MB 1.65 MB 33.47 kB\r\r\nAll archives: 2.02 GB 1.60 GB 399.61 MB\r\r\n\r\r\n Unique chunks Total chunks\r\r\n"] [63.830571, "o", "Chunk index: 187 765\r\r\n"] [63.831243, "o", "------------------------------------------------------------------------------\r\r\n"] [63.876336, "o", "$ \r\n"] [63.876652, "o", "$ #"] [63.910702, "o", " "] [63.92204, "o", "L"] [63.93149, "o", "e"] [63.947129, "o", "t"] [64.147375, "o", "'"] [64.181549, "o", "s"] [64.19927, "o", " "] [64.285772, "o", "c"] [64.317175, "o", "o"] [64.390987, "o", "n"] [64.430118, "o", "t"] [64.462988, "o", "i"] [64.489445, "o", "n"] [64.55958, "o", "u"] [64.759862, "o", "e"] [64.960077, "o", " "] [64.969536, "o", "w"] [65.097873, "o", "i"] [65.145536, "o", "t"] [65.175076, "o", "h"] [65.291736, "o", " "] [65.337115, "o", "s"] [65.397665, "o", "o"] [65.407179, "o", "m"] [65.435062, "o", "e"] [65.503335, "o", " "] [65.518915, "o", "s"] [65.530115, "o", "i"] [65.543504, "o", "m"] [65.606961, "o", "p"] [65.660102, "o", "l"] [65.693965, "o", "e"] [65.895054, "o", " "] [65.965676, "o", "t"] [66.034322, "o", "h"] [66.139148, "o", "i"] [66.148314, "o", "n"] [66.270932, "o", "g"] [66.302594, "o", "s"] [66.371076, "o", ":"] [66.400889, "o", "\r\n$ #"] [66.466516, "o", "#"] [66.562937, "o", " "] [66.601084, "o", "U"] [66.664675, "o", "S"] [66.747098, "o", "E"] [66.765397, "o", "F"] [66.817143, "o", "U"] [66.827826, "o", "L"] [66.903094, "o", " "] [66.912246, "o", "C"] [66.991072, "o", "O"] [67.004464, "o", "M"] [67.01897, "o", "M"] [67.167193, "o", "A"] [67.231511, "o", "N"] [67.246067, "o", "D"] [67.255524, "o", "S"] [67.391135, "o", " "] [67.545458, "o", "#"] [67.590966, "o", "#"] [67.604457, "o", "\r\n$ #"] [67.613944, "o", " "] [67.800648, "o", "Y"] [67.83914, "o", "o"] [67.848506, "o", "u"] [67.954889, "o", " "] [68.026487, "o", "c"] [68.050923, "o", "a"] [68.060401, "o", "n"] [68.136499, "o", " "] [68.145993, "o", "s"] [68.211723, "o", "h"] [68.302823, "o", "o"] [68.391106, "o", "w"] [68.427221, "o", " "] [68.436687, "o", "s"] [68.450242, "o", "o"] [68.487072, "o", "m"] [68.54245, "o", "e"] [68.584131, "o", " "] [68.593457, "o", "i"] [68.711179, "o", "n"] [68.734, "o", "f"] [68.93436, "o", "o"] [68.963047, "o", "r"] [68.987598, "o", "m"] [69.109977, "o", "a"] [69.11933, "o", "t"] [69.142931, "o", "i"] [69.176318, "o", "o"] [69.199092, "o", "n"] [69.399296, "o", " "] [69.44696, "o", "a"] [69.456119, "o", "b"] [69.47174, "o", "o"] [69.505723, "o", "u"] [69.542938, "o", "t"] [69.552441, "o", " "] [69.654686, "o", "a"] [69.680471, "o", "n"] [69.720608, "o", " "] [69.921506, "o", "a"] [69.93091, "o", "r"] [69.940379, "o", "c"] [69.983696, "o", "h"] [69.995146, "o", "i"] [70.00694, "o", "v"] [70.052253, "o", "e"] [70.173065, "o", "."] [70.209802, "o", " "] [70.344882, "o", "Y"] [70.38098, "o", "o"] [70.391455, "o", "u"] [70.400885, "o", " "] [70.436288, "o", "c"] [70.479658, "o", "a"] [70.511128, "o", "n"] [70.532543, "o", " "] [70.564681, "o", "e"] [70.605056, "o", "v"] [70.663093, "o", "e"] [70.710312, "o", "n"] [70.746508, "o", " "] [70.75596, "o", "d"] [70.789044, "o", "o"] [70.874953, "o", " "] [70.8951, "o", "i"] [70.904455, "o", "t"] [70.951104, "o", " "] [70.960345, "o", "w"] [70.990959, "o", "i"] [71.049385, "o", "t"] [71.178649, "o", "h"] [71.199039, "o", "o"] [71.247286, "o", "u"] [71.263928, "o", "t"] [71.341404, "o", " "] [71.400933, "o", "n"] [71.504487, "o", "e"] [71.513992, "o", "e"] [71.702213, "o", "d"] [71.745271, "o", "i"] [71.78307, "o", "n"] [71.792346, "o", "g"] [71.846181, "o", " "] [71.927444, "o", "t"] [71.963354, "o", "o"] [72.167077, "o", " "] [72.367322, "o", "s"] [72.567847, "o", "p"] [72.578326, "o", "e"] [72.608109, "o", "c"] [72.63193, "o", "i"] [72.666017, "o", "f"] [72.681392, "o", "y"] [72.880734, "o", " "] [72.891089, "o", "t"] [72.911506, "o", "h"] [72.964726, "o", "e"] [73.022153, "o", " "] [73.05761, "o", "a"] [73.067139, "o", "r"] [73.178498, "o", "c"] [73.191079, "o", "h"] [73.201342, "o", "i"] [73.237445, "o", "v"] [73.254932, "o", "e"] [73.324386, "o", " "] [73.51517, "o", "n"] [73.627725, "o", "a"] [73.663138, "o", "m"] [73.7273, "o", "e"] [73.790958, "o", ":"] [73.872668, "o", "\r\n"] [73.873096, "o", "$ b"] [73.926405, "o", "o"] [73.949729, "o", "r"] [73.958792, "o", "g"] [73.993115, "o", " "] [74.002344, "o", "i"] [74.011562, "o", "n"] [74.043878, "o", "f"] [74.088185, "o", "o"] [74.121451, "o", " "] [74.310737, "o", ":"] [74.359159, "o", ":"] [74.368338, "o", " "] [74.453642, "o", "-"] [74.615093, "o", "-"] [74.698272, "o", "l"] [74.707464, "o", "a"] [74.720773, "o", "s"] [74.776057, "o", "t"] [74.976332, "o", " "] [74.987519, "o", "1"] [75.085858, "o", "\r\n"] [76.043124, "o", "Archive name: specialbackup\r\r\n"] [76.043213, "o", "Archive fingerprint: 05e096b07e9031a5d8527f2a4014717c22d9d9005554c6867b608d0f0f670561\r\r\n"] [76.043561, "o", "Comment: \r\r\nHostname: bullseye\r\r\n"] [76.043782, "o", "Username: root"] [76.043834, "o", "\r\r\n"] [76.04424, "o", "Time (start): Wed, 2022-07-06 21:31:36\r\r\n"] [76.044293, "o", "Time (end): Wed, 2022-07-06 21:31:38\r\r\n"] [76.044483, "o", "Duration: 1.74 seconds"] [76.044742, "o", "\r\r\n"] [76.044782, "o", "Number of files: 1"] [76.04487, "o", "\r\r\n"] [76.045065, "o", "Command line: borg create --progress --stats ::specialbackup -"] [76.045356, "o", "\r\r\n"] [76.045395, "o", "Utilization of maximum supported archive size: 0%"] [76.045614, "o", "\r\r\n"] [76.045653, "o", "------------------------------------------------------------------------------"] [76.045739, "o", "\r\r\n"] [76.046056, "o", " Original size Compressed size Deduplicated size"] [76.046095, "o", "\r\r\n"] [76.046303, "o", "This archive: 419.43 MB 1.65 MB 33.67 kB"] [76.046342, "o", "\r\r\n"] [76.046558, "o", "All archives: 2.02 GB 1.60 GB 399.61 MB"] [76.046597, "o", "\r\r\n"] [76.046854, "o", "\r\r\n"] [76.046895, "o", " Unique chunks Total chunks"] [76.047217, "o", "\r\r\n"] [76.047276, "o", "Chunk index: 187 765"] [76.047573, "o", "\r\r\n"] [76.101724, "o", "$ \r\n"] [76.10207, "o", "$ #"] [76.110849, "o", " "] [76.25529, "o", "S"] [76.27759, "o", "o"] [76.379077, "o", " "] [76.40714, "o", "l"] [76.452484, "o", "e"] [76.46365, "o", "t"] [76.501988, "o", "'"] [76.516262, "o", "s"] [76.604546, "o", " "] [76.618853, "o", "r"] [76.637128, "o", "e"] [76.702425, "o", "n"] [76.71163, "o", "a"] [76.789956, "o", "m"] [76.799176, "o", "e"] [76.859403, "o", " "] [76.932679, "o", "o"] [76.965959, "o", "u"] [76.989297, "o", "r"] [77.033601, "o", " "] [77.063032, "o", "l"] [77.073168, "o", "a"] [77.09649, "o", "s"] [77.12679, "o", "t"] [77.224094, "o", " "] [77.233311, "o", "a"] [77.287591, "o", "r"] [77.3018, "o", "c"] [77.311098, "o", "h"] [77.320311, "o", "i"] [77.343514, "o", "v"] [77.411844, "o", "e"] [77.442132, "o", ":"] [77.518449, "o", "\r\n"] [77.518821, "o", "$ b"] [77.568049, "o", "o"] [77.640406, "o", "r"] [77.655604, "o", "g"] [77.76192, "o", " "] [77.771152, "o", "r"] [77.80733, "o", "e"] [77.977601, "o", "n"] [78.025855, "o", "a"] [78.082105, "o", "m"] [78.153352, "o", "e"] [78.278704, "o", " "] [78.393852, "o", ":"] [78.411165, "o", ":"] [78.422273, "o", "s"] [78.437519, "o", "p"] [78.464756, "o", "e"] [78.491126, "o", "c"] [78.50326, "o", "i"] [78.58451, "o", "a"] [78.685851, "o", "l"] [78.779088, "o", "b"] [78.829332, "o", "a"] [78.867606, "o", "c"] [78.876858, "o", "k"] [78.905176, "o", "u"] [78.933486, "o", "p"] [78.960769, "o", " "] [78.971173, "o", "b"] [79.002474, "o", "a"] [79.081807, "o", "c"] [79.091086, "o", "k"] [79.190364, "o", "u"] [79.231594, "o", "p"] [79.282855, "o", "-"] [79.319118, "o", "b"] [79.376382, "o", "l"] [79.434706, "o", "o"] [79.448864, "o", "c"] [79.467102, "o", "k"] [79.512354, "o", "-"] [79.58843, "o", "d"] [79.667638, "o", "e"] [79.682887, "o", "v"] [79.778153, "o", "i"] [79.850387, "o", "c"] [79.888649, "o", "e"] [79.92093, "o", "\r\n"] [80.929653, "o", "$ \r\n"] [80.930152, "o", "$ b"] [81.026102, "o", "o"] [81.110369, "o", "r"] [81.134711, "o", "g"] [81.201937, "o", " "] [81.21108, "o", "i"] [81.225431, "o", "n"] [81.35772, "o", "f"] [81.377012, "o", "o"] [81.407296, "o", " "] [81.513619, "o", ":"] [81.591079, "o", ":"] [81.600193, "o", " "] [81.60944, "o", "-"] [81.661749, "o", "-"] [81.686048, "o", "l"] [81.71232, "o", "a"] [81.852605, "o", "s"] [81.874732, "o", "t"] [82.0751, "o", " "] [82.085265, "o", "1"] [82.101556, "o", "\r\n"] [83.06916, "o", "Archive name: backup-block-device\r\r\nArchive fingerprint: c416cbf604ab7dce6512f4d48cb9ea3fc56e311d9c794e2ec0814703a7561551\r\r\nComment: \r\r\nHostname: bullseye\r\r\nUsername: root\r\r\nTime (start): Wed, 2022-07-06 21:31:36\r\r\nTime (end): Wed, 2022-07-06 21:31:38\r\r\nDuration: 1.74 seconds\r\r\nNumber of files: 1\r\r\nCommand line: borg create --progress --stats ::specialbackup -\r\r\nUtilization of maximum supported archive size: 0%\r\r\n------------------------------------------------------------------------------\r\r\n Original size Compressed size Deduplicated size\r\r\nThis archive: 419.43 MB 1.65 MB 33.69 kB\r\r\nAll archives: 2.02 GB 1.60 GB 399.61 MB\r\r\n\r\r\n Unique chunks Total chunks\r\r\nChunk index: 187 765"] [83.069711, "o", "\r\r\n"] [83.116244, "o", "$ "] [83.116346, "o", "\r\n"] [83.117641, "o", "$ #"] [83.180969, "o", " "] [83.197222, "o", "A"] [83.373416, "o", " "] [83.387658, "o", "v"] [83.431926, "o", "e"] [83.537164, "o", "r"] [83.548425, "o", "y"] [83.561647, "o", " "] [83.627053, "o", "i"] [83.63615, "o", "m"] [83.671335, "o", "p"] [83.871559, "o", "o"] [83.880773, "o", "r"] [83.918114, "o", "t"] [83.976375, "o", "a"] [83.989643, "o", "n"] [84.067881, "o", "t"] [84.152154, "o", " "] [84.161334, "o", "s"] [84.255576, "o", "t"] [84.299837, "o", "e"] [84.329092, "o", "p"] [84.356372, "o", " "] [84.429601, "o", "i"] [84.438781, "o", "f"] [84.44806, "o", " "] [84.499307, "o", "y"] [84.508554, "o", "o"] [84.5178, "o", "u"] [84.623066, "o", " "] [84.632231, "o", "c"] [84.69749, "o", "h"] [84.757754, "o", "o"] [84.81601, "o", "o"] [84.885254, "o", "s"] [84.901535, "o", "e"] [84.984793, "o", " "] [85.13606, "o", "k"] [85.162302, "o", "e"] [85.171486, "o", "y"] [85.345714, "o", "f"] [85.40499, "o", "i"] [85.414189, "o", "l"] [85.518496, "o", "e"] [85.543521, "o", " "] [85.612764, "o", "m"] [85.621973, "o", "o"] [85.698247, "o", "d"] [85.717486, "o", "e"] [85.917778, "o", " "] [85.96802, "o", "("] [86.024313, "o", "w"] [86.033549, "o", "h"] [86.084714, "o", "e"] [86.097024, "o", "r"] [86.106266, "o", "e"] [86.235495, "o", " "] [86.284754, "o", "t"] [86.293941, "o", "h"] [86.328215, "o", "e"] [86.337408, "o", " "] [86.346629, "o", "k"] [86.355891, "o", "e"] [86.425154, "o", "y"] [86.456417, "o", "f"] [86.525668, "o", "i"] [86.534847, "o", "l"] [86.617122, "o", "e"] [86.738378, "o", " "] [86.750643, "o", "i"] [86.85383, "o", "s"] [86.912086, "o", " "] [86.969355, "o", "o"] [87.106606, "o", "n"] [87.115827, "o", "l"] [87.172088, "o", "y"] [87.232346, "o", " "] [87.242557, "o", "s"] [87.272829, "o", "a"] [87.312082, "o", "v"] [87.412313, "o", "e"] [87.511544, "o", "d"] [87.711795, "o", " "] [87.781041, "o", "l"] [87.816298, "o", "o"] [87.828552, "o", "c"] [87.904814, "o", "a"] [88.105062, "o", "l"] [88.152317, "o", "l"] [88.287535, "o", "y"] [88.356789, "o", ")"] [88.412023, "o", " "] [88.555231, "o", "i"] [88.570459, "o", "s"] [88.624724, "o", " "] [88.634834, "o", "t"] [88.644101, "o", "o"] [88.669362, "o", " "] [88.869623, "o", "e"] [88.933856, "o", "x"] [88.963068, "o", "p"] [89.062317, "o", "o"] [89.199564, "o", "r"] [89.31488, "o", "t"] [89.399071, "o", " "] [89.408282, "o", "y"] [89.451519, "o", "o"] [89.460707, "o", "u"] [89.660976, "o", "r"] [89.670172, "o", " "] [89.691403, "o", "k"] [89.740641, "o", "e"] [89.795877, "o", "y"] [89.816131, "o", "f"] [89.83932, "o", "i"] [89.848581, "o", "l"] [89.867845, "o", "e"] [90.068096, "o", " "] [90.077304, "o", "a"] [90.219558, "o", "n"] [90.317806, "o", "d"] [90.376045, "o", " "] [90.423241, "o", "p"] [90.465504, "o", "o"] [90.527757, "o", "s"] [90.536949, "o", "s"] [90.607156, "o", "i"] [90.642411, "o", "b"] [90.651607, "o", "l"] [90.68693, "o", "y"] [90.88709, "o", " "] [90.919294, "o", "p"] [90.942543, "o", "r"] [91.078836, "o", "i"] [91.102079, "o", "n"] [91.246364, "o", "t"] [91.446583, "o", " "] [91.511009, "o", "i"] [91.578199, "o", "t"] [91.704476, "o", ","] [91.741724, "o", " "] [91.750942, "o", "e"] [91.771117, "o", "t"] [91.782313, "o", "c"] [91.803545, "o", "."] [91.865929, "o", "\r\n"] [91.866312, "o", "$ b"] [91.902524, "o", "o"] [91.929281, "o", "r"] [91.938795, "o", "g"] [92.001495, "o", " "] [92.019245, "o", "k"] [92.055794, "o", "e"] [92.148098, "o", "y"] [92.243444, "o", " "] [92.252817, "o", "e"] [92.304265, "o", "x"] [92.313724, "o", "p"] [92.330183, "o", "o"] [92.385758, "o", "r"] [92.438182, "o", "t"] [92.470969, "o", " "] [92.595227, "o", "-"] [92.674856, "o", "-"] [92.687324, "o", "q"] [92.707309, "o", "r"] [92.778481, "o", "-"] [92.920862, "o", "h"] [92.999202, "o", "t"] [93.008599, "o", "m"] [93.062016, "o", "l"] [93.071322, "o", " "] [93.080847, "o", ":"] [93.154772, "o", ":"] [93.171063, "o", " "] [93.180511, "o", "f"] [93.19507, "o", "i"] [93.223267, "o", "l"] [93.267445, "o", "e"] [93.377017, "o", "."] [93.395205, "o", "h"] [93.404616, "o", "t"] [93.466142, "o", "m"] [93.507241, "o", "l"] [93.614841, "o", " "] [93.634433, "o", " "] [93.676532, "o", "#"] [93.695231, "o", " "] [93.708813, "o", "t"] [93.756113, "o", "h"] [93.930579, "o", "i"] [93.982926, "o", "s"] [94.183278, "o", " "] [94.290766, "o", "c"] [94.303095, "o", "r"] [94.407434, "o", "e"] [94.549651, "o", "a"] [94.615164, "o", "t"] [94.700689, "o", "e"] [94.735132, "o", "s"] [94.874051, "o", " "] [94.888609, "o", "a"] [94.975046, "o", " "] [95.023542, "o", "n"] [95.032912, "o", "i"] [95.131124, "o", "c"] [95.189662, "o", "e"] [95.220861, "o", " "] [95.230334, "o", "H"] [95.239715, "o", "T"] [95.303126, "o", "M"] [95.31252, "o", "L"] [95.508921, "o", ","] [95.535693, "o", " "] [95.588304, "o", "b"] [95.624524, "o", "u"] [95.660455, "o", "t"] [95.863076, "o", " "] [95.951101, "o", "w"] [96.019068, "o", "h"] [96.064404, "o", "e"] [96.104629, "o", "n"] [96.122201, "o", " "] [96.159079, "o", "y"] [96.170341, "o", "o"] [96.194989, "o", "u"] [96.242074, "o", " "] [96.251522, "o", "w"] [96.359109, "o", "a"] [96.518803, "o", "n"] [96.57115, "o", "t"] [96.622541, "o", " "] [96.806376, "o", "s"] [96.818953, "o", "o"] [96.897732, "o", "m"] [96.971108, "o", "e"] [97.067593, "o", "t"] [97.103142, "o", "h"] [97.183278, "o", "i"] [97.239756, "o", "n"] [97.259141, "o", "g"] [97.329703, "o", " "] [97.403076, "o", "s"] [97.412422, "o", "i"] [97.53864, "o", "m"] [97.549121, "o", "p"] [97.604583, "o", "l"] [97.635096, "o", "e"] [97.64439, "o", "r"] [97.675134, "o", "…"] [97.814542, "o", "\r\n"] [98.722178, "o", "$ b"] [98.807795, "o", "o"] [98.910336, "o", "r"] [98.919913, "o", "g"] [98.929377, "o", " "] [98.938952, "o", "k"] [98.965665, "o", "e"] [99.11715, "o", "y"] [99.317399, "o", " "] [99.326787, "o", "e"] [99.350517, "o", "x"] [99.359844, "o", "p"] [99.4675, "o", "o"] [99.489996, "o", "r"] [99.589275, "o", "t"] [99.691859, "o", " "] [99.704219, "o", "-"] [99.713707, "o", "-"] [99.743603, "o", "p"] [99.798054, "o", "a"] [99.823767, "o", "p"] [99.885944, "o", "e"] [99.897636, "o", "r"] [99.9767, "o", " "] [100.027386, "o", ":"] [100.052192, "o", ":"] [100.067352, "o", " "] [100.156712, "o", " "] [100.19786, "o", "#"] [100.212631, "o", " "] [100.287297, "o", "t"] [100.29679, "o", "h"] [100.306215, "o", "i"] [100.359208, "o", "s"] [100.374671, "o", " "] [100.474986, "o", "i"] [100.503798, "o", "s"] [100.541912, "o", " "] [100.626098, "o", "a"] [100.666332, "o", " "] [100.697183, "o", "\""] [100.711032, "o", "m"] [100.737775, "o", "a"] [100.758421, "o", "n"] [100.797571, "o", "u"] [100.806956, "o", "a"] [100.87637, "o", "l"] [100.955112, "o", " "] [100.964372, "o", "i"] [100.975112, "o", "n"] [100.986557, "o", "p"] [101.026035, "o", "u"] [101.08241, "o", "t"] [101.103095, "o", "\""] [101.20461, "o", "-"] [101.223075, "o", "o"] [101.264982, "o", "n"] [101.362331, "o", "l"] [101.485717, "o", "y"] [101.686062, "o", " "] [101.695543, "o", "b"] [101.726369, "o", "a"] [101.926959, "o", "c"] [101.936329, "o", "k"] [101.947079, "o", "u"] [101.956316, "o", "p"] [102.123084, "o", " "] [102.163099, "o", "("] [102.176215, "o", "b"] [102.22553, "o", "u"] [102.279082, "o", "t"] [102.396342, "o", " "] [102.483081, "o", "i"] [102.567065, "o", "t"] [102.728838, "o", " "] [102.738243, "o", "i"] [102.786531, "o", "s"] [102.867342, "o", " "] [102.923016, "o", "a"] [102.998953, "o", "l"] [103.035273, "o", "s"] [103.087168, "o", "o"] [103.096547, "o", " "] [103.262476, "o", "i"] [103.347847, "o", "n"] [103.415063, "o", "c"] [103.427066, "o", "l"] [103.439044, "o", "u"] [103.482403, "o", "d"] [103.645195, "o", "e"] [103.655053, "o", "d"] [103.771462, "o", " "] [103.799107, "o", "i"] [103.879238, "o", "n"] [103.979692, "o", " "] [103.996193, "o", "t"] [104.057759, "o", "h"] [104.087079, "o", "e"] [104.175264, "o", " "] [104.315076, "o", "-"] [104.405243, "o", "-"] [104.605975, "o", "q"] [104.631634, "o", "r"] [104.735842, "o", "-"] [104.778042, "o", "c"] [104.788574, "o", "o"] [104.827883, "o", "d"] [104.838226, "o", "e"] [104.911691, "o", " "] [104.92405, "o", "o"] [104.988546, "o", "p"] [105.070922, "o", "t"] [105.121393, "o", "i"] [105.204919, "o", "o"] [105.243122, "o", "n"] [105.267738, "o", ")"] [105.286747, "o", "\r\n"] [106.149824, "o", "To restore key use borg key import --paper /path/to/repo\r\r\n\r\r\nBORG PAPER KEY v1\r\r\nid: 20 / 543b72 bac0b1 5cfa3d / 816b0a 201520 - 52\r\r\n 1: 86a961 6c676f 726974 686da6 736861 323536 - 14\r\r\n 2: a46461 7461da 00de7c 327081 c876c2 86754c - bc\r\r\n 3: dbfb68 784ce2 8047f6 1f7067 063d08 ecbe39 - 60\r\r\n 4: abd2be 2f5165 6a1641 ddbb49 32fc4a c7e391 - 30\r\r\n 5: d90522 c569ed 80d96c 1ae06f 3e50a0 3dbef6 - 0e\r\r\n 6: 499ee6 cacd65 727cc5 cb3ba2 5f7f79 5b852a - 07\r\r\n 7: 7e2b22 2ed3b8 41e1b7 0b7db2 24b1d7 1ba0bb - 80\r\r\n 8: 2967ab 7776fd 5c23c8 5366e9 256824 3d8df1 - d5\r\r\n 9: b2d3d5 c60f1b 5a68b0 7294e9 87fa86 18a987 - b0\r\r\n10: 944388 909b57 df48ce 8a8600 527a8f 637584 - c9\r\r\n11: 170511 8963a1 554fce f2ba95 5a758b 429719 - ee\r\r\n12: 88bee4 9720ba f725de dcdec0 894143 8a7ebc - 29\r\r\n13: a0d2e5 eb0ce8 956541 8cef38 c7194e b8b5fe - e6\r\r\n"] [106.149893, "o", "14: 2b9282 a8c540 ae69e9 0bca48 9b52a4 686173 - ba\r\r\n15: 68da00 207ba4 f1d621 e8edc4 57978b 04fe04 - af\r\r\n16: 45af0a eefb96 051947 106f38 b44520 1002cc - dd\r\r\n17: aa6974 657261 74696f 6e73ce 000186 a0a473 - 15\r\r\n18: 616c74 da0020 738815 8e60fb 2b5cc4 662110 - dd\r\r\n19: 9cdaff 4c7f78 b95f81 57009d ac0c27 f2e160 - 69\r\r\n"] [106.150871, "o", "20: facfa7 766572 73696f 6e01 - 49\r\r\n"] [106.200923, "o", "$ \r\n"] [106.201933, "o", "$ #"] [106.311069, "o", "#"] [106.350876, "o", " "] [106.360114, "o", "M"] [106.381845, "o", "A"] [106.448659, "o", "I"] [106.631657, "o", "N"] [106.819891, "o", "T"] [106.829408, "o", "E"] [106.886858, "o", "N"] [106.897164, "o", "A"] [106.921071, "o", "N"] [106.947049, "o", "C"] [106.959083, "o", "E"] [107.007196, "o", " "] [107.083581, "o", "#"] [107.112356, "o", "#"] [107.250885, "o", "\r\n"] [107.251287, "o", "$ #"] [107.451883, "o", " "] [107.461255, "o", "S"] [107.470909, "o", "o"] [107.657323, "o", "m"] [107.753465, "o", "e"] [107.801524, "o", "t"] [107.848903, "o", "i"] [107.879027, "o", "m"] [107.88862, "o", "e"] [107.977214, "o", "s"] [108.110259, "o", " "] [108.179155, "o", "b"] [108.351926, "o", "a"] [108.400699, "o", "c"] [108.410203, "o", "k"] [108.419646, "o", "u"] [108.486479, "o", "p"] [108.567209, "o", "s"] [108.767773, "o", " "] [108.803208, "o", "g"] [108.846873, "o", "e"] [108.867075, "o", "t"] [108.977561, "o", " "] [109.136181, "o", "b"] [109.200763, "o", "r"] [109.259374, "o", "o"] [109.278942, "o", "k"] [109.3141, "o", "e"] [109.43095, "o", "n"] [109.46321, "o", " "] [109.47754, "o", "o"] [109.588012, "o", "r"] [109.605532, "o", " "] [109.61509, "o", "w"] [109.715362, "o", "e"] [109.724883, "o", " "] [109.782594, "o", "w"] [109.797213, "o", "a"] [109.808586, "o", "n"] [109.900685, "o", "t"] [109.911137, "o", " "] [110.056022, "o", "a"] [110.099232, "o", " "] [110.182919, "o", "r"] [110.203243, "o", "e"] [110.230058, "o", "g"] [110.385779, "o", "u"] [110.39538, "o", "l"] [110.406829, "o", "a"] [110.454471, "o", "r"] [110.539017, "o", " "] [110.61907, "o", "\""] [110.765729, "o", "c"] [110.96699, "o", "h"] [111.035241, "o", "e"] [111.090908, "o", "c"] [111.121846, "o", "k"] [111.214881, "o", "u"] [111.224693, "o", "p"] [111.268192, "o", "\""] [111.372347, "o", " "] [111.387051, "o", "t"] [111.396725, "o", "h"] [111.406456, "o", "a"] [111.422276, "o", "t"] [111.526997, "o", " "] [111.6099, "o", "e"] [111.755036, "o", "v"] [111.867197, "o", "e"] [111.87702, "o", "r"] [111.919062, "o", "y"] [111.986004, "o", "t"] [112.051883, "o", "h"] [112.171044, "o", "i"] [112.21494, "o", "n"] [112.227013, "o", "g"] [112.265067, "o", " "] [112.28199, "o", "i"] [112.400713, "o", "s"] [112.435962, "o", " "] [112.544903, "o", "o"] [112.557869, "o", "k"] [112.652822, "o", "a"] [112.742943, "o", "y"] [112.943874, "o", "…"] [112.989623, "o", "\r\n"] [112.990358, "o", "$ "] [112.990917, "o", "b"] [113.020704, "o", "o"] [113.0326, "o", "r"] [113.062861, "o", "g"] [113.146392, "o", " "] [113.158985, "o", "c"] [113.187026, "o", "h"] [113.230546, "o", "e"] [113.240481, "o", "c"] [113.270414, "o", "k"] [113.298937, "o", " "] [113.42575, "o", "-"] [113.43564, "o", "v"] [113.448634, "o", " "] [113.51099, "o", ":"] [113.572617, "o", ":"] [113.623082, "o", "\r\n"] [114.468125, "o", "Starting repository check\r\r\n"] [114.970867, "o", "finished segment check at segment 29\r\r\n"] [114.973282, "o", "Starting repository index check\r\r\n"] [114.973326, "o", "Index object count match.\r\r\n"] [114.974106, "o", "Finished full repository check, no problems found.\r\r\n"] [114.974507, "o", "Starting archive consistency check...\r\r\n"] [115.054678, "o", "Analyzing archive backup1 (1/6)\r\r\n"] [115.060833, "o", "Analyzing archive backup2 (2/6)\r\r\n"] [115.063603, "o", "Analyzing archive backup3 (3/6)\r\r\n"] [115.066201, "o", "Analyzing archive root-2022-07-06T21:31:12 (4/6)\r\r\n"] [115.068866, "o", "Analyzing archive root-2022-07-06T21:31:28 (5/6)\r\r\n"] [115.069725, "o", "Analyzing archive backup-block-device (6/6)\r\r\n"] [115.070801, "o", "Archive consistency check complete, no problems found.\r\r\n"] [115.13231, "o", "$ \r\n"] [115.132383, "o", "$ #"] [115.141656, "o", " "] [115.157976, "o", "N"] [115.18922, "o", "e"] [115.20151, "o", "x"] [115.354755, "o", "t"] [115.495108, "o", " "] [115.504182, "o", "p"] [115.514429, "o", "r"] [115.560709, "o", "o"] [115.700955, "o", "b"] [115.710156, "o", "l"] [115.889298, "o", "e"] [115.900524, "o", "m"] [115.948777, "o", ":"] [116.100016, "o", " "] [116.127186, "o", "U"] [116.273465, "o", "s"] [116.28276, "o", "u"] [116.372945, "o", "a"] [116.390155, "o", "l"] [116.399296, "o", "l"] [116.408552, "o", "y"] [116.481705, "o", " "] [116.571974, "o", "y"] [116.583156, "o", "o"] [116.609289, "o", "u"] [116.687464, "o", " "] [116.744699, "o", "d"] [116.753796, "o", "o"] [116.894075, "o", " "] [116.903189, "o", "n"] [117.10336, "o", "o"] [117.11259, "o", "t"] [117.185751, "o", " "] [117.197017, "o", "h"] [117.397254, "o", "a"] [117.406473, "o", "v"] [117.415684, "o", "e"] [117.61584, "o", " "] [117.643157, "o", "i"] [117.652315, "o", "n"] [117.757616, "o", "f"] [117.781874, "o", "i"] [117.791149, "o", "n"] [117.80637, "o", "i"] [117.827596, "o", "t"] [117.854946, "o", "e"] [118.055148, "o", " "] [118.064193, "o", "d"] [118.073447, "o", "i"] [118.088702, "o", "s"] [118.105948, "o", "k"] [118.115157, "o", " "] [118.155353, "o", "s"] [118.272461, "o", "p"] [118.288573, "o", "a"] [118.461837, "o", "c"] [118.474126, "o", "e"] [118.67441, "o", "."] [118.748669, "o", " "] [118.93194, "o", "S"] [119.010054, "o", "o"] [119.085321, "o", " "] [119.094526, "o", "y"] [119.112808, "o", "o"] [119.148083, "o", "u"] [119.291238, "o", " "] [119.300456, "o", "m"] [119.344754, "o", "a"] [119.405028, "o", "y"] [119.48428, "o", " "] [119.508528, "o", "n"] [119.551619, "o", "e"] [119.613877, "o", "e"] [119.638147, "o", "d"] [119.731323, "o", " "] [119.818835, "o", "t"] [119.86185, "o", "o"] [119.947949, "o", " "] [120.135432, "o", "p"] [120.182617, "o", "r"] [120.230827, "o", "u"] [120.240039, "o", "n"] [120.27029, "o", "e"] [120.289402, "o", " "] [120.436641, "o", "y"] [120.471903, "o", "o"] [120.595072, "o", "u"] [120.604288, "o", "r"] [120.631553, "o", " "] [120.66179, "o", "a"] [120.782954, "o", "r"] [120.792152, "o", "c"] [120.992294, "o", "h"] [121.117555, "o", "i"] [121.126807, "o", "v"] [121.161076, "o", "e"] [121.220316, "o", "…"] [121.23366, "o", "\r\n$ #"] [121.242855, "o", " "] [121.321154, "o", "Y"] [121.352419, "o", "o"] [121.386668, "o", "u"] [121.456777, "o", " "] [121.506033, "o", "c"] [121.515201, "o", "a"] [121.535296, "o", "n"] [121.55855, "o", " "] [121.588801, "o", "t"] [121.597994, "o", "u"] [121.642252, "o", "n"] [121.651435, "o", "e"] [121.66472, "o", " "] [121.673911, "o", "t"] [121.683098, "o", "h"] [121.692328, "o", "i"] [121.733587, "o", "s"] [121.933824, "o", " "] [121.94297, "o", "i"] [122.023164, "o", "n"] [122.073434, "o", " "] [122.197695, "o", "e"] [122.242944, "o", "v"] [122.267127, "o", "e"] [122.313365, "o", "r"] [122.375621, "o", "y"] [122.480878, "o", " "] [122.49007, "o", "d"] [122.555315, "o", "e"] [122.590572, "o", "t"] [122.601763, "o", "a"] [122.618969, "o", "i"] [122.818095, "o", "l"] [122.938459, "o", "."] [123.01755, "o", " "] [123.064808, "o", "S"] [123.106838, "o", "e"] [123.221128, "o", "e"] [123.310401, "o", " "] [123.319655, "o", "t"] [123.508935, "o", "h"] [123.519109, "o", "e"] [123.71934, "o", " "] [123.728563, "o", "d"] [123.741784, "o", "o"] [123.850044, "o", "c"] [123.989291, "o", "s"] [124.071531, "o", " "] [124.156791, "o", "f"] [124.301032, "o", "o"] [124.31025, "o", "r"] [124.510545, "o", " "] [124.608805, "o", "d"] [124.674988, "o", "e"] [124.684186, "o", "t"] [124.715408, "o", "a"] [124.727663, "o", "i"] [124.805915, "o", "l"] [124.842176, "o", "s"] [125.042443, "o", "."] [125.059688, "o", " "] [125.093771, "o", "H"] [125.102879, "o", "e"] [125.112002, "o", "r"] [125.2784, "o", "e"] [125.440604, "o", " "] [125.468854, "o", "o"] [125.507037, "o", "n"] [125.538304, "o", "l"] [125.547524, "o", "y"] [125.587786, "o", " "] [125.599977, "o", "a"] [125.622256, "o", " "] [125.63238, "o", "s"] [125.641635, "o", "i"] [125.652865, "o", "m"] [125.662088, "o", "p"] [125.671289, "o", "l"] [125.709579, "o", "e"] [125.820831, "o", " "] [125.888074, "o", "e"] [125.943287, "o", "x"] [125.996545, "o", "a"] [126.019798, "o", "m"] [126.028994, "o", "p"] [126.108255, "o", "l"] [126.154535, "o", "e"] [126.203713, "o", ":"] [126.247947, "o", "\r\n"] [126.248396, "o", "$ b"] [126.257524, "o", "o"] [126.310761, "o", "r"] [126.33483, "o", "g"] [126.346943, "o", " "] [126.40812, "o", "p"] [126.417361, "o", "r"] [126.475452, "o", "u"] [126.484575, "o", "n"] [126.538756, "o", "e"] [126.738889, "o", " "] [126.869013, "o", "-"] [127.069159, "o", "-"] [127.192274, "o", "l"] [127.201392, "o", "i"] [127.211523, "o", "s"] [127.220653, "o", "t"] [127.231777, "o", " "] [127.240901, "o", "-"] [127.320174, "o", "-"] [127.346272, "o", "k"] [127.407534, "o", "e"] [127.445718, "o", "e"] [127.454727, "o", "p"] [127.499843, "o", "-"] [127.554949, "o", "l"] [127.569072, "o", "a"] [127.699194, "o", "s"] [127.715297, "o", "t"] [127.769524, "o", " "] [127.80476, "o", "1"] [127.827003, "o", " "] [127.844259, "o", "-"] [127.981519, "o", "-"] [128.022874, "o", "d"] [128.033089, "o", "r"] [128.084347, "o", "y"] [128.201627, "o", "-"] [128.30488, "o", "r"] [128.344133, "o", "u"] [128.35339, "o", "n"] [128.437824, "o", "\r\n"] [129.45361, "o", "Keeping archive (rule: secondly #1): backup-block-device Wed, 2022-07-06 21:31:36 [c416cbf604ab7dce6512f4d48cb9ea3fc56e311d9c794e2ec0814703a7561551]"] [129.454727, "o", "\r\r\n"] [129.455338, "o", "Would prune: root-2022-07-06T21:31:28 Wed, 2022-07-06 21:31:28 [d292c218f6c32423a9de2e3fbdf51c8ee865dc83ac78f62624216ebdae7ca55e]"] [129.455722, "o", "\r\r\n"] [129.456283, "o", "Would prune: root-2022-07-06T21:31:12 Wed, 2022-07-06 21:31:12 [280fe9e3d92e2e61f04f0ef98de32b4d0a797ec9e3b0b29ddb2b0946c4a0236b]"] [129.456658, "o", "\r\r\n"] [129.457213, "o", "Would prune: backup3 Wed, 2022-07-06 21:30:32 [b715e25edecebd717c06cca04bcc1dc73a73fd87a9b126d1f190a15a068c0f69]"] [129.457618, "o", "\r\r\n"] [129.458159, "o", "Would prune: backup2 Wed, 2022-07-06 21:30:31 [cbd3400dcea913c0611a75681c2e2ee192d7b9f915f6d9e8e77cbabde976c239]"] [129.458583, "o", "\r\r\n"] [129.459193, "o", "Would prune: backup1 Wed, 2022-07-06 21:30:25 [a1f88905bc1f341893c92a4757ab3975b4dcd36ca6669dabc76c5ea073a9095b]"] [129.459581, "o", "\r\r\n"] [129.51897, "o", "$ #"] [129.625084, "o", " "] [129.65835, "o", "W"] [129.681607, "o", "h"] [129.772895, "o", "e"] [129.806128, "o", "n"] [129.834381, "o", " "] [129.938653, "o", "a"] [129.947855, "o", "c"] [130.001121, "o", "t"] [130.022385, "o", "u"] [130.043634, "o", "a"] [130.110916, "o", "l"] [130.174201, "o", "l"] [130.183388, "o", "y"] [130.328666, "o", " "] [130.379911, "o", "e"] [130.398181, "o", "x"] [130.555408, "o", "e"] [130.684651, "o", "c"] [130.717891, "o", "u"] [130.732147, "o", "t"] [130.831375, "o", "i"] [130.917655, "o", "n"] [130.946912, "o", "g"] [130.978155, "o", " "] [130.994409, "o", "i"] [131.003618, "o", "t"] [131.174879, "o", " "] [131.184112, "o", "i"] [131.236365, "o", "n"] [131.270612, "o", " "] [131.27982, "o", "a"] [131.294113, "o", " "] [131.322373, "o", "s"] [131.376617, "o", "c"] [131.40187, "o", "r"] [131.458129, "o", "i"] [131.476372, "o", "p"] [131.505657, "o", "t"] [131.545909, "o", ","] [131.600166, "o", " "] [131.642413, "o", "y"] [131.651619, "o", "o"] [131.675712, "o", "u"] [131.767878, "o", " "] [131.814969, "o", "h"] [131.894152, "o", "a"] [131.951416, "o", "v"] [131.993679, "o", "e"] [132.060965, "o", " "] [132.070207, "o", "t"] [132.115432, "o", "o"] [132.128652, "o", " "] [132.199925, "o", "u"] [132.281178, "o", "s"] [132.306425, "o", "e"] [132.473677, "o", " "] [132.526977, "o", "i"] [132.539148, "o", "t"] [132.739381, "o", " "] [132.75765, "o", "w"] [132.773923, "o", "i"] [132.859107, "o", "t"] [132.871348, "o", "h"] [132.932614, "o", "o"] [132.965873, "o", "u"] [132.990116, "o", "t"] [133.039327, "o", " "] [133.118587, "o", "t"] [133.16384, "o", "h"] [133.197097, "o", "e"] [133.258298, "o", " "] [133.26755, "o", "-"] [133.30881, "o", "-"] [133.356063, "o", "d"] [133.431264, "o", "r"] [133.579521, "o", "y"] [133.598889, "o", "-"] [133.611051, "o", "r"] [133.627291, "o", "u"] [133.682531, "o", "n"] [133.699785, "o", " "] [133.752037, "o", "o"] [133.83033, "o", "p"] [133.958481, "o", "t"] [133.98858, "o", "i"] [134.144897, "o", "o"] [134.211076, "o", "n"] [134.222281, "o", ","] [134.363538, "o", " "] [134.397804, "o", "o"] [134.408005, "o", "f"] [134.608297, "o", " "] [134.626522, "o", "c"] [134.635763, "o", "o"] [134.694895, "o", "u"] [134.774151, "o", "r"] [134.815387, "o", "s"] [134.911629, "o", "e"] [134.955897, "o", "."] [134.9656, "o", "\r\n$ \r\n"] [134.965766, "o", "$ #"] [134.974883, "o", "#"] [135.032143, "o", " "] [135.212403, "o", "R"] [135.320667, "o", "E"] [135.34591, "o", "S"] [135.355073, "o", "T"] [135.462336, "o", "O"] [135.520591, "o", "R"] [135.564825, "o", "E"] [135.574095, "o", " "] [135.583294, "o", "#"] [135.59252, "o", "#"] [135.76985, "o", "\r\n"] [135.77021, "o", "$ \r\n"] [135.770371, "o", "$ #"] [135.802643, "o", " "] [135.831898, "o", "W"] [135.841141, "o", "h"] [136.024428, "o", "e"] [136.074669, "o", "n"] [136.083752, "o", " "] [136.092876, "o", "y"] [136.177024, "o", "o"] [136.211126, "o", "u"] [136.308445, "o", " "] [136.317628, "o", "w"] [136.326945, "o", "a"] [136.527133, "o", "n"] [136.545348, "o", "t"] [136.572621, "o", " "] [136.59388, "o", "t"] [136.621124, "o", "o"] [136.74037, "o", " "] [136.759632, "o", "s"] [136.768838, "o", "e"] [136.870121, "o", "e"] [136.933357, "o", " "] [136.998616, "o", "t"] [137.01787, "o", "h"] [137.106114, "o", "e"] [137.256375, "o", " "] [137.284629, "o", "d"] [137.476905, "o", "i"] [137.546163, "o", "f"] [137.746423, "o", "f"] [137.755642, "o", " "] [137.955914, "o", "b"] [137.968135, "o", "e"] [138.083376, "o", "t"] [138.15063, "o", "w"] [138.19896, "o", "e"] [138.259157, "o", "e"] [138.339242, "o", "n"] [138.362365, "o", " "] [138.547758, "o", "t"] [138.574956, "o", "w"] [138.597213, "o", "o"] [138.797472, "o", " "] [138.80677, "o", "a"] [138.83602, "o", "r"] [138.845271, "o", "c"] [138.882571, "o", "h"] [138.932833, "o", "i"] [138.976096, "o", "v"] [139.069357, "o", "e"] [139.101615, "o", "s"] [139.160879, "o", " "] [139.170074, "o", "u"] [139.181311, "o", "s"] [139.254593, "o", "e"] [139.454948, "o", " "] [139.655134, "o", "t"] [139.840393, "o", "h"] [139.894686, "o", "i"] [139.929925, "o", "s"] [140.130188, "o", " "] [140.198433, "o", "c"] [140.233719, "o", "o"] [140.390928, "o", "m"] [140.418192, "o", "m"] [140.432452, "o", "a"] [140.475737, "o", "n"] [140.586824, "o", "d"] [140.757161, "o", "."] [140.833578, "o", "\r\n"] [140.834316, "o", "$ #"] [140.843373, "o", " "] [140.964668, "o", "E"] [141.09793, "o", "."] [141.108182, "o", "g"] [141.151418, "o", "."] [141.19968, "o", " "] [141.232918, "o", "w"] [141.433168, "o", "h"] [141.444444, "o", "a"] [141.463703, "o", "t"] [141.490935, "o", " "] [141.51719, "o", "h"] [141.5274, "o", "a"] [141.557672, "o", "p"] [141.711926, "o", "p"] [141.813187, "o", "e"] [141.842439, "o", "n"] [141.974914, "o", "e"] [142.011958, "o", "d"] [142.141211, "o", " "] [142.196474, "o", "b"] [142.205729, "o", "e"] [142.218871, "o", "t"] [142.268159, "o", "w"] [142.281389, "o", "e"] [142.295658, "o", "e"] [142.485918, "o", "n"] [142.499102, "o", " "] [142.59036, "o", "t"] [142.662621, "o", "h"] [142.86285, "o", "e"] [143.053083, "o", " "] [143.109365, "o", "f"] [143.174629, "o", "i"] [143.183817, "o", "r"] [143.264078, "o", "s"] [143.273304, "o", "t"] [143.347578, "o", " "] [143.38783, "o", "t"] [143.397029, "o", "w"] [143.445305, "o", "o"] [143.604558, "o", " "] [143.61982, "o", "b"] [143.662984, "o", "a"] [143.677246, "o", "c"] [143.68947, "o", "k"] [143.718812, "o", "u"] [143.750062, "o", "p"] [143.878314, "o", "s"] [144.014587, "o", "?"] [144.024937, "o", "\r\n"] [144.025577, "o", "$ b"] [144.103775, "o", "o"] [144.11397, "o", "r"] [144.314265, "o", "g"] [144.323463, "o", " "] [144.33574, "o", "d"] [144.404002, "o", "i"] [144.413199, "o", "f"] [144.422432, "o", "f"] [144.492721, "o", " "] [144.616987, "o", ":"] [144.735188, "o", ":"] [144.887429, "o", "b"] [144.913512, "o", "a"] [144.976805, "o", "c"] [145.012882, "o", "k"] [145.050988, "o", "u"] [145.06411, "o", "p"] [145.073236, "o", "1"] [145.122529, "o", " "] [145.172773, "o", "b"] [145.274964, "o", "a"] [145.329206, "o", "c"] [145.340477, "o", "k"] [145.451743, "o", "u"] [145.559999, "o", "p"] [145.587202, "o", "2"] [145.630681, "o", "\r\n"] [146.646473, "o", "added 14 B Wallpaper/newfile.txt"] [146.647431, "o", "\r\r\n"] [146.706774, "o", "$ #"] [146.716059, "o", " "] [146.755468, "o", "A"] [146.901789, "o", "h"] [147.035155, "o", ","] [147.070453, "o", " "] [147.132464, "o", "w"] [147.170608, "o", "e"] [147.298155, "o", " "] [147.328469, "o", "a"] [147.358818, "o", "d"] [147.434065, "o", "d"] [147.500279, "o", "e"] [147.523549, "o", "d"] [147.583913, "o", " "] [147.642189, "o", "a"] [147.724483, "o", " "] [147.733697, "o", "f"] [147.744978, "o", "i"] [147.754216, "o", "l"] [147.764569, "o", "e"] [147.773825, "o", ","] [147.807202, "o", " "] [147.892481, "o", "r"] [148.021787, "o", "i"] [148.148093, "o", "g"] [148.228377, "o", "h"] [148.312729, "o", "t"] [148.476959, "o", "…"] [148.677268, "o", "\r\n"] [148.678159, "o", "$ \r\n"] [148.678976, "o", "$ #"] [148.821721, "o", " "] [148.839132, "o", "T"] [148.848356, "o", "h"] [148.87758, "o", "e"] [148.951243, "o", "r"] [149.035959, "o", "e"] [149.239284, "o", " "] [149.272524, "o", "a"] [149.288132, "o", "r"] [149.299294, "o", "e"] [149.32949, "o", " "] [149.404801, "o", "a"] [149.425843, "o", "l"] [149.459032, "o", "s"] [149.490519, "o", "o"] [149.691261, "o", " "] [149.74465, "o", "o"] [149.775182, "o", "t"] [149.849871, "o", "h"] [149.940396, "o", "e"] [149.97516, "o", "r"] [149.984609, "o", " "] [150.015151, "o", "w"] [150.025493, "o", "a"] [150.179023, "o", "y"] [150.379968, "o", "s"] [150.528559, "o", " "] [150.566363, "o", "t"] [150.766933, "o", "o"] [150.892125, "o", " "] [150.95061, "o", "e"] [151.015184, "o", "x"] [151.191108, "o", "t"] [151.269469, "o", "r"] [151.278897, "o", "a"] [151.344629, "o", "c"] [151.37102, "o", "t"] [151.57479, "o", " "] [151.779091, "o", "t"] [151.807215, "o", "h"] [151.817017, "o", "e"] [151.826812, "o", " "] [151.837693, "o", "d"] [151.870989, "o", "a"] [151.906995, "o", "t"] [151.935865, "o", "a"] [151.951634, "o", "."] [151.961314, "o", "\r\n"] [151.961915, "o", "$ "] [151.962215, "o", "#"] [152.087058, "o", " "] [152.291084, "o", "E"] [152.491848, "o", "."] [152.643011, "o", "g"] [152.667003, "o", "."] [152.679008, "o", " "] [152.738984, "o", "a"] [152.750962, "o", "s"] [152.850896, "o", " "] [152.862923, "o", "a"] [153.066925, "o", " "] [153.09013, "o", "t"] [153.20094, "o", "a"] [153.254436, "o", "r"] [153.267051, "o", " "] [153.299031, "o", "a"] [153.342374, "o", "r"] [153.38735, "o", "c"] [153.433121, "o", "h"] [153.4661, "o", "i"] [153.508774, "o", "v"] [153.585774, "o", "e"] [153.630565, "o", "."] [153.675714, "o", "\r\n"] [153.676317, "o", "$ "] [153.676694, "o", "b"] [153.762637, "o", "o"] [153.772338, "o", "r"] [153.781992, "o", "g"] [153.912578, "o", " "] [153.92756, "o", "e"] [154.013549, "o", "x"] [154.066538, "o", "p"] [154.076608, "o", "o"] [154.140459, "o", "r"] [154.16366, "o", "t"] [154.364435, "o", "-"] [154.408099, "o", "t"] [154.418034, "o", "a"] [154.428065, "o", "r"] [154.455016, "o", " "] [154.469614, "o", "-"] [154.579003, "o", "-"] [154.599039, "o", "p"] [154.717754, "o", "r"] [154.735811, "o", "o"] [154.763071, "o", "g"] [154.775033, "o", "r"] [154.882988, "o", "e"] [155.083346, "o", "s"] [155.146533, "o", "s"] [155.189637, "o", " "] [155.23149, "o", ":"] [155.267181, "o", ":"] [155.330171, "o", "b"] [155.462667, "o", "a"] [155.477237, "o", "c"] [155.494642, "o", "k"] [155.503974, "o", "u"] [155.515675, "o", "p"] [155.595249, "o", "2"] [155.604431, "o", " "] [155.614019, "o", "b"] [155.643395, "o", "a"] [155.652943, "o", "c"] [155.691221, "o", "k"] [155.734922, "o", "u"] [155.744399, "o", "p"] [155.753882, "o", "."] [155.802861, "o", "t"] [155.977131, "o", "a"] [155.986236, "o", "r"] [156.087515, "o", "."] [156.136273, "o", "g"] [156.160903, "o", "z"] [156.198554, "o", "\r\n"] [157.172127, "o", "Calculating size \r"] [157.219086, "o", " 0.0% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.232012, "o", " 0.1% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.247489, "o", " 0.2% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.262907, "o", " 0.3% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.279081, "o", " 0.4% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.300524, "o", " 0.5% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.315831, "o", " 0.6% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.330977, "o", " 0.7% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.345515, "o", " 0.8% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.360863, "o", " 0.9% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.375449, "o", " 1.0% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.408876, "o", " 1.1% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.422884, "o", " 1.2% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.438354, "o", " 1.3% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.452712, "o", " 1.4% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.467172, "o", " 1.5% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.499915, "o", " 1.6% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.515169, "o", " 1.7% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.529275, "o", " 1.8% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.543338, "o", " 1.9% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.581406, "o", " 2.0% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.596978, "o", " 2.1% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.611373, "o", " 2.2% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.626227, "o", " 2.3% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.640994, "o", " 2.4% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.725886, "o", " 2.5% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.745778, "o", " 2.6% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.760489, "o", " 2.7% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.775759, "o", " 2.8% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.791933, "o", " 2.9% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.80658, "o", " 3.0% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.821733, "o", " 3.1% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.836702, "o", " 3.2% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.85165, "o", " 3.3% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.868112, "o", " 3.4% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.882944, "o", " 3.5% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.897961, "o", " 3.6% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.912838, "o", " 3.7% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.92886, "o", " 3.8% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.943857, "o", " 3.9% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [157.959085, "o", " 4.0% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.006007, "o", " 4.1% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.025829, "o", " 4.2% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.041193, "o", " 4.3% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.056682, "o", " 4.4% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.072103, "o", " 4.5% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.088539, "o", " 4.6% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.103897, "o", " 4.7% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.119636, "o", " 4.8% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.134689, "o", " 4.9% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.205307, "o", " 5.0% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.221344, "o", " 5.1% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.236706, "o", " 5.2% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.252215, "o", " 5.3% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.268821, "o", " 5.4% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.284465, "o", " 5.5% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.300428, "o", " 5.6% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.315921, "o", " 5.7% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.332981, "o", " 5.8% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.348576, "o", " 5.9% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.364666, "o", " 6.0% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.380214, "o", " 6.1% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.397047, "o", " 6.2% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.412564, "o", " 6.3% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.42787, "o", " 6.4% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.443443, "o", " 6.5% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.48121, "o", " 6.6% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.500545, "o", " 6.7% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.516723, "o", " 6.8% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.533792, "o", " 6.9% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.548206, "o", " 7.0% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.58743, "o", " 7.1% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.607343, "o", " 7.2% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.622246, "o", " 7.3% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.637241, "o", " 7.4% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.653785, "o", " 7.5% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.669092, "o", " 7.6% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.68441, "o", " 7.7% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.704085, "o", " 7.8% Processing: Wallpaper/bigcollection/Pse...nian_001_OpenCL_45154214_8K.jpg\r"] [158.744556, "o", " 7.9% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [158.760259, "o", " 8.0% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [158.776392, "o", " 8.1% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [158.793498, "o", " 8.2% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [158.8327, "o", " 8.3% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [158.850863, "o", " 8.4% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [158.866562, "o", " 8.5% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [158.88326, "o", " 8.6% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [158.899004, "o", " 8.7% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [158.918108, "o", " 8.8% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [158.947114, "o", " 8.9% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [158.969053, "o", " 9.0% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [158.984936, "o", " 9.1% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.010189, "o", " 9.2% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.028139, "o", " 9.3% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.108276, "o", " 9.4% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.127413, "o", " 9.5% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.142793, "o", " 9.6% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.158279, "o", " 9.7% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.17327, "o", " 9.8% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.189834, "o", " 9.9% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.205214, "o", " 10.0% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.220659, "o", " 10.1% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.236247, "o", " 10.2% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.25289, "o", " 10.3% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.268312, "o", " 10.4% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.284074, "o", " 10.5% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.300031, "o", " 10.6% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.332223, "o", " 10.7% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.34777, "o", " 10.8% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.38522, "o", " 10.9% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.402322, "o", " 11.0% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.419323, "o", " 11.1% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.434845, "o", " 11.2% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.449991, "o", " 11.3% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.465728, "o", " 11.4% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.504174, "o", " 11.5% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.521585, "o", " 11.6% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.53715, "o", " 11.7% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.553172, "o", " 11.8% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.570076, "o", " 11.9% Processing: Wallpaper/bigcollection/Men...bulb_OpenCL_528814521414_8K.jpg\r"] [159.592397, "o", " 12.0% Processing: Wallpaper/bigcollection/Gre...ng_under_tree_in_foreground.jpg\r"] [159.613245, "o", " 12.1% Processing: Wallpaper/bigcollection/Gre...ng_under_tree_in_foreground.jpg\r"] [159.64795, "o", " 12.2% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.663153, "o", " 12.3% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.679807, "o", " 12.4% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.695152, "o", " 12.5% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.744481, "o", " 12.6% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.760973, "o", " 12.7% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.776544, "o", " 12.8% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.792091, "o", " 12.9% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.807627, "o", " 13.0% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.824415, "o", " 13.1% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.839658, "o", " 13.2% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.856632, "o", " 13.3% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.886813, "o", " 13.4% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.907035, "o", " 13.5% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.922753, "o", " 13.6% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.938148, "o", " 13.7% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.969351, "o", " 13.8% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [159.986611, "o", " 13.9% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.002259, "o", " 14.0% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.018123, "o", " 14.1% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.066077, "o", " 14.2% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.0814, "o", " 14.3% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.098185, "o", " 14.4% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.11377, "o", " 14.5% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.185687, "o", " 14.6% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.204434, "o", " 14.7% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.220251, "o", " 14.8% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.236801, "o", " 14.9% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.25281, "o", " 15.0% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.269864, "o", " 15.1% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.284684, "o", " 15.2% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.301911, "o", " 15.3% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.318073, "o", " 15.4% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.339029, "o", " 15.5% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.356267, "o", " 15.6% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.372204, "o", " 15.7% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.388185, "o", " 15.8% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.404526, "o", " 15.9% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.421391, "o", " 16.0% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.447583, "o", " 16.1% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.466393, "o", " 16.2% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.485417, "o", " 16.3% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.519291, "o", " 16.4% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.53441, "o", " 16.5% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.551411, "o", " 16.6% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.567197, "o", " 16.7% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.606136, "o", " 16.8% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.621928, "o", " 16.9% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.638135, "o", " 17.0% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.653872, "o", " 17.1% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.669626, "o", " 17.2% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.694603, "o", " 17.3% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.712689, "o", " 17.4% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.756749, "o", " 17.5% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.772771, "o", " 17.6% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.789625, "o", " 17.7% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.805609, "o", " 17.8% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.821556, "o", " 17.9% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.838879, "o", " 18.0% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.871146, "o", " 18.1% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.889911, "o", " 18.2% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.905901, "o", " 18.3% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.925349, "o", " 18.4% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.941677, "o", " 18.5% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.978467, "o", " 18.6% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [160.994806, "o", " 18.7% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.01088, "o", " 18.8% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.027933, "o", " 18.9% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.082066, "o", " 19.0% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.098727, "o", " 19.1% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.114477, "o", " 19.2% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.131563, "o", " 19.3% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.145861, "o", " 19.4% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.162872, "o", " 19.5% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.178381, "o", " 19.6% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.195497, "o", " 19.7% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.210998, "o", " 19.8% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.226604, "o", " 19.9% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.281901, "o", " 20.0% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.301155, "o", " 20.1% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.317754, "o", " 20.2% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.333414, "o", " 20.3% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.349715, "o", " 20.4% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.367718, "o", " 20.5% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.386536, "o", " 20.6% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.402073, "o", " 20.7% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.417741, "o", " 20.8% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.434854, "o", " 20.9% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.449254, "o", " 21.0% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.466199, "o", " 21.1% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.508892, "o", " 21.2% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.525813, "o", " 21.3% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.541559, "o", " 21.4% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.557149, "o", " 21.5% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.572844, "o", " 21.6% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.588465, "o", " 21.7% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.645283, "o", " 21.8% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.659307, "o", " 21.9% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.676184, "o", " 22.0% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.691533, "o", " 22.1% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.70822, "o", " 22.2% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.723919, "o", " 22.3% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.73965, "o", " 22.4% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.755492, "o", " 22.5% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.772282, "o", " 22.6% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.787919, "o", " 22.7% Processing: Wallpaper/bigcollection/Men...Fold_Box_OpenCL_18915424_8K.jpg\r"] [161.831364, "o", " 22.8% Processing: Wallpaper/bigcollection/KIFS_OpenCL_54815_5K.jpg \r"] [161.8508, "o", " 22.9% Processing: Wallpaper/bigcollection/KIF...penCL_54815_5K.jpg \r"] [161.872417, "o", " 23.0% Processing: Wallpaper/bigcollection/KIF...penCL_54815_5K.jpg \r"] [161.893537, "o", " 23.1% Processing: Wallpaper/bigcollection/KIF...penCL_54815_5K.jpg \r"] [161.91235, "o", " 23.2% Processing: Wallpaper/bigcollection/KIF...penCL_54815_5K.jpg \r"] [161.929587, "o", " 23.3% Processing: Wallpaper/bigcollection/KIF...penCL_54815_5K.jpg \r"] [161.968449, "o", " 23.4% Processing: Wallpaper/bigcollection/KIF...penCL_54815_5K.jpg \r"] [161.990515, "o", " 23.5% Processing: Wallpaper/bigcollection/KIF...penCL_54815_5K.jpg \r"] [162.00623, "o", " 23.6% Processing: Wallpaper/bigcollection/KIF...penCL_54815_5K.jpg \r"] [162.023164, "o", " 23.7% Processing: Wallpaper/bigcollection/KIF...penCL_54815_5K.jpg \r"] [162.039545, "o", " 23.8% Processing: Wallpaper/bigcollection/KIF...penCL_54815_5K.jpg \r"] [162.05695, "o", " 23.9% Processing: Wallpaper/bigcollection/KIF...penCL_54815_5K.jpg \r"] [162.085475, "o", " 24.0% Processing: Wallpaper/bigcollection/ProjectStealth.png \r"] [162.099031, "o", " 24.1% Processing: Wallpaper/bigcollection/Pro...tStealth.png \r"] [162.112845, "o", " 24.2% Processing: Wallpaper/bigcollection/Pro...tStealth.png \r"] [162.131599, "o", " 24.3% Processing: Wallpaper/bigcollection/Pro...tStealth.png \r"] [162.158098, "o", " 24.4% Processing: Wallpaper/bigcollection/KIFS_OpenCL_5434735835_5K.jpg \r"] [162.173267, "o", " 24.5% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.244066, "o", " 24.6% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.260231, "o", " 24.7% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.275372, "o", " 24.8% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.290501, "o", " 24.9% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.305736, "o", " 25.0% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.322345, "o", " 25.1% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.337761, "o", " 25.2% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.353173, "o", " 25.3% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.368582, "o", " 25.4% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.383992, "o", " 25.5% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.400764, "o", " 25.6% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.416352, "o", " 25.7% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.432005, "o", " 25.8% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.447552, "o", " 25.9% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.46454, "o", " 26.0% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.480048, "o", " 26.1% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.514279, "o", " 26.2% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.529417, "o", " 26.3% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.546059, "o", " 26.4% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.561448, "o", " 26.5% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.576586, "o", " 26.6% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.611597, "o", " 26.7% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.631237, "o", " 26.8% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.646638, "o", " 26.9% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.66206, "o", " 27.0% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.677412, "o", " 27.1% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.693949, "o", " 27.2% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.724496, "o", " 27.3% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.739752, "o", " 27.4% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.755092, "o", " 27.5% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.781252, "o", " 27.6% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.797449, "o", " 27.7% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.827407, "o", " 27.8% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.847733, "o", " 27.9% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.864316, "o", " 28.0% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.879641, "o", " 28.1% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.896474, "o", " 28.2% Processing: Wallpaper/bigcollection/KIF...penCL_5434735835_5K.jpg \r"] [162.95962, "o", " 28.3% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [162.974269, "o", " 28.4% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [162.988897, "o", " 28.5% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.003383, "o", " 28.6% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.018078, "o", " 28.7% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.03435, "o", " 28.8% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.049134, "o", " 28.9% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.06415, "o", " 29.0% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.079264, "o", " 29.1% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.095033, "o", " 29.2% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.109954, "o", " 29.3% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.145147, "o", " 29.4% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.163279, "o", " 29.5% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.17801, "o", " 29.6% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.193503, "o", " 29.7% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.208067, "o", " 29.8% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.222848, "o", " 29.9% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.265416, "o", " 30.0% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.281955, "o", " 30.1% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.296749, "o", " 30.2% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.311728, "o", " 30.3% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.32726, "o", " 30.4% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.343787, "o", " 30.5% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.358255, "o", " 30.6% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.386172, "o", " 30.7% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.401217, "o", " 30.8% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.487853, "o", " 30.9% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.50415, "o", " 31.0% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.518983, "o", " 31.1% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.535014, "o", " 31.2% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.549825, "o", " 31.3% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.564416, "o", " 31.4% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.579236, "o", " 31.5% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.593644, "o", " 31.6% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.60972, "o", " 31.7% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.624526, "o", " 31.8% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.639323, "o", " 31.9% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.654362, "o", " 32.0% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.670841, "o", " 32.1% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.686164, "o", " 32.2% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.702037, "o", " 32.3% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.717948, "o", " 32.4% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.733275, "o", " 32.5% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.749689, "o", " 32.6% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.764971, "o", " 32.7% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.792328, "o", " 32.8% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.809063, "o", " 32.9% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.825566, "o", " 33.0% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.862521, "o", " 33.1% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.877663, "o", " 33.2% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.892846, "o", " 33.3% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.909113, "o", " 33.4% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.933228, "o", " 33.5% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.950611, "o", " 33.6% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [163.965592, "o", " 33.7% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.010098, "o", " 33.8% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.026093, "o", " 33.9% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.041194, "o", " 34.0% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.05754, "o", " 34.1% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.072675, "o", " 34.2% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.087708, "o", " 34.3% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.105121, "o", " 34.4% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.205315, "o", " 34.5% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.221054, "o", " 34.6% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.236048, "o", " 34.7% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.251256, "o", " 34.8% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.266101, "o", " 34.9% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.282491, "o", " 35.0% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.297453, "o", " 35.1% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.312481, "o", " 35.2% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.327597, "o", " 35.3% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.342124, "o", " 35.4% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.358177, "o", " 35.5% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.373151, "o", " 35.6% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.38827, "o", " 35.7% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.403196, "o", " 35.8% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.419188, "o", " 35.9% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.434077, "o", " 36.0% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.449026, "o", " 36.1% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.463925, "o", " 36.2% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.479708, "o", " 36.3% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.494372, "o", " 36.4% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.509307, "o", " 36.5% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.549395, "o", " 36.6% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.564805, "o", " 36.7% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.579672, "o", " 36.8% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.594262, "o", " 36.9% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.609097, "o", " 37.0% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.625262, "o", " 37.1% Processing: Wallpaper/bigcollection/Gen...ld_Box_OpenCL_4258952414_8K.jpg\r"] [164.71211, "o", " 37.2% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.725461, "o", " 37.3% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.740883, "o", " 37.4% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.755236, "o", " 37.5% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.76952, "o", " 37.6% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.783476, "o", " 37.7% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.798959, "o", " 37.8% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.811597, "o", " 37.9% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.827098, "o", " 38.0% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.841882, "o", " 38.1% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.857076, "o", " 38.2% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.871356, "o", " 38.3% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.885444, "o", " 38.4% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.90003, "o", " 38.5% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.915101, "o", " 38.6% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.929158, "o", " 38.7% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.943635, "o", " 38.8% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [164.95983, "o", " 38.9% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.016768, "o", " 39.0% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.031747, "o", " 39.1% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.045748, "o", " 39.2% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.060048, "o", " 39.3% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.074101, "o", " 39.4% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.089652, "o", " 39.5% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.104215, "o", " 39.6% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.118366, "o", " 39.7% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.132477, "o", " 39.8% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.146595, "o", " 39.9% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.161775, "o", " 40.0% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.216091, "o", " 40.1% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.235223, "o", " 40.2% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.250335, "o", " 40.3% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.264513, "o", " 40.4% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.27882, "o", " 40.5% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.29292, "o", " 40.6% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.308386, "o", " 40.7% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.325042, "o", " 40.8% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.339423, "o", " 40.9% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.354481, "o", " 41.0% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.370932, "o", " 41.1% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.386144, "o", " 41.2% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.406262, "o", " 41.3% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.439308, "o", " 41.4% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.457374, "o", " 41.5% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.471511, "o", " 41.6% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.485641, "o", " 41.7% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.499622, "o", " 41.8% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.540815, "o", " 41.9% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.555849, "o", " 42.0% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.5697, "o", " 42.1% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.584235, "o", " 42.2% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.598363, "o", " 42.3% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.613715, "o", " 42.4% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.679444, "o", " 42.5% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.693115, "o", " 42.6% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.707433, "o", " 42.7% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.721651, "o", " 42.8% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.739409, "o", " 42.9% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.753223, "o", " 43.0% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.767281, "o", " 43.1% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r"] [165.767825, "o", "l"] [165.808885, "o", "s"] [165.832142, "o", " "] [165.911302, "o", "-"] [165.931544, "o", "l"] [166.132117, "o", "\r\n 43.2% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r 43.3% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r 43.4% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r 43.5% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r 43.6% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r 43.7% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r 43.8% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r 43.9% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r 44.0% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r 44.1% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r 44.2% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r 44.3% Processing: Wallpaper/bigcollection/Man...ale_4D_OpenCL_9648145412_8K.jpg\r 44.4% Processing: Wallpaper/bigcollection/Man...a"] [166.132282, "o", "le_4D_OpenCL_9648145412_8K.jpg\r 44.5% Processing: Wallpaper/bigcollection/Trapper_cabin.jpg \r 44.6% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r 44.7% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r 44.8% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r 44.9% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r 45.0% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r 45.1% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.137111, "o", " 45.2% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.22457, "o", " 45.3% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.239156, "o", " 45.4% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.253881, "o", " 45.5% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.268644, "o", " 45.6% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.28505, "o", " 45.7% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.300194, "o", " 45.8% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.315375, "o", " 45.9% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.330249, "o", " 46.0% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.346349, "o", " 46.1% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.361565, "o", " 46.2% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.377064, "o", " 46.3% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.392388, "o", " 46.4% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.408886, "o", " 46.5% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.423419, "o", " 46.6% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.438061, "o", " 46.7% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.452779, "o", " 46.8% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.467486, "o", " 46.9% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.48437, "o", " 47.0% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.503504, "o", " 47.1% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.552176, "o", " 47.2% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.566378, "o", " 47.3% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.582761, "o", " 47.4% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.597476, "o", " 47.5% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.68726, "o", " 47.6% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.703484, "o", " 47.7% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.719492, "o", " 47.8% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.733919, "o", " 47.9% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.749032, "o", " 48.0% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.763838, "o", " 48.1% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.779922, "o", " 48.2% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.794452, "o", " 48.3% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.809086, "o", " 48.4% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.823751, "o", " 48.5% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.839424, "o", " 48.6% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.853844, "o", " 48.7% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.868371, "o", " 48.8% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.883116, "o", " 48.9% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.899475, "o", " 49.0% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.914904, "o", " 49.1% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.929672, "o", " 49.2% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.944956, "o", " 49.3% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.961651, "o", " 49.4% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.97701, "o", " 49.5% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [166.992797, "o", " 49.6% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.027683, "o", " 49.7% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.044163, "o", " 49.8% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.059611, "o", " 49.9% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.074996, "o", " 50.0% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.105621, "o", " 50.1% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.121448, "o", " 50.2% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.137932, "o", " 50.3% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.15341, "o", " 50.4% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.195728, "o", " 50.5% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.210778, "o", " 50.6% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.227386, "o", " 50.7% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.242559, "o", " 50.8% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.257732, "o", " 50.9% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.272797, "o", " 51.0% Processing: Wallpaper/bigcollection/Man...elbox_-_Variable_8K_6595424.jpg\r"] [167.309495, "o", " 51.1% Processing: Wallpaper/bigcollection/Men...ox_OpenCL_14048152404910_8K.jpg\r"] [167.325907, "o", " 51.2% Processing: Wallpaper/bigcollection/Men...ox_OpenCL_14048152404910_8K.jpg\r"] [167.342154, "o", " 51.3% Processing: Wallpaper/bigcollection/Men...ox_OpenCL_14048152404910_8K.jpg\r"] [167.359718, "o", " 51.4% Processing: Wallpaper/bigcollection/Men...ox_OpenCL_14048152404910_8K.jpg\r"] [167.398424, "o", " 51.5% Processing: Wallpaper/bigcollection/Men...ox_OpenCL_14048152404910_8K.jpg\r"] [167.421304, "o", " 51.6% Processing: Wallpaper/bigcollection/Men...ox_OpenCL_14048152404910_8K.jpg\r"] [167.437025, "o", " 51.7% Processing: Wallpaper/bigcollection/Men...ox_OpenCL_14048152404910_8K.jpg\r"] [167.453464, "o", " 51.8% Processing: Wallpaper/bigcollection/Men...ox_OpenCL_14048152404910_8K.jpg\r"] [167.468683, "o", " 51.9% Processing: Wallpaper/bigcollection/Men...ox_OpenCL_14048152404910_8K.jpg\r"] [167.484422, "o", " 52.0% Processing: Wallpaper/bigcollection/Men...ox_OpenCL_14048152404910_8K.jpg\r"] [167.49948, "o", " 52.1% Processing: Wallpaper/bigcollection/Men...ox_OpenCL_14048152404910_8K.jpg\r"] [167.544858, "o", " 52.2% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.561287, "o", " 52.3% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.575551, "o", " 52.4% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.589573, "o", " 52.5% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.604085, "o", " 52.6% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.620028, "o", " 52.7% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.63516, "o", " 52.8% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.655216, "o", " 52.9% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.673546, "o", " 53.0% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.712002, "o", " 53.1% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.726797, "o", " 53.2% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.741734, "o", " 53.3% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.756963, "o", " 53.4% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.772975, "o", " 53.5% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.794887, "o", " 53.6% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.820943, "o", " 53.7% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.841302, "o", " 53.8% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.857191, "o", " 53.9% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.878906, "o", " 54.0% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.898181, "o", " 54.1% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.920781, "o", " 54.2% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.940917, "o", " 54.3% Processing: Wallpaper/bigcollection/Sie...er_4D_OpenCL_51241841541_8K.jpg\r"] [167.971271, "o", " 54.4% Processing: Wallpaper/bigcollection/Airbus_Wing_01798_changed.jpg \r"] [167.989545, "o", " 54.5% Processing: Wallpaper/bigcollection/Air..._Wing_01798_changed.jpg \r"] [168.003567, "o", " 54.6% Processing: Wallpaper/bigcollection/Air..._Wing_01798_changed.jpg \r"] [168.019075, "o", " 54.7% Processing: Wallpaper/bigcollection/Air..._Wing_01798_changed.jpg \r"] [168.032985, "o", " 54.8% Processing: Wallpaper/bigcollection/Air..._Wing_01798_changed.jpg \r"] [168.066013, "o", " 54.9% Processing: Wallpaper/bigcollection/Holytrinfruitlandpark1b.jpg \r"] [168.080007, "o", " 55.0% Processing: Wallpaper/bigcollection/Hol...infruitlandpark1b.jpg \r"] [168.095155, "o", " 55.1% Processing: Wallpaper/bigcollection/Hol...infruitlandpark1b.jpg \r"] [168.11164, "o", " 55.2% Processing: Wallpaper/bigcollection/Hol...infruitlandpark1b.jpg \r"] [168.149798, "o", " 55.3% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.16731, "o", " 55.4% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.182878, "o", " 55.5% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.197377, "o", " 55.6% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.212045, "o", " 55.7% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.251289, "o", " 55.8% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.267957, "o", " 55.9% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.283528, "o", " 56.0% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.299004, "o", " 56.1% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.31406, "o", " 56.2% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.329499, "o", " 56.3% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.345926, "o", " 56.4% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.366048, "o", " 56.5% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.379607, "o", " 56.6% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.400899, "o", " 56.7% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.417018, "o", " 56.8% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.450473, "o", " 56.9% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.464717, "o", " 57.0% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.479182, "o", " 57.1% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.503588, "o", " 57.2% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.524622, "o", " 57.3% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.538213, "o", " 57.4% Processing: Wallpaper/bigcollection/Abo...od_12_OpenCL_45184521485_5K.jpg\r"] [168.639859, "o", " 57.5% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.655102, "o", " 57.6% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.671064, "o", " 57.7% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.686236, "o", " 57.8% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.703022, "o", " 57.9% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.717008, "o", " 58.0% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.733435, "o", " 58.1% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.748353, "o", " 58.2% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.764338, "o", " 58.3% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.777824, "o", " 58.4% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.793721, "o", " 58.5% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.808616, "o", " 58.6% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.823499, "o", " 58.7% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.839317, "o", " 58.8% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.852967, "o", " 58.9% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.869026, "o", " 59.0% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.883509, "o", " 59.1% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.900077, "o", " 59.2% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.914409, "o", " 59.3% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.930421, "o", " 59.4% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.945342, "o", " 59.5% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [168.999211, "o", " 59.6% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.016832, "o", " 59.7% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.031617, "o", " 59.8% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.046268, "o", " 59.9% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.061, "o", " 60.0% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.076713, "o", " 60.1% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.091634, "o", " 60.2% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.106464, "o", " 60.3% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.121532, "o", " 60.4% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.136652, "o", " 60.5% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.152763, "o", " 60.6% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.190922, "o", " 60.7% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.206963, "o", " 60.8% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.220276, "o", " 60.9% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.235989, "o", " 61.0% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.250363, "o", " 61.1% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.297467, "o", " 61.2% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.319013, "o", " 61.3% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.333854, "o", " 61.4% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.348863, "o", " 61.5% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.363703, "o", " 61.6% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.379802, "o", " 61.7% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.394659, "o", " 61.8% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.40965, "o", " 61.9% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.424664, "o", " 62.0% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.440895, "o", " 62.1% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.486794, "o", " 62.2% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.501244, "o", " 62.3% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.51593, "o", " 62.4% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.530869, "o", " 62.5% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.54712, "o", " 62.6% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.561963, "o", " 62.7% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.576793, "o", " 62.8% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.591753, "o", " 62.9% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.60768, "o", " 63.0% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.643056, "o", " 63.1% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.660544, "o", " 63.2% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.67538, "o", " 63.3% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.691966, "o", " 63.4% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.70663, "o", " 63.5% Processing: Wallpaper/bigcollection/Men...ternion_OpenCL_644289452_8K.jpg\r"] [169.724173, "o", " 63.6% Processing: Wallpaper/bigcollection/Gabrielsond.jpg \r"] [169.755422, "o", " 63.7% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [169.77475, "o", " 63.8% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [169.789891, "o", " 63.9% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [169.805116, "o", " 64.0% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [169.831568, "o", " 64.1% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [169.852789, "o", " 64.2% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [169.868043, "o", " 64.3% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [169.88704, "o", " 64.4% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [169.903364, "o", " 64.5% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [169.936235, "o", " 64.6% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [169.9512, "o", " 64.7% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [169.982339, "o", " 64.8% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [169.999446, "o", " 64.9% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.015661, "o", " 65.0% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.03056, "o", " 65.1% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.092201, "o", " 65.2% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.106882, "o", " 65.3% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.123126, "o", " 65.4% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.1399, "o", " 65.5% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.155076, "o", " 65.6% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.169973, "o", " 65.7% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.186293, "o", " 65.8% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.201513, "o", " 65.9% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.21664, "o", " 66.0% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.231604, "o", " 66.1% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.248006, "o", " 66.2% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.263039, "o", " 66.3% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.355062, "o", " 66.4% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.369727, "o", " 66.5% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.384513, "o", " 66.6% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.40045, "o", " 66.7% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.415694, "o", " 66.8% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.430163, "o", " 66.9% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.44498, "o", " 67.0% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.461066, "o", " 67.1% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.475821, "o", " 67.2% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.490933, "o", " 67.3% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.50695, "o", " 67.4% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.523218, "o", " 67.5% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.538283, "o", " 67.6% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.553378, "o", " 67.7% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.568527, "o", " 67.8% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.584645, "o", " 67.9% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.599594, "o", " 68.0% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.614904, "o", " 68.1% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.629862, "o", " 68.2% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.659018, "o", " 68.3% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.678932, "o", " 68.4% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.693859, "o", " 68.5% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.716413, "o", " 68.6% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.734956, "o", " 68.7% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.763419, "o", " 68.8% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.781631, "o", " 68.9% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.796773, "o", " 69.0% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.813144, "o", " 69.1% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.837495, "o", " 69.2% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.854888, "o", " 69.3% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.869913, "o", " 69.4% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.90722, "o", " 69.5% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.923005, "o", " 69.6% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.937825, "o", " 69.7% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.952976, "o", " 69.8% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.981995, "o", " 69.9% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [170.999233, "o", " 70.0% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [171.013875, "o", " 70.1% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [171.034083, "o", " 70.2% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [171.05009, "o", " 70.3% Processing: Wallpaper/bigcollection/Mix...schwamm_OpenCL_461481542_8K.jpg\r"] [171.071759, "o", " 70.4% Processing: Wallpaper/bigcollection/Bel..._hair_variant)_(16x9_ratio).jpg\r"] [171.101, "o", " 70.5% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.120041, "o", " 70.6% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.133117, "o", " 70.7% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.148628, "o", " 70.8% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.170476, "o", " 70.9% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.189523, "o", " 71.0% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.204942, "o", " 71.1% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.231339, "o", " 71.2% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.247225, "o", " 71.3% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.278438, "o", " 71.4% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.294749, "o", " 71.5% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.308543, "o", " 71.6% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.32423, "o", " 71.7% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.343463, "o", " 71.8% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.358279, "o", " 71.9% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.401928, "o", " 72.0% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.417339, "o", " 72.1% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.433765, "o", " 72.2% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.448139, "o", " 72.3% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.463803, "o", " 72.4% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.478013, "o", " 72.5% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.492003, "o", " 72.6% Processing: Wallpaper/bigcollection/Sie..._4D_OpenCL_2154188450481_8K.jpg\r"] [171.528374, "o", " 72.7% Processing: Wallpaper/bigcollection/Abox_4D_OpenCL_545185481_8K.jpg \r"] [171.544588, "o", " 72.8% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.559642, "o", " 72.9% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.573521, "o", " 73.0% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.587608, "o", " 73.1% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.6351, "o", " 73.2% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.652608, "o", " 73.3% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.666452, "o", " 73.4% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.680621, "o", " 73.5% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.69472, "o", " 73.6% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.709747, "o", " 73.7% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.723682, "o", " 73.8% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.738028, "o", " 73.9% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.752058, "o", " 74.0% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.76719, "o", " 74.1% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.790642, "o", " 74.2% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.820514, "o", " 74.3% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.834545, "o", " 74.4% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.849766, "o", " 74.5% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.863903, "o", " 74.6% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.923574, "o", " 74.7% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.942274, "o", " 74.8% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.957763, "o", " 74.9% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.971772, "o", " 75.0% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [171.986089, "o", " 75.1% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.000346, "o", " 75.2% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.015555, "o", " 75.3% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.029527, "o", " 75.4% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.043727, "o", " 75.5% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.057704, "o", " 75.6% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.072116, "o", " 75.7% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.087552, "o", " 75.8% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.102046, "o", " 75.9% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.147613, "o", " 76.0% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.161519, "o", " 76.1% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.176861, "o", " 76.2% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.191243, "o", " 76.3% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.205521, "o", " 76.4% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.219614, "o", " 76.5% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.235435, "o", " 76.6% Processing: Wallpaper/bigcollection/Abo...D_OpenCL_545185481_8K.jpg \r"] [172.335066, "o", " 76.7% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.349849, "o", " 76.8% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.365366, "o", " 76.9% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.381936, "o", " 77.0% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.397341, "o", " 77.1% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.412546, "o", " 77.2% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.427934, "o", " 77.3% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.444458, "o", " 77.4% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.459873, "o", " 77.5% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.475137, "o", " 77.6% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.490889, "o", " 77.7% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.507471, "o", " 77.8% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.522782, "o", " 77.9% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.537746, "o", " 78.0% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.553095, "o", " 78.1% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.568446, "o", " 78.2% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.585084, "o", " 78.3% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.600602, "o", " 78.4% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.616157, "o", " 78.5% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.636866, "o", " 78.6% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.656337, "o", " 78.7% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.676685, "o", " 78.8% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.695044, "o", " 78.9% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.795084, "o", " 79.0% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.811192, "o", " 79.1% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.826313, "o", " 79.2% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.841796, "o", " 79.3% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.857509, "o", " 79.4% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.873717, "o", " 79.5% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.888989, "o", " 79.6% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.904218, "o", " 79.7% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.920812, "o", " 79.8% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.936174, "o", " 79.9% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.951402, "o", " 80.0% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.966554, "o", " 80.1% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.981776, "o", " 80.2% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [172.998078, "o", " 80.3% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [173.017396, "o", " 80.4% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [173.034055, "o", " 80.5% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [173.049362, "o", " 80.6% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [173.065899, "o", " 80.7% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [173.081107, "o", " 80.8% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [173.097665, "o", " 80.9% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [173.111775, "o", " 81.0% Processing: Wallpaper/bigcollection/Sie...nski_4D_OpenCL_485274854_5K.jpg\r"] [173.148292, "o", " 81.1% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.165472, "o", " 81.2% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.181997, "o", " 81.3% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.198583, "o", " 81.4% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.216137, "o", " 81.5% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.245988, "o", " 81.6% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.262544, "o", " 81.7% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.278612, "o", " 81.8% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.313541, "o", " 81.9% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.333354, "o", " 82.0% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.349458, "o", " 82.1% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.366892, "o", " 82.2% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.381392, "o", " 82.3% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.416691, "o", " 82.4% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.433961, "o", " 82.5% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.451384, "o", " 82.6% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.467403, "o", " 82.7% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.516534, "o", " 82.8% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.531702, "o", " 82.9% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.547214, "o", " 83.0% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.564017, "o", " 83.1% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.578307, "o", " 83.2% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.595338, "o", " 83.3% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.610696, "o", " 83.4% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.627217, "o", " 83.5% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.653623, "o", " 83.6% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.669272, "o", " 83.7% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.684708, "o", " 83.8% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.727221, "o", " 83.9% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.746924, "o", " 84.0% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.762108, "o", " 84.1% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.777159, "o", " 84.2% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.792423, "o", " 84.3% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.80903, "o", " 84.4% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.82715, "o", " 84.5% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.847467, "o", " 84.6% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.922477, "o", " 84.7% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.939391, "o", " 84.8% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.956239, "o", " 84.9% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.971558, "o", " 85.0% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [173.988201, "o", " 85.1% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.002262, "o", " 85.2% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.018968, "o", " 85.3% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.034114, "o", " 85.4% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.051034, "o", " 85.5% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.065118, "o", " 85.6% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.08173, "o", " 85.7% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.097284, "o", " 85.8% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.112741, "o", " 85.9% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.129045, "o", " 86.0% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.184503, "o", " 86.1% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.206856, "o", " 86.2% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.223262, "o", " 86.3% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.239863, "o", " 86.4% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.253836, "o", " 86.5% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.270586, "o", " 86.6% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.286007, "o", " 86.7% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.30157, "o", " 86.8% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.318006, "o", " 86.9% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.333644, "o", " 87.0% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.350304, "o", " 87.1% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.36464, "o", " 87.2% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.389727, "o", " 87.3% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.407212, "o", " 87.4% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.422833, "o", " 87.5% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.453296, "o", " 87.6% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.467395, "o", " 87.7% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.520985, "o", " 87.8% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.533525, "o", " 87.9% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.55016, "o", " 88.0% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.56416, "o", " 88.1% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.580874, "o", " 88.2% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.596654, "o", " 88.3% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.614557, "o", " 88.4% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.628951, "o", " 88.5% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.645523, "o", " 88.6% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.660816, "o", " 88.7% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.675584, "o", " 88.8% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.704816, "o", " 88.9% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.722559, "o", " 89.0% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.737936, "o", " 89.1% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.756118, "o", " 89.2% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.77368, "o", " 89.3% Processing: Wallpaper/bigcollection/Ver...engerschwamm_OpenCl_6184524.jpg\r"] [174.799303, "o", " 89.4% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [174.832351, "o", " 89.5% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [174.845368, "o", " 89.6% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [174.863336, "o", " 89.7% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [174.879711, "o", " 89.8% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [174.896108, "o", " 89.9% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [174.916611, "o", " 90.0% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [174.939868, "o", " 90.1% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [174.956109, "o", " 90.2% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [174.980547, "o", " 90.3% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [174.99835, "o", " 90.4% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.024687, "o", " 90.5% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.042584, "o", " 90.6% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.075226, "o", " 90.7% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.090554, "o", " 90.8% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.107221, "o", " 90.9% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.148647, "o", " 91.0% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.163581, "o", " 91.1% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.178784, "o", " 91.2% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.193848, "o", " 91.3% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.210235, "o", " 91.4% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.22529, "o", " 91.5% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.280281, "o", " 91.6% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.295515, "o", " 91.7% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.31194, "o", " 91.8% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.327947, "o", " 91.9% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.342733, "o", " 92.0% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.357877, "o", " 92.1% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.374404, "o", " 92.2% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.388429, "o", " 92.3% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.40491, "o", " 92.4% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.425357, "o", " 92.5% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.442092, "o", " 92.6% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.483321, "o", " 92.7% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.498389, "o", " 92.8% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.513452, "o", " 92.9% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.530094, "o", " 93.0% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.545353, "o", " 93.1% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.579634, "o", " 93.2% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.593214, "o", " 93.3% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.609788, "o", " 93.4% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.625037, "o", " 93.5% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.640266, "o", " 93.6% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.656135, "o", " 93.7% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.688247, "o", " 93.8% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.703239, "o", " 93.9% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.718576, "o", " 94.0% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.754452, "o", " 94.1% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.769853, "o", " 94.2% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.770073, "o", "\r\n"] [175.784359, "o", " 94.3% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.800622, "o", " 94.4% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.815985, "o", " 94.5% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.837175, "o", " 94.6% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.857703, "o", " 94.7% Processing: Wallpaper/bigcollection/Men...schwamm_OpenCL_955141845_8K.jpg\r"] [175.942193, "o", " 94.8% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [175.961827, "o", " 94.9% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [175.97792, "o", " 95.0% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [175.995471, "o", " 95.1% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.01187, "o", " 95.2% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.028298, "o", " 95.3% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.045597, "o", " 95.4% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.060698, "o", " 95.5% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.078263, "o", " 95.6% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.093876, "o", " 95.7% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.10991, "o", " 95.8% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.125699, "o", " 95.9% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.142577, "o", " 96.0% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.158303, "o", " 96.1% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.173966, "o", " 96.2% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.190604, "o", " 96.3% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.206311, "o", " 96.4% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.222587, "o", " 96.5% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.238229, "o", " 96.6% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.259845, "o", " 96.7% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.276799, "o", " 96.8% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.301942, "o", " 96.9% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.31995, "o", " 97.0% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.350236, "o", " 97.1% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.37165, "o", " 97.2% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.387226, "o", " 97.3% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.402999, "o", " 97.4% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.418169, "o", " 97.5% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.511296, "o", " 97.6% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.526762, "o", " 97.7% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.542462, "o", " 97.8% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.558234, "o", " 97.9% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.575231, "o", " 98.0% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.590746, "o", " 98.1% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.606485, "o", " 98.2% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.622891, "o", " 98.3% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.636901, "o", " 98.4% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.653178, "o", " 98.5% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.667977, "o", " 98.6% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.68731, "o", " 98.7% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.70233, "o", " 98.8% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.718126, "o", " 98.9% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.733087, "o", " 99.0% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.748853, "o", " 99.1% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.765928, "o", " 99.2% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.78199, "o", " 99.3% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.797818, "o", " 99.4% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.813269, "o", " 99.5% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.847059, "o", " 99.6% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.865025, "o", " 99.7% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.881058, "o", " 99.8% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.89715, "o", " 99.9% Processing: Wallpaper/bigcollection/Fre...ial_projects._(14168975789).jpg\r"] [176.913918, "o", " \r"] [176.990837, "o", "$ #"] [177.002124, "o", " "] [177.031029, "o", "Y"] [177.07908, "o", "o"] [177.091476, "o", "u"] [177.155024, "o", " "] [177.179734, "o", "c"] [177.207211, "o", "a"] [177.22278, "o", "n"] [177.27989, "o", " "] [177.292519, "o", "m"] [177.326473, "o", "o"] [177.341919, "o", "u"] [177.40636, "o", "n"] [177.415817, "o", "t"] [177.425138, "o", " "] [177.469067, "o", "a"] [177.499804, "o", "n"] [177.509376, "o", " "] [177.539022, "o", "a"] [177.637345, "o", "r"] [177.665967, "o", "c"] [177.711071, "o", "h"] [177.852891, "o", "i"] [177.875061, "o", "v"] [177.996486, "o", "e"] [178.014829, "o", " "] [178.139564, "o", "o"] [178.153, "o", "r"] [178.340782, "o", " "] [178.431104, "o", "e"] [178.554985, "o", "v"] [178.640138, "o", "e"] [178.653782, "o", "n"] [178.68707, "o", " "] [178.696599, "o", "t"] [178.73106, "o", "h"] [178.785567, "o", "e"] [178.815053, "o", " "] [178.882046, "o", "w"] [178.891573, "o", "h"] [178.922301, "o", "o"] [178.934767, "o", "l"] [178.944116, "o", "e"] [179.14435, "o", " "] [179.153748, "o", "r"] [179.165375, "o", "e"] [179.182938, "o", "p"] [179.19851, "o", "o"] [179.211035, "o", "s"] [179.273267, "o", "i"] [179.282781, "o", "t"] [179.313448, "o", "o"] [179.323019, "o", "r"] [179.334972, "o", "y"] [179.503097, "o", ":"] [179.680855, "o", "\r\ntotal 821176\r\r\ndrwxr-xr-x 3 root root 4096 Jul 6 21:30 Wallpaper\r\r\ndrwxr-xr-x 3 root root 4096 Jul 6 21:28 Wallpaper.orig\r\r\n-rw------- 1 root root 398438415 Jul 6 21:33 backup.tar.gz\r\r\n-rw-r--r-- 1 root root 22929200 Jun 5 21:42 borg-linux64\r\r\n-rw-r--r-- 1 root root 862 Jun 5 21:42 borg-linux64.asc\r\r\n-rw------- 1 root root 66582 Jul 6 21:32 file.html\r\r\n-rw-r--r-- 1 root root 419430400 Jul 6 21:30 loopbackfile.img\r\r\n$ $ $ m"] [179.746167, "o", "k"] [179.758958, "o", "d"] [179.768486, "o", "i"] [179.791088, "o", "r"] [179.991469, "o", " "] [180.098874, "o", "/"] [180.119267, "o", "t"] [180.260824, "o", "m"] [180.320436, "o", "p"] [180.486884, "o", "/"] [180.510496, "o", "m"] [180.570831, "o", "o"] [180.5904, "o", "u"] [180.623022, "o", "n"] [180.632463, "o", "t"] [180.671542, "o", "\r\nb"] [180.774426, "o", "o"] [180.796051, "o", "r"] [180.843047, "o", "g"] [180.861582, "o", " "] [180.909768, "o", "m"] [180.959072, "o", "o"] [181.012766, "o", "u"] [181.050466, "o", "n"] [181.059659, "o", "t"] [181.125752, "o", " "] [181.171124, "o", ":"] [181.225894, "o", ":"] [181.256531, "o", " "] [181.303266, "o", "/"] [181.315278, "o", "t"] [181.343582, "o", "m"] [181.371377, "o", "p"] [181.439985, "o", "/"] [181.46064, "o", "m"] [181.479426, "o", "o"] [181.56903, "o", "u"] [181.578686, "o", "n"] [181.651202, "o", "t"] [181.70471, "o", "\r\nl"] [181.735837, "o", "s"] [181.867009, "o", " "] [181.92209, "o", "-"] [181.947235, "o", "l"] [182.02963, "o", "a"] [182.038794, "o", " "] [182.053892, "o", "/"] [182.062896, "o", "t"] [182.119006, "o", "m"] [182.130552, "o", "p"] [182.211559, "o", "/"] [182.230681, "o", "m"] [182.282454, "o", "o"] [182.291818, "o", "u"] [182.301301, "o", "n"] [182.31049, "o", "t"] [182.368951, "o", "\r\n"] [182.369429, "o", "$ b"] [182.413573, "o", "o"] [182.423034, "o", "r"] [182.598887, "o", "g"] [182.751119, "o", " "] [182.783431, "o", "u"] [182.796922, "o", "m"] [182.806458, "o", "o"] [182.843154, "o", "u"] [182.935406, "o", "n"] [183.062912, "o", "t"] [183.099442, "o", " "] [183.113601, "o", "/"] [183.135259, "o", "t"] [183.175463, "o", "m"] [183.247234, "o", "p"] [183.256812, "o", "/"] [183.384711, "o", "m"] [183.427408, "o", "o"] [183.436978, "o", "u"] [183.599101, "o", "n"] [183.632998, "o", "t"] [183.702153, "o", "\r\n$ total 4\r"] [183.702958, "o", "\r\ndrwxr-xr-x 1 root root 0 Jul 6 21:33 .\r\r\ndrwxrwxrwt 10 root root 4096 Jul 6 21:33 ..\r\r\ndrwxr-xr-x 1 root root 0 Jul 6 21:31 backup-block-device\r\r\ndrwxr-xr-x 1 root root 0 Jul 6 21:30 backup1\r\r\ndrwxr-xr-x 1 root root 0 Jul 6 21:30 backup2\r\r\n"] [183.70361, "o", "drwxr-xr-x 1 root root 0 Jul 6 21:30 backup3\r\r\ndrwxr-xr-x 1 root root 0 Jul 6 21:31 root-2022-07-06T21:31:12\r\r\ndrwxr-xr-x 1 root root 0 Jul 6 21:31 root-2022-07-06T21:31:28\r\r\n$ \r\n"] [183.704007, "o", "#"] [183.782891, "o", " "] [183.825479, "o", "T"] [183.938756, "o", "h"] [184.065807, "o", "a"] [184.095759, "o", "t"] [184.106989, "o", "'"] [184.172356, "o", "s"] [184.333635, "o", " "] [184.450955, "o", "i"] [184.482269, "o", "t"] [184.568569, "o", ","] [184.589754, "o", " "] [184.650885, "o", "b"] [184.680299, "o", "u"] [184.730828, "o", "t"] [184.768041, "o", " "] [184.791356, "o", "o"] [184.849649, "o", "f"] [184.932911, "o", " "] [184.966199, "o", "c"] [184.977311, "o", "o"] [184.986748, "o", "u"] [185.020926, "o", "r"] [185.13723, "o", "s"] [185.189548, "o", "e"] [185.230866, "o", " "] [185.331133, "o", "t"] [185.454426, "o", "h"] [185.544653, "o", "e"] [185.586996, "o", "r"] [185.653352, "o", "e"] [185.701637, "o", " "] [185.710915, "o", "i"] [185.7361, "o", "s"] [185.745458, "o", " "] [185.769768, "o", "m"] [185.805076, "o", "o"] [185.886372, "o", "r"] [185.896491, "o", "e"] [185.948792, "o", " "] [185.958167, "o", "t"] [186.029468, "o", "o"] [186.104744, "o", " "] [186.139952, "o", "e"] [186.340348, "o", "x"] [186.349559, "o", "p"] [186.435894, "o", "l"] [186.458035, "o", "o"] [186.506422, "o", "r"] [186.515622, "o", "e"] [186.707953, "o", ","] [186.79823, "o", " "] [186.812457, "o", "s"] [186.821601, "o", "o"] [186.892955, "o", " "] [186.951022, "o", "h"] [186.964333, "o", "a"] [186.973626, "o", "v"] [187.018026, "o", "e"] [187.201321, "o", " "] [187.264622, "o", "a"] [187.302927, "o", " "] [187.314958, "o", "l"] [187.366361, "o", "o"] [187.429373, "o", "o"] [187.446645, "o", "k"] [187.455846, "o", " "] [187.656118, "o", "a"] [187.759311, "o", "t"] [187.959528, "o", " "] [187.98579, "o", "t"] [188.042958, "o", "h"] [188.078209, "o", "e"] [188.278473, "o", " "] [188.289572, "o", "d"] [188.323028, "o", "o"] [188.425315, "o", "c"] [188.43457, "o", "s"] [188.532843, "o", "."] [188.567197, "o", "\r\n"] [188.567499, "o", "$ $ "] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/asciinema/advanced.tcl0000644000076500000240000000616714601576577020337 0ustar00twstaff# Configuration for send -h # Tries to emulate a human typing # Tweak this if typing is too fast or too slow set send_human {.05 .1 1 .01 .2} set script { # For the pro users, here are some advanced features of borg, so you can impress your friends. ;) # Note: This screencast was made with __BORG_VERSION__ – older or newer borg versions may behave differently. # First of all, we can use several environment variables for borg. # E.g. we do not want to type in our repo path and password again and again… export BORG_REPO='/media/backup/borgdemo' export BORG_PASSPHRASE='1234' # Problem solved, borg will use this automatically… :) # We'll use this right away… ## ADVANCED CREATION ## # We can also use some placeholders in our archive name… borg create --stats --progress --compression lz4 ::{user}-{now} Wallpaper # Notice the backup name. # And we can put completely different data, with different backup settings, in our backup. It will be deduplicated, anyway: borg create --stats --progress --compression zlib,6 --exclude ~/Downloads/big ::{user}-{now} ~/Downloads # Or let's backup a device via STDIN. sudo dd if=/dev/loop0 bs=10M | borg create --progress --stats ::specialbackup - # Let's continue with some simple things: ## USEFUL COMMANDS ## # You can show some information about an archive. You can even do it without needing to specify the archive name: borg info :: --last 1 # So let's rename our last archive: borg rename ::specialbackup backup-block-device borg info :: --last 1 # A very important step if you choose keyfile mode (where the keyfile is only saved locally) is to export your keyfile and possibly print it, etc. borg key export --qr-html :: file.html # this creates a nice HTML, but when you want something simpler… borg key export --paper :: # this is a "manual input"-only backup (but it is also included in the --qr-code option) ## MAINTENANCE ## # Sometimes backups get broken or we want a regular "checkup" that everything is okay… borg check -v :: # Next problem: Usually you do not have infinite disk space. So you may need to prune your archive… # You can tune this in every detail. See the docs for details. Here only a simple example: borg prune --list --keep-last 1 --dry-run # When actually executing it in a script, you have to use it without the --dry-run option, of course. ## RESTORE ## # When you want to see the diff between two archives use this command. # E.g. what happened between the first two backups? borg diff ::backup1 backup2 # Ah, we added a file, right… # There are also other ways to extract the data. # E.g. as a tar archive. borg export-tar --progress ::backup2 backup.tar.gz ls -l # You can mount an archive or even the whole repository: mkdir /tmp/mount borg mount :: /tmp/mount ls -la /tmp/mount borg umount /tmp/mount # That's it, but of course there is more to explore, so have a look at the docs. } set script [string trim $script] set script [string map [list __BORG_VERSION__ [exec borg -V]] $script] set script [split $script \n] set ::env(PS1) "$ " set stty_init -echo spawn -noecho /bin/sh foreach line $script { expect "$ " send_user -h $line\n send $line\n } expect "$ " ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/asciinema/basic.json0000644000076500000240000022017514601576577020037 0ustar00twstaff{"version": 2, "width": 80, "height": 24, "timestamp": 1657142858, "env": {"SHELL": "/bin/bash", "TERM": "vt100"}} [0.901997, "o", "$ #"] [0.911859, "o", " "] [0.945252, "o", "H"] [1.145466, "o", "e"] [1.219717, "o", "r"] [1.32403, "o", "e"] [1.380192, "o", " "] [1.430453, "o", "y"] [1.440629, "o", "o"] [1.477854, "o", "u"] [1.592158, "o", "'"] [1.604339, "o", "l"] [1.667607, "o", "l"] [1.740872, "o", " "] [1.762133, "o", "s"] [1.783381, "o", "e"] [1.823622, "o", "e"] [1.914891, "o", " "] [1.942144, "o", "s"] [2.003381, "o", "o"] [2.012556, "o", "m"] [2.061939, "o", "e"] [2.071228, "o", " "] [2.15549, "o", "b"] [2.326771, "o", "a"] [2.387008, "o", "s"] [2.449353, "o", "i"] [2.474589, "o", "c"] [2.674869, "o", " "] [2.697142, "o", "c"] [2.719411, "o", "o"] [2.753587, "o", "m"] [2.766854, "o", "m"] [2.815101, "o", "a"] [2.830358, "o", "n"] [2.903606, "o", "d"] [2.918861, "o", "s"] [2.982125, "o", " "] [3.008327, "o", "t"] [3.020509, "o", "o"] [3.165753, "o", " "] [3.174937, "o", "s"] [3.204287, "o", "t"] [3.237478, "o", "a"] [3.261594, "o", "r"] [3.338838, "o", "t"] [3.430109, "o", " "] [3.569377, "o", "w"] [3.593565, "o", "o"] [3.670838, "o", "r"] [3.697097, "o", "k"] [3.714347, "o", "i"] [3.724362, "o", "n"] [3.757578, "o", "g"] [3.957829, "o", " "] [4.086061, "o", "w"] [4.096274, "o", "i"] [4.141494, "o", "t"] [4.160749, "o", "h"] [4.180015, "o", " "] [4.245245, "o", "b"] [4.275507, "o", "o"] [4.304763, "o", "r"] [4.465051, "o", "g"] [4.474258, "o", "."] [4.537729, "o", "\r\n"] [4.543921, "o", "$ #"] [4.565316, "o", " "] [4.575589, "o", "N"] [4.696831, "o", "o"] [4.738093, "o", "t"] [4.801261, "o", "e"] [4.810475, "o", ":"] [4.819702, "o", " "] [4.830927, "o", "T"] [4.855195, "o", "h"] [4.89738, "o", "i"] [4.939628, "o", "s"] [4.948905, "o", " "] [5.013207, "o", "t"] [5.02248, "o", "e"] [5.057704, "o", "a"] [5.135974, "o", "s"] [5.155228, "o", "e"] [5.229419, "o", "r"] [5.341645, "o", " "] [5.350881, "o", "s"] [5.521412, "o", "c"] [5.536707, "o", "r"] [5.545931, "o", "e"] [5.668293, "o", "e"] [5.741519, "o", "n"] [5.783892, "o", "c"] [5.793081, "o", "a"] [5.858403, "o", "s"] [5.910676, "o", "t"] [6.096058, "o", " "] [6.126284, "o", "w"] [6.198546, "o", "a"] [6.277777, "o", "s"] [6.293117, "o", " "] [6.313411, "o", "m"] [6.349414, "o", "a"] [6.358599, "o", "d"] [6.36784, "o", "e"] [6.434105, "o", " "] [6.452354, "o", "w"] [6.481555, "o", "i"] [6.490802, "o", "t"] [6.499992, "o", "h"] [6.519263, "o", " "] [6.682501, "o", "b"] [6.711788, "o", "o"] [6.779036, "o", "r"] [6.788232, "o", "g"] [6.813434, "o", " "] [6.839723, "o", "1"] [6.848893, "o", "."] [7.008256, "o", "2"] [7.068455, "o", "."] [7.077624, "o", "1"] [7.179898, "o", " "] [7.192112, "o", "–"] [7.227365, "o", " "] [7.301542, "o", "o"] [7.364789, "o", "l"] [7.496018, "o", "d"] [7.544279, "o", "e"] [7.694535, "o", "r"] [7.81182, "o", " "] [7.822025, "o", "o"] [7.83126, "o", "r"] [7.988502, "o", " "] [8.019762, "o", "n"] [8.028979, "o", "e"] [8.146233, "o", "w"] [8.301411, "o", "e"] [8.310614, "o", "r"] [8.353893, "o", " "] [8.43015, "o", "b"] [8.444415, "o", "o"] [8.461635, "o", "r"] [8.529875, "o", "g"] [8.652126, "o", " "] [8.661253, "o", "v"] [8.740513, "o", "e"] [8.872782, "o", "r"] [8.884992, "o", "s"] [8.934245, "o", "i"] [8.987505, "o", "o"] [9.016764, "o", "n"] [9.058061, "o", "s"] [9.093246, "o", " "] [9.148492, "o", "m"] [9.157691, "o", "a"] [9.166916, "o", "y"] [9.184181, "o", " "] [9.257366, "o", "b"] [9.288631, "o", "e"] [9.297848, "o", "h"] [9.328139, "o", "a"] [9.379388, "o", "v"] [9.390641, "o", "e"] [9.399821, "o", " "] [9.507092, "o", "d"] [9.519377, "o", "i"] [9.57966, "o", "f"] [9.670911, "o", "f"] [9.689196, "o", "e"] [9.698414, "o", "r"] [9.722689, "o", "e"] [9.741948, "o", "n"] [9.751156, "o", "t"] [9.760393, "o", "l"] [9.769577, "o", "y"] [9.969826, "o", "."] [10.032, "o", "\r\n"] [10.038957, "o", "$ #"] [10.048219, "o", " "] [10.057455, "o", "B"] [10.0818, "o", "u"] [10.128106, "o", "t"] [10.237374, "o", " "] [10.295841, "o", "l"] [10.305257, "o", "e"] [10.341361, "o", "t"] [10.417542, "o", "'"] [10.429003, "o", "s"] [10.461357, "o", " "] [10.661763, "o", "s"] [10.671353, "o", "t"] [10.706527, "o", "a"] [10.733207, "o", "r"] [10.750995, "o", "t"] [10.829478, "o", "."] [10.847759, "o", "\r\n"] [10.853699, "o", "$ \r\n"] [10.857297, "o", "$ #"] [10.974637, "o", " "] [11.021433, "o", "F"] [11.030913, "o", "i"] [11.048388, "o", "r"] [11.106915, "o", "s"] [11.11632, "o", "t"] [11.140345, "o", " "] [11.204803, "o", "o"] [11.313637, "o", "f"] [11.429012, "o", " "] [11.442311, "o", "a"] [11.451818, "o", "l"] [11.529721, "o", "l"] [11.579317, "o", ","] [11.607295, "o", " "] [11.619676, "o", "y"] [11.714946, "o", "o"] [11.729578, "o", "u"] [11.860181, "o", " "] [11.884661, "o", "c"] [11.897179, "o", "a"] [11.97332, "o", "n"] [11.989804, "o", " "] [12.009297, "o", "a"] [12.025623, "o", "l"] [12.069382, "o", "w"] [12.117614, "o", "a"] [12.201573, "o", "y"] [12.251935, "o", "s"] [12.364579, "o", " "] [12.374052, "o", "g"] [12.413268, "o", "e"] [12.422739, "o", "t"] [12.623041, "o", " "] [12.670536, "o", "h"] [12.737348, "o", "e"] [12.821793, "o", "l"] [12.866273, "o", "p"] [12.893446, "o", ":"] [12.95329, "o", "\r\n"] [12.959698, "o", "$ b"] [12.99345, "o", "o"] [13.060998, "o", "r"] [13.149973, "o", "g"] [13.244305, "o", " "] [13.380017, "o", "h"] [13.389332, "o", "e"] [13.421149, "o", "l"] [13.430532, "o", "p"] [13.514054, "o", "\r\n"] [14.368385, "o", "usage: borg [-V] [-h] [--critical] [--error] [--warning] [--info] [--debug]"] [14.369035, "o", "\r\r\n"] [14.369334, "o", " [--debug-topic TOPIC] [-p] [--iec] [--log-json]"] [14.369527, "o", "\r\r\n"] [14.369709, "o", " [--lock-wait SECONDS] [--bypass-lock] [--show-version] [--show-rc]"] [14.36989, "o", "\r\r\n"] [14.370079, "o", " [--umask M] [--remote-path PATH] [--remote-ratelimit RATE]"] [14.370256, "o", "\r\r\n"] [14.370441, "o", " [--upload-ratelimit RATE] [--remote-buffer UPLOAD_BUFFER]"] [14.370624, "o", "\r\r\n"] [14.370801, "o", " [--upload-buffer UPLOAD_BUFFER] [--consider-part-files]"] [14.370976, "o", "\r\r\n"] [14.371151, "o", " [--debug-profile FILE] [--rsh RSH]"] [14.371358, "o", "\r\r\n"] [14.371536, "o", " ..."] [14.371713, "o", "\r\r\n"] [14.371888, "o", "\r\r\n"] [14.372204, "o", "Borg - Deduplicated Backups"] [14.372752, "o", "\r\r\n"] [14.373316, "o", "\r\r\n"] [14.373744, "o", "optional arguments:"] [14.374157, "o", "\r\r\n"] [14.374567, "o", " -V, --version show version number and exit"] [14.374875, "o", "\r\r\n"] [14.375184, "o", "\r\r\n"] [14.375501, "o", "Common options:"] [14.375811, "o", "\r\r\n"] [14.376121, "o", " -h, --help show this help message and exit"] [14.376429, "o", "\r\r\n"] [14.376733, "o", " --critical work on log level CRITICAL"] [14.377062, "o", "\r\r\n"] [14.37727, "o", " --error work on log level ERROR"] [14.37747, "o", "\r\r\n"] [14.377667, "o", " --warning work on log level WARNING (default)"] [14.377871, "o", "\r\r\n"] [14.378072, "o", " --info, -v, --verbose"] [14.378271, "o", "\r\r\n"] [14.378472, "o", " work on log level INFO"] [14.378671, "o", "\r\r\n"] [14.378873, "o", " --debug enable debug output, work on log level DEBUG"] [14.379071, "o", "\r\r\n"] [14.379314, "o", " --debug-topic TOPIC enable TOPIC debugging (can be specified multiple"] [14.379542, "o", "\r\r\n"] [14.379737, "o", " times). The logger path is borg.debug. if TOPIC"] [14.37995, "o", "\r\r\n"] [14.380162, "o", " is not fully qualified."] [14.380372, "o", "\r\r\n"] [14.380593, "o", " -p, --progress show progress information"] [14.38081, "o", "\r\r\n"] [14.381071, "o", " --iec format using IEC units (1KiB = 1024B)"] [14.381241, "o", "\r"] [14.381315, "o", "\r\n"] [14.381543, "o", " --log-json Output one JSON object per log line instead of"] [14.381761, "o", "\r"] [14.381844, "o", "\r\n"] [14.382072, "o", " formatted text."] [14.382294, "o", "\r"] [14.382368, "o", "\r\n"] [14.382586, "o", " --lock-wait SECONDS wait at most SECONDS for acquiring a repository/cache"] [14.382805, "o", "\r"] [14.382887, "o", "\r\n"] [14.383122, "o", " lock (default: 1)."] [14.383368, "o", "\r"] [14.383442, "o", "\r\n"] [14.383674, "o", " --bypass-lock Bypass locking mechanism"] [14.383904, "o", "\r"] [14.38399, "o", "\r\n"] [14.384227, "o", " --show-version show/log the borg version"] [14.38446, "o", "\r"] [14.384533, "o", "\r\n"] [14.384765, "o", " --show-rc show/log the return code (rc)"] [14.384993, "o", "\r"] [14.3851, "o", "\r\n"] [14.385332, "o", " --umask M set umask to M (local only, default: 0077)"] [14.38558, "o", "\r"] [14.385653, "o", "\r\n"] [14.38589, "o", " --remote-path PATH use PATH as borg executable on the remote (default:"] [14.386122, "o", "\r"] [14.386195, "o", "\r\n"] [14.386434, "o", " \"borg\")"] [14.386666, "o", "\r"] [14.386806, "o", "\r\n"] [14.387039, "o", " --remote-ratelimit RATE"] [14.387292, "o", "\r"] [14.387434, "o", "\r\n"] [14.387668, "o", " deprecated, use ``--upload-ratelimit`` instead"] [14.387898, "o", "\r"] [14.388037, "o", "\r\n"] [14.388279, "o", " --upload-ratelimit RATE"] [14.388509, "o", "\r"] [14.388648, "o", "\r\n"] [14.388884, "o", " set network upload rate limit in kiByte/s (default:"] [14.389135, "o", "\r"] [14.389273, "o", "\r\n"] [14.389509, "o", " "] [14.389743, "o", " 0=unlimited)"] [14.389979, "o", "\r"] [14.390123, "o", "\r\n"] [14.390378, "o", " --remote-buffer UPLOAD_BUFFER"] [14.390614, "o", "\r"] [14.390752, "o", "\r\n"] [14.390991, "o", " deprecated, use ``--upload-buffer`` instead"] [14.391273, "o", "\r"] [14.391422, "o", "\r\n"] [14.391661, "o", " --upload-buffer UPLOAD_BUFFER"] [14.391918, "o", "\r"] [14.392057, "o", "\r\n"] [14.392288, "o", " set network upload buffer size in MiB. (default: 0=no"] [14.39254, "o", "\r"] [14.392692, "o", "\r\n"] [14.392919, "o", " buffer)"] [14.393165, "o", "\r"] [14.393321, "o", "\r\n"] [14.39355, "o", " --consider-part-files"] [14.39378, "o", "\r"] [14.393956, "o", "\r\n"] [14.394208, "o", " treat part files like normal files (e.g. to"] [14.394468, "o", "\r"] [14.39462, "o", "\r\n"] [14.394868, "o", " list/extract them)"] [14.39511, "o", "\r"] [14.395272, "o", "\r\n"] [14.395511, "o", " --debug-profile FILE Write execution profile in Borg format into FILE. For"] [14.395745, "o", "\r"] [14.395915, "o", "\r\n"] [14.397106, "o", " local use a Python-compatible file can be generated by\r\r\n suffixing FILE with \".pyprof\".\r\r\n --rsh RSH Use this command to connect to the 'borg serve'\r\r\n process (default: 'ssh')\r\r\n\r\r\nrequired arguments:\r\r\n \r\r\n benchmark benchmark command\r\r\n break-lock break repository and cache locks\r\r\n check verify repository\r\r\n compact compact segment files / free space in repo\r\r\n config get and set configuration values\r\r\n create create backup\r\r\n debug debugging command (not intended for normal use)\r\r\n delete delete archive\r\r\n diff find differences in archive contents\r\r\n export-tar create tarball from archive\r\r\n extract extract archive contents\r\r\n info show repository or archive information\r\r\n init initialize empty repository\r\r\n k"] [14.397135, "o", "ey manage repository key\r\r\n list list archive or repository contents\r\r\n mount mount repository\r\r\n prune prune archives\r\r\n recreate Re-create archives\r\r\n rename rename archive\r\r\n serve start repository server process\r\r\n umount umount repository\r\r\n upgrade upgrade repository format\r\r\n with-lock run user command with lock held\r\r\n import-tar Create a backup archive from a tarball\r\r\n"] [14.44226, "o", "$ #"] [14.467585, "o", " "] [14.48689, "o", "T"] [14.561135, "o", "h"] [14.623399, "o", "e"] [14.66958, "o", "s"] [14.678779, "o", "e"] [14.689048, "o", " "] [14.69935, "o", "a"] [14.768546, "o", "r"] [14.807766, "o", "e"] [15.008011, "o", " "] [15.017332, "o", "a"] [15.104611, "o", " "] [15.204869, "o", "l"] [15.234109, "o", "o"] [15.252376, "o", "t"] [15.366682, "o", " "] [15.375836, "o", "o"] [15.39412, "o", "f"] [15.441422, "o", " "] [15.450632, "o", "c"] [15.52093, "o", "o"] [15.630962, "o", "m"] [15.750214, "o", "m"] [15.759497, "o", "a"] [15.845708, "o", "n"] [15.910964, "o", "d"] [15.957243, "o", "s"] [16.102504, "o", ","] [16.118774, "o", " "] [16.130979, "o", "s"] [16.266236, "o", "o"] [16.312506, "o", " "] [16.384774, "o", "b"] [16.437017, "o", "e"] [16.505236, "o", "t"] [16.612532, "o", "t"] [16.64879, "o", "e"] [16.781204, "o", "r"] [16.833255, "o", " "] [16.92251, "o", "w"] [17.053713, "o", "e"] [17.081033, "o", " "] [17.107308, "o", "s"] [17.185495, "o", "t"] [17.243755, "o", "a"] [17.277997, "o", "r"] [17.292215, "o", "t"] [17.35945, "o", " "] [17.439708, "o", "w"] [17.463952, "o", "i"] [17.506213, "o", "t"] [17.525383, "o", "h"] [17.558644, "o", " "] [17.572897, "o", "a"] [17.636143, "o", " "] [17.741311, "o", "f"] [17.810552, "o", "e"] [17.90177, "o", "w"] [18.005029, "o", ":"] [18.014935, "o", "\r\n"] [18.020184, "o", "$ #"] [18.063288, "o", " "] [18.072418, "o", "L"] [18.160547, "o", "e"] [18.360844, "o", "t"] [18.561132, "o", "'"] [18.593287, "o", "s"] [18.62653, "o", " "] [18.717781, "o", "c"] [18.826025, "o", "r"] [18.835218, "o", "e"] [18.873402, "o", "a"] [18.882603, "o", "t"] [18.899863, "o", "e"] [18.910079, "o", " "] [18.953439, "o", "a"] [19.11271, "o", " "] [19.133755, "o", "r"] [19.14299, "o", "e"] [19.16526, "o", "p"] [19.232505, "o", "o"] [19.286765, "o", " "] [19.346037, "o", "o"] [19.424305, "o", "n"] [19.481493, "o", " "] [19.616742, "o", "a"] [19.671994, "o", "n"] [19.692266, "o", " "] [19.766499, "o", "e"] [19.775721, "o", "x"] [19.975975, "o", "t"] [20.022241, "o", "e"] [20.05052, "o", "r"] [20.059738, "o", "n"] [20.071001, "o", "a"] [20.082205, "o", "l"] [20.091424, "o", " "] [20.148692, "o", "d"] [20.180965, "o", "r"] [20.236208, "o", "i"] [20.256457, "o", "v"] [20.278716, "o", "e"] [20.301976, "o", "…"] [20.333963, "o", "\r\n"] [20.339852, "o", "$ b"] [20.358126, "o", "o"] [20.442379, "o", "r"] [20.455655, "o", "g"] [20.510921, "o", " "] [20.565184, "o", "i"] [20.610429, "o", "n"] [20.619643, "o", "i"] [20.628869, "o", "t"] [20.638114, "o", " "] [20.794374, "o", "-"] [20.810588, "o", "-"] [20.88284, "o", "e"] [20.90811, "o", "n"] [20.940365, "o", "c"] [20.978597, "o", "r"] [21.01085, "o", "y"] [21.041143, "o", "p"] [21.162394, "o", "t"] [21.219624, "o", "i"] [21.242904, "o", "o"] [21.252069, "o", "n"] [21.358325, "o", "="] [21.369514, "o", "r"] [21.410761, "o", "e"] [21.433035, "o", "p"] [21.535257, "o", "o"] [21.649448, "o", "k"] [21.683731, "o", "e"] [21.77799, "o", "y"] [21.823212, "o", " "] [21.957402, "o", "/"] [22.088666, "o", "m"] [22.236898, "o", "e"] [22.24612, "o", "d"] [22.267377, "o", "i"] [22.314631, "o", "a"] [22.389887, "o", "/"] [22.406138, "o", "b"] [22.464386, "o", "a"] [22.486629, "o", "c"] [22.499854, "o", "k"] [22.569172, "o", "u"] [22.578364, "o", "p"] [22.630618, "o", "/"] [22.642925, "o", "b"] [22.698191, "o", "o"] [22.751434, "o", "r"] [22.780696, "o", "g"] [22.801933, "o", "d"] [22.822192, "o", "e"] [22.907435, "o", "m"] [22.938691, "o", "o"] [22.964601, "o", "\r\n"] [23.832422, "o", "Enter new passphrase: "] [25.486059, "o", "\r\r\n"] [25.487658, "o", "Enter same passphrase again: "] [26.856445, "o", "\r\r\n"] [26.857204, "o", "Do you want your passphrase to be displayed for verification? [yN]: \r\r\n"] [26.966484, "o", "\r\r\n"] [26.966685, "o", "By default repositories initialized with this version will produce security"] [26.966829, "o", "\r\r\n"] [26.966962, "o", "errors if written to with an older version (up to and including Borg 1.0.8)."] [26.967109, "o", "\r\r\n"] [26.967228, "o", "\r\r\n"] [26.967349, "o", "If you want to use these older versions, you can disable the check by running:"] [26.967488, "o", "\r\r\n"] [26.967607, "o", "borg upgrade --disable-tam /media/backup/borgdemo"] [26.96775, "o", "\r\r\n"] [26.967866, "o", "\r\r\n"] [26.968011, "o", "See https://borgbackup.readthedocs.io/en/stable/changes.html#pre-1-0-9-manifest-spoofing-vulnerability for details about the security implications."] [26.968147, "o", "\r\r\n"] [26.968375, "o", "\r\r\n"] [26.968503, "o", "IMPORTANT: you will need both KEY AND PASSPHRASE to access this repo!"] [26.968625, "o", "\r\r\n"] [26.968777, "o", "If you used a repokey mode, the key is stored in the repo, but you should back it up separately."] [26.968901, "o", "\r\r\n"] [26.969071, "o", "Use \"borg key export\" to export the key, optionally in printable format."] [26.969224, "o", "\r\r\n"] [26.969358, "o", "Write down the passphrase. Store both at safe place(s)."] [26.969507, "o", "\r\r\n"] [26.969633, "o", "\r\r\n"] [27.013625, "o", "$ #"] [27.13268, "o", " "] [27.147032, "o", "T"] [27.156512, "o", "h"] [27.177304, "o", "i"] [27.223151, "o", "s"] [27.27742, "o", " "] [27.286916, "o", "u"] [27.296388, "o", "s"] [27.325338, "o", "e"] [27.334603, "o", "s"] [27.359038, "o", " "] [27.369472, "o", "t"] [27.499665, "o", "h"] [27.521345, "o", "e"] [27.585341, "o", " "] [27.697371, "o", "r"] [27.706903, "o", "e"] [27.907207, "o", "p"] [27.920745, "o", "o"] [27.976277, "o", "k"] [28.085507, "o", "e"] [28.097114, "o", "y"] [28.293222, "o", " "] [28.302812, "o", "e"] [28.503212, "o", "n"] [28.554895, "o", "c"] [28.568404, "o", "r"] [28.769382, "o", "y"] [28.901385, "o", "p"] [28.961879, "o", "t"] [28.971352, "o", "i"] [29.017917, "o", "o"] [29.167349, "o", "n"] [29.34115, "o", "."] [29.46939, "o", " "] [29.509408, "o", "Y"] [29.518829, "o", "o"] [29.529334, "o", "u"] [29.585778, "o", " "] [29.613184, "o", "m"] [29.622616, "o", "a"] [29.677445, "o", "y"] [29.727828, "o", " "] [29.737091, "o", "l"] [29.807396, "o", "o"] [29.905617, "o", "o"] [29.971877, "o", "k"] [30.151165, "o", " "] [30.24344, "o", "a"] [30.432734, "o", "t"] [30.464016, "o", " "] [30.498308, "o", "\""] [30.508566, "o", "b"] [30.623871, "o", "o"] [30.66712, "o", "r"] [30.81244, "o", "g"] [30.890789, "o", " "] [30.974011, "o", "h"] [31.106339, "o", "e"] [31.119612, "o", "l"] [31.154894, "o", "p"] [31.278174, "o", " "] [31.325429, "o", "i"] [31.338742, "o", "n"] [31.377071, "o", "i"] [31.415261, "o", "t"] [31.494554, "o", "\""] [31.533795, "o", " "] [31.583029, "o", "o"] [31.652322, "o", "r"] [31.85262, "o", " "] [31.891954, "o", "t"] [31.901285, "o", "h"] [31.910553, "o", "e"] [31.959826, "o", " "] [32.090128, "o", "o"] [32.290413, "o", "n"] [32.299633, "o", "l"] [32.336944, "o", "i"] [32.383206, "o", "n"] [32.392447, "o", "e"] [32.476764, "o", " "] [32.485995, "o", "d"] [32.495203, "o", "o"] [32.526555, "o", "c"] [32.587775, "o", " "] [32.597044, "o", "a"] [32.673383, "o", "t"] [32.682665, "o", " "] [32.743954, "o", "h"] [32.823241, "o", "t"] [32.84252, "o", "t"] [32.868801, "o", "p"] [32.895065, "o", "s"] [32.974332, "o", ":"] [33.121548, "o", "/"] [33.183875, "o", "/"] [33.284921, "o", "b"] [33.300185, "o", "o"] [33.320424, "o", "r"] [33.368691, "o", "g"] [33.377904, "o", "b"] [33.431174, "o", "a"] [33.471457, "o", "c"] [33.480708, "o", "k"] [33.489935, "o", "u"] [33.522193, "o", "p"] [33.536454, "o", "."] [33.545628, "o", "r"] [33.620889, "o", "e"] [33.689179, "o", "a"] [33.698404, "o", "d"] [33.719676, "o", "t"] [33.768939, "o", "h"] [33.778141, "o", "e"] [33.822397, "o", "d"] [33.917604, "o", "o"] [34.021836, "o", "c"] [34.03105, "o", "s"] [34.231324, "o", "."] [34.274564, "o", "i"] [34.335814, "o", "o"] [34.359041, "o", "/"] [34.431296, "o", " "] [34.440495, "o", "f"] [34.477712, "o", "o"] [34.510921, "o", "r"] [34.603179, "o", " "] [34.628416, "o", "o"] [34.696681, "o", "t"] [34.758901, "o", "h"] [34.836318, "o", "e"] [34.905349, "o", "r"] [34.951592, "o", " "] [34.96081, "o", "m"] [35.073058, "o", "o"] [35.099305, "o", "d"] [35.144574, "o", "e"] [35.22482, "o", "s"] [35.268061, "o", "."] [35.277927, "o", "\r\n"] [35.283574, "o", "$ \r\n"] [35.287762, "o", "$ #"] [35.32211, "o", " "] [35.385009, "o", "S"] [35.401365, "o", "o"] [35.410748, "o", " "] [35.451919, "o", "n"] [35.461356, "o", "o"] [35.474934, "o", "w"] [35.536488, "o", ","] [35.570013, "o", " "] [35.631873, "o", "l"] [35.661368, "o", "e"] [35.670835, "o", "t"] [35.845321, "o", "'"] [36.021655, "o", "s"] [36.090853, "o", " "] [36.101264, "o", "c"] [36.110857, "o", "r"] [36.120334, "o", "e"] [36.241347, "o", "a"] [36.300965, "o", "t"] [36.339197, "o", "e"] [36.371614, "o", " "] [36.45487, "o", "o"] [36.466427, "o", "u"] [36.496514, "o", "r"] [36.516156, "o", " "] [36.525617, "o", "f"] [36.609341, "o", "i"] [36.628943, "o", "r"] [36.638409, "o", "s"] [36.6533, "o", "t"] [36.742041, "o", " "] [36.823533, "o", "("] [36.836112, "o", "c"] [36.891684, "o", "o"] [36.915431, "o", "m"] [36.937742, "o", "p"] [36.993129, "o", "r"] [37.038448, "o", "e"] [37.130855, "o", "s"] [37.14035, "o", "s"] [37.223271, "o", "e"] [37.273744, "o", "d"] [37.473926, "o", ")"] [37.549739, "o", " "] [37.559237, "o", "b"] [37.70135, "o", "a"] [37.759843, "o", "c"] [37.888108, "o", "k"] [37.985378, "o", "u"] [37.99491, "o", "p"] [38.057312, "o", "."] [38.070071, "o", "\r\n"] [38.076517, "o", "$ b"] [38.131758, "o", "o"] [38.176021, "o", "r"] [38.232284, "o", "g"] [38.290526, "o", " "] [38.299745, "o", "c"] [38.373041, "o", "r"] [38.391283, "o", "e"] [38.400486, "o", "a"] [38.422774, "o", "t"] [38.431987, "o", "e"] [38.477263, "o", " "] [38.557434, "o", "-"] [38.571684, "o", "-"] [38.635942, "o", "s"] [38.649196, "o", "t"] [38.696452, "o", "a"] [38.716708, "o", "t"] [38.74095, "o", "s"] [38.795156, "o", " "] [38.971421, "o", "-"] [38.980619, "o", "-"] [39.154846, "o", "p"] [39.259082, "o", "r"] [39.268286, "o", "o"] [39.295549, "o", "g"] [39.375798, "o", "r"] [39.505049, "o", "e"] [39.548298, "o", "s"] [39.55747, "o", "s"] [39.729698, "o", " "] [39.738886, "o", "-"] [39.770147, "o", "-"] [39.780361, "o", "c"] [39.851606, "o", "o"] [39.908862, "o", "m"] [39.9303, "o", "p"] [39.967419, "o", "r"] [40.167693, "o", "e"] [40.182868, "o", "s"] [40.236139, "o", "s"] [40.338402, "o", "i"] [40.387653, "o", "o"] [40.417878, "o", "n"] [40.431077, "o", " "] [40.440317, "o", "l"] [40.471602, "o", "z"] [40.50683, "o", "4"] [40.533153, "o", " "] [40.632398, "o", "/"] [40.765597, "o", "m"] [40.939842, "o", "e"] [41.013163, "o", "d"] [41.068403, "o", "i"] [41.161609, "o", "a"] [41.34683, "o", "/"] [41.35603, "o", "b"] [41.382307, "o", "a"] [41.420551, "o", "c"] [41.449776, "o", "k"] [41.461978, "o", "u"] [41.47121, "o", "p"] [41.505394, "o", "/"] [41.535648, "o", "b"] [41.553906, "o", "o"] [41.563104, "o", "r"] [41.572335, "o", "g"] [41.600598, "o", "d"] [41.621833, "o", "e"] [41.631044, "o", "m"] [41.690289, "o", "o"] [41.842576, "o", ":"] [41.9168, "o", ":"] [41.926071, "o", "b"] [41.939284, "o", "a"] [41.994532, "o", "c"] [42.006819, "o", "k"] [42.039076, "o", "u"] [42.048303, "o", "p"] [42.060532, "o", "1"] [42.102826, "o", " "] [42.207072, "o", "W"] [42.217211, "o", "a"] [42.226429, "o", "l"] [42.265646, "o", "l"] [42.344914, "o", "p"] [42.410163, "o", "a"] [42.436418, "o", "p"] [42.633625, "o", "e"] [42.64884, "o", "r"] [42.746842, "o", "\r\n"] [43.682327, "o", "Enter passphrase for key /media/backup/borgdemo: "] [45.082307, "o", "\r\r\n"] [45.169184, "o", "0 B O 0 B C 0 B D 0 N Wallpaper \r"] [45.187613, "o", "Initializing cache transaction: Reading config \r"] [45.188516, "o", "Initializing cache transaction: Reading chunks \r"] [45.189235, "o", "Initializing cache transaction: Reading files \r"] [45.189931, "o", " \r"] [45.382861, "o", "18.70 MB O 18.70 MB C 18.70 MB D 0 N Wallpaper/bigcolle...OpenCL_45154214_8K.jpg\r"] [45.589911, "o", "37.45 MB O 37.36 MB C 37.36 MB D 1 N Wallpaper/bigcolle...CL_528814521414_8K.jpg\r"] [45.792693, "o", "54.88 MB O 54.62 MB C 54.62 MB D 4 N Wallpaper/bigcolle...OpenCL_18915424_8K.jpg\r"] [46.015974, "o", "75.03 MB O 74.77 MB C 74.77 MB D 4 N Wallpaper/bigcolle...OpenCL_18915424_8K.jpg\r"] [46.256392, "o", "96.05 MB O 95.78 MB C 95.78 MB D 6 N Wallpaper/bigcolle...FS_OpenCL_54815_5K.jpg\r"] [46.475686, "o", "116.05 MB O 115.77 MB C 115.77 MB D 10 N Wallpaper/bigcol...CL_4258952414_8K.jpg\r"] [46.718361, "o", "138.21 MB O 137.93 MB C 137.93 MB D 10 N Wallpaper/bigcol...CL_4258952414_8K.jpg\r"] [46.933739, "o", "158.88 MB O 158.61 MB C 158.61 MB D 11 N Wallpaper/bigcol...CL_9648145412_8K.jpg\r"] [47.134629, "o", "177.87 MB O 177.59 MB C 177.59 MB D 11 N Wallpaper/bigcol...CL_9648145412_8K.jpg\r"] [47.337235, "o", "195.94 MB O 195.67 MB C 195.67 MB D 14 N Wallpaper/bigcol...iable_8K_6595424.jpg\r"] [47.56114, "o", "217.13 MB O 216.29 MB C 216.29 MB D 16 N Wallpaper/bigcol...L_51241841541_8K.jpg\r"] [47.769682, "o", "235.09 MB O 234.13 MB C 234.13 MB D 20 N Wallpaper/bigcol...nCL_644289452_8K.jpg\r"] [47.975793, "o", "252.58 MB O 251.62 MB C 251.62 MB D 20 N Wallpaper/bigcol...nCL_644289452_8K.jpg\r"] [48.183523, "o", "270.49 MB O 269.52 MB C 269.52 MB D 22 N Wallpaper/bigcol...nCL_461481542_8K.jpg\r"] [48.384239, "o", "288.97 MB O 287.80 MB C 287.80 MB D 24 N Wallpaper/bigcol...2154188450481_8K.jpg\r"] [48.587528, "o", "307.41 MB O 306.10 MB C 306.10 MB D 25 N Wallpaper/bigcol...nCL_545185481_8K.jpg\r"] [48.798353, "o", "326.35 MB O 324.95 MB C 324.95 MB D 27 N Wallpaper/bigcol...m_OpenCl_6184524.jpg\r"] [49.018803, "o", "347.31 MB O 345.91 MB C 345.91 MB D 27 N Wallpaper/bigcol...m_OpenCl_6184524.jpg\r"] [49.234361, "o", "366.64 MB O 365.24 MB C 365.24 MB D 28 N Wallpaper/bigcol...nCL_955141845_8K.jpg\r"] [49.438542, "o", "384.57 MB O 383.17 MB C 383.17 MB D 29 N Wallpaper/bigcol...s._(14168975789).jpg\r"] [49.616544, "o", " \r"] [49.707316, "o", "Saving files cache \r"] [49.708504, "o", "Saving chunks cache \r"] [49.709375, "o", "Saving cache config \r"] [49.711438, "o", " \r"] [49.713185, "o", "------------------------------------------------------------------------------\r\r\n"] [49.713432, "o", "Repository: /media/backup/borgdemo"] [49.713781, "o", "\r\r\n"] [49.713824, "o", "Archive name: backup1"] [49.714201, "o", "\r\r\nArchive fingerprint: 4b2f146af02ce3a4df8018411ce46d97fe7eaad0512c969a4bbcd4f113900746"] [49.71452, "o", "\r\r\nTime (start): Wed, 2022-07-06 21:28:23"] [49.714706, "o", "\r\r\n"] [49.715114, "o", "Time (end): Wed, 2022-07-06 21:28:28\r\r\n"] [49.715288, "o", "Duration: 4.45 seconds"] [49.715701, "o", "\r\r\nNumber of files: 31"] [49.715975, "o", "\r\r\n"] [49.716017, "o", "Utilization of max. archive size: 0%"] [49.716177, "o", "\r\r\n"] [49.716452, "o", "------------------------------------------------------------------------------"] [49.716679, "o", "\r\r\n"] [49.71705, "o", " Original size Compressed size Deduplicated size\r\r\n"] [49.717685, "o", "This archive: 401.15 MB 399.74 MB 399.55 MB\r\r\nAll archives: 401.15 MB 399.74 MB 399.56 MB\r\r\n\r\r\n Unique chunks Total chunks\r\r\nChunk index: 208 209\r\r\n"] [49.718032, "o", "------------------------------------------------------------------------------\r\r\n"] [49.763286, "o", "$ \r\n"] [49.769027, "o", "$ #"] [49.889538, "o", " "] [49.907123, "o", "T"] [49.98155, "o", "h"] [50.001932, "o", "a"] [50.077298, "o", "t"] [50.149538, "o", "'"] [50.265474, "o", "s"] [50.328143, "o", " "] [50.353649, "o", "n"] [50.376936, "o", "i"] [50.424241, "o", "c"] [50.433748, "o", "e"] [50.530161, "o", ","] [50.57866, "o", " "] [50.633469, "o", "s"] [50.652226, "o", "o"] [50.7495, "o", " "] [50.8277, "o", "f"] [50.85347, "o", "a"] [50.86547, "o", "r"] [50.897494, "o", "."] [51.013237, "o", "\r\n"] [51.019506, "o", "$ #"] [51.045786, "o", " "] [51.195486, "o", "S"] [51.225488, "o", "o"] [51.297491, "o", " "] [51.32406, "o", "l"] [51.333277, "o", "e"] [51.361489, "o", "t"] [51.391215, "o", "'"] [51.400666, "o", "s"] [51.525492, "o", " "] [51.534974, "o", "a"] [51.561475, "o", "d"] [51.575956, "o", "d"] [51.610233, "o", " "] [51.626877, "o", "a"] [51.753372, "o", " "] [51.806885, "o", "n"] [51.837461, "o", "e"] [51.846818, "o", "w"] [51.90324, "o", " "] [51.912612, "o", "f"] [51.945692, "o", "i"] [51.955259, "o", "l"] [51.976908, "o", "e"] [51.989371, "o", "…"] [52.09749, "o", "\r\n"] [52.104127, "o", "$ e"] [52.249858, "o", "c"] [52.381469, "o", "h"] [52.409548, "o", "o"] [52.429558, "o", " "] [52.450022, "o", "\""] [52.472755, "o", "n"] [52.494909, "o", "e"] [52.541666, "o", "w"] [52.621531, "o", " "] [52.657476, "o", "n"] [52.666897, "o", "i"] [52.749532, "o", "c"] [52.789582, "o", "e"] [52.857092, "o", " "] [52.901226, "o", "f"] [53.022805, "o", "i"] [53.091341, "o", "l"] [53.143908, "o", "e"] [53.34533, "o", "\""] [53.358764, "o", " "] [53.377334, "o", ">"] [53.424655, "o", " "] [53.434072, "o", "W"] [53.469238, "o", "a"] [53.479684, "o", "l"] [53.525369, "o", "l"] [53.569647, "o", "p"] [53.607808, "o", "a"] [53.640682, "o", "p"] [53.657289, "o", "e"] [53.741474, "o", "r"] [53.79733, "o", "/"] [53.933274, "o", "n"] [53.945655, "o", "e"] [53.993338, "o", "w"] [54.002684, "o", "f"] [54.024254, "o", "i"] [54.177552, "o", "l"] [54.228957, "o", "e"] [54.336453, "o", "."] [54.348932, "o", "t"] [54.377374, "o", "x"] [54.389764, "o", "t"] [54.531118, "o", "\r\n"] [54.538791, "o", "$ \r\n"] [54.541962, "o", "$ b"] [54.570224, "o", "o"] [54.597517, "o", "r"] [54.62934, "o", "g"] [54.680347, "o", " "] [54.708874, "o", "c"] [54.74704, "o", "r"] [54.789626, "o", "e"] [54.827188, "o", "a"] [54.836664, "o", "t"] [54.901889, "o", "e"] [55.102264, "o", " "] [55.128036, "o", "-"] [55.143576, "o", "-"] [55.169344, "o", "s"] [55.195965, "o", "t"] [55.291014, "o", "a"] [55.367836, "o", "t"] [55.388423, "o", "s"] [55.571545, "o", " "] [55.581105, "o", "-"] [55.604771, "o", "-"] [55.617271, "o", "p"] [55.717512, "o", "r"] [55.795014, "o", "o"] [55.889328, "o", "g"] [55.898787, "o", "r"] [56.099465, "o", "e"] [56.191812, "o", "s"] [56.282367, "o", "s"] [56.392067, "o", " "] [56.444626, "o", "-"] [56.453934, "o", "-"] [56.554258, "o", "c"] [56.661366, "o", "o"] [56.672796, "o", "m"] [56.690574, "o", "p"] [56.700005, "o", "r"] [56.76936, "o", "e"] [56.813887, "o", "s"] [57.01435, "o", "s"] [57.046765, "o", "i"] [57.061266, "o", "o"] [57.070634, "o", "n"] [57.143411, "o", " "] [57.169351, "o", "l"] [57.213313, "o", "z"] [57.237415, "o", "4"] [57.437721, "o", " "] [57.556322, "o", "/"] [57.569348, "o", "m"] [57.57873, "o", "e"] [57.588073, "o", "d"] [57.59747, "o", "i"] [57.740032, "o", "a"] [57.833541, "o", "/"] [57.910749, "o", "b"] [57.990587, "o", "a"] [58.03506, "o", "c"] [58.084774, "o", "k"] [58.110399, "o", "u"] [58.119855, "o", "p"] [58.129364, "o", "/"] [58.16133, "o", "b"] [58.272751, "o", "o"] [58.332329, "o", "r"] [58.384694, "o", "g"] [58.541406, "o", "d"] [58.593907, "o", "e"] [58.621372, "o", "m"] [58.640502, "o", "o"] [58.741339, "o", ":"] [58.792742, "o", ":"] [58.834033, "o", "b"] [58.913401, "o", "a"] [58.922796, "o", "c"] [59.073337, "o", "k"] [59.082618, "o", "u"] [59.101057, "o", "p"] [59.208574, "o", "2"] [59.217983, "o", " "] [59.233577, "o", "W"] [59.301331, "o", "a"] [59.396659, "o", "l"] [59.408194, "o", "l"] [59.437392, "o", "p"] [59.467902, "o", "a"] [59.668385, "o", "p"] [59.68178, "o", "e"] [59.766896, "o", "r"] [59.818201, "o", "\r\n"] [60.775328, "o", "Enter passphrase for key /media/backup/borgdemo: "] [62.286369, "o", "\r\r\n"] [62.373384, "o", "0 B O 0 B C 0 B D 0 N Wallpaper \r"] [62.374541, "o", "Initializing cache transaction: Reading config \r"] [62.375102, "o", "Initializing cache transaction: Reading chunks \r"] [62.37576, "o", "Initializing cache transaction: Reading files \r"] [62.376372, "o", " \r"] [62.390566, "o", " \r"] [62.405895, "o", "Saving files cache \r"] [62.407022, "o", "Saving chunks cache \r"] [62.407729, "o", "Saving cache config \r"] [62.409963, "o", " \r"] [62.41203, "o", "------------------------------------------------------------------------------\r\r\n"] [62.412335, "o", "Repository: /media/backup/borgdemo"] [62.412659, "o", "\r\r\n"] [62.412993, "o", "Archive name: backup2"] [62.413341, "o", "\r\r\n"] [62.413707, "o", "Archive fingerprint: 70d0bd96eb512ca96356308fb4aab162ae7d868c9fe491a36b1d8b00190e8e97"] [62.413751, "o", "\r\r\n"] [62.414127, "o", "Time (start): Wed, 2022-07-06 21:28:40"] [62.414342, "o", "\r\r\n"] [62.414764, "o", "Time (end): Wed, 2022-07-06 21:28:40"] [62.414975, "o", "\r\r\n"] [62.415392, "o", "Duration: 0.02 seconds"] [62.415608, "o", "\r\r\n"] [62.41596, "o", "Number of files: 32"] [62.416062, "o", "\r\r\n"] [62.416542, "o", "Utilization of max. archive size: 0%"] [62.416767, "o", "\r\r\n"] [62.417159, "o", "------------------------------------------------------------------------------"] [62.417305, "o", "\r\r\n"] [62.417746, "o", " Original size Compressed size Deduplicated size"] [62.417864, "o", "\r\r\n"] [62.418171, "o", "This archive: 401.15 MB 399.74 MB 604 B"] [62.418576, "o", "\r\r\n"] [62.418965, "o", "All archives: 802.29 MB 799.48 MB 399.57 MB"] [62.419161, "o", "\r\r\n"] [62.419577, "o", "\r\r\n"] [62.419938, "o", " Unique chunks Total chunks"] [62.420131, "o", "\r\r\n"] [62.420581, "o", "Chunk index: 211 419"] [62.420775, "o", "\r\r\n"] [62.421247, "o", "------------------------------------------------------------------------------"] [62.421462, "o", "\r\r\n"] [62.4692, "o", "$ \r\n"] [62.472497, "o", "$ #"] [62.48179, "o", " "] [62.651124, "o", "W"] [62.660372, "o", "o"] [62.786678, "o", "w"] [62.850953, "o", ","] [62.942261, "o", " "] [62.952488, "o", "t"] [63.093759, "o", "h"] [63.102987, "o", "i"] [63.150301, "o", "s"] [63.217527, "o", " "] [63.226846, "o", "w"] [63.286129, "o", "a"] [63.309429, "o", "s"] [63.364749, "o", " "] [63.425058, "o", "a"] [63.449431, "o", " "] [63.458651, "o", "l"] [63.467865, "o", "o"] [63.500253, "o", "t"] [63.573463, "o", " "] [63.616768, "o", "f"] [63.648075, "o", "a"] [63.692378, "o", "s"] [63.714715, "o", "t"] [63.829065, "o", "e"] [63.938338, "o", "r"] [64.13862, "o", "!"] [64.15888, "o", "\r\n"] [64.165067, "o", "$ #"] [64.215376, "o", " "] [64.241616, "o", "N"] [64.252934, "o", "o"] [64.294204, "o", "t"] [64.303462, "o", "i"] [64.332765, "o", "c"] [64.358045, "o", "e"] [64.36728, "o", " "] [64.391572, "o", "t"] [64.411851, "o", "h"] [64.467152, "o", "e"] [64.572418, "o", " "] [64.585626, "o", "\""] [64.594877, "o", "D"] [64.604145, "o", "e"] [64.658491, "o", "d"] [64.747708, "o", "u"] [64.756943, "o", "p"] [64.823255, "o", "l"] [64.914556, "o", "i"] [64.948846, "o", "c"] [64.958142, "o", "a"] [64.985391, "o", "t"] [65.152715, "o", "e"] [65.177085, "o", "d"] [65.259267, "o", " "] [65.309462, "o", "s"] [65.325649, "o", "i"] [65.357957, "o", "z"] [65.528257, "o", "e"] [65.600556, "o", "\""] [65.707856, "o", " "] [65.717101, "o", "f"] [65.750507, "o", "o"] [65.779701, "o", "r"] [65.788939, "o", " "] [65.849319, "o", "\""] [65.942606, "o", "T"] [65.951859, "o", "h"] [65.961183, "o", "i"] [66.115428, "o", "s"] [66.184735, "o", " "] [66.220023, "o", "a"] [66.230285, "o", "r"] [66.279587, "o", "c"] [66.297785, "o", "h"] [66.395105, "o", "i"] [66.408354, "o", "v"] [66.428654, "o", "e"] [66.473928, "o", "\""] [66.483134, "o", "!"] [66.571433, "o", "\r\n"] [66.576919, "o", "$ #"] [66.592225, "o", " "] [66.633417, "o", "B"] [66.708708, "o", "o"] [66.753999, "o", "r"] [66.76423, "o", "g"] [66.773413, "o", " "] [66.867707, "o", "r"] [66.877054, "o", "e"] [66.897308, "o", "c"] [66.962672, "o", "o"] [66.971899, "o", "g"] [67.017259, "o", "n"] [67.026475, "o", "i"] [67.035723, "o", "z"] [67.053037, "o", "e"] [67.132276, "o", "d"] [67.14758, "o", " "] [67.156806, "o", "t"] [67.226104, "o", "h"] [67.252425, "o", "a"] [67.307794, "o", "t"] [67.508065, "o", " "] [67.517322, "o", "m"] [67.717495, "o", "o"] [67.797754, "o", "s"] [67.80702, "o", "t"] [67.816244, "o", " "] [67.949494, "o", "f"] [68.062791, "o", "i"] [68.121067, "o", "l"] [68.140367, "o", "e"] [68.276663, "o", "s"] [68.304952, "o", " "] [68.407232, "o", "d"] [68.445403, "o", "i"] [68.474727, "o", "d"] [68.593061, "o", " "] [68.617374, "o", "n"] [68.640634, "o", "o"] [68.649862, "o", "t"] [68.662149, "o", " "] [68.69242, "o", "c"] [68.71764, "o", "h"] [68.726846, "o", "a"] [68.765193, "o", "n"] [68.79645, "o", "g"] [68.893677, "o", "e"] [68.953943, "o", " "] [69.002217, "o", "a"] [69.025452, "o", "n"] [69.127777, "o", "d"] [69.13708, "o", " "] [69.156345, "o", "d"] [69.214641, "o", "e"] [69.300936, "o", "d"] [69.409271, "o", "u"] [69.476576, "o", "p"] [69.596893, "o", "l"] [69.68111, "o", "i"] [69.706158, "o", "c"] [69.728513, "o", "a"] [69.846752, "o", "t"] [69.867018, "o", "e"] [69.9153, "o", "d"] [70.065545, "o", " "] [70.168883, "o", "t"] [70.258152, "o", "h"] [70.271501, "o", "e"] [70.28073, "o", "m"] [70.481093, "o", "."] [70.490785, "o", "\r\n"] [70.496448, "o", "$ \r\n"] [70.501664, "o", "$ #"] [70.529964, "o", " "] [70.714221, "o", "B"] [70.750476, "o", "u"] [70.866685, "o", "t"] [70.958924, "o", " "] [71.149225, "o", "w"] [71.166498, "o", "h"] [71.359756, "o", "a"] [71.407991, "o", "t"] [71.421261, "o", " "] [71.430476, "o", "h"] [71.448747, "o", "a"] [71.599001, "o", "p"] [71.750234, "o", "p"] [71.833426, "o", "e"] [71.882648, "o", "n"] [71.920917, "o", "s"] [71.945239, "o", ","] [72.145414, "o", " "] [72.174626, "o", "w"] [72.214867, "o", "h"] [72.239108, "o", "e"] [72.439393, "o", "n"] [72.461603, "o", " "] [72.473817, "o", "w"] [72.487089, "o", "e"] [72.687371, "o", " "] [72.86364, "o", "m"] [72.879841, "o", "o"] [72.999103, "o", "v"] [73.009202, "o", "e"] [73.075457, "o", " "] [73.127698, "o", "a"] [73.248951, "o", " "] [73.258159, "o", "d"] [73.272426, "o", "i"] [73.329649, "o", "r"] [73.354882, "o", " "] [73.364101, "o", "a"] [73.381275, "o", "n"] [73.402546, "o", "d"] [73.411758, "o", " "] [73.435011, "o", "c"] [73.44422, "o", "r"] [73.453394, "o", "e"] [73.610611, "o", "a"] [73.670846, "o", "t"] [73.711101, "o", "e"] [73.730366, "o", " "] [73.761533, "o", "a"] [73.909763, "o", " "] [73.94401, "o", "n"] [74.037243, "o", "e"] [74.109435, "o", "w"] [74.173634, "o", " "] [74.262874, "o", "b"] [74.335139, "o", "a"] [74.366424, "o", "c"] [74.447673, "o", "k"] [74.531912, "o", "u"] [74.555155, "o", "p"] [74.75541, "o", "?"] [74.765242, "o", "\r\n"] [74.77042, "o", "$ m"] [74.828702, "o", "v"] [75.028952, "o", " "] [75.038207, "o", "W"] [75.047413, "o", "a"] [75.059647, "o", "l"] [75.068871, "o", "l"] [75.080099, "o", "p"] [75.122399, "o", "a"] [75.131582, "o", "p"] [75.174824, "o", "e"] [75.201146, "o", "r"] [75.401312, "o", "/"] [75.439551, "o", "b"] [75.543797, "o", "i"] [75.558018, "o", "g"] [75.590267, "o", "c"] [75.610529, "o", "o"] [75.687784, "o", "l"] [75.888044, "o", "l"] [75.898236, "o", "e"] [75.938527, "o", "c"] [75.950758, "o", "t"] [76.027992, "o", "i"] [76.037221, "o", "o"] [76.097415, "o", "n"] [76.186636, "o", " "] [76.241869, "o", "W"] [76.251078, "o", "a"] [76.294356, "o", "l"] [76.361534, "o", "l"] [76.465786, "o", "p"] [76.478756, "o", "a"] [76.546004, "o", "p"] [76.606267, "o", "e"] [76.615487, "o", "r"] [76.745722, "o", "/"] [76.816982, "o", "b"] [76.826175, "o", "i"] [76.843447, "o", "g"] [76.896703, "o", "c"] [76.96194, "o", "o"] [76.994172, "o", "l"] [77.014431, "o", "l"] [77.023644, "o", "e"] [77.044914, "o", "c"] [77.067174, "o", "t"] [77.084444, "o", "i"] [77.142665, "o", "o"] [77.151852, "o", "n"] [77.161129, "o", "_"] [77.213291, "o", "N"] [77.244548, "o", "E"] [77.260819, "o", "W"] [77.318801, "o", "\r\n"] [77.329679, "o", "$ \r\n"] [77.33314, "o", "$ b"] [77.342305, "o", "o"] [77.54254, "o", "r"] [77.628787, "o", "g"] [77.676116, "o", " "] [77.743374, "o", "c"] [77.774609, "o", "r"] [77.801173, "o", "e"] [77.815449, "o", "a"] [77.825401, "o", "t"] [77.846987, "o", "e"] [78.047448, "o", " "] [78.106935, "o", "-"] [78.14254, "o", "-"] [78.153344, "o", "s"] [78.16262, "o", "t"] [78.172102, "o", "a"] [78.209331, "o", "t"] [78.241352, "o", "s"] [78.411208, "o", " "] [78.513499, "o", "-"] [78.59962, "o", "-"] [78.609363, "o", "p"] [78.6297, "o", "r"] [78.798934, "o", "o"] [78.808385, "o", "g"] [79.008927, "o", "r"] [79.020447, "o", "e"] [79.049225, "o", "s"] [79.059695, "o", "s"] [79.205329, "o", " "] [79.214453, "o", "-"] [79.227016, "o", "-"] [79.297294, "o", "c"] [79.49812, "o", "o"] [79.520796, "o", "m"] [79.536348, "o", "p"] [79.553311, "o", "r"] [79.592977, "o", "e"] [79.629942, "o", "s"] [79.656728, "o", "s"] [79.774107, "o", "i"] [79.808919, "o", "o"] [79.911941, "o", "n"] [79.933374, "o", " "] [79.951845, "o", "l"] [80.152409, "o", "z"] [80.313323, "o", "4"] [80.493778, "o", " "] [80.525364, "o", "/"] [80.601345, "o", "m"] [80.634233, "o", "e"] [80.643571, "o", "d"] [80.750189, "o", "i"] [80.83339, "o", "a"] [80.985813, "o", "/"] [81.068058, "o", "b"] [81.109491, "o", "a"] [81.309927, "o", "c"] [81.323449, "o", "k"] [81.392508, "o", "u"] [81.428703, "o", "p"] [81.449275, "o", "/"] [81.516755, "o", "b"] [81.526222, "o", "o"] [81.536684, "o", "r"] [81.546145, "o", "g"] [81.555588, "o", "d"] [81.582746, "o", "e"] [81.636297, "o", "m"] [81.681736, "o", "o"] [81.773332, "o", ":"] [81.793656, "o", ":"] [81.895127, "o", "b"] [81.926675, "o", "a"] [81.962742, "o", "c"] [81.973139, "o", "k"] [82.004992, "o", "u"] [82.021323, "o", "p"] [82.10136, "o", "3"] [82.217881, "o", " "] [82.229341, "o", "W"] [82.244659, "o", "a"] [82.281391, "o", "l"] [82.335922, "o", "l"] [82.345291, "o", "p"] [82.357335, "o", "a"] [82.389372, "o", "p"] [82.414494, "o", "e"] [82.497747, "o", "r"] [82.566107, "o", "\r\n"] [83.485219, "o", "Enter passphrase for key /media/backup/borgdemo: "] [85.045519, "o", "\r"] [85.046073, "o", "\r\n"] [85.13408, "o", "0 B O 0 B C 0 B D 0 N Wallpaper \r"] [85.151946, "o", "Initializing cache transaction: Reading config \r"] [85.152609, "o", "Initializing cache transaction: Reading chunks \r"] [85.153323, "o", "Initializing cache transaction: Reading files \r"] [85.153874, "o", " \r"] [85.3408, "o", "41.33 MB O 41.19 MB C 0 B D 1 N Wallpaper/bigcollecti...enCL_528814521414_8K.jpg\r"] [85.554348, "o", "85.57 MB O 85.31 MB C 0 B D 4 N Wallpaper/bigcollecti...x_OpenCL_18915424_8K.jpg\r"] [85.756273, "o", "126.96 MB O 126.69 MB C 0 B D 10 N Wallpaper/bigcollec...penCL_4258952414_8K.jpg\r"] [85.966979, "o", "170.79 MB O 170.51 MB C 0 B D 11 N Wallpaper/bigcollec...penCL_9648145412_8K.jpg\r"] [86.170995, "o", "212.59 MB O 211.81 MB C 0 B D 16 N Wallpaper/bigcollec...enCL_51241841541_8K.jpg\r"] [86.372217, "o", "255.05 MB O 254.10 MB C 0 B D 20 N Wallpaper/bigcollec...OpenCL_644289452_8K.jpg\r"] [86.585677, "o", "298.20 MB O 296.90 MB C 0 B D 25 N Wallpaper/bigcollec...OpenCL_545185481_8K.jpg\r"] [86.794185, "o", "342.20 MB O 340.79 MB C 0 B D 27 N Wallpaper/bigcollec...wamm_OpenCl_6184524.jpg\r"] [86.996936, "o", "384.57 MB O 383.17 MB C 0 B D 29 N Wallpaper/bigcollec...ects._(14168975789).jpg\r"] [87.073155, "o", " \r"] [87.092608, "o", "Saving files cache \r"] [87.094182, "o", "Saving chunks cache \r"] [87.094972, "o", "Saving cache config \r"] [87.096986, "o", " \r"] [87.099604, "o", "------------------------------------------------------------------------------\r\r\nRepository: /media/backup/borgdemo\r\r\n"] [87.099914, "o", "Archive name: backup3\r\r\n"] [87.100242, "o", "Archive fingerprint: 5ae78abd6558856f14289b7af3ea13f8550655c46624ad18d76e40da6ed1ee04\r\r\n"] [87.100603, "o", "Time (start): Wed, 2022-07-06 21:29:03\r\r\n"] [87.100876, "o", "Time (end): Wed, 2022-07-06 21:29:05\r\r\n"] [87.101208, "o", "Duration: 1.95 seconds\r\r\n"] [87.1015, "o", "Number of files: 32\r\r\n"] [87.10179, "o", "Utilization of max. archive size: 0%\r\r\n"] [87.102078, "o", "------------------------------------------------------------------------------\r\r\n"] [87.102524, "o", " Original size Compressed size Deduplicated size\r\r\n"] [87.102832, "o", "This archive: 401.15 MB 399.74 MB 550 B\r\r\n"] [87.103139, "o", "All archives: 1.20 GB 1.20 GB 399.58 MB\r\r\n"] [87.103436, "o", "\r\r\n"] [87.103734, "o", " Unique chunks Total chunks\r\r\n"] [87.104117, "o", "Chunk index: 213 629\r\r\n"] [87.104448, "o", "------------------------------------------------------------------------------\r\r\n"] [87.152849, "o", "$ \r\n"] [87.156745, "o", "$ #"] [87.209512, "o", " "] [87.293916, "o", "S"] [87.348284, "o", "t"] [87.366999, "o", "i"] [87.520505, "o", "l"] [87.53834, "o", "l"] [87.575493, "o", " "] [87.717844, "o", "q"] [87.821336, "o", "u"] [87.893612, "o", "i"] [87.933639, "o", "t"] [87.94296, "o", "e"] [87.976865, "o", " "] [88.014021, "o", "f"] [88.075291, "o", "a"] [88.097374, "o", "s"] [88.159006, "o", "t"] [88.203224, "o", "…"] [88.229661, "o", "\r\n"] [88.236034, "o", "$ #"] [88.37343, "o", " "] [88.40558, "o", "B"] [88.528518, "o", "u"] [88.569622, "o", "t"] [88.773509, "o", " "] [88.845482, "o", "w"] [88.876157, "o", "h"] [89.02846, "o", "e"] [89.037858, "o", "n"] [89.238437, "o", " "] [89.38946, "o", "y"] [89.441827, "o", "o"] [89.514261, "o", "u"] [89.610734, "o", " "] [89.677386, "o", "l"] [89.710803, "o", "o"] [89.767294, "o", "o"] [89.777491, "o", "k"] [89.833962, "o", " "] [89.873237, "o", "a"] [89.913382, "o", "t"] [90.045417, "o", " "] [90.06921, "o", "t"] [90.116559, "o", "h"] [90.131995, "o", "e"] [90.323428, "o", " "] [90.473531, "o", "\""] [90.648246, "o", "d"] [90.661648, "o", "e"] [90.670834, "o", "d"] [90.723701, "o", "u"] [90.849449, "o", "p"] [90.892885, "o", "l"] [90.9136, "o", "i"] [90.945812, "o", "c"] [90.986109, "o", "a"] [91.08574, "o", "t"] [91.146406, "o", "e"] [91.16304, "o", "d"] [91.363904, "o", " "] [91.417291, "o", "f"] [91.433481, "o", "i"] [91.451001, "o", "l"] [91.481545, "o", "e"] [91.57759, "o", " "] [91.58698, "o", "s"] [91.706954, "o", "i"] [91.724347, "o", "z"] [91.8136, "o", "e"] [91.825139, "o", "\""] [91.872525, "o", " "] [91.881863, "o", "a"] [91.973617, "o", "g"] [92.177436, "o", "a"] [92.251273, "o", "i"] [92.353509, "o", "n"] [92.537927, "o", ","] [92.571284, "o", " "] [92.663267, "o", "y"] [92.750934, "o", "o"] [92.797332, "o", "u"] [92.829397, "o", " "] [92.897956, "o", "s"] [92.908352, "o", "e"] [92.931057, "o", "e"] [92.955633, "o", " "] [92.992248, "o", "t"] [93.192829, "o", "h"] [93.320984, "o", "a"] [93.40135, "o", "t"] [93.60199, "o", " "] [93.645112, "o", "b"] [93.724391, "o", "o"] [93.862343, "o", "r"] [93.937373, "o", "g"] [93.990798, "o", " "] [94.150589, "o", "a"] [94.207057, "o", "l"] [94.241414, "o", "s"] [94.283425, "o", "o"] [94.436593, "o", " "] [94.446044, "o", "r"] [94.509642, "o", "e"] [94.535483, "o", "c"] [94.631229, "o", "o"] [94.665375, "o", "g"] [94.680755, "o", "n"] [94.713345, "o", "i"] [94.782818, "o", "z"] [94.792313, "o", "e"] [94.809509, "o", "d"] [94.853814, "o", " "] [94.863216, "o", "t"] [94.872586, "o", "h"] [94.89334, "o", "a"] [94.908753, "o", "t"] [94.941284, "o", " "] [94.987778, "o", "o"] [95.002399, "o", "n"] [95.147681, "o", "l"] [95.157126, "o", "y"] [95.173363, "o", " "] [95.19082, "o", "t"] [95.365301, "o", "h"] [95.454473, "o", "e"] [95.463921, "o", " "] [95.473274, "o", "d"] [95.551342, "o", "i"] [95.618296, "o", "r"] [95.718537, "o", " "] [95.741359, "o", "a"] [95.758986, "o", "n"] [95.793351, "o", "d"] [95.97488, "o", " "] [96.032312, "o", "n"] [96.05375, "o", "o"] [96.063143, "o", "t"] [96.26534, "o", " "] [96.349141, "o", "t"] [96.442503, "o", "h"] [96.52983, "o", "e"] [96.540245, "o", " "] [96.551714, "o", "f"] [96.616104, "o", "i"] [96.625488, "o", "l"] [96.642864, "o", "e"] [96.843732, "o", "s"] [96.861188, "o", " "] [96.977283, "o", "c"] [96.991812, "o", "h"] [97.095082, "o", "a"] [97.1045, "o", "n"] [97.113948, "o", "g"] [97.123416, "o", "e"] [97.209317, "o", "d"] [97.4101, "o", " "] [97.425341, "o", "i"] [97.589299, "o", "n"] [97.665522, "o", " "] [97.733363, "o", "t"] [97.755937, "o", "h"] [97.808313, "o", "i"] [97.817807, "o", "s"] [98.014252, "o", " "] [98.04937, "o", "b"] [98.081819, "o", "a"] [98.26958, "o", "c"] [98.317334, "o", "k"] [98.344896, "o", "u"] [98.385091, "o", "p"] [98.441567, "o", "."] [98.451772, "o", "\r\n"] [98.456939, "o", "$ \r\n"] [98.461066, "o", "$ #"] [98.529239, "o", " "] [98.570449, "o", "N"] [98.63069, "o", "o"] [98.696949, "o", "w"] [98.7642, "o", " "] [98.78642, "o", "l"] [98.855663, "o", "e"] [98.87792, "o", "t"] [98.906218, "o", "s"] [99.106438, "o", " "] [99.143675, "o", "l"] [99.152895, "o", "o"] [99.203156, "o", "o"] [99.240405, "o", "k"] [99.376664, "o", " "] [99.385854, "o", "i"] [99.400081, "o", "n"] [99.409194, "o", "t"] [99.495504, "o", "o"] [99.504768, "o", " "] [99.572024, "o", "a"] [99.581179, "o", " "] [99.617371, "o", "r"] [99.650628, "o", "e"] [99.705883, "o", "p"] [99.852135, "o", "o"] [99.861273, "o", "."] [99.964328, "o", "\r\n"] [99.969277, "o", "$ b"] [100.000532, "o", "o"] [100.009762, "o", "r"] [100.043017, "o", "g"] [100.052226, "o", " "] [100.0885, "o", "l"] [100.288738, "o", "i"] [100.305975, "o", "s"] [100.414237, "o", "t"] [100.549426, "o", " "] [100.576746, "o", "/"] [100.585934, "o", "m"] [100.63621, "o", "e"] [100.793379, "o", "d"] [100.802584, "o", "i"] [100.811831, "o", "a"] [100.840099, "o", "/"] [100.934358, "o", "b"] [100.945531, "o", "a"] [100.980793, "o", "c"] [101.046051, "o", "k"] [101.127323, "o", "u"] [101.146544, "o", "p"] [101.208799, "o", "/"] [101.316059, "o", "b"] [101.408305, "o", "o"] [101.559559, "o", "r"] [101.57882, "o", "g"] [101.619053, "o", "d"] [101.690318, "o", "e"] [101.851581, "o", "m"] [101.868834, "o", "o"] [101.895837, "o", "\r\n"] [102.82836, "o", "Enter passphrase for key /media/backup/borgdemo: "] [104.226926, "o", "\r\r\n"] [104.317737, "o", "backup1 Wed, 2022-07-06 21:28:23 [4b2f146af02ce3a4df8018411ce46d97fe7eaad0512c969a4bbcd4f113900746]\r\r\nbackup2 Wed, 2022-07-06 21:28:40 [70d0bd96eb512ca96356308fb4aab162ae7d868c9fe491a36b1d8b00190e8e97]\r\r\n"] [104.318451, "o", "backup3 Wed, 2022-07-06 21:29:03 [5ae78abd6558856f14289b7af3ea13f8550655c46624ad18d76e40da6ed1ee04]\r\r\n"] [104.367169, "o", "$ "] [104.368499, "o", "\r\n"] [104.373216, "o", "$ "] [104.373787, "o", "#"] [104.385513, "o", " "] [104.40682, "o", "Y"] [104.441491, "o", "o"] [104.477718, "o", "u"] [104.678172, "o", "'"] [104.762495, "o", "l"] [104.794922, "o", "l"] [104.812639, "o", " "] [104.833147, "o", "s"] [104.901743, "o", "e"] [104.950219, "o", "e"] [105.122774, "o", " "] [105.177278, "o", "a"] [105.221438, "o", " "] [105.240947, "o", "l"] [105.26949, "o", "i"] [105.289501, "o", "s"] [105.301427, "o", "t"] [105.501757, "o", " "] [105.544171, "o", "o"] [105.555657, "o", "f"] [105.665411, "o", " "] [105.835304, "o", "a"] [105.84469, "o", "l"] [105.853955, "o", "l"] [105.878739, "o", " "] [105.93132, "o", "b"] [105.952058, "o", "a"] [105.981516, "o", "c"] [105.993763, "o", "k"] [106.035503, "o", "u"] [106.065513, "o", "p"] [106.109516, "o", "s"] [106.118728, "o", "."] [106.182447, "o", "\r\n"] [106.188985, "o", "$ #"] [106.289073, "o", " "] [106.29939, "o", "Y"] [106.308874, "o", "o"] [106.321566, "o", "u"] [106.521863, "o", " "] [106.596728, "o", "c"] [106.67751, "o", "a"] [106.700634, "o", "n"] [106.714282, "o", " "] [106.723841, "o", "a"] [106.807925, "o", "l"] [106.821467, "o", "s"] [107.025494, "o", "o"] [107.098377, "o", " "] [107.169619, "o", "u"] [107.180018, "o", "s"] [107.329587, "o", "e"] [107.397557, "o", " "] [107.419245, "o", "t"] [107.511283, "o", "h"] [107.572895, "o", "e"] [107.717994, "o", " "] [107.870106, "o", "s"] [107.993322, "o", "a"] [108.194277, "o", "m"] [108.229638, "o", "e"] [108.271397, "o", " "] [108.290782, "o", "c"] [108.333358, "o", "o"] [108.422361, "o", "m"] [108.46158, "o", "m"] [108.472043, "o", "a"] [108.481327, "o", "n"] [108.541571, "o", "d"] [108.56016, "o", " "] [108.625484, "o", "t"] [108.661281, "o", "o"] [108.861774, "o", " "] [108.871238, "o", "l"] [108.89738, "o", "o"] [108.993343, "o", "o"] [109.118797, "o", "k"] [109.201386, "o", " "] [109.29193, "o", "i"] [109.303382, "o", "n"] [109.321804, "o", "t"] [109.402727, "o", "o"] [109.419369, "o", " "] [109.46062, "o", "a"] [109.470188, "o", "n"] [109.670891, "o", " "] [109.687394, "o", "a"] [109.745313, "o", "r"] [109.807824, "o", "c"] [109.969317, "o", "h"] [110.023573, "o", "i"] [110.036116, "o", "v"] [110.045485, "o", "e"] [110.245943, "o", "."] [110.324697, "o", " "] [110.441361, "o", "B"] [110.459869, "o", "u"] [110.52936, "o", "t"] [110.729573, "o", " "] [110.738933, "o", "w"] [110.864128, "o", "e"] [110.969566, "o", " "] [110.984119, "o", "b"] [111.037292, "o", "e"] [111.108067, "o", "t"] [111.156532, "o", "t"] [111.172115, "o", "e"] [111.201326, "o", "r"] [111.405311, "o", " "] [111.506429, "o", "f"] [111.533961, "o", "i"] [111.548383, "o", "l"] [111.603669, "o", "t"] [111.645926, "o", "e"] [111.730354, "o", "r"] [111.817926, "o", " "] [111.838689, "o", "t"] [111.937146, "o", "h"] [111.953327, "o", "e"] [111.962645, "o", " "] [112.009343, "o", "o"] [112.13978, "o", "u"] [112.149267, "o", "t"] [112.205358, "o", "p"] [112.262791, "o", "u"] [112.309253, "o", "t"] [112.393456, "o", " "] [112.594288, "o", "h"] [112.617351, "o", "e"] [112.650093, "o", "r"] [112.721339, "o", "e"] [112.730808, "o", ":"] [112.846207, "o", "\r\n"] [112.851859, "o", "$ b"] [112.911145, "o", "o"] [112.938366, "o", "r"] [112.963629, "o", "g"] [113.163858, "o", " "] [113.215117, "o", "l"] [113.235372, "o", "i"] [113.322596, "o", "s"] [113.331815, "o", "t"] [113.376114, "o", " "] [113.391322, "o", "/"] [113.416594, "o", "m"] [113.425798, "o", "e"] [113.519058, "o", "d"] [113.554283, "o", "i"] [113.636552, "o", "a"] [113.647766, "o", "/"] [113.667031, "o", "b"] [113.676302, "o", "a"] [113.757516, "o", "c"] [113.780751, "o", "k"] [113.869002, "o", "u"] [113.902268, "o", "p"] [114.040515, "o", "/"] [114.049714, "o", "b"] [114.063965, "o", "o"] [114.169232, "o", "r"] [114.195472, "o", "g"] [114.204679, "o", "d"] [114.334957, "o", "e"] [114.353261, "o", "m"] [114.362488, "o", "o"] [114.411742, "o", ":"] [114.446012, "o", ":"] [114.47327, "o", "b"] [114.494521, "o", "a"] [114.503728, "o", "c"] [114.575963, "o", "k"] [114.72126, "o", "u"] [114.903516, "o", "p"] [114.912735, "o", "3"] [114.936005, "o", " "] [114.959221, "o", "|"] [114.9684, "o", " "] [115.055677, "o", "g"] [115.107938, "o", "r"] [115.160181, "o", "e"] [115.220437, "o", "p"] [115.420688, "o", " "] [115.505923, "o", "'"] [115.540179, "o", "d"] [115.54932, "o", "e"] [115.576685, "o", "e"] [115.64793, "o", "r"] [115.847168, "o", "."] [115.889359, "o", "j"] [115.898548, "o", "p"] [115.965835, "o", "g"] [116.166111, "o", "'"] [116.175915, "o", "\r\n"] [117.125741, "o", "Enter passphrase for key /media/backup/borgdemo: "] [118.933873, "o", "\r\r\n"] [119.024246, "o", "-rw-r--r-- root root 193033 Wed, 2022-07-06 21:27:37 Wallpaper/deer.jpg\r\r\n"] [119.077744, "o", "$ \r\n"] [119.081473, "o", "$ #"] [119.201982, "o", " "] [119.211507, "o", "O"] [119.253728, "o", "h"] [119.389612, "o", ","] [119.439978, "o", " "] [119.461686, "o", "w"] [119.52014, "o", "e"] [119.721157, "o", " "] [119.829452, "o", "f"] [119.853429, "o", "o"] [119.873001, "o", "u"] [119.891743, "o", "n"] [119.905595, "o", "d"] [120.10608, "o", " "] [120.148696, "o", "o"] [120.209153, "o", "u"] [120.333417, "o", "r"] [120.364325, "o", " "] [120.466866, "o", "p"] [120.581481, "o", "i"] [120.621287, "o", "c"] [120.640078, "o", "t"] [120.840575, "o", "u"] [120.857471, "o", "r"] [120.893922, "o", "e"] [121.020255, "o", "."] [121.036901, "o", " "] [121.071178, "o", "N"] [121.081806, "o", "o"] [121.099482, "o", "w"] [121.131093, "o", " "] [121.232917, "o", "e"] [121.242505, "o", "x"] [121.290029, "o", "t"] [121.337675, "o", "r"] [121.377245, "o", "a"] [121.534948, "o", "c"] [121.545462, "o", "t"] [121.60099, "o", " "] [121.649524, "o", "i"] [121.677501, "o", "t"] [121.749168, "o", "…"] [121.870956, "o", "\r\n"] [121.878453, "o", "$ m"] [121.905742, "o", "v"] [121.937554, "o", " "] [121.946689, "o", "W"] [121.967065, "o", "a"] [121.985499, "o", "l"] [121.994821, "o", "l"] [122.009501, "o", "p"] [122.057426, "o", "a"] [122.066873, "o", "p"] [122.267285, "o", "e"] [122.360793, "o", "r"] [122.453415, "o", " "] [122.513784, "o", "W"] [122.6265, "o", "a"] [122.730453, "o", "l"] [122.841305, "o", "l"] [123.009354, "o", "p"] [123.129353, "o", "a"] [123.169359, "o", "p"] [123.275878, "o", "e"] [123.313358, "o", "r"] [123.513655, "o", "."] [123.545339, "o", "o"] [123.559582, "o", "r"] [123.749344, "o", "i"] [123.801308, "o", "g"] [123.994717, "o", "\r\n"] [124.003853, "o", "$ b"] [124.013117, "o", "o"] [124.051773, "o", "r"] [124.098153, "o", "g"] [124.181336, "o", " "] [124.225346, "o", "e"] [124.291694, "o", "x"] [124.345105, "o", "t"] [124.442852, "o", "r"] [124.478188, "o", "a"] [124.531671, "o", "c"] [124.545143, "o", "t"] [124.634767, "o", " "] [124.689277, "o", "/"] [124.701314, "o", "m"] [124.870659, "o", "e"] [124.893388, "o", "d"] [124.961893, "o", "i"] [125.0534, "o", "a"] [125.062828, "o", "/"] [125.125355, "o", "b"] [125.193722, "o", "a"] [125.232321, "o", "c"] [125.277111, "o", "k"] [125.286592, "o", "u"] [125.317362, "o", "p"] [125.473543, "o", "/"] [125.493323, "o", "b"] [125.505356, "o", "o"] [125.531743, "o", "r"] [125.652233, "o", "g"] [125.677305, "o", "d"] [125.717712, "o", "e"] [125.747622, "o", "m"] [125.909364, "o", "o"] [126.110164, "o", ":"] [126.185459, "o", ":"] [126.217306, "o", "b"] [126.235863, "o", "a"] [126.261279, "o", "c"] [126.437637, "o", "k"] [126.610395, "o", "u"] [126.660867, "o", "p"] [126.681269, "o", "3"] [126.774683, "o", " "] [126.808707, "o", "W"] [126.826052, "o", "a"] [126.889457, "o", "l"] [126.902847, "o", "l"] [126.913333, "o", "p"] [126.977672, "o", "a"] [127.040095, "o", "p"] [127.093863, "o", "e"] [127.108476, "o", "r"] [127.117779, "o", "/"] [127.177324, "o", "d"] [127.208027, "o", "e"] [127.258753, "o", "e"] [127.328181, "o", "r"] [127.430698, "o", "."] [127.440219, "o", "j"] [127.493813, "o", "p"] [127.69734, "o", "g"] [127.745783, "o", "\r\n"] [128.676873, "o", "Enter passphrase for key /media/backup/borgdemo: "] [130.262495, "o", "\r\r\n"] [130.40589, "o", "$ \r\n"] [130.408917, "o", "$ \r\n"] [130.411592, "o", "$ #"] [130.447042, "o", " "] [130.469463, "o", "A"] [130.486668, "o", "n"] [130.547045, "o", "d"] [130.610389, "o", " "] [130.619584, "o", "c"] [130.644904, "o", "h"] [130.684187, "o", "e"] [130.727571, "o", "c"] [130.781692, "o", "k"] [130.808069, "o", " "] [130.845396, "o", "t"] [131.045606, "o", "h"] [131.07192, "o", "a"] [131.104099, "o", "t"] [131.16738, "o", " "] [131.185618, "o", "i"] [131.305944, "o", "t"] [131.350278, "o", "'"] [131.362387, "o", "s"] [131.41572, "o", " "] [131.479032, "o", "t"] [131.525219, "o", "h"] [131.565355, "o", "e"] [131.739788, "o", " "] [131.765128, "o", "s"] [131.800433, "o", "a"] [131.844706, "o", "m"] [131.854797, "o", "e"] [131.949151, "o", ":"] [132.150155, "o", "\r\n"] [132.155207, "o", "$ d"] [132.222684, "o", "i"] [132.231755, "o", "f"] [132.241114, "o", "f"] [132.264447, "o", " "] [132.408761, "o", "-"] [132.429115, "o", "s"] [132.568351, "o", " "] [132.604616, "o", "W"] [132.613685, "o", "a"] [132.683021, "o", "l"] [132.768332, "o", "l"] [132.777539, "o", "p"] [132.786743, "o", "a"] [132.804149, "o", "p"] [132.817466, "o", "e"] [132.848787, "o", "r"] [132.872136, "o", "/"] [132.895368, "o", "d"] [132.925566, "o", "e"] [132.949702, "o", "e"] [133.049156, "o", "r"] [133.05835, "o", "."] [133.203686, "o", "j"] [133.261883, "o", "p"] [133.293183, "o", "g"] [133.31844, "o", " "] [133.327651, "o", "W"] [133.355059, "o", "a"] [133.445333, "o", "l"] [133.529542, "o", "l"] [133.635825, "o", "p"] [133.657146, "o", "a"] [133.761233, "o", "p"] [133.846807, "o", "e"] [133.887209, "o", "r"] [134.059607, "o", "."] [134.068957, "o", "o"] [134.078295, "o", "r"] [134.087635, "o", "i"] [134.097002, "o", "g"] [134.188374, "o", "/"] [134.322782, "o", "d"] [134.464061, "o", "e"] [134.534314, "o", "e"] [134.734581, "o", "r"] [134.934851, "o", "."] [135.135128, "o", "j"] [135.320397, "o", "p"] [135.339681, "o", "g"] [135.399624, "o", "\r\n"] [135.408642, "o", "Files Wallpaper/deer.jpg and Wallpaper.orig/deer.jpg are identical"] [135.409223, "o", "\r\r\n"] [135.409952, "o", "$ \r\n"] [135.413234, "o", "$ #"] [135.429446, "o", " "] [135.438723, "o", "A"] [135.491005, "o", "n"] [135.527292, "o", "d"] [135.563541, "o", ","] [135.594797, "o", " "] [135.732052, "o", "o"] [135.854273, "o", "f"] [135.887539, "o", " "] [135.972804, "o", "c"] [135.988043, "o", "o"] [136.120305, "o", "u"] [136.146563, "o", "r"] [136.190803, "o", "s"] [136.227062, "o", "e"] [136.310282, "o", ","] [136.338551, "o", " "] [136.388793, "o", "w"] [136.423051, "o", "e"] [136.432242, "o", " "] [136.473487, "o", "c"] [136.501693, "o", "a"] [136.544266, "o", "n"] [136.626521, "o", " "] [136.650782, "o", "a"] [136.689019, "o", "l"] [136.707277, "o", "s"] [136.740528, "o", "o"] [136.881757, "o", " "] [136.938024, "o", "c"] [136.968272, "o", "r"] [136.977439, "o", "e"] [137.048718, "o", "a"] [137.150961, "o", "t"] [137.191223, "o", "e"] [137.371461, "o", " "] [137.391709, "o", "r"] [137.406966, "o", "e"] [137.443207, "o", "m"] [137.467471, "o", "o"] [137.561694, "o", "t"] [137.600935, "o", "e"] [137.610129, "o", " "] [137.624379, "o", "r"] [137.63356, "o", "e"] [137.680854, "o", "p"] [137.766106, "o", "o"] [137.786347, "o", "s"] [137.813574, "o", " "] [137.832788, "o", "v"] [137.856081, "o", "i"] [137.89933, "o", "a"] [138.040577, "o", " "] [138.169867, "o", "s"] [138.290282, "o", "s"] [138.416402, "o", "h"] [138.442662, "o", " "] [138.454915, "o", "w"] [138.481224, "o", "h"] [138.552458, "o", "e"] [138.606708, "o", "n"] [138.805983, "o", " "] [138.886205, "o", "b"] [138.913396, "o", "o"] [138.948659, "o", "r"] [138.957905, "o", "g"] [138.967112, "o", " "] [139.060381, "o", "i"] [139.084639, "o", "s"] [139.162897, "o", " "] [139.172116, "o", "s"] [139.185255, "o", "e"] [139.194524, "o", "t"] [139.203728, "o", "u"] [139.218969, "o", "p"] [139.228203, "o", " "] [139.289406, "o", "t"] [139.298608, "o", "h"] [139.318866, "o", "e"] [139.383122, "o", "r"] [139.408376, "o", "e"] [139.473599, "o", "."] [139.490838, "o", " "] [139.623075, "o", "T"] [139.632287, "o", "h"] [139.82857, "o", "i"] [139.876831, "o", "s"] [139.896088, "o", " "] [139.907357, "o", "c"] [139.987619, "o", "o"] [140.128858, "o", "m"] [140.228101, "o", "m"] [140.256356, "o", "a"] [140.323596, "o", "n"] [140.332852, "o", "d"] [140.533173, "o", " "] [140.583433, "o", "c"] [140.677646, "o", "r"] [140.753916, "o", "e"] [140.834173, "o", "a"] [140.843438, "o", "t"] [140.883697, "o", "e"] [140.965972, "o", "s"] [140.995221, "o", " "] [141.022466, "o", "a"] [141.031666, "o", " "] [141.04089, "o", "n"] [141.050092, "o", "e"] [141.059315, "o", "w"] [141.073493, "o", " "] [141.122761, "o", "r"] [141.14304, "o", "e"] [141.167297, "o", "m"] [141.226536, "o", "o"] [141.235739, "o", "t"] [141.276996, "o", "e"] [141.389249, "o", " "] [141.41551, "o", "r"] [141.482767, "o", "e"] [141.534045, "o", "p"] [141.546224, "o", "o"] [141.606491, "o", " "] [141.641716, "o", "i"] [141.693986, "o", "n"] [141.894212, "o", " "] [141.913388, "o", "a"] [142.001611, "o", " "] [142.047848, "o", "s"] [142.057089, "o", "u"] [142.066324, "o", "b"] [142.075553, "o", "d"] [142.096819, "o", "i"] [142.156056, "o", "r"] [142.351298, "o", "e"] [142.37455, "o", "c"] [142.383749, "o", "t"] [142.470022, "o", "o"] [142.479243, "o", "r"] [142.488459, "o", "y"] [142.688723, "o", " "] [142.70696, "o", "c"] [142.734179, "o", "a"] [142.743376, "o", "l"] [142.802639, "o", "l"] [142.811856, "o", "e"] [142.917176, "o", "d"] [142.926375, "o", " "] [143.00463, "o", "\""] [143.02188, "o", "d"] [143.096092, "o", "e"] [143.124341, "o", "m"] [143.140599, "o", "o"] [143.165884, "o", "\""] [143.228134, "o", ":"] [143.358243, "o", "\r\n"] [143.365151, "o", "$ b"] [143.448537, "o", "o"] [143.48368, "o", "r"] [143.525012, "o", "g"] [143.725553, "o", " "] [143.753366, "o", "i"] [143.821405, "o", "n"] [143.830651, "o", "i"] [143.941335, "o", "t"] [144.008199, "o", " "] [144.021391, "o", "-"] [144.03068, "o", "-"] [144.049269, "o", "e"] [144.077379, "o", "n"] [144.158636, "o", "c"] [144.168094, "o", "r"] [144.201268, "o", "y"] [144.213931, "o", "p"] [144.332512, "o", "t"] [144.34301, "o", "i"] [144.352454, "o", "o"] [144.429126, "o", "n"] [144.441555, "o", "="] [144.498294, "o", "r"] [144.593347, "o", "e"] [144.666045, "o", "p"] [144.772517, "o", "o"] [144.781762, "o", "k"] [144.853563, "o", "e"] [144.899268, "o", "y"] [145.073332, "o", " "] [145.138026, "o", "b"] [145.1648, "o", "o"] [145.174212, "o", "r"] [145.185355, "o", "g"] [145.1947, "o", "d"] [145.231028, "o", "e"] [145.27231, "o", "m"] [145.281718, "o", "o"] [145.33316, "o", "@"] [145.345321, "o", "r"] [145.354569, "o", "e"] [145.363955, "o", "m"] [145.385359, "o", "o"] [145.417098, "o", "t"] [145.617375, "o", "e"] [145.626779, "o", "s"] [145.789323, "o", "e"] [145.974634, "o", "r"] [145.984032, "o", "v"] [146.109293, "o", "e"] [146.118745, "o", "r"] [146.220215, "o", "."] [146.229573, "o", "e"] [146.300845, "o", "x"] [146.315386, "o", "a"] [146.371957, "o", "m"] [146.423406, "o", "p"] [146.432879, "o", "l"] [146.461326, "o", "e"] [146.470777, "o", ":"] [146.649372, "o", "."] [146.781057, "o", "/"] [146.79142, "o", "d"] [146.883059, "o", "e"] [147.083607, "o", "m"] [147.093004, "o", "o"] [147.137975, "o", "\r\n"] [149.114901, "o", "Enter new passphrase: "] [150.597186, "o", "\r\r\n"] [150.598088, "o", "Enter same passphrase again: "] [151.665595, "o", "\r\r\n"] [151.666627, "o", "Do you want your passphrase to be displayed for verification? [yN]: "] [151.667848, "o", "\r\r\n"] [151.782469, "o", "\r"] [151.782892, "o", "\r\n"] [151.783244, "o", "By default repositories initialized with this version will produce security\r"] [151.783615, "o", "\r\n"] [151.783964, "o", "errors if written to with an older version (up to and including Borg 1.0.8).\r"] [151.784342, "o", "\r\n"] [151.784708, "o", "\r"] [151.785137, "o", "\r\n"] [151.785483, "o", "If you want to use these older versions, you can disable the check by running:\r"] [151.785943, "o", "\r\n"] [151.786346, "o", "borg upgrade --disable-tam ssh://borgdemo@remoteserver.example/./demo\r"] [151.787328, "o", "\r\n"] [151.787744, "o", "\r"] [151.788193, "o", "\r\n"] [151.788604, "o", "See https://borgbackup.readthedocs.io/en/stable/changes.html#pre-1-0-9-manifest-spoofing-vulnerability for details about the security implications.\r"] [151.789148, "o", "\r\n"] [151.789649, "o", "\r"] [151.79011, "o", "\r\n"] [151.790513, "o", "IMPORTANT: you will need both KEY AND PASSPHRASE to access this repo!\r"] [151.790949, "o", "\r\n"] [151.791357, "o", "If you used a repokey mode, the key is stored in the repo, but you should back it up separately.\r"] [151.791803, "o", "\r\n"] [151.792205, "o", "Use \"borg key export\" to export the key, optionally in printable format.\r"] [151.792628, "o", "\r\n"] [151.793095, "o", "Write down the passphrase. Store both at safe place(s).\r"] [151.793499, "o", "\r\n"] [151.793912, "o", "\r"] [151.794148, "o", "\r\n"] [151.895133, "o", "$ \r\n"] [151.898504, "o", "$ #"] [151.907686, "o", " "] [151.970961, "o", "E"] [152.046187, "o", "a"] [152.072458, "o", "s"] [152.089663, "o", "y"] [152.169934, "o", ","] [152.22125, "o", " "] [152.241441, "o", "i"] [152.250723, "o", "s"] [152.259967, "o", "n"] [152.356266, "o", "'"] [152.365406, "o", "t"] [152.431677, "o", " "] [152.44087, "o", "i"] [152.53011, "o", "t"] [152.602336, "o", "?"] [152.629528, "o", " "] [152.680785, "o", "T"] [152.72004, "o", "h"] [152.762269, "o", "a"] [152.859548, "o", "t"] [152.943805, "o", "'"] [153.102059, "o", "s"] [153.202278, "o", " "] [153.216515, "o", "a"] [153.238773, "o", "l"] [153.301008, "o", "l"] [153.31021, "o", " "] [153.320433, "o", "y"] [153.451705, "o", "o"] [153.460955, "o", "u"] [153.473202, "o", " "] [153.499452, "o", "n"] [153.562694, "o", "e"] [153.599963, "o", "e"] [153.661289, "o", "d"] [153.692535, "o", " "] [153.701752, "o", "t"] [153.786055, "o", "o"] [153.795264, "o", " "] [153.804528, "o", "k"] [153.832785, "o", "n"] [153.842005, "o", "o"] [153.859276, "o", "w"] [153.896547, "o", " "] [153.945818, "o", "f"] [154.028037, "o", "o"] [154.063319, "o", "r"] [154.094576, "o", " "] [154.103766, "o", "b"] [154.112993, "o", "a"] [154.122204, "o", "s"] [154.182445, "o", "i"] [154.265671, "o", "c"] [154.407925, "o", " "] [154.47121, "o", "u"] [154.48045, "o", "s"] [154.51172, "o", "a"] [154.583955, "o", "g"] [154.59321, "o", "e"] [154.643476, "o", "."] [154.705542, "o", "\r\n"] [154.710326, "o", "$ #"] [154.794577, "o", " "] [154.822836, "o", "I"] [154.900102, "o", "f"] [154.934346, "o", " "] [154.965547, "o", "y"] [155.030799, "o", "o"] [155.039994, "o", "u"] [155.078232, "o", " "] [155.116495, "o", "w"] [155.13473, "o", "a"] [155.154984, "o", "n"] [155.243264, "o", "t"] [155.339518, "o", " "] [155.512774, "o", "t"] [155.563011, "o", "o"] [155.576324, "o", " "] [155.585503, "o", "s"] [155.623775, "o", "e"] [155.819025, "o", "e"] [155.832295, "o", " "] [155.841447, "o", "m"] [155.919707, "o", "o"] [155.972905, "o", "r"] [156.038151, "o", "e"] [156.168404, "o", ","] [156.281607, "o", " "] [156.296815, "o", "h"] [156.347095, "o", "a"] [156.356281, "o", "v"] [156.423555, "o", "e"] [156.62379, "o", " "] [156.72804, "o", "a"] [156.928302, "o", " "] [156.957503, "o", "l"] [157.093718, "o", "o"] [157.102981, "o", "o"] [157.25025, "o", "k"] [157.414462, "o", " "] [157.448727, "o", "a"] [157.482013, "o", "t"] [157.494193, "o", " "] [157.586441, "o", "t"] [157.604659, "o", "h"] [157.638907, "o", "e"] [157.725193, "o", " "] [157.789369, "o", "s"] [157.814605, "o", "c"] [157.835888, "o", "r"] [157.851084, "o", "e"] [157.874326, "o", "e"] [157.907588, "o", "n"] [157.922828, "o", "c"] [158.06203, "o", "a"] [158.183285, "o", "s"] [158.255522, "o", "t"] [158.312769, "o", " "] [158.356027, "o", "s"] [158.43626, "o", "h"] [158.445422, "o", "o"] [158.496674, "o", "w"] [158.534909, "o", "i"] [158.600166, "o", "n"] [158.616418, "o", "g"] [158.681622, "o", " "] [158.827882, "o", "t"] [158.840093, "o", "h"] [158.896394, "o", "e"] [159.096639, "o", " "] [159.105862, "o", "\""] [159.238097, "o", "a"] [159.247287, "o", "d"] [159.36655, "o", "v"] [159.404799, "o", "a"] [159.436042, "o", "n"] [159.462286, "o", "c"] [159.471478, "o", "e"] [159.625717, "o", "d"] [159.79098, "o", " "] [159.823215, "o", "u"] [159.835412, "o", "s"] [159.99268, "o", "a"] [160.057935, "o", "g"] [160.072129, "o", "e"] [160.181283, "o", "\""] [160.207533, "o", "."] [160.217424, "o", "\r\n"] [160.222722, "o", "$ #"] [160.422984, "o", " "] [160.50824, "o", "I"] [160.650484, "o", "n"] [160.749699, "o", " "] [160.758908, "o", "a"] [160.78818, "o", "n"] [160.845324, "o", "y"] [160.892602, "o", " "] [160.916871, "o", "c"] [161.034093, "o", "a"] [161.116346, "o", "s"] [161.125547, "o", "e"] [161.245789, "o", ","] [161.311995, "o", " "] [161.323216, "o", "e"] [161.346473, "o", "n"] [161.362715, "o", "j"] [161.390948, "o", "o"] [161.469285, "o", "y"] [161.487562, "o", " "] [161.511806, "o", "u"] [161.53403, "o", "s"] [161.586248, "o", "i"] [161.609441, "o", "n"] [161.628709, "o", "g"] [161.64495, "o", " "] [161.694222, "o", "b"] [161.765407, "o", "o"] [161.785601, "o", "r"] [161.836877, "o", "g"] [162.041912, "o", "!"] [162.062134, "o", "\r\n"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/asciinema/basic.tcl0000644000076500000240000000621214601576577017642 0ustar00twstaff# Configuration for send -h # Tries to emulate a human typing # Tweak this if typing is too fast or too slow set send_human {.05 .1 1 .01 .2} set script { # Here you'll see some basic commands to start working with borg. # Note: This teaser screencast was made with __BORG_VERSION__ – older or newer borg versions may behave differently. # But let's start. # First of all, you can always get help: borg help # These are a lot of commands, so better we start with a few: # Let's create a repo on an external drive… borg init --encryption=repokey /media/backup/borgdemo # This uses the repokey encryption. You may look at "borg help init" or the online doc at https://borgbackup.readthedocs.io/ for other modes. # So now, let's create our first (compressed) backup. borg create --stats --progress --compression lz4 /media/backup/borgdemo::backup1 Wallpaper # That's nice, so far. # So let's add a new file… echo "new nice file" > Wallpaper/newfile.txt borg create --stats --progress --compression lz4 /media/backup/borgdemo::backup2 Wallpaper # Wow, this was a lot faster! # Notice the "Deduplicated size" for "This archive"! # Borg recognized that most files did not change and deduplicated them. # But what happens, when we move a dir and create a new backup? mv Wallpaper/bigcollection Wallpaper/bigcollection_NEW borg create --stats --progress --compression lz4 /media/backup/borgdemo::backup3 Wallpaper # Still quite fast… # But when you look at the "deduplicated file size" again, you see that borg also recognized that only the dir and not the files changed in this backup. # Now lets look into a repo. borg list /media/backup/borgdemo # You'll see a list of all backups. # You can also use the same command to look into an archive. But we better filter the output here: borg list /media/backup/borgdemo::backup3 | grep 'deer.jpg' # Oh, we found our picture. Now extract it… mv Wallpaper Wallpaper.orig borg extract /media/backup/borgdemo::backup3 Wallpaper/deer.jpg # And check that it's the same: diff -s Wallpaper/deer.jpg Wallpaper.orig/deer.jpg # And, of course, we can also create remote repos via ssh when borg is setup there. This command creates a new remote repo in a subdirectory called "demo": borg init --encryption=repokey borgdemo@remoteserver.example:./demo # Easy, isn't it? That's all you need to know for basic usage. # If you want to see more, have a look at the screencast showing the "advanced usage". # In any case, enjoy using borg! } set script [string trim $script] set script [string map [list __BORG_VERSION__ [exec borg -V]] $script] set script [split $script \n] foreach line $script { send_user "$ " send_user -h $line\n spawn -noecho /bin/sh -c $line expect { "Enter new passphrase: " { send -h "correct horse battery staple\n" exp_continue } "Enter same passphrase again: " { send -h "correct horse battery staple\n" exp_continue } "Enter passphrase for key /media/backup/borgdemo: " { send -h "correct horse battery staple\n" exp_continue } -ex {Do you want your passphrase to be displayed for verification? [yN]: } { send \n exp_continue } eof } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/asciinema/install.json0000644000076500000240000005725614601576577020434 0ustar00twstaff{"version": 2, "width": 80, "height": 24, "timestamp": 1657142795, "env": {"SHELL": "/bin/bash", "TERM": "vt100"}} [0.014053, "o", "$ "] [0.014802, "o", "#"] [0.145809, "o", " "] [0.154894, "o", "T"] [0.185287, "o", "h"] [0.19475, "o", "i"] [0.211085, "o", "s"] [0.321388, "o", " "] [0.389817, "o", "a"] [0.433819, "o", "s"] [0.465846, "o", "c"] [0.477783, "o", "i"] [0.489867, "o", "i"] [0.502142, "o", "n"] [0.562389, "o", "e"] [0.584049, "o", "m"] [0.621852, "o", "a"] [0.657828, "o", " "] [0.667252, "o", "w"] [0.701711, "o", "i"] [0.721812, "o", "l"] [0.761768, "o", "l"] [0.842137, "o", " "] [0.977783, "o", "s"] [1.072032, "o", "h"] [1.117847, "o", "o"] [1.28305, "o", "w"] [1.347577, "o", " "] [1.420286, "o", "y"] [1.441849, "o", "o"] [1.465377, "o", "u"] [1.52183, "o", " "] [1.531208, "o", "t"] [1.548712, "o", "h"] [1.566005, "o", "e"] [1.609195, "o", " "] [1.621698, "o", "i"] [1.663977, "o", "n"] [1.741742, "o", "s"] [1.809851, "o", "t"] [1.929424, "o", "a"] [1.943799, "o", "l"] [2.039221, "o", "l"] [2.129831, "o", "a"] [2.144218, "o", "t"] [2.165843, "o", "i"] [2.17517, "o", "o"] [2.244748, "o", "n"] [2.294097, "o", " "] [2.304666, "o", "o"] [2.319139, "o", "f"] [2.436131, "o", " "] [2.469029, "o", "b"] [2.511209, "o", "o"] [2.558617, "o", "r"] [2.567952, "o", "g"] [2.768307, "o", " "] [2.813853, "o", "a"] [2.83777, "o", "s"] [2.901612, "o", " "] [2.957777, "o", "a"] [3.080693, "o", " "] [3.197825, "o", "s"] [3.240999, "o", "t"] [3.31471, "o", "a"] [3.486239, "o", "n"] [3.545799, "o", "d"] [3.746438, "o", "a"] [3.765807, "o", "l"] [3.799391, "o", "o"] [3.86407, "o", "n"] [3.877844, "o", "e"] [4.010577, "o", " "] [4.033887, "o", "b"] [4.119113, "o", "i"] [4.312337, "o", "n"] [4.341142, "o", "a"] [4.541664, "o", "r"] [4.576179, "o", "y"] [4.753067, "o", "."] [4.898458, "o", " "] [4.969834, "o", "U"] [4.986322, "o", "s"] [4.996775, "o", "u"] [5.016159, "o", "a"] [5.071603, "o", "l"] [5.088116, "o", "l"] [5.102688, "o", "y"] [5.113827, "o", " "] [5.165786, "o", "y"] [5.201829, "o", "o"] [5.233547, "o", "u"] [5.243953, "o", " "] [5.269861, "o", "o"] [5.279354, "o", "n"] [5.379507, "o", "l"] [5.388926, "o", "y"] [5.461163, "o", " "] [5.510869, "o", "n"] [5.539994, "o", "e"] [5.740252, "o", "e"] [5.749606, "o", "d"] [5.858895, "o", " "] [5.893866, "o", "t"] [5.907335, "o", "h"] [5.940743, "o", "i"] [5.955261, "o", "s"] [6.013788, "o", " "] [6.027155, "o", "i"] [6.097878, "o", "f"] [6.12984, "o", " "] [6.29044, "o", "y"] [6.32487, "o", "o"] [6.344524, "o", "u"] [6.361864, "o", " "] [6.406259, "o", "w"] [6.480319, "o", "a"] [6.491805, "o", "n"] [6.539051, "o", "t"] [6.621658, "o", " "] [6.666422, "o", "t"] [6.677777, "o", "o"] [6.687147, "o", " "] [6.745773, "o", "h"] [6.761323, "o", "a"] [6.857807, "o", "v"] [6.877241, "o", "e"] [6.928841, "o", " "] [6.953838, "o", "a"] [7.018219, "o", "n"] [7.204957, "o", " "] [7.214395, "o", "u"] [7.308934, "o", "p"] [7.341736, "o", "-"] [7.391894, "o", "t"] [7.488804, "o", "o"] [7.565371, "o", "-"] [7.673841, "o", "d"] [7.691235, "o", "a"] [7.730541, "o", "t"] [7.779885, "o", "e"] [7.82105, "o", " "] [7.857857, "o", "v"] [7.973835, "o", "e"] [8.005875, "o", "r"] [8.03317, "o", "s"] [8.04773, "o", "i"] [8.05921, "o", "o"] [8.108989, "o", "n"] [8.309151, "o", " "] [8.382718, "o", "o"] [8.395123, "o", "f"] [8.53363, "o", " "] [8.575906, "o", "b"] [8.633447, "o", "o"] [8.710623, "o", "r"] [8.738389, "o", "g"] [8.887713, "o", " "] [9.045955, "o", "o"] [9.085246, "o", "r"] [9.157826, "o", " "] [9.241871, "o", "n"] [9.2714, "o", "o"] [9.285859, "o", " "] [9.381743, "o", "p"] [9.391193, "o", "a"] [9.572886, "o", "c"] [9.582362, "o", "k"] [9.648051, "o", "a"] [9.657843, "o", "g"] [9.718129, "o", "e"] [9.732774, "o", " "] [9.757155, "o", "i"] [9.946487, "o", "s"] [10.107944, "o", " "] [10.195191, "o", "a"] [10.22102, "o", "v"] [10.230446, "o", "a"] [10.311846, "o", "i"] [10.393675, "o", "l"] [10.453805, "o", "a"] [10.475504, "o", "b"] [10.55474, "o", "l"] [10.585603, "o", "e"] [10.785858, "o", " "] [10.81877, "o", "f"] [10.828264, "o", "o"] [10.83782, "o", "r"] [10.869826, "o", " "] [11.040749, "o", "y"] [11.069832, "o", "o"] [11.106365, "o", "u"] [11.144892, "o", "r"] [11.345408, "o", " "] [11.396637, "o", "d"] [11.421235, "o", "i"] [11.577404, "o", "s"] [11.602081, "o", "t"] [11.621845, "o", "r"] [11.652436, "o", "o"] [11.709814, "o", "/"] [11.719056, "o", "O"] [11.763376, "o", "S"] [11.849898, "o", "."] [11.923473, "o", "\r\n"] [11.930008, "o", "$ \r\n"] [11.934924, "o", "$ #"] [12.07232, "o", " "] [12.084395, "o", "F"] [12.280637, "o", "i"] [12.347896, "o", "r"] [12.477136, "o", "s"] [12.486354, "o", "t"] [12.562609, "o", ","] [12.571853, "o", " "] [12.581063, "o", "w"] [12.656333, "o", "e"] [12.672544, "o", " "] [12.78082, "o", "n"] [12.789995, "o", "e"] [12.799208, "o", "e"] [12.81747, "o", "d"] [12.897764, "o", " "] [12.951046, "o", "t"] [12.961262, "o", "o"] [13.161539, "o", " "] [13.246799, "o", "d"] [13.367039, "o", "o"] [13.388292, "o", "w"] [13.397491, "o", "n"] [13.406729, "o", "l"] [13.44496, "o", "o"] [13.57222, "o", "a"] [13.60648, "o", "d"] [13.618687, "o", " "] [13.64895, "o", "t"] [13.758167, "o", "h"] [13.7754, "o", "e"] [13.824655, "o", " "] [13.929823, "o", "v"] [13.939035, "o", "e"] [14.053307, "o", "r"] [14.134551, "o", "s"] [14.143768, "o", "i"] [14.155002, "o", "o"] [14.304273, "o", "n"] [14.504522, "o", ","] [14.702776, "o", " "] [14.805964, "o", "w"] [14.84922, "o", "e"] [15.001462, "o", "'"] [15.073745, "o", "d"] [15.18095, "o", " "] [15.19016, "o", "l"] [15.200396, "o", "i"] [15.292691, "o", "k"] [15.301811, "o", "e"] [15.311038, "o", " "] [15.348299, "o", "t"] [15.358512, "o", "o"] [15.558793, "o", " "] [15.612999, "o", "i"] [15.677237, "o", "n"] [15.724497, "o", "s"] [15.733689, "o", "t"] [15.826957, "o", "a"] [15.836163, "o", "l"] [15.920434, "o", "l"] [16.120714, "o", "…"] [16.133834, "o", "\r\n"] [16.140577, "o", "$ w"] [16.205727, "o", "g"] [16.214938, "o", "e"] [16.230177, "o", "t"] [16.331425, "o", " "] [16.34063, "o", "-"] [16.540913, "o", "q"] [16.581185, "o", " "] [16.590402, "o", "-"] [16.689716, "o", "-"] [16.80895, "o", "s"] [16.858176, "o", "h"] [16.883419, "o", "o"] [16.924718, "o", "w"] [17.055923, "o", "-"] [17.100182, "o", "p"] [17.109385, "o", "r"] [17.189703, "o", "o"] [17.212934, "o", "g"] [17.24417, "o", "r"] [17.439442, "o", "e"] [17.456641, "o", "s"] [17.572948, "o", "s"] [17.636192, "o", " "] [17.692451, "o", "h"] [17.780737, "o", "t"] [17.789845, "o", "t"] [17.825103, "o", "p"] [17.876375, "o", "s"] [18.076633, "o", ":"] [18.142884, "o", "/"] [18.152087, "o", "/"] [18.186329, "o", "g"] [18.235563, "o", "i"] [18.244785, "o", "t"] [18.253969, "o", "h"] [18.31823, "o", "u"] [18.327417, "o", "b"] [18.389743, "o", "."] [18.431976, "o", "c"] [18.50525, "o", "o"] [18.514446, "o", "m"] [18.536746, "o", "/"] [18.54586, "o", "b"] [18.578085, "o", "o"] [18.638338, "o", "r"] [18.708609, "o", "g"] [18.735857, "o", "b"] [18.770084, "o", "a"] [18.779288, "o", "c"] [18.834558, "o", "k"] [18.84879, "o", "u"] [18.859012, "o", "p"] [18.944287, "o", "/"] [18.978548, "o", "b"] [19.079789, "o", "o"] [19.105012, "o", "r"] [19.148268, "o", "g"] [19.164519, "o", "/"] [19.249746, "o", "r"] [19.271014, "o", "e"] [19.37926, "o", "l"] [19.438524, "o", "e"] [19.530793, "o", "a"] [19.546996, "o", "s"] [19.64026, "o", "e"] [19.70851, "o", "s"] [19.860819, "o", "/"] [19.931099, "o", "d"] [20.12236, "o", "o"] [20.131597, "o", "w"] [20.168902, "o", "n"] [20.178072, "o", "l"] [20.19734, "o", "o"] [20.255574, "o", "a"] [20.278822, "o", "d"] [20.441069, "o", "/"] [20.453262, "o", "1"] [20.613542, "o", "."] [20.631798, "o", "2"] [20.643995, "o", "."] [20.657161, "o", "1"] [20.696419, "o", "/"] [20.714662, "o", "b"] [20.797846, "o", "o"] [20.84203, "o", "r"] [20.988304, "o", "g"] [21.012612, "o", "-"] [21.021913, "o", "l"] [21.031187, "o", "i"] [21.111465, "o", "n"] [21.147724, "o", "u"] [21.217034, "o", "x"] [21.25131, "o", "6"] [21.260514, "o", "4"] [21.425711, "o", "\r\n"] [22.122156, "o", "\rborg-linux64 0%[ ] 0 --.-KB/s "] [22.325471, "o", "\rborg-linux64 6%[> ] 1.33M 6.59MB/s "] [22.526514, "o", "\rborg-linux64 13%[=> ] 3.04M 7.52MB/s "] [22.728037, "o", "\rborg-linux64 22%[===> ] 4.88M 8.06MB/s "] [22.928693, "o", "\rborg-linux64 30%[=====> ] 6.69M 8.30MB/s "] [23.130437, "o", "\rborg-linux64 38%[======> ] 8.35M 8.28MB/s "] [23.331937, "o", "\rborg-linux64 45%[========> ] 10.01M 8.27MB/s "] [23.589691, "o", "\rborg-linux64 53%[=========> ] 11.65M 7.95MB/s "] [23.789016, "o", "\rborg-linux64 64%[===========> ] 14.08M 8.45MB/s "] [23.989182, "o", "\rborg-linux64 72%[=============> ] 15.90M 8.52MB/s "] [24.189963, "o", "\rborg-linux64 81%[===============> ] 17.74M 8.58MB/s "] [24.391128, "o", "\rborg-linux64 90%[=================> ] 19.71M 8.69MB/s "] [24.591843, "o", "\rborg-linux64 99%[==================> ] 21.73M 8.80MB/s "] [24.605717, "o", "\rborg-linux64 100%[===================>] 21.87M 8.80MB/s in 2.5s \r\r\n"] [24.607438, "o", "$ #"] [24.711803, "o", " "] [24.756021, "o", "a"] [24.857266, "o", "n"] [24.95354, "o", "d"] [25.079747, "o", " "] [25.105032, "o", "d"] [25.173253, "o", "o"] [25.342493, "o", " "] [25.437792, "o", "n"] [25.545976, "o", "o"] [25.57012, "o", "t"] [25.750321, "o", " "] [25.780607, "o", "f"] [25.839866, "o", "o"] [25.989118, "o", "r"] [26.047359, "o", "g"] [26.125925, "o", "e"] [26.13532, "o", "t"] [26.205567, "o", " "] [26.229863, "o", "t"] [26.297164, "o", "h"] [26.330399, "o", "e"] [26.369653, "o", " "] [26.41591, "o", "G"] [26.42509, "o", "P"] [26.472358, "o", "G"] [26.485557, "o", " "] [26.525908, "o", "s"] [26.594062, "o", "i"] [26.730283, "o", "g"] [26.785586, "o", "n"] [26.809996, "o", "a"] [26.827218, "o", "t"] [26.865434, "o", "u"] [26.874623, "o", "r"] [26.949922, "o", "e"] [27.071147, "o", "…"] [27.108423, "o", "!"] [27.13146, "o", "\r\n"] [27.138689, "o", "$ w"] [27.164997, "o", "g"] [27.235295, "o", "e"] [27.24448, "o", "t"] [27.44489, "o", " "] [27.456982, "o", "-"] [27.466218, "o", "q"] [27.548499, "o", " "] [27.557754, "o", "-"] [27.676013, "o", "-"] [27.765225, "o", "s"] [27.774412, "o", "h"] [27.82672, "o", "o"] [27.835881, "o", "w"] [27.903196, "o", "-"] [27.912432, "o", "p"] [27.921642, "o", "r"] [27.930901, "o", "o"] [27.940177, "o", "g"] [28.000419, "o", "r"] [28.049667, "o", "e"] [28.058871, "o", "s"] [28.068098, "o", "s"] [28.077311, "o", " "] [28.086537, "o", "h"] [28.116886, "o", "t"] [28.125965, "o", "t"] [28.262193, "o", "p"] [28.271401, "o", "s"] [28.354675, "o", ":"] [28.371903, "o", "/"] [28.4042, "o", "/"] [28.473444, "o", "g"] [28.482611, "o", "i"] [28.533931, "o", "t"] [28.581202, "o", "h"] [28.596485, "o", "u"] [28.633743, "o", "b"] [28.833992, "o", "."] [28.84319, "o", "c"] [28.854415, "o", "o"] [28.893704, "o", "m"] [28.917922, "o", "/"] [28.966099, "o", "b"] [29.016343, "o", "o"] [29.048615, "o", "r"] [29.099864, "o", "g"] [29.176115, "o", "b"] [29.198358, "o", "a"] [29.239604, "o", "c"] [29.253897, "o", "k"] [29.454082, "o", "u"] [29.507152, "o", "p"] [29.525347, "o", "/"] [29.599604, "o", "b"] [29.682859, "o", "o"] [29.731069, "o", "r"] [29.774349, "o", "g"] [29.846616, "o", "/"] [29.885768, "o", "r"] [30.007016, "o", "e"] [30.091268, "o", "l"] [30.170547, "o", "e"] [30.224826, "o", "a"] [30.327058, "o", "s"] [30.338255, "o", "e"] [30.347493, "o", "s"] [30.362751, "o", "/"] [30.428961, "o", "d"] [30.629286, "o", "o"] [30.640516, "o", "w"] [30.700784, "o", "n"] [30.740021, "o", "l"] [30.75024, "o", "o"] [30.769489, "o", "a"] [30.920759, "o", "d"] [30.978979, "o", "/"] [31.143241, "o", "1"] [31.213513, "o", "."] [31.294751, "o", "2"] [31.369956, "o", "."] [31.441199, "o", "1"] [31.522445, "o", "/"] [31.681726, "o", "b"] [31.7419, "o", "o"] [31.762117, "o", "r"] [31.800363, "o", "g"] [31.809617, "o", "-"] [31.93189, "o", "l"] [31.941089, "o", "i"] [31.991348, "o", "n"] [32.078615, "o", "u"] [32.181768, "o", "x"] [32.192943, "o", "6"] [32.207217, "o", "4"] [32.279486, "o", "."] [32.305741, "o", "a"] [32.324004, "o", "s"] [32.352294, "o", "c"] [32.553465, "o", "\r\n"] [33.113725, "o", "\rborg-linux64.asc 0%[ ] 0 --.-KB/s "] [33.114471, "o", "\rborg-linux64.asc 100%[===================>] 862 --.-KB/s in 0s "] [33.115443, "o", "\r\r\n"] [33.116813, "o", "$ "] [33.116988, "o", "\r\n"] [33.121869, "o", "$ #"] [33.16912, "o", " "] [33.216381, "o", "I"] [33.238635, "o", "n"] [33.438905, "o", " "] [33.45917, "o", "t"] [33.540412, "o", "h"] [33.594643, "o", "i"] [33.684918, "o", "s"] [33.777142, "o", " "] [33.9464, "o", "c"] [33.968656, "o", "a"] [33.977801, "o", "s"] [34.107087, "o", "e"] [34.116346, "o", ","] [34.131568, "o", " "] [34.144888, "o", "w"] [34.15405, "o", "e"] [34.174323, "o", " "] [34.183524, "o", "h"] [34.223794, "o", "a"] [34.298019, "o", "v"] [34.359288, "o", "e"] [34.501551, "o", " "] [34.510743, "o", "a"] [34.523012, "o", "l"] [34.663265, "o", "r"] [34.68352, "o", "e"] [34.710755, "o", "a"] [34.759018, "o", "d"] [34.784347, "o", "y"] [34.957675, "o", " "] [34.966934, "o", "i"] [35.042172, "o", "m"] [35.052399, "o", "p"] [35.113748, "o", "o"] [35.123019, "o", "r"] [35.323308, "o", "t"] [35.346586, "o", "e"] [35.408868, "o", "d"] [35.453094, "o", " "] [35.46233, "o", "t"] [35.471566, "o", "h"] [35.488874, "o", "e"] [35.498087, "o", " "] [35.507337, "o", "p"] [35.548601, "o", "u"] [35.55776, "o", "b"] [35.566991, "o", "l"] [35.635262, "o", "i"] [35.644481, "o", "c"] [35.745742, "o", " "] [35.763015, "o", "k"] [35.845261, "o", "e"] [35.854518, "o", "y"] [35.893716, "o", " "] [35.973922, "o", "o"] [36.078141, "o", "f"] [36.13339, "o", " "] [36.145654, "o", "a"] [36.213852, "o", " "] [36.259106, "o", "b"] [36.268328, "o", "o"] [36.303574, "o", "r"] [36.326849, "o", "g"] [36.527126, "o", " "] [36.72738, "o", "d"] [36.741621, "o", "e"] [36.81091, "o", "v"] [36.833128, "o", "e"] [37.011383, "o", "l"] [37.020627, "o", "o"] [37.051895, "o", "p"] [37.139138, "o", "e"] [37.165375, "o", "r"] [37.196621, "o", "."] [37.387884, "o", " "] [37.455143, "o", "S"] [37.46434, "o", "o"] [37.638618, "o", " "] [37.671888, "o", "w"] [37.682073, "o", "e"] [37.744311, "o", " "] [37.783588, "o", "o"] [37.98384, "o", "n"] [38.009046, "o", "l"] [38.060303, "o", "y"] [38.216548, "o", " "] [38.231823, "o", "n"] [38.323064, "o", "e"] [38.398326, "o", "e"] [38.436586, "o", "d"] [38.493752, "o", " "] [38.581003, "o", "t"] [38.664281, "o", "o"] [38.751561, "o", " "] [38.79584, "o", "v"] [38.87505, "o", "e"] [38.92131, "o", "r"] [38.930503, "o", "i"] [39.0568, "o", "f"] [39.077954, "o", "y"] [39.103209, "o", " "] [39.112401, "o", "i"] [39.121714, "o", "t"] [39.173003, "o", ":"] [39.184854, "o", "\r\n"] [39.190079, "o", "$ g"] [39.199322, "o", "p"] [39.215605, "o", "g"] [39.415869, "o", " "] [39.441075, "o", "-"] [39.472334, "o", "-"] [39.483535, "o", "v"] [39.546835, "o", "e"] [39.560054, "o", "r"] [39.583316, "o", "i"] [39.605629, "o", "f"] [39.683888, "o", "y"] [39.738098, "o", " "] [39.749313, "o", "b"] [39.791608, "o", "o"] [39.951853, "o", "r"] [40.003141, "o", "g"] [40.021369, "o", "-"] [40.06064, "o", "l"] [40.260916, "o", "i"] [40.298124, "o", "n"] [40.318369, "o", "u"] [40.363631, "o", "x"] [40.41488, "o", "6"] [40.424082, "o", "4"] [40.624368, "o", "."] [40.674613, "o", "a"] [40.684742, "o", "s"] [40.693927, "o", "c"] [40.860898, "o", "\r\n"] [40.870862, "o", "gpg: assuming signed data in 'borg-linux64'\r\r\n"] [40.991536, "o", "gpg: Signature made Sun Jun 5 21:37:49 2022 UTC\r\r\ngpg: using RSA key 2F81AFFBAB04E11FE8EE65D4243ACFA951F78E01\r\r\ngpg: issuer \"tw@waldmann-edv.de\"\r\r\n"] [40.993561, "o", "gpg: Good signature from \"Thomas Waldmann \" [unknown]\r\r\ngpg: aka \"Thomas Waldmann \" [unknown]\r\r\ngpg: aka \"Thomas Waldmann \" [unknown]\r\r\n"] [40.993881, "o", "gpg: aka \"Thomas Waldmann \" [unknown]\r\r\n"] [40.995059, "o", "gpg: WARNING: This key is not certified with a trusted signature!\r\r\ngpg: There is no indication that the signature belongs to the owner.\r\r\nPrimary key fingerprint: 6D5B EF9A DD20 7580 5747 B70F 9F88 FB52 FAF7 B393\r\r\n Subkey fingerprint: 2F81 AFFB AB04 E11F E8EE 65D4 243A CFA9 51F7 8E01\r\r\n"] [40.995876, "o", "$ #"] [41.005075, "o", " "] [41.069956, "o", "O"] [41.098443, "o", "k"] [41.17776, "o", "a"] [41.292576, "o", "y"] [41.493851, "o", ","] [41.569465, "o", " "] [41.617829, "o", "t"] [41.738039, "o", "h"] [41.747542, "o", "e"] [41.94792, "o", " "] [41.979034, "o", "b"] [42.021223, "o", "i"] [42.1784, "o", "n"] [42.262162, "o", "a"] [42.281862, "o", "r"] [42.327452, "o", "y"] [42.465774, "o", " "] [42.559207, "o", "i"] [42.581862, "o", "s"] [42.614201, "o", " "] [42.624612, "o", "v"] [42.782706, "o", "a"] [42.841839, "o", "l"] [42.853148, "o", "i"] [42.909309, "o", "d"] [42.949832, "o", "!"] [42.960042, "o", "\r\n"] [42.966483, "o", "$ \r\n"] [42.970541, "o", "$ #"] [43.010934, "o", " "] [43.061848, "o", "N"] [43.113854, "o", "o"] [43.314271, "o", "w"] [43.367734, "o", " "] [43.38522, "o", "i"] [43.440638, "o", "n"] [43.453794, "o", "s"] [43.58115, "o", "t"] [43.602848, "o", "a"] [43.612287, "o", "l"] [43.66838, "o", "l"] [43.757865, "o", " "] [43.816951, "o", "i"] [43.867188, "o", "t"] [43.918628, "o", ":"] [44.119535, "o", "\r\n"] [44.126514, "o", "$ s"] [44.137836, "o", "u"] [44.230129, "o", "d"] [44.252649, "o", "o"] [44.277864, "o", " "] [44.357858, "o", "c"] [44.37639, "o", "p"] [44.483759, "o", " "] [44.558149, "o", "b"] [44.629854, "o", "o"] [44.701844, "o", "r"] [44.763291, "o", "g"] [44.845639, "o", "-"] [44.900115, "o", "l"] [44.909474, "o", "i"] [45.058144, "o", "n"] [45.067669, "o", "u"] [45.077063, "o", "x"] [45.09981, "o", "6"] [45.122149, "o", "4"] [45.183708, "o", " "] [45.205868, "o", "/"] [45.222081, "o", "u"] [45.265325, "o", "s"] [45.297806, "o", "r"] [45.429847, "o", "/"] [45.439219, "o", "l"] [45.448719, "o", "o"] [45.473872, "o", "c"] [45.494395, "o", "a"] [45.609828, "o", "l"] [45.654127, "o", "/"] [45.688425, "o", "b"] [45.72362, "o", "i"] [45.787042, "o", "n"] [45.849829, "o", "/"] [45.859297, "o", "b"] [45.939606, "o", "o"] [45.949095, "o", "r"] [46.042557, "o", "g"] [46.12613, "o", "\r\n"] [46.172357, "o", "$ s"] [46.212876, "o", "u"] [46.325809, "o", "d"] [46.344195, "o", "o"] [46.431558, "o", " "] [46.522936, "o", "c"] [46.546729, "o", "h"] [46.570006, "o", "o"] [46.7523, "o", "w"] [46.819922, "o", "n"] [46.911633, "o", " "] [46.929206, "o", "r"] [47.008063, "o", "o"] [47.157866, "o", "o"] [47.225417, "o", "t"] [47.301865, "o", ":"] [47.31111, "o", "r"] [47.362558, "o", "o"] [47.41601, "o", "o"] [47.446448, "o", "t"] [47.482538, "o", " "] [47.560369, "o", "/"] [47.623683, "o", "u"] [47.708182, "o", "s"] [47.743531, "o", "r"] [47.764228, "o", "/"] [47.872949, "o", "l"] [47.882292, "o", "o"] [47.891809, "o", "c"] [47.926001, "o", "a"] [47.952744, "o", "l"] [48.082119, "o", "/"] [48.168883, "o", "b"] [48.181753, "o", "i"] [48.191226, "o", "n"] [48.242791, "o", "/"] [48.298909, "o", "b"] [48.325598, "o", "o"] [48.364784, "o", "r"] [48.41717, "o", "g"] [48.618559, "o", "\r\n"] [48.636317, "o", "$ #"] [48.699577, "o", " "] [48.761734, "o", "a"] [48.860994, "o", "n"] [48.891258, "o", "d"] [48.90051, "o", " "] [49.089716, "o", "m"] [49.191956, "o", "a"] [49.220201, "o", "k"] [49.231415, "o", "e"] [49.250657, "o", " "] [49.262875, "o", "i"] [49.389135, "o", "t"] [49.58942, "o", " "] [49.623673, "o", "e"] [49.726909, "o", "x"] [49.778141, "o", "e"] [49.820374, "o", "c"] [49.883634, "o", "u"] [49.89287, "o", "t"] [49.96108, "o", "a"] [49.99032, "o", "b"] [50.164588, "o", "l"] [50.248871, "o", "e"] [50.315101, "o", "…"] [50.324917, "o", "\r\n"] [50.33024, "o", "$ s"] [50.444521, "o", "u"] [50.474784, "o", "d"] [50.600042, "o", "o"] [50.771296, "o", " "] [50.794535, "o", "c"] [50.845756, "o", "h"] [50.864021, "o", "m"] [50.873172, "o", "o"] [50.974454, "o", "d"] [50.983643, "o", " "] [51.052941, "o", "7"] [51.120224, "o", "5"] [51.15648, "o", "5"] [51.357071, "o", " "] [51.41431, "o", "/"] [51.481616, "o", "u"] [51.532876, "o", "s"] [51.562085, "o", "r"] [51.588324, "o", "/"] [51.604621, "o", "l"] [51.613907, "o", "o"] [51.656228, "o", "c"] [51.665393, "o", "a"] [51.67459, "o", "l"] [51.683892, "o", "/"] [51.696188, "o", "b"] [51.764495, "o", "i"] [51.820817, "o", "n"] [51.945194, "o", "/"] [52.055405, "o", "b"] [52.195699, "o", "o"] [52.218896, "o", "r"] [52.228068, "o", "g"] [52.424062, "o", "\r\n"] [52.440156, "o", "$ \r\n"] [52.442959, "o", "$ #"] [52.45844, "o", " "] [52.473717, "o", "N"] [52.508037, "o", "o"] [52.51717, "o", "w"] [52.56058, "o", " "] [52.574808, "o", "c"] [52.625984, "o", "h"] [52.728251, "o", "e"] [52.906625, "o", "c"] [52.926856, "o", "k"] [52.974077, "o", " "] [52.9983, "o", "i"] [53.037619, "o", "t"] [53.085974, "o", ":"] [53.141237, "o", " "] [53.167627, "o", "("] [53.231899, "o", "p"] [53.261185, "o", "o"] [53.287514, "o", "s"] [53.371779, "o", "s"] [53.427951, "o", "i"] [53.437239, "o", "b"] [53.470586, "o", "l"] [53.494825, "o", "y"] [53.665141, "o", " "] [53.693108, "o", "n"] [53.765334, "o", "e"] [53.902605, "o", "e"] [53.911802, "o", "d"] [53.977067, "o", "s"] [53.998324, "o", " "] [54.017644, "o", "a"] [54.026845, "o", " "] [54.058086, "o", "t"] [54.071336, "o", "e"] [54.080542, "o", "r"] [54.112803, "o", "m"] [54.218011, "o", "i"] [54.291263, "o", "n"] [54.321518, "o", "a"] [54.513747, "o", "l"] [54.529947, "o", " "] [54.621171, "o", "r"] [54.65843, "o", "e"] [54.675688, "o", "s"] [54.69193, "o", "t"] [54.752183, "o", "a"] [54.761376, "o", "r"] [54.826641, "o", "t"] [54.837793, "o", ")"] [54.929883, "o", "\r\n"] [54.936116, "o", "$ b"] [54.945364, "o", "o"] [54.954567, "o", "r"] [54.995831, "o", "g"] [55.094044, "o", " "] [55.1113, "o", "-"] [55.120527, "o", "V"] [55.161722, "o", "\r\n"] [55.941702, "o", "borg 1.2.1"] [55.942305, "o", "\r\r\n"] [55.993719, "o", "$ \r\n"] [55.997213, "o", "$ #"] [56.034666, "o", " "] [56.063952, "o", "T"] [56.110197, "o", "h"] [56.182455, "o", "a"] [56.23975, "o", "t"] [56.309047, "o", "'"] [56.32334, "o", "s"] [56.405649, "o", " "] [56.420997, "o", "i"] [56.452158, "o", "t"] [56.489524, "o", "!"] [56.499708, "o", " "] [56.517909, "o", "C"] [56.564198, "o", "h"] [56.597539, "o", "e"] [56.619709, "o", "c"] [56.63787, "o", "k"] [56.67012, "o", " "] [56.679362, "o", "o"] [56.689617, "o", "u"] [56.79894, "o", "t"] [56.808157, "o", " "] [56.820469, "o", "t"] [57.020769, "o", "h"] [57.067066, "o", "e"] [57.164398, "o", " "] [57.180648, "o", "o"] [57.211946, "o", "t"] [57.370177, "o", "h"] [57.396486, "o", "e"] [57.405783, "o", "r"] [57.435073, "o", " "] [57.484351, "o", "s"] [57.513656, "o", "c"] [57.591941, "o", "r"] [57.610164, "o", "e"] [57.754423, "o", "e"] [57.76372, "o", "n"] [57.815018, "o", "c"] [58.015317, "o", "a"] [58.050594, "o", "s"] [58.2029, "o", "t"] [58.212147, "o", "s"] [58.300444, "o", " "] [58.340736, "o", "t"] [58.394987, "o", "o"] [58.595282, "o", " "] [58.693538, "o", "s"] [58.755821, "o", "e"] [58.838122, "o", "e"] [58.847379, "o", " "] [58.892674, "o", "h"] [58.986978, "o", "o"] [58.996253, "o", "w"] [59.084591, "o", " "] [59.096866, "o", "t"] [59.124943, "o", "o"] [59.226381, "o", " "] [59.256843, "o", "a"] [59.26637, "o", "c"] [59.350895, "o", "t"] [59.388513, "o", "u"] [59.399086, "o", "a"] [59.42835, "o", "l"] [59.495588, "o", "l"] [59.695833, "o", "y"] [59.837075, "o", " "] [59.867315, "o", "u"] [59.945609, "o", "s"] [59.954833, "o", "e"] [60.01306, "o", " "] [60.030314, "o", "b"] [60.039559, "o", "o"] [60.086806, "o", "r"] [60.14006, "o", "g"] [60.169338, "o", "b"] [60.178534, "o", "a"] [60.201718, "o", "c"] [60.210988, "o", "k"] [60.28122, "o", "u"] [60.43448, "o", "p"] [60.447748, "o", "."] [60.469955, "o", "\r\n"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/asciinema/install.tcl0000644000076500000240000000244014601576577020226 0ustar00twstaff# Configuration for send -h # Tries to emulate a human typing # Tweak this if typing is too fast or too slow set send_human {.05 .1 1 .01 .2} set script [string trim { # This asciinema will show you the installation of borg as a standalone binary. Usually you only need this if you want to have an up-to-date version of borg or no package is available for your distro/OS. # First, we need to download the version, we'd like to install… wget -q --show-progress https://github.com/borgbackup/borg/releases/download/1.2.1/borg-linux64 # and do not forget the GPG signature…! wget -q --show-progress https://github.com/borgbackup/borg/releases/download/1.2.1/borg-linux64.asc # In this case, we have already imported the public key of a borg developer. So we only need to verify it: gpg --verify borg-linux64.asc # Okay, the binary is valid! # Now install it: sudo cp borg-linux64 /usr/local/bin/borg sudo chown root:root /usr/local/bin/borg # and make it executable… sudo chmod 755 /usr/local/bin/borg # Now check it: (possibly needs a terminal restart) borg -V # That's it! Check out the other screencasts to see how to actually use borgbackup. }] # wget may be slow set timeout -1 foreach line [split $script \n] { send_user "$ " send_user -h $line\n spawn -noecho /bin/sh -c $line expect eof } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/asciinema/sample-wallpapers.txt0000644000076500000240000000567714601576577022265 0ustar00twstaffhttps://upload.wikimedia.org/wikipedia/commons/2/22/Pseudo_kleinian_001_OpenCL_45154214_8K.jpg https://upload.wikimedia.org/wikipedia/commons/d/da/Mengerschwamm_Iteration_5_x_Mandelbulb_OpenCL_528814521414_8K.jpg https://upload.wikimedia.org/wikipedia/commons/e/eb/Blixos_logon_screen.jpg https://upload.wikimedia.org/wikipedia/commons/9/90/Great_smokey_mountains_national_park_with_woman_sitting_under_tree_in_foreground.jpg https://upload.wikimedia.org/wikipedia/commons/d/d2/Mengerschwamm_x_Generalized_Fold_Box_OpenCL_18915424_8K.jpg https://upload.wikimedia.org/wikipedia/commons/3/3d/Red_interesting_background.jpg https://upload.wikimedia.org/wikipedia/commons/4/43/KIFS_OpenCL_54815_5K.jpg https://upload.wikimedia.org/wikipedia/commons/a/a1/ProjectStealth.png https://upload.wikimedia.org/wikipedia/commons/8/8d/KIFS_OpenCL_5434735835_5K.jpg https://upload.wikimedia.org/wikipedia/commons/d/db/Harvett_Fox_-_Wallpaper_%2816x9_ratio%2C_without_character_logo%2C_transparent_variant%29_%28vector_version%29.svg https://upload.wikimedia.org/wikipedia/commons/7/7f/Generalized_Fold_Box_OpenCL_4258952414_8K.jpg https://upload.wikimedia.org/wikipedia/commons/5/58/Mandelbox_Vary_Scale_4D_OpenCL_9648145412_8K.jpg https://upload.wikimedia.org/wikipedia/commons/6/62/Trapper_cabin.jpg https://upload.wikimedia.org/wikipedia/commons/f/fd/Openarch.png https://upload.wikimedia.org/wikipedia/commons/a/a5/Mandelbox_-_Variable_8K_6595424.jpg https://upload.wikimedia.org/wikipedia/commons/d/d6/Mengerschwamm_Iteration_6_x_Generalized_Fold_Box_OpenCL_14048152404910_8K.jpg https://upload.wikimedia.org/wikipedia/commons/c/cf/Sierp_Oktaeder_x_Menger_4D_OpenCL_51241841541_8K.jpg https://upload.wikimedia.org/wikipedia/commons/5/59/Airbus_Wing_01798_changed.jpg https://upload.wikimedia.org/wikipedia/commons/8/8a/Holytrinfruitlandpark1b.jpg https://upload.wikimedia.org/wikipedia/commons/5/5c/Abox_-_Mod_12_OpenCL_45184521485_5K.jpg https://upload.wikimedia.org/wikipedia/commons/d/d2/Menger_4D_x_Quaternion_OpenCL_644289452_8K.jpg https://upload.wikimedia.org/wikipedia/commons/3/3e/Gabrielsond.jpg https://upload.wikimedia.org/wikipedia/commons/8/80/Mix_Pinski_4D_x_Mengerschwamm_OpenCL_461481542_8K.jpg https://upload.wikimedia.org/wikipedia/commons/8/84/Belinda_Vixen_-_Wallpaper_%28without_character_wordmark_and_hair_variant%29_%2816x9_ratio%29.jpg https://upload.wikimedia.org/wikipedia/commons/0/00/Sierp_Oktaeder_Iteration_7_x_Menger_4D_OpenCL_2154188450481_8K.jpg https://upload.wikimedia.org/wikipedia/commons/2/24/Abox_4D_OpenCL_545185481_8K.jpg https://upload.wikimedia.org/wikipedia/commons/0/08/Sierpinski_4D_OpenCL_485274854_5K.jpg https://upload.wikimedia.org/wikipedia/commons/0/09/Vereinigung_Sierpinski_4D_und_Mengerschwamm_OpenCl_6184524.jpg https://upload.wikimedia.org/wikipedia/commons/a/ae/Mengerschwamm_OpenCL_955141845_8K.jpg https://upload.wikimedia.org/wikipedia/commons/6/64/Free_high-resolution_pictures_you_can_use_on_your_personal_and_commercial_projects._%2814168975789%29.jpg ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/benchmark-crud.txt0000644000076500000240000000677414601576577017567 0ustar00twstaffborg benchmark crud =================== Here is some example of borg benchmark crud output. I ran it on my laptop, Core i5-4200u, 8GB RAM, SATA SSD, Linux, ext4 fs. "src" as well as repo is local, on this SSD. $ BORG_PASSPHRASE=secret borg init --encryption repokey-blake2 repo $ BORG_PASSPHRASE=secret borg benchmark crud repo src C-Z-BIG 116.06 MB/s (10 * 100.00 MB all-zero files: 8.62s) R-Z-BIG 197.00 MB/s (10 * 100.00 MB all-zero files: 5.08s) U-Z-BIG 418.07 MB/s (10 * 100.00 MB all-zero files: 2.39s) D-Z-BIG 724.94 MB/s (10 * 100.00 MB all-zero files: 1.38s) C-R-BIG 42.21 MB/s (10 * 100.00 MB random files: 23.69s) R-R-BIG 134.45 MB/s (10 * 100.00 MB random files: 7.44s) U-R-BIG 316.83 MB/s (10 * 100.00 MB random files: 3.16s) D-R-BIG 251.10 MB/s (10 * 100.00 MB random files: 3.98s) C-Z-MEDIUM 118.53 MB/s (1000 * 1.00 MB all-zero files: 8.44s) R-Z-MEDIUM 218.49 MB/s (1000 * 1.00 MB all-zero files: 4.58s) U-Z-MEDIUM 591.59 MB/s (1000 * 1.00 MB all-zero files: 1.69s) D-Z-MEDIUM 730.04 MB/s (1000 * 1.00 MB all-zero files: 1.37s) C-R-MEDIUM 31.46 MB/s (1000 * 1.00 MB random files: 31.79s) R-R-MEDIUM 129.64 MB/s (1000 * 1.00 MB random files: 7.71s) U-R-MEDIUM 621.86 MB/s (1000 * 1.00 MB random files: 1.61s) D-R-MEDIUM 234.82 MB/s (1000 * 1.00 MB random files: 4.26s) C-Z-SMALL 19.81 MB/s (10000 * 10.00 kB all-zero files: 5.05s) R-Z-SMALL 97.69 MB/s (10000 * 10.00 kB all-zero files: 1.02s) U-Z-SMALL 36.35 MB/s (10000 * 10.00 kB all-zero files: 2.75s) D-Z-SMALL 57.04 MB/s (10000 * 10.00 kB all-zero files: 1.75s) C-R-SMALL 9.81 MB/s (10000 * 10.00 kB random files: 10.19s) R-R-SMALL 92.21 MB/s (10000 * 10.00 kB random files: 1.08s) U-R-SMALL 64.62 MB/s (10000 * 10.00 kB random files: 1.55s) D-R-SMALL 51.62 MB/s (10000 * 10.00 kB random files: 1.94s) A second run some time later gave: C-Z-BIG 115.22 MB/s (10 * 100.00 MB all-zero files: 8.68s) R-Z-BIG 196.06 MB/s (10 * 100.00 MB all-zero files: 5.10s) U-Z-BIG 439.50 MB/s (10 * 100.00 MB all-zero files: 2.28s) D-Z-BIG 671.11 MB/s (10 * 100.00 MB all-zero files: 1.49s) C-R-BIG 43.40 MB/s (10 * 100.00 MB random files: 23.04s) R-R-BIG 133.17 MB/s (10 * 100.00 MB random files: 7.51s) U-R-BIG 464.50 MB/s (10 * 100.00 MB random files: 2.15s) D-R-BIG 245.19 MB/s (10 * 100.00 MB random files: 4.08s) C-Z-MEDIUM 110.82 MB/s (1000 * 1.00 MB all-zero files: 9.02s) R-Z-MEDIUM 217.96 MB/s (1000 * 1.00 MB all-zero files: 4.59s) U-Z-MEDIUM 601.54 MB/s (1000 * 1.00 MB all-zero files: 1.66s) D-Z-MEDIUM 686.99 MB/s (1000 * 1.00 MB all-zero files: 1.46s) C-R-MEDIUM 39.91 MB/s (1000 * 1.00 MB random files: 25.06s) R-R-MEDIUM 128.91 MB/s (1000 * 1.00 MB random files: 7.76s) U-R-MEDIUM 599.00 MB/s (1000 * 1.00 MB random files: 1.67s) D-R-MEDIUM 230.69 MB/s (1000 * 1.00 MB random files: 4.33s) C-Z-SMALL 14.78 MB/s (10000 * 10.00 kB all-zero files: 6.76s) R-Z-SMALL 96.86 MB/s (10000 * 10.00 kB all-zero files: 1.03s) U-Z-SMALL 35.22 MB/s (10000 * 10.00 kB all-zero files: 2.84s) D-Z-SMALL 64.93 MB/s (10000 * 10.00 kB all-zero files: 1.54s) C-R-SMALL 11.08 MB/s (10000 * 10.00 kB random files: 9.02s) R-R-SMALL 92.34 MB/s (10000 * 10.00 kB random files: 1.08s) U-R-SMALL 64.49 MB/s (10000 * 10.00 kB random files: 1.55s) D-R-SMALL 46.96 MB/s (10000 * 10.00 kB random files: 2.13s) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/borg-data-flow.png0000644000076500000240000030354714601576577017452 0ustar00twstaffPNG  IHDRPEehJsRGBgAMA a pHYs.#.#x?vIDATxee}'/c~q*fƤ*Qz2vc &# ( D*\HQ"vM7ܺ|~X}9g_^?}^k^{>~ T*@ @PT*T*@ @PT*̃_WՏ~Y,韪O#xSl2N*ծ]FW_]=<9"YSw)u]|WTVTi-sΩ~{mIlê8۰ϟKp~\L=<o߭y;GDx/+`u>3>lkN:묳_5wn4za'@S)e]xUҲ*?Emt:͠{. J9B Y8.(]}ub:'5 @-(IuuԢVFGZVӼ#Q1?Ou kޫ;!H)+N؋c0~Su]-(Hk\v @XؐKS˳uj^YѣU߁uE{5u~n|i9B 23 Q} @X8y@RȓW1u)1Қji?unT Pg5G}n9g4jb8էTSVo[R tgem595ny=GPauԸ>l(5UPN~'tTU*@u϶_^k:P}#B,U*@e5.Y;B=߶/{cz?I`=W{ibYmF:ry%ܴM5m[|,߇i/kj:.m)CCXf \㱱=igZ~|Խ]$1=\~5H7@ͷ)g6 =M6{$C)xsjlwy#\nzqna,oTMMMsT퇮m}mt[爟 ܻ!l<6w?~D/R}} *cMzӡJ΃FU]woXJH6 PRb@O5[w*@m R`5]]'MZu׾z RK Pc=c}i]έ'4n~v_xsF<>jY!1uu  P S4]wjlYu:5 0*?Zimm]aTjʧ>!$-eAΤ爍 Psl ZR{y.(_x|9EBk8/;U9 ?;ƻtgJ:zkP3Pf4w.qԲ+] hR\Ƿ]|mJIԴt Z0#1P.5-4HLAF˝FZR m0:i~n]Kh8֣Y7z i+/)tȕ牾XItJź*qH_ \'9N#Ю B4vLk,*UI^Ce窺ף sێqM򘙇y}ާ;3<7}֗oۗu]wXθ- m7TBm fv ~C߶uUIuhjF:Iq5P7Cv5 *^]Ig0صMf]-e2iz4o"Ug[ PWקmȹ-ɏIZO>C7#6:@mIPջ*֥Pu_پ׫\Iot4)Պt}.h_&}.ZN;@m߷I^ۨgs6UI2@` }|[3b bԡP>-dZ54++%~' PjuQj>3ry}C!UBIþce>%}˂\狰WylםTWW֭;N]_ YY P L`u]O8jנ >i1 bԡs:[CKU},!Q8>AH)$y6`r =N  5uyo:8ب׷ܶq煮sIZ |;P*B k0gP:mR5jW:r TL#u{k#q{1֧6?9L+Vh\.zx `lq޷Z~ ר|瞭/ðPohuzOs*q|nqq?S!@tZBK) MAjS,YY}yPˁP~'6mUINk@-C#@]u@oTkˬ;njH㴎pv8tL30:翜4)<1*@M7죜fCsn>"*cgZDu.' P^cuCJ4b^XdqξUPgQ7nM 26Mc1@]MsOYP0ɃuԺ,s:E:؈u Ϻh37+ۖ^u9ԺEyo6jӗN]濭 iۖ5odϭlN R7pZ 1o()b=6"@,}ץi|_?In3vU*O3@mzmgsҪMum.PN9Pg]!ڬYFܿ*?{OU~oZs6M#ljJTi8+yPWl^|Tԕ PM{ԲiYΫٵ_4eVc#I1ЩO9t{1_i/}"ljL!7[=-M8#Q>~q6 Y״?TC:\,y4Ui܎4Biչܞ>aJ;a7U2N3@ZP -yi]QV0cohLj}]L *hjyvdVjIFYvI>Z*ˆeT7Y*az=@bMYڵuszN+ZV \Ԯj}?ب| .ægi CuI~U(afCu{Ӽ P*@{U;4tܛHM3g+/]tW2{"i`7 @ڶmr{{طuw4(MS7_w\ˮc ;ب[둞i}-{Yyi \ofUO_۶ж-qѵO#@M.o/M|R嗨iۮwmm1|/FE/wuyLa2iЛ_ @@ |P46]/jZ:1Dj]5Qw4 4AZn:sN)7sGi>;TX}Z9*T UMw dR]Juq1?PXVQCT'gOS@ܳ>*2"Ϳ+Ej̓T @PSO=U߿_kc=PÇ={T~uꤓNٲeKu'?0=:F~׻Um߾袋{:v`nGV7p(0=Sւ`}E`w޵/2"\?uN-)0|g1Pg쮻Z N_|ꭷ3<6/<ꌪ٢mǎաCR~BԛoY P%:?^zB(X;wU; ۷ڲeK `\/Fܹav_W'XBGs Qm۷o~`1 @ Z{:MoUW]%D} fo`D+w!DmzM|M`yTe&<|j֭kjx饗 @]M{mV#'t *T F? p %\"4֜uYBzA*PWϮ]}kB#`ٟ͟U{9!@ ; @])gqFuw5q^=zT 7MT Pj&>4@*@ o)Dj:T Pj^{M T* @/ P@*@uz-*P{O~#?/~Q}cO#y{n:&YuNEH8rx953X[N?K8Z~wT@:GjT\F@GTW!a -bې~pFͷ-XX.Y~]5)@q$wmKzL7F:\LTŋmՖM0w_1E_NP?n﯅~;vX瞧>rG PT*T@<??/{m;FaYh}C}lY,+^{ ˉ>\xmuԮOGt ,¬M8ۑ¼&ed[d}ٶM}-۶'7>_.ak].7m۶+_umq,6=_ PcCyYX.e3ƨ +;RRK P%P+~oƱ^Ө R*RyN}|cnv͛T@*@]u[i6B HZSվ_'ۙu!uS P+. ǑϿeo}V95[},@T*!|i |m7DN#TRy+ySXh^nlcX>Aml[ii{=NuT P#(? P`ܤCT*Km#D P̭C4۽}"@Tnv7{Uʊ9O/C P P* @T@*@T PU PT* @3P_}U5T@*@U PuE|wVW\q PɽQ[u\x#< Pyx˘F PYj"6UzUp P $Pqj-@{@ PT*T@*@G_~y|`&1?em߾:x`2w?pg?뵭x|ܷv mJj],ZWU"1u" mna,'~dc5jW5iXզi]TꂋJ<$LTj˴rYJ3=117!B9"ĭ{ض|=j|[SțTʫV T* @.<4lCǼ:.@Md]-miWֵ5usl2w@T wuAk]e6Co*mnԦ3DJ P+.BϺ`:&RmOmj~yZ.#IU P7<@}嗙% n޼mԶl۶mm9̦i]qmuuz>U>o mAd[ / PY5%dz! P}ц-CޡU P 4Mv9v80: PUbPPM7qON>ۂֶnkT@*@wUfS0-'u2 Pk֖?CئJ٘75~W]S @%)`2 CyV] ֶ+@OXƿg6r=bmMqSl7~/˱$@KNM=Ӹoq{9!t-㚖?O j/}ie?mZ]׶OMt Puä3IQQtˊ8pq}O׮kZX~~]WMfU P Ph%`SڧTVF>wxU\nU P+#NCܘ)BoTʋVB ;w~i* @ P= F P P* @T@:M'|ru} 5[n]vU\sXs PjkQF@uϞ='?I^=T@wn"PKV/"@CU7odս+<.KW_}U Ւn$%<n %@Jڿ1#XmO?tiӦ4;vL ճe˖܇B$X]\sMuo{=\!COPIs='@ڻwoug `۷ڼysgi<ֹ+mǎ7׿5".ꪫj O@ ѣG[V=` V@|aryu(40~0$Zx#Pb>ԏ}c`yOSo)@*?g8UFxwO;E̅|z衇O$sF~UO@J5*Q `AEz%ng䥗^Ng>ͥo `ADW_]~ե^;8 /oTu9*R#H=r0~_V7t(8{'z-* @رcp;//GAM6կ~Qw}ɂO~26/8wW\pAE:dS@:W^ye-hoQPMބۿ6o\tI̹]o' رcxM6M iFs @52|@5›pWrAn[ O/s.g1u]7z}SYn<SUj,a9OEZ[%@e1 @]G17jR PC>lu7)R|WFAM6es M '@:j7ᥗ^^}UAΜxBwkﳘ_^{/PA PA PA PA T @@ T @@ T @@ T @@ T @@ T @_*P*P*P*P*PPPA PA PA PA PA PT @@ T @@ T @@ T @@ T @@ T @ @*T @*T @*T @*T @*T @*P*PBGy:vXtСCs+wR}_~_v~~wվB8S38+'x4@{}s{޵TX*<MZt˖-k"RUNS*BU} @UO U)TES*BU} UO U)TES*BU} UOUO U)TT*LR;UOB*TէPQ*BU} PU @*TէP U)TBU} PU @c=VuYխ*@S{ꢋ.o^mݺ:餓ZعsLGVÇG?_}gp;Vu]'< q"ٳgOa 狋p7qt)ޗs @ev 7Tgqh <-[Twbn_kN;jǎѣGae|Z @ KЮ]^{A 8Fpﭷد~Q5j\[f",M?Oz b`}|"LbkxƵ@ 7gP_[? ہ˭}{(XpqvǠdyѼǏw}PaQ䣏>:.Bm 7P]r%$XQQr-_wmPaڢ3Ө\~էMF_,s# @)GgTs.[[?bØvEFsFÇX ¤J]?8"?S رcN$Xmzku /0Ksz.Z-[Tvb'|rꫯ @g-@ qnp0]&0O_?V6?xQۮ۶mU5+*l׏+WnݺU[?vY}#]vYu饗B02)W^x㍕裏Vgu~`w߽wDַN58p *?j׏΃P__״K%*K"x 6<@ k@ ]+qm v(kEt;0:9r5 @h׏+Xv}m*9餓Ӂ5~[ϺÇBo6m"`?aF zc=u=Տ~*Y*PB8g8wTVn|׎n]ۿ?c(xƅ^Xi<ԅu}Rw~wFۿەI*0@ʵGe]]mG;wdtWW5y{; ) yP#0La L$@6:@5* @ee?Q~$ a6qT٫X@%Ԯ 5_F PìP;>y*j\Gv,h?S~믿Ό hEWAc0UFغU]93Zר?<O P\SSv,{VK/z #<,m2ըP*T`Z>< PYv;vT۶mlh `mgyaU*@)@|T6]?NovmKhn2|MmO4 3A`<>M0͹HxySSxio5g޴I 9崭ws`Զnw}†xL~ T@ @eBʥv 濭o04@`)C|)(+obUY]۞~D8tí9TG^;Xлv1lWxZ(4[lS~sg=2]t(ӮXmxp v onc^we4E@inw6oO1ީR3.붡k]EMmzS)U㿳 T@J-Bo}[._be˖l'@̓2*?Nsp85jܮiIJfM5mC^y?G[ڏenr_7=@5* @RسgϨ]nr1 m{V5@ͫ B/|́?oh>O9Fg1(@SO?hק۶mՎ;hgYwgzja]ai^M6wn*a,Q{tGm^{m*j^=xjb> >j?!* @e?j׏^ 1X!SOi+T~}^xσ4W^w@W^y.3_F>z Pc8N͟ɟ,|;a]}ծ]e*9m3-D\Պ}WkT꒷Gp/}i]S6`AHT"@ew 0s07ImMm/w]S R=@׿~BH_D@4>3#\M]V5I' P,]߾}r4QyZV`qjWL#H]5Mb?LOT꒶Gks8G~^y)鶬u:tm~Wg]}咖g$*u:Qkz1ii@UTc38C[?G] R͟ӣ]-ߥ@.>S.>k9g P糾iYܨ5J딮n'";2]?Z9Z5IiUIgQqI "-K7ꪂp/mAf>]|*wiV>A5@KǙ30p/s9 R%@_y>?74}ʗ?]5TH¼ Me9Ш`;ALܦ[|S05G]wN+ HiJ-G\MaoXeB[?^|Y1n:$H]5?yM/Ϩ|^z_u5}Z~>` rC ׶r4mCmuziWa** @޽{Gz뷅t}˪պVՓm1mj>)[ ַ2̧(()X͟/oL>m]3,C~H1-7緝Am0{]tj u&޷u]G{uɟ/3 9i-Mܶk|_q __ P*;v>яnH~b$o PPԺ^]H9IgeźVśoOf惱AlSUᛷiA]{l4Hev"T 6m4-H] P/tԝeMwy #lnr3;庭JާNV}+k},zȽyzzȗG @%Gm6M;@m3i.׺隿t|?Ե;ߕU]!h}n`6 uԼ.Hm]csz|]]Ug4;6:@}4:BY 矽wV?}T8 '-c6Xs6Zm5 zyuK9l iNզi^^;3S`bU Pn\g.jِ22e@uqU5?O-y:jԛßn0#ڎ8BhGuHf^RΓ6IpKqt(6i{ZvקWy P}O  NSJ 9K>oCmkx %@B Gzȩn/*HD%Ngo5ɅzC2 Pc鮿MVyPZo]M7ׯk?+_JuW_}uUWUg}+g7?=y7 ep*@(?f柁C>K%@$ Bs~Vk77+PnT l?Fꆸ+Nt=oߧ n=<{ۗ+M_qמ+M>iQj}Q=qT[n~5Bܠ7pcϡYy:Y+Ժϝt~R̾W.'>k$m]*Cn2@:/"*@4*Tn6uJ;ڴiӨE׿⢽m@(>Bݲ` [&>so} r#<2ѠǦ0k@oAi۾']ol NK1-[oB8Sf P?k /1Btί,+񙺈$_}& PJӦ'~!]$ƍE90~=l^]鎳~~7oSD"` 2 b1e`o[,7_X4Y`1X8U/)mʍ}Qޥπ2_rWVԔ\ԮWT:@?Gyp j7*nsl2?7}1 !/Z-@ͫ~~?aU 0 F`Zw>KL.h;'],Fm6?½/gA<_|CֺϘXVۗM".ofǿR3Ӷƶڶ?s]5oG6ym) อCŔqC˛I+.`ᬃUP%难j : ھ˃˰>74@ #+uhO_}׻WuUz} CTi:~ږVwPWBs8 haVUOR>,(ؼy), N=oX1DU5M#Sj)} R3. 2 PӾP~׾-DݽfNjAn8w[F`cԼkj1EELU]6N#8]le[YF>xI1{ōd_[ @KV矽m!< Xj_!w_&i>U01Ev}XGp* @X9QKE+}TmY[gY?mlW\D9R9@ծg˖-j|P&T TrѪՇ>ӟt_r/4^Clonu޷-M0zu N`vT*PiJtqyu׍8sȑ#՗c 0}w^T Pf'*N*'~yD s|Ali?sc< @Sze9P.YOqg??ծP*΅z/QR мN_wqκGSIDK$1E4P&@y[/~s)PO}?#m̴]?K.D> @3~\ T?~:tw߭?* @@7^}ZR_~[[T |.ѮP*;nRҷ׿څ?^]pG}](@u.rTy~w.*o޼0RuOg* @cqӨFRǏ^zQt0]SzݻwOܮ!kέ`FC8i}ՓO>b;SڴiDj`}PAʔ;v섶̢ѣ]s듴>* @o4]^z饣 [ouabܸ* @T#Gyi_vϵT@ @?s'GEbT&F }* @mY[T$FebT(qUk6$@urT*PYT'xE|vh@ THmMb1G%45p{T*P!sѷ}n9բv~ܸ* @:fRm6lOj 0gnQpaik@ T;mQGP{nmsҮgWwv}@ PQK.?'wyv}@ P PT,cp]@ P @XojӦMՕW^Y>|x1}/]Xy P @eeӅoܹ:3~ SVv횸]* @To1:M?яFm{n_€aBַ&n׏4x@ Pߠa_/v}@ Pyi.~Q[3xٮڼysuwOTuz1pѕG @*oݺ'>7Q_]XhvZӟԹX묉/TBW_8vq|7joi׿ Ka:5qј#_ @* h?3Ggǎ pWԧTB_￿Ї>T}򓟬7 YW??jݻ+b@]vU^z@ T_`FA5\zH>[}ق#`$n(GT*Pʶ/xp)M]0is=]XrJv'M65^ĔFΙTS#p\׾av^z"0.t%$Xm1G|s&W^9<.!i]?nㆧGѮ,{:sHb:;S:'Lwl1Ek٨jZJ_o2Y)1}Qίk:TǏ:vQ"vծBU cXgϞ랣G/OB~{u9t^ĜΕTm֕. .1mXQv}*y8gm?p @*̪?Jwtݨ4c]Xvw݉[K..련Tu(E_>j馛uP`*,8o%Xr7;׵*Pazf: xgO~G5j⼷uֹ͗>߷o_鋜 @;sT-chG˿ip%Ba,{w۝k&֫k1V>6J _]` >\m߾}ji`ż۶m}ϻ&ֻȑ#'[]wծ0 _/O⦞1/|$n\ @*lЀ<7o橭?!).cMxGٻ/;_9ț[0F]0 3b&*$3ʨd$b*41a((~sfש׋k]*zקދ?|i`T|*۷tQG*co^}lْ֭%KթqDq}w34W\qE_L.[ti ؞R8߽ Bo&g>>ָ~\'s5ScE[GL_|$AoV}WԂz_s?>D=C-8MTq"*"/| z`r "KS_|Ŵ@osp3{+~q,bH<PA@>pފGʸ#t~?ާۉ5@_4PA@>K/M9cǎO8A= BHM%>gϞ͎9"FA~ 8q}}4~a{ @@4Ι3';}tvA=*VHpV00ҊST_g39~J MT8[=wI*0T@@E@PPTT@@PA@@@PA@TPA@TP[lٲ;Te˖,ݻ7:#~TˀhѢӟtC]vYV+**0pD'W+OPPA@ gZbƪX}ZHTTPa`j~;TTPA@PPA@B6l޽{.]u{T@@ .l@kZz CPryT4ƟvC@@ThѢW\Yy#zT@@ gҲUⲱڴ}a* 0czՀ+N,*iq8Ȕ@@T*`N5ƪdzf_Q *t5N>PӃGՋiPT@@kA * O@{-TTP-Z4qZ( W+W8T CP۽]T@@ *kYT@@ !hqRq]gq6 *h@ ]vY6F ZѴSZ +ݛEt-Ŀ* * * *  * * *  *T@@PA@@@TPP=/ *  * * *  * ** * ** * * *  * * *  'U@@@TPPTT@@U@TPA@TP[Aщm *̈5kd/6o3gdټyݻw *_~D ]|yj@FFFYfXy@@jjECDV">|8kPٓd]bEdɒ좋.z>@@vj>6}^OPA@YiV C ST@@*TOPP>@@TUV VZ} 'P>@@TBPP>@@BP ST@@VZ} *Xj)* bV * STP*TOP_j)*I*TOP_j)* bj*TOPjBPbէ 5VZ} P ST@@;wfsV^ @_oݺ5[jUbŊl޼yhߜ9sKgw}@@Ç6oޜ@馛={a#ccc{Y f˗/h6>>=:l߾}W|[|_Lc|_zTPa~Oci|8H!5V4Ň_pE4_n ;ߛߗ]tQlٲ,PA@@>G8q_`r?oDȂ^!X*0ʋqΟ?СCLbbԧOn}y/TP]6ߨ'`ӦM(V/;&HAsLx_PA@>׏###m(?OpXu*@`_8/Y/TP#F<}W;X?@ޫ}f ={ ұ~Mh}i"_`@/G"zPA@{|%tC;wn3Ф/Iwm$T8H~+c h`͚5!|IG*0SW>qԞ_xJ0,d׮]B *Խ{" *tp\?Vqj^7֭[ H`j 5"cl B;Μ9318CC:R:bdd${$r>`v=5P/~ c@x se-PBPA@Ҹ~sF$ܸq"(ELw`b뮻 ;v6 *ĸ~0E]=K/_ :|ĸUHS_v@g>$5i7TPqUV2jXΝ;0T`m 2\1~~}Xe###{ٶPA@e8/^!K}|e~@@%9rv *-/f>lvYѣ=c@T@V@=tT֭[/ٽޛ8qi+Tm 2xK,,X@0b?Vvwfc?A~@m3*ո~:}}0#>O7|nl2qT@@PA@"|X),N!& * ұqKQθ~G^fM&~$?+^ * JS"EokOgڱ4\r"MTP@@T<-q?)_A+m? q@j]ve qP@@CcX.>7C7֟5f .k+Kd-v ׊0,u~ *kf_?я%KԵnݺHz7V_6m.Gr@E,aÆl*5{nMZ|݊TT@@>ן?~}/kb,"je>O m@- a MEJ@-v6ʧ/ZhRD^  ҇###yD۾}{_G?_sjyꫯO~2_PC_VjzU*;* cFGGq_W}ҪU^߰X֭[aA/mPk]J+PPA@e޽;Pos:(cjZPPTTz|\ٲeq\SZyL@ۊw*~:_jU6BV/53PU@PA@G#? .S/׊ӋpĪT0fsF5==~A@T@@gϞqggΜ(i@m2i@u}7nyZ*֭tڠ=_ϾX33p vec L Jό/_<_um۶/׿3롇 tr J@mfkqG 뫱fG}FLj6RG! /\0[r\^,IZ"bŊ+]]6l8_=k]&=0Tfn; PTTYl/PmO>PV%Vǿ9h˔cڵk/8Ox{hO#^ V}  *  2|qqPꉃUyޛG{yvɈRvN> 0A@PA@ۗht݆*me>OּLq}/⤯[~ N}޽'a:!uj"߿\3cǎyjE<ύ7~y\PT@@q+VG8mp`@ ZQS׼.~X? /!ujC!ZUig~KOϳ G8cǎخ!>Ƈ_M_&VZR\_l4˗i\&{]jR=ϋ4ϑ|!~.4[qAsСaYhfE*PX8{챑j80bdɐ:5}ZZkjiACω*Pt\?Fcٸ>4+#.rh&v"z@m7 P㵪ObJ4" 2^{m>l\a]U̝;X?LC+*v/APk3U (Ǻf|݈~8O~kW|.TPZ1#؅~O9RiܦrZjUԫKPi(q oo'F-h* R3V4=pU=}USP,zkk5xqaW* *u͈CX?̤KP P#W$:TULdĵt<|t%c9U"E+n3n<<>P״ZWl(^}X@@@u(,Y}k_3Si螱<0z!ujyf!}*:Tsv+PxV+ej4}^(S]tE>gR!VLCjgPH(*堙^3.]ډC@m岵buթ^a8x}a\VTPA@{4VZ)4a i l@Mh ջVV+,,-s'LP_Oa(k~7َTPJqDG u}tWRG/^fj4jtbVPӐ8J'_b?Pշ&#PA@*q 8 Ny]y꧛oYYf|gj8}Si}v*֋qS )<ٕo `Rx/y&:48bOGq jӑ_twy'?Kv5 n@R9Հ/qzZ`G=Z/?SޟaQ4VզBjO@u(obL׿[g[l+>|8;w *Ps遫f2*tӻGg/[_5#jf*Ceݺue({٩S``ۋ]4}ꩧt{W^8o`r@uw˖C:vˠO="jS P(6R1|5dccc"C)\k\Y{ 4Kna'׬YS<ꅱ"e3 rL-GFN5 *@bڟUF۾ई*:b8O ,WXA$~Zq$XyhuRrKSٳgm$@"v;C@-"%\R"?\+RWa4i8j@Hm>?>:QdN+hӛ i)̪]<ױMSq$ ]}T\J@uh~8F?ċQ~8.>$,-P.j}8iˊ?hu\|0'O8]tjw/h%cⱸza/mֺVj_YVYkPR7U._~*tRpjߨ+{2PA@؇c#1/9mPԪXhHzʜn/~]U\uU؅Ti||6 ` 9TtXVn&jjc|m645sZySwf@mj4ISW *@E/_!PA@m~PP;ӕ3SqEE¸>O/袮a ilx.%Kh+x/^,~6i~#5x>˯Mf._<ߵ#cכ_*ycW ذ|/D@"=uo+N/V\[oy8G|+b_w217Ŀ*NӀ:?%ݣ+GTP{?g? bUchſ׬YSP#KYu[5ʷU/ OoFM/Su$4 WZN 2ՀZ\Oܯf?O׏]Xׇ[bŴSPG^PA@J0|_n"E=W^Y4Z5Beru`͈d9+96YuٸLoնX\+:Kܧq}m5Qկ҉q8F]X@Plk~,Jc/nmzD4,jgѱ8}*% qj^lzʁ(}Ǒj"֊iDmPܝէqto|Pڟ HbG_v|C7b+t5gQQ,fN*Z/[gk+Fk]1f4V7N{+ѶcA@T{c*Lu;ȾկvtHX/6訙ZG8{* ̐o} O:4sutVtv@Uڧd@MOyEhz@ td6]ԺPqPfPA@U'N`?V ]j8=Bj`O i(|7j cnz=T@@=~T:{Mlk-ܒر#ڪ8z蚽?2i@M^{r"vWfPӯ[h8Zu}}iX~j~ضmD[{@[K@P:ͻTPX*V6tZw;+LEN"GMCN}Mos*bPzWՁTPǏņiv,"nZvj7XP((V\FLm9-G\>^p([qlmG?} F %KukT:9 7dvjjc7?h"2ƟqZy̽LlVzЦncXYWx qr`%}NӸ(M;uٳ׏pj\P`z agΜ4,VkSciLbիWw$VVq0PoS +8-V6ܤZZ<4kqP#ǸEθsT@@PA@#{oRx≦(Ut?Wk俈ΪӋ9zӫ㴪"Vݯz1"d-"iG(?7wE.zke1<Ɵ1^>8U9rĸ>0#u~PTPX+? Xя~t"#Ɵcj=8p> * *cAbb1+y>VNTX"zwb\?^7PTP!1>>~XjшW^ye[o\ *`?&Uln߾=_+x zO<ӈS `#T@@PA@$XHL#-_|e\nnf * 2bEbLX=cGׯ_o\P@@܀J/JX~o b1dzٳgg+V>9:q}om % 2cņod\sMAϖ-[;c\P@@TӹsU-[o}[_qq]*  GK#X ^m=zԸ> * Xc&6_|K_R/~쪫ʞ{V9sP@@zb/PTPX~xUj\X_CT{_ *XbCOӡm6{g^ G@ *Ь;饗þ}}s\v!\l]zQնM& *8156CΞ=qǏn){g$rۢz?m*ɓ''?Cypj׿;USq}]6-v#Ή_{PA@)Gxocz6A5 "W\q1~b15_{& *?l i׏UajժlٲeB {]_ 8q₱+2ߏq}̙>*mTPͱ{ob^Br۶mi3-ҥK԰f͚|Z/g4߿?;TXUW] N8p>0P.\`Hlذ![`AS7'TP"4Fp,6cZ1_jZ?Dĸ~\q}`ϟ޽[\s|2m'OPA@|zoo{\?VM1~4@wmSB۷ooj > *ty$4_~}{ʇ+W͛7g7x OڱMvm"TƱwygbc{Co~󛎍_}wm\ap=ciST8A:75֟oܸѸ>@^=S>۷ST聱N1_7;;}tVBXEl^hQKS:qP9*Cc)gϞľL~=~o}fޛomlH*cB"^ꫯ׿Σi|Xdq}p]<@d8~7cl7g|kI1 t9I*ЋcAFGG9sdgr\rI[q'N `N>x?_x]|??Y ӳ´)>g?_>?S^KfTq!uӦM4q}vԩIildd$?@Mw饗N|~pIS@@hѣ 16q([0> *^ziո>@E|vS:*Яn:j\`?~<;p}&o@@,^qz3!'.T:gΜ,]q} ޿X=b߾}3~ @@8+zwUScǎJ *t_|s߇ޓTPa** * ** * * `ԏY?uxmPA@^{ayuwʕymPA@{WFm4VymPA@ͮBݰaէ fV^veV P˫P-[4k\Gf-u4s˗ݵk* tjtO#S׻e˖_hQ *H@-B]re~էABDbiUϪqzmt+TPA@j@ZZDFOch9biqH˧;-w+ 0tڵѳfΗ^wyjq8@@TɀzOciF,*@yEi =Pӕͬ>MǛZZ^iZB,* Œ*zOCzOOkTPf413gm&bVZ@@TԾ͜Y4.x PE&Bg#gl*7* B_X-ZDݺ/* B_P췴 *]qbj\vÆ \n˖-ٵ^{AUq;qvЍխ "l\OY"ty˥QP'j\.bh9~a4"kX}ZLqX 3]{bdnrvt9? 0pTPA@TP@@TPA@TPA@TP@@T?hPTT@@PA@@@ * *  * * *  * *   * * * * *  * * *TPA@@@TPPTTTP@@TPA@TPA@TP@@TPA@TPa@f̙3Mh'>|8[jUsN?PA@Q.f͚-[,۽{w:66-]4?y`@@Gr6oޜ۷/k5F}g%Kq6=T9*wPYp]w5r5bkK]է @BOPA@]Ox~p+Vv VZ} 0bN~f͚, PA@P>FۄX**`է$P0wlϞ=T S+K/_(FKv@@B`mݺ5[tmE@@B*|Acǎ믯\jPA@]jE{;Kw@@B! @4i*>Ys ЬBpI5V^uU/Ξ=kPA@_j)*cǎemߟ=lC槝:uʶ$ S9sLvWOچ[&V * *TOhڵk'mG^|5VTPa WZ} @3b_m۷ODw PA@Zj)(oO>s8u ۗ * *TOhŬY&mO>쳓j?6& SZ5gΜI۔7oPӧmg*P>݀u|ߧi@}wmg*߫P>S{TUVЩz ϝ;g{PA@\j) r@=smN@@sէt:ؾ  B߯Bn **TOPP 6FFF %/[jUg P{3<-]45kVo֮]o I~[|y6w,>Ċ;w^T@@ci,Yfޘ۷/[fM6즛nv{PPcL?ŋz+;<6mڔ,~a{ * v+1W_}U>~<:P * vPlʵ"cǎ+c * )NNc#0522bPP* ;5˖.]jPP"`p-_<[j @@T@@mE4 _xݺu6.Pqp?7* j qШ9uTCb׮]UW]?~F * +OW^-(w={gCPPۗ͝;SU]w]gϞwߵ ŋg$bsOjmp* jPr[} Cnǎ*ɓ'mt* 3|F *P#P4۷F ** VTAT@@U@:ٳ6DPA@AP/>}چ ** V9q @@TPT@@T@@T@@P~{O}*=z4Kk t@CPTul޼y~ N" Ah#~}c_?   ~|^"@kqZVT;H* **j[b|*Vԟ']_9کzWw9կ~֏cU@P@@uPjD4ƿӱ{"FpvwPؿ/£*  J@}ULM?V~}Yd}it?zA{(Q#*  "3TC@PPgj˴M*]n]bQ ֏~kq+]k;w6}9ߗ#wzk={|إAF.n+[z{q;(|y<v_fjq;q]7ʯQ܏z+* 4XV0ZXuVV6 h*A+a뮧jmmX㾴mTuiz^nʸצ?o :$5]AʺvjZ2 ʿ fjX4Ubeb[-VJfnB2to1<S}mi<|zzqC"U@T! wTjl@wfV網ձFN</ۮ_.Z *  j42ubl_jڬF1ـ FcߡSm;N</~/^:  F7!o'R5]smtt@}k6T@@PA@/aƍR;וzKX q::[4M4{=+V4[n<95z;}_Rm\|_?ت?M@T@@ij tR>W3G 8=W6*  i@ 3PwXas4VV./* @@Mʸ:=#xPGy&~בV./* q@M~PӀZo%fv|Wew bLZܖ/>T@@T@@퓀VF+Y5YXP(hTChzѲޘ{t:j'~aP7PTP(QQD0~#.Zq+q:|p˷QuXuk4cLNLs/zcqUT@@PA@ЀZBFG3OXqYu7c5:> qǖ> !7u7Dkww (n#W~xʫ`;|_K/;޾ cz뭺LzmNܗFMC}駟mcMPP{ ,fD""E`wQ˗+E"jEz#.sܧ8Q z[Mo|-NT /ZСCGkߺq_P'__1 :=* *  * *   ** *   **  * **  * *  **   ** * N%:u `  * *   *v%\}cwme' >x`}WtqxmSO=  j+|<}"@O_wW  j+"Ջk֬XXK:xPPU@mRx{"5ҫ+=s# :4>6Ӏ珨FX*^@U :ɓUj[ox/By~L'gPx^uhxھ} PPJXw޷PH)?O]* * MƹXO!^-n/W ck|؅@35+^#Λ^˗o7Vzj簙zrT@@T@@PfhV+*zEꩺ:1Ժ9Rr@H[:fw  0L@MS+ v"V, i-V7#V#VƿoˆtEj۫ V6TPTOgiNOof_ZҀAժ3k5@}CDVwPu`rE-/o[u fT?4PA@P:6niˤOG@Mbrܬ)=HSimegzzSY'J% *Pӫ̀Z݃,պ|me_fGTPP'N4;_Im۶-_yUWM Z[1,X`6W^u69)Ի|y?1Bus*To *v%FFF&nԈcۉl9Z[}# * :rt@MP4CjՊTU@TTujd@MS|mX;P Yc+** :QTF@5L=f3݀fLeQ5VTPTuh5:_z2UFaX (v262Pu;Z * *PK`FtjfDqzzPYof7jPPU@#:QUNE8,ZuJGr#cȗN-ߏT74tc(|w\w<4p^[A@PPA@P^L\#±V< W[D_`S.[VOaq;˗e>Ճ>P  ǏWG:O)-ܒیe6 z˗ջ/.ş[qҶr.)_=5/~u #PPJ~P# o b7 b** `hZB_mh** *R[ߞd|_@T@@TU\A$?,* * M6݃\@T@@Tu bolloυ  * * T8PPT@@T@@TPPuJf͚mٲE4&|s֯_/* Hzj~7{GT@@T@@]bEv}Fn T@@T@@ .l* * n޼97!" eW;v {׳ٳg :F3o^m6~ӟfw}ẁ *C8x;T8y @@TPΝ;믿^G{W]'`j~é]LәkLiojlB0ct(ǔ-E=(-8aPjRJC MPHyIr#gf?Ow?"t?kwߵ0 n?իWwnO[={A *AG?b ]vW_}uөW_u *͛oYd! xm۶4:u *6nX|ӟ6t֭[[v *_`HPA@;wW\qESO P0fff Fi{PTطo_^>OT(ŕW^Yr-ݻ[,Ξ=PT/B+ g˖-e]V^`^( ۷KpY)ɁLT@@v;v<K/vw=`5^#/6Qxyd=83:}G ):u * vȑ# >1n+G-G_wu: v]x]@lY5g_(y   zUa޶圀s`5OP"DxTӾ}^{8{ @@T@@],fѨ*СCӧX* RQcD8%ҟ[w ̙3űcPFPY>{)>\?~: o"R8rHITPY3H?w~* *t OPq *  ** *uTTTPPTPA@@@PA@ **   * * ** *   * *   ** ** *  * * U@@@T@@PTT@@TPPU@TTTPPTPA@@@PA@U@TPA@@@PTP@@TPA@PA@U@@@T@@PTT@@TPPU@TTTPPTPA@@@PA@U@TPA@@@PTP@@TPA@PA@U@@@T@@PTT@@TPPU@TTTPPTPA@@@PA@U@TPA@@@PTP@@TPA@PA@  ** *   TPPTPA@@@PA@@@TT *  * * * * 2dNJ[;^v ²xmb?-=쩓MwԈ{LPPTny{Cw^__߳8<1,^ڕ ݃c@@TPaFQ4՗54iDz5bi|Gn+~O)6PA@>@@TPaFѧ *j)* F} QF * bB5PA@P>@@T@@(TOPP1 ST@@TXQF *`ѧ j)* *t= ST@@B5PPn)* *t3 ST@@j)* *> ST@@TB5PZj)* *j)* *j)* *j)* *j)* *j)* bF]ʃ{ظqcjժs_xǼ"~glܞ={ * [n-֭[W,ڵkחq'K MʟkK.˟;'% b[D]tQݻwT@@ԥ@Nc[:OR+W,a? * .G"?-Ξ= z wy@@T@@k?`@+=Slذ0H5.{zallq<* v%\"X .;^s }4+V&''߀ 5k֔Wq b@M"Tڒ[D#`Aر(T`(jkF8PA@Զ HF@~ I{:6&q T@@mI\,h,-[7Nt]IcԔP;+W,~򓟈FTz!@x| * v,j#E¾}3?'=/9PTZa dzzza4jS_  <8A i4Ą jg T1* _݁ ** ֺ   ."X@/T@@P:;;[SäQvSSS͛  *0uffhՀ}TPT`j$M4hBi$j5kPA@PU@614㤏 &'' SN,Pai4iLͯVkTPT@@P Gg* * j* * #P;@PA@U@TڀE+5_75FzA@U@TڀW#jJTPT@@Py,Ma4?2,Nj6gPA@PU@>V#j3TPG4 @"P9Os *|i{OA@TPPU@T@@T@@PPk ڍ8dyVMOO;&TPT`ěgmjj1 *P#QtŊ%#PA@ԡ _җ-ݻw6֕W^Y~/moPKKS8DA@Ԧɓ=#7p9~9rƋ<öm[o-_~mug]mmܸ}|?ַ^%PSPujĦ; m__Zy{''q^V*}/GPT`8j> KPS#X|,TG>Gb4ezxIZ WW\q*j5O߀ P(ϵчRP; PxyL*jU\ *bR7Puj[ M1JW  cU@Ԫمi1{* .J@M)PG+֊ݬ* Ž * .^@j`4uZ@Z5k8pfhZmK5uiugCfׯ w~OWW "Gʹj]Pk;/~ o%"PKMTNK54S+k6yLjۅ4}"ϧF_-^XR]R!FY5zf#:2vޥ>F6 HC?*0*#P;a* KPcdiSU+B6lVmXk-kN_z#mwTbn֑ͷiPyNuɆZQ>7Z~B@F=* .j@̓b_d'W+5vRV7E@M_OѥvP]ʀ{׋Z9A  P\jP5^vA Ջmw.OX~\K@T@@ T<) IKPc}ϸM*&g7Se z:v1@NǏ4[U@T \5pNk@X.kU@]KqZO-.V@ ydM]k=^P;7;;[LMM\G *B@5OgN)9#P +_r@ 1+/V@cj]ǃoO@X<6x'"zA@U@m*DFZSS+u)9f@Ŵ3vVh7j{]^kF* vPcT@@m+m׿~Nٿۏ/~q1~ᚷƯ竡*\ݶw׽Mlslݿצ֮]L+3,{c{Z}m{T`Xd166V|eDu9@@U@m)pHfV"ӦM:\s|mK/T7B5U׿q7ZOV~ ~o-e@{ʶ :5Fk֊tAV-ng>EY@Coɸm<_lK;Q-._"zz_ *PSPȬSkyj8B$VG ]ZͶ5zx= }m1W/呮s/f'qNF.U@ktFX@މuQBRmmG멦uU7oTPBj|daf1,*GTOֺPzfa,nW}O%!zHU?YzU_6Zѐ\)mk6;굗Vk~ܾ|C޻pgM" Z[\=(Ąc$PA@PQ"PciwRſ7TPT@@P c_G-PA@z *0*5;550rrradi>5?nS+גF:VTPCPcdis14j@VoFF=** qP#tVih#U}PA@P` juKJ׊1_@TPT`(|K#PuMiJ *05_(:%?"i6m6PA@P`jZ4D0K4J4,l{:m6PA@P`j>ҴLpZw5zA@u(I#5OCDuR@߼ysQ/(U? LX4?k<OA@U@Tɀ ** *  * jKbrMVMOO;&TPT`ěgmjj1 *P#QtŊ%#PA@Ԛرc T`jNQPU@T@@sss #OSPU@T@@Pc T@@PP3iS7  ZQq1)** ffggǔ~** ffffDTPU@T@@/bQ[5==TPG-=z` {@MMTPT`$FvTPA@PU@:* |gqAXp/  ׯ/~FoT@@T@@;7F}c֭[T@@T@@ݴiS/~Q4\uUŶmT`j\).411ŅVX"R :j{3h,^@>NMMywAQfff|cm1 :Bpq;T@106* .z׻h"O=Tzt޽o@P :Z֭[W<#Plذ뮻@=~@@^ hmxP-vQ7>hxΜ9 :??_NxL5 _<V#0hPuQիi oM74 *jD]v M0fggɦ랆89{@@TPEXYjUSO N0Dfff5kִO>T@@ =z?^|;`|-R8 0PT/B#qps7_~Y>P}-S'7 چ-[^ziy8ܹ'?Y\}?V<5uPPxRwuWR/۷o/^}U{8N0?p~.+PMgΜ)D|ː.2DT/xPF'|\Dq}v8 ) * v~2Drj~EyNJ|?--e]z;Q^Ѧw( ڭ7x -B̶mۊ;vtrXmYauiLT@@Qj1}XP|. ĉEk;F9SSS-{A@F6!U@e0ą^uԙbŊo^ЖkPA@c6jնw^J@CǏ;F:n޼01* Ә&MHUœ~S^-*Ѧ1R&@@-.GNLLc j_8uTqı:'? 8/IM *”}NA@TPPk@=PA@@@Hk8PA@@@MOO/B=z_PTDQ   #P4?!7ov * z]ހi'"zA@T CP;% *v4 u@T@@PTT@@PA@@@Tԥ233S׼`Xނ U@`jlG <%l&n7778TPA@PZiiFQA@TSSS.-R * 05,m5:)ZTPA@`hB 7є  iZӴi *v4* *:?jlKPA@T!]D*FrG>R  q!t!Yc}PClKuiUTPTvsM:VGEr1+*֭;'yPPA@`Qmܸ񜀺rJPA@Ν; a׮]  iPcӱr=wڪy ąP~s'sŊo]PyD5* @bt%\rN@}׻Uرc!;vlj *,͛77 K/-ʀzȑb#gL?c[:qQ |E /}9TPA@`^F~7PA@^DM֮][Z~}k׮%9MS۹O}TPhH 64"Rѱ>TTPk[ 6mˀDH~ *^eݻ2NNN  @ *v4 u@A4Bj+mNq * z]΀#NhؖvkPA@voGwԳN|^{!ƚLO * ~ 7iqo_{u@=wWwTuX`j\lJ@T/s_?{gqӀ݃)l~x\9PCZtzzh> * (zv""އ>_5Ŏs;ʏ_}ʠZq>`jZ4͛nDy{TQS1.4-?8IҘF*0bPe)PC<6}T0 S>rMF!5>Sc F} $)4 *F} T@@TB5P{ѧ *j)PՏud͎A@B5O/x/>ޮTP(TO~S* QF05knnѧ QF0Pdѧ *j)* *F^i)* *t3 S5XX ӎA@B5 ssso](# *j)=u||9#PA@B5 hP>@@T@@:P>@@T@@P>`Tj\|*~ PsѧJ@m(VXQ$},>?;;TP(TO:33s^4m$BTPaG} (͛77tll<* *;w7M~x_NOEظqcq7k׮-V^}Nz/~† ʟ8fh8jE'''mju #xo߾Xn]qwﶳXf{E]TF8[n-K/.'38u~~~!NMMqQQBT6m*V\Yjժ;۫ 5!֯__>zGsVCӴF#O"bTPaȧG .9R={<'O, swoL>p@@Q1`vUTPA@!t^x8y7`iaAVZ>gP@MYt||~PA@a CutROӁ[?(pqPME@@t~Tz[eZ?@ڵU3sϕ?-o@ O[Q_@jrrq0 °L׏h4{yR`Z?@oeSεQ{)1s1=?F֒im} €+ƁE=1ȧ:4@{#D8VAZ5ISiTiq2/v~W\Y]?FwF|gD! 7`Z?شiSqmI0b˖-e<<} *,4FwɂiF˗^zi@@TPGcgoQkƲ+"Z<8:>>^u$.j*'t9,#/Ҵ]v١#c啸$m=Pq/DÇ dŊo ^Б)DZ -6]iV*zoZ?0 袋_]@/ J4#P7oTPa9_{EFa￿aSټھ}{9mW<š5k)$1&&&? *,tOHXpզCiÆ =(}_-㎡:J5Fڷ *,Ȥ8М)N8Q9sf$=E u?~uPCa԰|8nTX{)Ǵ}9MJ?m۶N<)2;NTX7n,#a@;8_eaw_ 禦LTXNt}Qt PzP+* "9xt}:ֿyf;A`f@U^APiOCL巏T[^$t _(ܹ0: 3n%Fͤ G%o =Ͽկi 6`DbV3;;[0T`0:(F[s*Ћ֭+E$,~^ϕDovtQ81 ,O@HFmTP,ÇMoPC0Pa@^@%TTR/N 3 5Bi7u~~B+Z`0h񶷽m_\|_PԞW,*`7\F9kZM6P ?oVj,w/"r}}vU@ZzTPi"?p4cd˪UFbZ54%,ݍ_IQ@m/WMkv=~ǩfm7 y g#0Z .H`z{޳?z3=b?c^~6F˟sK3/=Wͷ#>qv}[ ܧQDOz,DU@]ѧ,9cPA@e?.FTDZkgXU.1LzbP ͞G^Dx<֚PowoIrZRK'k{FۛOYoL^F!5>辵q'&exzTٔNU1Y6 HٺukUOj>2i)֟1.  2"1.<ҥY:#>,H|Cu|c]5.m_1_KXo{}di4h5֊r [}N:Q~T⩀_0F%V׮ ca.ZɿEkixWkOk~# \R@Pc Yk^TPA@eG;v7걓Qy7->VĠQt7m0FLip:UW5Hz#cs6j4G){Ű$ nFTԺOIFƪ`jf=J־$k_}Jkfh͍8fk&hU/ݿ&ܧQFC? j/KM>o q%Tr~İo}[=X+SD)iQkcjMu'^ѫNvrj~Έf5Vixu`iwqGrʁf7:OF|4~h[CdoiKUG}c~ͷz͖I>f}:J@mA@P)&1T1RD6f؀ *C+NBt{k|EsO1bwϧzy@~.űqs>ۑz۝Im:zj}-Xi;[yNޓV?Z_W>z^|ki\ĩzRP>ylǯGeZP;ٿ<Ѿ>6~_^k>%Z_[moGmS[}|}fu]FnӊZ55:~W4&Yڌx * t;Su"* OZ_[5vO!6}І~Pc Z+5|FX'05`;nw|/RFN@ͷ'AQK&gⱫ>FCmVDmI@P{%J3Q!F*^c^Xl$gW^lvwO%6{Įww};Zv>iuj~_<}#Yڋ 95Dhp}Zq\( jŶ`* tkgkN_Z=臀^59jRvuZ:og t[ˆiQ05R>݀^PګUG6I}r=\Z5^$ * RCl(xZkKP9iZZkZ`Xjuꚤjjuq: Xr TF5 ^P<70aYDPiV*ong }̉ w1.[6vPU_;5_?Ja}ZZ/Sjډ5Q,Pf^[_u$N!FlgZbNcr|Znݺ/{L{3^),CYnZϳQDmA;hj«NΨ"*.|-\u ԸT) 20'&&z'hF.1M@Ojit^_"#]J_j2;;[6ɽg>ųKR));coW1: =\̪Q^|h9jڦx!ZvjO>_>3:{$j5@ۚNמ?~zׯffکtjHogy@@%S#J},9RoU??posN"G?QMv؆V_Fۗ<{XH]k܍Ni^ڕeD][A56c[~Ecq>Lj}Tu>ε^؞-qMĿO׬^W?6H@]1W;EUko *={cjt{1dĦՓ8U@Oڪ'+tT5?iti@wҔ>Nڵ˴~#羷P"c@M-~G8XGtjtM2bvXvi{&=j<}^V*Tu~jtXc8ީ>~*.4CԸm|4~PA@IA\'\8QZ4Q NX笞8_/6 a۝NrZj=WiTOlCkvsr lZ?􁸐TjD]:Sm1bzku$h'Mg;_G+QW@P[q4Mٯ7ʴQ@؟ *}3]?t~''Kzi0l~?P#B@v: uj* !8qa |Z,Kg;^TU@T@@M׏1y)C?i1:]aX_k)" *j\D*.:e_ *,t81]{Z,UrJa Am'vRT q!ZI8^Tv~La15,X´~X|_EvC:ŽQA@:;;D#F h"nb ʒKc ib atevB PC$Ma*X *=QLa9]VB P͛F6PcD' ׯ__\s5CbW_3~gQHPj>jj TC;>& K6]?F?fpZ[c8*jT@@e#NÁ0C`Ż>w *׭[WNC6u:?G? R0 ~qVa ? @ C%냀 *.jժQFB7Y\zŃ>X}?űbmއ>xoE @ DbŊRQX5?T@@0qƕŭ =ˊ 6O:WܯYT@ N1~TPK˵Q_x_CLā5k;v= tЁnlQiPnrUZoi֨}' ,~#nrZjFY ~_^lݺa8gϞumx?\p*jF6Z4>cj^TT~u]w!u˖-ڵkŦM#p ~x饗F%-g;8o~b4-К3'^[p*jԙsFs|jj? *,ӎ:p_}WWΝ;\/qPs)+(򖷜7M*>={ST3I8MEq* G@M4FOFHVQPA@eB_8z)kZu45mKx={<{p'#'Nppm= 5SSS?g7EXQPA@eYڍG-,K/-xCP#_;25EzTw_㝆Y)t/\T@]fZ\р5ܧ뛇ع9_ y7Ç/XM7TNq/74XFn߾ꪫ[nf~,wtk,I8PgЊ~ڗ X;'R??>/~8APx]wսm\ĸ_ܿǍ_ۥxidܯ{WZϟ ǯjo|v5]?0]zc-o_pAgPu0h=U@TPQS~;`uMC;v,6_P֕z꩚qjj ۑ?_5zvN"OÆ ݻ;E ]q-Y8PTU@TPYi19tTSrD0boD|hnnD2?QqUOw|T/>*#ݯ\狏}cŚ5kʯKubd% jؗTgoIGfbLۓqV{1M_S>#6iX#?.HK|?  * ,FH*);<2ƉT~U=L'6)) !gm5^mՓضAio/.b֭ގq?0<i4K>3&i?.%ymkcV.% PP@."% *3^;gZL~_N|fFT'>I`}`[o;ZI[ܶ_NL#q]M׏iA@eqj2 UZ;՚mQ Q<4Tg_4Mڎyu;KTGsmTPA@e8p`@lnn:O}^x4 ꈕF3YѸQZqeds=W\s5?q8=x`q);xPY[6f䑴ߋH̐~6Ԭ$O#B5Pa 7o.GR< 2p?^Ĥ-[W>׾V^NkIRkKNl'換Z#TrͶ)0][o-?]M׏3 *fz<,_q Mommgeu-jPa 4Soi@me`j୊ xqRi8ȑ#ٳgA@`ͧW/8Q4}:zcfTNϯ~V g_CVTU@T@@b1:D8iqe8Ѫ!'a'7ϧ'|rctB<|FI,'UW]UtM嚻L?s挝9t1zUVJ@XX v2j+vt>ejE]U@PPG@Ӆ~^ȟMo_rdyZ5x\Xkjx]p닫ضm[4?#hv< kmq茀**մ*O+XrNbzVdXO'=NӀ]1wXNl"(>tG*j[>W@PT@@PA@#O>gZ\1}rr???dNҕ{tT=| X>rk+VO aQ5KiebXnML׏ }1ClwfYTU@]cccŊ+JSSS@@wvu7Ϋ1cѣn-] 8AJІ2h>)!CATR\! FS*`A@&E.䞐7}=3\sy}~|3TPW@P3E8OXqF{ t]C!sZZR&0󪗍4^7].j ҥ ѸnYcn[Y+PchD>h8Ç Ԁ:5ƦꇐcNP<m_*Pso.+dTR [ 7P\}s=h' ]NeXa2g\HE4cFDcO8`w}@@Z Mq2VV3h@Dۥ-=|ukRZG@PM<)w T3ic.N.~[?K#"y {)N8apE C&鄌 P=G"2s!j<=w~!uS[uj]H?@@߭?v7bt?nLqd0b4ѣG ʀգ !S>XP^3>uR5uuY") »b}ۭn*>O* -V1Qq ]T>P$VOVѰ>bim$mcd  (w7 Eb{'"bw+b /0p8} +0 * +Fa?O?OM];]81l=j@T@@PPYl[/Otd0￿ wuPvT@@PPYjG)]HE7a>D+[ou5?yAPU@T@@evnq£.n=-R\qO<1Ԫcǎ<ms=W@PTX6yr}s L&拓oj{wpO>m<&8 v%\" *ԉ6m*7W^mwŪq_~yxo>9ƲeˊzJ8wtU@TPA G}t&'2+N80͊+G0wb8j PA@>w$-_Gdv{)ON$[ÄÇ f͚u(><PT@@:EL=XczΝS;yxGUqRX;h<=p >'mF@PA@X!?TMbumXm4U8#mqif׮]* *9up ŕW^Y CUc09r`L+W$qq88YfvݻTTt|'K.#D8m;Wpew}`m޼HPTPL]wUu}M$WUznjwLI^a70{]@PA@>Ŋ}mlƱn2LF Bo/r0N$[~PIw6mjTTPldFPw@rNLo]{8vVB&#JvxmUK~ ,K/x{~< s* c[[o5oq_rQ'Zb֭CoCk֬)-*l;;H3V *0w=!+b/~ed 7&qR|pp: Ǎ7X<L 7ihZ  X:|ponذܝ~T+b+*={4?~qN,VPE&NWPwVM@TPaw돍|UV]tQy|҅l۶+>dn־q4m. *Ln1^zi~6?Gޚ$PA@vyLct ssPΧ7̍_e܃o_?>ϔO||xfcT@@>uv7bhmP/첹Exf+qi1J@eγv|xPA@tӿ+gWw_]WZ} 0߉'86**4>Y|PTܹԊk.J@ea=嬭8PTـ:jV4n/kOt^ɭE|{ć^!V* q7!p?hPTPA@ 5bilH4.bkU| o/7z词/* jPQ>O2M+H/_>/EXe[n5  zP&VWVjэ7СCEsq؀l]ܭ: * .+AchVѺ^zuQwXT@@Eՠ]VFwlrvrUJSTPPaljR˪|7ؕJmƊz TPTP$P{> l mePۊێz * qx{'f ް5_?* *,l*#H@1ih׀|F4yPTPaW[2-&hOdͼKzwo+WkSfvjXcfkx~je޸^\..WΩ=OPTPaVէX:ZV" "B](V`#k>E@PA@ ^j)V>o) *Xj),*TOPTPaWZ} ]j)* *L*TO`P>E@PA@ ^j)vէ*0P>ѮB@@&xէ0UV" „BF STTUVhWZ}  S*TOPTPaWZ} ]j)* *L*TO`P>E@PA@ ^j)vէ*0P>ѮB@@&xէ0UV" „BF ST  S*TOP^ *L*TO`P>E@PTUVhWZ}**0P>ѮBU@TPaWZ} ]j)  „BF STPA@ ^j)vէ* :[n kז.yPvbBVmܸq]۷?񏍳//۞ݼyswcc  ҿ;wׯ/-[VCu`4.VZ5wwg+V(6mT:tLKW^]D`Z"oq۶mT@@v%6 c)6"RXz1͕+WsNquי(k-GMCK ,}Xǫ\l! ۼys/|(8vX^x8y 89O>fL/_8p@! ꄊ /x7l ]p ĉ66`lR|3*V?޽{˕  NN`bWNjD&_~3 5ٷzK@PIбfC=TtMfݎ (5\|X9aeU@5>*b۶mml㲝{Ŏ;ʉʹbiժU<fw]"ѣG @@u]ܘQ 8j.byW /p^Hc cS/nypo^=y晹qv߾}* 8۹sggcB .7]==u]S U@TPΖ-[ũSJ1'8Ӹ{!,+V>[獳Ӵ U@TPΦM/6f9] m-[V?O˗/+T@@ujw+1 $I]vOƄ{+8#8zo{l|]@PA@C_|q)Y X8`#{ x!' :AÇ *&uNAiC@TPMꀙӰk!kqk' ɓskэ{PTPMIq&qkьvPTPMIԽ&;`Fa jRԁ0 +T:-R}+ Ns=jR'0 I_/韖&r6kl2T@ۈfR'v|[ߚCcJci|Soo>mV@T@@5{^{\ ^zIkiYGYߏ)^tc jR7RL-ƪڴ%Vԁ{0x.ɿW7}ϘlPPԝ8qE裏M>;PbŊ/~1~`'_4Ƹm6cZPTPM&YLҪ;vEʘ ԁ31. r}cZPդn(V 3)U61) ~je2iIkaƤ3V6]zٮZ>} Ou>X+*&u,igr;q,i*֘ u qiЀ!g-\jxM-ִ&&u`!oX4& zii; Atx9fC { qLZPA@5cP:')4 Dzi M+UউkAVbRɖ0@I8Z}.QCjtIAVZ#?DTc  Ա(W'yX5)'2*^uP&Ր9H'|v5yčǗOx`1 AǤ|ϐ+^AqB淕O4q\o+pո* jRG.x&yl\5MIо}Zw[cqmAwĤ:6Rz都roqBCPc{]A0 I#=FX?'hۅ??knxDu5ZY?vC5c5v~b m إL{aV6|6kT@@u&'u䓎~'?|ev*~:qdry&{0T0֎-׆b5wͮU>]rL1X+* --ʎ^XQ~L"lH&8XuOSǏIkkЮcRאXA>OUgqY@5 &uc/~1qiNzE~&Pyԁv6|wꇕAjWM+mTc *&u)_ҏX*ZjRZWV 3Z=BX:jPU@դn'tvς[ B5?85jRZ=\Nݸo@]cg{uPV@T@@5|ʣm5F,c5\]4n:NlbR 9ȇzbfdu~bcJqP@5 *&uSy~_?t].:E*A&J&yn:V #;;y_]V:zFZٻwo=XKI]xL>'6-kTT}kˣkjbӴ{~4D2#L&Չm uUZj?p _*O׿i8ki1>}xcFC3&UjЌw!qcajPU@uDǏ3dmkO~r^D?Xʯկ~u2O=s͋ko}'n<3I]NߔZjǬC1%^uLqigfUkh~6qtݛoq sbiZPTPM^+}dM~\m1&Di#1Lx 0mzu)=߭ɞՅӰ?qFwkicD1)"j|4Չ>E.q  jRdb*uӸ"'&ZIA;Bo*&qUǖ#:)&k7k)ƣQrLq5w}i|t{vA5k5Bl I`R1&wmq0 *&ulL꺆O7;6kc jRLp Dkc jRLnpw6l0Z@@դI049V6kc jRLϤn!ir6kc jRL_zvibX kTT:`'uNW NöK?O[U@TPT`&u 0閳cxTPA@PiԽoNaz.1***0 :OacU@TPT`&u OͿw'Wz/***0RO,m0Z@@դIN~6|iBAc-`PTP~Rw19]&u Rnc-`PTPM R<Ɇ ZX+ 1}n9㎩}n R;m`* I}{߻=);m>QO m`* I]O~ehqY `ԓ!5q7k]>l9.;* LRKԚ& 2R𮧞z* LM&y~q)TU@I][H÷ٸX zWV:MZ@@դn8' xNBj6p0BoU^c HG< r<-czg^׽3ksOcRW _cc-1X\  -j@vgKLm+.tuεt1Q.n3].n'n\f1ZWz~swנ.twy_e璘u]>8lİ{lٲ۬{,k=NŜԥ?vJXY҇H>J0\z~?L.3L@C?Cϥ9 * Ij?'ʵm@VbEk{P{|Dt~=b1(U_I^ m˿mܶ4Vo{=Ƕ.B1N4F(VNj>t{l{ϭ_Mwn~hKRhL}6Ocv0\kzlbm* ԘF}.~{Rzi5K7u)M1'Oz,FH>x1LIi+x.vdi9zz^un  x~9ߍ^֤P>lW߫=1>kTë0=m4W%on<[a }181}8g<*{C@PTPMniU$~B zmcRug6N^+M ^so7.Ik%x?@6~WJ~.m47痉1{}H0Zao]kVڽc'Lq:Ӈ:<,s;ZP. # y īi2+.<k@bԶ4y@*6̃I=ه BԮj][\媿ߗ\r@U\dq?V?fc] ]s{.iA:> vc5x0u!?Ouw*V\W^}qYV@TPMFxq<^1a ckRTP:vQ޹KЌhZ}O'6z@>C4  MA@9DF@/V')a&&u`g{n\˪ʸ\<~]D])j.{>ݾPTT1mMz@'Tm/ǜ煈uqT .E@[]]kq n,^ewzmez܃G@PTPMFPL~FJs5.~c:A]Ȁ%X+|*a^QLLX;5;kӱK1Oz=դ](jRm9H@w׏(~^'U@PA@5a@Xyf(j>atUȰP*ϻ8-U@]o0c$ԮwW/PIĠAW8>L@um!8<?N * jR7TkV U|hem['6S=VC7k8aT@@ul&u^R }{;vLMytMO'&Dm=o>o~Cx{1٩~^Gz]/W~&mOU_\~;Mv.=ζ4qy,b"K%ύՎ]Ƨ{|u_'mϩmu-קQ<ƶ6uf2TPA@5KشiS9Jl~H56o5Ly~93!⶛izϷߟS1NU_?~sL8{2s5c,DAxoOMq>{x?]Dzxm^U.in  jRԁxmőVqX+* &u`P%* I`Ru& Z@@դ0c-TzIc jRԁej`x.**&uI`* :CPX kT@@T:@@c-`PPM:0ZPu G]vِ|.^~eT@@PPW\Y<6d9rKq #JU@TP'Ě5kkژ|s+6m$9_ P~W\x  NwqY`E]TlݺU@ڠgz⪫PT@@uRl޼m #r7>Ϻ馛TPA@۶m+Ş={}CS;jժ"TPA@f۹s ZOZ\s58;cCҗ=(]}c=& $Yre-Pr-Ž8;r ʏ|# o30b¹[رJ@@`Ť^h Ǐ!g`Ţ㟾* 8ٟwyVկ~U\qpI"07~qhaÆ ޽{Tqg~Gl Zu]:@E?Ja\и~ؿ :xL]t̨ӶI]Ox};)V^=|P(.' }߶ 3/|I4VK}2 A4DEPA@ w]8pK.iЅ#G!_\fcC>}Z@PA@5I|`|x'zNfeˊ_ރ`F<5\i8  Όw ^"fN#,W]uU}vC0mVc_9=zT@PA@$]w]i _^wyg.>|,8W^Y}y?qgy8qqvPԙrСru] a:*nS<է2qe˖kQaJi"*T@@uv1?! S$q뭷vЅcǎټysYӷ~x:MOT@@ufOvq7 _ swuWx'^ӟ>` ?/֭[;rSPA@yk֬)WȘdq|O*vkq.'Jbi{?Pԙ>Q/'ť^Zp Nb1+b`2o\pAǛo} oQm?{xc;* 45V]x{^hz:B0>^zbݺu駟kBDr`1Y]_@TPY#d~ ;wPYI\8tLf59rطo_nOXc]'r4PTjQoFA* *ɷ+T@@TرcGQ q.PA@qyەsv  l/]rJPA@6o|[lPTPmCO?yt׮]* *gŊg>ݴiӼm<'NTPA@`WZxzUW۞|X8TPA@`W9r\eN  * z]PTT@@PA@@@ * *  * * *  * *   * * * * *  * * *^TPA@@@TPPTTTP@@TPA@TPA@TP@@TPA@TPA@PPiiӦb˖-ao]mxPA@}2^|ƍ{ PwQY8KR DE/ KK@ybժU˯_ *L*&jtٲeeT<{sNv2e>۱TU`)*0Pe)f֭b\Kxq~ VZ} 8*|OIǷ˗!Km' UV0V^]vm=mܸ^/cWoZ0+EXqz VZ} bl)NV7.a2<ǒ/ **TO1bb>nHM9c5խP@@&dէC@m S? *0P>`jW4?0a2Kn\j_?r_=@?3]]!=V-_dVxqJTPa*VZ} t㐶tGø^+?b61P#xhm>vo\mB  S1RԮ)v]u5_}Zzvm7)6W^ *0P>`j7u ]g{$Q)*q_TA*0UP>`wᯋv|Ef:qT qvm>zȏc<֭Rr@@&zէk@]̓HU^K\ߕP{cUV6LTUV &N\tq yW"M~>mW{TPaWZ} z.܆ XSM_>=mn@@vէ1'Ur©|Wzt,C]@@nէ1 ڎ?f:J+KcM @@rէ1阡yČظ5_5<#*c6: ԯByubUf8*nܸqvǙ?*8 @:u5VmVܹӶ 0*TOZ1A#Ir/T@MLUXr * S&!8aѠm]F$DDSmoǙ?zUq=t^RT@@]4m͚5ŲeJsNށO|[늵k6m((,c v`z!'Vb|;)~ߔe˖2/>h@vpRHPui/ ;vc$j|UVq7 1N!:j8fT_ӧ1!5V[.RT@@,?䓢LǏ˿XA= s~!@@Ջ*VFcD1 &מ={ؼy f^QHPA@e~e?VO0=Q:_}Y 7 *̋q(tk׮QZ~UHPA@=+V({1 Xٸq fҖ-[B* ꌊivۇpu/l\0s~,X@RTPgȦM ?a fK+60`Lm۶d7RTP޽{O|If%/Rq%ǎc(N&H Xzuq0c/>`q!0fPB X&&yU/xc)*K(I`jQ>l$R* vosէ0~_'x Ֆ-[SPټysL@Ô_~y.he:6l&P=`Io:)SG[0XYVs Iم?}<<#h^:$@vzREǏkO=7<|,o}{vUkV>0I?9NPY ]VRVauEfǐ|z̃5g;P\HsʶM!@@u#lXM2*ͥӺգMSs:A~B}i= :UUǦ2~{#*oZ;5`~T@@]䀚κ%5Oud*t QvuMXɛn;?, NE@We"K@rXugu ~PC@f=Vh_gZj~] ?kz~ZJKԉ  rBni 3U@P#&Ns@XmIzV6Lԉ2Xq9$  P{ sƀ|i*tKn#_:k*:5sw5kNOHcG淑4Np5}-ǿاM+py =6fs5Ա yp^籧RlZYw"|1P ]۞ CcmU Wxf3Zs膴Z.揷g?۬L;7ǰxHvu sՀY~ML+?yQnƍ#5mI m4N*SPS([Kc`Դڮ1[5J7kЀVKDea.I^ÀmuUt!gו qaNx%!?fW]"jR2&*̺uB^OR@ͿkhXe<PuW鲻|וjjxWcrFPߓJ ռ]f3uѶBR}uPI)Yzq>b: kN ϡ^j[: :\ B*  JѦ]Od5ϹԴuj{ {tT-Ԫ.PY߅~#V>Fq;]ϪOn#̟߯o:uSeϷPj{c?` * $vY)G֦vjH'~MJ@;P(j~xa&]BԈN5+pɜ!1oT/iu-z&:x@M}gXO@V"h9.d@XT8ӍW9# * .H@Oӏ-t@m:T? -d@w 5?j7.[ mij~bXZP}مmOk jm@=vXgG煪6˖-!^׏6]y >.9=w1cu}~׻]޽{T?=5 * P_}yO46y PThwtB* J,g:52Mq*?e]zcc@WnFLԀZ}Vuvym!K(Su+::c6M6]nz󿗶?^_]* }_]M$"P[4V ӊ<\źicz٣i"t>* km; B)4$͖{-q[5rz1[5_ߺXnikϴi{{?UkӲ5.0 ^}́ *Ϊ>B?q0PɸalI="Ym7AcX,F@*vwVeTߋO?C B*  L/{zRTPX@=r o}P!TP*`(w * !0m) * @ * T* NT@@P,^-**E,)**) * * -:t` **   *   **  ** $ԃPPT@@T@@PP/~0`s K.-V\)3nb* ˋ{W4f|/֮]+* YE#`_\lܸQ@T@@]~}q׋Fs=ضm dŁ[]tQe<Pԩ`bP<wQP? :]-[VP\wuE.9r@@ulڴꪫ#rnqԞ Tlz*^9s(~Gzb{$u9 /D ?^tM4:u?|jϡzK/4|d9؎ֳ :gZXtS//֭[WOcӧ}"pf4[Y** ˝7oY0znh}cBψP)Ӄgk, )>hd1 ݻ;An}C0pKQ/"i̒p/qR$ zOy  j??txW:.'M4;v̇`98^?u85G@@@!Cc&Zmm +sx}v?4gN  *ccu]'.owƍ>}|x&Ōaq6"`.f~#-VᄏzLui/y,+8 i{ũ9~Mm$[ޞ~PTPgͪUKL0Gڵ>V|}ق)EF@?GX+̺ևPҠR/ɷWfPeźE0oqt{,<6\mc2D✨07lڴKk׶f]ӹ8{ Iycn]P]ṛ {9P\5;<|x}mm9v N*zΉFx`rŠ9Ox@م<՝3YP.y(bY|]f}Tֹ)$sO P=Ӷt>*c?yq%t ψQ09^yLkغukx{>x}g'zHl19:uIft5XNӅʡ|<$oKϟ*֦= * ܱcNJ袋~ů~b߾}z7|3C۞4Nh PۮK Ì\+_NyqSHN9_74? @l_PA@+*mVu]yיojɵaÆbѢE[oeL2~qeu~ٱzirq8>5?%iMgV=_ݲs5 }TP'JwYmwqG'D&<ꄘ?~q9t,^6qnGy^)a6[pbu0䁮|%Pt噩UsZ kf.* Lrt`A5MRRMaxovmZDԵktq*v2|V.480)}SZa@@Y5oyYqS`TPό).t6jCaM-_$*36@@PA@['N(v)@ L={HQpe,QK~Af]Pl6MlͣtYZӈU Dҿc1J@e!w Z̬|G;/aDPx̳Py o:|k߯Pi9=.* ӧO(o.N eǑ#G|fMRxx.4rѲ|J9bpyjȃ4gF/n|ۄuHqQ-T@@;1mf I0٦ 8톟QيylR5qX5~)gv;WlN M3@GPٰۙyT~Q:^"DPxkʈ{k߾}Z:~Ccu8rK,BaUTKA04&_)ʇW=zLŴQKuHqju}ICwM3O랯)O+6}-KBHPٰau޼yv4JL?pkٯWZCHPṮ[f *%LUHPT* *; *T|B* *0u޺UHPA@PT`z=T!@@T FUHPA@նCo0p@RTPA@` :3I*'#_~eC B* *%~w!@@Tf//HԘg *;_hTzvYq1TPA@`TNQrŵ/my)  TCSPA@yNT@@ŽNT@@JhNTPA@-KB8PT^)  SPA@ * PuOp *e7NT@@/)  k)  T9@@TPPTTTP@@TPA@TPA@TP@@TPA@TF@@PPTT@@PA@@@PA@TPA@TP@@TPA@TPA@T8{iG1s@~Up) ,(֬YS?~N߀tٲe OT\uђE.ٶX{+yx֘T+?Xxq3^jG}[fw047|䮙WhmjDo}W3s߫iA@eZgQ4k;F#F8ZP#m>`t"E>O,r /_ޙ9^D q['Ă4TB)O,Q1TBi<5ٞ:Lf #j): SPaP>`g} *t٧L,TOA@B5Ij)0Yf0P>F: S&y٧ Hg} B5T,TOYf #j): SPaP>`g} *t٧L,TOA@B5Ij)0Yf0P>F: S&y٧ Hg} B5T,TOYf #j): SPaP>`g} *t٧L,TOA@B5Ij)0Yf0P>F: S&y٧ Hg} B5ԉe˖bÆ 3WmL;Y ϻҶ1mm۶ sWf͚[nokXhv[jU=O}77.Xxq}wq=">7}w>Wck;{+cѓ=`NWGX cy… _ެ0G~sgMr$% oF=i&cZus4uQDPhD꫋8}̥sWfAv4p7x sBL)gbG5PgO^{͛ˋ{xph˖-+VXs>L'xg?[:uʘV@;yokϞ=ϵ|.q4u뜢N@f{׽` \XdI'N`,ZXv0t7pCgL3Q@5,͛7wv8f0Ilҙ=LZ=XgLk&: ,(z-o80 ƹd|S`ŕW^Ӛ$ݶmۊl0vU\p3;{0bم^X߿zr Yib:T>`qz… {n&8p~ⓟ@/~793ݷo1:Lb7bŊbҥ3;ogϯ|_|kӺP:={t7{!΅|%C,W1Q<g֭[N@ 6t|X s9gTPW,?|Gu֝1gڷ5k+6nh*co˖-Xs<|c=vƘ6.(gڷt)o0 ϸTؾ} c']@x )_HʑPN~ hm]8~ _u]5=t1ڟ  ̈HU0V>3őC\HM/qo.EΝ;l0&v\0~&i`L LĉƵ 0ڀq`L Lѣƴj;'Oh .$1-0ZPlTq# v6Hw4q)cZ`ǵ~v v6Hgi`L  v6 ƴL/şv;q- `G1\yb~0-'L6S~c^q- `G1휎)%]w]_3]@ŸV@vrE+..DTz_pPcvo:E#<{׸V@͉'f`L;y^|řWW3җr|-z狠81^kzݱMGuv55Pl;0ms1;R@PkPl;id@CO?Šg+ wkT;ig=n۶ YK!|x@ 5ź]u{ǧ&ֽˎ(?&5}\ v6 Ӷ٘c}Lж5TPf)9kܺ窊6*?"fS@ Ga3M?¸V@hE@pbRlt65ِ1s2器4y,$issſ|}i B<>ݖʪoߴi1<7nאk 0Pl;0myn6MQ/-ѵpwޮi{Ԉ娚cq,Ѫ9^Ws>qqjg %3.&Uun/^÷sM3VB_ݹKa$5q6eFXn'fu/4̀s v6 -\>4\иO?Ԉ@=w1׀*z #\ƵƵ `G3|3JU3S55OO@#bܿuf@M,wÜ:s v6 -\"fv; ;֝ֈ|&oݩua>G6+0˸ָV@i-~P7@~Gmvm {xF@eHP{mPܖooݭOu%NIDvucb}[69詧L7m&źe0˸ָV@i^C2u?p7ssY:)+fVjG׶UQT0Pot3WMiܤ8U-#=ioAf v4?w6FaT0P!3 P@Phhh+Ww *`\+ C<|* tS'o˵9Na,3Fq;; {bOZAB  j/3P}ϟttZ "_|\dhL6!U@kTubVb ж!9P٢Mh:yiN)ngjf=|?:|_/sjVݧϗ"t-κrL/fuwfZ\ʷ]|n<&k y85i9U[@`t># hŸ2DU7iWu'q*WwǾn3VcG3s^Ӷ { m]mM2"jv4 2qOs<dTո/iΰȜmiRT/1Pԁ!olwM?|c'Y~xLXWu>6M3/XAΏ֧~mLҶHiq,j'ֵ|zKٵk x_6p: c MP"v#J'q|O<ZU@hhòGܖ߷,-kz]^_ q`sM4jAl+ =叉/Vu(yx=m_CkH)sξƵ `L;ҀYxlfˋ iaSE@c)-ُԦfV-;-_um Mѵ*3iYU_תy@M<~? v6 ƴC c-}uHكM65?mP PS m_.oYmρ-泅+ݶS]ο&ܸƵ  Ma-gZ^SKWE6,Pˇݯ*P~ܣf,E:۩i[, v6jG1n%jypק)XyaԺm^5N>$~6z~|Ƶ>|` ƴ|F1ΙYv5,#_<*޴y@=<|mu|bV} Qz]n_m!an3ZP@@9#U^䑫%ZEu* `L;5X%vj%6gGH, v6 ƴC y$}7 nmҥK'&vu@-&)~P֣: *`G1Ԉuga\zC UYu:Aj~o@m:]BۯS]xt.nPPTcڱC_y啞X[ 6[@mZNmjSDHW] m_GȟMD\r<7nQ\@E@P;i2}b6h~ѡ8:6~?W,<5}w;S7ⶸOzuI֬Yyn=s㛾u#Ӵ=6i5ړ\v*Gi>öjYZP40m-fFt.wd1K2"\)6&kcz:s4-'Ba{kzU1.֡z.n)ox-M۲j+}}~C86q{:Nͺ v6 ƴ `f3b\`Lq- `G1-*Ƶv6 ƴ v6 ƴLv@bZ`\+v4`L;ĕc٧ 9x ;iZT;cZ05P|سg70+njG?Xl߾x`Fts=W@PgѢE~o0`^:ҸV@3wnvL~cZ?骅h@?jƍ;cǎ0Vlҹ7Xs>Lox衇i=jL+P'|қ@CCLI.(>}?pqwtO6P{… ;z34-\~^xkz:{ 9jD-[xS7pC͎;tv0lڴK[ӾƴhDX+p`yZS&;G196!(.qVcxw8'p`XxqdɒV;{nO虨8o<sgBPaΝf É+ŕ ~moX@?O7:x9ַU\wu*N1] AƴzNJ.4x㍝{v4`.я~9gbDCEZzuOcZ gE:߸_HOqK._~`+qG1m!`?G1={N@={N:9'bk"cV\ 5TعĹ?Ow^q 7=P;w^;Ġ+7No=ά<1cڅ vޣm/~۵k gɓ';ߌs͝P;sNe]MA70<_|+>n6gtL+)Rt|NϟoL #{,;{7tS/@T݅cuXp[ظqc@8_`fƬ/Rg\k0eF2T@0\0 i`$#G;v 7?~܎uر3NSLQѥ,P'f6jNH:z NI)NQ8fm߾ݛH'[>|Nq٘6sQ?PTLP;x~عĹc veJ({K.%N'NNM#39ydZF * * *  * * ** * *  * * ** ЏxVt#X:vIENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/borg-data-flow.vsd0000644000076500000240000033100014601576577017443 0ustar00twstaffࡱ>  Root EntryRoot EntryF`?np@VisioDocument SummaryInformation( DocumentSummaryInformation8  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~VisioInformation" ՜.+,D՜.+, `ht  D Zeichenblatt-1SProzessEntscheidung1SDynamischer VerbindermmDesigneffekte.1 Umgekehrte geschweifte Klammer ZeichenbltterMaster-Shapes0|_PID_LINKBASE_VPID_ALTERNATENAMES _TemplateIDATC010497201031Oh+'0 X`lx  Microsoft Visio@pgpVisio (TM) Drawing R`RP>w PP>u P>d le !K]oei (fT׀'50"fZOlO~C.ߐE#LQc:U  APn,34%5&6e&9Eaaj6ru8X7?!3mnv?? ?8t= =>URgL-Bak4quBtiQHe9DbT!c#1-r$9Ah\`(*'ut+A4)%%u+H>(>5ah34LQov9V0 H(MmcPr?`8`?=H9D?0 AEN`r$7U3/E/ 4?O@ Q`__J;a/_//\% a$G[A a i%&?8?J?\?n???O?????[!%_w} o-9aD/*jo|ooiAs2qR?QcWOF_cDU/@}_o/^#5=DAt5?=+jt28oFz7@o'9 O!6K]sH{O(Od"<6VHqS]ňƖ`!2PŷyϖO_a re'F^#Tfx0T"ɘP6?5ᰱ !5f| &-!|˰q.бܿ՟HN贁Nk?kGzHIGVAl~J\?u@ ϫ弅.- -L-e(hzyա{}|V̽t?ńxr>a#KH&_y+I@kQӄ\nπϱvAI"0 7AU_)_;_M__]Pp_G/L kk[xKKtV /(A&f,'Gр߳߬iI 0lcF]{3`'a1`Con@ect@rW!@ig&`tz`7P1O*O``H^3D}8AGOAlTBeM,@lMd3Dla`OUP@tR@e;n7Fa9ABO_hO@QT4 Cp5A"_URH@uJ@dn1@8E;ABq__C_H`] _rERSzN@8DJ2OrBN@gReqG` oSe4!`@o&g]1U E=AhouT;aJ@sp@UrN@nP@y7FCAB@ooklML25X%&qvqvqvqvqvqvqv  r1r~M/V+@A`B@cw g;UNb!wIWLVw b !w//+T@yQ#5Gą1UBUUUt4Y,b'@~/@  8C-9  AUBUUUt4Y,b'@~/@ c 8C-9 *7 A"#$t4Y,b'@~/@ lDG A-9 : ;UBUUU'(t4Y,b'@~/@ GX8C-v*7 AU)*U+,-.U/012U3456U789:U;<=>U?@ABUCDEFUGHIJUKLMNOt4(Y,b'@~/@ GC~-lkGn AU+,U-./0U1234U5678U9:;CDEUFGHIUJKLMUNORSUTUVWUXYZ[U\]^_t4/Y,b'@~/@ dC-袅T/AZU^+,U-./0U1689U<=>HU`abcUdefgt4Y,b'@~/@ TFNC>-nG7"AUZ+,U-./0U158HbUcdefghit4Y,b'@~/@ DCKC>-nG7"AUn+,U-./0U1678U;<=>UBGITUZjklmopt4Y,b'@~/@ XC-1/AZU]^rst4Y,b'@~/@ C-v*7 Az+,OtuvwxyT{|}U~UUUUUUUUUUUUUUUUUUt4^Y,b'@~/@ j5C-v( A;XUvUUUUUUt4Y,b'@~/@ bC-9 *7 AUFUUU^t4Y,b'@~/@ >;C-vy*7 At4Y,b'@~/@ DGA-w7 ;"W  t4Y,b'@~/@ 6C-$U*7 A+6t4Y,b'@~/@ ĞC>-tkG7"A+6./t4Y,b'@~/@ 䖍 C>-\kG"7"A+645t4Y,b'@~/@ mC-7AZ+689t4Y,b'@~/@ $C-ܥ7AZ+6:;t4Y,b'@~/@ D=C-wR*7 A+6>?t4Y,b'@~/@ dC-4w*7 AU_,-@At4Y,b'@~/@ C-Dw3*7 AUn+,U-./0U1678U;<=>UBGITUZlmo pY>Yt4Y,b'@~/@ XC-h 7AZU+U,OvwUxyz{U|}~UUUUUUUUU}ijwt47Y,b'@~/@ 7C-9 *7 AR@ PR@-G NR@t.G MR@tGNR@pHR@~ZPR@< IR@*IR@(7PR@TNR@̝2RR@!GNR@.GNR@-GMR@*G_NR@$GIR@#G$IR@TGMR@TGMR@,\NR@NR@ =NR@|NR@#GNRH<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(H<(RE$-GMRE.G[REGiREGwRE~RE REGRE GRE)RERE<.GRET!GRE\-GREL*GRE,$GRE GREG-REG;REGIREdWREԔeREDsRERETRUFDfP h(TPYYBUe@xTVmt(1B~ٟh> @b(P9 Q# BAUF~? ?F\.u?P6 #A >AA mm u` ? 5AAA/ i#F/8&ur#rI$>-yU5 HLa##԰# `0"$Z'1/45 13Vis_PRXY.chm!#5?902q00- `5G)#6367?9 (###&A6/&7 a"# !=$/M=z@C71SE@7366 2$#@C2NoNk?\C*GB@CX_ @w-ab rTw"R24RI'f_BIq$i@A159 MX@G4"]'2qN`6!41fJmoGɊ_T E"d"d2!!2 t2$t3 (^c*5*5 `y8"`K}oS0ten1&2`Bx36Aq%jN`zp%^s Prpzp]sS0nue0mpru6261`_w~cs}!pBAip~5T74SewFnkpipn" atP`SwQ0ml n`VwISEAWIN0TQX]` A7AnPgnE/ISCW0SI1v5dD`zݏPq USpatԐap|3CyEpd.=uM%`zsvsZ1sO;Yia2t0bpgBn ;Kn0*Tg9A)gb1lrs5ZrKa0k]ql=]tWuf0EQ0 Ta) 7qՑȫW2/]rҲ!qoiiA+cqqA@?CR$U!2%c]yW42?()3E%&]b1avy8y22p}NQTWOY0K0NsE0X1OR]IQQ&ȣCΌ4 pC3&ГdvrWߧfl+F"vBd%~Sr3x3%2"w"c % 65HZl~7V3Gm ]ex%䏢rJ`Umr_0RTHKg#%7*H$ 'qIa{a"qaid 1/UF0VFF5^Sw"S%RJV&%MS]T:%'Vs|HSH5Z .V F"cS"g,tYj5m8c11"!س41g!!2X(1T,@2y<%d`V3y8$dפQRRq" HSVuE5<%@UL/^&q-\ -5/y82b'R_3nCuLPPR#.Wя2P"`@@lcDQoaURZiQ7E5/gCA0'(<#Uj6j6bLTްrTcS)Fa# bDF"x4[ O|Z3H^E%nNUO.IQ^EnE  M*MI2O5yN3 jR-pShYgUHU# !v͗)uBGF #w"MB ]Ba\T@oTPUEU5"#ij+UFDfP h(TPYYBUe@xTR9NR[h> @b(P9 Q# BAUF~? ?F\.u?P6 A >AAA AmmG u` ?5AAAAIi yF/8&ur#r$>Fs(??FůbX%%̖@w-:1*5w)4"I#a Iq$񉄰-#5 HL3=l4)Z# AD5 1`Vis_PRXY.chm!#590o23 2 `UA<:%G )C0`EGuI#CG?9 EY2#6J6uO&7 a21A!/=#AECFF B$B##2NNk?! >Wq@a% F#VVVE?5Aa%R*dovh534w"g5koj3g23f5Md& ,5JemnW_"T TU"%2A22AA"LBY3 2(^ E E YH"`K}o3@tenUA&B`RXC_Fv·5j%^ Przǀ]s3@nuE@mǀrUF_B܏6_A`!B]@L'} V6lX-1u. Bj2u2Z2j2#u9r#ULH/MZ1+B#AD5 60`Vis_SE.cTm!#20AD%`>Copy<0wigTt ?DU1f@M)@c<0o+@'ofdBUArX@Aad@iV@n3@ WAl@ef@R@6AtBvBb@hB@lBAJ@=## AoG?9/#n"E&444 7I0#AHB59_lj#"SV6U8*l>(Uhn E /J$&9"(Ln eg5Le#pheo'T ll Js~o/|R12'"U7A%O3W_7xr5(-6 `F2 xqu\J@Q:0b)&`AAs)@E T@xd@Di e i=M]UTP;7͑4U@ !@ү&P lA4u7` u(Jݩ c?EDe347 D    4ũ2qL lvF BP(?7L04 `LineTray sp r{ ncmyz۶&4W` v%P t e n&$4` v%W{ igTt&a` v%Rouy dw"g&Al'2A{Gz?6>5Q?An"/`Fw lo0~/+n"j?|?+5/z!44`STa ow?<*+` 9?& `U 9S5@y0eK} $KXOf@Qs{ 'B $KYO 64`9M*@gy i@i04Ai '.4 `9Dw AcNl:>k$Uhh@2Q2QE,T/JH> 04[_q_[_o!otgU__o_oTEooo,o17>Te\Lsc!2TtM$t7tt ItEtURtUttU  U -3":GqTlan{TARɄքeㄐqJ"4T c 14.m"?7Rpsf0M8@H8 MEJUU8!r5GrK&HZ&H͟@G+G3"?GERml&Jlmyome>mGG-GAUV>6HԂyϿHႻFSP6HiKte (=MQ_'0Uhv$on!3` LDnFxB'3` A@iBݞ"4` C m@bAxB}lewL9WIFE/HU= _6p9yEW49dFT( s>#d9>?B T]@ekVo+$M X]:aGl)+ ]UFDfP h VTB UYuU??Fxha T,aaUQJ\UF BP(?@?F~?$-?P nLel]V^ ] $g]#Q"U"Y*"]>"~#}&&&Or&"u` ?PYu"bl""& M&u"u&u"u"u"u" ,u" ,u"4,u"H,66t#\"u"///$u)a}r 1'u `Fx3N3^3n3~3333333333Vf4bVqfH0φЬ u"Z1qٝܦaٟ333'373Gc ƢEt&EAxÎ;ܢܗ"3@"3."c Ӣpp V/3,S/e p>{/ RfSIA62abZHaLxV3Ǫ *:Jz׊ך@'OgEa@GraĠAQJ`FWO@rF ЀiC}F@`I@)@AEBsZeȓ3U,puӐe(p 4zTcNV:fcfR:bWKBpY!UbSi@(h#FtϿF?@ ЯqRfa+_i&7=akVQhaRkQclQohaR2brlˡF@#I~urW{ 3~@J}O qO Xy`I@Ё`sRUڀfK@ڀbooOlw wKuK`1s8zK@JqyozI@ eoo /u& s!x7SlP;walPe,0kq @Jq.-8r cJqM-Nt 'E1 *dbt+r sQJQLŌH{M$0tb;{O,=CƁS=ԯןƋM2'Kx~{׵am(zD1-?F|!ǓnJ\˟ݟITjU˫ˬ7F:-˫˴YkIՎ_TF BP(?UQ!@>1 Iz{7 Fgcfb@&7 kRka},1ff,1~,1ff,1WVHf312E1kaB12lQ12a712 ,1HfWV3UYkaO} mlQYBaYOB ,1WVHf3ka lQ"(;"FHfWV3Y"]#kaS#" q#lQ]#"]#S#"QocjQWV3,1HfFWVCaHf``xd䰈qGA8rL5k08QL5 Hk0L5k5L5|0V!CTBL5 = |5QQ5&t? : =|5Q-5S47 PQ zqE4Hd7&qn24Dp)TU+0F2o$0jjSRaR9Q#HeM1sB=yIZR1s^lZLªV_4pQL׌$H.5Oa 5. ^fapnfỀ3p` T]`avelShiftH/$xa_d@a}3p`O`gfs`m!*_a@@ VV%Sr V:67sGr:2!7s[r 27.i$3s 1T$y@aP@dvkQXU.q.'2rq⺏y!,0}Mv`n$@qqt7v-ɏxI8cʁl@byf%!r*򊭄!DŽkqԄ .A\.A"rkQ.Xa Xa.IᒟcFpBT}/b.񜗔*r.񞱔1?A?A1˔(Aؔz ..񯑯[Rx a;`"[TBf"< A`B`sc`rdu g`f&+o]`m`t`l`g`l*?`0qH/.:jx +\T`x`bdvq_v!L5~*ϫ16DPᦄ/);\R`Ht%k ` cTfx8wQ02xQᦎuݿ)/&\BanAM`_qx2Ϛ0dP*(O`a`*4FxgyQ2?Wʷշhۯ-K0&4Fb@[mD{ǿD!As 1ϱPGϠYϋ6ѲĢϩ5hA`#^ &AsEy)3UĻɯxAv2`OQnwzT9ߠ)ഉĽBTfAvH`U ta)Yp!3EWLnew)ncbĿ Av9H`R#t6Hl~)99:'@ϟo$tm-t!BT)ங'7İZE9Bs&1)/I4+r/o/Av(* t/% *M{Sa#/?4Vr4F:?^?mAiCOOOO6O=nO Z\9?K8/ ` GAAtPT&pa=rb+ rt*P@?#o54trp8tb T mkqőԁw`3PtquːtcF~ϿF@g п%ukp`?u %Vrq t@V4`AszSPsT3 5jːqi!stgq xʬŕhtbrQ?l !3'󿂱/ cː[яJakr}`Ѡ ː *ː}<9ᙓ @ː^rlA$Q2U lQQ؉uhU+24rH1yɒ^rx@7.?@hzi?wԐH}߈7!c6z#o/|U`a1H_\"p1&WrBdeKTv7.Cԟ@V@)`aGo ,o>oPobo1qyo>TooODow QS+F搞R6);=[v+rrQvq>LUF+q StFUevS߅x$?~&qޗ݅ԯ1).UaO|㱩ǢKT*ϰd4)'ߕr|$<b/WkNȏ%rQ|evʭVlߟU[#Իg.gaxweۿoo1τCϟSCS/e/w/S~eB]v KcVO/GXidU:@ڄE-,/wb/cƻ͟E/?'?*&)BOZlcƄdUO7c*&tG);AEOb1ҿ)M_b.ϛ]!_R8Vb=x]@('_ ݂?z-sT_Na2s/.g(O]Sa5 _;;L7pwrra2wcCB|WV.gWi gU1gtO|{럘p*7 7D g,l;;.o@o*aa Oo0jo2kJΆb`b~Mk 2(s.®R%nd\Sl$Z`@K ay!Tt1K쐴^VQ2CƒUdUpς1aan9Oa7Pp7߈y!X16BòÀJaa WE$1߂!UަQbЀŦ72Ce$6N@¸ջVcIpRbL(aO[Ne4 (ga@# f8 \ 2@dT!bu-(;M_qgֿ`hT4@?F\A`0ΉjM09 Q)(3b8ye/˸?FƣE?Fp5Ys?F5&!Q@Ή( 0̿Bԃa2U0*s3?+0QԂ2 k+ݓ0]'%Qn?Fѡ?FW^bd%?FԊu-Q/c,DJ}?5^I,QI,$~//'% ?F*^Y?F[E?F?|{T?c,|гY,vݥ,-C6*~??b'%OZ`?Fa)P?Fů$_?FPv'͚?c,տ h"lxp_Qҏ,mV}b!)?/L FAOSO+'%`0DLJ?FNqVUO:Oc,~jt7A`,׿p= ,?7d__Kh'% QuU?F`4p?F?FLADX@t]_c,io~JY8F~Z4 qۄV$N@a__'%X/~%l3!?FeY?FJ~ӗ oc,z6>LZ4?kw#awi?vQooj'%'-q?FmUa`2#+?F(%oc,Yo }j GfZ4 ץ,ClL^!UFV{WE?FcJ(c  )H},""3\ \ -Y@qjǻ )1̶ Ւ Ѡn}δS m|ݠaQaCS@Gh4cfUpHi5,)֝F3L&DAPiЧ Z3*#q@8|`rӗze2ZՀ #5.?\zi{B Aȧ#ޯ&&JJ\M @㖿+Ģz`rl%ǨΩ)1π8"4߀ߒƿ./1*Tύrˤ]!c-=( =5-52Z{#.,"~T Ȓ@@ 7ӄ`Ofrsetx%@p6'<g ( "^p7'(] #5GYk}Favorit_en.s] 2DVhz D BASFLO_M/.VS] 2DVhzD XFUNC_M.oVS] 2DVhzD BLOCK_M.VS] 2DVhzD CONE_M.oVS] 2DVhz BASIC_M.VS( I*pnC! qU/Dן_=Q/ן=OSO PrPSRS/)#) ~  !"#$%&+,Otuvwxyz{|}TUUUUUUUUUUUUUUUUUUUUUUUU+ - - - - !- D- E- F- G- H- I- J- K*- L- M- N- t4&d26@~/@ sC-Dvu A T !U"#$%U&UU"#$%&'()*+012*367t4GY,b'@~/@ *uC-Tvʯv A"  !T#$%U&UUJ !#U()*+U0123UBCbcUdefgt4H&d26@~/@ D+$wC-dvx A "#$)+,Ovwxyz{|}~JUUUUU $%QRSTUfgijx}~+t4dY,b'@~/@ ڟixHC-@GyA;X@LFuPR@\FvPR@<'GxPR@*GyQRH<(H<(H<(H<(EFzRE]FzRE*GzREԛzRUC`8\Uu?Ƹ1n@qc7@ LFDTyB uhn0CT w0UFY,b'@F~/@Fx%@@p6@??0!s#?2?/M8"mu ` 4p2lBrBu|IgO7+ P D=2!!" Tp2g(T}DB @=2'Q%7VoBSw2_="@`_{0B'Qp53[CWB(P,~zFF?6Kmo $BU fs#3aGQS3aP` Verbind]bPQTb _` Coe`n]`ct`lbe=5n-o?oP%Uc F_lus`dc`a@r`m`noaaow`h`r`h%oo0!X` B]`srrc`f`ue`Ai}cUC`l ruweAaq Q!Q!"Qa(D/1:!5 <>g2Hu&%rA/r!f=5x %xp5x5xxxu%xtqttqQtUxQQt!!tQt!tt&!&!ttTTttrArAt``xt0!Q taat&"t)At, t$t:!:!"t3,t60tD!4t2gxq2xQAZ"!Pi)jDq ST> , 0!cQ|~Yf!!ex}&!fgrAR~`$% 0!1:!%D!22EHr!$}L.E nBrq1SI[}F_< AL,@P(W@@hl6@@2?@ZV™?SJ[rq$'u}@`FF?uՁ _0b 0pg@VcB}244 417l7r.);M_p=2l1$|<=23{3) `p22tRX`:!BB)y9CQB`)lBQ{Q"GR $    {GR$ RU&TGSQ3U&4ؘ /o.ƒ̀!BpA{Gz?%Q;?{ٱ` Srd ra q&lazیQ"//` Fc`qPB`t`^aa#SRu`q'3 >` XP? "߃1݃1Q-c A`c]`n`s?4*B` )-?Q<`U%5Tbn`pr.1cy;?O<#|!`)-OTK" +S`y`q#2 ;Xfkps]`qH ;YxOP `)Dc`oAaib%`)Mbniri`1[yx2qP?@@@}FP(?QBr#"0Lcbe?"xb $u`,?u' X֑@aV FV '!yDF壮$` URrnbrs" 3 `UaO?K` U ?S.>pesô2;Ta.BbvHzqecka{v%px8)ٱ=TF vw6QQWAAx pU{`"|mH>~_¢㢱!F!!gўrivT,,@*!09~p**4!CP)A_aA; p&s$*܊R t^? @дF@ؕ@*"ev57|!)*4s{ D"4g%5EE1د%'s%wUMew\$8wdZ&RrVQ !RRl+@r˓Ф=WQ0@\Zep@᜹ @\4U>@uBdaq-QBVPiPգnroMu钡$'>t<82<p#ue5+mFvգG/Y"//,/>/{*F`0(p////?o;1[/??+?=?O?P"u???`--wF&cOO*O>N-8Ϧ&8.q(:Lgyӯh䄆{@ƆHQEatt: @@ `@@p8@@q#Y0' grAU޲s@wѤq axMSU &41qldcx*pef|34T޲HDDDUD D D D D%L޲4k )bwrdq0 Auaq(Ѝ`-̀A{Gz?Q?t`K F@l!2*3BG+6`9Ml?@@`uR%L@eD%Wih\Jr` %RAg* `%qxqcdҡa+"bGvсaGāzZsxe~??;CAmEÓ{?O;ȅ_TcbDbD`q`q(aa"qqcsu5O~XO+mCѴ qC(x9mCZUZYOGQS_e_w_ۅRl4K?M?t-1Lc\MRmC뱬bww d(qq"ٴTt,@aau71bp@F5qfu`ogqaEjE␂rZaW+=ruL pu p+ @FbI3l@@2[@XfNgB@0a%dcos¿_=沎@`{񣰁c72Mї%ff@ $֏o@,hŏq3ҘMɇcLyИ בk DLИq'))asυϗϩϻ'9K]o߁ߓ߽R}kf)m5i'@jZV?@Nq:<#@@?@hQ F??,AaIIhI¦IpH;K}_m}‹•Ox?s~PbtʓţUBBPK"`BtBO?S7g*H<@TDWX=A m幅hdhge%1ikakg8qEq D1jZV?@-Wu"@@L&d2?@@i_9]ֿgƿPh"ϱp8o"@tufSv=@`uA `zpArUguq`} ?A% |Ah"Qs0UB ׁ[g ?Eq@0sNaNgkm? tNLC;zsA "Q#x?C&BB?w#TS'&&&U& & & & &&"""d_2 &3& 4\(26"222??22??|Adgw~Oà9AsA{Gz۔?EEQk?@J al`C`l`r2!J" sOE&` S2a`owOCOO` EP@tbrURC`ET@aUPsp@r*`n`y[)_;_:C4a`Ic_TE[B KS`ybڇE*"` IXO`f`e`Z [Y_}1 `EID`Qai`W6a`IM*@gtPibcRQkO5tO(C2q`?\000F B_P(??pUsa* ` Lz`nJ`Net9Mssٜ>* oo @mr{acV31a{An(rTMaAWJ`iv`h&g7ryOaYgu3a _q9r@gu5a$R|@uPd&rt;rsE gua=ba_uZCr t[Q}O8O@A%EjEso?@~@rb?@I5?הߕ? iwwOL$cO9(!TsJ,J/Rş0|o(?5)\.k?֤1@%µA>2Paam ejRB@`mf9SQvo~"_=]@`{i@­,(`%'0cs&\!hSQ+ 2ETJ`x&`OBbcnFRb'gB/3` AIiE.B4` {AmKQx0qEA1kCU@Fy!)9%޷UujCPʰ8rյ}Q^ L{^D>+鍖+m3,@r)8A15 CN~G@@gl6@@8?@U??P(-DT!s!׵%y/ y %my-/TL!7'a9YӬdl?@@лh|mrb[sE%EkE@į@ X `ABu[E_c3B.B9OKO]K!`OqOIOOO(_a_C_U_xy_______ oo-o?oQocouo oFd#bCV>1A2ֲ"A2 6.2_232237 6 6 6 6 6 6 6H6ֲL2L2 R6p3R62!6$}qL߃U`߂‘qłRL%@AaErO OINOO6WgaN _OD_qaOO_O.@Rdvд$3ݿ%9K]oρϓϥǼqu˼6Ep졥hTaq" D" dq+ N 6qs6ᠡᤅ rF Y`yч'M6sFh4@@:@@JRT?@ vHfMnpus`u 9Aeu V`:aj pu:N @@}o=!@@~@@il,-_@)p V`@z3e p .1<]I\n6r5ⲨDΧ%9ؓGa/uw"tNzM@, 2;)2  ݢ83\6L36ܣ.2T`3\\\ \U \ \ \ 523 5#32d gq|w uH4` P"t r(dJ\߾//*/
&(é4*[Z,FtBѦѦѦѦUѦ Ѧ Ѧ Ѧ Ѧe٣]F ] a">q#uq7aF8I[ewYp y,@߂dvߚK*q>qxbr EE2#I4F,@L0 Ap[IOTFL6u`/f:a1:&?#AuWI?h@=br[uLo0uo0{@Ffbu d@2 -?/OAIUOgOyOV,!5S ,! o'o9oRoٰOmӾGuNJ_O*zq @d,?wQ@y"uM0;Bxze_̴_!A2D?ux`*0#۟ XocQuk#?^o.h$uu`Ms&­kYtRDdlrڳH<TDX0ZA0QT5hT Yp8gki*[Oq䆱HMjZV@@2}!@@D*JR?@L&d2?:j$r%?PQu`u[`f@@mQs&#DJ"@@_z^g![!@@}o=]g )p9Q%$ 7Op@z312 ??"pE>1_6K?^?p>$D4U$7WQrs`BsOos+!G$29۰r Cagh"g tN9Qzs&Q B*C&A>CB*B!BB)B4BFFF F F F F0q>C^R4^R!B^R F3F4ZV۲O~g0pr~̀Q;A{Gz۔?UQ?z۰$3 FiWlC`lb2}1" oe&"` Sad`w1oCcJo\oW` *ePu`tbrnf`*eT`a`spu`r`ncykooc4PriokDb pkS`y`ef*B` riX@fps`tg{ v{Y A `riD`8qcҥ`i`g6`riMu`g`ipi`a{K޻S2q@?@@@F BP(?ꙠFva䊀c*0` L`n`~nUx0۱x0ѓy{faj1vEanqaW`5i hw7Ə"ޅlqo9k}?@"ޅpq5vR`u`d g;}1 ޅq=bv*jCmkȍ:(EJ@6?@uT"?@U ob4ɓK*wAwAA^B1T۱QQ۱/ A(>\.5?Ŀ,A @&ATu {0@wAN1$B@޲Ro_=/@`{Q%i6H"kES`'0@ l$u"eT`x `~o"bxnqw"b3` AiDb4` am dqxހa{U ю,wA$ՉX@U"?U@?& 1BH$մ@l%B8{7{L@V 5O&s, lkd`ve4 QQSQ&A"E0ղU5 0+=Oa/28}t߆߿+# r.) Xo))A-Y"?@eo" @@Y!?@fl6?Bˍ+3s}ORJBQUBJCQTCLB&CL&K"B2 1B2 1:Ct&1n#5n?02^AGS)kCMɅCO@CUV*#S64C.6+"S.6.6.6;"S.6.6.6 .6 .6 .6 .6^S6+"hS6 AA")XCj@?@?Fh$E/C4&"K20#U{ _V___ ooCo{ogo_ooiyv{^_oooo_ _BTfxH,#?lGk[EEERu0LWRu_V `Ruo[S%CTbCec1R e 5P?m/6R5f_&!&!!""!!4HZZn"Q!!-U4 oI+!!0$փ/p!R&R㏎?K]oUn8`~b7~?<DmR11!"11"))0!0!4n`"2,@"0"%TF# u`춻(&"bG4aKG2r3]uF `u*`@?6vb'!d@벞@.lϾ%>RTݍ%vl%RNـ->dnq!ߑc&!4 zS>?á׀›H M&8wdnңL/2"P-ՓϦt/S-B&!PtOfB!TDڰ !!pE%Su<`A>`)[!@!j!~u#L&d2jȧ9]ֿ͹ƿA%u6j `u `sAIγP( B!@տ< @ufDZ*`uN``zp.a1`mp5@g. @z3]gy&"K"!!&#%E/ QrmH; >2K/Z3t!#p!/$a 3tN*zs.a "#323&P3&&3&n3&&& & & & &&3&*"3& "3&42:2W+&fD2?02=hE gyOFOOOOOY_9_%_OI_[____ __o<_'o9oKo]oooooooooot%3't/uP(G?Cp^KroaPK-`QAnUr#aTWANىPbfbQ{"e1a9o|@7a,idrez`IOa!i2`BeA5aR*V@u@d`n)sB !&b*QU ea=aG m_ZCCr[mѿэQ֨ED_Ep8?@z`s;@rb?@b4ɓK?yF? mCe 6-]!CAAAA5/gC?=%)Rk?6a=8=P]^+ɓ?Qciܥ8(`'0c&u9A)Q+/eET@xPOeB` LXdnʐ)aWeB3` Ait͍R4` QAm퀧Qx@sΊьq=ACUᑠՊщU<pPѓ9ߚ(ڔWAJ\tԚ^$bACϓչ캂d4W-@Rv$8?bϖϐϺL1+hz+.LW\gy-?`ϳq[!a@&!%1(`#@JRT @@ # @@?@hQ GF?˜`&" [j/+, 1#:#j`//%#҄X$٩*# Xb2&" a" a36ާTadWE@qoکo+38C>ä`eBdOQkJ?*?ڼ޼cHEMATDyOCY rEO!ӷ h9T8\OQHHL@90lZV@@.T" @@<*JR?@L&d2?0T8U?Ppơu0`uL`6W@^9VtS[O$@@eo-Q!Q@@ #7 @uf[;`u``zpʁ)_`)Wpg@@z3VonBZg0$p|E;`ڷ1#Ӥ^0YpӵzR tNkLzsvV :r1CsOӓdvӒbӒ@ctu[wwӒdvUdvdv dv dv dv dvvdȐrbr vF3Bv4r@rJ.rdlg۠0q̀A{Gz?Qڻz?$c FiWlCl2Uq'a" &{R` S@adw'.@W` PYtqrw`uTawspYncy(43Vڟ$( TyP*5r` VXWOfkstK ZYiP K`VD뀮cii62`VUMYgiiHȫ=Ė񑟃2q$?@@@F BP(?G^ a'`KEA`Bn£b^'`b.'a{LVA1Z_Wf5ih7ͿdRL᠎&``꿄9p7I @{R\LP5ZgURludn۵aχϟ{Q LUK`C9KߨXEO Fh4?@0?@r$?@b_4ɓKSZ#$ q qWD*\OTq !(/`X߂u(CAI5\.?俄OF ;=FG# q15bc@(XDB@"l%1|o2_=@@`{!UioD?OY,Y(Oe&"'0$:y2562G;6Tfxjb` Lnʡ/3` Aiҳ .(4` mHxQ-ArAA!UT-rArA qd${.U@"?,7U@?,>1B@2UByhAQ wKα BI٠e0 5Nl LBAYLO#tCA#|e -#?QcuAE#!R:yhBpCE Ys@j Q?@~i+,?@hQ F?yD@CS[`bBcBBCdCCC(BBaCa BF&SBBBB(CFRapYpmpbBbcc !acVRW# dVRxa?o`ba3aCs#_rEsjf3jf4E%VFR|sffRsfUff f f f f%csPvRv @R!%1NyOJTR@S:͏p(`L:pNZ؟̟ڏ &8J\nȯگ@q5~df0 @I2LE*4l+(pZLes8† `:@q5H. ύ2Fx_fդqѹ 0 EJ%Cgt``ntoU hgk UBgzYsY)~ @rHSOD RX!PqPqpZqZq!!QQRR:_0LBlPr},@ bRu0eTF c~u`Sև᾵ꀾRwlRW=rdsuLuԐ@=F^b%I@P@8 B@Kah2LEoB2_=K@`{h1 $!! ͑!LE4O8ZUtup~uqD$l!!l!%hFG$!G!Q!/q^Bd?uTh52 (nu`/))z{}W._/̆'{!_PrR]Tq~q /i(/t"&"$&_ 3?T^\WzO?Ua $z" jFX?$&"o+'u5`??M%6QòO0L5"J.VOhOMP#,4ޑO0L5NCOOM =_0Ly9?m;_M_Nsލw_0L_____.JBFP<HB$ u(Hx}aTD[̱=̿cidqԶ Ū xK 'w"A 8T퐑xB88&nʗ83` Ai}*4` qmtx~ h &Wx ZjZV?@PۗA@n>=?PfAib7^ @2 `@?% B@@a/r!$So C{r_=@`_{/q21l(!;U7 !mlj7a0($n/cU@6EͶOaNֵG N:_O7 q2aXq7 aeOe5 e71X_ɱ`_{A`ckd?fbtcQ/u[#/^ o2h7u7`@oRi7zno73gfa#"$Ua۱oh`1bObt6saQ\Dz!xE6d;>E>>K>U>> > > > >DP;[ "ms!DӬ31KAja@@Fh~6=@3[y7`qP~xj?̀;`A{Gzm?QE?3 -$ F"lH2ذ1` ]S adBw8*` P!r*e1` @ njB`T%sFae&cy¥ o`@  9 > S yH| XOfsܰ DŽ Y}0 `@ D"0c 6e1`@ Mg&iiP1Xu2q8 l?@@@6QTиLeK*b%Wܰi h.c8b% `b%" 1DAE ~DCbu&k I  "cbe?Pm?sCTQ?VW80&AN44XttB4!RѲLR)Q)QB4BqqNzLQLQG?W:Dr9I+3!Q/DXqERBID!RH93ilE!dlE?#YA!UeOwOOW;Qh8S4W9~?74g@/L5oz0 S?a0ow?'3.?@?` 5P Y0tPePA3UW`5TPaPsPaRQy;??3!`PV9?;(2 T;SPyHe&*` V9XOfk@s*PtKK ZKYiOB.R `V9D QcPi 76%`V9MY0gPiB@iP1KJ4џ#2q$P?JdRF BP(?p|rakA݁G+`W` APc2ta8y5%d9ASqAᡓ]_o__TR{%aNS@1ZQb1n@C!Wj0i@hG7R_o_UI~`*o?Q9pRwooKo@UpUQQ`oR* u@d n@eA;RooA%e UUQq`( O:CRy;]JEk p?@|01@r$R?@I'?%?d$^>#U !1OEi#s3U Ja[[*JEOasSLT8    KV#j#K~#4a[e  /////// ??V?B?I[x?????O/,O>OPObOtOOOOOOOO__(_:_L_____~???__-oo$o$OZolooooObOoo-? _DVhC_)/_U@S.$6H`Zl~4l?pM8YJrObT2!L-ReeLWO@qG U_ `_" _83 ̔})C{g Y //A/S/e/w//////P//??,87׀@lqA`j IbbKU U   )P-- 33;^^7A4rg5\k@2?.Jst?V2` AOׁ+:L^'a=VHSEڲhڲ!j/"/4)IH.a/s/'j/5///jeZ/?~/to /ASew 5@b`T29c)bCVh28ΟvM_煒Ju'1+OM+ODbCa_,_>_P_bVbżDbOO_Db_!o3oEoWob_t___ٚ%4X(G'\SAE5TeQSh=se, ,થapfwpoB  b8gpce  ,!zzmlj܈zPj=@cF6GL7@rs7IՔXj!A0.IM4bh'Ǣf(!3DGFaijBk>32lgkۏ폀h Cȡڱį$ڱE18GP # 80s64^ځ 4Erq$#o0@@X2L&?@d2+?u0`u`[W@)D:\؈Nn6f ʳڱ!Jg2ׄI'aEHƾ;'1%~1t J~1hTǻj9 ) tNp0~1zs< x1~1PƗU }h3d7ť$ ~13VֵV֥2bU?̀A{Gz-?տQk?yC` }g2N{`"0/` Pq9rv`չQs}ѣ> DSHy@rOu*s` XOpfeLxD JYYP..zA `FDdp cbrIqOu6`JFMugsc`t-A4-A2bq?@W F BP(?G-A^ba0K`Rn R!A0±wbrSq{zB<z1JOWpiphLwA7}o *,<|3Jtx9'9@A<pE5J*WRWuddr1챓wzA <E=JC)/;/ -AHEޤFRiѯtI%?@"J3}b4ɓK%#s}qetA+e7>B?ƜAA?jE=6@!AXyAyA!Ab/ C/?M(nUm1bU@[UC@@3@U@kqD[Uex )r)8:\r631y1UY@vFίx<@+=OanvjubPO8!?fІa4?E@0_ߋ@`Bjc⹐g ^b'ȿڿé (ω1W?Qcuq#AATAAAAA A A AF2æFӢæ d3u@’142ߔߦ߸J6߿Zl~}/Se 2DVhzў JdR 17>aG%! Acf YRD9Prq .1L,p//$/o/Z/0B//.Rgk)/?1l3OLG'?9?K?]?o??9n???B??AyO)O;OMO_OqOOOOOOO(rO!@DGO%'YҀQ+?_Q]+b_OXh___[s"8#U%&a>_P_Pc?u4OXHr????6_ o_Oo0oBoTofoxooooo&p,VWQWQM?)HWSPж#TRO[_TPz[ ooiI6mjjtj!d06x/-ŕHFo!32bm7n6h!I0Jw2/-v>d#s"7ktooBLHS#H$|aqTH!`0!1uWՕAn8!'$FnYYP|a`qf!HqXT @@x<1@WPkP?Tj ̣u;`u`p`$܃a[Qt:Nj6f K0rH/>TqF@wBPJhuI0Vg 4U 9<U#R0W ,05* Ⱑ0/D9UDDDDQ D D D0JʢWʢ9ʢʢ 7H9RW9R3>Pbtοÿ(:Lyp7IϦ$6HZl~ߐߢߴ =!m߆eڇ` On=VKo#nGY%~ b4ɓK ODI3;$6HZl~/&/_J/\/n///////"dvL(9{e?O"Oy795ROdOOOxOIOOOO __0_B_T_f_x__ "HIi8!YhCkkjq#]t|(A0As-K(\!3E/// ?80?B?T?f?x???????׀S#WQZ@X3I!s@JRT\M@@?@hQ GF?d ps GqARAS;r:s:s!:sA:sb;rC:sؑ ,r'A7 iN?h(?ZqytA%!YA;U/gCQ|qqX!??̄f!\Y$?upoNEU>uOHu`OI^E0NO@\WAG@)>_H[_nBB]V`rR__USPC\4?zЙ_75߁Dr&!/(99>)@S p5UGRuodw n;>h =b`C> /+/_t +˜1Ee M 2'0ƔAn᎛(F FTxM` LnFxm3` A=qih"]4` Q mn x^+ n*a,Ua e1E%L|1('a@"3@Fe2?o6o1BiEe@k=u ,쵘Bw+bQ)Q4zXj|ğ֘T @@Dv}@B?@ܪo'ӿ /<'6%up`u4Qq2vD΂ɏۏ#4ơ!Q<ѳmRdvQߖ|̳ߖAQܿ$F;EJQZË|nBRËU U GrólӽGr"1CUgyߋߝ-?h/czόϙ);M_qBTf7=Om/P3K]{ /V/A/Oaw//.S?@9D'I)/8`?'?9?K?]?o?0;a?9??9QOO)O;OMO_OqOOOOOOOO__%_7_I_[_m_________fa"bu_k=oWoiol;NoM2-t"?[!Cߑ+ PqÁqPDi0_{djZV?@HP&piȧ'9]8SJ\nVk@W QIf1oo!IfooVڒÁq#wϝÁ㓕v2rvvÁ#5GYQj!Á A2rAÃuArգAAA A A A Aixdu9i sBWsfxҿ.Ϫ,ΟPbtϝ0Bt(:L^p߂ߔߦ߸zd@@@ 1^ K.mQ`hMqb P'9zoϓ HZ5G}ߚ~E&f?b4ɓKv&8J\n[us7Yya/+/=/O/a/s////////??'?9?K?]?o?????????O#O@5OGOYOkO}OCPOĨO ,TOD6ňzB?NouDUYؠsPQ,T_$_/C[ouY_k_}____]_vX^3_o#o5oGoQE>CQdh?i¨vgNqu!BoO.OcsoF[OnO r!'yG=Oas}!s2VBs2Vt!1&5!EVBYV!m!wVORVUVV V V V Vf"fBf" f.@RdvGk*N̯ޯ&8J\n4ͬ!Sm ֳӹBS` Apc@9;@-Q!'9xϊo-ϥϲϩ#5xߊk%ߡ߳߭wC3s?s@4lIO|OÎ 4!9Ke_q)!#5GYk} 1CUgy //D/V+|4J?mFdӣ!q/Wq///??(?:?L?^??@ / }y8>:?????1Lmf7HX?GJEOiC]#LC%&/$/O-RqIv'OOO_"]q6S&"JS&YTqd_v___ZWXAY%S6u"cr@&c62(c66U6 6 6 6 66c6u"c6c6oooo%7IO!___!3_Wi{ÏՏ /ASewʟܟFXX=OυدTf(X9Ko޿_ɿXj#5GYk}ϡϳ 0B߄Ql~ߐߢߴ 2DVhz .@Rdv@ߣ/ =1FnD!YYAY A=1  %xCDV]2}It]o4]ϯπ p8?2.Ŏ}++^"ǧ!)]]䈡M0BT~@8q!8w!%M ġ1ј%{ȡjZV?@\M@@?@hQ F? #$AA7#,8" "9&#e#BQ"¾!}/28"23A3L"P3&"u# a#&a#)A%b}IY@q(ҤԨo&ȡf3CT'(T1/%BT36364C%p&B&\"2."26"2666 6 6 6 6$3EB8"EB\"EB."EB 9%#wB2TFH2ԡ"A"A֑ D .O"H\"#cg1̀WQ0A{Gz?6cUQ{?.k.__/` Fpl2f$z/>$/bj2āAAô,νŕϸUy6/?!3K2a+kG12~T86VFPνP>3É`J$Vk?//ϼӼXHbP=ATD^C> ?ENOj=VhkµqϹ|rיX=|A5$ i.jZV?@ҵ}@@L&d2?@@C{Կ:T UĿPeCkV<@u@`u5`CSVMUr,@Gv@ufPр`uN``zpa_ p)eUj4R 3tN<zs74RQ rrraP@5v_*bP@eGsPHwO5vU5v5v 5v 5v 5v 5v]vEarOar*bar gvEP3gv4`rZE`rr5dg0X̀nA{Gz?Qϻx$Af` FiWlClr+z۴Q" υ]&JR` Sa'ow` ߅P*t✲r\у`߅Tʀa\sp*rnyb(qу43'T %SyдPЄ*r` 'XOfH*?@t~<@r?@b4ɓKk?'/?%YPH5WYs@!!LDQbTpAAp/ x(DguQ\.?IQOP1 Da=#!1%#$  6B@2%S1om2_=B@`{/aB\Ui{3XU_\` 2%Q3(l?@C@@5WuLe27f5TFW3ȑ u`o'")WDѤWJGrCuL)u)p@Fb @ q.'V%KT@baT%&W%hE@{EפK((Uhq#0+h !}K2hiA4":?u+l@/u`=O݅F}ìQA$QOA*Lѳ?B\dm!A՟13<^ sލ?LQ61!Pz6aL P{PHF]1PTD#4 1$"+]1Piؤs;kK1єdYQ3't6Jќ1@aK2݄#|T7pxH;p|psnx@qw|3` Acqi_-tR4` u!mex.8H2DQ3A+/$-7/I"R(d/v///.݅$#/#/(aS3f4U VVR0{R0Qfb0ffUf f f f fqctrRtrRtr{Rtr USrb҃vxbӜD䂽1Z-yD_V_H©R)ՑBÙ8A{Gz?QH?#{B`s S@ow:OzCЏ` ]FlPBtrԒC*w` pg{` TB@[As@aB@e.@cyKZl:C4w`扔Tv{B S@y" 0XO f(V@" 0Y# ^vQ `D@֑cBA%6w`M@g@i cD+E ` ί2J)d4l?@@@F BP(?~L*Bee@g@,p N>P `Nȟv+EN֯7 NR<@u@dBg%Fxa 6"ze+@Z5l:qD >a9l`!G28|/A/dp?uz`%/(nduP`/)Pe%4&>.?PU7J1'zeh 0?P88?oB"zV oz1Ozb!%)@m?ea$<EI2 sލ?N4"p#*a%5P ]~OC?'.md!`! >a@o-ё9#P'+R>_)Te_)T_L@jQAqKŕtfo!36bTJ2a2dfOp>RyB2Ok^__wHL@k\!qTDsl *u95щ'\!;Oh@衑!)!\! $`!TAOM,1jZV?@*"@@L&d2?@C{ԿtuĿEArkvk@ѷup`uT`.A }Z)@w 1@0ufo`uN`zp1 )kp_!ćwT@z3ď֎)kb)AXwHqJ$„Yx;tz5CQkA7(Awmm3A%' tNsfQ _1k@ %k@!2o3 U   HrLLL Rro3R4z5Br5Bzu 5%g wl0A{Goz?xQSqQ FlPзݲ" &5` SkpaKdwѿ^` ʵPtrҼ$`ʵTaspncyM\nϼ4cɖx ˼yh*` _XOf'st Y%ߠx_ `DJciL6b`MgKiBQiyBAQzA[2q?@@@F BP(?^3^aKL@-8L@`nK~T΁(ُ@b>搀b{j2lq1UWih/׃7Iw;Y `@D9@5幀U5#RuKEdnЗjqC[ 倌_`xʣCwPA xwftnBB霬tAE4s?@ @r?@I??gdh!!/|t#k7AX2TSqqS/Q{1!A]C(xtRY\.?< qaQ]U@S!aUB2C3L@(QDB@Se!m5>au&o#Xb_=r@`+{Sli'toDo\X m.B'0Ӽbj1bZe>fgµTxxY²` LanPZ²3`ݠsE=4` mJx>AdqQU!u )()tS%$NUt;T^U@PHfR }\wHuA8D:?~GiqA $A*_N`rs"uLKx[TxS!3EWpވ4sil,-goAfڏoAg0BTfxAavAʟܟAA$vb8vALAVvjrjvvv v *v v v!Bģ!bأ+B+avBۏ.HZl~ƿؿϯӟ2DVCz5Gy .@dv߈߬߾*feArial UncodeMiS6?/?`4 R$fSymbol$67fWingds*7 fECalibr@  ?$fSwimunt(S$fPMingLU(w$fMS PGothicj@ $fDotum"|i0@ S$fESylaen  $fEstrangeloU dsaC 9$fEVrind1a8 Q$fEShrutqi| Q$fEM_angl$fETunga<"@ Q$fGSendya*/ (   R$fERavi"o Q$fGDhenu"*/ (  R$fELath#/ R$fEGautmqi | Q$fECordia NewE R$fEArial"*Cx /@~4 R$fMalgun Gothic |w L$fETimes NwRoan*Ax /@4$fDConsla@ `  $,F>EB4F!?.BFO?%BFt?=B$?3B?5B@GBt`@:B䷌@7BT@ABĸA0B4BA1BsA0BA0BA;BB.BdNG3,XN"G3lMzNE3$MNG3NG3N*G3TN$G3 OG3+OG3IO)G3rO'G3iLOG3iLOG3jLOG3jLOG34jLOG3LjLPG3djL)PG3jLAPG3jLYPG3<qPG3XPG3PG3PG3PG3آPG3 QG3 'QG3<AQG3X[QG3uQG3Q&G3QG3ԏQ G3DMQG3dMRG3M-RG3MJRG3fRG3,RG3TR&G3,aGRE3MRG34aGRE3SE3aGBSE3aGFSE3aGJSE3aGNSE3bGRSE3 bGVSE3bGZSE3bG^SE3$bGbSE3,bGfSE34bGjSE3TE3TˆBTE3`FTE3`JTE3`NTE3RT"G3ԱtT G3dT,G3T*G3T.G3>U8G3PU/G34U+G3U.G3kU?G3?V8G34OV5G3D?V9G3lV4G3V0G3܉!W2G3?SW;G3?W9G3W8G3dW/G3D.X:G3hX2G3X/G3LX2G3X2G3-Y=G3jY7G3Y&G3`YE30YE30YE3`YE3`YE30YE30YE30YE31YE3 1YE31YE31YE3$1YE3,1YE341YE3<1ZE3D1ZE3` ZE3L1ZE3`ZE3`ZE3ZG3ؤ6ZG3`QZE3`UZE3$<YZE3<]ZE3<aZE3 <eZE3<iZE3;mZE3;qZE3;uZE3T1yZE3\1}ZE3;ZE3T7ZE3<ZE3䈽ZE3Z6G3Z/G3܈ZE3;ZE3;ZE38[E37[E37 [E3d1[E3l1[E3t1[E3|1[E37[E37"[E3|7&[E3*[E31.[E312[E3Ԉ6[E3̈:[E31>[E31B[E31F[E31J[E31N[E34<R[E3D<V[E3L<Z[E3T<^[E3ˆb[E3Ĉf[E3t7j[E3l7n[E3d7r[E34`Gv[E3`Gz[E3`G~[E3`G[E3`G[E3`G[E3`G[E3`G[E3`G[E3`G[E3`G[E3[G3D[6G3[G3\G3,)\/G3< GX\2G3l<\A3\<\A3Dˆ\E3Lˆ\E3L>\E3T>\E3d>\E3l>\E3t>\E3?\E3?\E3`G\E3aG\E3 aG\E3aG\E3D\6G3\/G3?+]E3k/];G3j]!G3?]E3\]E3d]E3l]E3t]E3|]E3t<]A3|<]A3<]A3<]A3]E3]E3]9G3Jw](G3<^A3< ^A3<$^A3<(^A30,^G3,RK^(G3$s^G3DI^G3TS^(G3A^G3䛍^G3_G3<0_E3<4_E3<8_E3<<_E3<@_E3<D_E3<H_E3<L_E3<P_E3dT_G3<q_E3=u_E3 =y_E3=}_E3=_E3$=_E3,=_E34=_E3<=_E3D=_E3L=_E3T=_E3\=_E3d=_E3l=_E3t=_E3|=_E3=_E3=_E3=_E3=_E3=_E3=_E3=_E3=_E3=_E3=_E3=_E3=_E3=_E3=_E3=_E3=_E3>_E3 >_E3\>_E3>`E3>`E3> `E3> `E3>`E3>`E3>`E3>`E3>!`E3>%`E3>)`E3>-`E3>1`E3>5`E3>9`E3>=`E3?A`E3E`E3I`E3M`E3Q`E3|U`E3Y`E3 ?]`E3?a`E3?e`E3$?i`E3,?m`E34?q`E3?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~]>rFxml:spaceprsrv]rFxmlns:v14htp/'sUcea .i-ro oUf.-o/GUf=e'v=s=]o'200'Eet1 ln]rFxmlns:vdhtp/%scea .i+ro of.+o/i iA/20*c6%et/ \nbQueMarkrEvntsol=BASFLO /cmd#DcUCetd`QueMarkrEvntsol=BASFLO /cWmd#Dc/5pe9"CF14/cmd="wCF14/cmd=2"/ ]C/T _[C/a/a/a/a/a/a/a/a/0a/2a/5a/7a/:a/<a/a/a/a/$C/,ތ%C Ut4Y,b'@~/@ MC-9  W AUPQV%t4 MdC>-dkGx7"AJ@NR@#Gz7RH<(H<(JEߐREd$GR$qFv14:ValidtonZ<>' Prpeges Sh_wIgog}e='0]/E Rue{es D1 NSmTmFuwsiSggm Uc}aoDucgikbmpgfRmnyt,yoK) -{akxdugwgmmyv*mbdYsI.e UTrCnmcCeyK&kvtP*<^!ryV$z#iwn"Ba[LZEkbFl/"rEROL ()1G//*/TutEAD0AGS0C UT0G U D{HI0P S040)y=By1eR?d?v35}:) ?=:?e+?2{t%WQ"~u@1r$/6'Af"g%Z/l+awp$Ik"! DhY/ 'E1rGs|G/?H7O(o2S A?0E0QY0"P)*eOLI0Y Q_SQN 1_[,G|ESS?0R{ASM21F2MRRQR.e700R9R oo0oBoTi1Yd"COgS~1???B7??x111&.t;y0o???3O$6OHOZOlO~OOOOOMaAO__$_6_H_l_~_______"hozoooVoğ֟oooof.r72KsoH4o+HV 0BA-hEDҏ &t"FL1S n9LP{3f"%\DFX)o^ϊϜϮ $6&>Hbhp߸NE $̯~16Հ.@d5AS#tο(:LFpφߘߪ߸$Z0Zl~D*//%a/Omv?\? ??H?2_l?~??s7?/I %@M%dp`Q??@OO$MDVbsREopl5xl/BzAxcO>_O////j|/oh_B__fUxn__$?69/4=6__Zl8ocPoUFw*u?pC4s;oMo_oqo#FEoB btɒa2aAcialO.arhnEAoCcbOAASPƑc[Bcw8t vb_G PUY1 %!3Em9an_?͟+F(Oaiw%r" e4OFOa kr4tO__B(U$ Zl_S=_ߧi`xW,cv^$OϺglG )B"i`tߖ4pXV&_ @ vέJXTbr".@@=~ACFy9FBJðsKkrnAfG@Cz.vATzAtbqllCnA$cBD -fBBtIF鰥LSSqPڡs@RqmzAe$#87@&s~: -p?71//("R`ց܃TՠIҡ %/$),/>#@%=r7V/4DFOS"O4Gs=LVb2a>2&G   g"4FX88 F(t~7@(REu ey zvBPWF P QFN|`S>> BvRoR$~FXp !4Fx)r2'<1J=\d5D8P25`TGOΊ$ B="Ĺ}K&Y zK". P?DU*"DVϑ%>!(7) 7!././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/create_chunker-params.txt0000644000076500000240000001046714601576577021137 0ustar00twstaffAbout borg create --chunker-params ================================== --chunker-params CHUNK_MIN_EXP,CHUNK_MAX_EXP,HASH_MASK_BITS,HASH_WINDOW_SIZE CHUNK_MIN_EXP and CHUNK_MAX_EXP give the exponent N of the 2^N minimum and maximum chunk size. Required: CHUNK_MIN_EXP < CHUNK_MAX_EXP. Defaults: 19 (2^19 == 512KiB) minimum, 23 (2^23 == 8MiB) maximum. Currently it is not supported to give more than 23 as maximum. HASH_MASK_BITS is the number of least-significant bits of the rolling hash that need to be zero to trigger a chunk cut. Recommended: CHUNK_MIN_EXP + X <= HASH_MASK_BITS <= CHUNK_MAX_EXP - X, X >= 2 (this allows the rolling hash some freedom to make its cut at a place determined by the windows contents rather than the min/max. chunk size). Default: 21 (statistically, chunks will be about 2^21 == 2MiB in size) HASH_WINDOW_SIZE: the size of the window used for the rolling hash computation. Default: 4095B Trying it out ============= I backed up a VM directory to demonstrate how different chunker parameters influence repo size, index size / chunk count, compression, deduplication. repo-sm: ~64kiB chunks (16 bits chunk mask), min chunk size 1kiB (2^10B) (these are attic / borg 0.23 internal defaults) repo-lg: ~1MiB chunks (20 bits chunk mask), min chunk size 64kiB (2^16B) repo-xl: 8MiB chunks (2^23B max chunk size), min chunk size 64kiB (2^16B). The chunk mask bits was set to 31, so it (almost) never triggers. This degrades the rolling hash based dedup to a fixed-offset dedup as the cutting point is now (almost) always the end of the buffer (at 2^23B == 8MiB). The repo index size is an indicator for the RAM needs of Borg. In this special case, the total RAM needs are about 2.1x the repo index size. You see index size of repo-sm is 16x larger than of repo-lg, which corresponds to the ratio of the different target chunk sizes. Note: RAM needs were not a problem in this specific case (37GB data size). But just imagine, you have 37TB of such data and much less than 42GB RAM, then you'ld definitely want the "lg" chunker params so you only need 2.6GB RAM. Or even bigger chunks than shown for "lg" (see "xl"). You also see compression works better for larger chunks, as expected. Duplication works worse for larger chunks, also as expected. small chunks ============ $ borg info /extra/repo-sm::1 Command line: /home/tw/w/borg-env/bin/borg create --chunker-params 10,23,16,4095 /extra/repo-sm::1 /home/tw/win Number of files: 3 Original size Compressed size Deduplicated size This archive: 37.12 GB 14.81 GB 12.18 GB All archives: 37.12 GB 14.81 GB 12.18 GB Unique chunks Total chunks Chunk index: 378374 487316 $ ls -l /extra/repo-sm/index* -rw-rw-r-- 1 tw tw 20971538 Jun 20 23:39 index.2308 $ du -sk /extra/repo-sm 11930840 /extra/repo-sm large chunks ============ $ borg info /extra/repo-lg::1 Command line: /home/tw/w/borg-env/bin/borg create --chunker-params 16,23,20,4095 /extra/repo-lg::1 /home/tw/win Number of files: 3 Original size Compressed size Deduplicated size This archive: 37.10 GB 14.60 GB 13.38 GB All archives: 37.10 GB 14.60 GB 13.38 GB Unique chunks Total chunks Chunk index: 25889 29349 $ ls -l /extra/repo-lg/index* -rw-rw-r-- 1 tw tw 1310738 Jun 20 23:10 index.2264 $ du -sk /extra/repo-lg 13073928 /extra/repo-lg xl chunks ========= (borg-env)tw@tux:~/w/borg$ borg info /extra/repo-xl::1 Command line: /home/tw/w/borg-env/bin/borg create --chunker-params 16,23,31,4095 /extra/repo-xl::1 /home/tw/win Number of files: 3 Original size Compressed size Deduplicated size This archive: 37.10 GB 14.59 GB 14.59 GB All archives: 37.10 GB 14.59 GB 14.59 GB Unique chunks Total chunks Chunk index: 4319 4434 $ ls -l /extra/repo-xl/index* -rw-rw-r-- 1 tw tw 327698 Jun 21 00:52 index.2011 $ du -sk /extra/repo-xl/ 14253464 /extra/repo-xl/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/internals-picture.txt0000644000076500000240000000344114601576577020336 0ustar00twstaffBorgBackup from 10.000m ======================= +--------+ +--------+ +--------+ |archive0| |archive1| ... |archiveN| +--------+ +--------+ +--+-----+ | | | | | | | +---+ | | | | | | | +------+-------+ | | | | | /chunk\/chunk\/chunk\... /maybe different chunks lists\ +-----------------------------------------------------------------+ |item list | +-----------------------------------------------------------------+ | +-------------------------------------+--------------+ | | | | | | +-------------+ +-------------+ | |item0 | |item1 | | | - owner | | - owner | | | - size | | - size | ... | - ... | | - ... | | - chunks | | - chunks | +----+--------+ +-----+-------+ | | | +-----+----------------------------+-----------------+ | | | | +-o-----o------------+ | | | | | | /chunk0\/chunk1\ ... /chunkN\ /chunk0\/chunk1\ ... /chunkN'\ +-----------------------------+ +------------------------------+ |file0 | |file0' | +-----------------------------+ +------------------------------+ Thanks to anarcat for drawing the picture! ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/logging.conf0000644000076500000240000000046114601576577016421 0ustar00twstaff[loggers] keys=root [handlers] keys=logfile [formatters] keys=logfile [logger_root] level=NOTSET handlers=logfile [handler_logfile] class=FileHandler level=INFO formatter=logfile args=('borg.log', 'w') [formatter_logfile] format=%(asctime)s %(levelname)s %(message)s datefmt= class=logging.Formatter ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/misc/prune-example.txt0000644000076500000240000001021314601576577017443 0ustar00twstaffborg prune visualized ===================== Assume it is 2016-01-01, today's backup has not yet been made, you have created at least one backup on each day in 2015 except on 2015-12-19 (no backup made on that day), and you started backing up with borg on 2015-01-01. This is what borg prune --keep-daily 14 --keep-monthly 6 --keep-yearly 1 would keep. Backups kept by the --keep-daily rule are marked by a "d" to the right, backups kept by the --keep-monthly rule are marked by a "m" to the right, and backups kept by the --keep-yearly rule are marked by a "y" to the right. Calendar view ------------- 2015 January February March Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 1y 2 3 4 1 1 5 6 7 8 9 10 11 2 3 4 5 6 7 8 2 3 4 5 6 7 8 12 13 14 15 16 17 18 9 10 11 12 13 14 15 9 10 11 12 13 14 15 19 20 21 22 23 24 25 16 17 18 19 20 21 22 16 17 18 19 20 21 22 26 27 28 29 30 31 23 24 25 26 27 28 23 24 25 26 27 28 29 30 31 April May June Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 1 2 3 4 5 1 2 3 1 2 3 4 5 6 7 6 7 8 9 10 11 12 4 5 6 7 8 9 10 8 9 10 11 12 13 14 13 14 15 16 17 18 19 11 12 13 14 15 16 17 15 16 17 18 19 20 21 20 21 22 23 24 25 26 18 19 20 21 22 23 24 22 23 24 25 26 27 28 27 28 29 30 25 26 27 28 29 30 31 29 30m July August September Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 1 2 3 4 5 1 2 1 2 3 4 5 6 6 7 8 9 10 11 12 3 4 5 6 7 8 9 7 8 9 10 11 12 13 13 14 15 16 17 18 19 10 11 12 13 14 15 16 14 15 16 17 18 19 20 20 21 22 23 24 25 26 17 18 19 20 21 22 23 21 22 23 24 25 26 27 27 28 29 30 31m 24 25 26 27 28 29 30 28 29 30m 31m October November December Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 1 2 3 4 1 1 2 3 4 5 6 5 6 7 8 9 10 11 2 3 4 5 6 7 8 7 8 9 10 11 12 13 12 13 14 15 16 17 18 9 10 11 12 13 14 15 14 15 16 17d18d19 20d 19 20 21 22 23 24 25 16 17 18 19 20 21 22 21d22d23d24d25d26d27d 26 27 28 29 30 31m 23 24 25 26 27 28 29 28d29d30d31d 30m List view --------- --keep-daily 14 --keep-monthly 6 --keep-yearly 1 ---------------------------------------------------------------- 1. 2015-12-31 (2015-12-31 kept (2015-12-31 kept 2. 2015-12-30 by daily rule) by daily rule) 3. 2015-12-29 1. 2015-11-30 1. 2015-01-01 (oldest) 4. 2015-12-28 2. 2015-10-31 5. 2015-12-27 3. 2015-09-30 6. 2015-12-26 4. 2015-08-31 7. 2015-12-25 5. 2015-07-31 8. 2015-12-24 6. 2015-06-30 9. 2015-12-23 10. 2015-12-22 11. 2015-12-21 12. 2015-12-20 (no backup made on 2015-12-19) 13. 2015-12-18 14. 2015-12-17 Notes ----- 2015-12-31 is kept due to the --keep-daily 14 rule (because it is applied first), not due to the --keep-monthly or --keep-yearly rule. The --keep-yearly 1 rule does not consider the December 31st backup because it has already been kept due to the daily rule. There are no backups available from previous years, so the --keep-yearly target of 1 backup is not satisfied. Because of this, the 2015-01-01 archive (the oldest archive available) is kept. The --keep-monthly 6 rule keeps Nov, Oct, Sep, Aug, Jul and Jun. December is not considered for this rule, because that backup was already kept because of the daily rule. 2015-12-17 is kept to satisfy the --keep-daily 14 rule - because no backup was made on 2015-12-19. If a backup had been made on that day, it would not keep the one from 2015-12-17. We did not include weekly, hourly, minutely or secondly rules to keep this example simple. They all work in basically the same way. The weekly rule is easy to understand roughly, but hard to understand in all details. If interested, read "ISO 8601:2000 standard week-based year". ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/quickstart.rst0000644000076500000240000005312514601576577016122 0ustar00twstaff.. include:: global.rst.inc .. highlight:: bash .. _quickstart: Quick Start =========== This chapter will get you started with Borg and covers various use cases. A step by step example ---------------------- .. include:: quickstart_example.rst.inc Archives and repositories ------------------------- A *Borg archive* is the result of a single backup (``borg create``). An archive stores a snapshot of the data of the files "inside" it. One can later extract or mount an archive to restore from a backup. *Repositories* are filesystem directories acting as self-contained stores of archives. Repositories can be accessed locally via path or remotely via ssh. Under the hood, repositories contain data blocks and a manifest tracking which blocks are in each archive. If some data hasn't changed from one backup to another, Borg can simply reference an already uploaded data chunk (deduplication). .. _about_free_space: Important note about free space ------------------------------- Before you start creating backups, please make sure that there is *always* a good amount of free space on the filesystem that has your backup repository (and also on ~/.cache). A few GB should suffice for most hard-drive sized repositories. See also :ref:`cache-memory-usage`. Borg doesn't use space reserved for root on repository disks (even when run as root), on file systems which do not support this mechanism (e.g. XFS) we recommend to reserve some space in Borg itself just to be safe by adjusting the ``additional_free_space`` setting (a good starting point is ``2G``):: borg config /path/to/repo additional_free_space 2G If Borg runs out of disk space, it tries to free as much space as it can while aborting the current operation safely, which allows the user to free more space by deleting/pruning archives. This mechanism is not bullet-proof in some circumstances [1]_. If you *really* run out of disk space, it can be hard or impossible to free space, because Borg needs free space to operate - even to delete backup archives. You can use some monitoring process or just include the free space information in your backup log files (you check them regularly anyway, right?). Also helpful: - create a big file as a "space reserve", that you can delete to free space - if you use LVM: use a LV + a filesystem that you can resize later and have some unallocated PEs you can add to the LV. - consider using quotas - use `prune` and `compact` regularly .. [1] This failsafe can fail in these circumstances: - The underlying file system doesn't support statvfs(2), or returns incorrect data, or the repository doesn't reside on a single file system - Other tasks fill the disk simultaneously - Hard quotas (which may not be reflected in statvfs(2)) Important note about permissions -------------------------------- To avoid permissions issues (in your borg repository or borg cache), **always access the repository using the same user account**. If you want to backup files of other users or the operating system, running borg as root likely will be required (otherwise you'ld get `Permission denied` errors). If you only back up your own files, you neither need nor want to run borg as root, just run it as your normal user. For a local repository just always use the same user to invoke borg. For a remote repository: always use e.g. borg@remote_host. You can use this from different local users, the remote user running borg and accessing the repo will always be `borg`. If you need to access a local repository from different users, you can use the same method by using ssh to borg@localhost. Important note about files changing during the backup process ------------------------------------------------------------- Borg does not do anything about the internal consistency of the data it backs up. It just reads and backs up each file in whatever state that file is when Borg gets to it. On an active system, this can lead to two kinds of inconsistency: - By the time Borg backs up a file, it might have changed since the backup process was initiated - A file could change while Borg is backing it up, making the file internally inconsistent If you have a set of files and want to ensure that they are backed up in a specific or consistent state, you must take steps to prevent changes to those files during the backup process. There are a few common techniques to achieve this. - Avoid running any programs that might change the files. - Snapshot files, filesystems, container storage volumes, or logical volumes. LVM or ZFS might be useful here. - Dump databases or stop the database servers. - Shut down virtual machines before backing up their images. - Shut down containers before backing up their storage volumes. For some systems Borg might work well enough without these precautions. If you are simply backing up the files on a system that isn't very active (e.g. in a typical home directory), Borg usually works well enough without further care for consistency. Log files and caches might not be in a perfect state, but this is rarely a problem. For databases, virtual machines, and containers, there are specific techniques for backing them up that do not simply use Borg to backup the underlying filesystem. For databases, check your database documentation for techniques that will save the database state between transactions. For virtual machines, consider running the backup on the VM itself or mounting the filesystem while the VM is shut down. For Docker containers, perhaps docker's "save" command can help. Automating backups ------------------ The following example script is meant to be run daily by the ``root`` user on different local machines. It backs up a machine's important files (but not the complete operating system) to a repository ``~/backup/main`` on a remote server. Some files which aren't necessarily needed in this backup are excluded. See :ref:`borg_patterns` on how to add more exclude options. After the backup this script also uses the :ref:`borg_prune` subcommand to keep only a certain number of old archives and deletes the others. Finally, it uses the :ref:`borg_compact` subcommand to remove deleted objects from the segment files in the repository to preserve disk space. Before running, make sure that the repository is initialized as documented in :ref:`remote_repos` and that the script has the correct permissions to be executable by the root user, but not executable or readable by anyone else, i.e. root:root 0700. You can use this script as a starting point and modify it where it's necessary to fit your setup. Do not forget to test your created backups to make sure everything you need is being backed up and that the ``prune`` command is keeping and deleting the correct backups. :: #!/bin/sh # Setting this, so the repo does not need to be given on the commandline: export BORG_REPO=ssh://username@example.com:2022/~/backup/main # See the section "Passphrase notes" for more infos. export BORG_PASSPHRASE='XYZl0ngandsecurepa_55_phrasea&&123' # some helpers and error handling: info() { printf "\n%s %s\n\n" "$( date )" "$*" >&2; } trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM info "Starting backup" # Backup the most important directories into an archive named after # the machine this script is currently running on: borg create \ --verbose \ --filter AME \ --list \ --stats \ --show-rc \ --compression lz4 \ --exclude-caches \ --exclude 'home/*/.cache/*' \ --exclude 'var/tmp/*' \ \ ::'{hostname}-{now}' \ /etc \ /home \ /root \ /var backup_exit=$? info "Pruning repository" # Use the `prune` subcommand to maintain 7 daily, 4 weekly and 6 monthly # archives of THIS machine. The '{hostname}-*' matching is very important to # limit prune's operation to this machine's archives and not apply to # other machines' archives also: borg prune \ --list \ --glob-archives '{hostname}-*' \ --show-rc \ --keep-daily 7 \ --keep-weekly 4 \ --keep-monthly 6 prune_exit=$? # actually free repo disk space by compacting segments info "Compacting repository" borg compact compact_exit=$? # use highest exit code as global exit code global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit )) global_exit=$(( compact_exit > global_exit ? compact_exit : global_exit )) if [ ${global_exit} -eq 0 ]; then info "Backup, Prune, and Compact finished successfully" elif [ ${global_exit} -eq 1 ]; then info "Backup, Prune, and/or Compact finished with warnings" else info "Backup, Prune, and/or Compact finished with errors" fi exit ${global_exit} Pitfalls with shell variables and environment variables ------------------------------------------------------- This applies to all environment variables you want Borg to see, not just ``BORG_PASSPHRASE``. The short explanation is: always ``export`` your variable, and use single quotes if you're unsure of the details of your shell's expansion behavior. E.g.:: export BORG_PASSPHRASE='complicated & long' This is because ``export`` exposes variables to subprocesses, which Borg may be one of. More on ``export`` can be found in the "ENVIRONMENT" section of the bash(1) man page. Beware of how ``sudo`` interacts with environment variables. For example, you may be surprised that the following ``export`` has no effect on your command:: export BORG_PASSPHRASE='complicated & long' sudo ./yourborgwrapper.sh # still prompts for password For more information, refer to the sudo(8) man page and ``env_keep`` in the sudoers(5) man page. .. Tip:: To debug what your borg process is actually seeing, find its PID (``ps aux|grep borg``) and then look into ``/proc//environ``. .. passphrase_notes: Passphrase notes ---------------- If you use encryption (or authentication), Borg will interactively ask you for a passphrase to encrypt/decrypt the keyfile / repokey. A passphrase should be a single line of text, a trailing linefeed will be stripped. For your own safety, you maybe want to avoid empty passphrases as well extremely long passphrase (much more than 256 bits of entropy). Also avoid passphrases containing non-ASCII characters. Borg is technically able to process all unicode text, but you might get into trouble reproducing the same encoded utf-8 bytes or with keyboard layouts, so better just avoid non-ASCII stuff. If you want to automate, you can alternatively supply the passphrase directly or indirectly using some environment variables. You can directly give a passphrase:: # use this passphrase (use safe permissions on the script!): export BORG_PASSPHRASE='my super secret passphrase' Or ask an external program to supply the passphrase:: # use the "pass" password manager to get the passphrase: export BORG_PASSCOMMAND='pass show backup' # use GPG to get the passphrase contained in a gpg-encrypted file: export BORG_PASSCOMMAND='gpg --decrypt borg-passphrase.gpg' Or read the passphrase from an open file descriptor:: export BORG_PASSPHRASE_FD=42 Using hardware crypto devices (like Nitrokey, Yubikey and others) is not directly supported by borg, but you can use these indirectly. E.g. if your crypto device supports GPG and borg calls ``gpg`` via ``BORG_PASSCOMMAND``, it should just work. .. backup_compression: Backup compression ------------------ The default is lz4 (very fast, but low compression ratio), but other methods are supported for different situations. You can use zstd for a wide range from high speed (and relatively low compression) using N=1 to high compression (and lower speed) using N=22. zstd is a modern compression algorithm and might be preferable over zlib and lzma, except if you need compatibility to older borg versions (< 1.1.4) that did not yet offer zstd.:: $ borg create --compression zstd,N /path/to/repo::arch ~ Other options are: If you have a fast repo storage and you want minimum CPU usage, no compression:: $ borg create --compression none /path/to/repo::arch ~ If you have a less fast repo storage and you want a bit more compression (N=0..9, 0 means no compression, 9 means high compression): :: $ borg create --compression zlib,N /path/to/repo::arch ~ If you have a very slow repo storage and you want high compression (N=0..9, 0 means low compression, 9 means high compression): :: $ borg create --compression lzma,N /path/to/repo::arch ~ You'll need to experiment a bit to find the best compression for your use case. Keep an eye on CPU load and throughput. .. _encrypted_repos: Repository encryption --------------------- You can choose the repository encryption mode at repository creation time:: $ borg init --encryption=MODE PATH For a list of available encryption MODEs and their descriptions, please refer to :ref:`borg_init`. If you use encryption, all data is encrypted on the client before being written to the repository. This means that an attacker who manages to compromise the host containing an encrypted repository will not be able to access any of the data, even while the backup is being made. Key material is stored in encrypted form and can be only decrypted by providing the correct passphrase. For automated backups the passphrase can be specified using the `BORG_PASSPHRASE` environment variable. .. note:: Be careful about how you set that environment, see :ref:`this note about password environments ` for more information. .. warning:: The repository data is totally inaccessible without the key and the key passphrase. Make a backup copy of the key file (``keyfile`` mode) or repo config file (``repokey`` mode) and keep it at a safe place, so you still have the key in case it gets corrupted or lost. Also keep your passphrase at a safe place. You can make backups using :ref:`borg_key_export` subcommand. If you want to print a backup of your key to paper use the ``--paper`` option of this command and print the result, or print this `template`_ if you need a version with QR-Code. A backup inside of the backup that is encrypted with that key/passphrase won't help you with that, of course. In case you lose your repository and the security information, but have an older copy of it to restore from, don't use that later for creating new backups – you would run into security issues (reuse of nonce counter values). It is better to initialize a new Borg repository. See also: :ref:`faq_corrupt_repo` .. _template: paperkey.html .. _remote_repos: Remote repositories ------------------- Borg can initialize and access repositories on remote hosts if the host is accessible using SSH. This is fastest and easiest when Borg is installed on the remote host, in which case the following syntax is used:: $ borg init user@hostname:/path/to/repo Note: please see the usage chapter for a full documentation of repo URLs. Remote operations over SSH can be automated with SSH keys. You can restrict the use of the SSH keypair by prepending a forced command to the SSH public key in the remote server's `authorized_keys` file. This example will start Borg in server mode and limit it to a specific filesystem path:: command="borg serve --restrict-to-path /path/to/repo",restrict ssh-rsa AAAAB3[...] If it is not possible to install Borg on the remote host, it is still possible to use the remote host to store a repository by mounting the remote filesystem, for example, using sshfs:: $ sshfs user@hostname:/path/to /path/to $ borg init /path/to/repo $ fusermount -u /path/to You can also use other remote filesystems in a similar way. Just be careful, not all filesystems out there are really stable and working good enough to be acceptable for backup usage. Restoring a backup ------------------ Please note that we are only describing the most basic commands and options here - please refer to the command reference to see more. For restoring, you usually want to work **on the same machine as the same user** that was also used to create the backups of the wanted files. Doing it like that avoids quite some issues: - no confusion relating to paths - same mapping of user/group names to user/group IDs - no permission issues - you likely already have a working borg setup there, - maybe including a environment variable for the key passphrase (for encrypted repos), - maybe including a keyfile for the repo (not needed for repokey mode), - maybe including a ssh key for the repo server (not needed for locally mounted repos), - maybe including a valid borg cache for that repo (quicker than cache rebuild). The **user** might be: - root (if full backups, backups including system stuff or multiple users' files were made) - some specific user using sudo to execute borg as root - some specific user (if backups of that user's files were made) A borg **backup repository** can be either: - in a local directory (like e.g. a locally mounted USB disk) - on a remote backup server machine that is reachable via ssh (client/server) If the repository is encrypted, you will also need the **key** and the **passphrase** (which is protecting the key). The **key** can be located: - in the repository (**repokey** mode). Easy, this will usually "just work". - in the home directory of the user who did the backup (**keyfile** mode). This may cause a bit more effort: - if you have just lost that home directory and you first need to restore the borg key (e.g. from the separate backup you have made of it or from another user or machine accessing the same repository). - if you first must find out the correct machine / user / home directory (where the borg client was run to make the backups). The **passphrase** for the key has been either: - entered interactively at backup time (not practical if backup is automated / unattended). - acquired via some environment variable driven mechanism in the backup script (look there for BORG_PASSPHRASE, BORG_PASSCOMMAND, etc. and just do it like that). There are **2 ways to restore** files from a borg backup repository: - **borg mount** - use this if: - you don't precisely know what files you want to restore - you don't know which archive contains the files (in the state) you want - you need to look into files / directories before deciding what you want - you need a relatively low volume of data restored - you don't care for restoring stuff that the FUSE mount is not implementing yet (like special fs flags, ACLs) - you have a client with good resources (RAM, CPU, temp. disk space) - you want to rather use some filemanager to restore (copy) files than borg extract shell commands - **borg extract** - use this if: - you precisely know what you want (repo, archive, path) - you need a high volume of files restored (best speed) - you want a as-complete-as-it-gets reproduction of file metadata (like special fs flags, ACLs) - you have a client with low resources (RAM, CPU, temp. disk space) Example with **borg mount**: :: # open a new, separate terminal (this terminal will be blocked until umount) # now we find out the archive names we have in the repo: borg list /mnt/backup/borg_repo # mount one archive from a borg repo: borg mount /mnt/backup/borg_repo::myserver-system-2019-08-11 /mnt/borg # alternatively, mount all archives from a borg repo (slower): borg mount /mnt/backup/borg_repo /mnt/borg # it may take a while until you will see stuff in /mnt/borg. # now use another terminal or file browser and look into /mnt/borg. # when finished, umount to unlock the repo and unblock the terminal: borg umount /mnt/borg Example with **borg extract**: :: # borg extract always extracts into current directory and that directory # should be empty (borg does not support transforming a non-empty dir to # the state as present in your backup archive). mkdir borg_restore cd borg_restore # now we find out the archive names we have in the repo: borg list /mnt/backup/borg_repo # we could find out the archive contents, esp. the path layout: borg list /mnt/backup/borg_repo::myserver-system-2019-08-11 # we extract only some specific path (note: no leading / !): borg extract /mnt/backup/borg_repo::myserver-system-2019-08-11 path/to/extract # alternatively, we could fully extract the archive: borg extract /mnt/backup/borg_repo::myserver-system-2019-08-11 # now move the files to the correct place... Difference when using a **remote borg backup server**: It is basically all the same as with the local repository, but you need to refer to the repo using a ``ssh://`` URL. In the given example, ``borg`` is the user name used to log into the machine ``backup.example.org`` which runs ssh on port ``2222`` and has the borg repo in ``/path/to/repo``. Instead of giving a FQDN or a hostname, you can also give an IP address. As usual, you either need a password to log in or the backup server might have authentication set up via ssh ``authorized_keys`` (which is likely the case if unattended, automated backups were done). :: borg mount ssh://borg@backup.example.org:2222/path/to/repo /mnt/borg # or borg extract ssh://borg@backup.example.org:2222/path/to/repo ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/quickstart_example.rst.inc0000644000076500000240000000525214601576577020403 0ustar00twstaff1. Before a backup can be made a repository has to be initialized:: $ borg init --encryption=repokey /path/to/repo 2. Backup the ``~/src`` and ``~/Documents`` directories into an archive called *Monday*:: $ borg create /path/to/repo::Monday ~/src ~/Documents 3. The next day create a new archive called *Tuesday*:: $ borg create --stats /path/to/repo::Tuesday ~/src ~/Documents This backup will be a lot quicker and a lot smaller since only new never before seen data is stored. The ``--stats`` option causes Borg to output statistics about the newly created archive such as the amount of unique data (not shared with other archives):: ------------------------------------------------------------------------------ Archive name: Tuesday Archive fingerprint: bd31004d58f51ea06ff735d2e5ac49376901b21d58035f8fb05dbf866566e3c2 Time (start): Tue, 2016-02-16 18:15:11 Time (end): Tue, 2016-02-16 18:15:11 Duration: 0.19 seconds Number of files: 127 ------------------------------------------------------------------------------ Original size Compressed size Deduplicated size This archive: 4.16 MB 4.17 MB 26.78 kB All archives: 8.33 MB 8.34 MB 4.19 MB Unique chunks Total chunks Chunk index: 132 261 ------------------------------------------------------------------------------ 4. List all archives in the repository:: $ borg list /path/to/repo Monday Mon, 2016-02-15 19:14:44 Tuesday Tue, 2016-02-16 19:15:11 5. List the contents of the *Monday* archive:: $ borg list /path/to/repo::Monday drwxr-xr-x user group 0 Mon, 2016-02-15 18:22:30 home/user/Documents -rw-r--r-- user group 7961 Mon, 2016-02-15 18:22:30 home/user/Documents/Important.doc ... 6. Restore the *Monday* archive by extracting the files relative to the current directory:: $ borg extract /path/to/repo::Monday 7. Delete the *Monday* archive (please note that this does **not** free repo disk space):: $ borg delete /path/to/repo::Monday 8. Recover disk space by compacting the segment files in the repo:: $ borg compact /path/to/repo .. Note:: Borg is quiet by default (it works on WARNING log level). You can use options like ``--progress`` or ``--list`` to get specific reports during command execution. You can also add the ``-v`` (or ``--verbose`` or ``--info``) option to adjust the log level to INFO to get other informational messages. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/support.rst0000644000076500000240000000400714601576577015437 0ustar00twstaff.. _support: Support ======= Support and Services -------------------- Please see https://www.borgbackup.org/ for free and paid support and service options. .. _security-contact: Security -------- In case you discover a security issue, please use this contact for reporting it privately and please, if possible, use encrypted E-Mail: Thomas Waldmann GPG Key Fingerprint: 6D5B EF9A DD20 7580 5747 B70F 9F88 FB52 FAF7 B393 The public key can be fetched from any GPG keyserver, but be careful: you must use the **full fingerprint** to check that you got the correct key. Verifying signed releases ------------------------- `Releases `_ are signed with the same GPG key and a .asc file is provided for each binary. To verify a signature, the public key needs to be known to GPG. It can be imported into the local keystore from a keyserver with the fingerprint:: gpg --recv-keys "6D5B EF9A DD20 7580 5747 B70F 9F88 FB52 FAF7 B393" If GPG successfully imported the key, the output should include (among other things):: ... gpg: Total number processed: 1 ... To verify for example the signature of the borg-linux64 binary:: gpg --verify borg-linux64.asc GPG outputs if it finds a good signature. The output should look similar to this:: gpg: Signature made Sat 30 Dec 2017 01:07:36 PM CET using RSA key ID 51F78E01 gpg: Good signature from "Thomas Waldmann " gpg: aka "Thomas Waldmann " gpg: aka "Thomas Waldmann " gpg: aka "Thomas Waldmann " gpg: WARNING: This key is not certified with a trusted signature! gpg: There is no indication that the signature belongs to the owner. Primary key fingerprint: 6D5B EF9A DD20 7580 5747 B70F 9F88 FB52 FAF7 B393 Subkey fingerprint: 2F81 AFFB AB04 E11F E8EE 65D4 243A CFA9 51F7 8E01 If you want to make absolutely sure that you have the right key, you need to verify it via another channel and assign a trust-level to it. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1711734324.138968 borgbackup-1.2.8/docs/usage/0000755000076500000240000000000014601577064014264 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/benchmark.rst0000644000076500000240000000004414601576577016756 0ustar00twstaff.. include:: benchmark_crud.rst.inc ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/benchmark_crud.rst.inc0000644000076500000240000001021114601576577020540 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_benchmark_crud: borg benchmark crud ------------------- .. code-block:: none borg [common options] benchmark crud [options] REPOSITORY PATH .. only:: html .. class:: borg-options-table +-------------------------------------------------------+----------------+----------------------------------------------+ | **positional arguments** | +-------------------------------------------------------+----------------+----------------------------------------------+ | | ``REPOSITORY`` | repository to use for benchmark (must exist) | +-------------------------------------------------------+----------------+----------------------------------------------+ | | ``PATH`` | path were to create benchmark input data | +-------------------------------------------------------+----------------+----------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+----------------+----------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY repository to use for benchmark (must exist) PATH path were to create benchmark input data :ref:`common_options` | Description ~~~~~~~~~~~ This command benchmarks borg CRUD (create, read, update, delete) operations. It creates input data below the given PATH and backups this data into the given REPO. The REPO must already exist (it could be a fresh empty repo or an existing repo, the command will create / read / update / delete some archives named borg-benchmark-crud\* there. Make sure you have free space there, you'll need about 1GB each (+ overhead). If your repository is encrypted and borg needs a passphrase to unlock the key, use:: BORG_PASSPHRASE=mysecret borg benchmark crud REPO PATH Measurements are done with different input file sizes and counts. The file contents are very artificial (either all zero or all random), thus the measurement results do not necessarily reflect performance with real data. Also, due to the kind of content used, no compression is used in these benchmarks. C- == borg create (1st archive creation, no compression, do not use files cache) C-Z- == all-zero files. full dedup, this is primarily measuring reader/chunker/hasher. C-R- == random files. no dedup, measuring throughput through all processing stages. R- == borg extract (extract archive, dry-run, do everything, but do not write files to disk) R-Z- == all zero files. Measuring heavily duplicated files. R-R- == random files. No duplication here, measuring throughput through all processing stages, except writing to disk. U- == borg create (2nd archive creation of unchanged input files, measure files cache speed) The throughput value is kind of virtual here, it does not actually read the file. U-Z- == needs to check the 2 all-zero chunks' existence in the repo. U-R- == needs to check existence of a lot of different chunks in the repo. D- == borg delete archive (delete last remaining archive, measure deletion + compaction) D-Z- == few chunks to delete / few segments to compact/remove. D-R- == many chunks to delete / many segments to compact/remove. Please note that there might be quite some variance in these measurements. Try multiple measurements and having a otherwise idle machine (and network, if you use it).././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/borgfs.rst0000644000076500000240000000004614601576577016310 0ustar00twstaff:orphan: .. include:: borgfs.rst.inc ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/borgfs.rst.inc0000644000076500000240000003737514601576577017077 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_borgfs: borg borgfs ----------- .. code-block:: none borg [common options] borgfs [options] REPOSITORY_OR_ARCHIVE MOUNTPOINT [PATH...] .. only:: html .. class:: borg-options-table +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``REPOSITORY_OR_ARCHIVE`` | repository/archive to mount | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``MOUNTPOINT`` | where to mount filesystem | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``PATH`` | paths to extract; patterns are supported | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-V``, ``--version`` | show version number and exit | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-f``, ``--foreground`` | stay in foreground, do not daemonize | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-o`` | Extra mount options | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Archive filters** — Archive filters can be applied to repository targets. | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-P PREFIX``, ``--prefix PREFIX`` | only consider archive names starting with this prefix. | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-a GLOB``, ``--glob-archives GLOB`` | only consider archive names matching the glob. sh: rules apply, see "borg help patterns". ``--prefix`` and ``--glob-archives`` are mutually exclusive. | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--sort-by KEYS`` | Comma-separated list of sorting keys; valid keys are: timestamp, name, id; default is: timestamp | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--first N`` | consider first N archives after other filters were applied | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--last N`` | consider last N archives after other filters were applied | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Exclusion options** | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-e PATTERN``, ``--exclude PATTERN`` | exclude paths matching PATTERN | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--exclude-from EXCLUDEFILE`` | read exclude patterns from EXCLUDEFILE, one per line | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--pattern PATTERN`` | experimental: include/exclude paths matching PATTERN | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--patterns-from PATTERNFILE`` | experimental: read include/exclude patterns from PATTERNFILE, one per line | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--strip-components NUMBER`` | Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY_OR_ARCHIVE repository/archive to mount MOUNTPOINT where to mount filesystem PATH paths to extract; patterns are supported optional arguments -V, --version show version number and exit -f, --foreground stay in foreground, do not daemonize -o Extra mount options :ref:`common_options` | Archive filters -P PREFIX, --prefix PREFIX only consider archive names starting with this prefix. -a GLOB, --glob-archives GLOB only consider archive names matching the glob. sh: rules apply, see "borg help patterns". ``--prefix`` and ``--glob-archives`` are mutually exclusive. --sort-by KEYS Comma-separated list of sorting keys; valid keys are: timestamp, name, id; default is: timestamp --first N consider first N archives after other filters were applied --last N consider last N archives after other filters were applied Exclusion options -e PATTERN, --exclude PATTERN exclude paths matching PATTERN --exclude-from EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line --pattern PATTERN experimental: include/exclude paths matching PATTERN --patterns-from PATTERNFILE experimental: read include/exclude patterns from PATTERNFILE, one per line --strip-components NUMBER Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. Description ~~~~~~~~~~~ This command mounts an archive as a FUSE filesystem. This can be useful for browsing an archive or restoring individual files. Unless the ``--foreground`` option is given the command will run in the background until the filesystem is ``umounted``. The command ``borgfs`` provides a wrapper for ``borg mount``. This can also be used in fstab entries: ``/path/to/repo /mnt/point fuse.borgfs defaults,noauto 0 0`` To allow a regular user to use fstab entries, add the ``user`` option: ``/path/to/repo /mnt/point fuse.borgfs defaults,noauto,user 0 0`` For mount options, see the fuse(8) manual page. Additional mount options supported by borg: - versions: when used with a repository mount, this gives a merged, versioned view of the files in the archives. EXPERIMENTAL, layout may change in future. - allow_damaged_files: by default damaged files (where missing chunks were replaced with runs of zeros by borg check ``--repair``) are not readable and return EIO (I/O error). Set this option to read such files. The BORG_MOUNT_DATA_CACHE_ENTRIES environment variable is meant for advanced users to tweak the performance. It sets the number of cached data chunks; additional memory usage can be up to ~8 MiB times this number. The default is the number of CPU cores. When the daemonized process receives a signal or crashes, it does not unmount. Unmounting in these cases could cause an active rsync or similar process to unintentionally delete data. When running in the foreground ^C/SIGINT unmounts cleanly, but other signals or crashes do not.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/break-lock.rst.inc0000644000076500000240000000350314601576577017611 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_break-lock: borg break-lock --------------- .. code-block:: none borg [common options] break-lock [options] [REPOSITORY] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+----------------+-----------------------------------------+ | **positional arguments** | +-------------------------------------------------------+----------------+-----------------------------------------+ | | ``REPOSITORY`` | repository for which to break the locks | +-------------------------------------------------------+----------------+-----------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+----------------+-----------------------------------------+ .. raw:: html .. only:: latex REPOSITORY repository for which to break the locks :ref:`common_options` | Description ~~~~~~~~~~~ This command breaks the repository and cache locks. Please use carefully and only while no borg process (on any machine) is trying to access the Cache or the Repository.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/change-passphrase.rst.inc0000644000076500000240000000331414601576577021173 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_change-passphrase: borg change-passphrase ---------------------- .. code-block:: none borg [common options] change-passphrase [options] [REPOSITORY] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+----------------+--+ | **positional arguments** | +-------------------------------------------------------+----------------+--+ | | ``REPOSITORY`` | | +-------------------------------------------------------+----------------+--+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+----------------+--+ .. raw:: html .. only:: latex REPOSITORY :ref:`common_options` | Description ~~~~~~~~~~~ The key files used for repository encryption are optionally passphrase protected. This command can be used to change this passphrase. Please note that this command only changes the passphrase, but not any secret protected by it (like e.g. encryption/MAC keys or chunker seed). Thus, changing the passphrase after passphrase and borg key got compromised does not protect future (nor past) backups to the same repository.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/check.rst0000644000076500000240000000003314601576577016077 0ustar00twstaff.. include:: check.rst.inc ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/check.rst.inc0000644000076500000240000004161714601576577016664 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_check: borg check ---------- .. code-block:: none borg [common options] check [options] [REPOSITORY_OR_ARCHIVE] .. only:: html .. class:: borg-options-table +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``REPOSITORY_OR_ARCHIVE`` | repository or archive to check consistency of | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--repository-only`` | only perform repository checks | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--archives-only`` | only perform archives checks | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--verify-data`` | perform cryptographic archive data integrity verification (conflicts with ``--repository-only``) | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--repair`` | attempt to repair any inconsistencies found | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--save-space`` | work slower, but using less space | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--max-duration SECONDS`` | do only a partial repo check for max. SECONDS seconds (Default: unlimited) | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **Archive filters** — Archive filters can be applied to repository targets. | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-P PREFIX``, ``--prefix PREFIX`` | only consider archive names starting with this prefix. (deprecated) | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-a GLOB``, ``--glob-archives GLOB`` | only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see "borg help patterns". | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--sort-by KEYS`` | Comma-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--first N`` | consider first N archives after other filters were applied | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--last N`` | consider last N archives after other filters were applied | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY_OR_ARCHIVE repository or archive to check consistency of optional arguments --repository-only only perform repository checks --archives-only only perform archives checks --verify-data perform cryptographic archive data integrity verification (conflicts with ``--repository-only``) --repair attempt to repair any inconsistencies found --save-space work slower, but using less space --max-duration SECONDS do only a partial repo check for max. SECONDS seconds (Default: unlimited) :ref:`common_options` | Archive filters -P PREFIX, --prefix PREFIX only consider archive names starting with this prefix. (deprecated) -a GLOB, --glob-archives GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see "borg help patterns". --sort-by KEYS Comma-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp --first N consider first N archives after other filters were applied --last N consider last N archives after other filters were applied Description ~~~~~~~~~~~ The check command verifies the consistency of a repository and its archives. It consists of two major steps: 1. Checking the consistency of the repository itself. This includes checking the segment magic headers, and both the metadata and data of all objects in the segments. The read data is checked by size and CRC. Bit rot and other types of accidental damage can be detected this way. Running the repository check can be split into multiple partial checks using ``--max-duration``. When checking a remote repository, please note that the checks run on the server and do not cause significant network traffic. 2. Checking consistency and correctness of the archive metadata and optionally archive data (requires ``--verify-data``). This includes ensuring that the repository manifest exists, the archive metadata chunk is present, and that all chunks referencing files (items) in the archive exist. This requires reading archive and file metadata, but not data. To cryptographically verify the file (content) data integrity pass ``--verify-data``, but keep in mind that this requires reading all data and is hence very time consuming. When checking archives of a remote repository, archive checks run on the client machine because they require decrypting data and therefore the encryption key. Both steps can also be run independently. Pass ``--repository-only`` to run the repository checks only, or pass ``--archives-only`` to run the archive checks only. The ``--max-duration`` option can be used to split a long-running repository check into multiple partial checks. After the given number of seconds the check is interrupted. The next partial check will continue where the previous one stopped, until the full repository has been checked. Assuming a complete check would take 7 hours, then running a daily check with ``--max-duration=3600`` (1 hour) would result in one full repository check per week. Doing a full repository check aborts any previous partial check; the next partial check will restart from the beginning. With partial repository checks you can run neither archive checks, nor enable repair mode. Consequently, if you want to use ``--max-duration`` you must also pass ``--repository-only``, and must not pass ``--archives-only``, nor ``--repair``. **Warning:** Please note that partial repository checks (i.e. running it with ``--max-duration``) can only perform non-cryptographic checksum checks on the segment files. A full repository check (i.e. without ``--max-duration``) can also do a repository index check. Enabling partial repository checks excepts archive checks for the same reason. Therefore partial checks may be useful with very large repositories only where a full check would take too long. The ``--verify-data`` option will perform a full integrity verification (as opposed to checking the CRC32 of the segment) of data, which means reading the data from the repository, decrypting and decompressing it. It is a complete cryptographic verification and hence very time consuming, but will detect any accidental and malicious corruption. Tamper-resistance is only guaranteed for encrypted repositories against attackers without access to the keys. You can not use ``--verify-data`` with ``--repository-only``. About repair mode +++++++++++++++++ The check command is a readonly task by default. If any corruption is found, Borg will report the issue and proceed with checking. To actually repair the issues found, pass ``--repair``. .. note:: ``--repair`` is a **POTENTIALLY DANGEROUS FEATURE** and might lead to data loss! This does not just include data that was previously lost anyway, but might include more data for kinds of corruption it is not capable of dealing with. **BE VERY CAREFUL!** Pursuant to the previous warning it is also highly recommended to test the reliability of the hardware running Borg with stress testing software. This especially includes storage and memory testers. Unreliable hardware might lead to additional data loss. It is highly recommended to create a backup of your repository before running in repair mode (i.e. running it with ``--repair``). Repair mode will attempt to fix any corruptions found. Fixing corruptions does not mean recovering lost data: Borg can not magically restore data lost due to e.g. a hardware failure. Repairing a repository means sacrificing some data for the sake of the repository as a whole and the remaining data. Hence it is, by definition, a potentially lossy task. In practice, repair mode hooks into both the repository and archive checks: 1. When checking the repository's consistency, repair mode will try to recover as many objects from segments with integrity errors as possible, and ensure that the index is consistent with the data stored in the segments. 2. When checking the consistency and correctness of archives, repair mode might remove whole archives from the manifest if their archive metadata chunk is corrupt or lost. On a chunk level (i.e. the contents of files), repair mode will replace corrupt or lost chunks with a same-size replacement chunk of zeroes. If a previously zeroed chunk reappears, repair mode will restore this lost chunk using the new chunk. Lastly, repair mode will also delete orphaned chunks (e.g. caused by read errors while creating the archive). Most steps taken by repair mode have a one-time effect on the repository, like removing a lost archive from the repository. However, replacing a corrupt or lost chunk with an all-zero replacement will have an ongoing effect on the repository: When attempting to extract a file referencing an all-zero chunk, the ``extract`` command will distinctly warn about it. The FUSE filesystem created by the ``mount`` command will reject reading such a "zero-patched" file unless a special mount option is given. As mentioned earlier, Borg might be able to "heal" a "zero-patched" file in repair mode, if all its previously lost chunks reappear (e.g. via a later backup). This is achieved by Borg not only keeping track of the all-zero replacement chunks, but also by keeping metadata about the lost chunks. In repair mode Borg will check whether a previously lost chunk reappeared and will replace the all-zero replacement chunk by the reappeared chunk. If all lost chunks of a "zero-patched" file reappear, this effectively "heals" the file. Consequently, if lost chunks were repaired earlier, it is advised to run ``--repair`` a second time after creating some new backups.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/common-options.rst.inc0000644000076500000240000000324714601576577020565 0ustar00twstaff-h, --help show this help message and exit --critical work on log level CRITICAL --error work on log level ERROR --warning work on log level WARNING (default) --info, -v, --verbose work on log level INFO --debug enable debug output, work on log level DEBUG --debug-topic TOPIC enable TOPIC debugging (can be specified multiple times). The logger path is borg.debug. if TOPIC is not fully qualified. -p, --progress show progress information --iec format using IEC units (1KiB = 1024B) --log-json Output one JSON object per log line instead of formatted text. --lock-wait SECONDS wait at most SECONDS for acquiring a repository/cache lock (default: 1). --bypass-lock Bypass locking mechanism --show-version show/log the borg version --show-rc show/log the return code (rc) --umask M set umask to M (local only, default: 0077) --remote-path PATH use PATH as borg executable on the remote (default: "borg") --remote-ratelimit RATE deprecated, use ``--upload-ratelimit`` instead --upload-ratelimit RATE set network upload rate limit in kiByte/s (default: 0=unlimited) --remote-buffer UPLOAD_BUFFER deprecated, use ``--upload-buffer`` instead --upload-buffer UPLOAD_BUFFER set network upload buffer size in MiB. (default: 0=no buffer) --consider-part-files treat part files like normal files (e.g. to list/extract them) --debug-profile FILE Write execution profile in Borg format into FILE. For local use a Python-compatible file can be generated by suffixing FILE with ".pyprof". --rsh RSH Use this command to connect to the 'borg serve' process (default: 'ssh') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/compact.rst0000644000076500000240000000037114601576577016455 0ustar00twstaff.. include:: compact.rst.inc Examples ~~~~~~~~ :: # compact segments and free repo disk space $ borg compact /path/to/repo # same as above plus clean up 17byte commit-only segments $ borg compact --cleanup-commits /path/to/repo ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/compact.rst.inc0000644000076500000240000001012014601576577017216 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_compact: borg compact ------------ .. code-block:: none borg [common options] compact [options] [REPOSITORY] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+-------------------------+----------------------------------------------------------------+ | **positional arguments** | +-------------------------------------------------------+-------------------------+----------------------------------------------------------------+ | | ``REPOSITORY`` | repository to compact | +-------------------------------------------------------+-------------------------+----------------------------------------------------------------+ | **optional arguments** | +-------------------------------------------------------+-------------------------+----------------------------------------------------------------+ | | ``--cleanup-commits`` | cleanup commit-only 17-byte segment files | +-------------------------------------------------------+-------------------------+----------------------------------------------------------------+ | | ``--threshold PERCENT`` | set minimum threshold for saved space in PERCENT (Default: 10) | +-------------------------------------------------------+-------------------------+----------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+-------------------------+----------------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY repository to compact optional arguments --cleanup-commits cleanup commit-only 17-byte segment files --threshold PERCENT set minimum threshold for saved space in PERCENT (Default: 10) :ref:`common_options` | Description ~~~~~~~~~~~ This command frees repository space by compacting segments. Use this regularly to avoid running out of space - you do not need to use this after each borg command though. It is especially useful after deleting archives, because only compaction will really free repository space. borg compact does not need a key, so it is possible to invoke it from the client or also from the server. Depending on the amount of segments that need compaction, it may take a while, so consider using the ``--progress`` option. A segment is compacted if the amount of saved space is above the percentage value given by the ``--threshold`` option. If omitted, a threshold of 10% is used. When using ``--verbose``, borg will output an estimate of the freed space. After upgrading borg (server) to 1.2+, you can use ``borg compact --cleanup-commits`` to clean up the numerous 17byte commit-only segments that borg 1.1 did not clean up due to a bug. It is enough to do that once per repository. After cleaning up the commits, borg will also do a normal compaction. See :ref:`separate_compaction` in Additional Notes for more details.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/config.rst0000644000076500000240000000075014601576577016275 0ustar00twstaff.. include:: config.rst.inc .. note:: The repository & cache config files are some of the only directly manipulable parts of a repository that aren't versioned or backed up, so be careful when making changes\! Examples ~~~~~~~~ :: # find cache directory $ cd ~/.cache/borg/$(borg config /path/to/repo id) # reserve some space $ borg config /path/to/repo additional_free_space 2G # make a repo append-only $ borg config /path/to/repo append_only 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/config.rst.inc0000644000076500000240000001015014601576577017040 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_config: borg config ----------- .. code-block:: none borg [common options] config [options] [REPOSITORY] [NAME] [VALUE] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+----------------------+----------------------------------------+ | **positional arguments** | +-------------------------------------------------------+----------------------+----------------------------------------+ | | ``REPOSITORY`` | repository to configure | +-------------------------------------------------------+----------------------+----------------------------------------+ | | ``NAME`` | name of config key | +-------------------------------------------------------+----------------------+----------------------------------------+ | | ``VALUE`` | new value for key | +-------------------------------------------------------+----------------------+----------------------------------------+ | **optional arguments** | +-------------------------------------------------------+----------------------+----------------------------------------+ | | ``-c``, ``--cache`` | get and set values from the repo cache | +-------------------------------------------------------+----------------------+----------------------------------------+ | | ``-d``, ``--delete`` | delete the key from the config file | +-------------------------------------------------------+----------------------+----------------------------------------+ | | ``-l``, ``--list`` | list the configuration of the repo | +-------------------------------------------------------+----------------------+----------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+----------------------+----------------------------------------+ .. raw:: html .. only:: latex REPOSITORY repository to configure NAME name of config key VALUE new value for key optional arguments -c, --cache get and set values from the repo cache -d, --delete delete the key from the config file -l, --list list the configuration of the repo :ref:`common_options` | Description ~~~~~~~~~~~ This command gets and sets options in a local repository or cache config file. For security reasons, this command only works on local repositories. To delete a config value entirely, use ``--delete``. To list the values of the configuration file or the default values, use ``--list``. To get and existing key, pass only the key name. To set a key, pass both the key name and the new value. Keys can be specified in the format "section.name" or simply "name"; the section will default to "repository" and "cache" for the repo and cache configs, respectively. By default, borg config manipulates the repository config file. Using ``--cache`` edits the repository cache's config file instead.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/create.rst0000644000076500000240000000761114601576577016276 0ustar00twstaff.. include:: create.rst.inc Examples ~~~~~~~~ :: # Backup ~/Documents into an archive named "my-documents" $ borg create /path/to/repo::my-documents ~/Documents # same, but list all files as we process them $ borg create --list /path/to/repo::my-documents ~/Documents # Backup ~/Documents and ~/src but exclude pyc files $ borg create /path/to/repo::my-files \ ~/Documents \ ~/src \ --exclude '*.pyc' # Backup home directories excluding image thumbnails (i.e. only # /home//.thumbnails is excluded, not /home/*/*/.thumbnails etc.) $ borg create /path/to/repo::my-files /home \ --exclude 'sh:home/*/.thumbnails' # Backup the root filesystem into an archive named "root-YYYY-MM-DD" # use zlib compression (good, but slow) - default is lz4 (fast, low compression ratio) $ borg create -C zlib,6 --one-file-system /path/to/repo::root-{now:%Y-%m-%d} / # Backup onto a remote host ("push" style) via ssh to port 2222, # logging in as user "borg" and storing into /path/to/repo $ borg create ssh://borg@backup.example.org:2222/path/to/repo::{fqdn}-root-{now} / # Backup a remote host locally ("pull" style) using sshfs $ mkdir sshfs-mount $ sshfs root@example.com:/ sshfs-mount $ cd sshfs-mount $ borg create /path/to/repo::example.com-root-{now:%Y-%m-%d} . $ cd .. $ fusermount -u sshfs-mount # Make a big effort in fine granular deduplication (big chunk management # overhead, needs a lot of RAM and disk space, see formula in internals # docs - same parameters as borg < 1.0 or attic): $ borg create --chunker-params buzhash,10,23,16,4095 /path/to/repo::small /smallstuff # Backup a raw device (must not be active/in use/mounted at that time) $ borg create --read-special --chunker-params fixed,4194304 /path/to/repo::my-sdx /dev/sdX # Backup a sparse disk image (must not be active/in use/mounted at that time) $ borg create --sparse --chunker-params fixed,4194304 /path/to/repo::my-disk my-disk.raw # No compression (none) $ borg create --compression none /path/to/repo::arch ~ # Super fast, low compression (lz4, default) $ borg create /path/to/repo::arch ~ # Less fast, higher compression (zlib, N = 0..9) $ borg create --compression zlib,N /path/to/repo::arch ~ # Even slower, even higher compression (lzma, N = 0..9) $ borg create --compression lzma,N /path/to/repo::arch ~ # Only compress compressible data with lzma,N (N = 0..9) $ borg create --compression auto,lzma,N /path/to/repo::arch ~ # Use short hostname, user name and current time in archive name $ borg create /path/to/repo::{hostname}-{user}-{now} ~ # Similar, use the same datetime format that is default as of borg 1.1 $ borg create /path/to/repo::{hostname}-{user}-{now:%Y-%m-%dT%H:%M:%S} ~ # As above, but add nanoseconds $ borg create /path/to/repo::{hostname}-{user}-{now:%Y-%m-%dT%H:%M:%S.%f} ~ # Backing up relative paths by moving into the correct directory first $ cd /home/user/Documents # The root directory of the archive will be "projectA" $ borg create /path/to/repo::daily-projectA-{now:%Y-%m-%d} projectA # Use external command to determine files to archive # Use --paths-from-stdin with find to only backup files less than 1MB in size $ find ~ -size -1000k | borg create --paths-from-stdin /path/to/repo::small-files-only # Use --paths-from-command with find to only backup files from a given user $ borg create --paths-from-command /path/to/repo::joes-files -- find /srv/samba/shared -user joe # Use --paths-from-stdin with --paths-delimiter (for example, for filenames with newlines in them) $ find ~ -size -1000k -print0 | borg create \ --paths-from-stdin \ --paths-delimiter "\0" \ /path/to/repo::smallfiles-handle-newline ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/create.rst.inc0000644000076500000240000012150014601576577017040 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_create: borg create ----------- .. code-block:: none borg [common options] create [options] ARCHIVE [PATH...] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``ARCHIVE`` | name of archive to create (must be also a valid directory name) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``PATH`` | paths to archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-n``, ``--dry-run`` | do not create a backup archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-s``, ``--stats`` | print statistics for the created archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--list`` | output verbose list of items (files, dirs, ...) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--filter STATUSCHARS`` | only display items with the given status characters (see description) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--json`` | output stats as JSON. Implies ``--stats``. | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--no-cache-sync`` | experimental: do not synchronize the cache. Implies not using the files cache. | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--stdin-name NAME`` | use NAME in archive for stdin data (default: 'stdin') | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--stdin-user USER`` | set user USER in archive for stdin data (default: 'root') | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--stdin-group GROUP`` | set group GROUP in archive for stdin data (default: 'wheel') | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--stdin-mode M`` | set mode to M in archive for stdin data (default: 0660) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--content-from-command`` | interpret PATH as command and store its stdout. See also section Reading from stdin below. | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--paths-from-stdin`` | read DELIM-separated list of paths to backup from stdin. All control is external: it will back up all files given - no more, no less. | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--paths-from-command`` | interpret PATH as command and treat its output as ``--paths-from-stdin`` | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--paths-delimiter DELIM`` | set path delimiter for ``--paths-from-stdin`` and ``--paths-from-command`` (default: ``\n``) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Include/Exclude options** | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-e PATTERN``, ``--exclude PATTERN`` | exclude paths matching PATTERN | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--exclude-from EXCLUDEFILE`` | read exclude patterns from EXCLUDEFILE, one per line | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--pattern PATTERN`` | include/exclude paths matching PATTERN | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--patterns-from PATTERNFILE`` | read include/exclude patterns from PATTERNFILE, one per line | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--exclude-caches`` | exclude directories that contain a CACHEDIR.TAG file (http://www.bford.info/cachedir/spec.html) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--exclude-if-present NAME`` | exclude directories that are tagged by containing a filesystem object with the given NAME | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--keep-exclude-tags`` | if tag objects are specified with ``--exclude-if-present``, don't omit the tag objects themselves from the backup archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--exclude-nodump`` | exclude files flagged NODUMP | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Filesystem options** | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-x``, ``--one-file-system`` | stay in the same file system and do not store mount points of other file systems - this might behave different from your expectations, see the description below. | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--numeric-owner`` | deprecated, use ``--numeric-ids`` instead | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--numeric-ids`` | only store numeric user and group identifiers | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--noatime`` | do not store atime into archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--atime`` | do store atime into archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--noctime`` | do not store ctime into archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--nobirthtime`` | do not store birthtime (creation date) into archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--nobsdflags`` | deprecated, use ``--noflags`` instead | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--noflags`` | do not read and store flags (e.g. NODUMP, IMMUTABLE) into archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--noacls`` | do not read and store ACLs into archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--noxattrs`` | do not read and store xattrs into archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--sparse`` | detect sparse holes in input (supported only by fixed chunker) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--files-cache MODE`` | operate files cache in MODE. default: ctime,size,inode | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--read-special`` | open and read block and char device files as well as FIFOs as if they were regular files. Also follows symlinks pointing to these kinds of files. | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Archive options** | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--comment COMMENT`` | add a comment text to the archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--timestamp TIMESTAMP`` | manually specify the archive creation date/time (UTC, yyyy-mm-ddThh:mm:ss format). Alternatively, give a reference file/directory. | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-c SECONDS``, ``--checkpoint-interval SECONDS`` | write checkpoint every SECONDS seconds (Default: 1800) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--chunker-params PARAMS`` | specify the chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, HASH_MASK_BITS, HASH_WINDOW_SIZE). default: buzhash,19,23,21,4095 | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-C COMPRESSION``, ``--compression COMPRESSION`` | select compression algorithm, see the output of the "borg help compression" command for details. | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex ARCHIVE name of archive to create (must be also a valid directory name) PATH paths to archive optional arguments -n, --dry-run do not create a backup archive -s, --stats print statistics for the created archive --list output verbose list of items (files, dirs, ...) --filter STATUSCHARS only display items with the given status characters (see description) --json output stats as JSON. Implies ``--stats``. --no-cache-sync experimental: do not synchronize the cache. Implies not using the files cache. --stdin-name NAME use NAME in archive for stdin data (default: 'stdin') --stdin-user USER set user USER in archive for stdin data (default: 'root') --stdin-group GROUP set group GROUP in archive for stdin data (default: 'wheel') --stdin-mode M set mode to M in archive for stdin data (default: 0660) --content-from-command interpret PATH as command and store its stdout. See also section Reading from stdin below. --paths-from-stdin read DELIM-separated list of paths to backup from stdin. All control is external: it will back up all files given - no more, no less. --paths-from-command interpret PATH as command and treat its output as ``--paths-from-stdin`` --paths-delimiter DELIM set path delimiter for ``--paths-from-stdin`` and ``--paths-from-command`` (default: ``\n``) :ref:`common_options` | Include/Exclude options -e PATTERN, --exclude PATTERN exclude paths matching PATTERN --exclude-from EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line --pattern PATTERN include/exclude paths matching PATTERN --patterns-from PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line --exclude-caches exclude directories that contain a CACHEDIR.TAG file (http://www.bford.info/cachedir/spec.html) --exclude-if-present NAME exclude directories that are tagged by containing a filesystem object with the given NAME --keep-exclude-tags if tag objects are specified with ``--exclude-if-present``, don't omit the tag objects themselves from the backup archive --exclude-nodump exclude files flagged NODUMP Filesystem options -x, --one-file-system stay in the same file system and do not store mount points of other file systems - this might behave different from your expectations, see the description below. --numeric-owner deprecated, use ``--numeric-ids`` instead --numeric-ids only store numeric user and group identifiers --noatime do not store atime into archive --atime do store atime into archive --noctime do not store ctime into archive --nobirthtime do not store birthtime (creation date) into archive --nobsdflags deprecated, use ``--noflags`` instead --noflags do not read and store flags (e.g. NODUMP, IMMUTABLE) into archive --noacls do not read and store ACLs into archive --noxattrs do not read and store xattrs into archive --sparse detect sparse holes in input (supported only by fixed chunker) --files-cache MODE operate files cache in MODE. default: ctime,size,inode --read-special open and read block and char device files as well as FIFOs as if they were regular files. Also follows symlinks pointing to these kinds of files. Archive options --comment COMMENT add a comment text to the archive --timestamp TIMESTAMP manually specify the archive creation date/time (UTC, yyyy-mm-ddThh:mm:ss format). Alternatively, give a reference file/directory. -c SECONDS, --checkpoint-interval SECONDS write checkpoint every SECONDS seconds (Default: 1800) --chunker-params PARAMS specify the chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, HASH_MASK_BITS, HASH_WINDOW_SIZE). default: buzhash,19,23,21,4095 -C COMPRESSION, --compression COMPRESSION select compression algorithm, see the output of the "borg help compression" command for details. Description ~~~~~~~~~~~ This command creates a backup archive containing all files found while recursively traversing all paths specified. Paths are added to the archive as they are given, that means if relative paths are desired, the command has to be run from the correct directory. When giving '-' as path, borg will read data from standard input and create a file 'stdin' in the created archive from that data. In some cases it's more appropriate to use --content-from-command, however. See section *Reading from stdin* below for details. The archive will consume almost no disk space for files or parts of files that have already been stored in other archives. The archive name needs to be unique. It must not end in '.checkpoint' or '.checkpoint.N' (with N being a number), because these names are used for checkpoints and treated in special ways. In the archive name, you may use the following placeholders: {now}, {utcnow}, {fqdn}, {hostname}, {user} and some others. Backup speed is increased by not reprocessing files that are already part of existing archives and weren't modified. The detection of unmodified files is done by comparing multiple file metadata values with previous values kept in the files cache. This comparison can operate in different modes as given by ``--files-cache``: - ctime,size,inode (default) - mtime,size,inode (default behaviour of borg versions older than 1.1.0rc4) - ctime,size (ignore the inode number) - mtime,size (ignore the inode number) - rechunk,ctime (all files are considered modified - rechunk, cache ctime) - rechunk,mtime (all files are considered modified - rechunk, cache mtime) - disabled (disable the files cache, all files considered modified - rechunk) inode number: better safety, but often unstable on network filesystems Normally, detecting file modifications will take inode information into consideration to improve the reliability of file change detection. This is problematic for files located on sshfs and similar network file systems which do not provide stable inode numbers, such files will always be considered modified. You can use modes without `inode` in this case to improve performance, but reliability of change detection might be reduced. ctime vs. mtime: safety vs. speed - ctime is a rather safe way to detect changes to a file (metadata and contents) as it can not be set from userspace. But, a metadata-only change will already update the ctime, so there might be some unnecessary chunking/hashing even without content changes. Some filesystems do not support ctime (change time). E.g. doing a chown or chmod to a file will change its ctime. - mtime usually works and only updates if file contents were changed. But mtime can be arbitrarily set from userspace, e.g. to set mtime back to the same value it had before a content change happened. This can be used maliciously as well as well-meant, but in both cases mtime based cache modes can be problematic. The mount points of filesystems or filesystem snapshots should be the same for every creation of a new archive to ensure fast operation. This is because the file cache that is used to determine changed files quickly uses absolute filenames. If this is not possible, consider creating a bind mount to a stable location. The ``--progress`` option shows (from left to right) Original, Compressed and Deduplicated (O, C and D, respectively), then the Number of files (N) processed so far, followed by the currently processed path. When using ``--stats``, you will get some statistics about how much data was added - the "This Archive" deduplicated size there is most interesting as that is how much your repository will grow. Please note that the "All archives" stats refer to the state after creation. Also, the ``--stats`` and ``--dry-run`` options are mutually exclusive because the data is not actually compressed and deduplicated during a dry run. For more help on include/exclude patterns, see the :ref:`borg_patterns` command output. For more help on placeholders, see the :ref:`borg_placeholders` command output. .. man NOTES The ``--exclude`` patterns are not like tar. In tar ``--exclude`` .bundler/gems will exclude foo/.bundler/gems. In borg it will not, you need to use ``--exclude`` '\*/.bundler/gems' to get the same effect. In addition to using ``--exclude`` patterns, it is possible to use ``--exclude-if-present`` to specify the name of a filesystem object (e.g. a file or folder name) which, when contained within another folder, will prevent the containing folder from being backed up. By default, the containing folder and all of its contents will be omitted from the backup. If, however, you wish to only include the objects specified by ``--exclude-if-present`` in your backup, and not include any other contents of the containing folder, this can be enabled through using the ``--keep-exclude-tags`` option. The ``-x`` or ``--one-file-system`` option excludes directories, that are mountpoints (and everything in them). It detects mountpoints by comparing the device number from the output of ``stat()`` of the directory and its parent directory. Specifically, it excludes directories for which ``stat()`` reports a device number different from the device number of their parent. In general: be aware that there are directories with device number different from their parent, which the kernel does not consider a mountpoint and also the other way around. Linux examples for this are bind mounts (possibly same device number, but always a mountpoint) and ALL subvolumes of a btrfs (different device number from parent but not necessarily a mountpoint). macOS examples are the apfs mounts of a typical macOS installation. Therefore, when using ``--one-file-system``, you should double-check that the backup works as intended. .. _list_item_flags: Item flags ++++++++++ ``--list`` outputs a list of all files, directories and other file system items it considered (no matter whether they had content changes or not). For each item, it prefixes a single-letter flag that indicates type and/or status of the item. If you are interested only in a subset of that output, you can give e.g. ``--filter=AME`` and it will only show regular files with A, M or E status (see below). A uppercase character represents the status of a regular file relative to the "files" cache (not relative to the repo -- this is an issue if the files cache is not used). Metadata is stored in any case and for 'A' and 'M' also new data chunks are stored. For 'U' all data chunks refer to already existing chunks. - 'A' = regular file, added (see also :ref:`a_status_oddity` in the FAQ) - 'M' = regular file, modified - 'U' = regular file, unchanged - 'C' = regular file, it changed while we backed it up - 'E' = regular file, an error happened while accessing/reading *this* file A lowercase character means a file type other than a regular file, borg usually just stores their metadata: - 'd' = directory - 'b' = block device - 'c' = char device - 'h' = regular file, hardlink (to already seen inodes) - 's' = symlink - 'f' = fifo Other flags used include: - 'i' = backup data was read from standard input (stdin) - '-' = dry run, item was *not* backed up - 'x' = excluded, item was *not* backed up - '?' = missing status code (if you see this, please file a bug report!) Reading from stdin ++++++++++++++++++ There are two methods to read from stdin. Either specify ``-`` as path and pipe directly to borg:: backup-vm --id myvm --stdout | borg create REPO::ARCHIVE - Or use ``--content-from-command`` to have Borg manage the execution of the command and piping. If you do so, the first PATH argument is interpreted as command to execute and any further arguments are treated as arguments to the command:: borg create --content-from-command REPO::ARCHIVE -- backup-vm --id myvm --stdout ``--`` is used to ensure ``--id`` and ``--stdout`` are **not** considered arguments to ``borg`` but rather ``backup-vm``. The difference between the two approaches is that piping to borg creates an archive even if the command piping to borg exits with a failure. In this case, **one can end up with truncated output being backed up**. Using ``--content-from-command``, in contrast, borg is guaranteed to fail without creating an archive should the command fail. The command is considered failed when it returned a non-zero exit code. Reading from stdin yields just a stream of data without file metadata associated with it, and the files cache is not needed at all. So it is safe to disable it via ``--files-cache disabled`` and speed up backup creation a bit. By default, the content read from stdin is stored in a file called 'stdin'. Use ``--stdin-name`` to change the name.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/debug.rst0000644000076500000240000000330214601576577016112 0ustar00twstaffDebugging Facilities -------------------- There is a ``borg debug`` command that has some subcommands which are all **not intended for normal use** and **potentially very dangerous** if used incorrectly. For example, ``borg debug put-obj`` and ``borg debug delete-obj`` will only do what their name suggests: put objects into repo / delete objects from repo. Please note: - they will not update the chunks cache (chunks index) about the object - they will not update the manifest (so no automatic chunks index resync is triggered) - they will not check whether the object is in use (e.g. before delete-obj) - they will not update any metadata which may point to the object They exist to improve debugging capabilities without direct system access, e.g. in case you ever run into some severe malfunction. Use them only if you know what you are doing or if a trusted Borg developer tells you what to do. Borg has a ``--debug-topic TOPIC`` option to enable specific debugging messages. Topics are generally not documented. A ``--debug-profile FILE`` option exists which writes a profile of the main program's execution to a file. The format of these files is not directly compatible with the Python profiling tools, since these use the "marshal" format, which is not intended to be secure (quoting the Python docs: "Never unmarshal data received from an untrusted or unauthenticated source."). The ``borg debug profile-convert`` command can be used to take a Borg profile and convert it to a profile file that is compatible with the Python tools. Additionally, if the filename specified for ``--debug-profile`` ends with ".pyprof" a Python compatible profile is generated. This is only intended for local use by developers. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/delete.rst0000644000076500000240000000202214601576577016264 0ustar00twstaff.. include:: delete.rst.inc Examples ~~~~~~~~ :: # delete a single backup archive: $ borg delete /path/to/repo::Monday # actually free disk space: $ borg compact /path/to/repo # delete all archives whose names begin with the machine's hostname followed by "-" $ borg delete --glob-archives '{hostname}-*' /path/to/repo # delete all archives whose names contain "-2012-" $ borg delete --glob-archives '*-2012-*' /path/to/repo # see what would be deleted if delete was run without --dry-run $ borg delete --list --dry-run -a '*-May-*' /path/to/repo # delete the whole repository and the related local cache: $ borg delete /path/to/repo You requested to completely DELETE the repository *including* all archives it contains: repo Mon, 2016-02-15 19:26:54 root-2016-02-15 Mon, 2016-02-15 19:36:29 newname Mon, 2016-02-15 19:50:19 Type 'YES' if you understand this and want to continue: YES ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/delete.rst.inc0000644000076500000240000003346614601576577017054 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_delete: borg delete ----------- .. code-block:: none borg [common options] delete [options] [REPOSITORY_OR_ARCHIVE] [ARCHIVE...] .. only:: html .. class:: borg-options-table +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``REPOSITORY_OR_ARCHIVE`` | repository or archive to delete | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``ARCHIVE`` | archives to delete | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-n``, ``--dry-run`` | do not change repository | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--list`` | output verbose list of archives | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-s``, ``--stats`` | print statistics for the deleted archive | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--cache-only`` | delete only the local cache for the given repository | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--force`` | force deletion of corrupted archives, use ``--force --force`` in case ``--force`` does not work. | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--keep-security-info`` | keep the local security info when deleting a repository | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--save-space`` | work slower, but using less space | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-c SECONDS``, ``--checkpoint-interval SECONDS`` | write checkpoint every SECONDS seconds (Default: 1800) | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **Archive filters** — Archive filters can be applied to repository targets. | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-P PREFIX``, ``--prefix PREFIX`` | only consider archive names starting with this prefix. (deprecated) | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-a GLOB``, ``--glob-archives GLOB`` | only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see "borg help patterns". | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--sort-by KEYS`` | Comma-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--first N`` | consider first N archives after other filters were applied | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--last N`` | consider last N archives after other filters were applied | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY_OR_ARCHIVE repository or archive to delete ARCHIVE archives to delete optional arguments -n, --dry-run do not change repository --list output verbose list of archives -s, --stats print statistics for the deleted archive --cache-only delete only the local cache for the given repository --force force deletion of corrupted archives, use ``--force --force`` in case ``--force`` does not work. --keep-security-info keep the local security info when deleting a repository --save-space work slower, but using less space -c SECONDS, --checkpoint-interval SECONDS write checkpoint every SECONDS seconds (Default: 1800) :ref:`common_options` | Archive filters -P PREFIX, --prefix PREFIX only consider archive names starting with this prefix. (deprecated) -a GLOB, --glob-archives GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see "borg help patterns". --sort-by KEYS Comma-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp --first N consider first N archives after other filters were applied --last N consider last N archives after other filters were applied Description ~~~~~~~~~~~ This command deletes an archive from the repository or the complete repository. Important: When deleting archives, repository disk space is **not** freed until you run ``borg compact``. When you delete a complete repository, the security info and local cache for it (if any) are also deleted. Alternatively, you can delete just the local cache with the ``--cache-only`` option, or keep the security info with the ``--keep-security-info`` option. When in doubt, use ``--dry-run --list`` to see what would be deleted. When using ``--stats``, you will get some statistics about how much data was deleted - the "Deleted data" deduplicated size there is most interesting as that is how much your repository will shrink. Please note that the "All archives" stats refer to the state after deletion. You can delete multiple archives by specifying a shell pattern to match multiple archives using the ``--glob-archives GLOB`` option (for more info on these patterns, see :ref:`borg_patterns`). To avoid accidentally deleting archives, especially when using glob patterns, it might be helpful to use the ``--dry-run`` to test out the command without actually making any changes to the repository.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/diff.rst0000644000076500000240000000247214601576577015743 0ustar00twstaff.. include:: diff.rst.inc Examples ~~~~~~~~ :: $ borg init -e=none testrepo $ mkdir testdir $ cd testdir $ echo asdf > file1 $ dd if=/dev/urandom bs=1M count=4 > file2 $ touch file3 $ borg create ../testrepo::archive1 . $ chmod a+x file1 $ echo "something" >> file2 $ borg create ../testrepo::archive2 . $ echo "testing 123" >> file1 $ rm file3 $ touch file4 $ borg create ../testrepo::archive3 . $ cd .. $ borg diff testrepo::archive1 archive2 [-rw-r--r-- -> -rwxr-xr-x] file1 +135 B -252 B file2 $ borg diff testrepo::archive2 archive3 +17 B -5 B file1 added 0 B file4 removed 0 B file3 $ borg diff testrepo::archive1 archive3 +17 B -5 B [-rw-r--r-- -> -rwxr-xr-x] file1 +135 B -252 B file2 added 0 B file4 removed 0 B file3 $ borg diff --json-lines testrepo::archive1 archive3 {"path": "file1", "changes": [{"type": "modified", "added": 17, "removed": 5}, {"type": "mode", "old_mode": "-rw-r--r--", "new_mode": "-rwxr-xr-x"}]} {"path": "file2", "changes": [{"type": "modified", "added": 135, "removed": 252}]} {"path": "file4", "changes": [{"type": "added", "size": 0}]} {"path": "file3", "changes": [{"type": "removed", "size": 0}]}././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/diff.rst.inc0000644000076500000240000002111314601576577016504 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_diff: borg diff --------- .. code-block:: none borg [common options] diff [options] REPO::ARCHIVE1 ARCHIVE2 [PATH...] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | **positional arguments** | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``REPO::ARCHIVE1`` | repository location and ARCHIVE1 name | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``ARCHIVE2`` | ARCHIVE2 name (no repository location allowed) | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``PATH`` | paths of items inside the archives to compare; patterns are supported | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | **optional arguments** | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``--numeric-owner`` | deprecated, use ``--numeric-ids`` instead | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``--numeric-ids`` | only consider numeric user and group identifiers | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``--same-chunker-params`` | Override check of chunker parameters. | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``--sort`` | Sort the output lines by file path. | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``--content-only`` | Only compare differences in content (exclude metadata differences) | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``--json-lines`` | Format output as JSON Lines. | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | **Include/Exclude options** | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``-e PATTERN``, ``--exclude PATTERN`` | exclude paths matching PATTERN | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``--exclude-from EXCLUDEFILE`` | read exclude patterns from EXCLUDEFILE, one per line | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``--pattern PATTERN`` | include/exclude paths matching PATTERN | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ | | ``--patterns-from PATTERNFILE`` | read include/exclude patterns from PATTERNFILE, one per line | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------+ .. raw:: html .. only:: latex REPO::ARCHIVE1 repository location and ARCHIVE1 name ARCHIVE2 ARCHIVE2 name (no repository location allowed) PATH paths of items inside the archives to compare; patterns are supported optional arguments --numeric-owner deprecated, use ``--numeric-ids`` instead --numeric-ids only consider numeric user and group identifiers --same-chunker-params Override check of chunker parameters. --sort Sort the output lines by file path. --content-only Only compare differences in content (exclude metadata differences) --json-lines Format output as JSON Lines. :ref:`common_options` | Include/Exclude options -e PATTERN, --exclude PATTERN exclude paths matching PATTERN --exclude-from EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line --pattern PATTERN include/exclude paths matching PATTERN --patterns-from PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line Description ~~~~~~~~~~~ This command finds differences (file contents, user/group/mode) between archives. A repository location and an archive name must be specified for REPO::ARCHIVE1. ARCHIVE2 is just another archive name in same repository (no repository location allowed). For archives created with Borg 1.1 or newer diff automatically detects whether the archives are created with the same chunker params. If so, only chunk IDs are compared, which is very fast. For archives prior to Borg 1.1 chunk contents are compared by default. If you did not create the archives with different chunker params, pass ``--same-chunker-params``. Note that the chunker params changed from Borg 0.xx to 1.0. For more help on include/exclude patterns, see the :ref:`borg_patterns` command output.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/export-tar.rst.inc0000644000076500000240000002213514601576577017706 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_export-tar: borg export-tar --------------- .. code-block:: none borg [common options] export-tar [options] ARCHIVE FILE [PATH...] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``ARCHIVE`` | archive to export | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``FILE`` | output tar file. "-" to write to stdout instead. | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``PATH`` | paths to extract; patterns are supported | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--tar-filter`` | filter program to pipe data through | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--list`` | output verbose list of items (files, dirs, ...) | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | **Include/Exclude options** | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``-e PATTERN``, ``--exclude PATTERN`` | exclude paths matching PATTERN | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--exclude-from EXCLUDEFILE`` | read exclude patterns from EXCLUDEFILE, one per line | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--pattern PATTERN`` | include/exclude paths matching PATTERN | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--patterns-from PATTERNFILE`` | read include/exclude patterns from PATTERNFILE, one per line | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--strip-components NUMBER`` | Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex ARCHIVE archive to export FILE output tar file. "-" to write to stdout instead. PATH paths to extract; patterns are supported optional arguments --tar-filter filter program to pipe data through --list output verbose list of items (files, dirs, ...) :ref:`common_options` | Include/Exclude options -e PATTERN, --exclude PATTERN exclude paths matching PATTERN --exclude-from EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line --pattern PATTERN include/exclude paths matching PATTERN --patterns-from PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line --strip-components NUMBER Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. Description ~~~~~~~~~~~ This command creates a tarball from an archive. When giving '-' as the output FILE, Borg will write a tar stream to standard output. By default (``--tar-filter=auto``) Borg will detect whether the FILE should be compressed based on its file extension and pipe the tarball through an appropriate filter before writing it to FILE: - .tar.gz or .tgz: gzip - .tar.bz2 or .tbz: bzip2 - .tar.xz or .txz: xz - .tar.zstd or .tar.zst: zstd - .tar.lz4: lz4 Alternatively, a ``--tar-filter`` program may be explicitly specified. It should read the uncompressed tar stream from stdin and write a compressed/filtered tar stream to stdout. The generated tarball uses the GNU tar format. export-tar is a lossy conversion: BSD flags, ACLs, extended attributes (xattrs), atime and ctime are not exported. Timestamp resolution is limited to whole seconds, not the nanosecond resolution otherwise supported by Borg. A ``--sparse`` option (as found in borg extract) is not supported. By default the entire archive is extracted but a subset of files and directories can be selected by passing a list of ``PATHs`` as arguments. The file selection can further be restricted by using the ``--exclude`` option. For more help on include/exclude patterns, see the :ref:`borg_patterns` command output. ``--progress`` can be slower than no progress display, since it makes one additional pass over the archive metadata.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/extract.rst0000644000076500000240000000140214601576577016475 0ustar00twstaff.. include:: extract.rst.inc Examples ~~~~~~~~ :: # Extract entire archive $ borg extract /path/to/repo::my-files # Extract entire archive and list files while processing $ borg extract --list /path/to/repo::my-files # Verify whether an archive could be successfully extracted, but do not write files to disk $ borg extract --dry-run /path/to/repo::my-files # Extract the "src" directory $ borg extract /path/to/repo::my-files home/USERNAME/src # Extract the "src" directory but exclude object files $ borg extract /path/to/repo::my-files home/USERNAME/src --exclude '*.o' # Restore a raw device (must not be active/in use/mounted at that time) $ borg extract --stdout /path/to/repo::my-sdx | dd of=/dev/sdx bs=10M ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/extract.rst.inc0000644000076500000240000003007014601576577017250 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_extract: borg extract ------------ .. code-block:: none borg [common options] extract [options] ARCHIVE [PATH...] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``ARCHIVE`` | archive to extract | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``PATH`` | paths to extract; patterns are supported | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--list`` | output verbose list of items (files, dirs, ...) | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``-n``, ``--dry-run`` | do not actually change any files | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--numeric-owner`` | deprecated, use ``--numeric-ids`` instead | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--numeric-ids`` | only obey numeric user and group identifiers | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--nobsdflags`` | deprecated, use ``--noflags`` instead | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--noflags`` | do not extract/set flags (e.g. NODUMP, IMMUTABLE) | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--noacls`` | do not extract/set ACLs | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--noxattrs`` | do not extract/set xattrs | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--stdout`` | write all extracted data to stdout | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--sparse`` | create holes in output sparse file from all-zero chunks | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | **Include/Exclude options** | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``-e PATTERN``, ``--exclude PATTERN`` | exclude paths matching PATTERN | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--exclude-from EXCLUDEFILE`` | read exclude patterns from EXCLUDEFILE, one per line | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--pattern PATTERN`` | include/exclude paths matching PATTERN | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--patterns-from PATTERNFILE`` | read include/exclude patterns from PATTERNFILE, one per line | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | ``--strip-components NUMBER`` | Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. | +-------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex ARCHIVE archive to extract PATH paths to extract; patterns are supported optional arguments --list output verbose list of items (files, dirs, ...) -n, --dry-run do not actually change any files --numeric-owner deprecated, use ``--numeric-ids`` instead --numeric-ids only obey numeric user and group identifiers --nobsdflags deprecated, use ``--noflags`` instead --noflags do not extract/set flags (e.g. NODUMP, IMMUTABLE) --noacls do not extract/set ACLs --noxattrs do not extract/set xattrs --stdout write all extracted data to stdout --sparse create holes in output sparse file from all-zero chunks :ref:`common_options` | Include/Exclude options -e PATTERN, --exclude PATTERN exclude paths matching PATTERN --exclude-from EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line --pattern PATTERN include/exclude paths matching PATTERN --patterns-from PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line --strip-components NUMBER Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. Description ~~~~~~~~~~~ This command extracts the contents of an archive. By default the entire archive is extracted but a subset of files and directories can be selected by passing a list of ``PATHs`` as arguments. The file selection can further be restricted by using the ``--exclude`` option. For more help on include/exclude patterns, see the :ref:`borg_patterns` command output. By using ``--dry-run``, you can do all extraction steps except actually writing the output data: reading metadata and data chunks from the repo, checking the hash/hmac, decrypting, decompressing. ``--progress`` can be slower than no progress display, since it makes one additional pass over the archive metadata. .. note:: Currently, extract always writes into the current working directory ("."), so make sure you ``cd`` to the right place before calling ``borg extract``. When parent directories are not extracted (because of using file/directory selection or any other reason), borg can not restore parent directories' metadata, e.g. owner, group, permission, etc.././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1409454 borgbackup-1.2.8/docs/usage/general/0000755000076500000240000000000014601577064015701 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/general/date-time.rst.inc0000644000076500000240000000045114601576577021064 0ustar00twstaffDate and Time ~~~~~~~~~~~~~ We format date and time conforming to ISO-8601, that is: YYYY-MM-DD and HH:MM:SS (24h clock). For more information about that, see: https://xkcd.com/1179/ Unless otherwise noted, we display local date and time. Internally, we store and process date and time as UTC. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/general/environment.rst.inc0000644000076500000240000002747314601576577021574 0ustar00twstaffEnvironment Variables ~~~~~~~~~~~~~~~~~~~~~ Borg uses some environment variables for automation: General: BORG_REPO When set, use the value to give the default repository location. If a command needs an archive parameter, you can abbreviate as ``::archive``. If a command needs a repository parameter, you can either leave it away or abbreviate as ``::``, if a positional parameter is required. BORG_PASSPHRASE When set, use the value to answer the passphrase question for encrypted repositories. It is used when a passphrase is needed to access an encrypted repo as well as when a new passphrase should be initially set when initializing an encrypted repo. See also BORG_NEW_PASSPHRASE. BORG_PASSCOMMAND When set, use the standard output of the command (trailing newlines are stripped) to answer the passphrase question for encrypted repositories. It is used when a passphrase is needed to access an encrypted repo as well as when a new passphrase should be initially set when initializing an encrypted repo. Note that the command is executed without a shell. So variables, like ``$HOME`` will work, but ``~`` won't. If BORG_PASSPHRASE is also set, it takes precedence. See also BORG_NEW_PASSPHRASE. BORG_PASSPHRASE_FD When set, specifies a file descriptor to read a passphrase from. Programs starting borg may choose to open an anonymous pipe and use it to pass a passphrase. This is safer than passing via BORG_PASSPHRASE, because on some systems (e.g. Linux) environment can be examined by other processes. If BORG_PASSPHRASE or BORG_PASSCOMMAND are also set, they take precedence. BORG_NEW_PASSPHRASE When set, use the value to answer the passphrase question when a **new** passphrase is asked for. This variable is checked first. If it is not set, BORG_PASSPHRASE and BORG_PASSCOMMAND will also be checked. Main usecase for this is to fully automate ``borg change-passphrase``. BORG_DISPLAY_PASSPHRASE When set, use the value to answer the "display the passphrase for verification" question when defining a new passphrase for encrypted repositories. BORG_HOST_ID Borg usually computes a host id from the FQDN plus the results of ``uuid.getnode()`` (which usually returns a unique id based on the MAC address of the network interface. Except if that MAC happens to be all-zero - in that case it returns a random value, which is not what we want (because it kills automatic stale lock removal). So, if you have a all-zero MAC address or other reasons to better externally control the host id, just set this environment variable to a unique value. If all your FQDNs are unique, you can just use the FQDN. If not, use fqdn@uniqueid. BORG_LOGGING_CONF When set, use the given filename as INI_-style logging configuration. A basic example conf can be found at ``docs/misc/logging.conf``. BORG_RSH When set, use this command instead of ``ssh``. This can be used to specify ssh options, such as a custom identity file ``ssh -i /path/to/private/key``. See ``man ssh`` for other options. Using the ``--rsh CMD`` commandline option overrides the environment variable. BORG_REMOTE_PATH When set, use the given path as borg executable on the remote (defaults to "borg" if unset). Using ``--remote-path PATH`` commandline option overrides the environment variable. BORG_FILES_CACHE_SUFFIX When set to a value at least one character long, instructs borg to use a specifically named (based on the suffix) alternative files cache. This can be used to avoid loading and saving cache entries for backup sources other than the current sources. BORG_FILES_CACHE_TTL When set to a numeric value, this determines the maximum "time to live" for the files cache entries (default: 20). The files cache is used to quickly determine whether a file is unchanged. The FAQ explains this more detailed in: :ref:`always_chunking` BORG_SHOW_SYSINFO When set to no (default: yes), system information (like OS, Python version, ...) in exceptions is not shown. Please only use for good reasons as it makes issues harder to analyze. BORG_FUSE_IMPL Choose the lowlevel FUSE implementation borg shall use for ``borg mount``. This is a comma-separated list of implementation names, they are tried in the given order, e.g.: - ``pyfuse3,llfuse``: default, first try to load pyfuse3, then try to load llfuse. - ``llfuse,pyfuse3``: first try to load llfuse, then try to load pyfuse3. - ``pyfuse3``: only try to load pyfuse3 - ``llfuse``: only try to load llfuse - ``none``: do not try to load an implementation BORG_SELFTEST This can be used to influence borg's builtin self-tests. The default is to execute the tests at the beginning of each borg command invocation. BORG_SELFTEST=disabled can be used to switch off the tests and rather save some time. Disabling is not recommended for normal borg users, but large scale borg storage providers can use this to optimize production servers after at least doing a one-time test borg (with selftests not disabled) when installing or upgrading machines / OS / borg. BORG_WORKAROUNDS A list of comma separated strings that trigger workarounds in borg, e.g. to work around bugs in other software. Currently known strings are: basesyncfile Use the more simple BaseSyncFile code to avoid issues with sync_file_range. You might need this to run borg on WSL (Windows Subsystem for Linux) or in systemd.nspawn containers on some architectures (e.g. ARM). Using this does not affect data safety, but might result in a more bursty write to disk behaviour (not continuously streaming to disk). retry_erofs Retry opening a file without O_NOATIME if opening a file with O_NOATIME caused EROFS. You will need this to make archives from volume shadow copies in WSL1 (Windows Subsystem for Linux 1). authenticated_no_key Work around a lost passphrase or key for an ``authenticated`` mode repository (these are only authenticated, but not encrypted). If the key is missing in the repository config, add ``key = anything`` there. This workaround is **only** for emergencies and **only** to extract data from an affected repository (read-only access):: BORG_WORKAROUNDS=authenticated_no_key borg extract repo::archive After you have extracted all data you need, you MUST delete the repository:: BORG_WORKAROUNDS=authenticated_no_key borg delete repo Now you can init a fresh repo. Make sure you do not use the workaround any more. ignore_invalid_archive_tam Work around invalid archive TAMs created by borg < 1.2.5, see :issue:`7791`. This workaround likely needs to get used only once when following the upgrade instructions for CVE-2023-36811, see :ref:`archives_tam_vuln`. In normal production operations, this workaround should never be used. Some automatic "answerers" (if set, they automatically answer confirmation questions): BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no (or =yes) For "Warning: Attempting to access a previously unknown unencrypted repository" BORG_RELOCATED_REPO_ACCESS_IS_OK=no (or =yes) For "Warning: The repository at location ... was previously located at ..." BORG_CHECK_I_KNOW_WHAT_I_AM_DOING=NO (or =YES) For "This is a potentially dangerous function..." (check --repair) BORG_DELETE_I_KNOW_WHAT_I_AM_DOING=NO (or =YES) For "You requested to completely DELETE the repository *including* all archives it contains:" Note: answers are case sensitive. setting an invalid answer value might either give the default answer or ask you interactively, depending on whether retries are allowed (they by default are allowed). So please test your scripts interactively before making them a non-interactive script. .. _XDG env var: https://specifications.freedesktop.org/basedir-spec/0.6/ar01s03.html Directories and files: BORG_BASE_DIR Defaults to ``$HOME`` or ``~$USER`` or ``~`` (in that order). If you want to move all borg-specific folders to a custom path at once, all you need to do is to modify ``BORG_BASE_DIR``: the other paths for cache, config etc. will adapt accordingly (assuming you didn't set them to a different custom value). BORG_CACHE_DIR Defaults to ``$BORG_BASE_DIR/.cache/borg``. If ``BORG_BASE_DIR`` is not explicitly set while `XDG env var`_ ``XDG_CACHE_HOME`` is set, then ``$XDG_CACHE_HOME/borg`` is being used instead. This directory contains the local cache and might need a lot of space for dealing with big repositories. Make sure you're aware of the associated security aspects of the cache location: :ref:`cache_security` BORG_CONFIG_DIR Defaults to ``$BORG_BASE_DIR/.config/borg``. If ``BORG_BASE_DIR`` is not explicitly set while `XDG env var`_ ``XDG_CONFIG_HOME`` is set, then ``$XDG_CONFIG_HOME/borg`` is being used instead. This directory contains all borg configuration directories, see the FAQ for a security advisory about the data in this directory: :ref:`home_config_borg` BORG_SECURITY_DIR Defaults to ``$BORG_CONFIG_DIR/security``. This directory contains information borg uses to track its usage of NONCES ("numbers used once" - usually in encryption context) and other security relevant data. BORG_KEYS_DIR Defaults to ``$BORG_CONFIG_DIR/keys``. This directory contains keys for encrypted repositories. BORG_KEY_FILE When set, use the given path as repository key file. Please note that this is only for rather special applications that externally fully manage the key files: - this setting only applies to the keyfile modes (not to the repokey modes). - using a full, absolute path to the key file is recommended. - all directories in the given path must exist. - this setting forces borg to use the key file at the given location. - the key file must either exist (for most commands) or will be created (``borg init``). - you need to give a different path for different repositories. - you need to point to the correct key file matching the repository the command will operate on. TMPDIR This is where temporary files are stored (might need a lot of temporary space for some operations), see tempfile_ for details. Building: BORG_OPENSSL_PREFIX Adds given OpenSSL header file directory to the default locations (setup.py). BORG_LIBLZ4_PREFIX Adds given prefix directory to the default locations. If a 'include/lz4.h' is found Borg will be linked against the system liblz4 instead of a bundled implementation. (setup.py) BORG_LIBZSTD_PREFIX Adds given prefix directory to the default locations. If a 'include/zstd.h' is found Borg will be linked against the system libzstd instead of a bundled implementation. (setup.py) Please note: - Be very careful when using the "yes" sayers, the warnings with prompt exist for your / your data's security/safety. - Also be very careful when putting your passphrase into a script, make sure it has appropriate file permissions (e.g. mode 600, root:root). .. _INI: https://docs.python.org/3/library/logging.config.html#configuration-file-format .. _tempfile: https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/general/file-metadata.rst.inc0000644000076500000240000000577414601576577021725 0ustar00twstaffSupport for file metadata ~~~~~~~~~~~~~~~~~~~~~~~~~ Besides regular file and directory structures, Borg can preserve * symlinks (stored as symlink, the symlink is not followed) * special files: * character and block device files (restored via mknod) * FIFOs ("named pipes") * special file *contents* can be backed up in ``--read-special`` mode. By default the metadata to create them with mknod(2), mkfifo(2) etc. is stored. * hardlinked regular files, devices, FIFOs (considering all items in the same archive) * timestamps in nanosecond precision: mtime, atime, ctime * other timestamps: birthtime (on platforms supporting it) * permissions: * IDs of owning user and owning group * names of owning user and owning group (if the IDs can be resolved) * Unix Mode/Permissions (u/g/o permissions, suid, sgid, sticky) On some platforms additional features are supported: .. Yes/No's are grouped by reason/mechanism/reference. +-------------------------+----------+-----------+------------+ | Platform | ACLs | xattr | Flags | | | [#acls]_ | [#xattr]_ | [#flags]_ | +=========================+==========+===========+============+ | Linux | Yes | Yes | Yes [1]_ | +-------------------------+----------+-----------+------------+ | macOS | Yes | Yes | Yes (all) | +-------------------------+----------+-----------+------------+ | FreeBSD | Yes | Yes | Yes (all) | +-------------------------+----------+-----------+------------+ | OpenBSD | n/a | n/a | Yes (all) | +-------------------------+----------+-----------+------------+ | NetBSD | n/a | No [2]_ | Yes (all) | +-------------------------+----------+-----------+------------+ | Solaris and derivatives | No [3]_ | No [3]_ | n/a | +-------------------------+----------+-----------+------------+ | Windows (cygwin) | No [4]_ | No | No | +-------------------------+----------+-----------+------------+ Other Unix-like operating systems may work as well, but have not been tested at all. Note that most of the platform-dependent features also depend on the file system. For example, ntfs-3g on Linux isn't able to convey NTFS ACLs. .. [1] Only "nodump", "immutable", "compressed" and "append" are supported. Feature request :issue:`618` for more flags. .. [2] Feature request :issue:`1332` .. [3] Feature request :issue:`1337` .. [4] Cygwin tries to map NTFS ACLs to permissions with varying degrees of success. .. [#acls] The native access control list mechanism of the OS. This normally limits access to non-native ACLs. For example, NTFS ACLs aren't completely accessible on Linux with ntfs-3g. .. [#xattr] extended attributes; key-value pairs attached to a file, mainly used by the OS. This includes resource forks on Mac OS X. .. [#flags] aka *BSD flags*. The Linux set of flags [1]_ is portable across platforms. The BSDs define additional flags. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/general/file-systems.rst.inc0000644000076500000240000000275214601576577021645 0ustar00twstaffFile systems ~~~~~~~~~~~~ We strongly recommend against using Borg (or any other database-like software) on non-journaling file systems like FAT, since it is not possible to assume any consistency in case of power failures (or a sudden disconnect of an external drive or similar failures). While Borg uses a data store that is resilient against these failures when used on journaling file systems, it is not possible to guarantee this with some hardware -- independent of the software used. We don't know a list of affected hardware. If you are suspicious whether your Borg repository is still consistent and readable after one of the failures mentioned above occurred, run ``borg check --verify-data`` to make sure it is consistent. .. rubric:: Requirements for Borg repository file systems - Long file names - At least three directory levels with short names - Typically, file sizes up to a few hundred MB. Large repositories may require large files (>2 GB). - Up to 1000 files per directory. - rename(2) / MoveFile(Ex) should work as specified, i.e. on the same file system it should be a move (not a copy) operation, and in case of a directory it should fail if the destination exists and is not an empty directory, since this is used for locking. - Hardlinks are needed for :ref:`borg_upgrade` (if ``--inplace`` option is not used). Also hardlinks are used for more safe and secure file updating (e.g. of the repo config file), but the code tries to work also if hardlinks are not supported. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/general/logging.rst.inc0000644000076500000240000000320214601576577020636 0ustar00twstaffLogging ~~~~~~~ Borg writes all log output to stderr by default. But please note that something showing up on stderr does *not* indicate an error condition just because it is on stderr. Please check the log levels of the messages and the return code of borg for determining error, warning or success conditions. If you want to capture the log output to a file, just redirect it: :: borg create repo::archive myfiles 2>> logfile Custom logging configurations can be implemented via BORG_LOGGING_CONF. The log level of the builtin logging configuration defaults to WARNING. This is because we want Borg to be mostly silent and only output warnings, errors and critical messages, unless output has been requested by supplying an option that implies output (e.g. ``--list`` or ``--progress``). Log levels: DEBUG < INFO < WARNING < ERROR < CRITICAL Use ``--debug`` to set DEBUG log level - to get debug, info, warning, error and critical level output. Use ``--info`` (or ``-v`` or ``--verbose``) to set INFO log level - to get info, warning, error and critical level output. Use ``--warning`` (default) to set WARNING log level - to get warning, error and critical level output. Use ``--error`` to set ERROR log level - to get error and critical level output. Use ``--critical`` to set CRITICAL log level - to get critical level output. While you can set misc. log levels, do not expect that every command will give different output on different log levels - it's just a possibility. .. warning:: Options ``--critical`` and ``--error`` are provided for completeness, their usage is not recommended as you might miss important information. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/general/positional-arguments.rst.inc0000644000076500000240000000120514601576577023375 0ustar00twstaffPositional Arguments and Options: Order matters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Borg only supports taking options (``-s`` and ``--progress`` in the example) to the left or right of all positional arguments (``repo::archive`` and ``path`` in the example), but not in between them: :: borg create -s --progress repo::archive path # good and preferred borg create repo::archive path -s --progress # also works borg create -s repo::archive path --progress # works, but ugly borg create repo::archive -s --progress path # BAD This is due to a problem in the argparse module: https://bugs.python.org/issue15112 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/general/repository-locations.rst.inc0000644000076500000240000000112514601576577023422 0ustar00twstaffRepository / Archive Locations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Many commands want either a repository (just give the repo URL, see above) or an archive location, which is a repo URL followed by ``::archive_name``. Archive names must not contain the ``/`` (slash) character. For simplicity, maybe also avoid blanks or other characters that have special meaning on the shell or in a filesystem (borg mount will use the archive name as directory name). If you have set BORG_REPO (see above) and an archive location is needed, use ``::archive_name`` - the repo URL part is then read from BORG_REPO. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/general/repository-urls.rst.inc0000644000076500000240000000350614601576577022421 0ustar00twstaffRepository URLs ~~~~~~~~~~~~~~~ **Local filesystem** (or locally mounted network filesystem): ``/path/to/repo`` - filesystem path to repo directory, absolute path ``path/to/repo`` - filesystem path to repo directory, relative path Also, stuff like ``~/path/to/repo`` or ``~other/path/to/repo`` works (this is expanded by your shell). Note: you may also prepend a ``file://`` to a filesystem path to get URL style. **Remote repositories** accessed via ssh user@host: ``user@host:/path/to/repo`` - remote repo, absolute path ``ssh://user@host:port/path/to/repo`` - same, alternative syntax, port can be given **Remote repositories with relative paths** can be given using this syntax: ``user@host:path/to/repo`` - path relative to current directory ``user@host:~/path/to/repo`` - path relative to user's home directory ``user@host:~other/path/to/repo`` - path relative to other's home directory Note: giving ``user@host:/./path/to/repo`` or ``user@host:/~/path/to/repo`` or ``user@host:/~other/path/to/repo`` is also supported, but not required here. **Remote repositories with relative paths, alternative syntax with port**: ``ssh://user@host:port/./path/to/repo`` - path relative to current directory ``ssh://user@host:port/~/path/to/repo`` - path relative to user's home directory ``ssh://user@host:port/~other/path/to/repo`` - path relative to other's home directory If you frequently need the same repo URL, it is a good idea to set the ``BORG_REPO`` environment variable to set a default for the repo URL: :: export BORG_REPO='ssh://user@host:port/path/to/repo' Then just leave away the repo URL if only a repo URL is needed and you want to use the default - it will be read from BORG_REPO then. Use ``::`` syntax to give the repo URL when syntax requires giving a positional argument for the repo (e.g. ``borg mount :: /mnt``). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/general/resources.rst.inc0000644000076500000240000001101414601576577021222 0ustar00twstaffResource Usage ~~~~~~~~~~~~~~ Borg might use a lot of resources depending on the size of the data set it is dealing with. If one uses Borg in a client/server way (with a ssh: repository), the resource usage occurs in part on the client and in another part on the server. If one uses Borg as a single process (with a filesystem repo), all the resource usage occurs in that one process, so just add up client + server to get the approximate resource usage. CPU client: - **borg create:** does chunking, hashing, compression, crypto (high CPU usage) - **chunks cache sync:** quite heavy on CPU, doing lots of hashtable operations. - **borg extract:** crypto, decompression (medium to high CPU usage) - **borg check:** similar to extract, but depends on options given. - **borg prune / borg delete archive:** low to medium CPU usage - **borg delete repo:** done on the server It won't go beyond 100% of 1 core as the code is currently single-threaded. Especially higher zlib and lzma compression levels use significant amounts of CPU cycles. Crypto might be cheap on the CPU (if hardware accelerated) or expensive (if not). CPU server: It usually doesn't need much CPU, it just deals with the key/value store (repository) and uses the repository index for that. borg check: the repository check computes the checksums of all chunks (medium CPU usage) borg delete repo: low CPU usage CPU (only for client/server operation): When using borg in a client/server way with a ssh:-type repo, the ssh processes used for the transport layer will need some CPU on the client and on the server due to the crypto they are doing - esp. if you are pumping big amounts of data. Memory (RAM) client: The chunks index and the files index are read into memory for performance reasons. Might need big amounts of memory (see below). Compression, esp. lzma compression with high levels might need substantial amounts of memory. Memory (RAM) server: The server process will load the repository index into memory. Might need considerable amounts of memory, but less than on the client (see below). Chunks index (client only): Proportional to the amount of data chunks in your repo. Lots of chunks in your repo imply a big chunks index. It is possible to tweak the chunker params (see create options). Files index (client only): Proportional to the amount of files in your last backups. Can be switched off (see create options), but next backup might be much slower if you do. The speed benefit of using the files cache is proportional to file size. Repository index (server only): Proportional to the amount of data chunks in your repo. Lots of chunks in your repo imply a big repository index. It is possible to tweak the chunker params (see create options) to influence the amount of chunks being created. Temporary files (client): Reading data and metadata from a FUSE mounted repository will consume up to the size of all deduplicated, small chunks in the repository. Big chunks won't be locally cached. Temporary files (server): A non-trivial amount of data will be stored on the remote temp directory for each client that connects to it. For some remotes, this can fill the default temporary directory at /tmp. This can be remediated by ensuring the $TMPDIR, $TEMP, or $TMP environment variable is properly set for the sshd process. For some OSes, this can be done just by setting the correct value in the .bashrc (or equivalent login config file for other shells), however in other cases it may be necessary to first enable ``PermitUserEnvironment yes`` in your ``sshd_config`` file, then add ``environment="TMPDIR=/my/big/tmpdir"`` at the start of the public key to be used in the ``authorized_hosts`` file. Cache files (client only): Contains the chunks index and files index (plus a collection of single- archive chunk indexes which might need huge amounts of disk space, depending on archive count and size - see FAQ about how to reduce). Network (only for client/server operation): If your repository is remote, all deduplicated (and optionally compressed/ encrypted) data of course has to go over the connection (``ssh://`` repo url). If you use a locally mounted network filesystem, additionally some copy operations used for transaction support also go over the connection. If you backup multiple sources to one target repository, additional traffic happens for cache resynchronization. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/general/return-codes.rst.inc0000644000076500000240000000117214601576577021626 0ustar00twstaffReturn codes ~~~~~~~~~~~~ Borg can exit with the following return codes (rc): =========== ======= Return code Meaning =========== ======= 0 success (logged as INFO) 1 warning (operation reached its normal end, but there were warnings -- you should check the log, logged as WARNING) 2 error (like a fatal error, a local or remote exception, the operation did not reach its normal end, logged as ERROR) 128+N killed by signal N (e.g. 137 == kill -9) =========== ======= If you use ``--show-rc``, the return code is also logged at the indicated level as the last log entry. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/general/units.rst.inc0000644000076500000240000000076314601576577020363 0ustar00twstaffUnits ~~~~~ To display quantities, Borg takes care of respecting the usual conventions of scale. Disk sizes are displayed in `decimal `_, using powers of ten (so ``kB`` means 1000 bytes). For memory usage, `binary prefixes `_ are used, and are indicated using the `IEC binary prefixes `_, using powers of two (so ``KiB`` means 1024 bytes). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/general.rst0000644000076500000240000000361514601576577016450 0ustar00twstaffGeneral ------- Borg consists of a number of commands. Each command accepts a number of arguments and options and interprets various environment variables. The following sections will describe each command in detail. Commands, options, parameters, paths and such are ``set in fixed-width``. Option values are `underlined`. Borg has few options accepting a fixed set of values (e.g. ``--encryption`` of :ref:`borg_init`). .. container:: experimental Experimental features are marked with red stripes on the sides, like this paragraph. Experimental features are not stable, which means that they may be changed in incompatible ways or even removed entirely without prior notice in following releases. .. include:: usage_general.rst.inc In case you are interested in more details (like formulas), please see :ref:`internals`. For details on the available JSON output, refer to :ref:`json_output`. .. _common_options: Common options ~~~~~~~~~~~~~~ All Borg commands share these options: .. include:: common-options.rst.inc Option ``--bypass-lock`` allows you to access the repository while bypassing borg's locking mechanism. This is necessary if your repository is on a read-only storage where you don't have write permissions or capabilities and therefore cannot create a lock. Examples are repositories stored on a Bluray disc or a read-only network storage. Avoid this option if you are able to use locks as that is the safer way; see the warning below. .. warning:: If you do use ``--bypass-lock``, you are responsible to ensure that no other borg instances have write access to the repository. Otherwise, you might experience errors and read broken data if changes to that repository are being made at the same time. Examples ~~~~~~~~ :: # Create an archive and log: borg version, files list, return code $ borg create --show-version --list --show-rc /path/to/repo::my-files files ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/help.rst0000644000076500000240000000010114601576577015746 0ustar00twstaffMiscellaneous Help ------------------ .. include:: help.rst.inc ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/help.rst.inc0000644000076500000240000004077114601576577016537 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_patterns: borg help patterns ~~~~~~~~~~~~~~~~~~ The path/filenames used as input for the pattern matching start from the currently active recursion root. You usually give the recursion root(s) when invoking borg and these can be either relative or absolute paths. Starting with Borg 1.2, paths that are matched against patterns always appear relative. If you give ``/absolute/`` as root, the paths going into the matcher will start with ``absolute/``. If you give ``../../relative`` as root, the paths will be normalized as ``relative/``. A directory exclusion pattern can end either with or without a slash ('/'). If it ends with a slash, such as `some/path/`, the directory will be included but not its content. If it does not end with a slash, such as `some/path`, both the directory and content will be excluded. Borg supports different pattern styles. To define a non-default style for a specific pattern, prefix it with two characters followed by a colon ':' (i.e. ``fm:path/*``, ``sh:path/**``). `Fnmatch `_, selector `fm:` This is the default style for ``--exclude`` and ``--exclude-from``. These patterns use a variant of shell pattern syntax, with '\*' matching any number of characters, '?' matching any single character, '[...]' matching any single character specified, including ranges, and '[!...]' matching any character not specified. For the purpose of these patterns, the path separator (backslash for Windows and '/' on other systems) is not treated specially. Wrap meta-characters in brackets for a literal match (i.e. `[?]` to match the literal character `?`). For a path to match a pattern, the full path must match, or it must match from the start of the full path to just before a path separator. Except for the root path, paths will never end in the path separator when matching is attempted. Thus, if a given pattern ends in a path separator, a '\*' is appended before matching is attempted. A leading path separator is always removed. Shell-style patterns, selector `sh:` This is the default style for ``--pattern`` and ``--patterns-from``. Like fnmatch patterns these are similar to shell patterns. The difference is that the pattern may include `**/` for matching zero or more directory levels, `*` for matching zero or more arbitrary characters with the exception of any path separator. A leading path separator is always removed. Regular expressions, selector `re:` Regular expressions similar to those found in Perl are supported. Unlike shell patterns regular expressions are not required to match the full path and any substring match is sufficient. It is strongly recommended to anchor patterns to the start ('^'), to the end ('$') or both. Path separators (backslash for Windows and '/' on other systems) in paths are always normalized to a forward slash ('/') before applying a pattern. The regular expression syntax is described in the `Python documentation for the re module `_. Path prefix, selector `pp:` This pattern style is useful to match whole sub-directories. The pattern `pp:root/somedir` matches `root/somedir` and everything therein. A leading path separator is always removed. Path full-match, selector `pf:` This pattern style is (only) useful to match full paths. This is kind of a pseudo pattern as it can not have any variable or unspecified parts - the full path must be given. `pf:root/file.ext` matches `root/file.ext` only. A leading path separator is always removed. Implementation note: this is implemented via very time-efficient O(1) hashtable lookups (this means you can have huge amounts of such patterns without impacting performance much). Due to that, this kind of pattern does not respect any context or order. If you use such a pattern to include a file, it will always be included (if the directory recursion encounters it). Other include/exclude patterns that would normally match will be ignored. Same logic applies for exclude. .. note:: `re:`, `sh:` and `fm:` patterns are all implemented on top of the Python SRE engine. It is very easy to formulate patterns for each of these types which requires an inordinate amount of time to match paths. If untrusted users are able to supply patterns, ensure they cannot supply `re:` patterns. Further, ensure that `sh:` and `fm:` patterns only contain a handful of wildcards at most. Exclusions can be passed via the command line option ``--exclude``. When used from within a shell, the patterns should be quoted to protect them from expansion. The ``--exclude-from`` option permits loading exclusion patterns from a text file with one pattern per line. Lines empty or starting with the number sign ('#') after removing whitespace on both ends are ignored. The optional style selector prefix is also supported for patterns loaded from a file. Due to whitespace removal, paths with whitespace at the beginning or end can only be excluded using regular expressions. To test your exclusion patterns without performing an actual backup you can run ``borg create --list --dry-run ...``. Examples:: # Exclude '/home/user/file.o' but not '/home/user/file.odt': $ borg create -e '*.o' backup / # Exclude '/home/user/junk' and '/home/user/subdir/junk' but # not '/home/user/importantjunk' or '/etc/junk': $ borg create -e 'home/*/junk' backup / # Exclude the contents of '/home/user/cache' but not the directory itself: $ borg create -e home/user/cache/ backup / # The file '/home/user/cache/important' is *not* backed up: $ borg create -e home/user/cache/ backup / /home/user/cache/important # The contents of directories in '/home' are not backed up when their name # ends in '.tmp' $ borg create --exclude 're:^home/[^/]+\.tmp/' backup / # Load exclusions from file $ cat >exclude.txt <`_, e.g. {now:%Y-%m-%d_%H:%M:%S} {utcnow} The current UTC date and time, by default in ISO-8601 format. You can also supply your own `format string `_, e.g. {utcnow:%Y-%m-%d_%H:%M:%S} {user} The user name (or UID, if no name is available) of the user running borg. {pid} The current process ID. {borgversion} The version of borg, e.g.: 1.0.8rc1 {borgmajor} The version of borg, only the major version, e.g.: 1 {borgminor} The version of borg, only major and minor version, e.g.: 1.0 {borgpatch} The version of borg, only major, minor and patch version, e.g.: 1.0.8 If literal curly braces need to be used, double them for escaping:: borg create /path/to/repo::{{literal_text}} Examples:: borg create /path/to/repo::{hostname}-{user}-{utcnow} ... borg create /path/to/repo::{hostname}-{now:%Y-%m-%d_%H:%M:%S} ... borg prune --glob-archives '{hostname}-*' ... .. note:: systemd uses a difficult, non-standard syntax for command lines in unit files (refer to the `systemd.unit(5)` manual page). When invoking borg from unit files, pay particular attention to escaping, especially when using the now/utcnow placeholders, since systemd performs its own %-based variable replacement even in quoted text. To avoid interference from systemd, double all percent signs (``{hostname}-{now:%Y-%m-%d_%H:%M:%S}`` becomes ``{hostname}-{now:%%Y-%%m-%%d_%%H:%%M:%%S}``). .. _borg_compression: borg help compression ~~~~~~~~~~~~~~~~~~~~~ It is no problem to mix different compression methods in one repo, deduplication is done on the source data chunks (not on the compressed or encrypted data). If some specific chunk was once compressed and stored into the repo, creating another backup that also uses this chunk will not change the stored chunk. So if you use different compression specs for the backups, whichever stores a chunk first determines its compression. See also borg recreate. Compression is lz4 by default. If you want something else, you have to specify what you want. Valid compression specifiers are: none Do not compress. lz4 Use lz4 compression. Very high speed, very low compression. (default) zstd[,L] Use zstd ("zstandard") compression, a modern wide-range algorithm. If you do not explicitly give the compression level L (ranging from 1 to 22), it will use level 3. Archives compressed with zstd are not compatible with borg < 1.1.4. zlib[,L] Use zlib ("gz") compression. Medium speed, medium compression. If you do not explicitly give the compression level L (ranging from 0 to 9), it will use level 6. Giving level 0 (means "no compression", but still has zlib protocol overhead) is usually pointless, you better use "none" compression. lzma[,L] Use lzma ("xz") compression. Low speed, high compression. If you do not explicitly give the compression level L (ranging from 0 to 9), it will use level 6. Giving levels above 6 is pointless and counterproductive because it does not compress better due to the buffer size used by borg - but it wastes lots of CPU cycles and RAM. auto,C[,L] Use a built-in heuristic to decide per chunk whether to compress or not. The heuristic tries with lz4 whether the data is compressible. For incompressible data, it will not use compression (uses "none"). For compressible data, it uses the given C[,L] compression - with C[,L] being any valid compression specifier. obfuscate,SPEC,C[,L] Use compressed-size obfuscation to make fingerprinting attacks based on the observable stored chunk size more difficult. Note: - You must combine this with encryption, or it won't make any sense. - Your repo size will be bigger, of course. - A chunk is limited by the constant ``MAX_DATA_SIZE`` (cur. ~20MiB). The SPEC value determines how the size obfuscation works: *Relative random reciprocal size variation* (multiplicative) Size will increase by a factor, relative to the compressed data size. Smaller factors are used often, larger factors rarely. Available factors:: 1: 0.01 .. 100 2: 0.1 .. 1,000 3: 1 .. 10,000 4: 10 .. 100,000 5: 100 .. 1,000,000 6: 1,000 .. 10,000,000 Example probabilities for SPEC ``1``:: 90 % 0.01 .. 0.1 9 % 0.1 .. 1 0.9 % 1 .. 10 0.09% 10 .. 100 *Randomly sized padding up to the given size* (additive) :: 110: 1kiB (2 ^ (SPEC - 100)) ... 120: 1MiB ... 123: 8MiB (max.) Examples:: borg create --compression lz4 REPO::ARCHIVE data borg create --compression zstd REPO::ARCHIVE data borg create --compression zstd,10 REPO::ARCHIVE data borg create --compression zlib REPO::ARCHIVE data borg create --compression zlib,1 REPO::ARCHIVE data borg create --compression auto,lzma,6 REPO::ARCHIVE data borg create --compression auto,lzma ... borg create --compression obfuscate,110,none ... borg create --compression obfuscate,3,auto,zstd,10 ... borg create --compression obfuscate,2,zstd,6 ... ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/import-tar.rst.inc0000644000076500000240000003022514601576577017676 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_import-tar: borg import-tar --------------- .. code-block:: none borg [common options] import-tar [options] ARCHIVE TARFILE .. only:: html .. class:: borg-options-table +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``ARCHIVE`` | name of archive to create (must be also a valid directory name) | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``TARFILE`` | input tar file. "-" to read from stdin instead. | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``--tar-filter`` | filter program to pipe data through | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``-s``, ``--stats`` | print statistics for the created archive | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``--list`` | output verbose list of items (files, dirs, ...) | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``--filter STATUSCHARS`` | only display items with the given status characters | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``--json`` | output stats as JSON (implies --stats) | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``--ignore-zeros`` | ignore zero-filled blocks in the input tarball | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | **Archive options** | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``--comment COMMENT`` | add a comment text to the archive | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``--timestamp TIMESTAMP`` | manually specify the archive creation date/time (UTC, yyyy-mm-ddThh:mm:ss format). alternatively, give a reference file/directory. | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``-c SECONDS``, ``--checkpoint-interval SECONDS`` | write checkpoint every SECONDS seconds (Default: 1800) | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``--chunker-params PARAMS`` | specify the chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, HASH_MASK_BITS, HASH_WINDOW_SIZE). default: buzhash,19,23,21,4095 | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ | | ``-C COMPRESSION``, ``--compression COMPRESSION`` | select compression algorithm, see the output of the "borg help compression" command for details. | +-------------------------------------------------------+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex ARCHIVE name of archive to create (must be also a valid directory name) TARFILE input tar file. "-" to read from stdin instead. optional arguments --tar-filter filter program to pipe data through -s, --stats print statistics for the created archive --list output verbose list of items (files, dirs, ...) --filter STATUSCHARS only display items with the given status characters --json output stats as JSON (implies --stats) --ignore-zeros ignore zero-filled blocks in the input tarball :ref:`common_options` | Archive options --comment COMMENT add a comment text to the archive --timestamp TIMESTAMP manually specify the archive creation date/time (UTC, yyyy-mm-ddThh:mm:ss format). alternatively, give a reference file/directory. -c SECONDS, --checkpoint-interval SECONDS write checkpoint every SECONDS seconds (Default: 1800) --chunker-params PARAMS specify the chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, HASH_MASK_BITS, HASH_WINDOW_SIZE). default: buzhash,19,23,21,4095 -C COMPRESSION, --compression COMPRESSION select compression algorithm, see the output of the "borg help compression" command for details. Description ~~~~~~~~~~~ This command creates a backup archive from a tarball. When giving '-' as path, Borg will read a tar stream from standard input. By default (--tar-filter=auto) Borg will detect whether the file is compressed based on its file extension and pipe the file through an appropriate filter: - .tar.gz or .tgz: gzip -d - .tar.bz2 or .tbz: bzip2 -d - .tar.xz or .txz: xz -d - .tar.zstd or .tar.zst: zstd -d - .tar.lz4: lz4 -d Alternatively, a --tar-filter program may be explicitly specified. It should read compressed data from stdin and output an uncompressed tar stream on stdout. Most documentation of borg create applies. Note that this command does not support excluding files. import-tar is a lossy conversion: BSD flags, ACLs, extended attributes (xattrs), atime and ctime are not exported. Timestamp resolution is limited to whole seconds, not the nanosecond resolution otherwise supported by Borg. A ``--sparse`` option (as found in borg create) is not supported. import-tar reads POSIX.1-1988 (ustar), POSIX.1-2001 (pax), GNU tar, UNIX V7 tar and SunOS tar with extended attributes. To import multiple tarballs into a single archive, they can be simply concatenated (e.g. using "cat") into a single file, and imported with an ``--ignore-zeros`` option to skip through the stop markers between them.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/info.rst0000644000076500000240000000521214601576577015761 0ustar00twstaff.. include:: info.rst.inc Examples ~~~~~~~~ :: $ borg info /path/to/repo::2017-06-29T11:00-srv Archive name: 2017-06-29T11:00-srv Archive fingerprint: b2f1beac2bd553b34e06358afa45a3c1689320d39163890c5bbbd49125f00fe5 Comment: Hostname: myhostname Username: root Time (start): Thu, 2017-06-29 11:03:07 Time (end): Thu, 2017-06-29 11:03:13 Duration: 5.66 seconds Number of files: 17037 Command line: /usr/sbin/borg create /path/to/repo::2017-06-29T11:00-srv /srv Utilization of max. archive size: 0% ------------------------------------------------------------------------------ Original size Compressed size Deduplicated size This archive: 12.53 GB 12.49 GB 1.62 kB All archives: 121.82 TB 112.41 TB 215.42 GB Unique chunks Total chunks Chunk index: 1015213 626934122 $ borg info /path/to/repo --last 1 Archive name: 2017-06-29T11:00-srv Archive fingerprint: b2f1beac2bd553b34e06358afa45a3c1689320d39163890c5bbbd49125f00fe5 Comment: Hostname: myhostname Username: root Time (start): Thu, 2017-06-29 11:03:07 Time (end): Thu, 2017-06-29 11:03:13 Duration: 5.66 seconds Number of files: 17037 Command line: /usr/sbin/borg create /path/to/repo::2017-06-29T11:00-srv /srv Utilization of max. archive size: 0% ------------------------------------------------------------------------------ Original size Compressed size Deduplicated size This archive: 12.53 GB 12.49 GB 1.62 kB All archives: 121.82 TB 112.41 TB 215.42 GB Unique chunks Total chunks Chunk index: 1015213 626934122 $ borg info /path/to/repo Repository ID: d857ce5788c51272c61535062e89eac4e8ef5a884ffbe976e0af9d8765dedfa5 Location: /path/to/repo Encrypted: Yes (repokey) Cache: /root/.cache/borg/d857ce5788c51272c61535062e89eac4e8ef5a884ffbe976e0af9d8765dedfa5 Security dir: /root/.config/borg/security/d857ce5788c51272c61535062e89eac4e8ef5a884ffbe976e0af9d8765dedfa5 ------------------------------------------------------------------------------ Original size Compressed size Deduplicated size All archives: 121.82 TB 112.41 TB 215.42 GB Unique chunks Total chunks Chunk index: 1015213 626934122 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/info.rst.inc0000644000076500000240000002050414601576577016532 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_info: borg info --------- .. code-block:: none borg [common options] info [options] [REPOSITORY_OR_ARCHIVE] .. only:: html .. class:: borg-options-table +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``REPOSITORY_OR_ARCHIVE`` | repository or archive to display information about | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--json`` | format output as JSON | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **Archive filters** — Archive filters can be applied to repository targets. | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-P PREFIX``, ``--prefix PREFIX`` | only consider archive names starting with this prefix. (deprecated) | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-a GLOB``, ``--glob-archives GLOB`` | only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see "borg help patterns". | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--sort-by KEYS`` | Comma-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--first N`` | consider first N archives after other filters were applied | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--last N`` | consider last N archives after other filters were applied | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY_OR_ARCHIVE repository or archive to display information about optional arguments --json format output as JSON :ref:`common_options` | Archive filters -P PREFIX, --prefix PREFIX only consider archive names starting with this prefix. (deprecated) -a GLOB, --glob-archives GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see "borg help patterns". --sort-by KEYS Comma-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp --first N consider first N archives after other filters were applied --last N consider last N archives after other filters were applied Description ~~~~~~~~~~~ This command displays detailed information about the specified archive or repository. Please note that the deduplicated sizes of the individual archives do not add up to the deduplicated size of the repository ("all archives"), because the two are meaning different things: This archive / deduplicated size = amount of data stored ONLY for this archive = unique chunks of this archive. All archives / deduplicated size = amount of data stored in the repo = all chunks in the repository. Borg archives can only contain a limited amount of file metadata. The size of an archive relative to this limit depends on a number of factors, mainly the number of files, the lengths of paths and other metadata stored for files. This is shown as *utilization of maximum supported archive size*.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/init.rst0000644000076500000240000000121214601576577015765 0ustar00twstaff.. include:: init.rst.inc Examples ~~~~~~~~ :: # Local repository, repokey encryption, BLAKE2b (often faster, since Borg 1.1) $ borg init --encryption=repokey-blake2 /path/to/repo # Local repository (no encryption) $ borg init --encryption=none /path/to/repo # Remote repository (accesses a remote borg via ssh) # repokey: stores the (encrypted) key into /config $ borg init --encryption=repokey-blake2 user@hostname:backup # Remote repository (accesses a remote borg via ssh) # keyfile: stores the (encrypted) key into ~/.config/borg/keys/ $ borg init --encryption=keyfile user@hostname:backup ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/init.rst.inc0000644000076500000240000003306114601576577016544 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_init: borg init --------- .. code-block:: none borg [common options] init [options] [REPOSITORY] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-------------------------------------------------------+------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``REPOSITORY`` | repository to create | +-------------------------------------------------------+------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-------------------------------------------------------+------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-e MODE``, ``--encryption MODE`` | select encryption key mode **(required)** | +-------------------------------------------------------+------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--append-only`` | create an append-only mode repository. Note that this only affects the low level structure of the repository, and running `delete` or `prune` will still be allowed. See :ref:`append_only_mode` in Additional Notes for more details. | +-------------------------------------------------------+------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--storage-quota QUOTA`` | Set storage quota of the new repository (e.g. 5G, 1.5T). Default: no quota. | +-------------------------------------------------------+------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--make-parent-dirs`` | create the parent directories of the repository directory, if they are missing. | +-------------------------------------------------------+------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY repository to create optional arguments -e MODE, --encryption MODE select encryption key mode **(required)** --append-only create an append-only mode repository. Note that this only affects the low level structure of the repository, and running `delete` or `prune` will still be allowed. See :ref:`append_only_mode` in Additional Notes for more details. --storage-quota QUOTA Set storage quota of the new repository (e.g. 5G, 1.5T). Default: no quota. --make-parent-dirs create the parent directories of the repository directory, if they are missing. :ref:`common_options` | Description ~~~~~~~~~~~ This command initializes an empty repository. A repository is a filesystem directory containing the deduplicated data from zero or more archives. Encryption mode TLDR ++++++++++++++++++++ The encryption mode can only be configured when creating a new repository - you can neither configure it on a per-archive basis nor change the encryption mode of an existing repository. Use ``repokey``:: borg init --encryption repokey /path/to/repo Or ``repokey-blake2`` depending on which is faster on your client machines (see below):: borg init --encryption repokey-blake2 /path/to/repo Borg will: 1. Ask you to come up with a passphrase. 2. Create a borg key (which contains 3 random secrets. See :ref:`key_files`). 3. Encrypt the key with your passphrase. 4. Store the encrypted borg key inside the repository directory (in the repo config). This is why it is essential to use a secure passphrase. 5. Encrypt and sign your backups to prevent anyone from reading or forging them unless they have the key and know the passphrase. Make sure to keep a backup of your key **outside** the repository - do not lock yourself out by "leaving your keys inside your car" (see :ref:`borg_key_export`). For remote backups the encryption is done locally - the remote machine never sees your passphrase, your unencrypted key or your unencrypted files. Chunking and id generation are also based on your key to improve your privacy. 6. Use the key when extracting files to decrypt them and to verify that the contents of the backups have not been accidentally or maliciously altered. Picking a passphrase ++++++++++++++++++++ Make sure you use a good passphrase. Not too short, not too simple. The real encryption / decryption key is encrypted with / locked by your passphrase. If an attacker gets your key, he can't unlock and use it without knowing the passphrase. Be careful with special or non-ascii characters in your passphrase: - Borg processes the passphrase as unicode (and encodes it as utf-8), so it does not have problems dealing with even the strangest characters. - BUT: that does not necessarily apply to your OS / VM / keyboard configuration. So better use a long passphrase made from simple ascii chars than one that includes non-ascii stuff or characters that are hard/impossible to enter on a different keyboard layout. You can change your passphrase for existing repos at any time, it won't affect the encryption/decryption key or other secrets. More encryption modes +++++++++++++++++++++ Only use ``--encryption none`` if you are OK with anyone who has access to your repository being able to read your backups and tamper with their contents without you noticing. If you want "passphrase and having-the-key" security, use ``--encryption keyfile``. The key will be stored in your home directory (in ``~/.config/borg/keys``). If you do **not** want to encrypt the contents of your backups, but still want to detect malicious tampering use ``--encryption authenticated``. To normally work with ``authenticated`` repos, you will need the passphrase, but there is an emergency workaround, see ``BORG_WORKAROUNDS=authenticated_no_key`` docs. If ``BLAKE2b`` is faster than ``SHA-256`` on your hardware, use ``--encryption authenticated-blake2``, ``--encryption repokey-blake2`` or ``--encryption keyfile-blake2``. Note: for remote backups the hashing is done on your local machine. .. nanorst: inline-fill +----------+---------------+------------------------+--------------------------+ | Hash/MAC | Not encrypted | Not encrypted, | Encrypted (AEAD w/ AES) | | | no auth | but authenticated | and authenticated | +----------+---------------+------------------------+--------------------------+ | SHA-256 | none | `authenticated` | repokey | | | | | keyfile | +----------+---------------+------------------------+--------------------------+ | BLAKE2b | n/a | `authenticated-blake2` | `repokey-blake2` | | | | | `keyfile-blake2` | +----------+---------------+------------------------+--------------------------+ .. nanorst: inline-replace Modes `marked like this` in the above table are new in Borg 1.1 and are not backwards-compatible with Borg 1.0.x. On modern Intel/AMD CPUs (except very cheap ones), AES is usually hardware-accelerated. BLAKE2b is faster than SHA256 on Intel/AMD 64-bit CPUs (except AMD Ryzen and future CPUs with SHA extensions), which makes `authenticated-blake2` faster than `none` and `authenticated`. On modern ARM CPUs, NEON provides hardware acceleration for SHA256 making it faster than BLAKE2b-256 there. NEON accelerates AES as well. Hardware acceleration is always used automatically when available. `repokey` and `keyfile` use AES-CTR-256 for encryption and HMAC-SHA256 for authentication in an encrypt-then-MAC (EtM) construction. The chunk ID hash is HMAC-SHA256 as well (with a separate key). These modes are compatible with Borg 1.0.x. `repokey-blake2` and `keyfile-blake2` are also authenticated encryption modes, but use BLAKE2b-256 instead of HMAC-SHA256 for authentication. The chunk ID hash is a keyed BLAKE2b-256 hash. These modes are new and *not* compatible with Borg 1.0.x. `authenticated` mode uses no encryption, but authenticates repository contents through the same HMAC-SHA256 hash as the `repokey` and `keyfile` modes (it uses it as the chunk ID hash). The key is stored like `repokey`. This mode is new and *not* compatible with Borg 1.0.x. `authenticated-blake2` is like `authenticated`, but uses the keyed BLAKE2b-256 hash from the other blake2 modes. This mode is new and *not* compatible with Borg 1.0.x. `none` mode uses no encryption and no authentication. It uses SHA256 as chunk ID hash. This mode is not recommended, you should rather consider using an authenticated or authenticated/encrypted mode. This mode has possible denial-of-service issues when running ``borg create`` on contents controlled by an attacker. Use it only for new repositories where no encryption is wanted **and** when compatibility with 1.0.x is important. If compatibility with 1.0.x is not important, use `authenticated-blake2` or `authenticated` instead. This mode is compatible with Borg 1.0.x.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/key.rst0000644000076500000240000000272314601576577015622 0ustar00twstaff.. _borg-change-passphrase: .. include:: key_change-passphrase.rst.inc Examples ~~~~~~~~ :: # Create a key file protected repository $ borg init --encryption=keyfile -v /path/to/repo Initializing repository at "/path/to/repo" Enter new passphrase: Enter same passphrase again: Remember your passphrase. Your data will be inaccessible without it. Key in "/root/.config/borg/keys/mnt_backup" created. Keep this key safe. Your data will be inaccessible without it. Synchronizing chunks cache... Archives: 0, w/ cached Idx: 0, w/ outdated Idx: 0, w/o cached Idx: 0. Done. # Change key file passphrase $ borg key change-passphrase -v /path/to/repo Enter passphrase for key /root/.config/borg/keys/mnt_backup: Enter new passphrase: Enter same passphrase again: Remember your passphrase. Your data will be inaccessible without it. Key updated # Import a previously-exported key into the specified # key file (creating or overwriting the output key) # (keyfile repositories only) $ BORG_KEY_FILE=/path/to/output-key borg key import /path/to/repo /path/to/exported Fully automated using environment variables: :: $ BORG_NEW_PASSPHRASE=old borg init -e=repokey repo # now "old" is the current passphrase. $ BORG_PASSPHRASE=old BORG_NEW_PASSPHRASE=new borg key change-passphrase repo # now "new" is the current passphrase. .. include:: key_export.rst.inc .. include:: key_import.rst.inc ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/key_change-passphrase.rst.inc0000644000076500000240000000333414601576577022045 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_key_change-passphrase: borg key change-passphrase -------------------------- .. code-block:: none borg [common options] key change-passphrase [options] [REPOSITORY] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+----------------+--+ | **positional arguments** | +-------------------------------------------------------+----------------+--+ | | ``REPOSITORY`` | | +-------------------------------------------------------+----------------+--+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+----------------+--+ .. raw:: html .. only:: latex REPOSITORY :ref:`common_options` | Description ~~~~~~~~~~~ The key files used for repository encryption are optionally passphrase protected. This command can be used to change this passphrase. Please note that this command only changes the passphrase, but not any secret protected by it (like e.g. encryption/MAC keys or chunker seed). Thus, changing the passphrase after passphrase and borg key got compromised does not protect future (nor past) backups to the same repository.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/key_export.rst.inc0000644000076500000240000001164614601576577017777 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_key_export: borg key export --------------- .. code-block:: none borg [common options] key export [options] [REPOSITORY] [PATH] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+----------------+------------------------------------------------------------------------+ | **positional arguments** | +-------------------------------------------------------+----------------+------------------------------------------------------------------------+ | | ``REPOSITORY`` | | +-------------------------------------------------------+----------------+------------------------------------------------------------------------+ | | ``PATH`` | where to store the backup | +-------------------------------------------------------+----------------+------------------------------------------------------------------------+ | **optional arguments** | +-------------------------------------------------------+----------------+------------------------------------------------------------------------+ | | ``--paper`` | Create an export suitable for printing and later type-in | +-------------------------------------------------------+----------------+------------------------------------------------------------------------+ | | ``--qr-html`` | Create an html file suitable for printing and later type-in or qr scan | +-------------------------------------------------------+----------------+------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+----------------+------------------------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY PATH where to store the backup optional arguments --paper Create an export suitable for printing and later type-in --qr-html Create an html file suitable for printing and later type-in or qr scan :ref:`common_options` | Description ~~~~~~~~~~~ If repository encryption is used, the repository is inaccessible without the key. This command allows one to backup this essential key. Note that the backup produced does not include the passphrase itself (i.e. the exported key stays encrypted). In order to regain access to a repository, one needs both the exported key and the original passphrase. There are three backup formats. The normal backup format is suitable for digital storage as a file. The ``--paper`` backup format is optimized for printing and typing in while importing, with per line checks to reduce problems with manual input. The ``--qr-html`` creates a printable HTML template with a QR code and a copy of the ``--paper``-formatted key. For repositories using keyfile encryption the key is saved locally on the system that is capable of doing backups. To guard against loss of this key, the key needs to be backed up independently of the main data backup. For repositories using the repokey encryption the key is saved in the repository in the config file. A backup is thus not strictly needed, but guards against the repository becoming inaccessible if the file is damaged for some reason. Examples:: borg key export /path/to/repo > encrypted-key-backup borg key export --paper /path/to/repo > encrypted-key-backup.txt borg key export --qr-html /path/to/repo > encrypted-key-backup.html # Or pass the output file as an argument instead of redirecting stdout: borg key export /path/to/repo encrypted-key-backup borg key export --paper /path/to/repo encrypted-key-backup.txt borg key export --qr-html /path/to/repo encrypted-key-backup.html ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/key_import.rst.inc0000644000076500000240000000704414601576577017765 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_key_import: borg key import --------------- .. code-block:: none borg [common options] key import [options] [REPOSITORY] [PATH] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+----------------+----------------------------------------------------------+ | **positional arguments** | +-------------------------------------------------------+----------------+----------------------------------------------------------+ | | ``REPOSITORY`` | | +-------------------------------------------------------+----------------+----------------------------------------------------------+ | | ``PATH`` | path to the backup ('-' to read from stdin) | +-------------------------------------------------------+----------------+----------------------------------------------------------+ | **optional arguments** | +-------------------------------------------------------+----------------+----------------------------------------------------------+ | | ``--paper`` | interactively import from a backup done with ``--paper`` | +-------------------------------------------------------+----------------+----------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+----------------+----------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY PATH path to the backup ('-' to read from stdin) optional arguments --paper interactively import from a backup done with ``--paper`` :ref:`common_options` | Description ~~~~~~~~~~~ This command restores a key previously backed up with the export command. If the ``--paper`` option is given, the import will be an interactive process in which each line is checked for plausibility before proceeding to the next line. For this format PATH must not be given. For repositories using keyfile encryption, the key file which ``borg key import`` writes to depends on several factors. If the ``BORG_KEY_FILE`` environment variable is set and non-empty, ``borg key import`` creates or overwrites that file named by ``$BORG_KEY_FILE``. Otherwise, ``borg key import`` searches in the ``$BORG_KEYS_DIR`` directory for a key file associated with the repository. If a key file is found in ``$BORG_KEYS_DIR``, ``borg key import`` overwrites it; otherwise, ``borg key import`` creates a new key file in ``$BORG_KEYS_DIR``.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/key_migrate-to-repokey.rst.inc0000644000076500000240000000412414601576577022173 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_key_migrate-to-repokey: borg key migrate-to-repokey --------------------------- .. code-block:: none borg [common options] key migrate-to-repokey [options] [REPOSITORY] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+----------------+--+ | **positional arguments** | +-------------------------------------------------------+----------------+--+ | | ``REPOSITORY`` | | +-------------------------------------------------------+----------------+--+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+----------------+--+ .. raw:: html .. only:: latex REPOSITORY :ref:`common_options` | Description ~~~~~~~~~~~ This command migrates a repository from passphrase mode (removed in Borg 1.0) to repokey mode. You will be first asked for the repository passphrase (to open it in passphrase mode). This is the same passphrase as you used to use for this repo before 1.0. It will then derive the different secrets from this passphrase. Then you will be asked for a new passphrase (twice, for safety). This passphrase will be used to protect the repokey (which contains these same secrets in encrypted form). You may use the same passphrase as you used to use, but you may also use a different one. After migrating to repokey mode, you can change the passphrase at any time. But please note: the secrets will always stay the same and they could always be derived from your (old) passphrase-mode passphrase.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/list.rst0000644000076500000240000000407014601576577016002 0ustar00twstaff.. include:: list.rst.inc Examples ~~~~~~~~ :: $ borg list /path/to/repo Monday Mon, 2016-02-15 19:15:11 repo Mon, 2016-02-15 19:26:54 root-2016-02-15 Mon, 2016-02-15 19:36:29 newname Mon, 2016-02-15 19:50:19 ... $ borg list /path/to/repo::root-2016-02-15 drwxr-xr-x root root 0 Mon, 2016-02-15 17:44:27 . drwxrwxr-x root root 0 Mon, 2016-02-15 19:04:49 bin -rwxr-xr-x root root 1029624 Thu, 2014-11-13 00:08:51 bin/bash lrwxrwxrwx root root 0 Fri, 2015-03-27 20:24:26 bin/bzcmp -> bzdiff -rwxr-xr-x root root 2140 Fri, 2015-03-27 20:24:22 bin/bzdiff ... $ borg list /path/to/repo::root-2016-02-15 --pattern "- bin/ba*" drwxr-xr-x root root 0 Mon, 2016-02-15 17:44:27 . drwxrwxr-x root root 0 Mon, 2016-02-15 19:04:49 bin lrwxrwxrwx root root 0 Fri, 2015-03-27 20:24:26 bin/bzcmp -> bzdiff -rwxr-xr-x root root 2140 Fri, 2015-03-27 20:24:22 bin/bzdiff ... $ borg list /path/to/repo::archiveA --format="{mode} {user:6} {group:6} {size:8d} {isomtime} {path}{extra}{NEWLINE}" drwxrwxr-x user user 0 Sun, 2015-02-01 11:00:00 . drwxrwxr-x user user 0 Sun, 2015-02-01 11:00:00 code drwxrwxr-x user user 0 Sun, 2015-02-01 11:00:00 code/myproject -rw-rw-r-- user user 1416192 Sun, 2015-02-01 11:00:00 code/myproject/file.ext -rw-rw-r-- user user 1416192 Sun, 2015-02-01 11:00:00 code/myproject/file.text ... $ borg list /path/to/repo/::archiveA --pattern '+ re:\.ext$' --pattern '- re:^.*$' -rw-rw-r-- user user 1416192 Sun, 2015-02-01 11:00:00 code/myproject/file.ext ... $ borg list /path/to/repo/::archiveA --pattern '+ re:.ext$' --pattern '- re:^.*$' -rw-rw-r-- user user 1416192 Sun, 2015-02-01 11:00:00 code/myproject/file.ext -rw-rw-r-- user user 1416192 Sun, 2015-02-01 11:00:00 code/myproject/file.text ... ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/list.rst.inc0000644000076500000240000005616714601576577016570 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_list: borg list --------- .. code-block:: none borg [common options] list [options] [REPOSITORY_OR_ARCHIVE] [PATH...] .. only:: html .. class:: borg-options-table +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``REPOSITORY_OR_ARCHIVE`` | repository or archive to list contents of | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``PATH`` | paths to list; patterns are supported | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--consider-checkpoints`` | Show checkpoint archives in the repository contents list (default: hidden). | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--short`` | only print file/directory names, nothing else | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--format FORMAT`` | specify format for file or archive listing (default for files: "{mode} {user:6} {group:6} {size:8} {mtime} {path}{extra}{NL}"; for archives: "{archive:<36} {time} [{id}]{NL}") | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--json`` | Only valid for listing repository contents. Format output as JSON. The form of ``--format`` is ignored, but keys used in it are added to the JSON output. Some keys are always present. Note: JSON can only represent text. A "barchive" key is therefore not available. | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--json-lines`` | Only valid for listing archive contents. Format output as JSON Lines. The form of ``--format`` is ignored, but keys used in it are added to the JSON output. Some keys are always present. Note: JSON can only represent text. A "bpath" key is therefore not available. | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Archive filters** — Archive filters can be applied to repository targets. | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-P PREFIX``, ``--prefix PREFIX`` | only consider archive names starting with this prefix. (deprecated) | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-a GLOB``, ``--glob-archives GLOB`` | only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see "borg help patterns". | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--sort-by KEYS`` | Comma-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--first N`` | consider first N archives after other filters were applied | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--last N`` | consider last N archives after other filters were applied | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Include/Exclude options** | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-e PATTERN``, ``--exclude PATTERN`` | exclude paths matching PATTERN | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--exclude-from EXCLUDEFILE`` | read exclude patterns from EXCLUDEFILE, one per line | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--pattern PATTERN`` | include/exclude paths matching PATTERN | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--patterns-from PATTERNFILE`` | read include/exclude patterns from PATTERNFILE, one per line | +-----------------------------------------------------------------------------+---------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY_OR_ARCHIVE repository or archive to list contents of PATH paths to list; patterns are supported optional arguments --consider-checkpoints Show checkpoint archives in the repository contents list (default: hidden). --short only print file/directory names, nothing else --format FORMAT specify format for file or archive listing (default for files: "{mode} {user:6} {group:6} {size:8} {mtime} {path}{extra}{NL}"; for archives: "{archive:<36} {time} [{id}]{NL}") --json Only valid for listing repository contents. Format output as JSON. The form of ``--format`` is ignored, but keys used in it are added to the JSON output. Some keys are always present. Note: JSON can only represent text. A "barchive" key is therefore not available. --json-lines Only valid for listing archive contents. Format output as JSON Lines. The form of ``--format`` is ignored, but keys used in it are added to the JSON output. Some keys are always present. Note: JSON can only represent text. A "bpath" key is therefore not available. :ref:`common_options` | Archive filters -P PREFIX, --prefix PREFIX only consider archive names starting with this prefix. (deprecated) -a GLOB, --glob-archives GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see "borg help patterns". --sort-by KEYS Comma-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp --first N consider first N archives after other filters were applied --last N consider last N archives after other filters were applied Include/Exclude options -e PATTERN, --exclude PATTERN exclude paths matching PATTERN --exclude-from EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line --pattern PATTERN include/exclude paths matching PATTERN --patterns-from PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line Description ~~~~~~~~~~~ This command lists the contents of a repository or an archive. For more help on include/exclude patterns, see the :ref:`borg_patterns` command output. .. man NOTES The FORMAT specifier syntax +++++++++++++++++++++++++++ The ``--format`` option uses python's `format string syntax `_. Examples: :: $ borg list --format '{archive}{NL}' /path/to/repo ArchiveFoo ArchiveBar ... # {VAR:NUMBER} - pad to NUMBER columns. # Strings are left-aligned, numbers are right-aligned. # Note: time columns except ``isomtime``, ``isoctime`` and ``isoatime`` cannot be padded. $ borg list --format '{archive:36} {time} [{id}]{NL}' /path/to/repo ArchiveFoo Thu, 2021-12-09 10:22:28 [0b8e9a312bef3f2f6e2d0fc110c196827786c15eba0188738e81697a7fa3b274] $ borg list --format '{mode} {user:6} {group:6} {size:8} {mtime} {path}{extra}{NL}' /path/to/repo::ArchiveFoo -rw-rw-r-- user user 1024 Thu, 2021-12-09 10:22:17 file-foo ... # {VAR:NUMBER} - pad to NUMBER columns right-aligned. $ borg list --format '{mode} {user:>6} {group:>6} {size:<8} {mtime} {path}{extra}{NL}' /path/to/repo::ArchiveFoo -rw-rw-r-- user user 1024 Thu, 2021-12-09 10:22:17 file-foo ... The following keys are always available: - NEWLINE: OS dependent line separator - NL: alias of NEWLINE - NUL: NUL character for creating print0 / xargs -0 like output, see barchive and bpath keys below - SPACE - TAB - CR - LF Keys available only when listing archives in a repository: - archive: archive name interpreted as text (might be missing non-text characters, see barchive) - name: alias of "archive" - barchive: verbatim archive name, can contain any character except NUL - comment: archive comment interpreted as text (might be missing non-text characters, see bcomment) - bcomment: verbatim archive comment, can contain any character except NUL - id: internal ID of the archive - tam: TAM authentication state of this archive - start: time (start) of creation of the archive - time: alias of "start" - end: time (end) of creation of the archive - command_line: command line which was used to create the archive - hostname: hostname of host on which this archive was created - username: username of user who created this archive Keys available only when listing files in an archive: - type - mode - uid - gid - user - group - path: path interpreted as text (might be missing non-text characters, see bpath) - bpath: verbatim POSIX path, can contain any character except NUL - source: link target for links (identical to linktarget) - linktarget - flags - size - csize: compressed size - dsize: deduplicated size - dcsize: deduplicated compressed size - num_chunks: number of chunks in this file - unique_chunks: number of unique chunks in this file - mtime - ctime - atime - isomtime - isoctime - isoatime - blake2b - blake2s - md5 - sha1 - sha224 - sha256 - sha384 - sha3_224 - sha3_256 - sha3_384 - sha3_512 - sha512 - xxh64: XXH64 checksum of this file (note: this is NOT a cryptographic hash!) - archiveid - archivename - extra: prepends {source} with " -> " for soft links and " link to " for hard links - health: either "healthy" (file ok) or "broken" (if file has all-zero replacement chunks) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/lock.rst0000644000076500000240000000010014601576577015745 0ustar00twstaff.. include:: with-lock.rst.inc .. include:: break-lock.rst.inc ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/mount.rst0000644000076500000240000000431314601576577016171 0ustar00twstaff.. include:: mount.rst.inc .. include:: umount.rst.inc Examples ~~~~~~~~ :: # Mounting the repository shows all archives. # Archives are loaded lazily, expect some delay when navigating to an archive # for the first time. $ borg mount /path/to/repo /tmp/mymountpoint $ ls /tmp/mymountpoint root-2016-02-14 root-2016-02-15 $ borg umount /tmp/mymountpoint # Mounting a specific archive is possible as well. $ borg mount /path/to/repo::root-2016-02-15 /tmp/mymountpoint $ ls /tmp/mymountpoint bin boot etc home lib lib64 lost+found media mnt opt root sbin srv tmp usr var $ borg umount /tmp/mymountpoint # The "versions view" merges all archives in the repository # and provides a versioned view on files. $ borg mount -o versions /path/to/repo /tmp/mymountpoint $ ls -l /tmp/mymountpoint/home/user/doc.txt/ total 24 -rw-rw-r-- 1 user group 12357 Aug 26 21:19 doc.cda00bc9.txt -rw-rw-r-- 1 user group 12204 Aug 26 21:04 doc.fa760f28.txt $ borg umount /tmp/mymountpoint # Archive filters are supported. # These are especially handy for the "versions view", # which does not support lazy processing of archives. $ borg mount -o versions --glob-archives '*-my-home' --last 10 /path/to/repo /tmp/mymountpoint # Exclusion options are supported. # These can speed up mounting and lower memory needs significantly. $ borg mount /path/to/repo /tmp/mymountpoint only/that/path $ borg mount --exclude '...' /path/to/repo /tmp/mymountpoint borgfs ++++++ :: $ echo '/mnt/backup /tmp/myrepo fuse.borgfs defaults,noauto 0 0' >> /etc/fstab $ echo '/mnt/backup::root-2016-02-15 /tmp/myarchive fuse.borgfs defaults,noauto 0 0' >> /etc/fstab $ mount /tmp/myrepo $ mount /tmp/myarchive $ ls /tmp/myrepo root-2016-02-01 root-2016-02-2015 $ ls /tmp/myarchive bin boot etc home lib lib64 lost+found media mnt opt root sbin srv tmp usr var .. Note:: ``borgfs`` will be automatically provided if you used a distribution package, ``pip`` or ``setup.py`` to install Borg. Users of the standalone binary will have to manually create a symlink (see :ref:`pyinstaller-binary`). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/mount.rst.inc0000644000076500000240000004214714601576577016750 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_mount: borg mount ---------- .. code-block:: none borg [common options] mount [options] REPOSITORY_OR_ARCHIVE MOUNTPOINT [PATH...] .. only:: html .. class:: borg-options-table +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``REPOSITORY_OR_ARCHIVE`` | repository or archive to mount | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``MOUNTPOINT`` | where to mount filesystem | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``PATH`` | paths to extract; patterns are supported | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--consider-checkpoints`` | Show checkpoint archives in the repository contents list (default: hidden). | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-f``, ``--foreground`` | stay in foreground, do not daemonize | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-o`` | Extra mount options | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--numeric-owner`` | deprecated, use ``--numeric-ids`` instead | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--numeric-ids`` | use numeric user and group identifiers from archive(s) | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **Archive filters** — Archive filters can be applied to repository targets. | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-P PREFIX``, ``--prefix PREFIX`` | only consider archive names starting with this prefix. (deprecated) | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-a GLOB``, ``--glob-archives GLOB`` | only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see "borg help patterns". | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--sort-by KEYS`` | Comma-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--first N`` | consider first N archives after other filters were applied | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--last N`` | consider last N archives after other filters were applied | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **Include/Exclude options** | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-e PATTERN``, ``--exclude PATTERN`` | exclude paths matching PATTERN | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--exclude-from EXCLUDEFILE`` | read exclude patterns from EXCLUDEFILE, one per line | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--pattern PATTERN`` | include/exclude paths matching PATTERN | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--patterns-from PATTERNFILE`` | read include/exclude patterns from PATTERNFILE, one per line | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--strip-components NUMBER`` | Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. | +-----------------------------------------------------------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY_OR_ARCHIVE repository or archive to mount MOUNTPOINT where to mount filesystem PATH paths to extract; patterns are supported optional arguments --consider-checkpoints Show checkpoint archives in the repository contents list (default: hidden). -f, --foreground stay in foreground, do not daemonize -o Extra mount options --numeric-owner deprecated, use ``--numeric-ids`` instead --numeric-ids use numeric user and group identifiers from archive(s) :ref:`common_options` | Archive filters -P PREFIX, --prefix PREFIX only consider archive names starting with this prefix. (deprecated) -a GLOB, --glob-archives GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see "borg help patterns". --sort-by KEYS Comma-separated list of sorting keys; valid keys are: timestamp, archive, name, id; default is: timestamp --first N consider first N archives after other filters were applied --last N consider last N archives after other filters were applied Include/Exclude options -e PATTERN, --exclude PATTERN exclude paths matching PATTERN --exclude-from EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line --pattern PATTERN include/exclude paths matching PATTERN --patterns-from PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line --strip-components NUMBER Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. Description ~~~~~~~~~~~ This command mounts an archive as a FUSE filesystem. This can be useful for browsing an archive or restoring individual files. Unless the ``--foreground`` option is given the command will run in the background until the filesystem is ``umounted``. The command ``borgfs`` provides a wrapper for ``borg mount``. This can also be used in fstab entries: ``/path/to/repo /mnt/point fuse.borgfs defaults,noauto 0 0`` To allow a regular user to use fstab entries, add the ``user`` option: ``/path/to/repo /mnt/point fuse.borgfs defaults,noauto,user 0 0`` For FUSE configuration and mount options, see the mount.fuse(8) manual page. Borg's default behavior is to use the archived user and group names of each file and map them to the system's respective user and group ids. Alternatively, using ``numeric-ids`` will instead use the archived user and group ids without any mapping. The ``uid`` and ``gid`` mount options (implemented by Borg) can be used to override the user and group ids of all files (i.e., ``borg mount -o uid=1000,gid=1000``). The man page references ``user_id`` and ``group_id`` mount options (implemented by fuse) which specify the user and group id of the mount owner (aka, the user who does the mounting). It is set automatically by libfuse (or the filesystem if libfuse is not used). However, you should not specify these manually. Unlike the ``uid`` and ``gid`` mount options which affect all files, ``user_id`` and ``group_id`` affect the user and group id of the mounted (base) directory. Additional mount options supported by borg: - ``versions``: when used with a repository mount, this gives a merged, versioned view of the files in the archives. EXPERIMENTAL, layout may change in future. - ``allow_damaged_files``: by default damaged files (where missing chunks were replaced with runs of zeros by ``borg check --repair``) are not readable and return EIO (I/O error). Set this option to read such files. - ``ignore_permissions``: for security reasons the ``default_permissions`` mount option is internally enforced by borg. ``ignore_permissions`` can be given to not enforce ``default_permissions``. The BORG_MOUNT_DATA_CACHE_ENTRIES environment variable is meant for advanced users to tweak the performance. It sets the number of cached data chunks; additional memory usage can be up to ~8 MiB times this number. The default is the number of CPU cores. When the daemonized process receives a signal or crashes, it does not unmount. Unmounting in these cases could cause an active rsync or similar process to unintentionally delete data. When running in the foreground ^C/SIGINT unmounts cleanly, but other signals or crashes do not.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/notes.rst0000644000076500000240000003400514601576577016160 0ustar00twstaffAdditional Notes ---------------- Here are misc. notes about topics that are maybe not covered in enough detail in the usage section. .. _chunker-params: ``--chunker-params`` ~~~~~~~~~~~~~~~~~~~~ The chunker params influence how input files are cut into pieces (chunks) which are then considered for deduplication. They also have a big impact on resource usage (RAM and disk space) as the amount of resources needed is (also) determined by the total amount of chunks in the repository (see :ref:`cache-memory-usage` for details). ``--chunker-params=buzhash,10,23,16,4095`` results in a fine-grained deduplication and creates a big amount of chunks and thus uses a lot of resources to manage them. This is good for relatively small data volumes and if the machine has a good amount of free RAM and disk space. ``--chunker-params=buzhash,19,23,21,4095`` (default) results in a coarse-grained deduplication and creates a much smaller amount of chunks and thus uses less resources. This is good for relatively big data volumes and if the machine has a relatively low amount of free RAM and disk space. ``--chunker-params=fixed,4194304`` results in fixed 4MiB sized block deduplication and is more efficient than the previous example when used for for block devices (like disks, partitions, LVM LVs) or raw disk image files. ``--chunker-params=fixed,4096,512`` results in fixed 4kiB sized blocks, but the first header block will only be 512B long. This might be useful to dedup files with 1 header + N fixed size data blocks. Be careful to not produce a too big amount of chunks (like using small block size for huge files). If you already have made some archives in a repository and you then change chunker params, this of course impacts deduplication as the chunks will be cut differently. In the worst case (all files are big and were touched in between backups), this will store all content into the repository again. Usually, it is not that bad though: - usually most files are not touched, so it will just re-use the old chunks it already has in the repo - files smaller than the (both old and new) minimum chunksize result in only one chunk anyway, so the resulting chunks are same and deduplication will apply If you switch chunker params to save resources for an existing repo that already has some backup archives, you will see an increasing effect over time, when more and more files have been touched and stored again using the bigger chunksize **and** all references to the smaller older chunks have been removed (by deleting / pruning archives). If you want to see an immediate big effect on resource usage, you better start a new repository when changing chunker params. For more details, see :ref:`chunker_details`. ``--noatime / --noctime`` ~~~~~~~~~~~~~~~~~~~~~~~~~ You can use these ``borg create`` options to not store the respective timestamp into the archive, in case you do not really need it. Besides saving a little space for the not archived timestamp, it might also affect metadata stream deduplication: if only this timestamp changes between backups and is stored into the metadata stream, the metadata stream chunks won't deduplicate just because of that. ``--nobsdflags / --noflags`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can use this to not query and store (or not extract and set) flags - in case you don't need them or if they are broken somehow for your fs. On Linux, dealing with the flags needs some additional syscalls. Especially when dealing with lots of small files, this causes a noticeable overhead, so you can use this option also for speeding up operations. ``--umask`` ~~~~~~~~~~~ borg uses a safe default umask of 077 (that means the files borg creates have only permissions for owner, but no permissions for group and others) - so there should rarely be a need to change the default behaviour. This option only affects the process to which it is given. Thus, when you run borg in client/server mode and you want to change the behaviour on the server side, you need to use ``borg serve --umask=XXX ...`` as a ssh forced command in ``authorized_keys``. The ``--umask`` value given on the client side is **not** transferred to the server side. Also, if you choose to use the ``--umask`` option, always be consistent and use the same umask value so you do not create a mixup of permissions in a borg repository or with other files borg creates. ``--read-special`` ~~~~~~~~~~~~~~~~~~ The ``--read-special`` option is special - you do not want to use it for normal full-filesystem backups, but rather after carefully picking some targets for it. The option ``--read-special`` triggers special treatment for block and char device files as well as FIFOs. Instead of storing them as such a device (or FIFO), they will get opened, their content will be read and in the backup archive they will show up like a regular file. Symlinks will also get special treatment if (and only if) they point to such a special file: instead of storing them as a symlink, the target special file will get processed as described above. One intended use case of this is backing up the contents of one or multiple block devices, like e.g. LVM snapshots or inactive LVs or disk partitions. You need to be careful about what you include when using ``--read-special``, e.g. if you include ``/dev/zero``, your backup will never terminate. Restoring such files' content is currently only supported one at a time via ``--stdout`` option (and you have to redirect stdout to where ever it shall go, maybe directly into an existing device file of your choice or indirectly via ``dd``). To some extent, mounting a backup archive with the backups of special files via ``borg mount`` and then loop-mounting the image files from inside the mount point will work. If you plan to access a lot of data in there, it likely will scale and perform better if you do not work via the FUSE mount. Example +++++++ Imagine you have made some snapshots of logical volumes (LVs) you want to backup. .. note:: For some scenarios, this is a good method to get "crash-like" consistency (I call it crash-like because it is the same as you would get if you just hit the reset button or your machine would abruptly and completely crash). This is better than no consistency at all and a good method for some use cases, but likely not good enough if you have databases running. Then you create a backup archive of all these snapshots. The backup process will see a "frozen" state of the logical volumes, while the processes working in the original volumes continue changing the data stored there. You also add the output of ``lvdisplay`` to your backup, so you can see the LV sizes in case you ever need to recreate and restore them. After the backup has completed, you remove the snapshots again. :: $ # create snapshots here $ lvdisplay > lvdisplay.txt $ borg create --read-special /path/to/repo::arch lvdisplay.txt /dev/vg0/*-snapshot $ # remove snapshots here Now, let's see how to restore some LVs from such a backup. :: $ borg extract /path/to/repo::arch lvdisplay.txt $ # create empty LVs with correct sizes here (look into lvdisplay.txt). $ # we assume that you created an empty root and home LV and overwrite it now: $ borg extract --stdout /path/to/repo::arch dev/vg0/root-snapshot > /dev/vg0/root $ borg extract --stdout /path/to/repo::arch dev/vg0/home-snapshot > /dev/vg0/home .. _separate_compaction: Separate compaction ~~~~~~~~~~~~~~~~~~~ Borg does not auto-compact the segment files in the repository at commit time (at the end of each repository-writing command) any more. This is new since borg 1.2.0 and requires borg >= 1.2.0 on client and server. This causes a similar behaviour of the repository as if it was in append-only mode (see below) most of the time (until ``borg compact`` is invoked or an old client triggers auto-compaction). This has some notable consequences: - repository space is not freed immediately when deleting / pruning archives - commands finish quicker - repository is more robust and might be easier to recover after damages (as it contains data in a more sequential manner, historic manifests, multiple commits - until you run ``borg compact``) - user can choose when to run compaction (it should be done regularly, but not necessarily after each single borg command) - user can choose from where to invoke ``borg compact`` to do the compaction (from client or from server, it does not need a key) - less repo sync data traffic in case you create a copy of your repository by using a sync tool (like rsync, rclone, ...) You can manually run compaction by invoking the ``borg compact`` command. .. _append_only_mode: Append-only mode (forbid compaction) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A repository can be made "append-only", which means that Borg will never overwrite or delete committed data (append-only refers to the segment files, but borg will also reject to delete the repository completely). If ``borg compact`` command is used on a repo in append-only mode, there will be no warning or error, but no compaction will happen. append-only is useful for scenarios where a backup client machine backups remotely to a backup server using ``borg serve``, since a hacked client machine cannot delete backups on the server permanently. To activate append-only mode, set ``append_only`` to 1 in the repository config: :: borg config /path/to/repo append_only 1 Note that you can go back-and-forth between normal and append-only operation with ``borg config``; it's not a "one way trip." In append-only mode Borg will create a transaction log in the ``transactions`` file, where each line is a transaction and a UTC timestamp. In addition, ``borg serve`` can act as if a repository is in append-only mode with its option ``--append-only``. This can be very useful for fine-tuning access control in ``.ssh/authorized_keys``: :: command="borg serve --append-only ..." ssh-rsa command="borg serve ..." ssh-rsa Running ``borg init`` via a ``borg serve --append-only`` server will *not* create an append-only repository. Running ``borg init --append-only`` creates an append-only repository regardless of server settings. Example +++++++ Suppose an attacker remotely deleted all backups, but your repository was in append-only mode. A transaction log in this situation might look like this: :: transaction 1, UTC time 2016-03-31T15:53:27.383532 transaction 5, UTC time 2016-03-31T15:53:52.588922 transaction 11, UTC time 2016-03-31T15:54:23.887256 transaction 12, UTC time 2016-03-31T15:55:54.022540 transaction 13, UTC time 2016-03-31T15:55:55.472564 From your security logs you conclude the attacker gained access at 15:54:00 and all the backups where deleted or replaced by compromised backups. From the log you know that transactions 11 and later are compromised. Note that the transaction ID is the name of the *last* file in the transaction. For example, transaction 11 spans files 6 to 11. In a real attack you'll likely want to keep the compromised repository intact to analyze what the attacker tried to achieve. It's also a good idea to make this copy just in case something goes wrong during the recovery. Since recovery is done by deleting some files, a hard link copy (``cp -al``) is sufficient. The first step to reset the repository to transaction 5, the last uncompromised transaction, is to remove the ``hints.N``, ``index.N`` and ``integrity.N`` files in the repository (these files are always expendable). In this example N is 13. Then remove or move all segment files from the segment directories in ``data/`` starting with file 6:: rm data/**/{6..13} That's all to do in the repository. If you want to access this rolled back repository from a client that already has a cache for this repository, the cache will reflect a newer repository state than what you actually have in the repository now, after the rollback. Thus, you need to clear the cache:: borg delete --cache-only repo The cache will get rebuilt automatically. Depending on repo size and archive count, it may take a while. You also will need to remove ~/.config/borg/security/REPOID/manifest-timestamp. Drawbacks +++++++++ As data is only appended, and nothing removed, commands like ``prune`` or ``delete`` won't free disk space, they merely tag data as deleted in a new transaction. Be aware that as soon as you write to the repo in non-append-only mode (e.g. prune, delete or create archives from an admin machine), it will remove the deleted objects permanently (including the ones that were already marked as deleted, but not removed, in append-only mode). Automated edits to the repository (such as a cron job running ``borg prune``) will render append-only mode moot if data is deleted. Even if an archive appears to be available, it is possible an attacker could delete just a few chunks from an archive and silently corrupt its data. While in append-only mode, this is reversible, but ``borg check`` should be run before a writing/pruning operation on an append-only repository to catch accidental or malicious corruption:: # run without append-only mode borg check --verify-data repo && borg compact repo Aside from checking repository & archive integrity you may want to also manually check backups to ensure their content seems correct. Further considerations ++++++++++++++++++++++ Append-only mode is not respected by tools other than Borg. ``rm`` still works on the repository. Make sure that backup client machines only get to access the repository via ``borg serve``. Ensure that no remote access is possible if the repository is temporarily set to normal mode for e.g. regular pruning. Further protections can be implemented, but are outside of Borg's scope. For example, file system snapshots or wrapping ``borg serve`` to set special permissions or ACLs on new data files. SSH batch mode ~~~~~~~~~~~~~~ When running Borg using an automated script, ``ssh`` might still ask for a password, even if there is an SSH key for the target server. Use this to make scripts more robust:: export BORG_RSH='ssh -oBatchMode=yes' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/prune.rst0000644000076500000240000000320714601576577016161 0ustar00twstaff.. include:: prune.rst.inc Examples ~~~~~~~~ Be careful, prune is a potentially dangerous command, it will remove backup archives. The default of prune is to apply to **all archives in the repository** unless you restrict its operation to a subset of the archives using ``--glob-archives``. When using ``--glob-archives``, be careful to choose a good matching pattern - e.g. do not use "foo*" if you do not also want to match "foobar". It is strongly recommended to always run ``prune -v --list --dry-run ...`` first so you will see what it would do without it actually doing anything. :: # Keep 7 end of day and 4 additional end of week archives. # Do a dry-run without actually deleting anything. $ borg prune -v --list --dry-run --keep-daily=7 --keep-weekly=4 /path/to/repo # Same as above but only apply to archive names starting with the hostname # of the machine followed by a "-" character: $ borg prune -v --list --keep-daily=7 --keep-weekly=4 --glob-archives='{hostname}-*' /path/to/repo # actually free disk space: $ borg compact /path/to/repo # Keep 7 end of day, 4 additional end of week archives, # and an end of month archive for every month: $ borg prune -v --list --keep-daily=7 --keep-weekly=4 --keep-monthly=-1 /path/to/repo # Keep all backups in the last 10 days, 4 additional end of week archives, # and an end of month archive for every month: $ borg prune -v --list --keep-within=10d --keep-weekly=4 --keep-monthly=-1 /path/to/repo There is also a visualized prune example in ``docs/misc/prune-example.txt``: .. highlight:: none .. include:: ../misc/prune-example.txt :literal: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/prune.rst.inc0000644000076500000240000004140214601576577016730 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_prune: borg prune ---------- .. code-block:: none borg [common options] prune [options] [REPOSITORY] .. only:: html .. class:: borg-options-table +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``REPOSITORY`` | repository to prune | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-n``, ``--dry-run`` | do not change repository | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--force`` | force pruning of corrupted archives, use ``--force --force`` in case ``--force`` does not work. | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-s``, ``--stats`` | print statistics for the deleted archive | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--list`` | output verbose list of archives it keeps/prunes | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--keep-within INTERVAL`` | keep all archives within this time interval | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--keep-last``, ``--keep-secondly`` | number of secondly archives to keep | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--keep-minutely`` | number of minutely archives to keep | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-H``, ``--keep-hourly`` | number of hourly archives to keep | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-d``, ``--keep-daily`` | number of daily archives to keep | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-w``, ``--keep-weekly`` | number of weekly archives to keep | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-m``, ``--keep-monthly`` | number of monthly archives to keep | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-y``, ``--keep-yearly`` | number of yearly archives to keep | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``--save-space`` | work slower, but using less space | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-c SECONDS``, ``--checkpoint-interval SECONDS`` | write checkpoint every SECONDS seconds (Default: 1800) | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | **Archive filters** — Archive filters can be applied to repository targets. | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-P PREFIX``, ``--prefix PREFIX`` | only consider archive names starting with this prefix. (deprecated) | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | | ``-a GLOB``, ``--glob-archives GLOB`` | only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see "borg help patterns". | +-----------------------------------------------------------------------------+---------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY repository to prune optional arguments -n, --dry-run do not change repository --force force pruning of corrupted archives, use ``--force --force`` in case ``--force`` does not work. -s, --stats print statistics for the deleted archive --list output verbose list of archives it keeps/prunes --keep-within INTERVAL keep all archives within this time interval --keep-last, --keep-secondly number of secondly archives to keep --keep-minutely number of minutely archives to keep -H, --keep-hourly number of hourly archives to keep -d, --keep-daily number of daily archives to keep -w, --keep-weekly number of weekly archives to keep -m, --keep-monthly number of monthly archives to keep -y, --keep-yearly number of yearly archives to keep --save-space work slower, but using less space -c SECONDS, --checkpoint-interval SECONDS write checkpoint every SECONDS seconds (Default: 1800) :ref:`common_options` | Archive filters -P PREFIX, --prefix PREFIX only consider archive names starting with this prefix. (deprecated) -a GLOB, --glob-archives GLOB only consider archive names matching the glob. sh: rules apply (without actually using the sh: prefix), see "borg help patterns". Description ~~~~~~~~~~~ The prune command prunes a repository by deleting all archives not matching any of the specified retention options. Important: Repository disk space is **not** freed until you run ``borg compact``. This command is normally used by automated backup scripts wanting to keep a certain number of historic backups. This retention policy is commonly referred to as `GFS `_ (Grandfather-father-son) backup rotation scheme. Also, prune automatically removes checkpoint archives (incomplete archives left behind by interrupted backup runs) except if the checkpoint is the latest archive (and thus still needed). Checkpoint archives are not considered when comparing archive counts against the retention limits (``--keep-X``). If a prefix is set with -P, then only archives that start with the prefix are considered for deletion and only those archives count towards the totals specified by the rules. Otherwise, *all* archives in the repository are candidates for deletion! There is no automatic distinction between archives representing different contents. These need to be distinguished by specifying matching prefixes. If you have multiple sequences of archives with different data sets (e.g. from different machines) in one shared repository, use one prune call per data set that matches only the respective archives using the -P option. The ``--keep-within`` option takes an argument of the form "", where char is "H", "d", "w", "m", "y". For example, ``--keep-within 2d`` means to keep all archives that were created within the past 48 hours. "1m" is taken to mean "31d". The archives kept with this option do not count towards the totals specified by any other options. A good procedure is to thin out more and more the older your backups get. As an example, ``--keep-daily 7`` means to keep the latest backup on each day, up to 7 most recent days with backups (days without backups do not count). The rules are applied from secondly to yearly, and backups selected by previous rules do not count towards those of later rules. The time that each backup starts is used for pruning purposes. Dates and times are interpreted in the local timezone, and weeks go from Monday to Sunday. Specifying a negative number of archives to keep means that there is no limit. As of borg 1.2.0, borg will retain the oldest archive if any of the secondly, minutely, hourly, daily, weekly, monthly, or yearly rules was not otherwise able to meet its retention target. This enables the first chronological archive to continue aging until it is replaced by a newer archive that meets the retention criteria. The ``--keep-last N`` option is doing the same as ``--keep-secondly N`` (and it will keep the last N archives under the assumption that you do not create more than one backup archive in the same second). When using ``--stats``, you will get some statistics about how much data was deleted - the "Deleted data" deduplicated size there is most interesting as that is how much your repository will shrink. Please note that the "All archives" stats refer to the state after pruning.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/recreate.rst0000644000076500000240000000247014601576577016623 0ustar00twstaff.. include:: recreate.rst.inc Examples ~~~~~~~~ :: # Make old (Attic / Borg 0.xx) archives deduplicate with Borg 1.x archives. # Archives created with Borg 1.1+ and the default chunker params are skipped # (archive ID stays the same). $ borg recreate /mnt/backup --chunker-params default --progress # Create a backup with little but fast compression $ borg create /mnt/backup::archive /some/files --compression lz4 # Then compress it - this might take longer, but the backup has already completed, # so no inconsistencies from a long-running backup job. $ borg recreate /mnt/backup::archive --recompress --compression zlib,9 # Remove unwanted files from all archives in a repository. # Note the relative path for the --exclude option - archives only contain relative paths. $ borg recreate /mnt/backup --exclude home/icke/Pictures/drunk_photos # Change archive comment $ borg create --comment "This is a comment" /mnt/backup::archivename ~ $ borg info /mnt/backup::archivename Name: archivename Fingerprint: ... Comment: This is a comment ... $ borg recreate --comment "This is a better comment" /mnt/backup::archivename $ borg info /mnt/backup::archivename Name: archivename Fingerprint: ... Comment: This is a better comment ... ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/recreate.rst.inc0000644000076500000240000011734014601576577017376 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_recreate: borg recreate ------------- .. code-block:: none borg [common options] recreate [options] [REPOSITORY_OR_ARCHIVE] [PATH...] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **positional arguments** | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``REPOSITORY_OR_ARCHIVE`` | repository or archive to recreate | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``PATH`` | paths to recreate; patterns are supported | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--list`` | output verbose list of items (files, dirs, ...) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--filter STATUSCHARS`` | only display items with the given status characters (listed in borg create --help) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-n``, ``--dry-run`` | do not change anything | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-s``, ``--stats`` | print statistics at end | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Include/Exclude options** | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-e PATTERN``, ``--exclude PATTERN`` | exclude paths matching PATTERN | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--exclude-from EXCLUDEFILE`` | read exclude patterns from EXCLUDEFILE, one per line | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--pattern PATTERN`` | include/exclude paths matching PATTERN | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--patterns-from PATTERNFILE`` | read include/exclude patterns from PATTERNFILE, one per line | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--exclude-caches`` | exclude directories that contain a CACHEDIR.TAG file (http://www.bford.info/cachedir/spec.html) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--exclude-if-present NAME`` | exclude directories that are tagged by containing a filesystem object with the given NAME | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--keep-exclude-tags`` | if tag objects are specified with ``--exclude-if-present``, don't omit the tag objects themselves from the backup archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Archive options** | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--target TARGET`` | create a new archive with the name ARCHIVE, do not replace existing archive (only applies for a single archive) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-c SECONDS``, ``--checkpoint-interval SECONDS`` | write checkpoint every SECONDS seconds (Default: 1800) | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--comment COMMENT`` | add a comment text to the archive | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--timestamp TIMESTAMP`` | manually specify the archive creation date/time (UTC, yyyy-mm-ddThh:mm:ss format). alternatively, give a reference file/directory. | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``-C COMPRESSION``, ``--compression COMPRESSION`` | select compression algorithm, see the output of the "borg help compression" command for details. | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--recompress MODE`` | recompress data chunks according to `MODE` and ``--compression``. Possible modes are `if-different`: recompress if current compression is with a different compression algorithm (the level is not considered); `always`: recompress even if current compression is with the same compression algorithm (use this to change the compression level); and `never`: do not recompress (use this option to explicitly prevent recompression). If no MODE is given, `if-different` will be used. Not passing --recompress is equivalent to "--recompress never". | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--chunker-params PARAMS`` | rechunk using given chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, HASH_MASK_BITS, HASH_WINDOW_SIZE) or `default` to use the chunker defaults. default: do not rechunk | +-------------------------------------------------------+---------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY_OR_ARCHIVE repository or archive to recreate PATH paths to recreate; patterns are supported optional arguments --list output verbose list of items (files, dirs, ...) --filter STATUSCHARS only display items with the given status characters (listed in borg create --help) -n, --dry-run do not change anything -s, --stats print statistics at end :ref:`common_options` | Include/Exclude options -e PATTERN, --exclude PATTERN exclude paths matching PATTERN --exclude-from EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line --pattern PATTERN include/exclude paths matching PATTERN --patterns-from PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line --exclude-caches exclude directories that contain a CACHEDIR.TAG file (http://www.bford.info/cachedir/spec.html) --exclude-if-present NAME exclude directories that are tagged by containing a filesystem object with the given NAME --keep-exclude-tags if tag objects are specified with ``--exclude-if-present``, don't omit the tag objects themselves from the backup archive Archive options --target TARGET create a new archive with the name ARCHIVE, do not replace existing archive (only applies for a single archive) -c SECONDS, --checkpoint-interval SECONDS write checkpoint every SECONDS seconds (Default: 1800) --comment COMMENT add a comment text to the archive --timestamp TIMESTAMP manually specify the archive creation date/time (UTC, yyyy-mm-ddThh:mm:ss format). alternatively, give a reference file/directory. -C COMPRESSION, --compression COMPRESSION select compression algorithm, see the output of the "borg help compression" command for details. --recompress MODE recompress data chunks according to `MODE` and ``--compression``. Possible modes are `if-different`: recompress if current compression is with a different compression algorithm (the level is not considered); `always`: recompress even if current compression is with the same compression algorithm (use this to change the compression level); and `never`: do not recompress (use this option to explicitly prevent recompression). If no MODE is given, `if-different` will be used. Not passing --recompress is equivalent to "--recompress never". --chunker-params PARAMS rechunk using given chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, HASH_MASK_BITS, HASH_WINDOW_SIZE) or `default` to use the chunker defaults. default: do not rechunk Description ~~~~~~~~~~~ Recreate the contents of existing archives. recreate is a potentially dangerous function and might lead to data loss (if used wrongly). BE VERY CAREFUL! Important: Repository disk space is **not** freed until you run ``borg compact``. ``--exclude``, ``--exclude-from``, ``--exclude-if-present``, ``--keep-exclude-tags`` and PATH have the exact same semantics as in "borg create", but they only check for files in the archives and not in the local file system. If PATHs are specified, the resulting archives will only contain files from these PATHs. Note that all paths in an archive are relative, therefore absolute patterns/paths will *not* match (``--exclude``, ``--exclude-from``, PATHs). ``--recompress`` allows one to change the compression of existing data in archives. Due to how Borg stores compressed size information this might display incorrect information for archives that were not recreated at the same time. There is no risk of data loss by this. ``--chunker-params`` will re-chunk all files in the archive, this can be used to have upgraded Borg 0.xx or Attic archives deduplicate with Borg 1.x archives. **USE WITH CAUTION.** Depending on the PATHs and patterns given, recreate can be used to permanently delete files from archives. When in doubt, use ``--dry-run --verbose --list`` to see how patterns/PATHS are interpreted. See :ref:`list_item_flags` in ``borg create`` for details. The archive being recreated is only removed after the operation completes. The archive that is built during the operation exists at the same time at ".recreate". The new archive will have a different archive ID. With ``--target`` the original archive is not replaced, instead a new archive is created. When rechunking (or recompressing), space usage can be substantial - expect at least the entire deduplicated size of the archives using the previous chunker (or compression) params. If you recently ran borg check --repair and it had to fix lost chunks with all-zero replacement chunks, please first run another backup for the same data and re-run borg check --repair afterwards to heal any archives that had lost chunks which are still generated from the input data. Important: running borg recreate to re-chunk will remove the chunks_healthy metadata of all items with replacement chunks, so healing will not be possible any more after re-chunking (it is also unlikely it would ever work: due to the change of chunking parameters, the missing chunk likely will never be seen again even if you still have the data that produced it).././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/rename.rst0000644000076500000240000000053014601576577016273 0ustar00twstaff.. include:: rename.rst.inc Examples ~~~~~~~~ :: $ borg create /path/to/repo::archivename ~ $ borg list /path/to/repo archivename Mon, 2016-02-15 19:50:19 $ borg rename /path/to/repo::archivename newname $ borg list /path/to/repo newname Mon, 2016-02-15 19:50:19 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/rename.rst.inc0000644000076500000240000000351414601576577017050 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_rename: borg rename ----------- .. code-block:: none borg [common options] rename [options] ARCHIVE NEWNAME .. only:: html .. class:: borg-options-table +-------------------------------------------------------+-------------+-----------------------------+ | **positional arguments** | +-------------------------------------------------------+-------------+-----------------------------+ | | ``ARCHIVE`` | archive to rename | +-------------------------------------------------------+-------------+-----------------------------+ | | ``NEWNAME`` | the new archive name to use | +-------------------------------------------------------+-------------+-----------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+-------------+-----------------------------+ .. raw:: html .. only:: latex ARCHIVE archive to rename NEWNAME the new archive name to use :ref:`common_options` | Description ~~~~~~~~~~~ This command renames an archive in the repository. This results in a different archive ID.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/serve.rst0000644000076500000240000001030114601576577016145 0ustar00twstaff.. include:: serve.rst.inc Examples ~~~~~~~~ ``borg serve`` has special support for ssh forced commands (see ``authorized_keys`` example below): if the environment variable SSH_ORIGINAL_COMMAND is set it will ignore some options given on the command line and use the values from the variable instead. This only applies to a carefully controlled allowlist of safe options. This list currently contains: - Options that control the log level and debug topics printed such as ``--verbose``, ``--info``, ``--debug``, ``--debug-topic``, etc. - ``--lock-wait`` to allow the client to control how long to wait before giving up and aborting the operation when another process is holding a lock. Environment variables (such as BORG_XXX) contained in the original command sent by the client are *not* interpreted, but ignored. If BORG_XXX environment variables should be set on the ``borg serve`` side, then these must be set in system-specific locations like ``/etc/environment`` or in the forced command itself (example below). :: # Allow an SSH keypair to only run borg, and only have access to /path/to/repo. # Use key options to disable unneeded and potentially dangerous SSH functionality. # This will help to secure an automated remote backup system. $ cat ~/.ssh/authorized_keys command="borg serve --restrict-to-path /path/to/repo",restrict ssh-rsa AAAAB3[...] # Set a BORG_XXX environment variable on the "borg serve" side $ cat ~/.ssh/authorized_keys command="export BORG_XXX=value; borg serve [...]",restrict ssh-rsa [...] .. note:: The examples above use the ``restrict`` directive. This does automatically block potential dangerous ssh features, even when they are added in a future update. Thus, this option should be preferred. If you're using openssh-server < 7.2, however, you have to explicitly specify the ssh features to restrict and cannot simply use the restrict option as it has been introduced in v7.2. We recommend to use ``no-port-forwarding,no-X11-forwarding,no-pty,no-agent-forwarding,no-user-rc`` in this case. Details about sshd usage: `sshd(8) `_ SSH Configuration ~~~~~~~~~~~~~~~~~ ``borg serve``'s pipes (``stdin``/``stdout``/``stderr``) are connected to the ``sshd`` process on the server side. In the event that the SSH connection between ``borg serve`` and the client is disconnected or stuck abnormally (for example, due to a network outage), it can take a long time for ``sshd`` to notice the client is disconnected. In the meantime, ``sshd`` continues running, and as a result so does the ``borg serve`` process holding the lock on the repository. This can cause subsequent ``borg`` operations on the remote repository to fail with the error: ``Failed to create/acquire the lock``. In order to avoid this, it is recommended to perform the following additional SSH configuration: Either in the client side's ``~/.ssh/config`` file, or in the client's ``/etc/ssh/ssh_config`` file: :: Host backupserver ServerAliveInterval 10 ServerAliveCountMax 30 Replacing ``backupserver`` with the hostname, FQDN or IP address of the borg server. This will cause the client to send a keepalive to the server every 10 seconds. If 30 consecutive keepalives are sent without a response (a time of 300 seconds), the ssh client process will be terminated, causing the borg process to terminate gracefully. On the server side's ``sshd`` configuration file (typically ``/etc/ssh/sshd_config``): :: ClientAliveInterval 10 ClientAliveCountMax 30 This will cause the server to send a keep alive to the client every 10 seconds. If 30 consecutive keepalives are sent without a response (a time of 300 seconds), the server's sshd process will be terminated, causing the ``borg serve`` process to terminate gracefully and release the lock on the repository. If you then run borg commands with ``--lock-wait 600``, this gives sufficient time for the borg serve processes to terminate after the SSH connection is torn down after the 300 second wait for the keepalives to fail. You may, of course, modify the timeout values demonstrated above to values that suit your environment and use case. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/serve.rst.inc0000644000076500000240000002366514601576577016736 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_serve: borg serve ---------- .. code-block:: none borg [common options] serve [options] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+-----------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | **optional arguments** | +-------------------------------------------------------+-----------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--restrict-to-path PATH`` | restrict repository access to PATH. Can be specified multiple times to allow the client access to several directories. Access to all sub-directories is granted implicitly; PATH doesn't need to directly point to a repository. | +-------------------------------------------------------+-----------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--restrict-to-repository PATH`` | restrict repository access. Only the repository located at PATH (no sub-directories are considered) is accessible. Can be specified multiple times to allow the client access to several repositories. Unlike ``--restrict-to-path`` sub-directories are not accessible; PATH needs to directly point at a repository location. PATH may be an empty directory or the last element of PATH may not exist, in which case the client may initialize a repository there. | +-------------------------------------------------------+-----------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--append-only`` | only allow appending to repository segment files. Note that this only affects the low level structure of the repository, and running `delete` or `prune` will still be allowed. See :ref:`append_only_mode` in Additional Notes for more details. | +-------------------------------------------------------+-----------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ``--storage-quota QUOTA`` | Override storage quota of the repository (e.g. 5G, 1.5T). When a new repository is initialized, sets the storage quota on the new repository as well. Default: no quota. | +-------------------------------------------------------+-----------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+-----------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex optional arguments --restrict-to-path PATH restrict repository access to PATH. Can be specified multiple times to allow the client access to several directories. Access to all sub-directories is granted implicitly; PATH doesn't need to directly point to a repository. --restrict-to-repository PATH restrict repository access. Only the repository located at PATH (no sub-directories are considered) is accessible. Can be specified multiple times to allow the client access to several repositories. Unlike ``--restrict-to-path`` sub-directories are not accessible; PATH needs to directly point at a repository location. PATH may be an empty directory or the last element of PATH may not exist, in which case the client may initialize a repository there. --append-only only allow appending to repository segment files. Note that this only affects the low level structure of the repository, and running `delete` or `prune` will still be allowed. See :ref:`append_only_mode` in Additional Notes for more details. --storage-quota QUOTA Override storage quota of the repository (e.g. 5G, 1.5T). When a new repository is initialized, sets the storage quota on the new repository as well. Default: no quota. :ref:`common_options` | Description ~~~~~~~~~~~ This command starts a repository server process. This command is usually not used manually.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/tar.rst0000644000076500000240000000134314601576577015615 0ustar00twstaff.. include:: import-tar.rst.inc .. include:: export-tar.rst.inc Examples ~~~~~~~~ :: # export as uncompressed tar $ borg export-tar /path/to/repo::Monday Monday.tar # exclude some types, compress using gzip $ borg export-tar /path/to/repo::Monday Monday.tar.gz --exclude '*.so' # use higher compression level with gzip $ borg export-tar --tar-filter="gzip -9" testrepo::linux Monday.tar.gz # export a tar, but instead of storing it on disk, # upload it to a remote site using curl. $ borg export-tar /path/to/repo::Monday - | curl --data-binary @- https://somewhere/to/POST # remote extraction via "tarpipe" $ borg export-tar /path/to/repo::Monday - | ssh somewhere "cd extracted; tar x" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/umount.rst.inc0000644000076500000240000000352114601576577017126 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_umount: borg umount ----------- .. code-block:: none borg [common options] umount [options] MOUNTPOINT .. only:: html .. class:: borg-options-table +-------------------------------------------------------+----------------+----------------------------------------+ | **positional arguments** | +-------------------------------------------------------+----------------+----------------------------------------+ | | ``MOUNTPOINT`` | mountpoint of the filesystem to umount | +-------------------------------------------------------+----------------+----------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+----------------+----------------------------------------+ .. raw:: html .. only:: latex MOUNTPOINT mountpoint of the filesystem to umount :ref:`common_options` | Description ~~~~~~~~~~~ This command un-mounts a FUSE filesystem that was mounted with ``borg mount``. This is a convenience wrapper that just calls the platform-specific shell command - usually this is either umount or fusermount -u.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/upgrade.rst0000644000076500000240000000167514601576577016466 0ustar00twstaff.. include:: upgrade.rst.inc Examples ~~~~~~~~ :: # Upgrade the borg repository to the most recent version. $ borg upgrade -v /path/to/repo making a hardlink copy in /path/to/repo.before-upgrade-2016-02-15-20:51:55 opening attic repository with borg and converting no key file found for repository converting repo index /path/to/repo/index.0 converting 1 segments... converting borg 0.xx to borg current no key file found for repository .. _borg_key_migrate-to-repokey: Upgrading a passphrase encrypted attic repo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ attic offered a "passphrase" encryption mode, but this was removed in borg 1.0 and replaced by the "repokey" mode (which stores the passphrase-protected encryption key into the repository config). Thus, to upgrade a "passphrase" attic repo to a "repokey" borg repo, 2 steps are needed, in this order: - borg upgrade repo - borg key migrate-to-repokey repo ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/upgrade.rst.inc0000644000076500000240000002417514601576577017236 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_upgrade: borg upgrade ------------ .. code-block:: none borg [common options] upgrade [options] [REPOSITORY] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ | **positional arguments** | +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ | | ``REPOSITORY`` | path to the repository to be upgraded | +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ | **optional arguments** | +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ | | ``-n``, ``--dry-run`` | do not change repository | +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ | | ``--inplace`` | rewrite repository in place, with no chance of going back to older versions of the repository. | +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ | | ``--force`` | Force upgrade | +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ | | ``--tam`` | Enable manifest authentication (in key and cache) (Borg 1.0.9 and later). | +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ | | ``--check-tam`` | check manifest authentication (in key and cache). | +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ | | ``--disable-tam`` | Disable manifest authentication (in key and cache). | +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ | | ``--check-archives-tam`` | check TAM authentication for all archives. | +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ | | ``--archives-tam`` | add TAM authentication for all archives. | +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+--------------------------+------------------------------------------------------------------------------------------------+ .. raw:: html .. only:: latex REPOSITORY path to the repository to be upgraded optional arguments -n, --dry-run do not change repository --inplace rewrite repository in place, with no chance of going back to older versions of the repository. --force Force upgrade --tam Enable manifest authentication (in key and cache) (Borg 1.0.9 and later). --check-tam check manifest authentication (in key and cache). --disable-tam Disable manifest authentication (in key and cache). --check-archives-tam check TAM authentication for all archives. --archives-tam add TAM authentication for all archives. :ref:`common_options` | Description ~~~~~~~~~~~ Upgrade an existing, local Borg repository. When you do not need borg upgrade +++++++++++++++++++++++++++++++++ Not every change requires that you run ``borg upgrade``. You do **not** need to run it when: - moving your repository to a different place - upgrading to another point release (like 1.0.x to 1.0.y), except when noted otherwise in the changelog - upgrading from 1.0.x to 1.1.x, except when noted otherwise in the changelog Borg 1.x.y upgrades +++++++++++++++++++ Archive TAM authentication: Use ``borg upgrade --archives-tam REPO`` to add archive TAMs to all archives that are not TAM authenticated yet. This is a convenient method to just trust all archives present - if an archive does not have TAM authentication yet, a TAM will be added. Archives created by old borg versions < 1.0.9 do not have TAMs. Archives created by newer borg version should have TAMs already. If you have a high risk environment, you should not just run this, but first verify that the archives are authentic and not malicious (== have good content, have a good timestamp). Borg 1.2.5+ needs all archives to be TAM authenticated for safety reasons. This upgrade needs to be done once per repository. Manifest TAM authentication: Use ``borg upgrade --tam REPO`` to require manifest authentication introduced with Borg 1.0.9 to address security issues. This means that modifying the repository after doing this with a version prior to 1.0.9 will raise a validation error, so only perform this upgrade after updating all clients using the repository to 1.0.9 or newer. This upgrade should be done on each client for safety reasons. If a repository is accidentally modified with a pre-1.0.9 client after this upgrade, use ``borg upgrade --tam --force REPO`` to remedy it. If you routinely do this you might not want to enable this upgrade (which will leave you exposed to the security issue). You can reverse the upgrade by issuing ``borg upgrade --disable-tam REPO``. See https://borgbackup.readthedocs.io/en/stable/changes.html#pre-1-0-9-manifest-spoofing-vulnerability for details. Attic and Borg 0.xx to Borg 1.x +++++++++++++++++++++++++++++++ This currently supports converting an Attic repository to Borg and also helps with converting Borg 0.xx to 1.0. Currently, only LOCAL repositories can be upgraded (issue #465). Please note that ``borg create`` (since 1.0.0) uses bigger chunks by default than old borg or attic did, so the new chunks won't deduplicate with the old chunks in the upgraded repository. See ``--chunker-params`` option of ``borg create`` and ``borg recreate``. ``borg upgrade`` will change the magic strings in the repository's segments to match the new Borg magic strings. The keyfiles found in $ATTIC_KEYS_DIR or ~/.attic/keys/ will also be converted and copied to $BORG_KEYS_DIR or ~/.config/borg/keys. The cache files are converted, from $ATTIC_CACHE_DIR or ~/.cache/attic to $BORG_CACHE_DIR or ~/.cache/borg, but the cache layout between Borg and Attic changed, so it is possible the first backup after the conversion takes longer than expected due to the cache resync. Upgrade should be able to resume if interrupted, although it will still iterate over all segments. If you want to start from scratch, use `borg delete` over the copied repository to make sure the cache files are also removed:: borg delete borg Unless ``--inplace`` is specified, the upgrade process first creates a backup copy of the repository, in REPOSITORY.before-upgrade-DATETIME, using hardlinks. This requires that the repository and its parent directory reside on same filesystem so the hardlink copy can work. This takes longer than in place upgrades, but is much safer and gives progress information (as opposed to ``cp -al``). Once you are satisfied with the conversion, you can safely destroy the backup copy. WARNING: Running the upgrade in place will make the current copy unusable with older version, with no way of going back to previous versions. This can PERMANENTLY DAMAGE YOUR REPOSITORY! Attic CAN NOT READ BORG REPOSITORIES, as the magic strings have changed. You have been warned.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/usage_general.rst.inc0000644000076500000240000000101314601576577020372 0ustar00twstaff.. include:: general/positional-arguments.rst.inc .. include:: general/repository-urls.rst.inc .. include:: general/repository-locations.rst.inc .. include:: general/logging.rst.inc .. include:: general/return-codes.rst.inc .. _env_vars: .. include:: general/environment.rst.inc .. _file-systems: .. include:: general/file-systems.rst.inc .. include:: general/units.rst.inc .. include:: general/date-time.rst.inc .. include:: general/resources.rst.inc .. _platforms: .. include:: general/file-metadata.rst.inc ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage/with-lock.rst.inc0000644000076500000240000000524114601576577017501 0ustar00twstaff.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! .. _borg_with-lock: borg with-lock -------------- .. code-block:: none borg [common options] with-lock [options] REPOSITORY COMMAND [ARGS...] .. only:: html .. class:: borg-options-table +-------------------------------------------------------+----------------+--------------------+ | **positional arguments** | +-------------------------------------------------------+----------------+--------------------+ | | ``REPOSITORY`` | repository to lock | +-------------------------------------------------------+----------------+--------------------+ | | ``COMMAND`` | command to run | +-------------------------------------------------------+----------------+--------------------+ | | ``ARGS`` | command arguments | +-------------------------------------------------------+----------------+--------------------+ | .. class:: borg-common-opt-ref | | | | :ref:`common_options` | +-------------------------------------------------------+----------------+--------------------+ .. raw:: html .. only:: latex REPOSITORY repository to lock COMMAND command to run ARGS command arguments :ref:`common_options` | Description ~~~~~~~~~~~ This command runs a user-specified command while locking the repository. For example: :: $ borg with-lock /mnt/borgrepo rsync -av /mnt/borgrepo /somewhere/else/borgrepo It will first try to acquire the lock (make sure that no other operation is running in the repo), then execute the given command as a subprocess and wait for its termination, release the lock and return the user command's return code as borg's return code. .. note:: If you copy a repository with the lock held, the lock will be present in the copy. Thus, before using borg on the copy from a different host, you need to use "borg break-lock" on the copied repository, because Borg is cautious and does not automatically remove stale locks made by a different host.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage.rst0000644000076500000240000000303514601576577015027 0ustar00twstaff.. include:: global.rst.inc .. highlight:: none .. _detailed_usage: Usage ===== .. raw:: html Redirecting... .. toctree:: usage/general usage/init usage/create usage/extract usage/check usage/rename usage/list usage/diff usage/delete usage/prune usage/compact usage/info usage/mount usage/key usage/upgrade usage/recreate usage/tar usage/serve usage/config usage/lock usage/benchmark usage/help usage/debug usage/notes ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/docs/usage_general.rst.inc0000644000076500000240000000103314601576577017270 0ustar00twstaff.. include:: usage/general/positional-arguments.rst.inc .. include:: usage/general/repository-urls.rst.inc .. include:: usage/general/repository-locations.rst.inc .. include:: usage/general/logging.rst.inc .. include:: usage/general/return-codes.rst.inc .. include:: usage/general/environment.rst.inc .. include:: usage/general/file-systems.rst.inc .. include:: usage/general/units.rst.inc .. include:: usage/general/date-time.rst.inc .. include:: usage/general/resources.rst.inc .. include:: usage/general/file-metadata.rst.inc ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/pyproject.toml0000644000076500000240000000021014601576577015145 0ustar00twstaff[build-system] requires = ["setuptools", "wheel", "pkgconfig", "Cython", "setuptools_scm>=1.7"] build-backend = "setuptools.build_meta" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1711734324.141404 borgbackup-1.2.8/requirements.d/0000755000076500000240000000000014601577064015175 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/requirements.d/development.lock.txt0000644000076500000240000000033114601576577021214 0ustar00twstaffsetuptools==68.2.2 setuptools-scm==8.0.4 pip==23.3.1 virtualenv==20.24.6 pkgconfig==1.5.5 tox==4.11.3 pytest==7.4.3 pytest-xdist==3.3.1 pytest-cov==4.1.0 pytest-benchmark==4.0.0 Cython==0.29.37 python-dateutil==2.8.2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/requirements.d/development.txt0000644000076500000240000000024214601576577020266 0ustar00twstaffsetuptools >=45, !=60.6.0, !=60.7.0 setuptools_scm pip virtualenv pkgconfig tox pytest pytest-xdist pytest-cov pytest-benchmark Cython python-dateutil pre-commit ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/requirements.d/docs.txt0000644000076500000240000000003314601576577016672 0ustar00twstaffsphinx guzzle_sphinx_theme ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1441088 borgbackup-1.2.8/scripts/0000755000076500000240000000000014601577064013717 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/borg.exe.spec0000644000076500000240000000425314601576577016320 0ustar00twstaff# -*- mode: python -*- # this pyinstaller spec file is used to build borg binaries on posix platforms import os, sys is_win32 = sys.platform.startswith('win32') # Note: SPEC contains the spec file argument given to pyinstaller here = os.path.dirname(os.path.abspath(SPEC)) basepath = os.path.abspath(os.path.join(here, '..')) if is_win32: hiddenimports = [] else: hiddenimports = ['borg.platform.posix', 'pkg_resources.py2_warn', ] block_cipher = None a = Analysis([os.path.join(basepath, 'src', 'borg', '__main__.py'), ], pathex=[basepath, ], binaries=[], datas=[ (os.path.join(basepath, 'src', 'borg', 'paperkey.html'), 'borg'), ], hiddenimports=hiddenimports, hookspath=[], runtime_hooks=[], excludes=[ '_ssl', 'ssl', ], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher) if sys.platform == 'darwin': # do not bundle the osxfuse libraries, so we do not get a version # mismatch to the installed kernel driver of osxfuse. a.binaries = [b for b in a.binaries if 'libosxfuse' not in b[0]] pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, name='borg.exe', debug=False, strip=False, upx=True, console=True) # Build a directory-based binary in addition to a packed # single file. This allows one to easily look at all included # files (e.g. without having to strace or halt the built binary # and introspect /tmp). Also avoids unpacking all libs when # running the app, which is better for app signing on various OS. slim_exe = EXE(pyz, a.scripts, exclude_binaries=True, name='borg.exe', debug=False, strip=False, upx=False, console=True) coll = COLLECT(slim_exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=False, name='borg-dir') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/errorlist.py0000755000076500000240000000074014601576577016332 0ustar00twstaff#!/usr/bin/env python3 from textwrap import indent import borg.archiver # noqa: F401 - need import to get Error and ErrorWithTraceback subclasses. from borg.helpers import Error, ErrorWithTraceback classes = Error.__subclasses__() + ErrorWithTraceback.__subclasses__() for cls in sorted(classes, key=lambda cls: (cls.__module__, cls.__qualname__)): if cls is ErrorWithTraceback: continue print(' ', cls.__qualname__) print(indent(cls.__doc__, ' ' * 8)) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1449993 borgbackup-1.2.8/scripts/fuzz-cache-sync/0000755000076500000240000000000014601577064016730 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/fuzz-cache-sync/HOWTO0000644000076500000240000000055114601576577017564 0ustar00twstaff- Install AFL and the requirements for LLVM mode (see docs) - Compile the fuzzing target, e.g. AFL_HARDEN=1 afl-clang-fast main.c -o fuzz-target -O3 (other options, like using ASan or MSan are possible as well) - Add additional test cases to testcase_dir - Run afl, easiest (but inefficient) way; afl-fuzz -i testcase_dir -o findings_dir ./fuzz-target ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/fuzz-cache-sync/main.c0000644000076500000240000000130114601576577020023 0ustar00twstaff #define BORG_NO_PYTHON #include "../../src/borg/_hashindex.c" #include "../../src/borg/cache_sync/cache_sync.c" #define BUFSZ 32768 int main() { char buf[BUFSZ]; int len, ret; CacheSyncCtx *ctx; HashIndex *idx; /* capacity, key size, value size */ idx = hashindex_init(0, 32, 12); ctx = cache_sync_init(idx); while (1) { len = read(0, buf, BUFSZ); if (!len) { break; } ret = cache_sync_feed(ctx, buf, len); if(!ret && cache_sync_error(ctx)) { fprintf(stderr, "error: %s\n", cache_sync_error(ctx)); return 1; } } hashindex_free(idx); cache_sync_free(ctx); return 0; } ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1711734324.145254 borgbackup-1.2.8/scripts/fuzz-cache-sync/testcase_dir/0000755000076500000240000000000014601577064021401 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/fuzz-cache-sync/testcase_dir/test_simple0000644000076500000240000000022314601576577023661 0ustar00twstaff��foo�bar�baz�ңbar�.�user�chunks�chunks��� 00000000000000000000000000000001�� 00000000000000000000000000000002././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/glibc_check.py0000755000076500000240000000320614601576577016522 0ustar00twstaff#!/usr/bin/env python3 """ Check if all given binaries work with the given glibc version. glibc_check.py 2.11 bin [bin ...] rc = 0 means "yes", rc = 1 means "no". """ import re import subprocess import sys verbose = True objdump = "objdump -T %s" glibc_re = re.compile(r'GLIBC_([0-9]\.[0-9]+)') def parse_version(v): major, minor = v.split('.') return int(major), int(minor) def format_version(version): return "%d.%d" % version def main(): given = parse_version(sys.argv[1]) filenames = sys.argv[2:] overall_versions = set() for filename in filenames: try: output = subprocess.check_output(objdump % filename, shell=True, stderr=subprocess.STDOUT) output = output.decode() versions = {parse_version(match.group(1)) for match in glibc_re.finditer(output)} requires_glibc = max(versions) overall_versions.add(requires_glibc) if verbose: print(f"{filename} {format_version(requires_glibc)}") except subprocess.CalledProcessError: if verbose: print("%s errored." % filename) wanted = max(overall_versions) ok = given >= wanted if verbose: if ok: print("The binaries work with the given glibc %s." % format_version(given)) else: print("The binaries do not work with the given glibc %s. " "Minimum is: %s" % (format_version(given), format_version(wanted))) return ok if __name__ == '__main__': ok = main() sys.exit(0 if ok else 1) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/hash_sizes.py0000644000076500000240000000543714601576577016452 0ustar00twstaff""" Compute hashtable sizes with nices properties - prime sizes (for small to medium sizes) - 2 prime-factor sizes (for big sizes) - fast growth for small sizes - slow growth for big sizes Note: this is just a tool for developers. within borgbackup, it is just used to generate hash_sizes definition for _hashindex.c. """ from collections import namedtuple K, M, G = 2**10, 2**20, 2**30 # hash table size (in number of buckets) start, end_p1, end_p2 = 1 * K, 127 * M, 2 * G - 10 * M # stay well below 2^31 - 1 Policy = namedtuple("Policy", "upto grow") policies = [ # which growth factor to use when growing a hashtable of size < upto # grow fast (*2.0) at the start so we do not have to resize too often (expensive). # grow slow (*1.1) for huge hash tables (do not jump too much in memory usage) Policy(256*K, 2.0), Policy(2*M, 1.7), Policy(16*M, 1.4), Policy(128*M, 1.2), Policy(2*G-1, 1.1), ] # slightly modified version of: # http://www.macdevcenter.com/pub/a/python/excerpt/pythonckbk_chap1/index1.html?page=2 def eratosthenes(): """Yields the sequence of prime numbers via the Sieve of Eratosthenes.""" D = {} # map each composite integer to its first-found prime factor q = 2 # q gets 2, 3, 4, 5, ... ad infinitum while True: p = D.pop(q, None) if p is None: # q not a key in D, so q is prime, therefore, yield it yield q # mark q squared as not-prime (with q as first-found prime factor) D[q * q] = q else: # let x <- smallest (N*p)+q which wasn't yet known to be composite # we just learned x is composite, with p first-found prime factor, # since p is the first-found prime factor of q -- find and mark it x = p + q while x in D: x += p D[x] = p q += 1 def two_prime_factors(pfix=65537): """Yields numbers with 2 prime factors pfix and p.""" for p in eratosthenes(): yield pfix * p def get_grow_factor(size): for p in policies: if size < p.upto: return p.grow def find_bigger_prime(gen, i): while True: p = next(gen) if p >= i: return p def main(): sizes = [] i = start gen = eratosthenes() while i < end_p1: grow_factor = get_grow_factor(i) p = find_bigger_prime(gen, i) sizes.append(p) i = int(i * grow_factor) gen = two_prime_factors() # for lower ram consumption while i < end_p2: grow_factor = get_grow_factor(i) p = find_bigger_prime(gen, i) sizes.append(p) i = int(i * grow_factor) print("""\ static int hash_sizes[] = { %s }; """ % ', '.join(str(size) for size in sizes)) if __name__ == '__main__': main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/msys2-build0000644000076500000240000000016214601576577016023 0ustar00twstaff#!/bin/bash python setup.py build_ext --inplace python setup.py bdist_wheel pyinstaller -y scripts/borg.exe.spec ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/msys2-install-deps0000644000076500000240000000036314601576577017326 0ustar00twstaff#!/bin/bash pacman -S --needed --noconfirm git mingw-w64-ucrt-x86_64-{toolchain,pkgconf,zstd,lz4,xxhash,openssl,python,cython,python-setuptools,python-wheel,python-pkgconfig,python-packaging,python-msgpack,python-pip} pip install pyinstaller ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/sdist-sign0000755000076500000240000000046714601576577015750 0ustar00twstaff#!/bin/bash R=$1 if [ "$R" = "" ]; then echo "Usage: sdist-sign 1.2.3" exit fi if [ "$QUBES_GPG_DOMAIN" = "" ]; then GPG=gpg else GPG=qubes-gpg-client-wrapper fi python setup.py sdist D=dist/borgbackup-$R.tar.gz $GPG --detach-sign --local-user "Thomas Waldmann" --armor --output $D.asc $D ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.0927234 borgbackup-1.2.8/scripts/shell_completions/0000755000076500000240000000000014601577064017442 5ustar00twstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1453967 borgbackup-1.2.8/scripts/shell_completions/bash/0000755000076500000240000000000014601577064020357 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/shell_completions/bash/borg0000644000076500000240000002144214601576577021246 0ustar00twstaff# Completions for borg # https://www.borgbackup.org/ # Note: # Listing archives works on password protected repositories only if $BORG_PASSPHRASE is set. # Install: # Copy this file to /usr/share/bash-completion/completions/ or /etc/bash_completion.d/ _borg() { compopt -o default COMPREPLY=() local cur="${COMP_WORDS[COMP_CWORD]}" local prev="${COMP_WORDS[COMP_CWORD-1]}" local prevprev="${COMP_WORDS[COMP_CWORD-2]}" local common_opts="-h --help --version --critical --error --warning --info -v --verbose --debug --debug-topic -p --progress --log-json --lock-wait --show-version --show-rc --umask --remote-path --remote-ratelimit --consider-part-files --debug-profile --rsh" local opts="${common_opts}" # Commands if [[ ${COMP_CWORD} == 1 ]] ; then local borg_commands="init create extract check rename list diff delete prune compact info mount umount key serve upgrade recreate export-tar with-lock break-lock config benchmark help" COMPREPLY=( $(compgen -W "${borg_commands}" -- ${cur}) ) compopt +o default return 0 fi case "${prev}" in 'key') COMPREPLY=( $(compgen -W "import export change-passphrase" -- ${cur}) ) return 0 ;; 'benchmark') COMPREPLY=( $(compgen -W "crud" -- ${cur}) ) return 0 ;; 'help') COMPREPLY=( $(compgen -W "patterns placeholders compression" -- ${cur}) ) return 0 ;; '--encryption' | '-e') local encryption_modes="none keyfile keyfile-blake2 repokey repokey-blake2 authenticated authenticated-blake2" COMPREPLY=( $(compgen -W "${encryption_modes}" -- ${cur}) ) return 0 ;; '--files-cache') local files_cache_mode="ctime,size,inode mtime,size,inode ctime,size mtime,size rechunk,ctime rechunk,mtime size disabled" COMPREPLY=( $(compgen -W "${files_cache_mode}" -- ${cur}) ) return 0 ;; '--compression' | '-C') local compression_methods="none auto lz4 zstd,1 zstd,2 zstd,3 zstd,4 zstd,5 zstd,6 zstd,7 zstd,8 zstd,9 zstd,10 zstd,11 zstd,12 zstd,13 zstd,14 zstd,15 zstd,16 zstd,17 zstd,18 zstd,19 zstd,20 zstd,21 zstd,22 zlib,1 zlib,2 zlib,3 zlib,4 zlib,5 zlib,6 zlib,7 zlib,8 zlib,9 lzma,0 lzma,1 lzma,2 lzma,3 lzma,4 lzma,5 lzma,6 lzma,7 lzma,8 lzma,9" COMPREPLY=( $(compgen -W "${compression_methods}" -- ${cur}) ) return 0 ;; '--sort-by') local sort_keys="timestamp name id" COMPREPLY=( $(compgen -W "${sort_keys}" -- ${cur}) ) return 0 ;; '-o') # FIXME This list is probably not full, but I tried to pick only those that are relevant to borg mount -o: local fuse_options="ac_attr_timeout= allow_damaged_files allow_other allow_root attr_timeout= auto auto_cache auto_unmount default_permissions entry_timeout= gid= group_id= kernel_cache max_read= negative_timeout= noauto noforget remember= remount rootmode= uid= umask= user user_id= versions" COMPREPLY=( $(compgen -W "${fuse_options}" -- ${cur}) ) return 0 ;; '--recompress') local recompress_when="if-different always never" COMPREPLY=( $(compgen -W "${recompress_when}" -- ${cur}) ) return 0 ;; esac if [[ ${cur} == -* ]] ; then case "${COMP_LINE}" in *' init '*) local opts="-e --encryption --append-only --storage-quota --make-parent-dirs ${common_opts}" ;; *' create '*) local opts="-n --dry-run -s --stats --list --filter --json --no-cache-sync --stdin-name --content-from-command -e --exclude --exclude-from --pattern --patterns-from --exclude-caches --exclude-if-present --keep-exclude-tags --exclude-nodump -x --one-file-system --numeric-owner --noatime --noctime --nobirthtime --nobsdflags --noacls --noxattrs --noflags --files-cache --read-special --comment --timestamp -c --checkpoint-interval --chunker-params -C --compression ${common_opts}" ;; *' extract '*) local opts="--list -n --dry-run --numeric-owner --nobsdflags --noacls --noxattrs --stdout --sparse -e --exclude --exclude-from --pattern --patterns-from --strip-components ${common_opts}" ;; *' check '*) local opts="--repository-only --archives-only --verify-data --repair --save-space --max-duration -P --prefix -a --glob-archives --sort-by --first --last ${common_opts}" ;; # rename # no specific options *" list "*) local opts="--short --format --json --json-lines -P --prefix -a --glob-archives --sort-by --first --last -e --exclude --exclude-from --pattern --patterns-from ${common_opts}" ;; *' diff '*) local opts="--numeric-owner --same-chunker-params --sort --json-lines -e --exclude --exclude-from --pattern --patterns-from ${common_opts}" ;; *' delete '*) local opts="-n --dry-run -s --stats --cache-only --force --save-space -P --prefix -a --glob-archives --sort-by --first --last ${common_opts}" ;; *' prune '*) local opts="-n --dry-run --force -s --stats --list --keep-within --keep-last --keep-secondly --keep-minutely -H --keep-hourly -d --keep-daily -w --keep-weekly -m --keep-monthly -y --keep-yearly --save-space -P --prefix -a --glob-archives ${common_opts}" ;; *' compact '*) local opts="--cleanup-commits ${common_opts}" ;; *' info '*) local opts="--json -P --prefix -a --glob-archives --sort-by --first --last ${common_opts}" ;; *' mount '*) local opts="-f --foreground -o -P --prefix -a --glob-archives --sort-by --first --last -e --exclude --exclude-from --pattern --patterns-from --strip-components ${common_opts}" ;; # umount # no specific options # key change-passphrase # no specific options *' export '*) local opts="--paper --qr-html ${common_opts}" ;; *' import '*) local opts="--paper ${common_opts}" ;; *' upgrade '*) local opts="-n --dry-run --inplace --force --tam --disable-tam ${common_opts}" ;; *' recreate '*) local opts="--list --filter -n dry-run -s stats -e exclude --exclude-from --pattern --patterns-from --exclude-caches --exclude-if-present --keep-exclude-tags --target -c checkpoint-interval --comment --timestamp --timestamp -C compression --recompress --chunker-params ${common_opts}" ;; *' export-tar '*) local opts="--tar-filter --list -e exclude --exclude-from --pattern --patterns-from --strip-components ${common_opts}" ;; *' serve '*) local opts="--restrict-to-path --restrict-to-repository --append-only --storage-quota ${common_opts}" ;; *' config '*) local opts="-c --cache -d --delete --list ${common_opts}" ;; # with-lock # no specific options # break-lock # no specific options # benchmark crud # no specific options esac COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi # Get the repository name if available # If there is a space before the "::" it means that no repository name was typed, # so probably $BORG_REPO was set and we can still list the archives. local repository_name="${COMP_LINE%%::*}" repository_name=${repository_name##* } # Listing archives. # Since "::" is treated as separate word in Bash, # it is $cur when the cursor is right behind it # and $prev if the user has started to type an archive name. local typed_word=${cur} local -i list_archives=0 if [[ ${cur} == "::" ]] ; then list_archives=1 typed_word="" fi if [[ ${prev} == "::" ]] ; then list_archives=1 fi # Second archive listing for borg diff if [[ ${COMP_LINE} =~ ^.*\ diff\ .*::[^\ ]+\ ${cur}$ ]] ; then list_archives=1 fi # Additional archive listing for borg delete if [[ ${COMP_LINE} =~ ^.*\ delete\ .*::[^\ ]+.*${cur}$ ]] ; then list_archives=1 fi if (( $list_archives )) ; then local archives=$(borg list --short "${repository_name}" 2>/dev/null) COMPREPLY=( $(compgen -W "${archives}" -- "${typed_word}" ) ) return 0 fi return 0 } complete -F _borg borg ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1455863 borgbackup-1.2.8/scripts/shell_completions/fish/0000755000076500000240000000000014601577064020373 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/shell_completions/fish/borg.fish0000644000076500000240000010157014601576577022213 0ustar00twstaff# Completions for borg # https://www.borgbackup.org/ # Note: # Listing archives works on password protected repositories only if $BORG_PASSPHRASE is set. # Install: # Copy this file to /usr/share/fish/vendor_completions.d/ # Commands complete -c borg -f -n __fish_is_first_token -a 'init' -d 'Initialize an empty repository' complete -c borg -f -n __fish_is_first_token -a 'create' -d 'Create new archive' complete -c borg -f -n __fish_is_first_token -a 'extract' -d 'Extract archive contents' complete -c borg -f -n __fish_is_first_token -a 'check' -d 'Check repository consistency' complete -c borg -f -n __fish_is_first_token -a 'rename' -d 'Rename an existing archive' complete -c borg -f -n __fish_is_first_token -a 'list' -d 'List archive or repository contents' complete -c borg -f -n __fish_is_first_token -a 'diff' -d 'Find differences between archives' complete -c borg -f -n __fish_is_first_token -a 'delete' -d 'Delete a repository or archive' complete -c borg -f -n __fish_is_first_token -a 'prune' -d 'Prune repository archives' complete -c borg -f -n __fish_is_first_token -a 'compact' -d 'Free repository space' complete -c borg -f -n __fish_is_first_token -a 'info' -d 'Show archive details' complete -c borg -f -n __fish_is_first_token -a 'mount' -d 'Mount archive or a repository' complete -c borg -f -n __fish_is_first_token -a 'umount' -d 'Un-mount the mounted archive' function __fish_borg_seen_key if __fish_seen_subcommand_from key and not __fish_seen_subcommand_from import export change-passphrase return 0 end return 1 end complete -c borg -f -n __fish_is_first_token -a 'key' -d 'Manage a repository key' complete -c borg -f -n __fish_borg_seen_key -a 'import' -d 'Import a repository key' complete -c borg -f -n __fish_borg_seen_key -a 'export' -d 'Export a repository key' complete -c borg -f -n __fish_borg_seen_key -a 'change-passphrase' -d 'Change key file passphrase' complete -c borg -f -n __fish_is_first_token -a 'serve' -d 'Start in server mode' complete -c borg -f -n __fish_is_first_token -a 'upgrade' -d 'Upgrade a repository' complete -c borg -f -n __fish_is_first_token -a 'recreate' -d 'Recreate contents of existing archives' complete -c borg -f -n __fish_is_first_token -a 'export-tar' -d 'Create tarball from an archive' complete -c borg -f -n __fish_is_first_token -a 'with-lock' -d 'Run a command while repository lock held' complete -c borg -f -n __fish_is_first_token -a 'break-lock' -d 'Break the repository lock' complete -c borg -f -n __fish_is_first_token -a 'config' -d 'Get/set options in repo/cache config' function __fish_borg_seen_benchmark if __fish_seen_subcommand_from benchmark and not __fish_seen_subcommand_from crud return 0 end return 1 end complete -c borg -f -n __fish_is_first_token -a 'benchmark' -d 'Benchmark borg operations' complete -c borg -f -n __fish_borg_seen_benchmark -a 'crud' -d 'Benchmark borg CRUD operations' function __fish_borg_seen_help if __fish_seen_subcommand_from help and not __fish_seen_subcommand_from patterns placeholders compression return 0 end return 1 end complete -c borg -f -n __fish_is_first_token -a 'help' -d 'Miscellaneous Help' complete -c borg -f -n __fish_borg_seen_help -a 'patterns' -d 'Help for patterns' complete -c borg -f -n __fish_borg_seen_help -a 'placeholders' -d 'Help for placeholders' complete -c borg -f -n __fish_borg_seen_help -a 'compression' -d 'Help for compression' # Common options complete -c borg -f -s h -l 'help' -d 'Show help information' complete -c borg -f -l 'version' -d 'Show version information' complete -c borg -f -l 'critical' -d 'Log level CRITICAL' complete -c borg -f -l 'error' -d 'Log level ERROR' complete -c borg -f -l 'warning' -d 'Log level WARNING (default)' complete -c borg -f -l 'info' -d 'Log level INFO' complete -c borg -f -s v -l 'verbose' -d 'Log level INFO' complete -c borg -f -l 'debug' -d 'Log level DEBUG' complete -c borg -f -l 'debug-topic' -d 'Enable TOPIC debugging' complete -c borg -f -s p -l 'progress' -d 'Show progress information' complete -c borg -f -l 'log-json' -d 'Output one JSON object per log line' complete -c borg -f -l 'lock-wait' -d 'Wait for lock max N seconds [1]' complete -c borg -f -l 'show-version' -d 'Log version information' complete -c borg -f -l 'show-rc' -d 'Log the return code' complete -c borg -f -l 'umask' -d 'Set umask to M [0077]' complete -c borg -l 'remote-path' -d 'Use PATH as remote borg executable' complete -c borg -f -l 'remote-ratelimit' -d 'Set remote network upload RATE limit' complete -c borg -f -l 'consider-part-files' -d 'Treat part files like normal files' complete -c borg -l 'debug-profile' -d 'Write execution profile into FILE' complete -c borg -l 'rsh' -d 'Use COMMAND instead of ssh' # borg init options set -l encryption_modes "none keyfile keyfile-blake2 repokey repokey-blake2 authenticated authenticated-blake2" complete -c borg -f -s e -l 'encryption' -d 'Encryption key MODE' -a "$encryption_modes" -n "__fish_seen_subcommand_from init" complete -c borg -f -l 'append-only' -d 'Create an append-only mode repository' -n "__fish_seen_subcommand_from init" complete -c borg -f -l 'storage-quota' -d 'Set storage QUOTA of the repository' -n "__fish_seen_subcommand_from init" complete -c borg -f -l 'make-parent-dirs' -d 'Create parent directories' -n "__fish_seen_subcommand_from init" # borg create options complete -c borg -f -s n -l 'dry-run' -d 'Do not change the repository' -n "__fish_seen_subcommand_from create" complete -c borg -f -s s -l 'stats' -d 'Print verbose statistics' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'list' -d 'Print verbose list of items' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'filter' -d 'Only items with given STATUSCHARS' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'json' -d 'Print verbose stats as json' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'no-cache-sync' -d 'Do not synchronize the cache' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'stdin-name' -d 'Use NAME in archive for stdin data' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'content-from-command' -d 'Interpret PATH as command and store its stdout' -n "__fish_seen_subcommand_from create" # Exclusion options complete -c borg -s e -l 'exclude' -d 'Exclude paths matching PATTERN' -n "__fish_seen_subcommand_from create" complete -c borg -l 'exclude-from' -d 'Read exclude patterns from EXCLUDEFILE' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'pattern' -d 'Include/exclude paths matching PATTERN' -n "__fish_seen_subcommand_from create" complete -c borg -l 'patterns-from' -d 'Include/exclude paths from PATTERNFILE' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'exclude-caches' -d 'Exclude directories tagged as cache' -n "__fish_seen_subcommand_from create" complete -c borg -l 'exclude-if-present' -d 'Exclude directories that contain FILENAME' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'keep-exclude-tags' -d 'Keep tag files of excluded directories' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'keep-tag-files' -d 'Keep tag files of excluded directories' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'exclude-nodump' -d 'Exclude files flagged NODUMP' -n "__fish_seen_subcommand_from create" # Filesystem options complete -c borg -f -s x -l 'one-file-system' -d 'Stay in the same file system' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'numeric-owner' -d 'Only store numeric user:group identifiers' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'noatime' -d 'Do not store atime' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'noctime' -d 'Do not store ctime' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'nobirthtime' -d 'Do not store creation date' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'nobsdflags' -d 'Do not store bsdflags' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'noacls' -d 'Do not read and store ACLs into archive' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'noxattrs' -d 'Do not read and store xattrs into archive' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'noflags' -d 'Do not store flags' -n "__fish_seen_subcommand_from create" set -l files_cache_mode "ctime,size,inode mtime,size,inode ctime,size mtime,size rechunk,ctime rechunk,mtime size disabled" complete -c borg -f -l 'files-cache' -d 'Operate files cache in MODE' -a "$files_cache_mode" -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'read-special' -d 'Open device files like regular files' -n "__fish_seen_subcommand_from create" # Archive options complete -c borg -f -l 'comment' -d 'Add COMMENT to the archive' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'timestamp' -d 'Set creation TIME (yyyy-mm-ddThh:mm:ss)' -n "__fish_seen_subcommand_from create" complete -c borg -l 'timestamp' -d 'Set creation time by reference FILE' -n "__fish_seen_subcommand_from create" complete -c borg -f -s c -l 'checkpoint-interval' -d 'Write checkpoint every N seconds [1800]' -n "__fish_seen_subcommand_from create" complete -c borg -f -l 'chunker-params' -d 'Chunker PARAMETERS [19,23,21,4095]' -n "__fish_seen_subcommand_from create" set -l compression_methods "none auto lz4 zstd,1 zstd,2 zstd,3 zstd,4 zstd,5 zstd,6 zstd,7 zstd,8 zstd,9 zstd,10 zstd,11 zstd,12 zstd,13 zstd,14 zstd,15 zstd,16 zstd,17 zstd,18 zstd,19 zstd,20 zstd,21 zstd,22 zlib,1 zlib,2 zlib,3 zlib,4 zlib,5 zlib,6 zlib,7 zlib,8 zlib,9 lzma,0 lzma,1 lzma,2 lzma,3 lzma,4 lzma,5 lzma,6 lzma,7 lzma,8 lzma,9" complete -c borg -f -s C -l 'compression' -d 'Select compression ALGORITHM,LEVEL [lz4]' -a "$compression_methods" -n "__fish_seen_subcommand_from create" # borg extract options complete -c borg -f -l 'list' -d 'Print verbose list of items' -n "__fish_seen_subcommand_from extract" complete -c borg -f -s n -l 'dry-run' -d 'Do not actually extract any files' -n "__fish_seen_subcommand_from extract" complete -c borg -f -l 'numeric-owner' -d 'Only obey numeric user:group identifiers' -n "__fish_seen_subcommand_from extract" complete -c borg -f -l 'nobsdflags' -d 'Do not extract/set bsdflags' -n "__fish_seen_subcommand_from extract" complete -c borg -f -l 'noflags' -d 'Do not extract/set flags' -n "__fish_seen_subcommand_from extract" complete -c borg -f -l 'noacls' -d 'Do not extract/set ACLs' -n "__fish_seen_subcommand_from extract" complete -c borg -f -l 'noxattrs' -d 'Do not extract/set xattrs' -n "__fish_seen_subcommand_from extract" complete -c borg -f -l 'stdout' -d 'Write all extracted data to stdout' -n "__fish_seen_subcommand_from extract" complete -c borg -f -l 'sparse' -d 'Create holes in output sparse file' -n "__fish_seen_subcommand_from extract" # Exclusion options complete -c borg -s e -l 'exclude' -d 'Exclude paths matching PATTERN' -n "__fish_seen_subcommand_from extract" complete -c borg -l 'exclude-from' -d 'Read exclude patterns from EXCLUDEFILE' -n "__fish_seen_subcommand_from extract" complete -c borg -l 'pattern' -d 'Include/exclude paths matching PATTERN' -n "__fish_seen_subcommand_from extract" complete -c borg -l 'patterns-from' -d 'Include/exclude paths from PATTERNFILE' -n "__fish_seen_subcommand_from extract" complete -c borg -f -l 'strip-components' -d 'Remove NUMBER of leading path elements' -n "__fish_seen_subcommand_from extract" # borg check options complete -c borg -f -l 'repository-only' -d 'Only perform repository checks' -n "__fish_seen_subcommand_from check" complete -c borg -f -l 'archives-only' -d 'Only perform archives checks' -n "__fish_seen_subcommand_from check" complete -c borg -f -l 'verify-data' -d 'Cryptographic integrity verification' -n "__fish_seen_subcommand_from check" complete -c borg -f -l 'repair' -d 'Attempt to repair found inconsistencies' -n "__fish_seen_subcommand_from check" complete -c borg -f -l 'save-space' -d 'Work slower but using less space' -n "__fish_seen_subcommand_from check" complete -c borg -f -l 'max-duration' -d 'Partial repo check for max. SECONDS' -n "__fish_seen_subcommand_from check" # Archive filters complete -c borg -f -s P -l 'prefix' -d 'Only archive names starting with PREFIX' -n "__fish_seen_subcommand_from check" complete -c borg -f -s a -l 'glob-archives' -d 'Only archive names matching GLOB' -n "__fish_seen_subcommand_from check" set -l sort_keys "timestamp name id" complete -c borg -f -l 'sort-by' -d 'Sorting KEYS [timestamp]' -a "$sort_keys" -n "__fish_seen_subcommand_from check" complete -c borg -f -l 'first' -d 'Only first N archives' -n "__fish_seen_subcommand_from check" complete -c borg -f -l 'last' -d 'Only last N archives' -n "__fish_seen_subcommand_from check" # borg rename # no specific options # borg list options complete -c borg -f -l 'short' -d 'Only print file/directory names' -n "__fish_seen_subcommand_from list" complete -c borg -f -l 'format' -d 'Specify FORMAT for file listing' -n "__fish_seen_subcommand_from list" complete -c borg -f -l 'json' -d 'List contents in json format' -n "__fish_seen_subcommand_from list" complete -c borg -f -l 'json-lines' -d 'List contents in json lines format' -n "__fish_seen_subcommand_from list" # Archive filters complete -c borg -f -s P -l 'prefix' -d 'Only archive names starting with PREFIX' -n "__fish_seen_subcommand_from list" complete -c borg -f -s a -l 'glob-archives' -d 'Only archive names matching GLOB' -n "__fish_seen_subcommand_from list" complete -c borg -f -l 'sort-by' -d 'Sorting KEYS [timestamp]' -a "$sort_keys" -n "__fish_seen_subcommand_from list" complete -c borg -f -l 'first' -d 'Only first N archives' -n "__fish_seen_subcommand_from list" complete -c borg -f -l 'last' -d 'Only last N archives' -n "__fish_seen_subcommand_from list" # Exclusion options complete -c borg -s e -l 'exclude' -d 'Exclude paths matching PATTERN' -n "__fish_seen_subcommand_from list" complete -c borg -l 'exclude-from' -d 'Read exclude patterns from EXCLUDEFILE' -n "__fish_seen_subcommand_from list" complete -c borg -f -l 'pattern' -d 'Include/exclude paths matching PATTERN' -n "__fish_seen_subcommand_from list" complete -c borg -l 'patterns-from' -d 'Include/exclude paths from PATTERNFILE' -n "__fish_seen_subcommand_from list" # borg diff options complete -c borg -f -l 'numeric-owner' -d 'Only consider numeric user:group' -n "__fish_seen_subcommand_from diff" complete -c borg -f -l 'same-chunker-params' -d 'Override check of chunker parameters' -n "__fish_seen_subcommand_from diff" complete -c borg -f -l 'sort' -d 'Sort the output lines by file path' -n "__fish_seen_subcommand_from diff" complete -c borg -f -l 'json-lines' -d 'Format output as JSON Lines' -n "__fish_seen_subcommand_from diff" # Exclusion options complete -c borg -s e -l 'exclude' -d 'Exclude paths matching PATTERN' -n "__fish_seen_subcommand_from diff" complete -c borg -l 'exclude-from' -d 'Read exclude patterns from EXCLUDEFILE' -n "__fish_seen_subcommand_from diff" complete -c borg -f -l 'pattern' -d 'Include/exclude paths matching PATTERN' -n "__fish_seen_subcommand_from diff" complete -c borg -l 'patterns-from' -d 'Include/exclude paths from PATTERNFILE' -n "__fish_seen_subcommand_from diff" # borg delete options complete -c borg -f -s n -l 'dry-run' -d 'Do not change the repository' -n "__fish_seen_subcommand_from delete" complete -c borg -f -s s -l 'stats' -d 'Print verbose statistics' -n "__fish_seen_subcommand_from delete" complete -c borg -f -l 'cache-only' -d "Delete only the local cache" -n "__fish_seen_subcommand_from delete" complete -c borg -f -l 'force' -d 'Force deletion of corrupted archives' -n "__fish_seen_subcommand_from delete" complete -c borg -f -l 'save-space' -d 'Work slower but using less space' -n "__fish_seen_subcommand_from delete" # Archive filters complete -c borg -f -s P -l 'prefix' -d 'Only archive names starting with PREFIX' -n "__fish_seen_subcommand_from delete" complete -c borg -f -s a -l 'glob-archives' -d 'Only archive names matching GLOB' -n "__fish_seen_subcommand_from delete" complete -c borg -f -l 'sort-by' -d 'Sorting KEYS [timestamp]' -a "$sort_keys" -n "__fish_seen_subcommand_from delete" complete -c borg -f -l 'first' -d 'Only first N archives' -n "__fish_seen_subcommand_from delete" complete -c borg -f -l 'last' -d 'Only last N archives' -n "__fish_seen_subcommand_from delete" # borg prune options complete -c borg -f -s n -l 'dry-run' -d 'Do not change the repository' -n "__fish_seen_subcommand_from prune" complete -c borg -f -l 'force' -d 'Force pruning of corrupted archives' -n "__fish_seen_subcommand_from prune" complete -c borg -f -s s -l 'stats' -d 'Print verbose statistics' -n "__fish_seen_subcommand_from prune" complete -c borg -f -l 'list' -d 'Print verbose list of items' -n "__fish_seen_subcommand_from prune" complete -c borg -f -l 'keep-within' -d 'Keep archives within time INTERVAL' -n "__fish_seen_subcommand_from prune" complete -c borg -f -l 'keep-last' -d 'NUMBER of secondly archives to keep' -n "__fish_seen_subcommand_from prune" complete -c borg -f -l 'keep-secondly' -d 'NUMBER of secondly archives to keep' -n "__fish_seen_subcommand_from prune" complete -c borg -f -l 'keep-minutely' -d 'NUMBER of minutely archives to keep' -n "__fish_seen_subcommand_from prune" complete -c borg -f -s H -l 'keep-hourly' -d 'NUMBER of hourly archives to keep' -n "__fish_seen_subcommand_from prune" complete -c borg -f -s d -l 'keep-daily' -d 'NUMBER of daily archives to keep' -n "__fish_seen_subcommand_from prune" complete -c borg -f -s w -l 'keep-weekly' -d 'NUMBER of weekly archives to keep' -n "__fish_seen_subcommand_from prune" complete -c borg -f -s m -l 'keep-monthly' -d 'NUMBER of monthly archives to keep' -n "__fish_seen_subcommand_from prune" complete -c borg -f -s y -l 'keep-yearly' -d 'NUMBER of yearly archives to keep' -n "__fish_seen_subcommand_from prune" complete -c borg -f -l 'save-space' -d 'Work slower but using less space' -n "__fish_seen_subcommand_from prune" # Archive filters complete -c borg -f -s P -l 'prefix' -d 'Only archive names starting with PREFIX' -n "__fish_seen_subcommand_from prune" complete -c borg -f -s a -l 'glob-archives' -d 'Only archive names matching GLOB' -n "__fish_seen_subcommand_from prune" # borg compact options complete -c borg -f -s n -l 'cleanup-commits' -d 'Cleanup commit-only segment files' -n "__fish_seen_subcommand_from compact" # borg info options complete -c borg -f -l 'json' -d 'Format output in json format' -n "__fish_seen_subcommand_from info" # Archive filters complete -c borg -f -s P -l 'prefix' -d 'Only archive names starting with PREFIX' -n "__fish_seen_subcommand_from info" complete -c borg -f -s a -l 'glob-archives' -d 'Only archive names matching GLOB' -n "__fish_seen_subcommand_from info" complete -c borg -f -l 'sort-by' -d 'Sorting KEYS [timestamp]' -a "$sort_keys" -n "__fish_seen_subcommand_from info" complete -c borg -f -l 'first' -d 'Only first N archives' -n "__fish_seen_subcommand_from info" complete -c borg -f -l 'last' -d 'Only last N archives' -n "__fish_seen_subcommand_from info" # borg mount options complete -c borg -f -s f -l 'foreground' -d 'Stay in foreground, do not daemonize' -n "__fish_seen_subcommand_from mount" # FIXME This list is probably not full, but I tried to pick only those that are relevant to borg mount -o: set -l fuse_options "ac_attr_timeout= allow_damaged_files allow_other allow_root attr_timeout= auto auto_cache auto_unmount default_permissions entry_timeout= gid= group_id= kernel_cache max_read= negative_timeout= noauto noforget remember= remount rootmode= uid= umask= user user_id= versions" complete -c borg -f -s o -d 'Fuse mount OPTION' -a "$fuse_options" -n "__fish_seen_subcommand_from mount" # Archive filters complete -c borg -f -s P -l 'prefix' -d 'Only archive names starting with PREFIX' -n "__fish_seen_subcommand_from mount" complete -c borg -f -s a -l 'glob-archives' -d 'Only archive names matching GLOB' -n "__fish_seen_subcommand_from mount" complete -c borg -f -l 'sort-by' -d 'Sorting KEYS [timestamp]' -a "$sort_keys" -n "__fish_seen_subcommand_from mount" complete -c borg -f -l 'first' -d 'Only first N archives' -n "__fish_seen_subcommand_from mount" complete -c borg -f -l 'last' -d 'Only last N archives' -n "__fish_seen_subcommand_from mount" # Exclusion options complete -c borg -s e -l 'exclude' -d 'Exclude paths matching PATTERN' -n "__fish_seen_subcommand_from mount" complete -c borg -l 'exclude-from' -d 'Read exclude patterns from EXCLUDEFILE' -n "__fish_seen_subcommand_from mount" complete -c borg -f -l 'pattern' -d 'Include/exclude paths matching PATTERN' -n "__fish_seen_subcommand_from mount" complete -c borg -l 'patterns-from' -d 'Include/exclude paths from PATTERNFILE' -n "__fish_seen_subcommand_from mount" complete -c borg -f -l 'strip-components' -d 'Remove NUMBER of leading path elements' -n "__fish_seen_subcommand_from mount" # borg umount # no specific options # borg key change-passphrase # no specific options # borg key export complete -c borg -f -l 'paper' -d 'Create an export for printing' -n "__fish_seen_subcommand_from export" complete -c borg -f -l 'qr-html' -d 'Create an html file for printing and qr' -n "__fish_seen_subcommand_from export" # borg key import complete -c borg -f -l 'paper' -d 'Import from a backup done with --paper' -n "__fish_seen_subcommand_from import" # borg upgrade complete -c borg -f -s n -l 'dry-run' -d 'Do not change the repository' -n "__fish_seen_subcommand_from upgrade" complete -c borg -f -l 'inplace' -d 'Rewrite repository in place' -n "__fish_seen_subcommand_from upgrade" complete -c borg -f -l 'force' -d 'Force upgrade' -n "__fish_seen_subcommand_from upgrade" complete -c borg -f -l 'tam' -d 'Enable manifest authentication' -n "__fish_seen_subcommand_from upgrade" complete -c borg -f -l 'disable-tam' -d 'Disable manifest authentication' -n "__fish_seen_subcommand_from upgrade" # borg recreate complete -c borg -f -l 'list' -d 'Print verbose list of items' -n "__fish_seen_subcommand_from recreate" complete -c borg -f -l 'filter' -d 'Only items with given STATUSCHARS' -n "__fish_seen_subcommand_from recreate" complete -c borg -f -s n -l 'dry-run' -d 'Do not change the repository' -n "__fish_seen_subcommand_from recreate" complete -c borg -f -s s -l 'stats' -d 'Print verbose statistics' -n "__fish_seen_subcommand_from recreate" # Exclusion options complete -c borg -s e -l 'exclude' -d 'Exclude paths matching PATTERN' -n "__fish_seen_subcommand_from recreate" complete -c borg -l 'exclude-from' -d 'Read exclude patterns from EXCLUDEFILE' -n "__fish_seen_subcommand_from recreate" complete -c borg -f -l 'pattern' -d 'Include/exclude paths matching PATTERN' -n "__fish_seen_subcommand_from recreate" complete -c borg -l 'patterns-from' -d 'Include/exclude paths from PATTERNFILE' -n "__fish_seen_subcommand_from recreate" complete -c borg -f -l 'exclude-caches' -d 'Exclude directories tagged as cache' -n "__fish_seen_subcommand_from recreate" complete -c borg -l 'exclude-if-present' -d 'Exclude directories that contain FILENAME' -n "__fish_seen_subcommand_from recreate" complete -c borg -f -l 'keep-exclude-tags' -d 'Keep tag files of excluded directories' -n "__fish_seen_subcommand_from recreate" # Archive options complete -c borg -f -l 'target' -d "Create a new ARCHIVE" -n "__fish_seen_subcommand_from recreate" complete -c borg -f -s c -l 'checkpoint-interval' -d 'Write checkpoint every N seconds [1800]' -n "__fish_seen_subcommand_from recreate" complete -c borg -f -l 'comment' -d 'Add COMMENT to the archive' -n "__fish_seen_subcommand_from recreate" complete -c borg -f -l 'timestamp' -d 'Set creation TIME (yyyy-mm-ddThh:mm:ss)' -n "__fish_seen_subcommand_from recreate" complete -c borg -l 'timestamp' -d 'Set creation time using reference FILE' -n "__fish_seen_subcommand_from recreate" complete -c borg -f -s C -l 'compression' -d 'Select compression ALGORITHM,LEVEL [lz4]' -a "$compression_methods" -n "__fish_seen_subcommand_from recreate" set -l recompress_when "if-different always never" complete -c borg -f -l 'recompress' -d 'Recompress chunks CONDITION' -a "$recompress_when" -n "__fish_seen_subcommand_from recreate" complete -c borg -f -l 'chunker-params' -d 'Chunker PARAMETERS [19,23,21,4095]' -n "__fish_seen_subcommand_from recreate" # borg export-tar options complete -c borg -l 'tar-filter' -d 'Filter program to pipe data through' -n "__fish_seen_subcommand_from export-tar" complete -c borg -f -l 'list' -d 'Print verbose list of items' -n "__fish_seen_subcommand_from export-tar" # Exclusion options complete -c borg -s e -l 'exclude' -d 'Exclude paths matching PATTERN' -n "__fish_seen_subcommand_from recreate" complete -c borg -l 'exclude-from' -d 'Read exclude patterns from EXCLUDEFILE' -n "__fish_seen_subcommand_from recreate" complete -c borg -f -l 'pattern' -d 'Include/exclude paths matching PATTERN' -n "__fish_seen_subcommand_from recreate" complete -c borg -l 'patterns-from' -d 'Include/exclude paths from PATTERNFILE' -n "__fish_seen_subcommand_from recreate" complete -c borg -f -l 'strip-components' -d 'Remove NUMBER of leading path elements' -n "__fish_seen_subcommand_from recreate" # borg serve complete -c borg -l 'restrict-to-path' -d 'Restrict repository access to PATH' -n "__fish_seen_subcommand_from serve" complete -c borg -l 'restrict-to-repository' -d 'Restrict repository access at PATH' -n "__fish_seen_subcommand_from serve" complete -c borg -f -l 'append-only' -d 'Only allow appending to repository' -n "__fish_seen_subcommand_from serve" complete -c borg -f -l 'storage-quota' -d 'Override storage QUOTA of the repository' -n "__fish_seen_subcommand_from serve" # borg config complete -c borg -f -s c -l 'cache' -d 'Get/set/list values in the repo cache' -n "__fish_seen_subcommand_from config" complete -c borg -f -s d -l 'delete' -d 'Delete the KEY from the config' -n "__fish_seen_subcommand_from config" complete -c borg -f -l 'list' -d 'List the configuration of the repo' -n "__fish_seen_subcommand_from config" # borg with-lock # no specific options # borg break-lock # no specific options # borg benchmark # no specific options # borg help # no specific options # List repositories::archives function __fish_borg_is_argument_n --description 'Test if current argument is on Nth place' --argument n set tokens (commandline --current-process --tokenize --cut-at-cursor) set -l tokencount 0 for token in $tokens switch $token case '-*' # ignore command line switches case '*' set tokencount (math $tokencount+1) end end return (test $tokencount -eq $n) end function __fish_borg_is_dir_a_repository set -l config_content if test -f $argv[1]/README and test -f $argv[1]/config read config_content < $argv[1]/config 2>/dev/null end return (string match --quiet '[repository]' $config_content) end function __fish_borg_list_repos_or_archives if string match --quiet --regex '.*::' '"'(commandline --current-token)'"' # If the current token contains "::" then list the archives: set -l repository_name (string replace --regex '::.*' '' (commandline --current-token)) borg list --format="$repository_name::{archive}{TAB}{comment}{NEWLINE}" "$repository_name" 2>/dev/null else # Otherwise list the repositories, directories and user@host entries: set -l directories (commandline --cut-at-cursor --current-token)*/ for directoryname in $directories if __fish_borg_is_dir_a_repository $directoryname printf '%s::\t%s\n' (string trim --right --chars='/' $directoryname) "Repository" else printf '%s\n' $directoryname end end __fish_complete_user_at_hosts | string replace --regex '$' ':' end end complete -c borg -f -n "__fish_borg_is_argument_n 2" -a '(__fish_borg_list_repos_or_archives)' # Additional archive listings function __fish_borg_is_diff_second_archive return (string match --quiet --regex ' diff .*::[^ ]+ '(commandline --current-token)'$' (commandline)) end function __fish_borg_is_delete_additional_archive return (string match --quiet --regex ' delete .*::[^ ]+ ' (commandline)) end function __fish_borg_list_only_archives set -l repo_matches (string match --regex '([^ ]*)::' (commandline)) borg list --format="{archive}{TAB}{comment}{NEWLINE}" "$repo_matches[2]" 2>/dev/null end complete -c borg -f -n __fish_borg_is_diff_second_archive -a '(__fish_borg_list_only_archives)' complete -c borg -f -n __fish_borg_is_delete_additional_archive -a '(__fish_borg_list_only_archives)' ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1711734324.14579 borgbackup-1.2.8/scripts/shell_completions/zsh/0000755000076500000240000000000014601577064020246 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/shell_completions/zsh/_borg0000644000076500000240000015723114601576577021302 0ustar00twstaff#compdef borg borgfs -P -value-,BORG_*,-default- # Zsh completion for Borg Backup 1.2.0a9 (2020-09-27). # # Recommended _borg specific settings: # # zstyle -e ':completion:*:*:borg-*:argument-rest:*' tag-order \ # '[[ $words[CURRENT] == -* ]] && reply=( "! archives archive-files" "-" )' # zstyle ':completion:*:*:(borg|-value-,BORG_)*' sort false # zstyle ':completion:*:*:borg-config:argument-2:keys' list-grouped false # zstyle ':completion:*:*:borg-*:*' gain-privileges true # zstyle ':completion:*' fake-parameters 'BORG_REPO:scalar' # # Custom styles: # # archive-description-format # Default: `{archive:<36} {time} [{id}]`. # archive-sort # In which order archive names should be listed. # Possible values are: `inverse`, `timestamp`, `name`, `id`. # file-description-format # Default: `{mode} {user:6} {group:6} {size:8d} {mtime} {path}{extra}`. # path-style-selector # Style selector used to select a path (when Borg would use either `pp` or `pf`). # Default: `fm`. # repository-suffix # This boolean style controls whether to add the `::` auto-removable suffix to a repository. # Default: `true`. (( $+functions[_borg_commands] )) || _borg_commands() { local -a commands_=( 'benchmark:benchmark command' 'break-lock:break the repository lock' 'check:check repository consistency' 'compact:compact segment files in the repository' 'config:get, set, and delete values in a repository or cache config file' 'create:create new archive' 'debug:debugging command (not intended for normal use)' 'delete:delete an existing repository or archives' 'diff:diff contents of two archives' 'export-tar:export archive contents as a tarball' 'extract:extract archive contents' 'help:extra help' 'info:show repository or archive information' 'init:initialize an empty repository' 'key:manage repository key' 'list:list archive or repository contents' 'mount:mount archive or an entire repository as a FUSE filesystem' 'prune:prune repository archives according to specified rules' 'recreate:re-create archives' 'rename:rename an existing archive' 'serve:start in server mode' 'umount:un-mount the FUSE filesystem' 'upgrade:upgrade a repository from a previous version' 'with-lock:run a user specified command with the repository lock held' ) _describe -t commands 'borg commands' commands_ } (( $+functions[_borg-benchmark] )) || _borg-benchmark() { local -a common_options __borg_setup_common_options _arguments -s -w -S : \ $common_options \ ':type:(crud)' \ ': :_borg_repository' \ ':PATH:_files' } (( $+functions[_borg-break-lock] )) || _borg-break-lock() { local -a common_options __borg_setup_common_options _arguments -s -w -S : \ $common_options \ ':: :_borg_repository' } (( $+functions[_borg-check] )) || _borg-check() { local -a common_options common_archive_filters_options __borg_setup_common_options __borg_setup_common_archive_filters_options _arguments -s -w -S : \ '--repository-only[only perform repository checks]' \ '--archives-only[only perform archives checks]' \ '(--repository-only)--verify-data[perform cryptographic archive data integrity verification]' \ '--repair[attempt to repair any inconsistencies found]' \ '--save-space[work slower, but using less space]' \ '--max-duration=[partial repo check for max. SECONDS]: : _borg_guard_unsigned_number "SECONDS"' \ $common_archive_filters_options \ $common_options \ '::REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' } (( $+functions[_borg-compact] )) || _borg-compact() { local -a common_options __borg_setup_common_options _arguments -s -w -S : \ $common_options \ '--cleanup-commits[cleanup commit-only 17-byte segment files]' \ '--threshold=[set minimum threshold for saved space in PERCENT (default: 10)]: : _borg_guard_unsigned_number "PERCENT (default\: 10)"' \ ':: :_borg_repository' } (( $+functions[_borg-config] )) || _borg-config() { local -a common_options __borg_setup_common_options _arguments -s -w -S : \ '(-c --cache)'{-c,--cache}'[get and set values from the repo cache]' \ '(-d --delete)'{-d,--delete}'[delete the key from the config]' \ '(-l --list)'{-l,--list}'[list the configuration of the repo]' \ $common_options \ ': :_borg_repository' \ ': : _borg_config $line[1]' \ '::VALUE' } (( $+functions[_borg-create] )) || _borg-create() { local -a common_options common_create_options state line local curcontext="$curcontext" state_descr declare -A opt_args local -i ret=1 __borg_setup_common_options __borg_setup_common_create_options local lastspec='*:PATH:_files' (( $words[(I)--content-from-command] )) && lastspec='*:::PATH:->command' _arguments -C -s -w -S : \ '*'{-e,--exclude}'=[exclude paths matching PATTERN]: : _borg_style_selector_or_archive_files -f -e "$line[1]" fm "${(@)line[2,-1]}"' \ '*--pattern=[experimental: include/exclude paths matching PATTERN]: : _borg_style_selector_or_archive_files -p -f -e "$line[1]" sh "${(@)line[2,-1]}"' \ $common_create_options \ '(-s --stats)--json[Output stats as JSON. Implies --stats.]' \ '--no-cache-sync[experimental: do not synchronize the cache. Implies not using the files cache.]' \ '--stdin-name=[use NAME in archive for stdin data (default: "stdin")]:NAME' \ '--content-from-command[interpret PATH as command and store its stdout]' \ '--exclude-nodump[exclude files flagged NODUMP]' \ '(-x --one-file-system)'{-x,--one-file-system}'[stay in the same file system]' \ '--numeric-owner[only store numeric user and group identifiers]' \ '--noatime[do not store atime into archive]' \ '--atime[do store atime into archive]' \ '--noctime[do not store ctime into archive]' \ '--nobirthtime[do not store birthtime (creation date) into archive]' \ '--nobsdflags[deprecated, use --noflags instead]' \ '--noacls[do not read and store ACLs into archive]' \ '--noxattrs[do not read and store xattrs into archive]' \ '--noflags[do not read and store flags (e.g. NODUMP, IMMUTABLE) into archive]' \ '--files-cache=[operate files cache in MODE. default: ctime,size,inode]:MODE:(ctime,size,inode mtime,size,inode ctime,size mtime,size rechunk,ctime rechunk,mtime size disabled)' \ '--read-special[open and read block and char device files as well as FIFOs as if they were regular files]' \ $common_options \ ':ARCHIVE: _borg_repository_or_archive -a -p' \ $lastspec && ret=0 case $state in (command) if (( CURRENT <= 1 )); then _command_names -e && ret=0 else _normal && ret=0 fi ;; esac return ret } (( $+functions[_borg-debug] )) || _borg-debug() { local -a state line common_options local curcontext="$curcontext" state_descr declare -A opt_args local -i ret=1 __borg_setup_common_options _arguments -s -w -C : \ $common_options \ ': :->command' \ '*:: :->option-or-argument' && ret=0 case $state in (command) local -a debug_commands=( 'info:show system infos for debugging / bug reports' 'dump-archive-items:dump archive items (metadata)' 'dump-archive:dump decoded archive metadata' 'dump-manifest:dump decoded repository metadata' 'dump-repo-objs:dump repo objects' 'search-repo-objs:search repo objects' 'get-obj:get object from repository' 'put-obj:put object to repository' 'delete-obj:delete object from repository' 'refcount-obj:show refcount for object from repository' 'dump-hints:dump repository hints' 'convert-profile:convert Borg profile to Python profile' ) _describe -t commands 'command' debug_commands && ret=0 ;; (option-or-argument) curcontext="${curcontext%:*}-$line[1]:" case $line[1] in (info) _arguments -s -w -S : \ $common_options && ret=0 ;; (dump-archive-items) _arguments -s -w -S : \ $common_options \ ':ARCHIVE: _borg_repository_or_archive -a' && ret=0 ;; (dump-archive) _arguments -s -w -S : \ $common_options \ ':ARCHIVE: _borg_repository_or_archive -a' \ ':PATH:_files' && ret=0 ;; (dump-manifest) _arguments -s -w -S : \ $common_options \ ': :_borg_repository' \ ':PATH:_files' && ret=0 ;; (dump-repo-objs) _arguments -s -w -S : \ $common_options \ ': :_borg_repository' && ret=0 ;; (search-repo-objs) _arguments -s -w -S : \ $common_options \ ': :_borg_repository' \ ':WANTED (hex or string):' && ret=0 ;; (get-obj) _arguments -s -w -S : \ $common_options \ ': :_borg_repository' \ ':ID (hex object):' \ ':PATH:_files' && ret=0 ;; (put-obj) _arguments -s -w -S : \ $common_options \ ': :_borg_repository' \ '*:PATH:_files' && ret=0 ;; (delete-obj) _arguments -s -w -S : \ $common_options \ ': :_borg_repository' \ '*:ID (hex object):' && ret=0 ;; (refcount-obj) _arguments -s -w -S : \ $common_options \ ': :_borg_repository' \ '*:ID (hex object):' && ret=0 ;; (dump-hints) _arguments -s -w -S : \ $common_options \ ': :_borg_repository' \ '*:PATH:_files' && ret=0 ;; (convert-profile) _arguments -s -w -S : \ $common_options \ ':INPUT:_files' \ ':OUTPUT:_files' && ret=0 ;; (*) if ! _call_function ret _borg_debug_$line[1]; then _default && ret=0 fi ;; esac ;; esac return ret } (( $+functions[_borg-delete] )) || _borg-delete() { local -a common_options common_archive_filters_options common_dry_run_stats_options __borg_setup_common_options __borg_setup_common_archive_filters_options __borg_setup_common_dry_run_stats_options _arguments -s -w -S : \ $common_dry_run_stats_options \ '--cache-only[delete only the local cache for the given repository]' \ '*--force[force deletion of corrupted archives, use "--force --force" in case "--force" does not work]' \ '--keep-security-info[keep the local security info when deleting a repository]' \ '--save-space[work slower, but using less space]' \ $common_archive_filters_options \ $common_options \ ':REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' \ '*:ARCHIVE: _borg_archive "${line[1]%%::*}"' } (( $+functions[_borg-diff] )) || _borg-diff() { local -a common_options common_exclude_options __borg_setup_common_options __borg_setup_common_exclude_options _arguments -s -w -S : \ '--numeric-owner[only obey numeric user and group identifiers]' \ '--same-chunker-params[override check of chunker parameters]' \ '--sort[sort the output lines by file path]' \ '--json-lines[format output as JSON Lines]' \ $common_exclude_options \ $common_options \ ':ARCHIVE1: _borg_repository_or_archive -a' \ ':ARCHIVE2: _borg_archive "${line[1]%%::*}"' \ '*: : _borg_style_selector_or_archive_files -e "$line[1]" pp' } (( $+functions[_borg-export-tar] )) || _borg-export-tar() { local -a common_options common_exclude_extract_options __borg_setup_common_options __borg_setup_common_exclude_extract_options _arguments -s -w -S : \ '--tar-filter[filter program to pipe data through]: :_cmdstring' \ '--list[output verbose list of items (files, dirs, ...)]' \ $common_exclude_extract_options \ $common_options \ ':ARCHIVE: _borg_repository_or_archive -a' \ ':FILE:_files' \ '*: : _borg_style_selector_or_archive_files -e "$line[1]" pp' } (( $+functions[_borg-extract] )) || _borg-extract() { local -a common_options common_exclude_extract_options __borg_setup_common_options __borg_setup_common_exclude_extract_options _arguments -s -w -S : \ '--list[output verbose list of items (files, dirs, ...)]' \ '(-n --dry-run)'{-n,--dry-run}'[do not actually change any files]' \ '--numeric-owner[only obey numeric user and group identifiers]' \ '--nobsdflags[deprecated, use --noflags instead]' \ '--noacls[do not extract/set ACLs]' \ '--noxattrs[do not extract/set xattrs]' \ '--noflags[do not extract/set flags (e.g. NODUMP, IMMUTABLE)]' \ '--stdout[write all extracted data to stdout]' \ '--sparse[create holes in output sparse file from all-zero chunks]' \ $common_exclude_extract_options \ $common_options \ ':ARCHIVE: _borg_repository_or_archive -a' \ '*: : _borg_style_selector_or_archive_files -e "$line[1]" pp' } (( $+functions[_borg-help] )) || _borg-help() { local -a common_options __borg_setup_common_options _arguments -s -w -S : \ '--epilog-only' \ '--usage-only' \ $common_options \ ':: : _alternative "topics:TOPIC:(patterns placeholders compression)" ": :_borg_commands"' } (( $+functions[_borg-info] )) || _borg-info() { local -a common_options common_archive_filters_options __borg_setup_common_options __borg_setup_common_archive_filters_options _arguments -s -w -S : \ '--json[format output as JSON]' \ $common_archive_filters_options \ $common_options \ '::REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' } (( $+functions[_borg-init] )) || _borg-init() { local -i ret=1 local -a common_options common_init_options __borg_setup_common_options __borg_setup_common_init_options # special handling for the required optional argument if (( ! ${words[(I)(-e|--encryption)(|=*)]} )); then local desc='select encryption key mode' local -a long=( "--encryption:$desc" ) short=( "-e:$desc" ) remove_chars=( -r '= \t\n\-' ) _describe -t required-options 'required option' long -S '=' $remove_chars -- short $remove_chars && ret=0 fi _arguments -s -w -S : \ '(-e --encryption)'{-e,--encryption}'=[select encryption key mode (required)]:MODE:(none keyfile keyfile-blake2 repokey repokey-blake2 authenticated authenticated-blake2)' \ $common_init_options \ '--make-parent-dirs[create parent directories]' \ '::REPOSITORY:_directories' && ret=0 return ret } (( $+functions[_borg-key] )) || _borg-key() { local -a state line common_options local curcontext="$curcontext" state_descr declare -A opt_args local -i ret=1 __borg_setup_common_options _arguments -s -w -C : \ $common_options \ ': :->command' \ '*:: :->option-or-argument' && ret=0 case $state in (command) local -a key_commands=( 'change-passphrase:Change repository key file passphrase' 'export:Export the repository key for backup' 'import:Import the repository key from backup' 'migrate-to-repokey:Migrate passphrase -> repokey' ) _describe -t commands 'command' key_commands && ret=0 ;; (option-or-argument) curcontext="${curcontext%:*}-$line[1]:" case $line[1] in (change-passphrase) _arguments -s -w -S : \ $common_options \ ': :_borg_repository' && ret=0 ;; (export) _arguments -s -w -S : \ '--paper[create an export suitable for printing and later type-in]' \ '--qr-html[create an html file suitable for printing and later type-in or qr scan]' \ $common_options \ ': :_borg_repository' \ '::PATH:_files' && ret=0 ;; (import) _arguments -s -w -S : \ '--paper[interactively import from a backup done with --paper]' \ $common_options \ ': :_borg_repository' \ '::PATH:_files' && ret=0 ;; (migrate-to-repokey) _arguments -s -w -S : \ $common_options \ ':: :_borg_repository' && ret=0 ;; (*) if ! _call_function ret _borg_key_$line[1]; then _default && ret=0 fi ;; esac ;; esac return ret } (( $+functions[_borg-list] )) || _borg-list() { local -a common_options common_exclude_options common_archive_filters_options __borg_setup_common_options __borg_setup_common_exclude_options __borg_setup_common_archive_filters_options _arguments -s -w -S : \ '--consider-checkpoints[show checkpoint archives in the repository contents list (default: hidden)]' \ '--short[only print file/directory names, nothing else]' \ {--format,--list-format}'=[specify format for file listing]:FORMAT: _borg_format_keys $line[1]' \ '--json[Only valid for listing repository contents. Format output as JSON.]' \ '--json-lines[Only valid for listing archive contents. Format output as JSON Lines.]' \ $common_archive_filters_options \ $common_exclude_options \ $common_options \ ':REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' \ '*: : _borg_style_selector_or_archive_files -e "$line[1]" pp' } (( $+functions[_borg-mount] )) || _borg-mount() { local -a common_options common_exclude_extract_options common_archive_filters_options __borg_setup_common_options __borg_setup_common_exclude_extract_options __borg_setup_common_archive_filters_options _arguments -s -w -S : \ $* \ '--consider-checkpoints[show checkpoint archives in the repository contents list (default: hidden)]' \ '(-f --foreground)'{-f,--foreground}'[stay in foreground, do not daemonize]' \ '-o[mount options]: :_fuse_values "mount options" "versions[merged, versioned view of the files in the archives]" "allow_damaged_files[read damaged files]" "ignore_permissions[not enforce \"default_permissions\"]"' \ $common_archive_filters_options \ $common_exclude_extract_options \ $common_options \ ':REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' \ ':MOUNTPOINT:_directories' \ '*: : _borg_style_selector_or_archive_files "$line[1]" pp' } (( $+functions[_borg-prune] )) || _borg-prune() { local -a common_options common_prefix_and_glob_archives_filter_options common_dry_run_stats_options __borg_setup_common_options __borg_setup_common_prefix_and_glob_archives_filter_options __borg_setup_common_dry_run_stats_options _arguments -s -w -S : \ $common_dry_run_stats_options \ '*--force[force pruning of corrupted archives, use "--force --force" in case "--force" does not work]' \ '--list[output verbose list of archives it keeps/prunes]' \ '--keep-within[keep all archives within this time interval]: : _borg_guard_unsigned_number "INTERVAL"' \ '(--keep-last --keep-secondly)'{--keep-last,--keep-secondly}'[number of secondly archives to keep]: : _borg_guard_unsigned_number "N"' \ '--keep-minutely[number of minutely archives to keep]: : _borg_guard_unsigned_number "N"' \ '(-H --keep-hourly)'{-H,--keep-hourly}'[number of hourly archives to keep]: : _borg_guard_unsigned_number "N"' \ '(-d --keep-daily)'{-d,--keep-daily}'[number of daily archives to keep]: : _borg_guard_unsigned_number "N"' \ '(-w --keep-weekly)'{-w,--keep-weekly}'[number of weekly archives to keep]: : _borg_guard_unsigned_number "N"' \ '(-m --keep-monthly)'{-m,--keep-monthly}'[number of monthly archives to keep]: : _borg_guard_unsigned_number "N"' \ '(-y --keep-yearly)'{-y,--keep-yearly}'[number of yearly archives to keep]: : _borg_guard_unsigned_number "N"' \ '--save-space[work slower, but using less space]' \ $common_prefix_and_glob_archives_filter_options \ $common_options \ ':: :_borg_repository' } (( $+functions[_borg-recreate] )) || _borg-recreate() { local -a common_options common_create_options __borg_setup_common_options __borg_setup_common_create_options local -a mods=( 'if-different:recompress if current compression is with a different compression algorithm (the level is not considered)' 'always:recompress even if current compression is with the same compression algorithm (use this to change the compression level)' 'never:do not recompress (use this option to explicitly prevent recompression)' ) mods=( ${(q)mods//\\/\\\\} ) mods=( ${mods//:/\\:} ) _arguments -s -w -S : \ $common_create_options \ '--target=[create a new archive with the name ARCHIVE]:ARCHIVE: _borg_placeholder_or_archive "${line[1]%%\:\:*}"' \ '--recompress=[recompress data chunks according to "MODE" and "--compression"]:MODE:'"(($mods))" \ $common_options \ ':REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' \ '*: : _borg_style_selector_or_archive_files -e "$line[1]" pp' } (( $+functions[_borg-rename] )) || _borg-rename() { local -a common_options __borg_setup_common_options _arguments -s -w -S : \ $common_options \ ':ARCHIVE: _borg_repository_or_archive -a' \ ':NEWNAME' } (( $+functions[_borg-serve] )) || _borg-serve() { local -a common_options common_init_options __borg_setup_common_options __borg_setup_common_init_options _arguments -s -w -S : \ $common_init_options \ '*--restrict-to-path=[restrict repository access to PATH]:PATH:_files' \ '*--restrict-to-repository=[restrict repository access]: :_borg_repository' } (( $+functions[_borg-umount] )) || _borg-umount() { local -a common_options __borg_setup_common_options _arguments -s -w -S : \ $common_options \ ':MOUNTPOINT:_umountable' } (( $+functions[_borg-upgrade] )) || _borg-upgrade() { local -a common_options __borg_setup_common_options _arguments -s -w -S : \ '(-n --dry-run)'{-n,--dry-run}'[do not change repository]' \ '--inplace[rewrite repository in place, with no chance of going back to older versions of the repository]' \ '--force[force upgrade]' \ '--tam[enable manifest authentication (in key and cache)]' \ '--disable-tam[disable manifest authentication (in key and cache)]' \ $common_options \ ':: :_borg_repository' } (( $+functions[_borg-with-lock] )) || _borg-with-lock() { local -a state line common_options local curcontext="$curcontext" state_descr declare -A opt_args local -i ret=1 __borg_setup_common_options _arguments -s -w -C -S : \ $common_options \ '(-): :_borg_repository' \ '(-):COMMAND: _command_names -e' \ '(-)*:ARGS:->normal' && ret=0 case $state in (normal) shift 2 words (( CURRENT -= 2 )) _normal && ret=0 ;; esac return ret } (( $+functions[__borg_setup_common_options] )) || __borg_setup_common_options() { typeset -ga common_options=( '(- :)'{-h,--help}'[show this help message and exit]' '--critical[work on log level CRITICAL]' '--error[work on log level ERROR]' '--warning[work on log level WARNING (default)]' '(--info -v --verbose)'{--info,-v,--verbose}'[work on log level INFO]' '--debug[work on log level DEBUG]' '--debug-topic=[enable TOPIC debugging (can be specified multiple times)]:TOPIC' '(-p --progress)'{-p,--progress}'[show progress information]' '--log-json[Output one JSON object per log line instead of formatted text.]' '--lock-wait=[wait at most SECONDS for acquiring a repository/cache lock (default: 1)]: : _borg_guard_unsigned_number "SECONDS"' '--bypass-lock[bypass locking mechanism]' '(- :)--show-version[show/log the borg version]' '--show-rc[show/log the return code (rc)]' '--umask=[set umask to M (local only, default: 0077)]:M' '--remote-path=[set remote path to executable (default: "borg")]: :_cmdstring' '--remote-ratelimit=[set remote network upload rate limit in kiByte/s (default: 0=unlimited)]: : _borg_guard_unsigned_number "RATE"' '--remote-buffer=[set upload buffer size in MiB. (default: 0=no buffer)]: : _borg_guard_unsigned_number "UPLOAD_BUFFER"' '--consider-part-files[treat part files like normal files (e.g. to list/extract them)]' '--debug-profile=[write execution profile in Borg format into FILE]:FILE:_files' '--rsh=[use COMMAND instead of ssh]: :_cmdstring' ) } (( $+functions[__borg_setup_common_exclude_options] )) || __borg_setup_common_exclude_options() { typeset -ga common_exclude_options=( '*'{-e,--exclude}'=[exclude paths matching PATTERN]: : _borg_style_selector_or_archive_files "$line[1]" fm' '*--exclude-from=[read exclude patterns from EXCLUDEFILE, one per line]:EXCLUDEFILE:_files' '*--pattern=[experimental: include/exclude paths matching PATTERN]: : _borg_style_selector_or_archive_files -p "$line[1]" sh' '*--patterns-from=[experimental: read include/exclude patterns from PATTERNFILE, one per line]:PATTERNFILE:_files' ) } (( $+functions[__borg_setup_common_exclude_extract_options] )) || __borg_setup_common_exclude_extract_options() { local -a common_exclude_options __borg_setup_common_exclude_options typeset -ga common_exclude_extract_options=( $common_exclude_options '--strip-components=[Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped.]: : _borg_guard_unsigned_number "NUMBER"' ) } (( $+functions[__borg_setup_common_prefix_and_glob_archives_filter_options] )) || __borg_setup_common_prefix_and_glob_archives_filter_options() { typeset -ga common_prefix_and_glob_archives_filter_options=( '(-P --prefix -a --glob-archives)'{-P,--prefix}'=[only consider archive names starting with this prefix]:PREFIX: _borg_archive -n "${line[1]%%\:\:*}"' '(-P --prefix)*'{-a,--glob-archives}'=[only consider archive names matching the glob]:GLOB: _borg_archive -n "${line[1]%%\:\:*}"' ) } (( $+functions[__borg_setup_common_archive_filters_options] )) || __borg_setup_common_archive_filters_options() { local -a common_prefix_and_glob_archives_filter_options __borg_setup_common_prefix_and_glob_archives_filter_options typeset -ga common_archive_filters_options=( $common_prefix_and_glob_archives_filter_options '--sort-by=[Comma-separated list of sorting keys, default: timestamp]:KEYS:(timestamp name id)' '(--last)--first=[consider first N archives after other filters were applied]:N: _borg_archive -n "${line[1]%%\:\:*}"' '(--first)--last=[consider last N archives after other filters were applied]:N: _borg_archive -n "${line[1]%%\:\:*}"' ) } (( $+functions[__borg_setup_common_dry_run_stats_options] )) || __borg_setup_common_dry_run_stats_options() { typeset -ga common_dry_run_stats_options=( '(-n --dry-run -s --stats)'{-n,--dry-run}'[do not change anything]' '(-n --dry-run -s --stats)'{-s,--stats}'[print statistics at end]' # NOTE: actual messages for subcommands differ in details ) } (( $+functions[__borg_setup_common_create_options] )) || __borg_setup_common_create_options() { local -a common_dry_run_stats_options common_exclude_options __borg_setup_common_dry_run_stats_options __borg_setup_common_exclude_options typeset -ga common_create_options=( $common_dry_run_stats_options '--list[output verbose list of items (files, dirs, ...)]' '--filter=[only display items with the given status characters]: :_borg_statuschars' $common_exclude_options '--exclude-caches[exclude directories that contain a CACHEDIR.TAG file]' '*--exclude-if-present=[exclude directories that are tagged by containing a filesystem object with the given NAME]:NAME:_files' '--keep-exclude-tags[if tag objects are specified with --exclude-if-present, don'\''t omit the tag objects themselves]' '--comment=[add a comment text to the archive]:COMMENT:_borg_placeholders' '--timestamp=[manually specify the archive creation date/time]:TIMESTAMP:_borg_timestamp' '(-c --checkpoint-interval)'{-c,--checkpoint-interval}'=[write checkpoint every SECONDS seconds (default: 1800)]: : _borg_guard_unsigned_number "SECONDS"' '--chunker-params=[specify the chunker parameters]: :_borg_chunker_params_examples' '(-C --compression)'{-C,--compression}'=[select compression algorithm]: :_borg_compression' ) } (( $+functions[__borg_setup_common_init_options] )) || __borg_setup_common_init_options() { local -a common_options __borg_setup_common_options typeset -ga common_init_options=( $common_options '--append-only[only allow appending to repository segment files]' '--storage-quota=[override storage quota of the repository]: :_borg_quota_suffixes' ) } (( $+functions[_borgfs] )) || _borgfs() { _borg-mount '(- :)'{-V,--version}'[show version number and exit]' } (( $+functions[_borg_parameters] )) || _borg_parameters() { local name=$1 shift local -i ret=1 local -a expl case $name in (REPO) local BORG_REPO unset BORG_REPO _borg_repository && ret=0 ;; ((|NEW_)PASSPHRASE) _message -e 'passphrase' && ret=0 ;; (DISPLAY_PASSPHRASE) _message -e 'answer to the "display the passphrase for verification" question' && ret=0 ;; (HOST_ID) _message -e 'unique ID' && ret=0 ;; (FILES_CACHE_TTL) _borg_guard_unsigned_number 'time to live (default: 20)' && ret=0 ;; (MOUNT_DATA_CACHE_ENTRIES) _borg_guard_unsigned_number 'number of cached data chunks' && ret=0 ;; (PASSCOMMAND|RSH|REMOTE_PATH) _cmdstring && ret=0 ;; (HOSTNAME_IS_UNIQUE|SHOW_SYSINFO|(UNKNOWN_UNENCRYPTED|RELOCATED)_REPO_ACCESS_IS_OK) _description values expl 'value' compadd "$expl[@]" yes no && ret=0 ;; ((CHECK|DELETE)_I_KNOW_WHAT_I_AM_DOING) _description values expl 'value' compadd "$expl[@]" YES NO && ret=0 ;; (SELFTEST) _description values expl 'value' compadd "$expl[@]" disabled && ret=0 ;; (WORKAROUNDS) _wanted workarounds expl 'workaround' _sequence compadd - basesyncfile && ret=0 ;; (KEYS_DIR) _directories && ret=0 ;; (*) _default && ret=0 ;; esac return ret } (( $+functions[_borg_repository] )) || _borg_repository() { local -a alts opts qopts zparseopts -E -a opts S: qopts=( ${(q)opts} ) [[ -n $BORG_REPO ]] && alts+=( "default-repository: : __borg_default_repository $qopts" ) alts+=( "cached-repositories:cached repositories:_borg_cached_repositories $qopts" ) alts+=( 'directories: :_directories -r ":/ \t\n\-"' ) alts+=( 'remote-repositories: : _borg_remote_repositories' ) _alternative $alts } (( $+functions[__borg_default_repository] )) || __borg_default_repository() { local -a opts suf zparseopts -E -a opts S: (( $opts[(I)-S] )) && suf=( -S '' ) local -a default_repository=( "\:\::$BORG_REPO" ) _describe -t default-repository 'default repository' default_repository "$suf[@]" } (( $+functions[_borg_cached_repositories] )) || _borg_cached_repositories() { local -a cached_repos local sed_script='/^previous_location = / { s/// # no port was given /ssh:\/\/[^/:]+:[0-9]+/! { # lstrip the `ssh://` prefix and add a colon before the first slash s!ssh://([^:/]+)/(.*)!\1:/\2! } p }' local cachedir=${BORG_CACHE_DIR:-${XDG_CACHE_HOME:-${BORG_BASE_DIR:-$HOME}/.cache}/borg} cached_repos=( ${(f)"$(_call_program -p cached-repositories sed -n -E ${(q)sed_script} \ "${(q)cachedir}/*/config(#qN.om)" 2>/dev/null)"} ) if [[ $compstate[quote] != (\'|\") ]]; then # hide ~BORG_REPO and other scalars local BORG_REPO unset BORG_REPO sed_script cachedir cached_repos=( "${(@D)cached_repos}" ) fi compadd -Q "$@" -r ': \t\n\-' -a cached_repos } (( $+functions[_borg_remote_repositories] )) || _borg_remote_repositories() { local -a match mbegin mend expl alts if compset -P '(#b)ssh://[^/]##@[^/]##:([0-9]##)/'; then _remote_files -/ -- ssh -p $match[1] return fi local -i have_scheme=0 compset -P 'ssh://' && have_scheme=1 if compset -P '*:'; then (( have_scheme )) && alts+=( 'ports: : _borg_guard_unsigned_number "port"' ) alts+=( 'remote-files:remote file: _remote_files -/ -- ssh' ) _alternative $alts elif compset -P 1 '(#b)(*)@'; then local user=$match[1] _wanted -C user-at hosts expl "host for $user" \ _combination -s '[:@]' accounts users-hosts users="$user" hosts -S ':' - elif compset -S '@*'; then _wanted users expl "user" \ _combination -s '[:@]' accounts users-hosts users -q - else alts=( 'users:user:_users -S "@"' 'hosts:host:_hosts -S ":"' ) (( ! have_scheme )) && alts+=( 'prefixes:ssh:compadd -S "" ssh://' ) _alternative $alts fi } # _borg_repository_or_archive [-a] [-p] # # -a archive is mandatory. The suffix `::` will be added to the repository if possible. # -p complete placeholders (( $+functions[_borg_repository_or_archive] )) || _borg_repository_or_archive() { local -A opts zparseopts -A opts -D -E a p if compset -P 1 '*::'; then local qrepo=$IPREFIX[1,-3] local -i def_repo=0 [[ -z $qrepo && -n $BORG_REPO ]] && qrepo=${(q)BORG_REPO} && def_repo=1 if [[ -n $qrepo ]]; then if (( ! def_repo )); then case $compstate[quote] in (\') qrepo=${(qq)qrepo} ;; (\") qrepo=${(qqq)${(e)qrepo}} ;; # NOTE: currently `(e)` don't have any effect, but maybe one day zsh will stop to change the quoting method # of double quoted parameters esac fi if (( $+opts[-p] )); then _borg_placeholder_or_archive $qrepo else _borg_archive $qrepo fi else _message "not a borg repository: ${(Q)qrepo}" return 1 fi else local -a suf if ! compset -S '::*'; then if (( $+opts[-a] )) || zstyle -T ":completion:${curcontext}:repositories" repository-suffix; then suf=( -S '::' ) fi local oqrepo="$PREFIX$SUFFIX" local qrepo=$oqrepo [[ $compstate[quote] != (\'|\") ]] && qrepo=${(Q)qrepo} if __borg_is_borg_repo $qrepo; then qrepo=${oqrepo%%/} [[ -z $SUFFIX ]] && PREFIX=${PREFIX%%/} || SUFFIX=${SUFFIX%%/} compadd -S '::' -r ':/ \t\n\-' -Q -- $qrepo return fi fi _borg_repository "$suf[@]" fi } # _borg_archive [-F] [-n] [qrepo] # # -F don't apply archive filter options on the command line # -n reverse order, disable matchers and don't do menu completion/selection (( $+functions[_borg_archive] )) || _borg_archive() { local -A opts zparseopts -A opts -D -E F n local qrepo=$1 if [[ -z $qrepo ]]; then if [[ -n $BORG_REPO ]]; then qrepo=${(q)BORG_REPO} else _message 'no repository specified' return 1 fi fi local -i ret=1 _tags archives while _tags; do if _requested archives; then local -a expl disp archive_filters local -i reversed_order=1 if (( ! $+opts[-F] )); then local -a archive_filter_options=( -P --prefix -a --glob-archives --first --last --sort-by ) tmp local k for k in $archive_filter_options; do if [[ -n $opt_args[$k] ]]; then IFS=: read -A tmp <<<$opt_args[$k] archive_filters+=( $k=${^tmp:#} ) fi done fi if (( $+opts[-n] )); then __borg_skip_pattern_matching || return 1 disp+=( -U ) compstate[insert]='' compstate[list]='list force' reversed_order=0 fi local -a asort zstyle -a ":completion:${curcontext}:archives" archive-sort asort if (( $asort[(I)inverse] )); then (( reversed_order = ! reversed_order )) fi local -a sort_by=( --sort-by=${(M)^asort:#(timestamp|name|id)} ) # NOTE: in case of option repetition, the later one takes precedence if (( ! $+__borg_archives_need_update )); then comppostfuncs+=( __borg_unset_archives_need_update ) typeset -gHi __borg_archives_need_update=1 if (( ! $#archive_filters && ! $+opts[-n] )); then local erepo [[ -n $1 ]] && __borg_expand_path ${(Q)qrepo} erepo local -a newest_file=( $erepo/(hints|index|integrity).<1->(#qN.om[1]) ) if [[ -n $newest_file ]]; then if zmodload -F zsh/stat b:zstat 2>/dev/null; then local -a stats zstat -A stats +mtime $newest_file local -i mtime=$stats[1] if [[ $__borg_prev_repo == $erepo && __borg_prev_mtime -ge mtime && $__borg_prev_order == $reversed_order && $__borg_prev_sort_by == $sort_by ]] then __borg_archives_need_update=0 else typeset -gH __borg_prev_repo=$erepo typeset -gHi __borg_prev_mtime=mtime __borg_prev_order=reversed_order typeset -gHa __borg_prev_sort_by=( $sort_by ) fi fi fi else unset __borg_prev_{repo,mtime,order,sort_by} comppostfuncs+=( __borg_unset_archives ) fi fi if zstyle -t ":completion:${curcontext}:archives" verbose; then if (( __borg_archives_need_update || ! $+__borg_archive_names || ! $+__borg_archive_descriptions )); then __borg_archives_need_update=0 typeset -gHa __borg_archive_names=() __borg_archive_descriptions=() local fmt descfmt name desc zstyle -s ":completion:${curcontext}:archives" archive-description-format descfmt || descfmt='{archive:<36} {time} [{id}]' fmt="{barchive}{NUL}$descfmt{NUL}" _call_program -p archive-descriptions \ ${(q)__borg_command:-borg} list --format=${(q)fmt} ${(q)sort_by} $archive_filters $qrepo 2>/dev/null | while IFS= read -r -d $'\0' name && IFS= read -r -d $'\0' descr; do __borg_archive_names[1,0]=( $name ) __borg_archive_descriptions[1,0]=( "$descr" ) done (( $pipestatus[1] )) && { _message "couldn't list repository: ${(Q)qrepo}" unset __borg_prev_{repo,mtime,order,sort_by} return 1 } (( ! reversed_order )) && __borg_archive_names=( "${(@aO)__borg_archive_names}" ) && __borg_archive_descriptions=( "${(@aO)__borg_archive_descriptions}" ) fi disp+=( -ld __borg_archive_descriptions ) elif (( __borg_archives_need_update || ! $+__borg_archive_names )); then __borg_archives_need_update=0 typeset -gHa __borg_archive_names=() local fmt='{barchive}{NUL}' __borg_archive_names=( ${(@0aO)"$(_call_program -p archives \ ${(q)__borg_command:-borg} list --format=${(q)fmt} ${(q)sort_by} $archive_filters $qrepo 2>/dev/null)"} ) (( $pipestatus[1] )) && { _message "couldn't list repository: ${(Q)qrepo}" unset __borg_prev_{repo,mtime,order,sort_by} return 1 } (( ! reversed_order )) && __borg_archive_names=( "${(@aO)__borg_archive_names}" ) fi _all_labels archives expl 'ARCHIVE' compadd "$disp[@]" -a __borg_archive_names && ret=0 fi (( ret )) || return 0 done return 1 } (( $+functions[__borg_unset_archives] )) || __borg_unset_archives() { unset __borg_archive_names __borg_archive_descriptions } (( $+functions[__borg_unset_archives_need_update] )) || __borg_unset_archives_need_update() { unset __borg_archives_need_update } (( $+functions[__borg_is_borg_repo] )) || __borg_is_borg_repo() { local repo=$1 __borg_expand_path $repo repo if [[ -d $repo && -d $repo/data ]]; then local -a files=( $repo/(hints|index|integrity).<1->(#qN.) ) (( $#files >= 3 )) && return 0 fi return 1 } (( $+functions[__borg_expand_path] )) || __borg_expand_path() { local _path=$1 local -a match mbegin mend if [[ $_path == (#b)(\~[^/]#)(|/*) ]]; then local etilde etilde=$~match[1] 2>/dev/null _path="$etilde$match[2]" fi _path=${(e)_path//\\\\/\\\\\\\\} eval typeset -g ${2:-REPLY}=\$_path } (( $+functions[_borg_placeholder_or_archive] )) || _borg_placeholder_or_archive() { local qrepo=$1 shift _alternative \ 'placeholders: :_borg_placeholders' \ "archives: : _borg_archive ${(q)qrepo}" } (( $+functions[_borg_placeholders] )) || _borg_placeholders() { local -a placeholders=( 'hostname:The (short) hostname of the machine.' 'fqdn:The full name of the machine.' 'reverse-fqdn:The full name of the machine in reverse domain name notation.' 'now:The current local date and time, by default in ISO-8601 format. You can also supply your own format string, e.g. {now:%Y-%m-%d_%H:%M:%S}' 'utcnow:The current UTC date and time, by default in ISO-8601 format. You can also supply your own format string, e.g. {utcnow:%Y-%m-%d_%H:%M:%S}' 'user:The user name (or UID, if no name is available) of the user running borg.' 'pid:The current process ID.' 'borgversion:The version of borg, e.g.: 1.0.8rc1' 'borgmajor:The version of borg, only the major version, e.g.: 1' 'borgminor:The version of borg, only major and minor version, e.g.: 1.0' 'borgpatch:The version of borg, only major, minor and patch version, e.g.: 1.0.8' ) __borg_complete_keys _describe -t placeholders 'placeholder' placeholders '"$copts[@]"' } (( $+functions[_borg_format_keys] )) || _borg_format_keys() { local repo_or_arch=${(Q)1} local -a keys=( NEWLINE NL NUL SPACE TAB CR LF ) local -a repository_keys=( archive name barchive comment bcomment id start time end command_line hostname username ) local -a archive_keys=( type mode uid gid user group path bpath source linktarget flags size csize dsize dcsize num_chunks unique_chunks mtime ctime atime isomtime isoctime isoatime blake2b blake2s md5 sha1 sha224 sha256 sha384 sha3_224 sha3_256 sha3_384 sha3_512 sha512 shake_128 shake_256 archiveid archivename extra health ) local akeys rkeys akeys='archive-keys:archive keys:compadd -a archive_keys' rkeys='repository-keys:repository keys:compadd -a repository_keys' local -a alts=( 'keys:keys:compadd -a keys' ) if [[ $repo_or_arch == *::?* ]]; then alts+=( $akeys ) elif [[ -n $repo_or_arch ]]; then alts+=( $rkeys ) else alts+=( $rkeys $akeys ) fi __borg_complete_keys _alternative -O copts ${(q)alts} } (( $+functions[__borg_complete_keys] )) || __borg_complete_keys() { compset -P '*[^A-Za-z]##' compset -S '[^A-Za-z]##*' [[ -n $ISUFFIX ]] && compstate[to_end]='' # NOTE: `[[ -n $ISUFFIX ]]` is a workaround for a bug that causes cursor movement to the right further than it should # NOTE: the _oldlist completer doesn't respect compstate[to_end]='' local ipref suf if [[ $IPREFIX[-1] != '{' ]]; then ipref='{' [[ $compstate[quote] != (\'|\") ]] && ipref='\{' fi if [[ $ISUFFIX[1] != (|\\)\} ]]; then suf='}' [[ $compstate[quote] != (\'|\") ]] && suf='\}' fi local -a copts=( -i "$ipref" -S "$suf" ) eval "$@" } # _borg_style_selector_or_archive_files [-e] [-p] archive default_style_selector # # -e apply exclusion options on the command line # -p complete `--pattern` # -f complete files rather than borg paths (( $+functions[_borg_style_selector_or_archive_files] )) || _borg_style_selector_or_archive_files() { local -A opts zparseopts -A opts -D -E e p f local arch=$1 default_style_selector=$2 shift 2 local -a match mbegin mend expl tags=( style-selectors archive-files ) ss_suf=( -S ':' -r ':' ) (( $+opts[-f] )) && tags=( style-selectors files ) local -i ret=1 if (( $+opts[-p] )); then if ! compset -P '(#b)([RP\+\-\!])'; then local -a pattern_rules=( 'P:pattern style' 'R:root path' '+:include' '-:exclude' '!:exclude non-recurse' ) _describe -t pattern-rules 'pattern rule' pattern_rules -S '' return else if [[ $compstate[quote] == (\'|\") ]]; then compset -P ' #' else compset -P '(\\ )#' fi if [[ $match[1] == 'R' ]]; then default_style_selector='pp' elif [[ $match[1] == 'P' ]]; then tags=( style-selectors ) ss_suf=() fi fi fi _tags $tags while _tags; do if _requested style-selectors; then _all_labels style-selectors expl 'style selector' \ __borg_style_selectors $default_style_selector "$ss_suf[@]" - && ret=0 fi if _requested archive-files; then _all_labels archive-files expl 'PATTERN' \ __borg_archive_files ${(k)opts} "$arch" $default_style_selector - && ret=0 fi if _requested files; then local -a borg_paths=( ${(Q)${(e)${~@}}} ) _all_labels files expl 'PATH' \ __borg_pattern_files ${(k)opts} borg_paths - && ret=0 fi (( ret )) || return 0 done return 1 } (( $+functions[__borg_style_selectors] )) || __borg_style_selectors() { local default_style_selector=$1 path_style_selector shift zstyle -s ":completion:${curcontext}:archive-files" path-style-selector path_style_selector || path_style_selector='fm' local -a disp local -A style_selectors __borg_setup_style_selectors if zstyle -T ":completion:${curcontext}:style-selectors" verbose; then local -a style_selector_descriptions extra local k v sep for k v in ${(kv)style_selectors}; do extra=() [[ $k == $default_style_selector ]] && extra+=( 'default' ) [[ $k == $path_style_selector ]] && __borg_choose_path_or_pattern "" "$default_style_selector" && extra+=( 'path' ) (( $#extra )) && v+=" (${(j:, :)extra})" style_selector_descriptions+=( "${${k//\\/\\\\}//:/\\:}:$v" ) done zstyle -s ":completion:${curcontext}:style-selectors" list-separator sep || sep=-- zformat -a style_selector_descriptions " $sep " $style_selector_descriptions disp=( -ld style_selector_descriptions ) fi compadd "$disp[@]" "$@" -k style_selectors } (( $+functions[__borg_archive_files] )) || __borg_archive_files() { local -A opts zparseopts -A opts -D e p local arch=$1 default_style_selector=$2 shift 2 if [[ -z $arch || $arch != *::?* ]]; then _message 'no archive specified' return 1 fi local -a qargs tmp disp pref match mbegin mend archive_files descs local -A style_selectors local k cword fmt descfmt style_selector path_style_selector name descr # take into account exclude options on the command line if (( $+opts[-e] )); then local -a exclude_options=( -e --exclude --exclude-from --pattern --pattern-from ) local -a excludes for k in $exclude_options; do if [[ -n $opt_args[$k] ]]; then IFS=: read -A tmp <<<$opt_args[$k] excludes+=( $k="${^tmp[@]}" ) fi done [[ -n $excludes ]] && qargs+=( "$excludes[@]" ) fi (( $_matcher_num > 1 )) && return 1 __borg_skip_pattern_matching || return 1 cword="$PREFIX$SUFFIX" [[ $compstate[quote] != (\'|\") ]] && cword=${(Q)cword} [[ -z $cword ]] && return 1 if zstyle -t ":completion:${curcontext}:archive-files" verbose; then zstyle -s ":completion:${curcontext}:archive-files" file-description-format descfmt || descfmt='{mode} {user:6} {group:6} {size:8d} {mtime} {path}{extra}' fmt="{bpath}{NUL}$descfmt{NUL}" else fmt='{bpath}{NUL}' fi qargs+=( --format=${(q)fmt} ) qargs+=( $arch ) __borg_setup_style_selectors [[ $cword == (#b)(${~${(j:|:)${(kb)style_selectors}}}):* ]] && style_selector=$match[1] local -i path_expected=0 __borg_choose_path_or_pattern "$style_selector" $default_style_selector $cword && path_expected=1 if [[ -n $cword ]]; then if (( path_expected )); then [[ -n $style_selector ]] && compset -P "$style_selector:" && pref=( -P "$style_selector:" ) cword="$PREFIX$SUFFIX" [[ $compstate[quote] != (\'|\") ]] && cword=${(Q)cword} zstyle -s ":completion:${curcontext}:archive-files" path-style-selector path_style_selector || path_style_selector='fm' cword="$path_style_selector:$cword" else [[ -z $style_selector ]] && cword="$default_style_selector:$cword" fi qargs+=( ${(q)cword} ) fi if zstyle -t ":completion:${curcontext}:archive-files" verbose; then _call_program -p archive-file-descriptions ${(q)__borg_command:-borg} list $qargs 2>/dev/null | while IFS= read -r -d $'\0' name && IFS= read -r -d $'\0' descr; do archive_files+=( $name ) descs+=( $descr ) done (( $pipestatus[1] )) && { _message "couldn't list archive: ${(Q)arch}"; return 1 } disp=( -ld descs ) else archive_files=( ${(0)"$(_call_program -p archive-files ${(q)__borg_command:-borg} list $qargs 2>/dev/null)"} ) (( $pipestatus[1] )) && { _message "couldn't list archive: ${(Q)arch}"; return 1 } fi if (( $#archive_files )); then if (( path_expected )); then compstate[insert]='automenu' else compstate[insert]='' compstate[list]='list force' fi fi compadd "$pref[@]" -U "$disp[@]" "$@" -a archive_files } (( $+functions[__borg_choose_path_or_pattern] )) || __borg_choose_path_or_pattern() { local ss=$1 defss=$2 cword=$3 shift 2 [[ $ss == (pp|pf) || ( -z $ss && $defss == (pp|pf) ) ]] } # transform borg exclude patterns into zsh ignore patterns and then complete files (( $+functions[__borg_pattern_files] )) || __borg_pattern_files() { local -A opts zparseopts -A opts -D -E e p f local paths_varname=$1 shift local -a args local -A style_selectors __borg_setup_style_selectors local pr_pat='[RP\+\-\!]' ss_pat="(${(j:|:)${(@kb)style_selectors}}):" local prs_pat="$pr_pat #" if (( $+opts[-e] )); then local -a borg_excludes exclude_options=( -e --exclude --pattern ) tmp local k cword local -i i for k in $exclude_options; do if [[ -n $opt_args[$k] ]]; then IFS=: read -A tmp <<<$opt_args[$k] tmp=( ${(Q)tmp} ) # lstrip style selectors and pattern rules [[ $+opts[-p] -gt 0 || $k == --pattern ]] && tmp=( ${tmp#$~prs_pat} ) tmp=( ${tmp#$~ss_pat} ) # don't take into account the word under the cursor cword="$PREFIX$SUFFIX" [[ $compstate[quote] != (\'|\") ]] && cword=${(Q)cword} [[ $+opts[-p] -gt 0 || $k == --pattern ]] && cword=${cword#$~prs_pat} cword=${cword#$~ss_pat} i=$tmp[(I)$cword] (( i )) && tmp=( "${(@)tmp[1,i-1]}" "${(@)tmp[i+1,-1]}" ) borg_excludes+=( "$tmp[@]" ) fi done [[ -n $borg_excludes ]] && args+=( -F borg_excludes ) fi [[ -n ${(P)paths_varname} ]] && args+=( -W $paths_varname ) args+=( "$@" ) # lstrip style selectors and pattern rules if (( $+opts[-p] )); then if [[ $compstate[quote] != (\'|\") ]]; then compset -P $pr_pat compset -P '(\\ )#' else compset -P $prs_pat fi fi compset -P $ss_pat compstate[insert]='' compstate[list]='list force' _path_files "$args[@]" } (( $+functions[__borg_setup_style_selectors] )) || __borg_setup_style_selectors() { typeset -gA style_selectors=( fm 'Fnmatch' sh 'Shell-style patterns' re 'Regular expressions' pp 'Path prefix' pf 'Path full-match' ) } (( $+functions[__borg_skip_pattern_matching] )) || __borg_skip_pattern_matching() { # unset glob_complete [[ $compstate[pattern_match] == '*' ]] && compstate[pattern_match]='' # skip the _match completer [[ -n $compstate[pattern_match] ]] && return 1 return 0 } (( $+functions[_borg_config] )) || _borg_config() { local qrepo=$1 shift if (( ! $+__borg_config_sect )); then comppostfuncs+=( __borg_unset_config ) typeset -gH __borg_config_sect= typeset -gHa __borg_config_keys=() local sect line local -a match mbegin mend _call_program -p keys ${(q)__borg_command:-borg} config --list $qrepo 2>/dev/null | { IFS= read -r sect sect=${${sect#\[}%\]} __borg_config_sect=$sect while IFS= read -r line && [[ $line == (#b)(*)\ =\ (*) ]]; do __borg_config_keys+=( "${${match[1]//\\/\\\\}//:/\\:}:(current: $match[2])" ) done } fi local -a alts=( 'keys:key: _describe -t keys "key" __borg_config_keys' ) compset -P "${__borg_config_sect}." || alts+=( 'sections:section:compadd -S "." $__borg_config_sect' ) _alternative $alts } (( $+functions[__borg_unset_config] )) || __borg_unset_config() { unset __borg_config_sect __borg_config_keys } # A simple prefix-oriented completion function for compressors. Can be improved by supporting the suffix. (( $+functions[_borg_compression] )) || _borg_compression() { local -a nolvl=( 'none:do not compress' 'lz4:very high speed, very low compression' ) local -a havelvl=( 'zstd:("zstandart")' 'zlib:("gz") medium speed, medium compression' 'lzma:("xz") low speed, high compression' ) local -a auto=( 'auto:compress compressible, otherwise "none"' ) local -a match mbegin mend # NOTE: Zsh's `-prefix` condition is confused by the leading parenthesis in the pattern. # Fortunately, we simply need to show a message. if compset -P '(#b)(|auto,)(zstd|zlib|lzma),'; then local -i from to def case $match[2] in (zstd) from=1 to=22 def=3 ;; (zlib|lzma) from=0 to=9 def=6 ;; esac _message -e "compression level (from $from to $to, default: $def)" elif compset -P 'auto,'; then _describe -t compression 'compression' nolvl -- havelvl -qS, else _describe -t compression 'compression' nolvl -- havelvl -qS, -- auto -S, fi } (( $+functions[_borg_chunker_params] )) || _borg_chunker_params() { if compset -P 'buzhash,'; then if compset -P '*,*,*,'; then _message -e 'HASH_WINDOW_SIZE' elif compset -P '*,*,'; then _message -e 'HASH_MASK_BITS (statistical medium chunk size ~= 2^HASH_MASK_BITS B)' elif compset -P '*,'; then _message -e 'CHUNK_MAX_EXP (maximum chunk size = 2^CHUNK_MAX_EXP B)' else _message -e 'CHUNK_MIN_EXP (minimum chunk size = 2^CHUNK_MIN_EXP B)' fi elif compset -P 'fixed,'; then if compset -P '*,'; then _message -e 'HEADER_SIZE (B)' else _message -e 'BLOCK_SIZE (B)' fi else local -a algorithms=( 'fixed:a simple, low cpu overhead, fixed blocksize chunker, optionally supporting a header block of different size' 'buzhash:variable, content-defined blocksize, uses a rolling hash computed by the Buzhash algorithm' ) _describe -t algorithm 'ALGO' algorithms -S , fi } (( $+functions[_borg_chunker_params_examples] )) || _borg_chunker_params_examples() { local -a params=( 'default:buzhash,19,23,21,4095' 'buzhash,19,23,21,4095:small amount of chunks (default)' 'buzhash,10,23,16,4095:big amount of chunks' ) params=( ${(q)params} ) _alternative \ 'chunker-params: :_borg_chunker_params' \ "chunker-params-examples:chunker params examples:(($params))" } (( $+functions[_borg_statuschars] )) || _borg_statuschars() { _values -s '' 'STATUSCHARS' \ 'A[regular file, added]' \ 'M[regular file, modified]' \ 'U[regular file, unchanged]' \ 'C[regular file, it changed while we backed it up]' \ 'E[regular file, an error happened while accessing/reading this file]' \ 'd[directory]' \ 'b[block device]' \ 'c[char device]' \ 'h[regular file, hardlink (to already seen inodes)]' \ 's[symlink]' \ 'f[fifo]' \ 'i[backup data was read from standard input (stdin)]' \ '-[dry run, item was not backed up]' \ 'x[excluded, item was not backed up]' \ '?[missing status code]' } (( $+functions[_borg_quota_suffixes] )) || _borg_quota_suffixes() { if compset -P '[0-9]##'; then local -a suffixes=( 'K:10 ** 3 bytes' 'M:10 ** 6 bytes' 'G:10 ** 9 bytes' 'T:10 ** 12 bytes' 'P:10 ** 15 bytes' ) # NOTE: tag `suffixes` is already in use (file extensions) _describe -t multiplier 'suffix' suffixes else _message -e 'QUOTA' fi } (( $+functions[_borg_timestamp] )) || _borg_timestamp() { _alternative \ "dates:TIMESTAMP: _dates -f '%FT%T'" \ 'files:reference:_files' } (( $+functions[_borg_guard_unsigned_number] )) || _borg_guard_unsigned_number() { local -A opts zparseopts -K -D -A opts M+: J+: V+: 1 2 o+: n F: x+: X+: _guard '[0-9]#' ${1:-number} } _borg() { local -a match mbegin mend line state local curcontext="$curcontext" state_descr typeset -A opt_args local -i ret=1 if [[ $service == 'borg' ]]; then local __borg_command=$words[1] local -a common_options __borg_setup_common_options _arguments -s -w -C : \ '(- :)'{-V,--version}'[show version number and exit]' \ $common_options \ '(-): :->command' \ '(-)*:: :->option-or-argument' && return case $state in (command) _borg_commands && ret=0 ;; (option-or-argument) curcontext="${curcontext%:*:*}:borg-$words[1]:" if ! _call_function ret _borg-$words[1]; then _default && ret=0 fi ;; esac elif [[ $service == (#b)-value-,BORG_(*),-default- ]]; then _borg_parameters $match[1] && ret=0 elif ! _call_function ret _$service; then _default && ret=0 fi return ret } _borg "$@" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/sign-binaries0000755000076500000240000000051414601576577016407 0ustar00twstaff#!/bin/bash D=$1 if [ "$D" = "" ]; then echo "Usage: sign-binaries 201912312359" exit fi if [ "$QUBES_GPG_DOMAIN" = "" ]; then GPG=gpg else GPG=qubes-gpg-client-wrapper fi for file in dist/borg-*; do $GPG --local-user "Thomas Waldmann" --armor --detach-sign --output $file.asc $file done touch -t $D dist/* ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/scripts/upload-pypi0000755000076500000240000000037114601576577016121 0ustar00twstaff#!/bin/bash R=$1 if [ "$R" = "" ]; then echo "Usage: upload-pypi 1.2.3 [test]" exit fi if [ "$2" = "test" ]; then export TWINE_REPOSITORY=testpypi else export TWINE_REPOSITORY=pypi fi D=dist/borgbackup-$R.tar.gz twine upload $D ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1711734324.222737 borgbackup-1.2.8/setup.cfg0000644000076500000240000000416314601577064014055 0ustar00twstaff[tool:pytest] python_files = testsuite/*.py markers = allow_cache_wipe [flake8] ignore = E226, W503 per_file_ignores = docs/conf.py:E121,E126,E265,E305,E401,E402 src/borg/archive.py:E122,E125,E127,E402,E501,F401,F405,W504 src/borg/archiver.py:E125,E126,E127,E128,E501,E722,E731,E741,F401,F405,W504 src/borg/cache.py:E127,E128,E402,E501,E722,W504 src/borg/fuse.py:E402,E501,E722,W504 src/borg/fuse_impl.py:F811 src/borg/locking.py:E128,E501,E722 src/borg/remote.py:E128,E501,F405 src/borg/repository.py:E126,E128,E501,F401,F405,W504 src/borg/shellpattern.py:E501 src/borg/upgrader.py:E501 src/borg/xattr.py:E402 src/borg/crypto/key.py:E125,E128,E402,E501,F401,F405,W504 src/borg/crypto/keymanager.py:E126,E128,E501,F401 src/borg/crypto/nonces.py:E128,E501 src/borg/helpers/__init__.py:F401,F405 src/borg/helpers/checks.py:F401 src/borg/helpers/errors.py:F405 src/borg/helpers/fs.py:F405 src/borg/helpers/manifest.py:E128,E402,E501,F405 src/borg/helpers/misc.py:E402,E722,F401,F405 src/borg/helpers/msgpack.py:E127,F405 src/borg/helpers/parseformat.py:E402,E501,E741,F401,F405 src/borg/helpers/process.py:E402,F401,W504 src/borg/helpers/progress.py:E402 src/borg/platform/__init__.py:F401,F811 src/borg/platform/base.py:E402 src/borg/testsuite/__init__.py:E501,F401 src/borg/testsuite/archive.py:E128,W504 src/borg/testsuite/archiver.py:E128,E501,E722,F401,F405,F811 src/borg/testsuite/benchmark.py:F401,F811 src/borg/testsuite/chunker.py:E501,F405 src/borg/testsuite/chunker_pytest.py:F401 src/borg/testsuite/chunker_slow.py:F405 src/borg/testsuite/crypto.py:E126,E501,E741 src/borg/testsuite/file_integrity.py:F401 src/borg/testsuite/hashindex.py:F401 src/borg/testsuite/helpers.py:E126,E127,E128,E501,F401 src/borg/testsuite/key.py:E501,F401 src/borg/testsuite/locking.py:E126,E128,E501,E722,F401 src/borg/testsuite/patterns.py:E123 src/borg/testsuite/platform.py:E128,E501,F401,F811 src/borg/testsuite/repository.py:E128,E501,F401 src/borg/testsuite/shellpattern.py:E123 src/borg/testsuite/upgrader.py:F405 max_line_length = 120 exclude = build,dist,.git,.idea,.cache,.tox [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/setup.py0000644000076500000240000002725114601576577013761 0ustar00twstaff# borgbackup - main setup code (see also other setup_*.py files) import os import sys from collections import defaultdict from glob import glob try: import multiprocessing except ImportError: multiprocessing = None from setuptools.command.build_ext import build_ext from setuptools import setup, find_namespace_packages, Extension, Command from setuptools.command.sdist import sdist try: from Cython.Build import cythonize except ImportError: cythonize = None sys.path += [os.path.dirname(__file__)] import setup_checksums import setup_compress import setup_crypto import setup_docs is_win32 = sys.platform.startswith('win32') # How the build process finds the system libs / uses the bundled code: # # 1. it will try to use (system) libs (see 1.1. and 1.2.), # except if you use these env vars to force using the bundled code: # BORG_USE_BUNDLED_XXX undefined --> try using system lib # BORG_USE_BUNDLED_XXX=YES --> use the bundled code # Note: do not use =NO, that is not supported! # 1.1. if BORG_LIBXXX_PREFIX is set, it will use headers and libs from there. # 1.2. if not and pkg-config can locate the lib, the lib located by # pkg-config will be used. We use the pkg-config tool via the pkgconfig # python package, which must be installed before invoking setup.py. # if pkgconfig is not installed, this step is skipped. # 2. if no system lib could be located via 1.1. or 1.2., it will fall back # to using the bundled code. # OpenSSL is required as a (system) lib in any case as we do not bundle it. # Thus, only step 1.1. and 1.2. apply to openssl (but not 1. and 2.). # needed: openssl >=1.0.2 or >=1.1.0 (or compatible) system_prefix_openssl = os.environ.get('BORG_OPENSSL_PREFIX') # needed: lz4 (>= 1.7.0 / r129) prefer_system_liblz4 = not bool(os.environ.get('BORG_USE_BUNDLED_LZ4')) system_prefix_liblz4 = os.environ.get('BORG_LIBLZ4_PREFIX') # needed: zstd (>= 1.3.0) prefer_system_libzstd = not bool(os.environ.get('BORG_USE_BUNDLED_ZSTD')) system_prefix_libzstd = os.environ.get('BORG_LIBZSTD_PREFIX') prefer_system_libxxhash = not bool(os.environ.get('BORG_USE_BUNDLED_XXHASH')) system_prefix_libxxhash = os.environ.get('BORG_LIBXXHASH_PREFIX') # Number of threads to use for cythonize, not used on windows cpu_threads = multiprocessing.cpu_count() if multiprocessing and multiprocessing.get_start_method() != 'spawn' else None # Are we building on ReadTheDocs? on_rtd = os.environ.get('READTHEDOCS') install_requires = [ # we are rather picky about msgpack versions, because a good working msgpack is # very important for borg, see: https://github.com/borgbackup/borg/issues/3753 # Please note: # using any other msgpack version is not supported by borg development and # any feedback related to issues caused by this will be ignored. 'msgpack >=0.5.6, <=1.0.8, !=1.0.1', 'packaging', ] # note for package maintainers: if you package borgbackup for distribution, # please (if available) add pyfuse3 (preferably) or llfuse (not maintained any more) # as a *requirement*. "borg mount" needs one of them to work. # if neither is available, do not require it, most of borgbackup will work. extras_require = { 'llfuse': [ 'llfuse >= 1.3.8', ], 'pyfuse3': [ 'pyfuse3 >= 3.1.1', ], 'nofuse': [], } # Extra cflags for all extensions, usually just warnings we want to explicitly enable cflags = [ '-Wall', '-Wextra', '-Wpointer-arith', ] compress_source = 'src/borg/compress.pyx' crypto_ll_source = 'src/borg/crypto/low_level.pyx' crypto_helpers = 'src/borg/crypto/_crypto_helpers.c' chunker_source = 'src/borg/chunker.pyx' hashindex_source = 'src/borg/hashindex.pyx' item_source = 'src/borg/item.pyx' checksums_source = 'src/borg/algorithms/checksums.pyx' platform_posix_source = 'src/borg/platform/posix.pyx' platform_linux_source = 'src/borg/platform/linux.pyx' platform_syncfilerange_source = 'src/borg/platform/syncfilerange.pyx' platform_darwin_source = 'src/borg/platform/darwin.pyx' platform_freebsd_source = 'src/borg/platform/freebsd.pyx' platform_windows_source = 'src/borg/platform/windows.pyx' cython_sources = [ compress_source, crypto_ll_source, chunker_source, hashindex_source, item_source, checksums_source, platform_posix_source, platform_linux_source, platform_syncfilerange_source, platform_freebsd_source, platform_darwin_source, platform_windows_source, ] if cythonize: Sdist = sdist else: class Sdist(sdist): def __init__(self, *args, **kwargs): raise Exception('Cython is required to run sdist') cython_c_files = [fn.replace('.pyx', '.c') for fn in cython_sources] if not on_rtd and not all(os.path.exists(path) for path in cython_c_files): raise ImportError('The GIT version of Borg needs Cython. Install Cython or use a released version.') def rm(file): try: os.unlink(file) print('rm', file) except FileNotFoundError: pass class Clean(Command): user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): for source in cython_sources: genc = source.replace('.pyx', '.c') rm(genc) compiled_glob = source.replace('.pyx', '.cpython*') for compiled in sorted(glob(compiled_glob)): rm(compiled) cmdclass = { 'build_ext': build_ext, 'build_usage': setup_docs.build_usage, 'build_man': setup_docs.build_man, 'sdist': Sdist, 'clean2': Clean, } ext_modules = [] if not on_rtd: def members_appended(*ds): result = defaultdict(list) for d in ds: for k, v in d.items(): assert isinstance(v, list) result[k].extend(v) return result try: import pkgconfig as pc except ImportError: print('Warning: can not import pkgconfig python package.') pc = None crypto_ext_kwargs = members_appended( dict(sources=[crypto_ll_source, crypto_helpers]), setup_crypto.crypto_ext_kwargs(pc, system_prefix_openssl), dict(extra_compile_args=cflags), ) compress_ext_kwargs = members_appended( dict(sources=[compress_source]), setup_compress.lz4_ext_kwargs(pc, prefer_system_liblz4, system_prefix_liblz4), setup_compress.zstd_ext_kwargs(pc, prefer_system_libzstd, system_prefix_libzstd, multithreaded=False, legacy=False), dict(extra_compile_args=cflags), ) checksums_ext_kwargs = members_appended( dict(sources=[checksums_source]), setup_checksums.xxhash_ext_kwargs(pc, prefer_system_libxxhash, system_prefix_libxxhash), dict(extra_compile_args=cflags), ) ext_modules += [ Extension('borg.crypto.low_level', **crypto_ext_kwargs), Extension('borg.compress', **compress_ext_kwargs), Extension('borg.hashindex', [hashindex_source], extra_compile_args=cflags), Extension('borg.item', [item_source], extra_compile_args=cflags), Extension('borg.chunker', [chunker_source], extra_compile_args=cflags), Extension('borg.algorithms.checksums', **checksums_ext_kwargs), ] posix_ext = Extension('borg.platform.posix', [platform_posix_source], extra_compile_args=cflags) linux_ext = Extension('borg.platform.linux', [platform_linux_source], libraries=['acl'], extra_compile_args=cflags) syncfilerange_ext = Extension('borg.platform.syncfilerange', [platform_syncfilerange_source], extra_compile_args=cflags) freebsd_ext = Extension('borg.platform.freebsd', [platform_freebsd_source], extra_compile_args=cflags) darwin_ext = Extension('borg.platform.darwin', [platform_darwin_source], extra_compile_args=cflags) windows_ext = Extension('borg.platform.windows', [platform_windows_source], extra_compile_args=cflags) if not is_win32: ext_modules.append(posix_ext) else: ext_modules.append(windows_ext) if sys.platform == 'linux': ext_modules.append(linux_ext) ext_modules.append(syncfilerange_ext) elif sys.platform.startswith('freebsd'): ext_modules.append(freebsd_ext) elif sys.platform == 'darwin': ext_modules.append(darwin_ext) # sometimes there's no need to cythonize # this breaks chained commands like 'clean sdist' cythonizing = len(sys.argv) > 1 and sys.argv[1] not in ( ('clean', 'clean2', 'egg_info', '--help-commands', '--version')) and '--help' not in sys.argv[1:] if cythonize and cythonizing: cython_opts = dict( # default language_level will be '3str' starting from Cython 3.0.0, # but old cython versions (< 0.29) do not know that, thus we use 3 for now. compiler_directives={'language_level': 3}, ) if not is_win32: # compile .pyx extensions to .c in parallel, does not work on windows cython_opts['nthreads'] = cpu_threads # generate C code from Cython for ALL supported platforms, so we have them in the sdist. # the sdist does not require Cython at install time, so we need all as C. cythonize([posix_ext, linux_ext, syncfilerange_ext, freebsd_ext, darwin_ext, windows_ext], **cython_opts) # generate C code from Cython for THIS platform (and for all platform-independent Cython parts). ext_modules = cythonize(ext_modules, **cython_opts) # make sure we have the same versioning scheme with all setuptools_scm versions, to avoid different autogenerated files # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1015052 # https://github.com/borgbackup/borg/issues/6875 setup( name='borgbackup', use_scm_version={ 'write_to': 'src/borg/_version.py', 'write_to_template': '__version__ = version = {version!r}\n', }, author='The Borg Collective (see AUTHORS file)', author_email='borgbackup@python.org', url='https://borgbackup.readthedocs.io/', description='Deduplicated, encrypted, authenticated and compressed backups', long_description=setup_docs.long_desc_from_readme(), license='BSD', platforms=['Linux', 'MacOS X', 'FreeBSD', 'OpenBSD', 'NetBSD', ], classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: BSD License', 'Operating System :: POSIX :: BSD :: FreeBSD', 'Operating System :: POSIX :: BSD :: OpenBSD', 'Operating System :: POSIX :: BSD :: NetBSD', 'Operating System :: MacOS :: MacOS X', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Topic :: Security :: Cryptography', 'Topic :: System :: Archiving :: Backup', ], packages=find_namespace_packages('src'), package_dir={'': 'src'}, zip_safe=False, entry_points={ 'console_scripts': [ 'borg = borg.archiver:main', 'borgfs = borg.archiver:main', ] }, # See also the MANIFEST.in file. # We want to install all the files in the package directories... include_package_data=True, # ...except the source files which have been compiled (C extensions): exclude_package_data={ '': ['*.c', '*.h', '*.pyx', ], }, cmdclass=cmdclass, ext_modules=ext_modules, setup_requires=['setuptools_scm>=1.7'], install_requires=install_requires, extras_require=extras_require, python_requires='>=3.8', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/setup_checksums.py0000644000076500000240000000252514601576577016023 0ustar00twstaff# Support code for building a C extension with checksums code import os def multi_join(paths, *path_segments): """apply os.path.join on a list of paths""" return [os.path.join(*(path_segments + (path,))) for path in paths] # xxhash files, structure as seen in xxhash project repository: # path relative (to this file) to the bundled library source code files xxhash_bundled_path = 'src/borg/algorithms/xxh64' xxhash_sources = [ 'xxhash.c', ] xxhash_includes = [ '', ] def xxhash_ext_kwargs(pc, prefer_system, system_prefix): if prefer_system: if system_prefix: print('Detected and preferring libxxhash [via BORG_LIBXXHASH_PREFIX]') return dict(include_dirs=[os.path.join(system_prefix, 'include')], library_dirs=[os.path.join(system_prefix, 'lib')], libraries=['xxhash']) if pc and pc.installed('libxxhash', '>= 0.7.3'): print('Detected and preferring libxxhash [via pkg-config]') return pc.parse('libxxhash') print('Using bundled xxhash') sources = multi_join(xxhash_sources, xxhash_bundled_path) include_dirs = multi_join(xxhash_includes, xxhash_bundled_path) define_macros = [('BORG_USE_BUNDLED_XXHASH', 'YES')] return dict(sources=sources, include_dirs=include_dirs, define_macros=define_macros) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/setup_compress.py0000644000076500000240000001063314601576577015670 0ustar00twstaff# Support code for building a C extension with compression code import os def multi_join(paths, *path_segments): """apply os.path.join on a list of paths""" return [os.path.join(*(path_segments + (path,))) for path in paths] # zstd files, structure as seen in zstd project repository: # path relative (to this file) to the bundled library source code files zstd_bundled_path = 'src/borg/algorithms/zstd' zstd_sources = [ 'lib/common/debug.c', 'lib/common/entropy_common.c', 'lib/common/error_private.c', 'lib/common/fse_decompress.c', 'lib/common/pool.c', 'lib/common/threading.c', 'lib/common/xxhash.c', 'lib/common/zstd_common.c', 'lib/compress/fse_compress.c', 'lib/compress/hist.c', 'lib/compress/huf_compress.c', 'lib/compress/zstd_compress.c', 'lib/compress/zstd_compress_literals.c', 'lib/compress/zstd_compress_sequences.c', 'lib/compress/zstd_compress_superblock.c', 'lib/compress/zstd_double_fast.c', 'lib/compress/zstd_fast.c', 'lib/compress/zstd_lazy.c', 'lib/compress/zstd_ldm.c', 'lib/compress/zstd_opt.c', 'lib/compress/zstdmt_compress.c', 'lib/decompress/huf_decompress.c', 'lib/decompress/zstd_ddict.c', 'lib/decompress/zstd_decompress.c', 'lib/decompress/zstd_decompress_block.c', 'lib/dictBuilder/cover.c', 'lib/dictBuilder/divsufsort.c', 'lib/dictBuilder/fastcover.c', 'lib/dictBuilder/zdict.c', ] zstd_sources_legacy = [ 'lib/deprecated/zbuff_common.c', 'lib/deprecated/zbuff_compress.c', 'lib/deprecated/zbuff_decompress.c', 'lib/legacy/zstd_v01.c', 'lib/legacy/zstd_v02.c', 'lib/legacy/zstd_v03.c', 'lib/legacy/zstd_v04.c', 'lib/legacy/zstd_v05.c', 'lib/legacy/zstd_v06.c', 'lib/legacy/zstd_v07.c', ] zstd_includes = [ 'lib', 'lib/common', 'lib/compress', 'lib/decompress', 'lib/dictBuilder', ] zstd_includes_legacy = [ 'lib/deprecated', 'lib/legacy', ] def zstd_ext_kwargs(pc, prefer_system, system_prefix, multithreaded=False, legacy=False): if prefer_system: if system_prefix: print('Detected and preferring libzstd [via BORG_LIBZSTD_PREFIX]') return dict(include_dirs=[os.path.join(system_prefix, 'include')], library_dirs=[os.path.join(system_prefix, 'lib')], libraries=['zstd']) if pc and pc.installed('libzstd', '>= 1.3.0'): print('Detected and preferring libzstd [via pkg-config]') return pc.parse('libzstd') print('Using bundled ZSTD') sources = multi_join(zstd_sources, zstd_bundled_path) if legacy: sources += multi_join(zstd_sources_legacy, zstd_bundled_path) include_dirs = multi_join(zstd_includes, zstd_bundled_path) if legacy: include_dirs += multi_join(zstd_includes_legacy, zstd_bundled_path) extra_compile_args = ['-DZSTDLIB_VISIBILITY=', '-DZDICTLIB_VISIBILITY=', '-DZSTDERRORLIB_VISIBILITY=', ] # '-fvisibility=hidden' does not work, doesn't find PyInit_compress then if legacy: extra_compile_args += ['-DZSTD_LEGACY_SUPPORT=1', ] if multithreaded: extra_compile_args += ['-DZSTD_MULTITHREAD', ] define_macros = [('BORG_USE_BUNDLED_ZSTD', 'YES')] return dict(sources=sources, include_dirs=include_dirs, extra_compile_args=extra_compile_args, define_macros=define_macros) # lz4 files, structure as seen in lz4 project repository: # path relative (to this file) to the bundled library source code files lz4_bundled_path = 'src/borg/algorithms/lz4' lz4_sources = [ 'lib/lz4.c', ] lz4_includes = [ 'lib', ] def lz4_ext_kwargs(pc, prefer_system, system_prefix): if prefer_system: if system_prefix: print('Detected and preferring liblz4 [via BORG_LIBLZ4_PREFIX]') return dict(include_dirs=[os.path.join(system_prefix, 'include')], library_dirs=[os.path.join(system_prefix, 'lib')], libraries=['lz4']) if pc and pc.installed('liblz4', '>= 1.7.0'): print('Detected and preferring liblz4 [via pkg-config]') return pc.parse('liblz4') print('Using bundled LZ4') sources = multi_join(lz4_sources, lz4_bundled_path) include_dirs = multi_join(lz4_includes, lz4_bundled_path) define_macros = [('BORG_USE_BUNDLED_LZ4', 'YES')] return dict(sources=sources, include_dirs=include_dirs, define_macros=define_macros) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/setup_crypto.py0000644000076500000240000000173414601576577015357 0ustar00twstaff# Support code for building a C extension with crypto code import os import sys is_win32 = sys.platform.startswith('win32') def multi_join(paths, *path_segments): """apply os.path.join on a list of paths""" return [os.path.join(*(path_segments + (path,))) for path in paths] def crypto_ext_kwargs(pc, system_prefix): if system_prefix: print('Detected OpenSSL [via BORG_OPENSSL_PREFIX]') if is_win32: lib_dir = system_prefix lib_name = 'libcrypto' else: lib_dir = os.path.join(system_prefix, 'lib') lib_name = 'crypto' return dict(include_dirs=[os.path.join(system_prefix, 'include')], library_dirs=[lib_dir], libraries=[lib_name]) if pc and pc.exists('libcrypto'): print('Detected OpenSSL [via pkg-config]') return pc.parse('libcrypto') raise Exception('Could not find OpenSSL lib/headers, please set BORG_OPENSSL_PREFIX') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/setup_docs.py0000644000076500000240000005275114601576577014774 0ustar00twstaff# Support code for building docs (build_usage, build_man) import os import io import re import sys import textwrap from collections import OrderedDict from datetime import datetime, timezone import time from setuptools import Command def long_desc_from_readme(): with open('README.rst') as fd: long_description = fd.read() # remove header, but have one \n before first headline start = long_description.find('What is BorgBackup?') assert start >= 0 long_description = '\n' + long_description[start:] # remove badges long_description = re.compile(r'^\.\. start-badges.*^\.\. end-badges', re.M | re.S).sub('', long_description) # remove unknown directives long_description = re.compile(r'^\.\. highlight:: \w+$', re.M).sub('', long_description) return long_description def format_metavar(option): if option.nargs in ('*', '...'): return '[%s...]' % option.metavar elif option.nargs == '?': return '[%s]' % option.metavar elif option.nargs is None: return option.metavar else: raise ValueError(f'Can\'t format metavar {option.metavar}, unknown nargs {option.nargs}!') class build_usage(Command): description = "generate usage for each command" user_options = [ ('output=', 'O', 'output directory'), ] def initialize_options(self): pass def finalize_options(self): pass def run(self): print('generating usage docs') import borg borg.doc_mode = 'build_man' if not os.path.exists('docs/usage'): os.mkdir('docs/usage') # allows us to build docs without the C modules fully loaded during help generation from borg.archiver import Archiver parser = Archiver(prog='borg').build_parser() # borgfs has a separate man page to satisfy debian's "every program from a package # must have a man page" requirement, but it doesn't need a separate HTML docs page #borgfs_parser = Archiver(prog='borgfs').build_parser() self.generate_level("", parser, Archiver) def generate_level(self, prefix, parser, Archiver, extra_choices=None): is_subcommand = False choices = {} for action in parser._actions: if action.choices is not None and 'SubParsersAction' in str(action.__class__): is_subcommand = True for cmd, parser in action.choices.items(): choices[prefix + cmd] = parser if extra_choices is not None: choices.update(extra_choices) if prefix and not choices: return print('found commands: %s' % list(choices.keys())) for command, parser in sorted(choices.items()): if command.startswith('debug'): print('skipping', command) continue print('generating help for %s' % command) if self.generate_level(command + " ", parser, Archiver): continue with open('docs/usage/%s.rst.inc' % command.replace(" ", "_"), 'w') as doc: doc.write(".. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit!\n\n") if command == 'help': for topic in Archiver.helptext: params = {"topic": topic, "underline": '~' * len('borg help ' + topic)} doc.write(".. _borg_{topic}:\n\n".format(**params)) doc.write("borg help {topic}\n{underline}\n\n".format(**params)) doc.write(Archiver.helptext[topic]) else: params = {"command": command, "command_": command.replace(' ', '_'), "underline": '-' * len('borg ' + command)} doc.write(".. _borg_{command_}:\n\n".format(**params)) doc.write("borg {command}\n{underline}\n.. code-block:: none\n\n borg [common options] {command}".format(**params)) self.write_usage(parser, doc) epilog = parser.epilog parser.epilog = None self.write_options(parser, doc) doc.write("\n\nDescription\n~~~~~~~~~~~\n") doc.write(epilog) if 'create' in choices: common_options = [group for group in choices['create']._action_groups if group.title == 'Common options'][0] with open('docs/usage/common-options.rst.inc', 'w') as doc: self.write_options_group(common_options, doc, False, base_indent=0) return is_subcommand def write_usage(self, parser, fp): if any(len(o.option_strings) for o in parser._actions): fp.write(' [options]') for option in parser._actions: if option.option_strings: continue fp.write(' ' + format_metavar(option)) fp.write('\n\n') def write_options(self, parser, fp): def is_positional_group(group): return any(not o.option_strings for o in group._group_actions) # HTML output: # A table using some column-spans def html_write(s): for line in s.splitlines(): fp.write(' ' + line + '\n') rows = [] for group in parser._action_groups: if group.title == 'Common options': # (no of columns used, columns, ...) rows.append((1, '.. class:: borg-common-opt-ref\n\n:ref:`common_options`')) else: if not group._group_actions: continue group_header = '**%s**' % group.title if group.description: group_header += ' — ' + group.description rows.append((1, group_header)) if is_positional_group(group): for option in group._group_actions: rows.append((3, '', '``%s``' % option.metavar, option.help or '')) else: for option in group._group_actions: if option.metavar: option_fmt = '``%s ' + option.metavar + '``' else: option_fmt = '``%s``' option_str = ', '.join(option_fmt % s for s in option.option_strings) option_desc = textwrap.dedent((option.help or '') % option.__dict__) rows.append((3, '', option_str, option_desc)) fp.write('.. only:: html\n\n') table = io.StringIO() table.write('.. class:: borg-options-table\n\n') self.rows_to_table(rows, table.write) fp.write(textwrap.indent(table.getvalue(), ' ' * 4)) # LaTeX output: # Regular rST option lists (irregular column widths) latex_options = io.StringIO() for group in parser._action_groups: if group.title == 'Common options': latex_options.write('\n\n:ref:`common_options`\n') latex_options.write(' |') else: self.write_options_group(group, latex_options) fp.write('\n.. only:: latex\n\n') fp.write(textwrap.indent(latex_options.getvalue(), ' ' * 4)) def rows_to_table(self, rows, write): def write_row_separator(): write('+') for column_width in column_widths: write('-' * (column_width + 1)) write('+') write('\n') # Find column count and width column_count = max(columns for columns, *_ in rows) column_widths = [0] * column_count for columns, *cells in rows: for i in range(columns): # "+ 1" because we want a space between the cell contents and the delimiting "|" in the output column_widths[i] = max(column_widths[i], len(cells[i]) + 1) for columns, *original_cells in rows: write_row_separator() # If a cell contains newlines, then the row must be split up in individual rows # where each cell contains no newline. rowspanning_cells = [] original_cells = list(original_cells) while any('\n' in cell for cell in original_cells): cell_bloc = [] for i, cell in enumerate(original_cells): pre, _, original_cells[i] = cell.partition('\n') cell_bloc.append(pre) rowspanning_cells.append(cell_bloc) rowspanning_cells.append(original_cells) for cells in rowspanning_cells: for i, column_width in enumerate(column_widths): if i < columns: write('| ') write(cells[i].ljust(column_width)) else: write(' ') write(''.ljust(column_width)) write('|\n') write_row_separator() # This bit of JavaScript kills the that is invariably inserted by docutils, # but does absolutely no good here. It sets bogus column widths which cannot be overridden # with CSS alone. # Since this is HTML-only output, it would be possible to just generate a directly, # but then we'd lose rST formatting. write(textwrap.dedent(""" .. raw:: html """)) def write_options_group(self, group, fp, with_title=True, base_indent=4): def is_positional_group(group): return any(not o.option_strings for o in group._group_actions) indent = ' ' * base_indent if is_positional_group(group): for option in group._group_actions: fp.write(option.metavar + '\n') fp.write(textwrap.indent(option.help or '', ' ' * base_indent) + '\n') return if not group._group_actions: return if with_title: fp.write('\n\n') fp.write(group.title + '\n') opts = OrderedDict() for option in group._group_actions: if option.metavar: option_fmt = '%s ' + option.metavar else: option_fmt = '%s' option_str = ', '.join(option_fmt % s for s in option.option_strings) option_desc = textwrap.dedent((option.help or '') % option.__dict__) opts[option_str] = textwrap.indent(option_desc, ' ' * 4) padding = len(max(opts)) + 1 for option, desc in opts.items(): fp.write(indent + option.ljust(padding) + desc + '\n') class build_man(Command): description = 'build man pages' user_options = [] see_also = { 'create': ('delete', 'prune', 'check', 'patterns', 'placeholders', 'compression'), 'recreate': ('patterns', 'placeholders', 'compression'), 'list': ('info', 'diff', 'prune', 'patterns'), 'info': ('list', 'diff'), 'init': ('create', 'delete', 'check', 'list', 'key-import', 'key-export', 'key-change-passphrase'), 'key-import': ('key-export', ), 'key-export': ('key-import', ), 'mount': ('umount', 'extract'), # Would be cooler if these two were on the same page 'umount': ('mount', ), 'extract': ('mount', ), 'delete': ('compact', ), 'prune': ('compact', ), } rst_prelude = textwrap.dedent(""" .. role:: ref(title) .. |project_name| replace:: Borg """) usage_group = { 'break-lock': 'lock', 'with-lock': 'lock', 'key_change-passphrase': 'key', 'key_export': 'key', 'key_import': 'key', 'key_migrate-to-repokey': 'key', 'export-tar': 'tar', 'import-tar': 'tar', 'benchmark_crud': 'benchmark', 'umount': 'mount', } def initialize_options(self): pass def finalize_options(self): pass def run(self): print('building man pages (in docs/man)', file=sys.stderr) import borg borg.doc_mode = 'build_man' os.makedirs('docs/man', exist_ok=True) # allows us to build docs without the C modules fully loaded during help generation from borg.archiver import Archiver parser = Archiver(prog='borg').build_parser() borgfs_parser = Archiver(prog='borgfs').build_parser() self.generate_level('', parser, Archiver, {'borgfs': borgfs_parser}) self.build_topic_pages(Archiver) self.build_intro_page() def generate_level(self, prefix, parser, Archiver, extra_choices=None): is_subcommand = False choices = {} for action in parser._actions: if action.choices is not None and 'SubParsersAction' in str(action.__class__): is_subcommand = True for cmd, parser in action.choices.items(): choices[prefix + cmd] = parser if extra_choices is not None: choices.update(extra_choices) if prefix and not choices: return for command, parser in sorted(choices.items()): if command.startswith('debug') or command == 'help': continue if command == "borgfs": man_title = command else: man_title = 'borg-' + command.replace(' ', '-') print('building man page', man_title + '(1)', file=sys.stderr) is_intermediary = self.generate_level(command + ' ', parser, Archiver) doc, write = self.new_doc() self.write_man_header(write, man_title, parser.description) self.write_heading(write, 'SYNOPSIS') if is_intermediary: subparsers = [action for action in parser._actions if 'SubParsersAction' in str(action.__class__)][0] for subcommand in subparsers.choices: write('| borg', '[common options]', command, subcommand, '...') self.see_also.setdefault(command, []).append(f'{command}-{subcommand}') else: if command == "borgfs": write(command, end='') else: write('borg', '[common options]', command, end='') self.write_usage(write, parser) write('\n') description, _, notes = parser.epilog.partition('\n.. man NOTES') if description: self.write_heading(write, 'DESCRIPTION') write(description) if not is_intermediary: self.write_heading(write, 'OPTIONS') write('See `borg-common(1)` for common options of Borg commands.') write() self.write_options(write, parser) self.write_examples(write, command) if notes: self.write_heading(write, 'NOTES') write(notes) self.write_see_also(write, man_title) self.gen_man_page(man_title, doc.getvalue()) # Generate the borg-common(1) man page with the common options. if 'create' in choices: doc, write = self.new_doc() man_title = 'borg-common' self.write_man_header(write, man_title, 'Common options of Borg commands') common_options = [group for group in choices['create']._action_groups if group.title == 'Common options'][0] self.write_heading(write, 'SYNOPSIS') self.write_options_group(write, common_options) self.write_see_also(write, man_title) self.gen_man_page(man_title, doc.getvalue()) return is_subcommand def build_topic_pages(self, Archiver): for topic, text in Archiver.helptext.items(): doc, write = self.new_doc() man_title = 'borg-' + topic print('building man page', man_title + '(1)', file=sys.stderr) self.write_man_header(write, man_title, 'Details regarding ' + topic) self.write_heading(write, 'DESCRIPTION') write(text) self.gen_man_page(man_title, doc.getvalue()) def build_intro_page(self): doc, write = self.new_doc() man_title = 'borg' print('building man page borg(1)', file=sys.stderr) with open('docs/man_intro.rst') as fd: man_intro = fd.read() self.write_man_header(write, man_title, "deduplicating and encrypting backup tool") self.gen_man_page(man_title, doc.getvalue() + man_intro) def new_doc(self): doc = io.StringIO(self.rst_prelude) doc.read() write = self.printer(doc) return doc, write def printer(self, fd): def write(*args, **kwargs): print(*args, file=fd, **kwargs) return write def write_heading(self, write, header, char='-', double_sided=False): write() if double_sided: write(char * len(header)) write(header) write(char * len(header)) write() def write_man_header(self, write, title, description): self.write_heading(write, title, '=', double_sided=True) self.write_heading(write, description, double_sided=True) # man page metadata write(':Author: The Borg Collective') source_date_epoch = int(os.environ.get("SOURCE_DATE_EPOCH", time.time())) write(':Date:', datetime.fromtimestamp(source_date_epoch, timezone.utc).date().isoformat()) write(':Manual section: 1') write(':Manual group: borg backup tool') write() def write_examples(self, write, command): command = command.replace(' ', '_') with open('docs/usage/%s.rst' % self.usage_group.get(command, command)) as fd: usage = fd.read() usage_include = '.. include:: %s.rst.inc' % command begin = usage.find(usage_include) end = usage.find('.. include', begin + 1) # If a command has a dedicated anchor, it will occur before the command's include. if 0 < usage.find('.. _', begin + 1) < end: end = usage.find('.. _', begin + 1) examples = usage[begin:end] examples = examples.replace(usage_include, '') examples = examples.replace('Examples\n~~~~~~~~', '') examples = examples.replace('Miscellaneous Help\n------------------', '') examples = examples.replace('``docs/misc/prune-example.txt``:', '``docs/misc/prune-example.txt``.') examples = examples.replace('.. highlight:: none\n', '') # we don't support highlight examples = re.sub('^(~+)$', lambda matches: '+' * len(matches.group(0)), examples, flags=re.MULTILINE) examples = examples.strip() if examples: self.write_heading(write, 'EXAMPLES', '-') write(examples) def write_see_also(self, write, man_title): see_also = self.see_also.get(man_title.replace('borg-', ''), ()) see_also = ['`borg-%s(1)`' % s for s in see_also] see_also.insert(0, '`borg-common(1)`') self.write_heading(write, 'SEE ALSO') write(', '.join(see_also)) def gen_man_page(self, name, rst): from docutils.writers import manpage from docutils.core import publish_string from docutils.nodes import inline from docutils.parsers.rst import roles def issue(name, rawtext, text, lineno, inliner, options={}, content=[]): return [inline(rawtext, '#' + text)], [] roles.register_local_role('issue', issue) # We give the source_path so that docutils can find relative includes # as-if the document where located in the docs/ directory. man_page = publish_string(source=rst, source_path='docs/%s.rst' % name, writer=manpage.Writer()) with open('docs/man/%s.1' % name, 'wb') as fd: fd.write(man_page) def write_usage(self, write, parser): if any(len(o.option_strings) for o in parser._actions): write(' [options] ', end='') for option in parser._actions: if option.option_strings: continue write(format_metavar(option), end=' ') def write_options(self, write, parser): for group in parser._action_groups: if group.title == 'Common options' or not group._group_actions: continue title = 'arguments' if group.title == 'positional arguments' else group.title self.write_heading(write, title, '+') self.write_options_group(write, group) def write_options_group(self, write, group): def is_positional_group(group): return any(not o.option_strings for o in group._group_actions) if is_positional_group(group): for option in group._group_actions: write(option.metavar) write(textwrap.indent(option.help or '', ' ' * 4)) return opts = OrderedDict() for option in group._group_actions: if option.metavar: option_fmt = '%s ' + option.metavar else: option_fmt = '%s' option_str = ', '.join(option_fmt % s for s in option.option_strings) option_desc = textwrap.dedent((option.help or '') % option.__dict__) opts[option_str] = textwrap.indent(option_desc, ' ' * 4) padding = len(max(opts)) + 1 for option, desc in opts.items(): write(option.ljust(padding), desc) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1711734324.093683 borgbackup-1.2.8/src/0000755000076500000240000000000014601577064013017 5ustar00twstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1658058 borgbackup-1.2.8/src/borg/0000755000076500000240000000000014601577064013750 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/__init__.py0000644000076500000240000000153314601576577016073 0ustar00twstafffrom packaging.version import parse as parse_version # IMPORTANT keep imports from borg here to a minimum because our testsuite depends on # being able to import borg.constants and then monkey patching borg.constants.PBKDF2_ITERATIONS from ._version import version as __version__ _v = parse_version(__version__) __version_tuple__ = _v._version.release # assert that all semver components are integers # this is mainly to show errors when people repackage poorly # and setuptools_scm determines a 0.1.dev... version assert all(isinstance(v, int) for v in __version_tuple__), \ """\ broken borgbackup version metadata: %r version metadata is obtained dynamically on installation via setuptools_scm, please ensure your git repo has the correct tags or you provide the version using SETUPTOOLS_SCM_PRETEND_VERSION in your build script. """ % __version__ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/__main__.py0000644000076500000240000000114314601576577016051 0ustar00twstaffimport sys import os # On windows loading the bundled libcrypto dll fails if the folder # containing the dll is not in the search path. The dll is shipped # with python in the "DLLs" folder, so let's add this folder # to the path. The folder is always in sys.path, get it from there. if sys.platform.startswith('win32'): # Keep it an iterable to support multiple folder which contain "DLLs". dll_path = (p for p in sys.path if 'DLLs' in os.path.normpath(p).split(os.path.sep)) os.environ['PATH'] = os.pathsep.join(dll_path) + os.pathsep + os.environ['PATH'] from borg.archiver import main main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/_chunker.c0000644000076500000240000002750214601576577015730 0ustar00twstaff#include #include #if !defined(_MSC_VER) # include #endif /* Cyclic polynomial / buzhash https://en.wikipedia.org/wiki/Rolling_hash http://www.serve.net/buz/Notes.1st.year/HTML/C6/rand.012.html (by "BUZ", the inventor) http://www.dcs.gla.ac.uk/~hamer/cakes-talk.pdf (see buzhash slide) Some properties of buzhash / of this implementation: (1) the hash is designed for inputs <= 32 bytes, but the chunker uses it on a 4095 byte window; any repeating bytes at distance 32 within those 4095 bytes can cause cancellation within the hash function, e.g. in "X X", the last X would cancel out the influence of the first X on the hash value. (2) the hash table is supposed to have (according to the BUZ) exactly a 50% distribution of 0/1 bit values per position, but the hard coded table below doesn't fit that property. (3) if you would use a window size divisible by 64, the seed would cancel itself out completely. this is why we use a window size of 4095 bytes. Another quirk is that, even with the 4095 byte window, XORing the entire table by a constant is equivalent to XORing the hash output with a different constant. but since the seed is stored encrypted, i think it still serves its purpose. */ static uint32_t table_base[] = { 0xe7f831ec, 0xf4026465, 0xafb50cae, 0x6d553c7a, 0xd639efe3, 0x19a7b895, 0x9aba5b21, 0x5417d6d4, 0x35fd2b84, 0xd1f6a159, 0x3f8e323f, 0xb419551c, 0xf444cebf, 0x21dc3b80, 0xde8d1e36, 0x84a32436, 0xbeb35a9d, 0xa36f24aa, 0xa4e60186, 0x98d18ffe, 0x3f042f9e, 0xdb228bcd, 0x096474b7, 0x5c20c2f7, 0xf9eec872, 0xe8625275, 0xb9d38f80, 0xd48eb716, 0x22a950b4, 0x3cbaaeaa, 0xc37cddd3, 0x8fea6f6a, 0x1d55d526, 0x7fd6d3b3, 0xdaa072ee, 0x4345ac40, 0xa077c642, 0x8f2bd45b, 0x28509110, 0x55557613, 0xffc17311, 0xd961ffef, 0xe532c287, 0xaab95937, 0x46d38365, 0xb065c703, 0xf2d91d0f, 0x92cd4bb0, 0x4007c712, 0xf35509dd, 0x505b2f69, 0x557ead81, 0x310f4563, 0xbddc5be8, 0x9760f38c, 0x701e0205, 0x00157244, 0x14912826, 0xdc4ca32b, 0x67b196de, 0x5db292e8, 0x8c1b406b, 0x01f34075, 0xfa2520f7, 0x73bc37ab, 0x1e18bc30, 0xfe2c6cb3, 0x20c522d0, 0x5639e3db, 0x942bda35, 0x899af9d1, 0xced44035, 0x98cc025b, 0x255f5771, 0x70fefa24, 0xe928fa4d, 0x2c030405, 0xb9325590, 0x20cb63bd, 0xa166305d, 0x80e52c0a, 0xa8fafe2f, 0x1ad13f7d, 0xcfaf3685, 0x6c83a199, 0x7d26718a, 0xde5dfcd9, 0x79cf7355, 0x8979d7fb, 0xebf8c55e, 0xebe408e4, 0xcd2affba, 0xe483be6e, 0xe239d6de, 0x5dc1e9e0, 0x0473931f, 0x851b097c, 0xac5db249, 0x09c0f9f2, 0xd8d2f134, 0xe6f38e41, 0xb1c71bf1, 0x52b6e4db, 0x07224424, 0x6cf73e85, 0x4f25d89c, 0x782a7d74, 0x10a68dcd, 0x3a868189, 0xd570d2dc, 0x69630745, 0x9542ed86, 0x331cd6b2, 0xa84b5b28, 0x07879c9d, 0x38372f64, 0x7185db11, 0x25ba7c83, 0x01061523, 0xe6792f9f, 0xe5df07d1, 0x4321b47f, 0x7d2469d8, 0x1a3a4f90, 0x48be29a3, 0x669071af, 0x8ec8dd31, 0x0810bfbf, 0x813a06b4, 0x68538345, 0x65865ddc, 0x43a71b8e, 0x78619a56, 0x5a34451d, 0x5bdaa3ed, 0x71edc7e9, 0x17ac9a20, 0x78d10bfa, 0x6c1e7f35, 0xd51839d9, 0x240cbc51, 0x33513cc1, 0xd2b4f795, 0xccaa8186, 0x0babe682, 0xa33cf164, 0x18c643ea, 0xc1ca105f, 0x9959147a, 0x6d3d94de, 0x0b654fbe, 0xed902ca0, 0x7d835cb5, 0x99ba1509, 0x6445c922, 0x495e76c2, 0xf07194bc, 0xa1631d7e, 0x677076a5, 0x89fffe35, 0x1a49bcf3, 0x8e6c948a, 0x0144c917, 0x8d93aea1, 0x16f87ddf, 0xc8f25d49, 0x1fb11297, 0x27e750cd, 0x2f422da1, 0xdee89a77, 0x1534c643, 0x457b7b8b, 0xaf172f7a, 0x6b9b09d6, 0x33573f7f, 0xf14e15c4, 0x526467d5, 0xaf488241, 0x87c3ee0d, 0x33be490c, 0x95aa6e52, 0x43ec242e, 0xd77de99b, 0xd018334f, 0x5b78d407, 0x498eb66b, 0xb1279fa8, 0xb38b0ea6, 0x90718376, 0xe325dee2, 0x8e2f2cba, 0xcaa5bdec, 0x9d652c56, 0xad68f5cb, 0xa77591af, 0x88e37ee8, 0xf8faa221, 0xfcbbbe47, 0x4f407786, 0xaf393889, 0xf444a1d9, 0x15ae1a2f, 0x40aa7097, 0x6f9486ac, 0x29d232a3, 0xe47609e9, 0xe8b631ff, 0xba8565f4, 0x11288749, 0x46c9a838, 0xeb1b7cd8, 0xf516bbb1, 0xfb74fda0, 0x010996e6, 0x4c994653, 0x1d889512, 0x53dcd9a3, 0xdd074697, 0x1e78e17c, 0x637c98bf, 0x930bb219, 0xcf7f75b0, 0xcb9355fb, 0x9e623009, 0xe466d82c, 0x28f968d3, 0xfeb385d9, 0x238e026c, 0xb8ed0560, 0x0c6a027a, 0x3d6fec4b, 0xbb4b2ec2, 0xe715031c, 0xeded011d, 0xcdc4d3b9, 0xc456fc96, 0xdd0eea20, 0xb3df8ec9, 0x12351993, 0xd9cbb01c, 0x603147a2, 0xcf37d17d, 0xf7fcd9dc, 0xd8556fa3, 0x104c8131, 0x13152774, 0xb4715811, 0x6a72c2c9, 0xc5ae37bb, 0xa76ce12a, 0x8150d8f3, 0x2ec29218, 0xa35f0984, 0x48c0647e, 0x0b5ff98c, 0x71893f7b }; #define BARREL_SHIFT(v, shift) ( ((v) << (shift)) | ((v) >> ((32 - (shift)) & 0x1f)) ) size_t pagemask; static uint32_t * buzhash_init_table(uint32_t seed) { int i; uint32_t *table = malloc(1024); for(i = 0; i < 256; i++) { table[i] = table_base[i] ^ seed; } return table; } static uint32_t buzhash(const unsigned char *data, size_t len, const uint32_t *h) { uint32_t i; uint32_t sum = 0, imod; for(i = len - 1; i > 0; i--) { imod = i & 0x1f; sum ^= BARREL_SHIFT(h[*data], imod); data++; } return sum ^ h[*data]; } static uint32_t buzhash_update(uint32_t sum, unsigned char remove, unsigned char add, size_t len, const uint32_t *h) { uint32_t lenmod = len & 0x1f; /* Note: replace by constant to get small speedup */ return BARREL_SHIFT(sum, 1) ^ BARREL_SHIFT(h[remove], lenmod) ^ h[add]; } typedef struct { uint32_t chunk_mask; uint32_t *table; uint8_t *data; PyObject *fd; int fh; int done, eof; size_t min_size, buf_size, window_size, remaining, position, last; off_t bytes_read, bytes_yielded; } Chunker; static Chunker * chunker_init(size_t window_size, uint32_t chunk_mask, size_t min_size, size_t max_size, uint32_t seed) { Chunker *c = calloc(sizeof(Chunker), 1); c->window_size = window_size; c->chunk_mask = chunk_mask; c->min_size = min_size; c->table = buzhash_init_table(seed); c->buf_size = max_size; c->data = malloc(c->buf_size); c->fh = -1; return c; } static void chunker_set_fd(Chunker *c, PyObject *fd, int fh) { Py_XDECREF(c->fd); c->fd = fd; Py_INCREF(fd); c->fh = fh; c->done = 0; c->remaining = 0; c->bytes_read = 0; c->bytes_yielded = 0; c->position = 0; c->last = 0; c->eof = 0; } static void chunker_free(Chunker *c) { Py_XDECREF(c->fd); free(c->table); free(c->data); free(c); } static int chunker_fill(Chunker *c) { ssize_t n; PyObject *data; PyThreadState *thread_state; memmove(c->data, c->data + c->last, c->position + c->remaining - c->last); c->position -= c->last; c->last = 0; n = c->buf_size - c->position - c->remaining; if(c->eof || n == 0) { return 1; } if(c->fh >= 0) { thread_state = PyEval_SaveThread(); #if ( ( _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L ) && defined(POSIX_FADV_DONTNEED) ) off_t offset = c->bytes_read; #endif // if we have a os-level file descriptor, use os-level API n = read(c->fh, c->data + c->position + c->remaining, n); if(n > 0) { c->remaining += n; c->bytes_read += n; } else if(n == 0) { c->eof = 1; } else { PyEval_RestoreThread(thread_state); // some error happened PyErr_SetFromErrno(PyExc_OSError); return 0; } #if ( ( _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L ) && defined(POSIX_FADV_DONTNEED) ) off_t length = c->bytes_read - offset; // Only do it once per run. if (pagemask == 0) pagemask = getpagesize() - 1; // We tell the OS that we do not need the data that we just have read any // more (that it maybe has in the cache). This avoids that we spoil the // complete cache with data that we only read once and (due to cache // size limit) kick out data from the cache that might be still useful // for the OS or other processes. // We rollback the initial offset back to the start of the page, // to avoid it not being truncated as a partial page request. int overshoot; if (length > 0) { // All Linux kernels (at least up to and including 4.6(.0)) have a bug where // they truncate last partial page of POSIX_FADV_DONTNEED request, so we need // to page-align it ourselves. We'll need the rest of this page on the next // read (assuming this was not EOF). overshoot = (offset + length) & pagemask; } else { // For length == 0 we set overshoot 0, so the below // length - overshoot is 0, which means till end of file for // fadvise. This will cancel the final page and is not part // of the above workaround. overshoot = 0; } posix_fadvise(c->fh, offset & ~pagemask, length - overshoot, POSIX_FADV_DONTNEED); #endif PyEval_RestoreThread(thread_state); } else { // no os-level file descriptor, use Python file object API data = PyObject_CallMethod(c->fd, "read", "i", n); if(!data) { return 0; } n = PyBytes_Size(data); if(PyErr_Occurred()) { // we wanted bytes(), but got something else return 0; } if(n) { memcpy(c->data + c->position + c->remaining, PyBytes_AsString(data), n); c->remaining += n; c->bytes_read += n; } else { c->eof = 1; } Py_DECREF(data); } return 1; } static PyObject * chunker_process(Chunker *c) { uint32_t sum, chunk_mask = c->chunk_mask; size_t n, old_last, min_size = c->min_size, window_size = c->window_size; if(c->done) { if(c->bytes_read == c->bytes_yielded) PyErr_SetNone(PyExc_StopIteration); else PyErr_SetString(PyExc_Exception, "chunkifier byte count mismatch"); return NULL; } while(c->remaining < min_size + window_size + 1 && !c->eof) { /* see assert in Chunker init */ if(!chunker_fill(c)) { return NULL; } } /* here we either are at eof ... */ if(c->eof) { c->done = 1; if(c->remaining) { c->bytes_yielded += c->remaining; return PyMemoryView_FromMemory((char *)(c->data + c->position), c->remaining, PyBUF_READ); } else { if(c->bytes_read == c->bytes_yielded) PyErr_SetNone(PyExc_StopIteration); else PyErr_SetString(PyExc_Exception, "chunkifier byte count mismatch"); return NULL; } } /* ... or we have at least min_size + window_size + 1 bytes remaining. * We do not want to "cut" a chunk smaller than min_size and the hash * window starts at the potential cutting place. */ c->position += min_size; c->remaining -= min_size; sum = buzhash(c->data + c->position, window_size, c->table); while(c->remaining > c->window_size && (sum & chunk_mask)) { uint8_t *p = c->data + c->position; uint8_t *stop_at = p + c->remaining - window_size; size_t did_bytes; while (p < stop_at && (sum & chunk_mask)) { sum = buzhash_update(sum, p[0], p[window_size], window_size, c->table); p++; } did_bytes = p - (c->data + c->position); c->position += did_bytes; c->remaining -= did_bytes; if(c->remaining <= window_size) { if(!chunker_fill(c)) { return NULL; } } } if(c->remaining <= window_size) { c->position += c->remaining; c->remaining = 0; } old_last = c->last; c->last = c->position; n = c->last - old_last; c->bytes_yielded += n; return PyMemoryView_FromMemory((char *)(c->data + old_last), n, PyBUF_READ); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/_endian.h0000644000076500000240000000201714601576577015526 0ustar00twstaff#if !defined(_MSC_VER) # include #endif #include #include #if defined (__SVR4) && defined (__sun) #include #endif #if (defined(BYTE_ORDER) && defined(BIG_ENDIAN) && (BYTE_ORDER == BIG_ENDIAN)) || \ (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) || \ (defined(_BIG_ENDIAN) && defined(__SVR4) && defined(__sun)) #define BORG_BIG_ENDIAN 1 #elif (defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && (BYTE_ORDER == LITTLE_ENDIAN)) || \ (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \ (defined(_LITTLE_ENDIAN) && defined(__SVR4) && defined(__sun)) || \ (defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_IX86))) #define BORG_BIG_ENDIAN 0 #else #error Unknown byte order #endif #if BORG_BIG_ENDIAN #define _le32toh(x) __builtin_bswap32(x) #define _htole32(x) __builtin_bswap32(x) #else #define _le32toh(x) (x) #define _htole32(x) (x) #endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/_hashindex.c0000644000076500000240000005702314601576577016245 0ustar00twstaff #include #include #include #include #include #include #include #include #include #if !defined(_MSC_VER) # include #endif #include "_endian.h" #if defined(_MSC_VER) # define BORG_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) #else # define BORG_PACKED(x) x __attribute__((packed)) #endif #define MAGIC "BORG_IDX" #define MAGIC_LEN 8 #define DEBUG 0 #define debug_print(fmt, ...) \ do { \ if (DEBUG) { \ fprintf(stderr, fmt, __VA_ARGS__); \ fflush(NULL); \ } \ } while (0) BORG_PACKED( typedef struct { char magic[MAGIC_LEN]; int32_t num_entries; int32_t num_buckets; int8_t key_size; int8_t value_size; }) HashHeader; typedef struct { unsigned char *buckets; int num_entries; int num_buckets; int num_empty; int key_size; int value_size; off_t bucket_size; int lower_limit; int upper_limit; int min_empty; #ifndef BORG_NO_PYTHON /* buckets may be backed by a Python buffer. If buckets_buffer.buf is NULL then this is not used. */ Py_buffer buckets_buffer; #endif } HashIndex; /* prime (or w/ big prime factors) hash table sizes * not sure we need primes for borg's usage (as we have a hash function based * on sha256, we can assume an even, seemingly random distribution of values), * but OTOH primes don't harm. * also, growth of the sizes starts with fast-growing 2x steps, but slows down * more and more down to 1.1x. this is to avoid huge jumps in memory allocation, * like e.g. 4G -> 8G. * these values are generated by hash_sizes.py. * * update: no, we don't need primes or w/ big prime factors, we followed some * incomplete / irrelevant advice here that did not match our use case. * otoh, for now, we do not need to change the sizes as they do no harm. * see ticket #2830. */ static int hash_sizes[] = { 1031, 2053, 4099, 8209, 16411, 32771, 65537, 131101, 262147, 445649, 757607, 1287917, 2189459, 3065243, 4291319, 6007867, 8410991, 11775359, 16485527, 23079703, 27695653, 33234787, 39881729, 47858071, 57429683, 68915617, 82698751, 99238507, 119086189, 144378011, 157223263, 173476439, 190253911, 209915011, 230493629, 253169431, 278728861, 306647623, 337318939, 370742809, 408229973, 449387209, 493428073, 543105119, 596976533, 657794869, 722676499, 795815791, 874066969, 962279771, 1057701643, 1164002657, 1280003147, 1407800297, 1548442699, 1703765389, 1873768367, 2062383853, /* 32bit int ends about here */ }; #define HASH_MIN_LOAD .25 #define HASH_MAX_LOAD .75 /* don't go higher than 0.75, otherwise performance severely suffers! */ #define HASH_MAX_EFF_LOAD .93 #define MAX(x, y) ((x) > (y) ? (x): (y)) #define NELEMS(x) (sizeof(x) / sizeof((x)[0])) #define EMPTY _htole32(0xffffffff) #define DELETED _htole32(0xfffffffe) #define BUCKET_ADDR(index, idx) (index->buckets + ((idx) * index->bucket_size)) #define BUCKET_MATCHES_KEY(index, idx, key) (memcmp(key, BUCKET_ADDR(index, idx), index->key_size) == 0) #define BUCKET_IS_DELETED(index, idx) (*((uint32_t *)(BUCKET_ADDR(index, idx) + index->key_size)) == DELETED) #define BUCKET_IS_EMPTY(index, idx) (*((uint32_t *)(BUCKET_ADDR(index, idx) + index->key_size)) == EMPTY) #define BUCKET_MARK_DELETED(index, idx) (*((uint32_t *)(BUCKET_ADDR(index, idx) + index->key_size)) = DELETED) #define BUCKET_MARK_EMPTY(index, idx) (*((uint32_t *)(BUCKET_ADDR(index, idx) + index->key_size)) = EMPTY) #define EPRINTF_MSG(msg, ...) fprintf(stderr, "hashindex: " msg "\n", ##__VA_ARGS__) #define EPRINTF_MSG_PATH(path, msg, ...) fprintf(stderr, "hashindex: %s: " msg "\n", path, ##__VA_ARGS__) #define EPRINTF(msg, ...) fprintf(stderr, "hashindex: " msg "(%s)\n", ##__VA_ARGS__, strerror(errno)) #define EPRINTF_PATH(path, msg, ...) fprintf(stderr, "hashindex: %s: " msg " (%s)\n", path, ##__VA_ARGS__, strerror(errno)) #ifndef BORG_NO_PYTHON static HashIndex *hashindex_read(PyObject *file_py, int permit_compact); static void hashindex_write(HashIndex *index, PyObject *file_py); #endif static uint64_t hashindex_compact(HashIndex *index); static HashIndex *hashindex_init(int capacity, int key_size, int value_size); static const unsigned char *hashindex_get(HashIndex *index, const unsigned char *key); static int hashindex_set(HashIndex *index, const unsigned char *key, const void *value); static int hashindex_delete(HashIndex *index, const unsigned char *key); static unsigned char *hashindex_next_key(HashIndex *index, const unsigned char *key); /* Private API */ static void hashindex_free(HashIndex *index); static void hashindex_free_buckets(HashIndex *index) { #ifndef BORG_NO_PYTHON if(index->buckets_buffer.buf) { PyBuffer_Release(&index->buckets_buffer); } else #endif { free(index->buckets); } } static int hashindex_index(HashIndex *index, const unsigned char *key) { return _le32toh(*((uint32_t *)key)) % index->num_buckets; } static int hashindex_lookup(HashIndex *index, const unsigned char *key, int *start_idx) { int didx = -1; int start = hashindex_index(index, key); /* perfect index for this key, if there is no collision. */ int idx = start; for(;;) { if(BUCKET_IS_EMPTY(index, idx)) { break; /* if we encounter an empty bucket, we do not need to look any further. */ } if(BUCKET_IS_DELETED(index, idx)) { if(didx == -1) { didx = idx; /* remember the index of the first deleted bucket. */ } } else if(BUCKET_MATCHES_KEY(index, idx, key)) { /* we found the bucket with the key we are looking for! */ if (didx != -1) { // note: although lookup is logically a read-only operation, // we optimize (change) the hashindex here "on the fly": // swap this full bucket with a previous deleted/tombstone bucket. memcpy(BUCKET_ADDR(index, didx), BUCKET_ADDR(index, idx), index->bucket_size); BUCKET_MARK_DELETED(index, idx); idx = didx; } return idx; } idx++; if (idx >= index->num_buckets) { /* triggers at == already */ idx = 0; } /* When idx == start, we have done a full pass over all buckets. * - We did not find a bucket with the key we searched for. * - We did not find an empty bucket either. * So all buckets are either full or deleted/tombstones. * This is an invalid state we never should get into, see * upper_limit and min_empty. */ assert(idx != start); } /* we get here if we did not find a bucket with the key we searched for. */ if (start_idx != NULL) { /* by giving a non-NULL pointer in start_idx, caller can request to * get the index of the first empty or deleted bucket we encountered, * e.g. to add a new entry for that key into that bucket. */ (*start_idx) = (didx == -1) ? idx : didx; } return -1; } static int hashindex_resize(HashIndex *index, int capacity) { HashIndex *new; unsigned char *key = NULL; int32_t key_size = index->key_size; if(!(new = hashindex_init(capacity, key_size, index->value_size))) { return 0; } while((key = hashindex_next_key(index, key))) { if(!hashindex_set(new, key, key + key_size)) { /* This can only happen if there's a bug in the code calculating capacity */ hashindex_free(new); return 0; } } assert(index->num_entries == new->num_entries); hashindex_free_buckets(index); index->buckets = new->buckets; index->num_buckets = new->num_buckets; index->num_empty = index->num_buckets - index->num_entries; index->lower_limit = new->lower_limit; index->upper_limit = new->upper_limit; index->min_empty = new->min_empty; free(new); return 1; } int get_lower_limit(int num_buckets){ int min_buckets = hash_sizes[0]; if (num_buckets <= min_buckets) return 0; return (int)(num_buckets * HASH_MIN_LOAD); } int get_upper_limit(int num_buckets){ int max_buckets = hash_sizes[NELEMS(hash_sizes) - 1]; if (num_buckets >= max_buckets) return num_buckets; return (int)(num_buckets * HASH_MAX_LOAD); } int get_min_empty(int num_buckets){ /* Differently from load, the effective load also considers tombstones (deleted buckets). * We always add 1, so this never can return 0 (0 empty buckets would be a bad HT state). */ return 1 + (int)(num_buckets * (1.0 - HASH_MAX_EFF_LOAD)); } int size_idx(int size){ /* find the smallest hash_sizes index with entry >= size */ int i = NELEMS(hash_sizes) - 1; while(i >= 0 && hash_sizes[i] >= size) i--; return i + 1; } int fit_size(int current){ int i = size_idx(current); return hash_sizes[i]; } int grow_size(int current){ int i = size_idx(current) + 1; int elems = NELEMS(hash_sizes); if (i >= elems) return hash_sizes[elems - 1]; return hash_sizes[i]; } int shrink_size(int current){ int i = size_idx(current) - 1; if (i < 0) return hash_sizes[0]; return hash_sizes[i]; } int count_empty(HashIndex *index) { /* count empty (never used) buckets. this does NOT include deleted buckets (tombstones). * TODO: if we ever change HashHeader, save the count there so we do not need this function. */ int i, count = 0, capacity = index->num_buckets; for(i = 0; i < capacity; i++) { if(BUCKET_IS_EMPTY(index, i)) count++; } return count; } /* Public API */ #ifndef BORG_NO_PYTHON static HashIndex * hashindex_read(PyObject *file_py, int permit_compact) { Py_ssize_t length, buckets_length, bytes_read; Py_buffer header_buffer; PyObject *header_bytes, *length_object, *bucket_bytes, *tmp; HashHeader *header; HashIndex *index = NULL; header_bytes = PyObject_CallMethod(file_py, "read", "n", (Py_ssize_t)sizeof(HashHeader)); if(!header_bytes) { assert(PyErr_Occurred()); goto fail; } bytes_read = PyBytes_Size(header_bytes); if(PyErr_Occurred()) { /* TypeError, not a bytes() object */ goto fail_decref_header; } if(bytes_read != sizeof(HashHeader)) { /* Truncated file */ /* Note: %zd is the format for Py_ssize_t, %zu is for size_t */ PyErr_Format(PyExc_ValueError, "Could not read header (expected %zu, but read %zd bytes)", sizeof(HashHeader), bytes_read); goto fail_decref_header; } /* * Hash the header * If the header is corrupted this bails before doing something stupid (like allocating 3.8 TB of memory) */ tmp = PyObject_CallMethod(file_py, "hash_part", "s", "HashHeader"); Py_XDECREF(tmp); if(PyErr_Occurred()) { if(PyErr_ExceptionMatches(PyExc_AttributeError)) { /* Be able to work with regular file objects which do not have a hash_part method. */ PyErr_Clear(); } else { goto fail_decref_header; } } /* Find length of file */ length_object = PyObject_CallMethod(file_py, "seek", "ni", (Py_ssize_t)0, SEEK_END); if(PyErr_Occurred()) { goto fail_decref_header; } length = PyNumber_AsSsize_t(length_object, PyExc_OverflowError); Py_DECREF(length_object); if(PyErr_Occurred()) { /* This shouldn't generally happen; but can if seek() returns something that's not a number */ goto fail_decref_header; } tmp = PyObject_CallMethod(file_py, "seek", "ni", (Py_ssize_t)sizeof(HashHeader), SEEK_SET); Py_XDECREF(tmp); if(PyErr_Occurred()) { goto fail_decref_header; } /* Set up the in-memory header */ if(!(index = malloc(sizeof(HashIndex)))) { PyErr_NoMemory(); goto fail_decref_header; } PyObject_GetBuffer(header_bytes, &header_buffer, PyBUF_SIMPLE); if(PyErr_Occurred()) { goto fail_free_index; } header = (HashHeader*) header_buffer.buf; if(memcmp(header->magic, MAGIC, MAGIC_LEN)) { PyErr_Format(PyExc_ValueError, "Unknown MAGIC in header"); goto fail_release_header_buffer; } buckets_length = (Py_ssize_t)_le32toh(header->num_buckets) * (header->key_size + header->value_size); if((Py_ssize_t)length != (Py_ssize_t)sizeof(HashHeader) + buckets_length) { PyErr_Format(PyExc_ValueError, "Incorrect file length (expected %zd, got %zd)", sizeof(HashHeader) + buckets_length, length); goto fail_release_header_buffer; } index->num_entries = _le32toh(header->num_entries); index->num_buckets = _le32toh(header->num_buckets); index->key_size = header->key_size; index->value_size = header->value_size; index->bucket_size = index->key_size + index->value_size; index->lower_limit = get_lower_limit(index->num_buckets); index->upper_limit = get_upper_limit(index->num_buckets); /* * For indices read from disk we don't malloc() the buckets ourselves, * we have them backed by a Python bytes() object instead, and go through * Python I/O. * * Note: Issuing read(buckets_length) is okay here, because buffered readers * will issue multiple underlying reads if necessary. This supports indices * >2 GB on Linux. We also compare lengths later. */ bucket_bytes = PyObject_CallMethod(file_py, "read", "n", buckets_length); if(!bucket_bytes) { assert(PyErr_Occurred()); goto fail_release_header_buffer; } bytes_read = PyBytes_Size(bucket_bytes); if(PyErr_Occurred()) { /* TypeError, not a bytes() object */ goto fail_decref_buckets; } if(bytes_read != buckets_length) { PyErr_Format(PyExc_ValueError, "Could not read buckets (expected %zd, got %zd)", buckets_length, bytes_read); goto fail_decref_buckets; } PyObject_GetBuffer(bucket_bytes, &index->buckets_buffer, PyBUF_SIMPLE); if(PyErr_Occurred()) { goto fail_decref_buckets; } index->buckets = index->buckets_buffer.buf; if(!permit_compact) { index->min_empty = get_min_empty(index->num_buckets); index->num_empty = count_empty(index); if(index->num_empty < index->min_empty) { /* too many tombstones here / not enough empty buckets, do a same-size rebuild */ if(!hashindex_resize(index, index->num_buckets)) { PyErr_Format(PyExc_ValueError, "Failed to rebuild table"); goto fail_free_buckets; } } } /* * Clean intermediary objects up. Note that index is only freed if an error has occurred. * Also note that the buffer in index->buckets_buffer holds a reference to buckets_bytes. */ fail_free_buckets: if(PyErr_Occurred()) { hashindex_free_buckets(index); } fail_decref_buckets: Py_DECREF(bucket_bytes); fail_release_header_buffer: PyBuffer_Release(&header_buffer); fail_free_index: if(PyErr_Occurred()) { free(index); index = NULL; } fail_decref_header: Py_DECREF(header_bytes); fail: return index; } #endif static HashIndex * hashindex_init(int capacity, int key_size, int value_size) { HashIndex *index; int i; capacity = fit_size(capacity); if(!(index = malloc(sizeof(HashIndex)))) { EPRINTF("malloc header failed"); return NULL; } if(!(index->buckets = calloc(capacity, key_size + value_size))) { EPRINTF("malloc buckets failed"); free(index); return NULL; } index->num_entries = 0; index->key_size = key_size; index->value_size = value_size; index->num_buckets = capacity; index->num_empty = capacity; index->bucket_size = index->key_size + index->value_size; index->lower_limit = get_lower_limit(index->num_buckets); index->upper_limit = get_upper_limit(index->num_buckets); index->min_empty = get_min_empty(index->num_buckets); #ifndef BORG_NO_PYTHON index->buckets_buffer.buf = NULL; #endif for(i = 0; i < capacity; i++) { BUCKET_MARK_EMPTY(index, i); } return index; } static void hashindex_free(HashIndex *index) { hashindex_free_buckets(index); free(index); } #ifndef BORG_NO_PYTHON static void hashindex_write(HashIndex *index, PyObject *file_py) { PyObject *length_object, *buckets_view, *tmp; Py_ssize_t length; Py_ssize_t buckets_length = (Py_ssize_t)index->num_buckets * index->bucket_size; HashHeader header = { .magic = MAGIC, .num_entries = _htole32(index->num_entries), .num_buckets = _htole32(index->num_buckets), .key_size = index->key_size, .value_size = index->value_size }; length_object = PyObject_CallMethod(file_py, "write", "y#", &header, (Py_ssize_t)sizeof(HashHeader)); if(PyErr_Occurred()) { return; } length = PyNumber_AsSsize_t(length_object, PyExc_OverflowError); Py_DECREF(length_object); if(PyErr_Occurred()) { return; } if(length != sizeof(HashHeader)) { PyErr_SetString(PyExc_ValueError, "Failed to write header"); return; } /* * Hash the header */ tmp = PyObject_CallMethod(file_py, "hash_part", "s", "HashHeader"); Py_XDECREF(tmp); if(PyErr_Occurred()) { if(PyErr_ExceptionMatches(PyExc_AttributeError)) { /* Be able to work with regular file objects which do not have a hash_part method. */ PyErr_Clear(); } else { return; } } /* Note: explicitly construct view; BuildValue can convert (pointer, length) to Python objects, but copies them for doing so */ buckets_view = PyMemoryView_FromMemory((char*)index->buckets, buckets_length, PyBUF_READ); if(!buckets_view) { assert(PyErr_Occurred()); return; } length_object = PyObject_CallMethod(file_py, "write", "O", buckets_view); Py_DECREF(buckets_view); if(PyErr_Occurred()) { return; } length = PyNumber_AsSsize_t(length_object, PyExc_OverflowError); Py_DECREF(length_object); if(PyErr_Occurred()) { return; } if(length != buckets_length) { PyErr_SetString(PyExc_ValueError, "Failed to write buckets"); return; } } #endif static const unsigned char * hashindex_get(HashIndex *index, const unsigned char *key) { int idx = hashindex_lookup(index, key, NULL); if(idx < 0) { return NULL; } return BUCKET_ADDR(index, idx) + index->key_size; } static int hashindex_set(HashIndex *index, const unsigned char *key, const void *value) { int start_idx; int idx = hashindex_lookup(index, key, &start_idx); /* if idx < 0: start_idx -> EMPTY or DELETED */ uint8_t *ptr; if(idx < 0) { if(index->num_entries > index->upper_limit) { /* hashtable too full, grow it! */ if(!hashindex_resize(index, grow_size(index->num_buckets))) { return 0; } /* we have just built a fresh hashtable and removed all tombstones, * so we only have EMPTY or USED buckets, but no DELETED ones any more. */ idx = hashindex_lookup(index, key, &start_idx); assert(idx < 0); assert(BUCKET_IS_EMPTY(index, start_idx)); } idx = start_idx; if(BUCKET_IS_EMPTY(index, idx)){ if(index->num_empty <= index->min_empty) { /* too many tombstones here / not enough empty buckets, do a same-size rebuild */ if(!hashindex_resize(index, index->num_buckets)) { return 0; } /* we have just built a fresh hashtable and removed all tombstones, * so we only have EMPTY or USED buckets, but no DELETED ones any more. */ idx = hashindex_lookup(index, key, &start_idx); assert(idx < 0); assert(BUCKET_IS_EMPTY(index, start_idx)); idx = start_idx; } index->num_empty--; } else { /* Bucket must be either EMPTY (see above) or DELETED. */ assert(BUCKET_IS_DELETED(index, idx)); } ptr = BUCKET_ADDR(index, idx); memcpy(ptr, key, index->key_size); memcpy(ptr + index->key_size, value, index->value_size); index->num_entries += 1; } else { memcpy(BUCKET_ADDR(index, idx) + index->key_size, value, index->value_size); } return 1; } static int hashindex_delete(HashIndex *index, const unsigned char *key) { int idx = hashindex_lookup(index, key, NULL); if (idx < 0) { return -1; } BUCKET_MARK_DELETED(index, idx); index->num_entries -= 1; if(index->num_entries < index->lower_limit) { if(!hashindex_resize(index, shrink_size(index->num_buckets))) { return 0; } } return 1; } static unsigned char * hashindex_next_key(HashIndex *index, const unsigned char *key) { int idx = 0; if(key) { idx = 1 + (key - index->buckets) / index->bucket_size; } if (idx == index->num_buckets) { return NULL; } while(BUCKET_IS_EMPTY(index, idx) || BUCKET_IS_DELETED(index, idx)) { idx ++; if (idx == index->num_buckets) { return NULL; } } return BUCKET_ADDR(index, idx); } static uint64_t hashindex_compact(HashIndex *index) { int idx = 0; int start_idx; int begin_used_idx; int empty_slot_count, count, buckets_to_copy; int compact_tail_idx = 0; uint64_t saved_size = (index->num_buckets - index->num_entries) * (uint64_t)index->bucket_size; if(index->num_buckets - index->num_entries == 0) { /* already compact */ return 0; } while(idx < index->num_buckets) { /* Phase 1: Find some empty slots */ start_idx = idx; while((idx < index->num_buckets) && (BUCKET_IS_EMPTY(index, idx) || BUCKET_IS_DELETED(index, idx))) { idx++; } /* everything from start_idx to idx-1 (inclusive) is empty or deleted */ count = empty_slot_count = idx - start_idx; begin_used_idx = idx; if(!empty_slot_count) { /* In case idx==compact_tail_idx, the areas overlap */ memmove(BUCKET_ADDR(index, compact_tail_idx), BUCKET_ADDR(index, idx), index->bucket_size); idx++; compact_tail_idx++; continue; } /* Phase 2: Find some non-empty/non-deleted slots we can move to the compact tail */ while(empty_slot_count && (idx < index->num_buckets) && !(BUCKET_IS_EMPTY(index, idx) || BUCKET_IS_DELETED(index, idx))) { idx++; empty_slot_count--; } buckets_to_copy = count - empty_slot_count; if(!buckets_to_copy) { /* Nothing to move, reached end of the buckets array with no used buckets. */ break; } memcpy(BUCKET_ADDR(index, compact_tail_idx), BUCKET_ADDR(index, begin_used_idx), buckets_to_copy * index->bucket_size); compact_tail_idx += buckets_to_copy; } index->num_buckets = index->num_entries; return saved_size; } static int hashindex_len(HashIndex *index) { return index->num_entries; } static int hashindex_size(HashIndex *index) { return sizeof(HashHeader) + index->num_buckets * index->bucket_size; } /* * Used by the FuseVersionsIndex. */ BORG_PACKED( typedef struct { uint32_t version; char hash[16]; } ) FuseVersionsElement; ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/_item.c0000644000076500000240000000271614601576577015227 0ustar00twstaff#include "Python.h" /* * This is not quite as dark magic as it looks. We just convert the address of (pointer to) * a PyObject into a bytes object in _wrap_object, and convert these bytes back to the * pointer to the original object. * * This mainly looks a bit confusing due to our mental special-casing of "char*" from other * pointers. * * The big upside to this is that this neither does *any* serialization (beyond creating tiny * bytes objects as "stand-ins"), nor has to copy the entire object that's passed around. */ static PyObject * _object_to_optr(PyObject *obj) { /* * Create a temporary reference to the object being passed around so it does not vanish. * Note that we never decref this one in _unwrap_object, since we just transfer that reference * there, i.e. there is an elided "Py_INCREF(x); Py_DECREF(x)". * Since the reference is transferred, calls to _wrap_object and _unwrap_object must be symmetric. */ Py_INCREF(obj); return PyBytes_FromStringAndSize((const char*) &obj, sizeof(void*)); } static PyObject * _optr_to_object(PyObject *bytes) { if(!PyBytes_Check(bytes)) { PyErr_SetString(PyExc_TypeError, "Cannot unwrap non-bytes object"); return NULL; } if(PyBytes_Size(bytes) != sizeof(void*)) { PyErr_SetString(PyExc_TypeError, "Invalid length of bytes object"); return NULL; } PyObject *object = * (PyObject **) PyBytes_AsString(bytes); return object; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734323.0 borgbackup-1.2.8/src/borg/_version.py0000644000076500000240000000004014601577063016137 0ustar00twstaff__version__ = version = '1.2.8' ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1684237 borgbackup-1.2.8/src/borg/algorithms/0000755000076500000240000000000014601577064016121 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/__init__.py0000644000076500000240000000046014601576577020242 0ustar00twstaff""" borg.algorithms =============== This package is intended for hash and checksum functions. Ideally these would be sourced from existing libraries, but: - are frequently not available yet (lz4, zstd), - are available but in poor form (crc32), or - don't really make sense as a library (xxHash). """ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734186.0 borgbackup-1.2.8/src/borg/algorithms/checksums.c0000644000076500000240000150541514601576652020267 0ustar00twstaff/* Generated by Cython 3.0.9 */ /* BEGIN: Cython Metadata { "distutils": { "depends": [ "src/borg/algorithms/crc32_dispatch.c", "src/borg/algorithms/xxhash-libselect.h" ], "extra_compile_args": [ "-Wall", "-Wextra", "-Wpointer-arith" ], "include_dirs": [ "src/borg/algorithms", "/opt/homebrew/Cellar/xxhash/0.8.2/include" ], "libraries": [ "xxhash" ], "library_dirs": [ "/opt/homebrew/Cellar/xxhash/0.8.2/lib" ], "name": "borg.algorithms.checksums", "sources": [ "src/borg/algorithms/checksums.pyx" ] }, "module_name": "borg.algorithms.checksums" } END: Cython Metadata */ #ifndef PY_SSIZE_T_CLEAN #define PY_SSIZE_T_CLEAN #endif /* PY_SSIZE_T_CLEAN */ #if defined(CYTHON_LIMITED_API) && 0 #ifndef Py_LIMITED_API #if CYTHON_LIMITED_API+0 > 0x03030000 #define Py_LIMITED_API CYTHON_LIMITED_API #else #define Py_LIMITED_API 0x03030000 #endif #endif #endif #include "Python.h" #ifndef Py_PYTHON_H #error Python headers needed to compile C extensions, please install development version of Python. #elif PY_VERSION_HEX < 0x02070000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000) #error Cython requires Python 2.7+ or Python 3.3+. #else #if defined(CYTHON_LIMITED_API) && CYTHON_LIMITED_API #define __PYX_EXTRA_ABI_MODULE_NAME "limited" #else #define __PYX_EXTRA_ABI_MODULE_NAME "" #endif #define CYTHON_ABI "3_0_9" __PYX_EXTRA_ABI_MODULE_NAME #define __PYX_ABI_MODULE_NAME "_cython_" CYTHON_ABI #define __PYX_TYPE_MODULE_PREFIX __PYX_ABI_MODULE_NAME "." #define CYTHON_HEX_VERSION 0x030009F0 #define CYTHON_FUTURE_DIVISION 1 #include #ifndef offsetof #define offsetof(type, member) ( (size_t) & ((type*)0) -> member ) #endif #if !defined(_WIN32) && !defined(WIN32) && !defined(MS_WINDOWS) #ifndef __stdcall #define __stdcall #endif #ifndef __cdecl #define __cdecl #endif #ifndef __fastcall #define __fastcall #endif #endif #ifndef DL_IMPORT #define DL_IMPORT(t) t #endif #ifndef DL_EXPORT #define DL_EXPORT(t) t #endif #define __PYX_COMMA , #ifndef HAVE_LONG_LONG #define HAVE_LONG_LONG #endif #ifndef PY_LONG_LONG #define PY_LONG_LONG LONG_LONG #endif #ifndef Py_HUGE_VAL #define Py_HUGE_VAL HUGE_VAL #endif #define __PYX_LIMITED_VERSION_HEX PY_VERSION_HEX #if defined(GRAALVM_PYTHON) /* For very preliminary testing purposes. Most variables are set the same as PyPy. The existence of this section does not imply that anything works or is even tested */ #define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_CPYTHON 0 #define CYTHON_COMPILING_IN_LIMITED_API 0 #define CYTHON_COMPILING_IN_GRAAL 1 #define CYTHON_COMPILING_IN_NOGIL 0 #undef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 0 #undef CYTHON_USE_TYPE_SPECS #define CYTHON_USE_TYPE_SPECS 0 #undef CYTHON_USE_PYTYPE_LOOKUP #define CYTHON_USE_PYTYPE_LOOKUP 0 #if PY_VERSION_HEX < 0x03050000 #undef CYTHON_USE_ASYNC_SLOTS #define CYTHON_USE_ASYNC_SLOTS 0 #elif !defined(CYTHON_USE_ASYNC_SLOTS) #define CYTHON_USE_ASYNC_SLOTS 1 #endif #undef CYTHON_USE_PYLIST_INTERNALS #define CYTHON_USE_PYLIST_INTERNALS 0 #undef CYTHON_USE_UNICODE_INTERNALS #define CYTHON_USE_UNICODE_INTERNALS 0 #undef CYTHON_USE_UNICODE_WRITER #define CYTHON_USE_UNICODE_WRITER 0 #undef CYTHON_USE_PYLONG_INTERNALS #define CYTHON_USE_PYLONG_INTERNALS 0 #undef CYTHON_AVOID_BORROWED_REFS #define CYTHON_AVOID_BORROWED_REFS 1 #undef CYTHON_ASSUME_SAFE_MACROS #define CYTHON_ASSUME_SAFE_MACROS 0 #undef CYTHON_UNPACK_METHODS #define CYTHON_UNPACK_METHODS 0 #undef CYTHON_FAST_THREAD_STATE #define CYTHON_FAST_THREAD_STATE 0 #undef CYTHON_FAST_GIL #define CYTHON_FAST_GIL 0 #undef CYTHON_METH_FASTCALL #define CYTHON_METH_FASTCALL 0 #undef CYTHON_FAST_PYCALL #define CYTHON_FAST_PYCALL 0 #ifndef CYTHON_PEP487_INIT_SUBCLASS #define CYTHON_PEP487_INIT_SUBCLASS (PY_MAJOR_VERSION >= 3) #endif #undef CYTHON_PEP489_MULTI_PHASE_INIT #define CYTHON_PEP489_MULTI_PHASE_INIT 1 #undef CYTHON_USE_MODULE_STATE #define CYTHON_USE_MODULE_STATE 0 #undef CYTHON_USE_TP_FINALIZE #define CYTHON_USE_TP_FINALIZE 0 #undef CYTHON_USE_DICT_VERSIONS #define CYTHON_USE_DICT_VERSIONS 0 #undef CYTHON_USE_EXC_INFO_STACK #define CYTHON_USE_EXC_INFO_STACK 0 #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 #endif #elif defined(PYPY_VERSION) #define CYTHON_COMPILING_IN_PYPY 1 #define CYTHON_COMPILING_IN_CPYTHON 0 #define CYTHON_COMPILING_IN_LIMITED_API 0 #define CYTHON_COMPILING_IN_GRAAL 0 #define CYTHON_COMPILING_IN_NOGIL 0 #undef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 0 #ifndef CYTHON_USE_TYPE_SPECS #define CYTHON_USE_TYPE_SPECS 0 #endif #undef CYTHON_USE_PYTYPE_LOOKUP #define CYTHON_USE_PYTYPE_LOOKUP 0 #if PY_VERSION_HEX < 0x03050000 #undef CYTHON_USE_ASYNC_SLOTS #define CYTHON_USE_ASYNC_SLOTS 0 #elif !defined(CYTHON_USE_ASYNC_SLOTS) #define CYTHON_USE_ASYNC_SLOTS 1 #endif #undef CYTHON_USE_PYLIST_INTERNALS #define CYTHON_USE_PYLIST_INTERNALS 0 #undef CYTHON_USE_UNICODE_INTERNALS #define CYTHON_USE_UNICODE_INTERNALS 0 #undef CYTHON_USE_UNICODE_WRITER #define CYTHON_USE_UNICODE_WRITER 0 #undef CYTHON_USE_PYLONG_INTERNALS #define CYTHON_USE_PYLONG_INTERNALS 0 #undef CYTHON_AVOID_BORROWED_REFS #define CYTHON_AVOID_BORROWED_REFS 1 #undef CYTHON_ASSUME_SAFE_MACROS #define CYTHON_ASSUME_SAFE_MACROS 0 #undef CYTHON_UNPACK_METHODS #define CYTHON_UNPACK_METHODS 0 #undef CYTHON_FAST_THREAD_STATE #define CYTHON_FAST_THREAD_STATE 0 #undef CYTHON_FAST_GIL #define CYTHON_FAST_GIL 0 #undef CYTHON_METH_FASTCALL #define CYTHON_METH_FASTCALL 0 #undef CYTHON_FAST_PYCALL #define CYTHON_FAST_PYCALL 0 #ifndef CYTHON_PEP487_INIT_SUBCLASS #define CYTHON_PEP487_INIT_SUBCLASS (PY_MAJOR_VERSION >= 3) #endif #if PY_VERSION_HEX < 0x03090000 #undef CYTHON_PEP489_MULTI_PHASE_INIT #define CYTHON_PEP489_MULTI_PHASE_INIT 0 #elif !defined(CYTHON_PEP489_MULTI_PHASE_INIT) #define CYTHON_PEP489_MULTI_PHASE_INIT 1 #endif #undef CYTHON_USE_MODULE_STATE #define CYTHON_USE_MODULE_STATE 0 #undef CYTHON_USE_TP_FINALIZE #define CYTHON_USE_TP_FINALIZE (PY_VERSION_HEX >= 0x030400a1 && PYPY_VERSION_NUM >= 0x07030C00) #undef CYTHON_USE_DICT_VERSIONS #define CYTHON_USE_DICT_VERSIONS 0 #undef CYTHON_USE_EXC_INFO_STACK #define CYTHON_USE_EXC_INFO_STACK 0 #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 #endif #elif defined(CYTHON_LIMITED_API) #ifdef Py_LIMITED_API #undef __PYX_LIMITED_VERSION_HEX #define __PYX_LIMITED_VERSION_HEX Py_LIMITED_API #endif #define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_CPYTHON 0 #define CYTHON_COMPILING_IN_LIMITED_API 1 #define CYTHON_COMPILING_IN_GRAAL 0 #define CYTHON_COMPILING_IN_NOGIL 0 #undef CYTHON_CLINE_IN_TRACEBACK #define CYTHON_CLINE_IN_TRACEBACK 0 #undef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 0 #undef CYTHON_USE_TYPE_SPECS #define CYTHON_USE_TYPE_SPECS 1 #undef CYTHON_USE_PYTYPE_LOOKUP #define CYTHON_USE_PYTYPE_LOOKUP 0 #undef CYTHON_USE_ASYNC_SLOTS #define CYTHON_USE_ASYNC_SLOTS 0 #undef CYTHON_USE_PYLIST_INTERNALS #define CYTHON_USE_PYLIST_INTERNALS 0 #undef CYTHON_USE_UNICODE_INTERNALS #define CYTHON_USE_UNICODE_INTERNALS 0 #ifndef CYTHON_USE_UNICODE_WRITER #define CYTHON_USE_UNICODE_WRITER 0 #endif #undef CYTHON_USE_PYLONG_INTERNALS #define CYTHON_USE_PYLONG_INTERNALS 0 #ifndef CYTHON_AVOID_BORROWED_REFS #define CYTHON_AVOID_BORROWED_REFS 0 #endif #undef CYTHON_ASSUME_SAFE_MACROS #define CYTHON_ASSUME_SAFE_MACROS 0 #undef CYTHON_UNPACK_METHODS #define CYTHON_UNPACK_METHODS 0 #undef CYTHON_FAST_THREAD_STATE #define CYTHON_FAST_THREAD_STATE 0 #undef CYTHON_FAST_GIL #define CYTHON_FAST_GIL 0 #undef CYTHON_METH_FASTCALL #define CYTHON_METH_FASTCALL 0 #undef CYTHON_FAST_PYCALL #define CYTHON_FAST_PYCALL 0 #ifndef CYTHON_PEP487_INIT_SUBCLASS #define CYTHON_PEP487_INIT_SUBCLASS 1 #endif #undef CYTHON_PEP489_MULTI_PHASE_INIT #define CYTHON_PEP489_MULTI_PHASE_INIT 0 #undef CYTHON_USE_MODULE_STATE #define CYTHON_USE_MODULE_STATE 1 #ifndef CYTHON_USE_TP_FINALIZE #define CYTHON_USE_TP_FINALIZE 0 #endif #undef CYTHON_USE_DICT_VERSIONS #define CYTHON_USE_DICT_VERSIONS 0 #undef CYTHON_USE_EXC_INFO_STACK #define CYTHON_USE_EXC_INFO_STACK 0 #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 #endif #elif defined(Py_GIL_DISABLED) || defined(Py_NOGIL) #define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_CPYTHON 0 #define CYTHON_COMPILING_IN_LIMITED_API 0 #define CYTHON_COMPILING_IN_GRAAL 0 #define CYTHON_COMPILING_IN_NOGIL 1 #ifndef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 1 #endif #undef CYTHON_USE_PYTYPE_LOOKUP #define CYTHON_USE_PYTYPE_LOOKUP 0 #ifndef CYTHON_USE_ASYNC_SLOTS #define CYTHON_USE_ASYNC_SLOTS 1 #endif #undef CYTHON_USE_PYLIST_INTERNALS #define CYTHON_USE_PYLIST_INTERNALS 0 #ifndef CYTHON_USE_UNICODE_INTERNALS #define CYTHON_USE_UNICODE_INTERNALS 1 #endif #undef CYTHON_USE_UNICODE_WRITER #define CYTHON_USE_UNICODE_WRITER 0 #undef CYTHON_USE_PYLONG_INTERNALS #define CYTHON_USE_PYLONG_INTERNALS 0 #ifndef CYTHON_AVOID_BORROWED_REFS #define CYTHON_AVOID_BORROWED_REFS 0 #endif #ifndef CYTHON_ASSUME_SAFE_MACROS #define CYTHON_ASSUME_SAFE_MACROS 1 #endif #ifndef CYTHON_UNPACK_METHODS #define CYTHON_UNPACK_METHODS 1 #endif #undef CYTHON_FAST_THREAD_STATE #define CYTHON_FAST_THREAD_STATE 0 #undef CYTHON_FAST_PYCALL #define CYTHON_FAST_PYCALL 0 #ifndef CYTHON_PEP489_MULTI_PHASE_INIT #define CYTHON_PEP489_MULTI_PHASE_INIT 1 #endif #ifndef CYTHON_USE_TP_FINALIZE #define CYTHON_USE_TP_FINALIZE 1 #endif #undef CYTHON_USE_DICT_VERSIONS #define CYTHON_USE_DICT_VERSIONS 0 #undef CYTHON_USE_EXC_INFO_STACK #define CYTHON_USE_EXC_INFO_STACK 0 #else #define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_CPYTHON 1 #define CYTHON_COMPILING_IN_LIMITED_API 0 #define CYTHON_COMPILING_IN_GRAAL 0 #define CYTHON_COMPILING_IN_NOGIL 0 #ifndef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 1 #endif #ifndef CYTHON_USE_TYPE_SPECS #define CYTHON_USE_TYPE_SPECS 0 #endif #ifndef CYTHON_USE_PYTYPE_LOOKUP #define CYTHON_USE_PYTYPE_LOOKUP 1 #endif #if PY_MAJOR_VERSION < 3 #undef CYTHON_USE_ASYNC_SLOTS #define CYTHON_USE_ASYNC_SLOTS 0 #elif !defined(CYTHON_USE_ASYNC_SLOTS) #define CYTHON_USE_ASYNC_SLOTS 1 #endif #ifndef CYTHON_USE_PYLONG_INTERNALS #define CYTHON_USE_PYLONG_INTERNALS 1 #endif #ifndef CYTHON_USE_PYLIST_INTERNALS #define CYTHON_USE_PYLIST_INTERNALS 1 #endif #ifndef CYTHON_USE_UNICODE_INTERNALS #define CYTHON_USE_UNICODE_INTERNALS 1 #endif #if PY_VERSION_HEX < 0x030300F0 || PY_VERSION_HEX >= 0x030B00A2 #undef CYTHON_USE_UNICODE_WRITER #define CYTHON_USE_UNICODE_WRITER 0 #elif !defined(CYTHON_USE_UNICODE_WRITER) #define CYTHON_USE_UNICODE_WRITER 1 #endif #ifndef CYTHON_AVOID_BORROWED_REFS #define CYTHON_AVOID_BORROWED_REFS 0 #endif #ifndef CYTHON_ASSUME_SAFE_MACROS #define CYTHON_ASSUME_SAFE_MACROS 1 #endif #ifndef CYTHON_UNPACK_METHODS #define CYTHON_UNPACK_METHODS 1 #endif #ifndef CYTHON_FAST_THREAD_STATE #define CYTHON_FAST_THREAD_STATE 1 #endif #ifndef CYTHON_FAST_GIL #define CYTHON_FAST_GIL (PY_MAJOR_VERSION < 3 || PY_VERSION_HEX >= 0x03060000 && PY_VERSION_HEX < 0x030C00A6) #endif #ifndef CYTHON_METH_FASTCALL #define CYTHON_METH_FASTCALL (PY_VERSION_HEX >= 0x030700A1) #endif #ifndef CYTHON_FAST_PYCALL #define CYTHON_FAST_PYCALL 1 #endif #ifndef CYTHON_PEP487_INIT_SUBCLASS #define CYTHON_PEP487_INIT_SUBCLASS 1 #endif #if PY_VERSION_HEX < 0x03050000 #undef CYTHON_PEP489_MULTI_PHASE_INIT #define CYTHON_PEP489_MULTI_PHASE_INIT 0 #elif !defined(CYTHON_PEP489_MULTI_PHASE_INIT) #define CYTHON_PEP489_MULTI_PHASE_INIT 1 #endif #ifndef CYTHON_USE_MODULE_STATE #define CYTHON_USE_MODULE_STATE 0 #endif #if PY_VERSION_HEX < 0x030400a1 #undef CYTHON_USE_TP_FINALIZE #define CYTHON_USE_TP_FINALIZE 0 #elif !defined(CYTHON_USE_TP_FINALIZE) #define CYTHON_USE_TP_FINALIZE 1 #endif #if PY_VERSION_HEX < 0x030600B1 #undef CYTHON_USE_DICT_VERSIONS #define CYTHON_USE_DICT_VERSIONS 0 #elif !defined(CYTHON_USE_DICT_VERSIONS) #define CYTHON_USE_DICT_VERSIONS (PY_VERSION_HEX < 0x030C00A5) #endif #if PY_VERSION_HEX < 0x030700A3 #undef CYTHON_USE_EXC_INFO_STACK #define CYTHON_USE_EXC_INFO_STACK 0 #elif !defined(CYTHON_USE_EXC_INFO_STACK) #define CYTHON_USE_EXC_INFO_STACK 1 #endif #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC #define CYTHON_UPDATE_DESCRIPTOR_DOC 1 #endif #endif #if !defined(CYTHON_FAST_PYCCALL) #define CYTHON_FAST_PYCCALL (CYTHON_FAST_PYCALL && PY_VERSION_HEX >= 0x030600B1) #endif #if !defined(CYTHON_VECTORCALL) #define CYTHON_VECTORCALL (CYTHON_FAST_PYCCALL && PY_VERSION_HEX >= 0x030800B1) #endif #define CYTHON_BACKPORT_VECTORCALL (CYTHON_METH_FASTCALL && PY_VERSION_HEX < 0x030800B1) #if CYTHON_USE_PYLONG_INTERNALS #if PY_MAJOR_VERSION < 3 #include "longintrepr.h" #endif #undef SHIFT #undef BASE #undef MASK #ifdef SIZEOF_VOID_P enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) }; #endif #endif #ifndef __has_attribute #define __has_attribute(x) 0 #endif #ifndef __has_cpp_attribute #define __has_cpp_attribute(x) 0 #endif #ifndef CYTHON_RESTRICT #if defined(__GNUC__) #define CYTHON_RESTRICT __restrict__ #elif defined(_MSC_VER) && _MSC_VER >= 1400 #define CYTHON_RESTRICT __restrict #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define CYTHON_RESTRICT restrict #else #define CYTHON_RESTRICT #endif #endif #ifndef CYTHON_UNUSED #if defined(__cplusplus) /* for clang __has_cpp_attribute(maybe_unused) is true even before C++17 * but leads to warnings with -pedantic, since it is a C++17 feature */ #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) #if __has_cpp_attribute(maybe_unused) #define CYTHON_UNUSED [[maybe_unused]] #endif #endif #endif #endif #ifndef CYTHON_UNUSED # if defined(__GNUC__) # if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) # define CYTHON_UNUSED __attribute__ ((__unused__)) # else # define CYTHON_UNUSED # endif # elif defined(__ICC) || (defined(__INTEL_COMPILER) && !defined(_MSC_VER)) # define CYTHON_UNUSED __attribute__ ((__unused__)) # else # define CYTHON_UNUSED # endif #endif #ifndef CYTHON_UNUSED_VAR # if defined(__cplusplus) template void CYTHON_UNUSED_VAR( const T& ) { } # else # define CYTHON_UNUSED_VAR(x) (void)(x) # endif #endif #ifndef CYTHON_MAYBE_UNUSED_VAR #define CYTHON_MAYBE_UNUSED_VAR(x) CYTHON_UNUSED_VAR(x) #endif #ifndef CYTHON_NCP_UNUSED # if CYTHON_COMPILING_IN_CPYTHON # define CYTHON_NCP_UNUSED # else # define CYTHON_NCP_UNUSED CYTHON_UNUSED # endif #endif #ifndef CYTHON_USE_CPP_STD_MOVE #if defined(__cplusplus) && (\ __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1600)) #define CYTHON_USE_CPP_STD_MOVE 1 #else #define CYTHON_USE_CPP_STD_MOVE 0 #endif #endif #define __Pyx_void_to_None(void_result) ((void)(void_result), Py_INCREF(Py_None), Py_None) #ifdef _MSC_VER #ifndef _MSC_STDINT_H_ #if _MSC_VER < 1300 typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif #endif #if _MSC_VER < 1300 #ifdef _WIN64 typedef unsigned long long __pyx_uintptr_t; #else typedef unsigned int __pyx_uintptr_t; #endif #else #ifdef _WIN64 typedef unsigned __int64 __pyx_uintptr_t; #else typedef unsigned __int32 __pyx_uintptr_t; #endif #endif #else #include typedef uintptr_t __pyx_uintptr_t; #endif #ifndef CYTHON_FALLTHROUGH #if defined(__cplusplus) /* for clang __has_cpp_attribute(fallthrough) is true even before C++17 * but leads to warnings with -pedantic, since it is a C++17 feature */ #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) #if __has_cpp_attribute(fallthrough) #define CYTHON_FALLTHROUGH [[fallthrough]] #endif #endif #ifndef CYTHON_FALLTHROUGH #if __has_cpp_attribute(clang::fallthrough) #define CYTHON_FALLTHROUGH [[clang::fallthrough]] #elif __has_cpp_attribute(gnu::fallthrough) #define CYTHON_FALLTHROUGH [[gnu::fallthrough]] #endif #endif #endif #ifndef CYTHON_FALLTHROUGH #if __has_attribute(fallthrough) #define CYTHON_FALLTHROUGH __attribute__((fallthrough)) #else #define CYTHON_FALLTHROUGH #endif #endif #if defined(__clang__) && defined(__apple_build_version__) #if __apple_build_version__ < 7000000 #undef CYTHON_FALLTHROUGH #define CYTHON_FALLTHROUGH #endif #endif #endif #ifdef __cplusplus template struct __PYX_IS_UNSIGNED_IMPL {static const bool value = T(0) < T(-1);}; #define __PYX_IS_UNSIGNED(type) (__PYX_IS_UNSIGNED_IMPL::value) #else #define __PYX_IS_UNSIGNED(type) (((type)-1) > 0) #endif #if CYTHON_COMPILING_IN_PYPY == 1 #define __PYX_NEED_TP_PRINT_SLOT (PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x030A0000) #else #define __PYX_NEED_TP_PRINT_SLOT (PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000) #endif #define __PYX_REINTERPRET_FUNCION(func_pointer, other_pointer) ((func_pointer)(void(*)(void))(other_pointer)) #ifndef CYTHON_INLINE #if defined(__clang__) #define CYTHON_INLINE __inline__ __attribute__ ((__unused__)) #elif defined(__GNUC__) #define CYTHON_INLINE __inline__ #elif defined(_MSC_VER) #define CYTHON_INLINE __inline #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define CYTHON_INLINE inline #else #define CYTHON_INLINE #endif #endif #define __PYX_BUILD_PY_SSIZE_T "n" #define CYTHON_FORMAT_SSIZE_T "z" #if PY_MAJOR_VERSION < 3 #define __Pyx_BUILTIN_MODULE_NAME "__builtin__" #define __Pyx_DefaultClassType PyClass_Type #define __Pyx_PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\ PyCode_New(a+k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) #else #define __Pyx_BUILTIN_MODULE_NAME "builtins" #define __Pyx_DefaultClassType PyType_Type #if CYTHON_COMPILING_IN_LIMITED_API static CYTHON_INLINE PyObject* __Pyx_PyCode_New(int a, int p, int k, int l, int s, int f, PyObject *code, PyObject *c, PyObject* n, PyObject *v, PyObject *fv, PyObject *cell, PyObject* fn, PyObject *name, int fline, PyObject *lnos) { PyObject *exception_table = NULL; PyObject *types_module=NULL, *code_type=NULL, *result=NULL; #if __PYX_LIMITED_VERSION_HEX < 0x030B0000 PyObject *version_info; PyObject *py_minor_version = NULL; #endif long minor_version = 0; PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); #if __PYX_LIMITED_VERSION_HEX >= 0x030B0000 minor_version = 11; #else if (!(version_info = PySys_GetObject("version_info"))) goto end; if (!(py_minor_version = PySequence_GetItem(version_info, 1))) goto end; minor_version = PyLong_AsLong(py_minor_version); Py_DECREF(py_minor_version); if (minor_version == -1 && PyErr_Occurred()) goto end; #endif if (!(types_module = PyImport_ImportModule("types"))) goto end; if (!(code_type = PyObject_GetAttrString(types_module, "CodeType"))) goto end; if (minor_version <= 7) { (void)p; result = PyObject_CallFunction(code_type, "iiiiiOOOOOOiOO", a, k, l, s, f, code, c, n, v, fn, name, fline, lnos, fv, cell); } else if (minor_version <= 10) { result = PyObject_CallFunction(code_type, "iiiiiiOOOOOOiOO", a,p, k, l, s, f, code, c, n, v, fn, name, fline, lnos, fv, cell); } else { if (!(exception_table = PyBytes_FromStringAndSize(NULL, 0))) goto end; result = PyObject_CallFunction(code_type, "iiiiiiOOOOOOOiOO", a,p, k, l, s, f, code, c, n, v, fn, name, name, fline, lnos, exception_table, fv, cell); } end: Py_XDECREF(code_type); Py_XDECREF(exception_table); Py_XDECREF(types_module); if (type) { PyErr_Restore(type, value, traceback); } return result; } #ifndef CO_OPTIMIZED #define CO_OPTIMIZED 0x0001 #endif #ifndef CO_NEWLOCALS #define CO_NEWLOCALS 0x0002 #endif #ifndef CO_VARARGS #define CO_VARARGS 0x0004 #endif #ifndef CO_VARKEYWORDS #define CO_VARKEYWORDS 0x0008 #endif #ifndef CO_ASYNC_GENERATOR #define CO_ASYNC_GENERATOR 0x0200 #endif #ifndef CO_GENERATOR #define CO_GENERATOR 0x0020 #endif #ifndef CO_COROUTINE #define CO_COROUTINE 0x0080 #endif #elif PY_VERSION_HEX >= 0x030B0000 static CYTHON_INLINE PyCodeObject* __Pyx_PyCode_New(int a, int p, int k, int l, int s, int f, PyObject *code, PyObject *c, PyObject* n, PyObject *v, PyObject *fv, PyObject *cell, PyObject* fn, PyObject *name, int fline, PyObject *lnos) { PyCodeObject *result; PyObject *empty_bytes = PyBytes_FromStringAndSize("", 0); if (!empty_bytes) return NULL; result = #if PY_VERSION_HEX >= 0x030C0000 PyUnstable_Code_NewWithPosOnlyArgs #else PyCode_NewWithPosOnlyArgs #endif (a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, name, fline, lnos, empty_bytes); Py_DECREF(empty_bytes); return result; } #elif PY_VERSION_HEX >= 0x030800B2 && !CYTHON_COMPILING_IN_PYPY #define __Pyx_PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\ PyCode_NewWithPosOnlyArgs(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) #else #define __Pyx_PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\ PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) #endif #endif #if PY_VERSION_HEX >= 0x030900A4 || defined(Py_IS_TYPE) #define __Pyx_IS_TYPE(ob, type) Py_IS_TYPE(ob, type) #else #define __Pyx_IS_TYPE(ob, type) (((const PyObject*)ob)->ob_type == (type)) #endif #if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_Is) #define __Pyx_Py_Is(x, y) Py_Is(x, y) #else #define __Pyx_Py_Is(x, y) ((x) == (y)) #endif #if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsNone) #define __Pyx_Py_IsNone(ob) Py_IsNone(ob) #else #define __Pyx_Py_IsNone(ob) __Pyx_Py_Is((ob), Py_None) #endif #if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsTrue) #define __Pyx_Py_IsTrue(ob) Py_IsTrue(ob) #else #define __Pyx_Py_IsTrue(ob) __Pyx_Py_Is((ob), Py_True) #endif #if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsFalse) #define __Pyx_Py_IsFalse(ob) Py_IsFalse(ob) #else #define __Pyx_Py_IsFalse(ob) __Pyx_Py_Is((ob), Py_False) #endif #define __Pyx_NoneAsNull(obj) (__Pyx_Py_IsNone(obj) ? NULL : (obj)) #if PY_VERSION_HEX >= 0x030900F0 && !CYTHON_COMPILING_IN_PYPY #define __Pyx_PyObject_GC_IsFinalized(o) PyObject_GC_IsFinalized(o) #else #define __Pyx_PyObject_GC_IsFinalized(o) _PyGC_FINALIZED(o) #endif #ifndef CO_COROUTINE #define CO_COROUTINE 0x80 #endif #ifndef CO_ASYNC_GENERATOR #define CO_ASYNC_GENERATOR 0x200 #endif #ifndef Py_TPFLAGS_CHECKTYPES #define Py_TPFLAGS_CHECKTYPES 0 #endif #ifndef Py_TPFLAGS_HAVE_INDEX #define Py_TPFLAGS_HAVE_INDEX 0 #endif #ifndef Py_TPFLAGS_HAVE_NEWBUFFER #define Py_TPFLAGS_HAVE_NEWBUFFER 0 #endif #ifndef Py_TPFLAGS_HAVE_FINALIZE #define Py_TPFLAGS_HAVE_FINALIZE 0 #endif #ifndef Py_TPFLAGS_SEQUENCE #define Py_TPFLAGS_SEQUENCE 0 #endif #ifndef Py_TPFLAGS_MAPPING #define Py_TPFLAGS_MAPPING 0 #endif #ifndef METH_STACKLESS #define METH_STACKLESS 0 #endif #if PY_VERSION_HEX <= 0x030700A3 || !defined(METH_FASTCALL) #ifndef METH_FASTCALL #define METH_FASTCALL 0x80 #endif typedef PyObject *(*__Pyx_PyCFunctionFast) (PyObject *self, PyObject *const *args, Py_ssize_t nargs); typedef PyObject *(*__Pyx_PyCFunctionFastWithKeywords) (PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames); #else #if PY_VERSION_HEX >= 0x030d00A4 # define __Pyx_PyCFunctionFast PyCFunctionFast # define __Pyx_PyCFunctionFastWithKeywords PyCFunctionFastWithKeywords #else # define __Pyx_PyCFunctionFast _PyCFunctionFast # define __Pyx_PyCFunctionFastWithKeywords _PyCFunctionFastWithKeywords #endif #endif #if CYTHON_METH_FASTCALL #define __Pyx_METH_FASTCALL METH_FASTCALL #define __Pyx_PyCFunction_FastCall __Pyx_PyCFunctionFast #define __Pyx_PyCFunction_FastCallWithKeywords __Pyx_PyCFunctionFastWithKeywords #else #define __Pyx_METH_FASTCALL METH_VARARGS #define __Pyx_PyCFunction_FastCall PyCFunction #define __Pyx_PyCFunction_FastCallWithKeywords PyCFunctionWithKeywords #endif #if CYTHON_VECTORCALL #define __pyx_vectorcallfunc vectorcallfunc #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET PY_VECTORCALL_ARGUMENTS_OFFSET #define __Pyx_PyVectorcall_NARGS(n) PyVectorcall_NARGS((size_t)(n)) #elif CYTHON_BACKPORT_VECTORCALL typedef PyObject *(*__pyx_vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames); #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET ((size_t)1 << (8 * sizeof(size_t) - 1)) #define __Pyx_PyVectorcall_NARGS(n) ((Py_ssize_t)(((size_t)(n)) & ~__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET)) #else #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET 0 #define __Pyx_PyVectorcall_NARGS(n) ((Py_ssize_t)(n)) #endif #if PY_MAJOR_VERSION >= 0x030900B1 #define __Pyx_PyCFunction_CheckExact(func) PyCFunction_CheckExact(func) #else #define __Pyx_PyCFunction_CheckExact(func) PyCFunction_Check(func) #endif #define __Pyx_CyOrPyCFunction_Check(func) PyCFunction_Check(func) #if CYTHON_COMPILING_IN_CPYTHON #define __Pyx_CyOrPyCFunction_GET_FUNCTION(func) (((PyCFunctionObject*)(func))->m_ml->ml_meth) #elif !CYTHON_COMPILING_IN_LIMITED_API #define __Pyx_CyOrPyCFunction_GET_FUNCTION(func) PyCFunction_GET_FUNCTION(func) #endif #if CYTHON_COMPILING_IN_CPYTHON #define __Pyx_CyOrPyCFunction_GET_FLAGS(func) (((PyCFunctionObject*)(func))->m_ml->ml_flags) static CYTHON_INLINE PyObject* __Pyx_CyOrPyCFunction_GET_SELF(PyObject *func) { return (__Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_STATIC) ? NULL : ((PyCFunctionObject*)func)->m_self; } #endif static CYTHON_INLINE int __Pyx__IsSameCFunction(PyObject *func, void *cfunc) { #if CYTHON_COMPILING_IN_LIMITED_API return PyCFunction_Check(func) && PyCFunction_GetFunction(func) == (PyCFunction) cfunc; #else return PyCFunction_Check(func) && PyCFunction_GET_FUNCTION(func) == (PyCFunction) cfunc; #endif } #define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCFunction(func, cfunc) #if __PYX_LIMITED_VERSION_HEX < 0x030900B1 #define __Pyx_PyType_FromModuleAndSpec(m, s, b) ((void)m, PyType_FromSpecWithBases(s, b)) typedef PyObject *(*__Pyx_PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *, size_t, PyObject *); #else #define __Pyx_PyType_FromModuleAndSpec(m, s, b) PyType_FromModuleAndSpec(m, s, b) #define __Pyx_PyCMethod PyCMethod #endif #ifndef METH_METHOD #define METH_METHOD 0x200 #endif #if CYTHON_COMPILING_IN_PYPY && !defined(PyObject_Malloc) #define PyObject_Malloc(s) PyMem_Malloc(s) #define PyObject_Free(p) PyMem_Free(p) #define PyObject_Realloc(p) PyMem_Realloc(p) #endif #if CYTHON_COMPILING_IN_LIMITED_API #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0) #define __Pyx_PyFrame_SetLineNumber(frame, lineno) #else #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0) #define __Pyx_PyFrame_SetLineNumber(frame, lineno) (frame)->f_lineno = (lineno) #endif #if CYTHON_COMPILING_IN_LIMITED_API #define __Pyx_PyThreadState_Current PyThreadState_Get() #elif !CYTHON_FAST_THREAD_STATE #define __Pyx_PyThreadState_Current PyThreadState_GET() #elif PY_VERSION_HEX >= 0x030d00A1 #define __Pyx_PyThreadState_Current PyThreadState_GetUnchecked() #elif PY_VERSION_HEX >= 0x03060000 #define __Pyx_PyThreadState_Current _PyThreadState_UncheckedGet() #elif PY_VERSION_HEX >= 0x03000000 #define __Pyx_PyThreadState_Current PyThreadState_GET() #else #define __Pyx_PyThreadState_Current _PyThreadState_Current #endif #if CYTHON_COMPILING_IN_LIMITED_API static CYTHON_INLINE void *__Pyx_PyModule_GetState(PyObject *op) { void *result; result = PyModule_GetState(op); if (!result) Py_FatalError("Couldn't find the module state"); return result; } #endif #define __Pyx_PyObject_GetSlot(obj, name, func_ctype) __Pyx_PyType_GetSlot(Py_TYPE(obj), name, func_ctype) #if CYTHON_COMPILING_IN_LIMITED_API #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((func_ctype) PyType_GetSlot((type), Py_##name)) #else #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((type)->name) #endif #if PY_VERSION_HEX < 0x030700A2 && !defined(PyThread_tss_create) && !defined(Py_tss_NEEDS_INIT) #include "pythread.h" #define Py_tss_NEEDS_INIT 0 typedef int Py_tss_t; static CYTHON_INLINE int PyThread_tss_create(Py_tss_t *key) { *key = PyThread_create_key(); return 0; } static CYTHON_INLINE Py_tss_t * PyThread_tss_alloc(void) { Py_tss_t *key = (Py_tss_t *)PyObject_Malloc(sizeof(Py_tss_t)); *key = Py_tss_NEEDS_INIT; return key; } static CYTHON_INLINE void PyThread_tss_free(Py_tss_t *key) { PyObject_Free(key); } static CYTHON_INLINE int PyThread_tss_is_created(Py_tss_t *key) { return *key != Py_tss_NEEDS_INIT; } static CYTHON_INLINE void PyThread_tss_delete(Py_tss_t *key) { PyThread_delete_key(*key); *key = Py_tss_NEEDS_INIT; } static CYTHON_INLINE int PyThread_tss_set(Py_tss_t *key, void *value) { return PyThread_set_key_value(*key, value); } static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) { return PyThread_get_key_value(*key); } #endif #if PY_MAJOR_VERSION < 3 #if CYTHON_COMPILING_IN_PYPY #if PYPY_VERSION_NUM < 0x07030600 #if defined(__cplusplus) && __cplusplus >= 201402L [[deprecated("`with nogil:` inside a nogil function will not release the GIL in PyPy2 < 7.3.6")]] #elif defined(__GNUC__) || defined(__clang__) __attribute__ ((__deprecated__("`with nogil:` inside a nogil function will not release the GIL in PyPy2 < 7.3.6"))) #elif defined(_MSC_VER) __declspec(deprecated("`with nogil:` inside a nogil function will not release the GIL in PyPy2 < 7.3.6")) #endif static CYTHON_INLINE int PyGILState_Check(void) { return 0; } #else // PYPY_VERSION_NUM < 0x07030600 #endif // PYPY_VERSION_NUM < 0x07030600 #else static CYTHON_INLINE int PyGILState_Check(void) { PyThreadState * tstate = _PyThreadState_Current; return tstate && (tstate == PyGILState_GetThisThreadState()); } #endif #endif #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000 || defined(_PyDict_NewPresized) #define __Pyx_PyDict_NewPresized(n) ((n <= 8) ? PyDict_New() : _PyDict_NewPresized(n)) #else #define __Pyx_PyDict_NewPresized(n) PyDict_New() #endif #if PY_MAJOR_VERSION >= 3 || CYTHON_FUTURE_DIVISION #define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y) #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceTrueDivide(x,y) #else #define __Pyx_PyNumber_Divide(x,y) PyNumber_Divide(x,y) #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceDivide(x,y) #endif #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX > 0x030600B4 && PY_VERSION_HEX < 0x030d0000 && CYTHON_USE_UNICODE_INTERNALS #define __Pyx_PyDict_GetItemStrWithError(dict, name) _PyDict_GetItem_KnownHash(dict, name, ((PyASCIIObject *) name)->hash) static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStr(PyObject *dict, PyObject *name) { PyObject *res = __Pyx_PyDict_GetItemStrWithError(dict, name); if (res == NULL) PyErr_Clear(); return res; } #elif PY_MAJOR_VERSION >= 3 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07020000) #define __Pyx_PyDict_GetItemStrWithError PyDict_GetItemWithError #define __Pyx_PyDict_GetItemStr PyDict_GetItem #else static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, PyObject *name) { #if CYTHON_COMPILING_IN_PYPY return PyDict_GetItem(dict, name); #else PyDictEntry *ep; PyDictObject *mp = (PyDictObject*) dict; long hash = ((PyStringObject *) name)->ob_shash; assert(hash != -1); ep = (mp->ma_lookup)(mp, name, hash); if (ep == NULL) { return NULL; } return ep->me_value; #endif } #define __Pyx_PyDict_GetItemStr PyDict_GetItem #endif #if CYTHON_USE_TYPE_SLOTS #define __Pyx_PyType_GetFlags(tp) (((PyTypeObject *)tp)->tp_flags) #define __Pyx_PyType_HasFeature(type, feature) ((__Pyx_PyType_GetFlags(type) & (feature)) != 0) #define __Pyx_PyObject_GetIterNextFunc(obj) (Py_TYPE(obj)->tp_iternext) #else #define __Pyx_PyType_GetFlags(tp) (PyType_GetFlags((PyTypeObject *)tp)) #define __Pyx_PyType_HasFeature(type, feature) PyType_HasFeature(type, feature) #define __Pyx_PyObject_GetIterNextFunc(obj) PyIter_Next #endif #if CYTHON_COMPILING_IN_LIMITED_API #define __Pyx_SetItemOnTypeDict(tp, k, v) PyObject_GenericSetAttr((PyObject*)tp, k, v) #else #define __Pyx_SetItemOnTypeDict(tp, k, v) PyDict_SetItem(tp->tp_dict, k, v) #endif #if CYTHON_USE_TYPE_SPECS && PY_VERSION_HEX >= 0x03080000 #define __Pyx_PyHeapTypeObject_GC_Del(obj) {\ PyTypeObject *type = Py_TYPE((PyObject*)obj);\ assert(__Pyx_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE));\ PyObject_GC_Del(obj);\ Py_DECREF(type);\ } #else #define __Pyx_PyHeapTypeObject_GC_Del(obj) PyObject_GC_Del(obj) #endif #if CYTHON_COMPILING_IN_LIMITED_API #define CYTHON_PEP393_ENABLED 1 #define __Pyx_PyUnicode_READY(op) (0) #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GetLength(u) #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_ReadChar(u, i) #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) ((void)u, 1114111U) #define __Pyx_PyUnicode_KIND(u) ((void)u, (0)) #define __Pyx_PyUnicode_DATA(u) ((void*)u) #define __Pyx_PyUnicode_READ(k, d, i) ((void)k, PyUnicode_ReadChar((PyObject*)(d), i)) #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GetLength(u)) #elif PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND) #define CYTHON_PEP393_ENABLED 1 #if PY_VERSION_HEX >= 0x030C0000 #define __Pyx_PyUnicode_READY(op) (0) #else #define __Pyx_PyUnicode_READY(op) (likely(PyUnicode_IS_READY(op)) ?\ 0 : _PyUnicode_Ready((PyObject *)(op))) #endif #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_LENGTH(u) #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i) #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) PyUnicode_MAX_CHAR_VALUE(u) #define __Pyx_PyUnicode_KIND(u) ((int)PyUnicode_KIND(u)) #define __Pyx_PyUnicode_DATA(u) PyUnicode_DATA(u) #define __Pyx_PyUnicode_READ(k, d, i) PyUnicode_READ(k, d, i) #define __Pyx_PyUnicode_WRITE(k, d, i, ch) PyUnicode_WRITE(k, d, i, (Py_UCS4) ch) #if PY_VERSION_HEX >= 0x030C0000 #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_LENGTH(u)) #else #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03090000 #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : ((PyCompactUnicodeObject *)(u))->wstr_length)) #else #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u))) #endif #endif #else #define CYTHON_PEP393_ENABLED 0 #define PyUnicode_1BYTE_KIND 1 #define PyUnicode_2BYTE_KIND 2 #define PyUnicode_4BYTE_KIND 4 #define __Pyx_PyUnicode_READY(op) (0) #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_SIZE(u) #define __Pyx_PyUnicode_READ_CHAR(u, i) ((Py_UCS4)(PyUnicode_AS_UNICODE(u)[i])) #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) ((sizeof(Py_UNICODE) == 2) ? 65535U : 1114111U) #define __Pyx_PyUnicode_KIND(u) ((int)sizeof(Py_UNICODE)) #define __Pyx_PyUnicode_DATA(u) ((void*)PyUnicode_AS_UNICODE(u)) #define __Pyx_PyUnicode_READ(k, d, i) ((void)(k), (Py_UCS4)(((Py_UNICODE*)d)[i])) #define __Pyx_PyUnicode_WRITE(k, d, i, ch) (((void)(k)), ((Py_UNICODE*)d)[i] = (Py_UNICODE) ch) #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_SIZE(u)) #endif #if CYTHON_COMPILING_IN_PYPY #define __Pyx_PyUnicode_Concat(a, b) PyNumber_Add(a, b) #define __Pyx_PyUnicode_ConcatSafe(a, b) PyNumber_Add(a, b) #else #define __Pyx_PyUnicode_Concat(a, b) PyUnicode_Concat(a, b) #define __Pyx_PyUnicode_ConcatSafe(a, b) ((unlikely((a) == Py_None) || unlikely((b) == Py_None)) ?\ PyNumber_Add(a, b) : __Pyx_PyUnicode_Concat(a, b)) #endif #if CYTHON_COMPILING_IN_PYPY #if !defined(PyUnicode_DecodeUnicodeEscape) #define PyUnicode_DecodeUnicodeEscape(s, size, errors) PyUnicode_Decode(s, size, "unicode_escape", errors) #endif #if !defined(PyUnicode_Contains) || (PY_MAJOR_VERSION == 2 && PYPY_VERSION_NUM < 0x07030500) #undef PyUnicode_Contains #define PyUnicode_Contains(u, s) PySequence_Contains(u, s) #endif #if !defined(PyByteArray_Check) #define PyByteArray_Check(obj) PyObject_TypeCheck(obj, &PyByteArray_Type) #endif #if !defined(PyObject_Format) #define PyObject_Format(obj, fmt) PyObject_CallMethod(obj, "__format__", "O", fmt) #endif #endif #define __Pyx_PyString_FormatSafe(a, b) ((unlikely((a) == Py_None || (PyString_Check(b) && !PyString_CheckExact(b)))) ? PyNumber_Remainder(a, b) : __Pyx_PyString_Format(a, b)) #define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None || (PyUnicode_Check(b) && !PyUnicode_CheckExact(b)))) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b)) #if PY_MAJOR_VERSION >= 3 #define __Pyx_PyString_Format(a, b) PyUnicode_Format(a, b) #else #define __Pyx_PyString_Format(a, b) PyString_Format(a, b) #endif #if PY_MAJOR_VERSION < 3 && !defined(PyObject_ASCII) #define PyObject_ASCII(o) PyObject_Repr(o) #endif #if PY_MAJOR_VERSION >= 3 #define PyBaseString_Type PyUnicode_Type #define PyStringObject PyUnicodeObject #define PyString_Type PyUnicode_Type #define PyString_Check PyUnicode_Check #define PyString_CheckExact PyUnicode_CheckExact #ifndef PyObject_Unicode #define PyObject_Unicode PyObject_Str #endif #endif #if PY_MAJOR_VERSION >= 3 #define __Pyx_PyBaseString_Check(obj) PyUnicode_Check(obj) #define __Pyx_PyBaseString_CheckExact(obj) PyUnicode_CheckExact(obj) #else #define __Pyx_PyBaseString_Check(obj) (PyString_Check(obj) || PyUnicode_Check(obj)) #define __Pyx_PyBaseString_CheckExact(obj) (PyString_CheckExact(obj) || PyUnicode_CheckExact(obj)) #endif #if CYTHON_COMPILING_IN_CPYTHON #define __Pyx_PySequence_ListKeepNew(obj)\ (likely(PyList_CheckExact(obj) && Py_REFCNT(obj) == 1) ? __Pyx_NewRef(obj) : PySequence_List(obj)) #else #define __Pyx_PySequence_ListKeepNew(obj) PySequence_List(obj) #endif #ifndef PySet_CheckExact #define PySet_CheckExact(obj) __Pyx_IS_TYPE(obj, &PySet_Type) #endif #if PY_VERSION_HEX >= 0x030900A4 #define __Pyx_SET_REFCNT(obj, refcnt) Py_SET_REFCNT(obj, refcnt) #define __Pyx_SET_SIZE(obj, size) Py_SET_SIZE(obj, size) #else #define __Pyx_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt) #define __Pyx_SET_SIZE(obj, size) Py_SIZE(obj) = (size) #endif #if CYTHON_ASSUME_SAFE_MACROS #define __Pyx_PySequence_ITEM(o, i) PySequence_ITEM(o, i) #define __Pyx_PySequence_SIZE(seq) Py_SIZE(seq) #define __Pyx_PyTuple_SET_ITEM(o, i, v) (PyTuple_SET_ITEM(o, i, v), (0)) #define __Pyx_PyList_SET_ITEM(o, i, v) (PyList_SET_ITEM(o, i, v), (0)) #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_GET_SIZE(o) #define __Pyx_PyList_GET_SIZE(o) PyList_GET_SIZE(o) #define __Pyx_PySet_GET_SIZE(o) PySet_GET_SIZE(o) #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o) #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_GET_SIZE(o) #else #define __Pyx_PySequence_ITEM(o, i) PySequence_GetItem(o, i) #define __Pyx_PySequence_SIZE(seq) PySequence_Size(seq) #define __Pyx_PyTuple_SET_ITEM(o, i, v) PyTuple_SetItem(o, i, v) #define __Pyx_PyList_SET_ITEM(o, i, v) PyList_SetItem(o, i, v) #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_Size(o) #define __Pyx_PyList_GET_SIZE(o) PyList_Size(o) #define __Pyx_PySet_GET_SIZE(o) PySet_Size(o) #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_Size(o) #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_Size(o) #endif #if __PYX_LIMITED_VERSION_HEX >= 0x030d00A1 #define __Pyx_PyImport_AddModuleRef(name) PyImport_AddModuleRef(name) #else static CYTHON_INLINE PyObject *__Pyx_PyImport_AddModuleRef(const char *name) { PyObject *module = PyImport_AddModule(name); Py_XINCREF(module); return module; } #endif #if PY_MAJOR_VERSION >= 3 #define PyIntObject PyLongObject #define PyInt_Type PyLong_Type #define PyInt_Check(op) PyLong_Check(op) #define PyInt_CheckExact(op) PyLong_CheckExact(op) #define __Pyx_Py3Int_Check(op) PyLong_Check(op) #define __Pyx_Py3Int_CheckExact(op) PyLong_CheckExact(op) #define PyInt_FromString PyLong_FromString #define PyInt_FromUnicode PyLong_FromUnicode #define PyInt_FromLong PyLong_FromLong #define PyInt_FromSize_t PyLong_FromSize_t #define PyInt_FromSsize_t PyLong_FromSsize_t #define PyInt_AsLong PyLong_AsLong #define PyInt_AS_LONG PyLong_AS_LONG #define PyInt_AsSsize_t PyLong_AsSsize_t #define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask #define PyNumber_Int PyNumber_Long #else #define __Pyx_Py3Int_Check(op) (PyLong_Check(op) || PyInt_Check(op)) #define __Pyx_Py3Int_CheckExact(op) (PyLong_CheckExact(op) || PyInt_CheckExact(op)) #endif #if PY_MAJOR_VERSION >= 3 #define PyBoolObject PyLongObject #endif #if PY_MAJOR_VERSION >= 3 && CYTHON_COMPILING_IN_PYPY #ifndef PyUnicode_InternFromString #define PyUnicode_InternFromString(s) PyUnicode_FromString(s) #endif #endif #if PY_VERSION_HEX < 0x030200A4 typedef long Py_hash_t; #define __Pyx_PyInt_FromHash_t PyInt_FromLong #define __Pyx_PyInt_AsHash_t __Pyx_PyIndex_AsHash_t #else #define __Pyx_PyInt_FromHash_t PyInt_FromSsize_t #define __Pyx_PyInt_AsHash_t __Pyx_PyIndex_AsSsize_t #endif #if CYTHON_USE_ASYNC_SLOTS #if PY_VERSION_HEX >= 0x030500B1 #define __Pyx_PyAsyncMethodsStruct PyAsyncMethods #define __Pyx_PyType_AsAsync(obj) (Py_TYPE(obj)->tp_as_async) #else #define __Pyx_PyType_AsAsync(obj) ((__Pyx_PyAsyncMethodsStruct*) (Py_TYPE(obj)->tp_reserved)) #endif #else #define __Pyx_PyType_AsAsync(obj) NULL #endif #ifndef __Pyx_PyAsyncMethodsStruct typedef struct { unaryfunc am_await; unaryfunc am_aiter; unaryfunc am_anext; } __Pyx_PyAsyncMethodsStruct; #endif #if defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS) #if !defined(_USE_MATH_DEFINES) #define _USE_MATH_DEFINES #endif #endif #include #ifdef NAN #define __PYX_NAN() ((float) NAN) #else static CYTHON_INLINE float __PYX_NAN() { float value; memset(&value, 0xFF, sizeof(value)); return value; } #endif #if defined(__CYGWIN__) && defined(_LDBL_EQ_DBL) #define __Pyx_truncl trunc #else #define __Pyx_truncl truncl #endif #define __PYX_MARK_ERR_POS(f_index, lineno) \ { __pyx_filename = __pyx_f[f_index]; (void)__pyx_filename; __pyx_lineno = lineno; (void)__pyx_lineno; __pyx_clineno = __LINE__; (void)__pyx_clineno; } #define __PYX_ERR(f_index, lineno, Ln_error) \ { __PYX_MARK_ERR_POS(f_index, lineno) goto Ln_error; } #ifdef CYTHON_EXTERN_C #undef __PYX_EXTERN_C #define __PYX_EXTERN_C CYTHON_EXTERN_C #elif defined(__PYX_EXTERN_C) #ifdef _MSC_VER #pragma message ("Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.") #else #warning Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead. #endif #else #ifdef __cplusplus #define __PYX_EXTERN_C extern "C" #else #define __PYX_EXTERN_C extern #endif #endif #define __PYX_HAVE__borg__algorithms__checksums #define __PYX_HAVE_API__borg__algorithms__checksums /* Early includes */ #include #include #include #include "crc32_dispatch.c" #include "xxhash-libselect.h" #ifdef _OPENMP #include #endif /* _OPENMP */ #if defined(PYREX_WITHOUT_ASSERTIONS) && !defined(CYTHON_WITHOUT_ASSERTIONS) #define CYTHON_WITHOUT_ASSERTIONS #endif typedef struct {PyObject **p; const char *s; const Py_ssize_t n; const char* encoding; const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry; #define __PYX_DEFAULT_STRING_ENCODING_IS_ASCII 0 #define __PYX_DEFAULT_STRING_ENCODING_IS_UTF8 0 #define __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT (PY_MAJOR_VERSION >= 3 && __PYX_DEFAULT_STRING_ENCODING_IS_UTF8) #define __PYX_DEFAULT_STRING_ENCODING "" #define __Pyx_PyObject_FromString __Pyx_PyBytes_FromString #define __Pyx_PyObject_FromStringAndSize __Pyx_PyBytes_FromStringAndSize #define __Pyx_uchar_cast(c) ((unsigned char)c) #define __Pyx_long_cast(x) ((long)x) #define __Pyx_fits_Py_ssize_t(v, type, is_signed) (\ (sizeof(type) < sizeof(Py_ssize_t)) ||\ (sizeof(type) > sizeof(Py_ssize_t) &&\ likely(v < (type)PY_SSIZE_T_MAX ||\ v == (type)PY_SSIZE_T_MAX) &&\ (!is_signed || likely(v > (type)PY_SSIZE_T_MIN ||\ v == (type)PY_SSIZE_T_MIN))) ||\ (sizeof(type) == sizeof(Py_ssize_t) &&\ (is_signed || likely(v < (type)PY_SSIZE_T_MAX ||\ v == (type)PY_SSIZE_T_MAX))) ) static CYTHON_INLINE int __Pyx_is_valid_index(Py_ssize_t i, Py_ssize_t limit) { return (size_t) i < (size_t) limit; } #if defined (__cplusplus) && __cplusplus >= 201103L #include #define __Pyx_sst_abs(value) std::abs(value) #elif SIZEOF_INT >= SIZEOF_SIZE_T #define __Pyx_sst_abs(value) abs(value) #elif SIZEOF_LONG >= SIZEOF_SIZE_T #define __Pyx_sst_abs(value) labs(value) #elif defined (_MSC_VER) #define __Pyx_sst_abs(value) ((Py_ssize_t)_abs64(value)) #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define __Pyx_sst_abs(value) llabs(value) #elif defined (__GNUC__) #define __Pyx_sst_abs(value) __builtin_llabs(value) #else #define __Pyx_sst_abs(value) ((value<0) ? -value : value) #endif static CYTHON_INLINE Py_ssize_t __Pyx_ssize_strlen(const char *s); static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject*); static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length); static CYTHON_INLINE PyObject* __Pyx_PyByteArray_FromString(const char*); #define __Pyx_PyByteArray_FromStringAndSize(s, l) PyByteArray_FromStringAndSize((const char*)s, l) #define __Pyx_PyBytes_FromString PyBytes_FromString #define __Pyx_PyBytes_FromStringAndSize PyBytes_FromStringAndSize static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char*); #if PY_MAJOR_VERSION < 3 #define __Pyx_PyStr_FromString __Pyx_PyBytes_FromString #define __Pyx_PyStr_FromStringAndSize __Pyx_PyBytes_FromStringAndSize #else #define __Pyx_PyStr_FromString __Pyx_PyUnicode_FromString #define __Pyx_PyStr_FromStringAndSize __Pyx_PyUnicode_FromStringAndSize #endif #define __Pyx_PyBytes_AsWritableString(s) ((char*) PyBytes_AS_STRING(s)) #define __Pyx_PyBytes_AsWritableSString(s) ((signed char*) PyBytes_AS_STRING(s)) #define __Pyx_PyBytes_AsWritableUString(s) ((unsigned char*) PyBytes_AS_STRING(s)) #define __Pyx_PyBytes_AsString(s) ((const char*) PyBytes_AS_STRING(s)) #define __Pyx_PyBytes_AsSString(s) ((const signed char*) PyBytes_AS_STRING(s)) #define __Pyx_PyBytes_AsUString(s) ((const unsigned char*) PyBytes_AS_STRING(s)) #define __Pyx_PyObject_AsWritableString(s) ((char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s)) #define __Pyx_PyObject_AsWritableSString(s) ((signed char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s)) #define __Pyx_PyObject_AsWritableUString(s) ((unsigned char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s)) #define __Pyx_PyObject_AsSString(s) ((const signed char*) __Pyx_PyObject_AsString(s)) #define __Pyx_PyObject_AsUString(s) ((const unsigned char*) __Pyx_PyObject_AsString(s)) #define __Pyx_PyObject_FromCString(s) __Pyx_PyObject_FromString((const char*)s) #define __Pyx_PyBytes_FromCString(s) __Pyx_PyBytes_FromString((const char*)s) #define __Pyx_PyByteArray_FromCString(s) __Pyx_PyByteArray_FromString((const char*)s) #define __Pyx_PyStr_FromCString(s) __Pyx_PyStr_FromString((const char*)s) #define __Pyx_PyUnicode_FromCString(s) __Pyx_PyUnicode_FromString((const char*)s) #define __Pyx_PyUnicode_FromOrdinal(o) PyUnicode_FromOrdinal((int)o) #define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode #define __Pyx_NewRef(obj) (Py_INCREF(obj), obj) #define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None) static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b); static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject*); static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x); #define __Pyx_PySequence_Tuple(obj)\ (likely(PyTuple_CheckExact(obj)) ? __Pyx_NewRef(obj) : PySequence_Tuple(obj)) static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*); static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t); static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*); #if CYTHON_ASSUME_SAFE_MACROS #define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) #else #define __pyx_PyFloat_AsDouble(x) PyFloat_AsDouble(x) #endif #define __pyx_PyFloat_AsFloat(x) ((float) __pyx_PyFloat_AsDouble(x)) #if PY_MAJOR_VERSION >= 3 #define __Pyx_PyNumber_Int(x) (PyLong_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Long(x)) #else #define __Pyx_PyNumber_Int(x) (PyInt_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Int(x)) #endif #if CYTHON_USE_PYLONG_INTERNALS #if PY_VERSION_HEX >= 0x030C00A7 #ifndef _PyLong_SIGN_MASK #define _PyLong_SIGN_MASK 3 #endif #ifndef _PyLong_NON_SIZE_BITS #define _PyLong_NON_SIZE_BITS 3 #endif #define __Pyx_PyLong_Sign(x) (((PyLongObject*)x)->long_value.lv_tag & _PyLong_SIGN_MASK) #define __Pyx_PyLong_IsNeg(x) ((__Pyx_PyLong_Sign(x) & 2) != 0) #define __Pyx_PyLong_IsNonNeg(x) (!__Pyx_PyLong_IsNeg(x)) #define __Pyx_PyLong_IsZero(x) (__Pyx_PyLong_Sign(x) & 1) #define __Pyx_PyLong_IsPos(x) (__Pyx_PyLong_Sign(x) == 0) #define __Pyx_PyLong_CompactValueUnsigned(x) (__Pyx_PyLong_Digits(x)[0]) #define __Pyx_PyLong_DigitCount(x) ((Py_ssize_t) (((PyLongObject*)x)->long_value.lv_tag >> _PyLong_NON_SIZE_BITS)) #define __Pyx_PyLong_SignedDigitCount(x)\ ((1 - (Py_ssize_t) __Pyx_PyLong_Sign(x)) * __Pyx_PyLong_DigitCount(x)) #if defined(PyUnstable_Long_IsCompact) && defined(PyUnstable_Long_CompactValue) #define __Pyx_PyLong_IsCompact(x) PyUnstable_Long_IsCompact((PyLongObject*) x) #define __Pyx_PyLong_CompactValue(x) PyUnstable_Long_CompactValue((PyLongObject*) x) #else #define __Pyx_PyLong_IsCompact(x) (((PyLongObject*)x)->long_value.lv_tag < (2 << _PyLong_NON_SIZE_BITS)) #define __Pyx_PyLong_CompactValue(x) ((1 - (Py_ssize_t) __Pyx_PyLong_Sign(x)) * (Py_ssize_t) __Pyx_PyLong_Digits(x)[0]) #endif typedef Py_ssize_t __Pyx_compact_pylong; typedef size_t __Pyx_compact_upylong; #else #define __Pyx_PyLong_IsNeg(x) (Py_SIZE(x) < 0) #define __Pyx_PyLong_IsNonNeg(x) (Py_SIZE(x) >= 0) #define __Pyx_PyLong_IsZero(x) (Py_SIZE(x) == 0) #define __Pyx_PyLong_IsPos(x) (Py_SIZE(x) > 0) #define __Pyx_PyLong_CompactValueUnsigned(x) ((Py_SIZE(x) == 0) ? 0 : __Pyx_PyLong_Digits(x)[0]) #define __Pyx_PyLong_DigitCount(x) __Pyx_sst_abs(Py_SIZE(x)) #define __Pyx_PyLong_SignedDigitCount(x) Py_SIZE(x) #define __Pyx_PyLong_IsCompact(x) (Py_SIZE(x) == 0 || Py_SIZE(x) == 1 || Py_SIZE(x) == -1) #define __Pyx_PyLong_CompactValue(x)\ ((Py_SIZE(x) == 0) ? (sdigit) 0 : ((Py_SIZE(x) < 0) ? -(sdigit)__Pyx_PyLong_Digits(x)[0] : (sdigit)__Pyx_PyLong_Digits(x)[0])) typedef sdigit __Pyx_compact_pylong; typedef digit __Pyx_compact_upylong; #endif #if PY_VERSION_HEX >= 0x030C00A5 #define __Pyx_PyLong_Digits(x) (((PyLongObject*)x)->long_value.ob_digit) #else #define __Pyx_PyLong_Digits(x) (((PyLongObject*)x)->ob_digit) #endif #endif #if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII #include static int __Pyx_sys_getdefaultencoding_not_ascii; static int __Pyx_init_sys_getdefaultencoding_params(void) { PyObject* sys; PyObject* default_encoding = NULL; PyObject* ascii_chars_u = NULL; PyObject* ascii_chars_b = NULL; const char* default_encoding_c; sys = PyImport_ImportModule("sys"); if (!sys) goto bad; default_encoding = PyObject_CallMethod(sys, (char*) "getdefaultencoding", NULL); Py_DECREF(sys); if (!default_encoding) goto bad; default_encoding_c = PyBytes_AsString(default_encoding); if (!default_encoding_c) goto bad; if (strcmp(default_encoding_c, "ascii") == 0) { __Pyx_sys_getdefaultencoding_not_ascii = 0; } else { char ascii_chars[128]; int c; for (c = 0; c < 128; c++) { ascii_chars[c] = (char) c; } __Pyx_sys_getdefaultencoding_not_ascii = 1; ascii_chars_u = PyUnicode_DecodeASCII(ascii_chars, 128, NULL); if (!ascii_chars_u) goto bad; ascii_chars_b = PyUnicode_AsEncodedString(ascii_chars_u, default_encoding_c, NULL); if (!ascii_chars_b || !PyBytes_Check(ascii_chars_b) || memcmp(ascii_chars, PyBytes_AS_STRING(ascii_chars_b), 128) != 0) { PyErr_Format( PyExc_ValueError, "This module compiled with c_string_encoding=ascii, but default encoding '%.200s' is not a superset of ascii.", default_encoding_c); goto bad; } Py_DECREF(ascii_chars_u); Py_DECREF(ascii_chars_b); } Py_DECREF(default_encoding); return 0; bad: Py_XDECREF(default_encoding); Py_XDECREF(ascii_chars_u); Py_XDECREF(ascii_chars_b); return -1; } #endif #if __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT && PY_MAJOR_VERSION >= 3 #define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_DecodeUTF8(c_str, size, NULL) #else #define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_Decode(c_str, size, __PYX_DEFAULT_STRING_ENCODING, NULL) #if __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT #include static char* __PYX_DEFAULT_STRING_ENCODING; static int __Pyx_init_sys_getdefaultencoding_params(void) { PyObject* sys; PyObject* default_encoding = NULL; char* default_encoding_c; sys = PyImport_ImportModule("sys"); if (!sys) goto bad; default_encoding = PyObject_CallMethod(sys, (char*) (const char*) "getdefaultencoding", NULL); Py_DECREF(sys); if (!default_encoding) goto bad; default_encoding_c = PyBytes_AsString(default_encoding); if (!default_encoding_c) goto bad; __PYX_DEFAULT_STRING_ENCODING = (char*) malloc(strlen(default_encoding_c) + 1); if (!__PYX_DEFAULT_STRING_ENCODING) goto bad; strcpy(__PYX_DEFAULT_STRING_ENCODING, default_encoding_c); Py_DECREF(default_encoding); return 0; bad: Py_XDECREF(default_encoding); return -1; } #endif #endif /* Test for GCC > 2.95 */ #if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95))) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #else /* !__GNUC__ or GCC < 2.95 */ #define likely(x) (x) #define unlikely(x) (x) #endif /* __GNUC__ */ static CYTHON_INLINE void __Pyx_pretend_to_initialize(void* ptr) { (void)ptr; } #if !CYTHON_USE_MODULE_STATE static PyObject *__pyx_m = NULL; #endif static int __pyx_lineno; static int __pyx_clineno = 0; static const char * __pyx_cfilenm = __FILE__; static const char *__pyx_filename; /* #### Code section: filename_table ### */ static const char *__pyx_f[] = { "", "src/borg/algorithms/checksums.pyx", "type.pxd", }; /* #### Code section: utility_code_proto_before_types ### */ /* ForceInitThreads.proto */ #ifndef __PYX_FORCE_INIT_THREADS #define __PYX_FORCE_INIT_THREADS 0 #endif /* #### Code section: numeric_typedefs ### */ /* #### Code section: complex_type_declarations ### */ /* #### Code section: type_declarations ### */ /*--- Type declarations ---*/ struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64; /* "borg/algorithms/checksums.pyx":84 * * * cdef class StreamingXXH64: # <<<<<<<<<<<<<< * cdef XXH64_state_t* state * */ struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 { PyObject_HEAD XXH64_state_t *state; }; /* #### Code section: utility_code_proto ### */ /* --- Runtime support code (head) --- */ /* Refnanny.proto */ #ifndef CYTHON_REFNANNY #define CYTHON_REFNANNY 0 #endif #if CYTHON_REFNANNY typedef struct { void (*INCREF)(void*, PyObject*, Py_ssize_t); void (*DECREF)(void*, PyObject*, Py_ssize_t); void (*GOTREF)(void*, PyObject*, Py_ssize_t); void (*GIVEREF)(void*, PyObject*, Py_ssize_t); void* (*SetupContext)(const char*, Py_ssize_t, const char*); void (*FinishContext)(void**); } __Pyx_RefNannyAPIStruct; static __Pyx_RefNannyAPIStruct *__Pyx_RefNanny = NULL; static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname); #define __Pyx_RefNannyDeclarations void *__pyx_refnanny = NULL; #ifdef WITH_THREAD #define __Pyx_RefNannySetupContext(name, acquire_gil)\ if (acquire_gil) {\ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__));\ PyGILState_Release(__pyx_gilstate_save);\ } else {\ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__));\ } #define __Pyx_RefNannyFinishContextNogil() {\ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\ __Pyx_RefNannyFinishContext();\ PyGILState_Release(__pyx_gilstate_save);\ } #else #define __Pyx_RefNannySetupContext(name, acquire_gil)\ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__)) #define __Pyx_RefNannyFinishContextNogil() __Pyx_RefNannyFinishContext() #endif #define __Pyx_RefNannyFinishContextNogil() {\ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\ __Pyx_RefNannyFinishContext();\ PyGILState_Release(__pyx_gilstate_save);\ } #define __Pyx_RefNannyFinishContext()\ __Pyx_RefNanny->FinishContext(&__pyx_refnanny) #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) #define __Pyx_XINCREF(r) do { if((r) == NULL); else {__Pyx_INCREF(r); }} while(0) #define __Pyx_XDECREF(r) do { if((r) == NULL); else {__Pyx_DECREF(r); }} while(0) #define __Pyx_XGOTREF(r) do { if((r) == NULL); else {__Pyx_GOTREF(r); }} while(0) #define __Pyx_XGIVEREF(r) do { if((r) == NULL); else {__Pyx_GIVEREF(r);}} while(0) #else #define __Pyx_RefNannyDeclarations #define __Pyx_RefNannySetupContext(name, acquire_gil) #define __Pyx_RefNannyFinishContextNogil() #define __Pyx_RefNannyFinishContext() #define __Pyx_INCREF(r) Py_INCREF(r) #define __Pyx_DECREF(r) Py_DECREF(r) #define __Pyx_GOTREF(r) #define __Pyx_GIVEREF(r) #define __Pyx_XINCREF(r) Py_XINCREF(r) #define __Pyx_XDECREF(r) Py_XDECREF(r) #define __Pyx_XGOTREF(r) #define __Pyx_XGIVEREF(r) #endif #define __Pyx_Py_XDECREF_SET(r, v) do {\ PyObject *tmp = (PyObject *) r;\ r = v; Py_XDECREF(tmp);\ } while (0) #define __Pyx_XDECREF_SET(r, v) do {\ PyObject *tmp = (PyObject *) r;\ r = v; __Pyx_XDECREF(tmp);\ } while (0) #define __Pyx_DECREF_SET(r, v) do {\ PyObject *tmp = (PyObject *) r;\ r = v; __Pyx_DECREF(tmp);\ } while (0) #define __Pyx_CLEAR(r) do { PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);} while(0) #define __Pyx_XCLEAR(r) do { if((r) != NULL) {PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);}} while(0) /* PyErrExceptionMatches.proto */ #if CYTHON_FAST_THREAD_STATE #define __Pyx_PyErr_ExceptionMatches(err) __Pyx_PyErr_ExceptionMatchesInState(__pyx_tstate, err) static CYTHON_INLINE int __Pyx_PyErr_ExceptionMatchesInState(PyThreadState* tstate, PyObject* err); #else #define __Pyx_PyErr_ExceptionMatches(err) PyErr_ExceptionMatches(err) #endif /* PyThreadStateGet.proto */ #if CYTHON_FAST_THREAD_STATE #define __Pyx_PyThreadState_declare PyThreadState *__pyx_tstate; #define __Pyx_PyThreadState_assign __pyx_tstate = __Pyx_PyThreadState_Current; #if PY_VERSION_HEX >= 0x030C00A6 #define __Pyx_PyErr_Occurred() (__pyx_tstate->current_exception != NULL) #define __Pyx_PyErr_CurrentExceptionType() (__pyx_tstate->current_exception ? (PyObject*) Py_TYPE(__pyx_tstate->current_exception) : (PyObject*) NULL) #else #define __Pyx_PyErr_Occurred() (__pyx_tstate->curexc_type != NULL) #define __Pyx_PyErr_CurrentExceptionType() (__pyx_tstate->curexc_type) #endif #else #define __Pyx_PyThreadState_declare #define __Pyx_PyThreadState_assign #define __Pyx_PyErr_Occurred() (PyErr_Occurred() != NULL) #define __Pyx_PyErr_CurrentExceptionType() PyErr_Occurred() #endif /* PyErrFetchRestore.proto */ #if CYTHON_FAST_THREAD_STATE #define __Pyx_PyErr_Clear() __Pyx_ErrRestore(NULL, NULL, NULL) #define __Pyx_ErrRestoreWithState(type, value, tb) __Pyx_ErrRestoreInState(PyThreadState_GET(), type, value, tb) #define __Pyx_ErrFetchWithState(type, value, tb) __Pyx_ErrFetchInState(PyThreadState_GET(), type, value, tb) #define __Pyx_ErrRestore(type, value, tb) __Pyx_ErrRestoreInState(__pyx_tstate, type, value, tb) #define __Pyx_ErrFetch(type, value, tb) __Pyx_ErrFetchInState(__pyx_tstate, type, value, tb) static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb); static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A6 #define __Pyx_PyErr_SetNone(exc) (Py_INCREF(exc), __Pyx_ErrRestore((exc), NULL, NULL)) #else #define __Pyx_PyErr_SetNone(exc) PyErr_SetNone(exc) #endif #else #define __Pyx_PyErr_Clear() PyErr_Clear() #define __Pyx_PyErr_SetNone(exc) PyErr_SetNone(exc) #define __Pyx_ErrRestoreWithState(type, value, tb) PyErr_Restore(type, value, tb) #define __Pyx_ErrFetchWithState(type, value, tb) PyErr_Fetch(type, value, tb) #define __Pyx_ErrRestoreInState(tstate, type, value, tb) PyErr_Restore(type, value, tb) #define __Pyx_ErrFetchInState(tstate, type, value, tb) PyErr_Fetch(type, value, tb) #define __Pyx_ErrRestore(type, value, tb) PyErr_Restore(type, value, tb) #define __Pyx_ErrFetch(type, value, tb) PyErr_Fetch(type, value, tb) #endif /* PyObjectGetAttrStr.proto */ #if CYTHON_USE_TYPE_SLOTS static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name); #else #define __Pyx_PyObject_GetAttrStr(o,n) PyObject_GetAttr(o,n) #endif /* PyObjectGetAttrStrNoError.proto */ static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name); /* GetBuiltinName.proto */ static PyObject *__Pyx_GetBuiltinName(PyObject *name); /* TupleAndListFromArray.proto */ #if CYTHON_COMPILING_IN_CPYTHON static CYTHON_INLINE PyObject* __Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n); static CYTHON_INLINE PyObject* __Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n); #endif /* IncludeStringH.proto */ #include /* BytesEquals.proto */ static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals); /* UnicodeEquals.proto */ static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals); /* fastcall.proto */ #if CYTHON_AVOID_BORROWED_REFS #define __Pyx_Arg_VARARGS(args, i) PySequence_GetItem(args, i) #elif CYTHON_ASSUME_SAFE_MACROS #define __Pyx_Arg_VARARGS(args, i) PyTuple_GET_ITEM(args, i) #else #define __Pyx_Arg_VARARGS(args, i) PyTuple_GetItem(args, i) #endif #if CYTHON_AVOID_BORROWED_REFS #define __Pyx_Arg_NewRef_VARARGS(arg) __Pyx_NewRef(arg) #define __Pyx_Arg_XDECREF_VARARGS(arg) Py_XDECREF(arg) #else #define __Pyx_Arg_NewRef_VARARGS(arg) arg #define __Pyx_Arg_XDECREF_VARARGS(arg) #endif #define __Pyx_NumKwargs_VARARGS(kwds) PyDict_Size(kwds) #define __Pyx_KwValues_VARARGS(args, nargs) NULL #define __Pyx_GetKwValue_VARARGS(kw, kwvalues, s) __Pyx_PyDict_GetItemStrWithError(kw, s) #define __Pyx_KwargsAsDict_VARARGS(kw, kwvalues) PyDict_Copy(kw) #if CYTHON_METH_FASTCALL #define __Pyx_Arg_FASTCALL(args, i) args[i] #define __Pyx_NumKwargs_FASTCALL(kwds) PyTuple_GET_SIZE(kwds) #define __Pyx_KwValues_FASTCALL(args, nargs) ((args) + (nargs)) static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s); #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues); #else #define __Pyx_KwargsAsDict_FASTCALL(kw, kwvalues) _PyStack_AsDict(kwvalues, kw) #endif #define __Pyx_Arg_NewRef_FASTCALL(arg) arg /* no-op, __Pyx_Arg_FASTCALL is direct and this needs to have the same reference counting */ #define __Pyx_Arg_XDECREF_FASTCALL(arg) #else #define __Pyx_Arg_FASTCALL __Pyx_Arg_VARARGS #define __Pyx_NumKwargs_FASTCALL __Pyx_NumKwargs_VARARGS #define __Pyx_KwValues_FASTCALL __Pyx_KwValues_VARARGS #define __Pyx_GetKwValue_FASTCALL __Pyx_GetKwValue_VARARGS #define __Pyx_KwargsAsDict_FASTCALL __Pyx_KwargsAsDict_VARARGS #define __Pyx_Arg_NewRef_FASTCALL(arg) __Pyx_Arg_NewRef_VARARGS(arg) #define __Pyx_Arg_XDECREF_FASTCALL(arg) __Pyx_Arg_XDECREF_VARARGS(arg) #endif #if CYTHON_COMPILING_IN_CPYTHON && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS #define __Pyx_ArgsSlice_VARARGS(args, start, stop) __Pyx_PyTuple_FromArray(&__Pyx_Arg_VARARGS(args, start), stop - start) #define __Pyx_ArgsSlice_FASTCALL(args, start, stop) __Pyx_PyTuple_FromArray(&__Pyx_Arg_FASTCALL(args, start), stop - start) #else #define __Pyx_ArgsSlice_VARARGS(args, start, stop) PyTuple_GetSlice(args, start, stop) #define __Pyx_ArgsSlice_FASTCALL(args, start, stop) PyTuple_GetSlice(args, start, stop) #endif /* RaiseDoubleKeywords.proto */ static void __Pyx_RaiseDoubleKeywordsError(const char* func_name, PyObject* kw_name); /* ParseKeywords.proto */ static int __Pyx_ParseOptionalKeywords(PyObject *kwds, PyObject *const *kwvalues, PyObject **argnames[], PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args, const char* function_name); /* RaiseArgTupleInvalid.proto */ static void __Pyx_RaiseArgtupleInvalid(const char* func_name, int exact, Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found); /* GetException.proto */ #if CYTHON_FAST_THREAD_STATE #define __Pyx_GetException(type, value, tb) __Pyx__GetException(__pyx_tstate, type, value, tb) static int __Pyx__GetException(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); #else static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb); #endif /* SwapException.proto */ #if CYTHON_FAST_THREAD_STATE #define __Pyx_ExceptionSwap(type, value, tb) __Pyx__ExceptionSwap(__pyx_tstate, type, value, tb) static CYTHON_INLINE void __Pyx__ExceptionSwap(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); #else static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb); #endif /* GetTopmostException.proto */ #if CYTHON_USE_EXC_INFO_STACK && CYTHON_FAST_THREAD_STATE static _PyErr_StackItem * __Pyx_PyErr_GetTopmostException(PyThreadState *tstate); #endif /* SaveResetException.proto */ #if CYTHON_FAST_THREAD_STATE #define __Pyx_ExceptionSave(type, value, tb) __Pyx__ExceptionSave(__pyx_tstate, type, value, tb) static CYTHON_INLINE void __Pyx__ExceptionSave(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); #define __Pyx_ExceptionReset(type, value, tb) __Pyx__ExceptionReset(__pyx_tstate, type, value, tb) static CYTHON_INLINE void __Pyx__ExceptionReset(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb); #else #define __Pyx_ExceptionSave(type, value, tb) PyErr_GetExcInfo(type, value, tb) #define __Pyx_ExceptionReset(type, value, tb) PyErr_SetExcInfo(type, value, tb) #endif /* PyObjectCall.proto */ #if CYTHON_COMPILING_IN_CPYTHON static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw); #else #define __Pyx_PyObject_Call(func, arg, kw) PyObject_Call(func, arg, kw) #endif /* RaiseException.proto */ static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause); /* KeywordStringCheck.proto */ static int __Pyx_CheckKeywordStrings(PyObject *kw, const char* function_name, int kw_allowed); /* PyDictVersioning.proto */ #if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS #define __PYX_DICT_VERSION_INIT ((PY_UINT64_T) -1) #define __PYX_GET_DICT_VERSION(dict) (((PyDictObject*)(dict))->ma_version_tag) #define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var)\ (version_var) = __PYX_GET_DICT_VERSION(dict);\ (cache_var) = (value); #define __PYX_PY_DICT_LOOKUP_IF_MODIFIED(VAR, DICT, LOOKUP) {\ static PY_UINT64_T __pyx_dict_version = 0;\ static PyObject *__pyx_dict_cached_value = NULL;\ if (likely(__PYX_GET_DICT_VERSION(DICT) == __pyx_dict_version)) {\ (VAR) = __pyx_dict_cached_value;\ } else {\ (VAR) = __pyx_dict_cached_value = (LOOKUP);\ __pyx_dict_version = __PYX_GET_DICT_VERSION(DICT);\ }\ } static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj); static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj); static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version); #else #define __PYX_GET_DICT_VERSION(dict) (0) #define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var) #define __PYX_PY_DICT_LOOKUP_IF_MODIFIED(VAR, DICT, LOOKUP) (VAR) = (LOOKUP); #endif /* GetModuleGlobalName.proto */ #if CYTHON_USE_DICT_VERSIONS #define __Pyx_GetModuleGlobalName(var, name) do {\ static PY_UINT64_T __pyx_dict_version = 0;\ static PyObject *__pyx_dict_cached_value = NULL;\ (var) = (likely(__pyx_dict_version == __PYX_GET_DICT_VERSION(__pyx_d))) ?\ (likely(__pyx_dict_cached_value) ? __Pyx_NewRef(__pyx_dict_cached_value) : __Pyx_GetBuiltinName(name)) :\ __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value);\ } while(0) #define __Pyx_GetModuleGlobalNameUncached(var, name) do {\ PY_UINT64_T __pyx_dict_version;\ PyObject *__pyx_dict_cached_value;\ (var) = __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value);\ } while(0) static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value); #else #define __Pyx_GetModuleGlobalName(var, name) (var) = __Pyx__GetModuleGlobalName(name) #define __Pyx_GetModuleGlobalNameUncached(var, name) (var) = __Pyx__GetModuleGlobalName(name) static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name); #endif /* PyFunctionFastCall.proto */ #if CYTHON_FAST_PYCALL #if !CYTHON_VECTORCALL #define __Pyx_PyFunction_FastCall(func, args, nargs)\ __Pyx_PyFunction_FastCallDict((func), (args), (nargs), NULL) static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, PyObject *kwargs); #endif #define __Pyx_BUILD_ASSERT_EXPR(cond)\ (sizeof(char [1 - 2*!(cond)]) - 1) #ifndef Py_MEMBER_SIZE #define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member) #endif #if !CYTHON_VECTORCALL #if PY_VERSION_HEX >= 0x03080000 #include "frameobject.h" #if PY_VERSION_HEX >= 0x030b00a6 && !CYTHON_COMPILING_IN_LIMITED_API #ifndef Py_BUILD_CORE #define Py_BUILD_CORE 1 #endif #include "internal/pycore_frame.h" #endif #define __Pxy_PyFrame_Initialize_Offsets() #define __Pyx_PyFrame_GetLocalsplus(frame) ((frame)->f_localsplus) #else static size_t __pyx_pyframe_localsplus_offset = 0; #include "frameobject.h" #define __Pxy_PyFrame_Initialize_Offsets()\ ((void)__Pyx_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)),\ (void)(__pyx_pyframe_localsplus_offset = ((size_t)PyFrame_Type.tp_basicsize) - Py_MEMBER_SIZE(PyFrameObject, f_localsplus))) #define __Pyx_PyFrame_GetLocalsplus(frame)\ (assert(__pyx_pyframe_localsplus_offset), (PyObject **)(((char *)(frame)) + __pyx_pyframe_localsplus_offset)) #endif #endif #endif /* PyObjectCallMethO.proto */ #if CYTHON_COMPILING_IN_CPYTHON static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg); #endif /* PyObjectFastCall.proto */ #define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(func, args, (size_t)(nargs), NULL) static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs); /* IncludeStructmemberH.proto */ #include /* FixUpExtensionType.proto */ #if CYTHON_USE_TYPE_SPECS static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject *type); #endif /* PyObjectCallNoArg.proto */ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func); /* PyObjectCallOneArg.proto */ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg); /* PyObjectGetMethod.proto */ static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method); /* PyObjectCallMethod0.proto */ static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name); /* ValidateBasesTuple.proto */ #if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_USE_TYPE_SPECS static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffset, PyObject *bases); #endif /* PyType_Ready.proto */ CYTHON_UNUSED static int __Pyx_PyType_Ready(PyTypeObject *t); /* PyObject_GenericGetAttrNoDict.proto */ #if CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP && PY_VERSION_HEX < 0x03070000 static CYTHON_INLINE PyObject* __Pyx_PyObject_GenericGetAttrNoDict(PyObject* obj, PyObject* attr_name); #else #define __Pyx_PyObject_GenericGetAttrNoDict PyObject_GenericGetAttr #endif /* PyObject_GenericGetAttr.proto */ #if CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP && PY_VERSION_HEX < 0x03070000 static PyObject* __Pyx_PyObject_GenericGetAttr(PyObject* obj, PyObject* attr_name); #else #define __Pyx_PyObject_GenericGetAttr PyObject_GenericGetAttr #endif /* SetupReduce.proto */ #if !CYTHON_COMPILING_IN_LIMITED_API static int __Pyx_setup_reduce(PyObject* type_obj); #endif /* TypeImport.proto */ #ifndef __PYX_HAVE_RT_ImportType_proto_3_0_9 #define __PYX_HAVE_RT_ImportType_proto_3_0_9 #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L #include #endif #if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || __cplusplus >= 201103L #define __PYX_GET_STRUCT_ALIGNMENT_3_0_9(s) alignof(s) #else #define __PYX_GET_STRUCT_ALIGNMENT_3_0_9(s) sizeof(void*) #endif enum __Pyx_ImportType_CheckSize_3_0_9 { __Pyx_ImportType_CheckSize_Error_3_0_9 = 0, __Pyx_ImportType_CheckSize_Warn_3_0_9 = 1, __Pyx_ImportType_CheckSize_Ignore_3_0_9 = 2 }; static PyTypeObject *__Pyx_ImportType_3_0_9(PyObject* module, const char *module_name, const char *class_name, size_t size, size_t alignment, enum __Pyx_ImportType_CheckSize_3_0_9 check_size); #endif /* Import.proto */ static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level); /* ImportFrom.proto */ static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name); /* FetchSharedCythonModule.proto */ static PyObject *__Pyx_FetchSharedCythonABIModule(void); /* FetchCommonType.proto */ #if !CYTHON_USE_TYPE_SPECS static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type); #else static PyTypeObject* __Pyx_FetchCommonTypeFromSpec(PyObject *module, PyType_Spec *spec, PyObject *bases); #endif /* PyMethodNew.proto */ #if CYTHON_COMPILING_IN_LIMITED_API static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ) { PyObject *typesModule=NULL, *methodType=NULL, *result=NULL; CYTHON_UNUSED_VAR(typ); if (!self) return __Pyx_NewRef(func); typesModule = PyImport_ImportModule("types"); if (!typesModule) return NULL; methodType = PyObject_GetAttrString(typesModule, "MethodType"); Py_DECREF(typesModule); if (!methodType) return NULL; result = PyObject_CallFunctionObjArgs(methodType, func, self, NULL); Py_DECREF(methodType); return result; } #elif PY_MAJOR_VERSION >= 3 static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ) { CYTHON_UNUSED_VAR(typ); if (!self) return __Pyx_NewRef(func); return PyMethod_New(func, self); } #else #define __Pyx_PyMethod_New PyMethod_New #endif /* PyVectorcallFastCallDict.proto */ #if CYTHON_METH_FASTCALL static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw); #endif /* CythonFunctionShared.proto */ #define __Pyx_CyFunction_USED #define __Pyx_CYFUNCTION_STATICMETHOD 0x01 #define __Pyx_CYFUNCTION_CLASSMETHOD 0x02 #define __Pyx_CYFUNCTION_CCLASS 0x04 #define __Pyx_CYFUNCTION_COROUTINE 0x08 #define __Pyx_CyFunction_GetClosure(f)\ (((__pyx_CyFunctionObject *) (f))->func_closure) #if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API #define __Pyx_CyFunction_GetClassObj(f)\ (((__pyx_CyFunctionObject *) (f))->func_classobj) #else #define __Pyx_CyFunction_GetClassObj(f)\ ((PyObject*) ((PyCMethodObject *) (f))->mm_class) #endif #define __Pyx_CyFunction_SetClassObj(f, classobj)\ __Pyx__CyFunction_SetClassObj((__pyx_CyFunctionObject *) (f), (classobj)) #define __Pyx_CyFunction_Defaults(type, f)\ ((type *)(((__pyx_CyFunctionObject *) (f))->defaults)) #define __Pyx_CyFunction_SetDefaultsGetter(f, g)\ ((__pyx_CyFunctionObject *) (f))->defaults_getter = (g) typedef struct { #if CYTHON_COMPILING_IN_LIMITED_API PyObject_HEAD PyObject *func; #elif PY_VERSION_HEX < 0x030900B1 PyCFunctionObject func; #else PyCMethodObject func; #endif #if CYTHON_BACKPORT_VECTORCALL __pyx_vectorcallfunc func_vectorcall; #endif #if PY_VERSION_HEX < 0x030500A0 || CYTHON_COMPILING_IN_LIMITED_API PyObject *func_weakreflist; #endif PyObject *func_dict; PyObject *func_name; PyObject *func_qualname; PyObject *func_doc; PyObject *func_globals; PyObject *func_code; PyObject *func_closure; #if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API PyObject *func_classobj; #endif void *defaults; int defaults_pyobjects; size_t defaults_size; int flags; PyObject *defaults_tuple; PyObject *defaults_kwdict; PyObject *(*defaults_getter)(PyObject *); PyObject *func_annotations; PyObject *func_is_coroutine; } __pyx_CyFunctionObject; #undef __Pyx_CyOrPyCFunction_Check #define __Pyx_CyFunction_Check(obj) __Pyx_TypeCheck(obj, __pyx_CyFunctionType) #define __Pyx_CyOrPyCFunction_Check(obj) __Pyx_TypeCheck2(obj, __pyx_CyFunctionType, &PyCFunction_Type) #define __Pyx_CyFunction_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_CyFunctionType) static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc); #undef __Pyx_IsSameCFunction #define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCyOrCFunction(func, cfunc) static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject* op, PyMethodDef *ml, int flags, PyObject* qualname, PyObject *closure, PyObject *module, PyObject *globals, PyObject* code); static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj); static CYTHON_INLINE void *__Pyx_CyFunction_InitDefaults(PyObject *m, size_t size, int pyobjects); static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *m, PyObject *tuple); static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *m, PyObject *dict); static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *m, PyObject *dict); static int __pyx_CyFunction_init(PyObject *module); #if CYTHON_METH_FASTCALL static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); #if CYTHON_BACKPORT_VECTORCALL #define __Pyx_CyFunction_func_vectorcall(f) (((__pyx_CyFunctionObject*)f)->func_vectorcall) #else #define __Pyx_CyFunction_func_vectorcall(f) (((PyCFunctionObject*)f)->vectorcall) #endif #endif /* CythonFunction.proto */ static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, int flags, PyObject* qualname, PyObject *closure, PyObject *module, PyObject *globals, PyObject* code); /* CLineInTraceback.proto */ #ifdef CYTHON_CLINE_IN_TRACEBACK #define __Pyx_CLineForTraceback(tstate, c_line) (((CYTHON_CLINE_IN_TRACEBACK)) ? c_line : 0) #else static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line); #endif /* CodeObjectCache.proto */ #if !CYTHON_COMPILING_IN_LIMITED_API typedef struct { PyCodeObject* code_object; int code_line; } __Pyx_CodeObjectCacheEntry; struct __Pyx_CodeObjectCache { int count; int max_count; __Pyx_CodeObjectCacheEntry* entries; }; static struct __Pyx_CodeObjectCache __pyx_code_cache = {0,0,NULL}; static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line); static PyCodeObject *__pyx_find_code_object(int code_line); static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object); #endif /* AddTraceback.proto */ static void __Pyx_AddTraceback(const char *funcname, int c_line, int py_line, const char *filename); /* GCCDiagnostics.proto */ #if !defined(__INTEL_COMPILER) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) #define __Pyx_HAS_GCC_DIAGNOSTIC #endif /* CIntToPy.proto */ static CYTHON_INLINE PyObject* __Pyx_PyInt_From_int(int value); /* CIntFromPy.proto */ static CYTHON_INLINE uint32_t __Pyx_PyInt_As_uint32_t(PyObject *); /* CIntToPy.proto */ static CYTHON_INLINE PyObject* __Pyx_PyInt_From_uint32_t(uint32_t value); /* CIntFromPy.proto */ static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_PyInt_As_unsigned_PY_LONG_LONG(PyObject *); /* FormatTypeName.proto */ #if CYTHON_COMPILING_IN_LIMITED_API typedef PyObject *__Pyx_TypeName; #define __Pyx_FMT_TYPENAME "%U" static __Pyx_TypeName __Pyx_PyType_GetName(PyTypeObject* tp); #define __Pyx_DECREF_TypeName(obj) Py_XDECREF(obj) #else typedef const char *__Pyx_TypeName; #define __Pyx_FMT_TYPENAME "%.200s" #define __Pyx_PyType_GetName(tp) ((tp)->tp_name) #define __Pyx_DECREF_TypeName(obj) #endif /* CIntToPy.proto */ static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value); /* CIntFromPy.proto */ static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *); /* CIntFromPy.proto */ static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *); /* FastTypeChecks.proto */ #if CYTHON_COMPILING_IN_CPYTHON #define __Pyx_TypeCheck(obj, type) __Pyx_IsSubtype(Py_TYPE(obj), (PyTypeObject *)type) #define __Pyx_TypeCheck2(obj, type1, type2) __Pyx_IsAnySubtype2(Py_TYPE(obj), (PyTypeObject *)type1, (PyTypeObject *)type2) static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b); static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, PyTypeObject *b); static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches(PyObject *err, PyObject *type); static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObject *type1, PyObject *type2); #else #define __Pyx_TypeCheck(obj, type) PyObject_TypeCheck(obj, (PyTypeObject *)type) #define __Pyx_TypeCheck2(obj, type1, type2) (PyObject_TypeCheck(obj, (PyTypeObject *)type1) || PyObject_TypeCheck(obj, (PyTypeObject *)type2)) #define __Pyx_PyErr_GivenExceptionMatches(err, type) PyErr_GivenExceptionMatches(err, type) #define __Pyx_PyErr_GivenExceptionMatches2(err, type1, type2) (PyErr_GivenExceptionMatches(err, type1) || PyErr_GivenExceptionMatches(err, type2)) #endif #define __Pyx_PyErr_ExceptionMatches2(err1, err2) __Pyx_PyErr_GivenExceptionMatches2(__Pyx_PyErr_CurrentExceptionType(), err1, err2) #define __Pyx_PyException_Check(obj) __Pyx_TypeCheck(obj, PyExc_Exception) /* CheckBinaryVersion.proto */ static unsigned long __Pyx_get_runtime_version(void); static int __Pyx_check_binary_version(unsigned long ct_version, unsigned long rt_version, int allow_newer); /* InitStrings.proto */ static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /* #### Code section: module_declarations ### */ /* Module declarations from "libc.stdint" */ /* Module declarations from "cpython.buffer" */ /* Module declarations from "libc.string" */ /* Module declarations from "libc.stdio" */ /* Module declarations from "__builtin__" */ /* Module declarations from "cpython.type" */ /* Module declarations from "cpython" */ /* Module declarations from "cpython.object" */ /* Module declarations from "cpython.bytes" */ /* Module declarations from "borg.algorithms.checksums" */ static Py_buffer __pyx_f_4borg_10algorithms_9checksums_ro_buffer(PyObject *); /*proto*/ /* #### Code section: typeinfo ### */ /* #### Code section: before_global_var ### */ #define __Pyx_MODULE_NAME "borg.algorithms.checksums" extern int __pyx_module_is_main_borg__algorithms__checksums; int __pyx_module_is_main_borg__algorithms__checksums = 0; /* Implementation of "borg.algorithms.checksums" */ /* #### Code section: global_var ### */ static PyObject *__pyx_builtin_TypeError; /* #### Code section: string_decls ### */ static const char __pyx_k__3[] = "."; static const char __pyx_k_gc[] = "gc"; static const char __pyx_k__19[] = "?"; static const char __pyx_k_val[] = "val"; static const char __pyx_k_data[] = "data"; static const char __pyx_k_hash[] = "hash"; static const char __pyx_k_main[] = "__main__"; static const char __pyx_k_name[] = "__name__"; static const char __pyx_k_seed[] = "seed"; static const char __pyx_k_self[] = "self"; static const char __pyx_k_test[] = "__test__"; static const char __pyx_k_crc32[] = "crc32"; static const char __pyx_k_value[] = "value"; static const char __pyx_k_xxh64[] = "xxh64"; static const char __pyx_k_digest[] = "digest"; static const char __pyx_k_enable[] = "enable"; static const char __pyx_k_import[] = "__import__"; static const char __pyx_k_reduce[] = "__reduce__"; static const char __pyx_k_seed_2[] = "_seed"; static const char __pyx_k_update[] = "update"; static const char __pyx_k_disable[] = "disable"; static const char __pyx_k_helpers[] = "helpers"; static const char __pyx_k_data_buf[] = "data_buf"; static const char __pyx_k_getstate[] = "__getstate__"; static const char __pyx_k_setstate[] = "__setstate__"; static const char __pyx_k_TypeError[] = "TypeError"; static const char __pyx_k_hexdigest[] = "hexdigest"; static const char __pyx_k_isenabled[] = "isenabled"; static const char __pyx_k_pyx_state[] = "__pyx_state"; static const char __pyx_k_reduce_ex[] = "__reduce_ex__"; static const char __pyx_k_bin_to_hex[] = "bin_to_hex"; static const char __pyx_k_have_clmul[] = "have_clmul"; static const char __pyx_k_crc32_clmul[] = "crc32_clmul"; static const char __pyx_k_is_coroutine[] = "_is_coroutine"; static const char __pyx_k_stringsource[] = ""; static const char __pyx_k_reduce_cython[] = "__reduce_cython__"; static const char __pyx_k_StreamingXXH64[] = "StreamingXXH64"; static const char __pyx_k_setstate_cython[] = "__setstate_cython__"; static const char __pyx_k_crc32_slice_by_8[] = "crc32_slice_by_8"; static const char __pyx_k_XXH64_reset_failed[] = "XXH64_reset failed"; static const char __pyx_k_asyncio_coroutines[] = "asyncio.coroutines"; static const char __pyx_k_cline_in_traceback[] = "cline_in_traceback"; static const char __pyx_k_XXH64_update_failed[] = "XXH64_update failed"; static const char __pyx_k_StreamingXXH64_digest[] = "StreamingXXH64.digest"; static const char __pyx_k_StreamingXXH64_update[] = "StreamingXXH64.update"; static const char __pyx_k_StreamingXXH64_hexdigest[] = "StreamingXXH64.hexdigest"; static const char __pyx_k_borg_algorithms_checksums[] = "borg.algorithms.checksums"; static const char __pyx_k_StreamingXXH64___reduce_cython[] = "StreamingXXH64.__reduce_cython__"; static const char __pyx_k_StreamingXXH64___setstate_cython[] = "StreamingXXH64.__setstate_cython__"; static const char __pyx_k_no_default___reduce___due_to_non[] = "no default __reduce__ due to non-trivial __cinit__"; static const char __pyx_k_src_borg_algorithms_checksums_py[] = "src/borg/algorithms/checksums.pyx"; /* #### Code section: decls ### */ static PyObject *__pyx_pf_4borg_10algorithms_9checksums_crc32_slice_by_8(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_data, PyObject *__pyx_v_value); /* proto */ static PyObject *__pyx_pf_4borg_10algorithms_9checksums_2crc32_clmul(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_data, PyObject *__pyx_v_value); /* proto */ static PyObject *__pyx_pf_4borg_10algorithms_9checksums_4xxh64(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_data, PyObject *__pyx_v_seed); /* proto */ static int __pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64___cinit__(struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self, PyObject *__pyx_v_seed); /* proto */ static void __pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_2__dealloc__(struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self); /* proto */ static PyObject *__pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_4update(struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self, PyObject *__pyx_v_data); /* proto */ static PyObject *__pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_6digest(struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self); /* proto */ static PyObject *__pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_8hexdigest(struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self); /* proto */ static PyObject *__pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_10__reduce_cython__(CYTHON_UNUSED struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self); /* proto */ static PyObject *__pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_12__setstate_cython__(CYTHON_UNUSED struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self, CYTHON_UNUSED PyObject *__pyx_v___pyx_state); /* proto */ static PyObject *__pyx_tp_new_4borg_10algorithms_9checksums_StreamingXXH64(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/ /* #### Code section: late_includes ### */ /* #### Code section: module_state ### */ typedef struct { PyObject *__pyx_d; PyObject *__pyx_b; PyObject *__pyx_cython_runtime; PyObject *__pyx_empty_tuple; PyObject *__pyx_empty_bytes; PyObject *__pyx_empty_unicode; #ifdef __Pyx_CyFunction_USED PyTypeObject *__pyx_CyFunctionType; #endif #ifdef __Pyx_FusedFunction_USED PyTypeObject *__pyx_FusedFunctionType; #endif #ifdef __Pyx_Generator_USED PyTypeObject *__pyx_GeneratorType; #endif #ifdef __Pyx_IterableCoroutine_USED PyTypeObject *__pyx_IterableCoroutineType; #endif #ifdef __Pyx_Coroutine_USED PyTypeObject *__pyx_CoroutineAwaitType; #endif #ifdef __Pyx_Coroutine_USED PyTypeObject *__pyx_CoroutineType; #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif PyTypeObject *__pyx_ptype_7cpython_4type_type; #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE PyObject *__pyx_type_4borg_10algorithms_9checksums_StreamingXXH64; #endif PyTypeObject *__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64; PyObject *__pyx_n_s_StreamingXXH64; PyObject *__pyx_n_s_StreamingXXH64___reduce_cython; PyObject *__pyx_n_s_StreamingXXH64___setstate_cython; PyObject *__pyx_n_s_StreamingXXH64_digest; PyObject *__pyx_n_s_StreamingXXH64_hexdigest; PyObject *__pyx_n_s_StreamingXXH64_update; PyObject *__pyx_n_s_TypeError; PyObject *__pyx_kp_u_XXH64_reset_failed; PyObject *__pyx_kp_u_XXH64_update_failed; PyObject *__pyx_n_s__19; PyObject *__pyx_kp_u__3; PyObject *__pyx_n_s_asyncio_coroutines; PyObject *__pyx_n_s_bin_to_hex; PyObject *__pyx_n_s_borg_algorithms_checksums; PyObject *__pyx_n_s_cline_in_traceback; PyObject *__pyx_n_s_crc32; PyObject *__pyx_n_s_crc32_clmul; PyObject *__pyx_n_s_crc32_slice_by_8; PyObject *__pyx_n_s_data; PyObject *__pyx_n_s_data_buf; PyObject *__pyx_n_s_digest; PyObject *__pyx_kp_u_disable; PyObject *__pyx_kp_u_enable; PyObject *__pyx_kp_u_gc; PyObject *__pyx_n_s_getstate; PyObject *__pyx_n_s_hash; PyObject *__pyx_n_s_have_clmul; PyObject *__pyx_n_s_helpers; PyObject *__pyx_n_s_hexdigest; PyObject *__pyx_n_s_import; PyObject *__pyx_n_s_is_coroutine; PyObject *__pyx_kp_u_isenabled; PyObject *__pyx_n_s_main; PyObject *__pyx_n_s_name; PyObject *__pyx_kp_s_no_default___reduce___due_to_non; PyObject *__pyx_n_s_pyx_state; PyObject *__pyx_n_s_reduce; PyObject *__pyx_n_s_reduce_cython; PyObject *__pyx_n_s_reduce_ex; PyObject *__pyx_n_s_seed; PyObject *__pyx_n_s_seed_2; PyObject *__pyx_n_s_self; PyObject *__pyx_n_s_setstate; PyObject *__pyx_n_s_setstate_cython; PyObject *__pyx_kp_s_src_borg_algorithms_checksums_py; PyObject *__pyx_kp_s_stringsource; PyObject *__pyx_n_s_test; PyObject *__pyx_n_s_update; PyObject *__pyx_n_s_val; PyObject *__pyx_n_s_value; PyObject *__pyx_n_s_xxh64; PyObject *__pyx_int_0; PyObject *__pyx_tuple_; PyObject *__pyx_tuple__2; PyObject *__pyx_tuple__4; PyObject *__pyx_tuple__6; PyObject *__pyx_tuple__8; PyObject *__pyx_tuple__10; PyObject *__pyx_tuple__12; PyObject *__pyx_tuple__14; PyObject *__pyx_tuple__17; PyObject *__pyx_codeobj__5; PyObject *__pyx_codeobj__7; PyObject *__pyx_codeobj__9; PyObject *__pyx_codeobj__11; PyObject *__pyx_codeobj__13; PyObject *__pyx_codeobj__15; PyObject *__pyx_codeobj__16; PyObject *__pyx_codeobj__18; } __pyx_mstate; #if CYTHON_USE_MODULE_STATE #ifdef __cplusplus namespace { extern struct PyModuleDef __pyx_moduledef; } /* anonymous namespace */ #else static struct PyModuleDef __pyx_moduledef; #endif #define __pyx_mstate(o) ((__pyx_mstate *)__Pyx_PyModule_GetState(o)) #define __pyx_mstate_global (__pyx_mstate(PyState_FindModule(&__pyx_moduledef))) #define __pyx_m (PyState_FindModule(&__pyx_moduledef)) #else static __pyx_mstate __pyx_mstate_global_static = #ifdef __cplusplus {}; #else {0}; #endif static __pyx_mstate *__pyx_mstate_global = &__pyx_mstate_global_static; #endif /* #### Code section: module_state_clear ### */ #if CYTHON_USE_MODULE_STATE static int __pyx_m_clear(PyObject *m) { __pyx_mstate *clear_module_state = __pyx_mstate(m); if (!clear_module_state) return 0; Py_CLEAR(clear_module_state->__pyx_d); Py_CLEAR(clear_module_state->__pyx_b); Py_CLEAR(clear_module_state->__pyx_cython_runtime); Py_CLEAR(clear_module_state->__pyx_empty_tuple); Py_CLEAR(clear_module_state->__pyx_empty_bytes); Py_CLEAR(clear_module_state->__pyx_empty_unicode); #ifdef __Pyx_CyFunction_USED Py_CLEAR(clear_module_state->__pyx_CyFunctionType); #endif #ifdef __Pyx_FusedFunction_USED Py_CLEAR(clear_module_state->__pyx_FusedFunctionType); #endif Py_CLEAR(clear_module_state->__pyx_ptype_7cpython_4type_type); Py_CLEAR(clear_module_state->__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64); Py_CLEAR(clear_module_state->__pyx_type_4borg_10algorithms_9checksums_StreamingXXH64); Py_CLEAR(clear_module_state->__pyx_n_s_StreamingXXH64); Py_CLEAR(clear_module_state->__pyx_n_s_StreamingXXH64___reduce_cython); Py_CLEAR(clear_module_state->__pyx_n_s_StreamingXXH64___setstate_cython); Py_CLEAR(clear_module_state->__pyx_n_s_StreamingXXH64_digest); Py_CLEAR(clear_module_state->__pyx_n_s_StreamingXXH64_hexdigest); Py_CLEAR(clear_module_state->__pyx_n_s_StreamingXXH64_update); Py_CLEAR(clear_module_state->__pyx_n_s_TypeError); Py_CLEAR(clear_module_state->__pyx_kp_u_XXH64_reset_failed); Py_CLEAR(clear_module_state->__pyx_kp_u_XXH64_update_failed); Py_CLEAR(clear_module_state->__pyx_n_s__19); Py_CLEAR(clear_module_state->__pyx_kp_u__3); Py_CLEAR(clear_module_state->__pyx_n_s_asyncio_coroutines); Py_CLEAR(clear_module_state->__pyx_n_s_bin_to_hex); Py_CLEAR(clear_module_state->__pyx_n_s_borg_algorithms_checksums); Py_CLEAR(clear_module_state->__pyx_n_s_cline_in_traceback); Py_CLEAR(clear_module_state->__pyx_n_s_crc32); Py_CLEAR(clear_module_state->__pyx_n_s_crc32_clmul); Py_CLEAR(clear_module_state->__pyx_n_s_crc32_slice_by_8); Py_CLEAR(clear_module_state->__pyx_n_s_data); Py_CLEAR(clear_module_state->__pyx_n_s_data_buf); Py_CLEAR(clear_module_state->__pyx_n_s_digest); Py_CLEAR(clear_module_state->__pyx_kp_u_disable); Py_CLEAR(clear_module_state->__pyx_kp_u_enable); Py_CLEAR(clear_module_state->__pyx_kp_u_gc); Py_CLEAR(clear_module_state->__pyx_n_s_getstate); Py_CLEAR(clear_module_state->__pyx_n_s_hash); Py_CLEAR(clear_module_state->__pyx_n_s_have_clmul); Py_CLEAR(clear_module_state->__pyx_n_s_helpers); Py_CLEAR(clear_module_state->__pyx_n_s_hexdigest); Py_CLEAR(clear_module_state->__pyx_n_s_import); Py_CLEAR(clear_module_state->__pyx_n_s_is_coroutine); Py_CLEAR(clear_module_state->__pyx_kp_u_isenabled); Py_CLEAR(clear_module_state->__pyx_n_s_main); Py_CLEAR(clear_module_state->__pyx_n_s_name); Py_CLEAR(clear_module_state->__pyx_kp_s_no_default___reduce___due_to_non); Py_CLEAR(clear_module_state->__pyx_n_s_pyx_state); Py_CLEAR(clear_module_state->__pyx_n_s_reduce); Py_CLEAR(clear_module_state->__pyx_n_s_reduce_cython); Py_CLEAR(clear_module_state->__pyx_n_s_reduce_ex); Py_CLEAR(clear_module_state->__pyx_n_s_seed); Py_CLEAR(clear_module_state->__pyx_n_s_seed_2); Py_CLEAR(clear_module_state->__pyx_n_s_self); Py_CLEAR(clear_module_state->__pyx_n_s_setstate); Py_CLEAR(clear_module_state->__pyx_n_s_setstate_cython); Py_CLEAR(clear_module_state->__pyx_kp_s_src_borg_algorithms_checksums_py); Py_CLEAR(clear_module_state->__pyx_kp_s_stringsource); Py_CLEAR(clear_module_state->__pyx_n_s_test); Py_CLEAR(clear_module_state->__pyx_n_s_update); Py_CLEAR(clear_module_state->__pyx_n_s_val); Py_CLEAR(clear_module_state->__pyx_n_s_value); Py_CLEAR(clear_module_state->__pyx_n_s_xxh64); Py_CLEAR(clear_module_state->__pyx_int_0); Py_CLEAR(clear_module_state->__pyx_tuple_); Py_CLEAR(clear_module_state->__pyx_tuple__2); Py_CLEAR(clear_module_state->__pyx_tuple__4); Py_CLEAR(clear_module_state->__pyx_tuple__6); Py_CLEAR(clear_module_state->__pyx_tuple__8); Py_CLEAR(clear_module_state->__pyx_tuple__10); Py_CLEAR(clear_module_state->__pyx_tuple__12); Py_CLEAR(clear_module_state->__pyx_tuple__14); Py_CLEAR(clear_module_state->__pyx_tuple__17); Py_CLEAR(clear_module_state->__pyx_codeobj__5); Py_CLEAR(clear_module_state->__pyx_codeobj__7); Py_CLEAR(clear_module_state->__pyx_codeobj__9); Py_CLEAR(clear_module_state->__pyx_codeobj__11); Py_CLEAR(clear_module_state->__pyx_codeobj__13); Py_CLEAR(clear_module_state->__pyx_codeobj__15); Py_CLEAR(clear_module_state->__pyx_codeobj__16); Py_CLEAR(clear_module_state->__pyx_codeobj__18); return 0; } #endif /* #### Code section: module_state_traverse ### */ #if CYTHON_USE_MODULE_STATE static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) { __pyx_mstate *traverse_module_state = __pyx_mstate(m); if (!traverse_module_state) return 0; Py_VISIT(traverse_module_state->__pyx_d); Py_VISIT(traverse_module_state->__pyx_b); Py_VISIT(traverse_module_state->__pyx_cython_runtime); Py_VISIT(traverse_module_state->__pyx_empty_tuple); Py_VISIT(traverse_module_state->__pyx_empty_bytes); Py_VISIT(traverse_module_state->__pyx_empty_unicode); #ifdef __Pyx_CyFunction_USED Py_VISIT(traverse_module_state->__pyx_CyFunctionType); #endif #ifdef __Pyx_FusedFunction_USED Py_VISIT(traverse_module_state->__pyx_FusedFunctionType); #endif Py_VISIT(traverse_module_state->__pyx_ptype_7cpython_4type_type); Py_VISIT(traverse_module_state->__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64); Py_VISIT(traverse_module_state->__pyx_type_4borg_10algorithms_9checksums_StreamingXXH64); Py_VISIT(traverse_module_state->__pyx_n_s_StreamingXXH64); Py_VISIT(traverse_module_state->__pyx_n_s_StreamingXXH64___reduce_cython); Py_VISIT(traverse_module_state->__pyx_n_s_StreamingXXH64___setstate_cython); Py_VISIT(traverse_module_state->__pyx_n_s_StreamingXXH64_digest); Py_VISIT(traverse_module_state->__pyx_n_s_StreamingXXH64_hexdigest); Py_VISIT(traverse_module_state->__pyx_n_s_StreamingXXH64_update); Py_VISIT(traverse_module_state->__pyx_n_s_TypeError); Py_VISIT(traverse_module_state->__pyx_kp_u_XXH64_reset_failed); Py_VISIT(traverse_module_state->__pyx_kp_u_XXH64_update_failed); Py_VISIT(traverse_module_state->__pyx_n_s__19); Py_VISIT(traverse_module_state->__pyx_kp_u__3); Py_VISIT(traverse_module_state->__pyx_n_s_asyncio_coroutines); Py_VISIT(traverse_module_state->__pyx_n_s_bin_to_hex); Py_VISIT(traverse_module_state->__pyx_n_s_borg_algorithms_checksums); Py_VISIT(traverse_module_state->__pyx_n_s_cline_in_traceback); Py_VISIT(traverse_module_state->__pyx_n_s_crc32); Py_VISIT(traverse_module_state->__pyx_n_s_crc32_clmul); Py_VISIT(traverse_module_state->__pyx_n_s_crc32_slice_by_8); Py_VISIT(traverse_module_state->__pyx_n_s_data); Py_VISIT(traverse_module_state->__pyx_n_s_data_buf); Py_VISIT(traverse_module_state->__pyx_n_s_digest); Py_VISIT(traverse_module_state->__pyx_kp_u_disable); Py_VISIT(traverse_module_state->__pyx_kp_u_enable); Py_VISIT(traverse_module_state->__pyx_kp_u_gc); Py_VISIT(traverse_module_state->__pyx_n_s_getstate); Py_VISIT(traverse_module_state->__pyx_n_s_hash); Py_VISIT(traverse_module_state->__pyx_n_s_have_clmul); Py_VISIT(traverse_module_state->__pyx_n_s_helpers); Py_VISIT(traverse_module_state->__pyx_n_s_hexdigest); Py_VISIT(traverse_module_state->__pyx_n_s_import); Py_VISIT(traverse_module_state->__pyx_n_s_is_coroutine); Py_VISIT(traverse_module_state->__pyx_kp_u_isenabled); Py_VISIT(traverse_module_state->__pyx_n_s_main); Py_VISIT(traverse_module_state->__pyx_n_s_name); Py_VISIT(traverse_module_state->__pyx_kp_s_no_default___reduce___due_to_non); Py_VISIT(traverse_module_state->__pyx_n_s_pyx_state); Py_VISIT(traverse_module_state->__pyx_n_s_reduce); Py_VISIT(traverse_module_state->__pyx_n_s_reduce_cython); Py_VISIT(traverse_module_state->__pyx_n_s_reduce_ex); Py_VISIT(traverse_module_state->__pyx_n_s_seed); Py_VISIT(traverse_module_state->__pyx_n_s_seed_2); Py_VISIT(traverse_module_state->__pyx_n_s_self); Py_VISIT(traverse_module_state->__pyx_n_s_setstate); Py_VISIT(traverse_module_state->__pyx_n_s_setstate_cython); Py_VISIT(traverse_module_state->__pyx_kp_s_src_borg_algorithms_checksums_py); Py_VISIT(traverse_module_state->__pyx_kp_s_stringsource); Py_VISIT(traverse_module_state->__pyx_n_s_test); Py_VISIT(traverse_module_state->__pyx_n_s_update); Py_VISIT(traverse_module_state->__pyx_n_s_val); Py_VISIT(traverse_module_state->__pyx_n_s_value); Py_VISIT(traverse_module_state->__pyx_n_s_xxh64); Py_VISIT(traverse_module_state->__pyx_int_0); Py_VISIT(traverse_module_state->__pyx_tuple_); Py_VISIT(traverse_module_state->__pyx_tuple__2); Py_VISIT(traverse_module_state->__pyx_tuple__4); Py_VISIT(traverse_module_state->__pyx_tuple__6); Py_VISIT(traverse_module_state->__pyx_tuple__8); Py_VISIT(traverse_module_state->__pyx_tuple__10); Py_VISIT(traverse_module_state->__pyx_tuple__12); Py_VISIT(traverse_module_state->__pyx_tuple__14); Py_VISIT(traverse_module_state->__pyx_tuple__17); Py_VISIT(traverse_module_state->__pyx_codeobj__5); Py_VISIT(traverse_module_state->__pyx_codeobj__7); Py_VISIT(traverse_module_state->__pyx_codeobj__9); Py_VISIT(traverse_module_state->__pyx_codeobj__11); Py_VISIT(traverse_module_state->__pyx_codeobj__13); Py_VISIT(traverse_module_state->__pyx_codeobj__15); Py_VISIT(traverse_module_state->__pyx_codeobj__16); Py_VISIT(traverse_module_state->__pyx_codeobj__18); return 0; } #endif /* #### Code section: module_state_defines ### */ #define __pyx_d __pyx_mstate_global->__pyx_d #define __pyx_b __pyx_mstate_global->__pyx_b #define __pyx_cython_runtime __pyx_mstate_global->__pyx_cython_runtime #define __pyx_empty_tuple __pyx_mstate_global->__pyx_empty_tuple #define __pyx_empty_bytes __pyx_mstate_global->__pyx_empty_bytes #define __pyx_empty_unicode __pyx_mstate_global->__pyx_empty_unicode #ifdef __Pyx_CyFunction_USED #define __pyx_CyFunctionType __pyx_mstate_global->__pyx_CyFunctionType #endif #ifdef __Pyx_FusedFunction_USED #define __pyx_FusedFunctionType __pyx_mstate_global->__pyx_FusedFunctionType #endif #ifdef __Pyx_Generator_USED #define __pyx_GeneratorType __pyx_mstate_global->__pyx_GeneratorType #endif #ifdef __Pyx_IterableCoroutine_USED #define __pyx_IterableCoroutineType __pyx_mstate_global->__pyx_IterableCoroutineType #endif #ifdef __Pyx_Coroutine_USED #define __pyx_CoroutineAwaitType __pyx_mstate_global->__pyx_CoroutineAwaitType #endif #ifdef __Pyx_Coroutine_USED #define __pyx_CoroutineType __pyx_mstate_global->__pyx_CoroutineType #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #define __pyx_ptype_7cpython_4type_type __pyx_mstate_global->__pyx_ptype_7cpython_4type_type #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #endif #if CYTHON_USE_MODULE_STATE #define __pyx_type_4borg_10algorithms_9checksums_StreamingXXH64 __pyx_mstate_global->__pyx_type_4borg_10algorithms_9checksums_StreamingXXH64 #endif #define __pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64 __pyx_mstate_global->__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64 #define __pyx_n_s_StreamingXXH64 __pyx_mstate_global->__pyx_n_s_StreamingXXH64 #define __pyx_n_s_StreamingXXH64___reduce_cython __pyx_mstate_global->__pyx_n_s_StreamingXXH64___reduce_cython #define __pyx_n_s_StreamingXXH64___setstate_cython __pyx_mstate_global->__pyx_n_s_StreamingXXH64___setstate_cython #define __pyx_n_s_StreamingXXH64_digest __pyx_mstate_global->__pyx_n_s_StreamingXXH64_digest #define __pyx_n_s_StreamingXXH64_hexdigest __pyx_mstate_global->__pyx_n_s_StreamingXXH64_hexdigest #define __pyx_n_s_StreamingXXH64_update __pyx_mstate_global->__pyx_n_s_StreamingXXH64_update #define __pyx_n_s_TypeError __pyx_mstate_global->__pyx_n_s_TypeError #define __pyx_kp_u_XXH64_reset_failed __pyx_mstate_global->__pyx_kp_u_XXH64_reset_failed #define __pyx_kp_u_XXH64_update_failed __pyx_mstate_global->__pyx_kp_u_XXH64_update_failed #define __pyx_n_s__19 __pyx_mstate_global->__pyx_n_s__19 #define __pyx_kp_u__3 __pyx_mstate_global->__pyx_kp_u__3 #define __pyx_n_s_asyncio_coroutines __pyx_mstate_global->__pyx_n_s_asyncio_coroutines #define __pyx_n_s_bin_to_hex __pyx_mstate_global->__pyx_n_s_bin_to_hex #define __pyx_n_s_borg_algorithms_checksums __pyx_mstate_global->__pyx_n_s_borg_algorithms_checksums #define __pyx_n_s_cline_in_traceback __pyx_mstate_global->__pyx_n_s_cline_in_traceback #define __pyx_n_s_crc32 __pyx_mstate_global->__pyx_n_s_crc32 #define __pyx_n_s_crc32_clmul __pyx_mstate_global->__pyx_n_s_crc32_clmul #define __pyx_n_s_crc32_slice_by_8 __pyx_mstate_global->__pyx_n_s_crc32_slice_by_8 #define __pyx_n_s_data __pyx_mstate_global->__pyx_n_s_data #define __pyx_n_s_data_buf __pyx_mstate_global->__pyx_n_s_data_buf #define __pyx_n_s_digest __pyx_mstate_global->__pyx_n_s_digest #define __pyx_kp_u_disable __pyx_mstate_global->__pyx_kp_u_disable #define __pyx_kp_u_enable __pyx_mstate_global->__pyx_kp_u_enable #define __pyx_kp_u_gc __pyx_mstate_global->__pyx_kp_u_gc #define __pyx_n_s_getstate __pyx_mstate_global->__pyx_n_s_getstate #define __pyx_n_s_hash __pyx_mstate_global->__pyx_n_s_hash #define __pyx_n_s_have_clmul __pyx_mstate_global->__pyx_n_s_have_clmul #define __pyx_n_s_helpers __pyx_mstate_global->__pyx_n_s_helpers #define __pyx_n_s_hexdigest __pyx_mstate_global->__pyx_n_s_hexdigest #define __pyx_n_s_import __pyx_mstate_global->__pyx_n_s_import #define __pyx_n_s_is_coroutine __pyx_mstate_global->__pyx_n_s_is_coroutine #define __pyx_kp_u_isenabled __pyx_mstate_global->__pyx_kp_u_isenabled #define __pyx_n_s_main __pyx_mstate_global->__pyx_n_s_main #define __pyx_n_s_name __pyx_mstate_global->__pyx_n_s_name #define __pyx_kp_s_no_default___reduce___due_to_non __pyx_mstate_global->__pyx_kp_s_no_default___reduce___due_to_non #define __pyx_n_s_pyx_state __pyx_mstate_global->__pyx_n_s_pyx_state #define __pyx_n_s_reduce __pyx_mstate_global->__pyx_n_s_reduce #define __pyx_n_s_reduce_cython __pyx_mstate_global->__pyx_n_s_reduce_cython #define __pyx_n_s_reduce_ex __pyx_mstate_global->__pyx_n_s_reduce_ex #define __pyx_n_s_seed __pyx_mstate_global->__pyx_n_s_seed #define __pyx_n_s_seed_2 __pyx_mstate_global->__pyx_n_s_seed_2 #define __pyx_n_s_self __pyx_mstate_global->__pyx_n_s_self #define __pyx_n_s_setstate __pyx_mstate_global->__pyx_n_s_setstate #define __pyx_n_s_setstate_cython __pyx_mstate_global->__pyx_n_s_setstate_cython #define __pyx_kp_s_src_borg_algorithms_checksums_py __pyx_mstate_global->__pyx_kp_s_src_borg_algorithms_checksums_py #define __pyx_kp_s_stringsource __pyx_mstate_global->__pyx_kp_s_stringsource #define __pyx_n_s_test __pyx_mstate_global->__pyx_n_s_test #define __pyx_n_s_update __pyx_mstate_global->__pyx_n_s_update #define __pyx_n_s_val __pyx_mstate_global->__pyx_n_s_val #define __pyx_n_s_value __pyx_mstate_global->__pyx_n_s_value #define __pyx_n_s_xxh64 __pyx_mstate_global->__pyx_n_s_xxh64 #define __pyx_int_0 __pyx_mstate_global->__pyx_int_0 #define __pyx_tuple_ __pyx_mstate_global->__pyx_tuple_ #define __pyx_tuple__2 __pyx_mstate_global->__pyx_tuple__2 #define __pyx_tuple__4 __pyx_mstate_global->__pyx_tuple__4 #define __pyx_tuple__6 __pyx_mstate_global->__pyx_tuple__6 #define __pyx_tuple__8 __pyx_mstate_global->__pyx_tuple__8 #define __pyx_tuple__10 __pyx_mstate_global->__pyx_tuple__10 #define __pyx_tuple__12 __pyx_mstate_global->__pyx_tuple__12 #define __pyx_tuple__14 __pyx_mstate_global->__pyx_tuple__14 #define __pyx_tuple__17 __pyx_mstate_global->__pyx_tuple__17 #define __pyx_codeobj__5 __pyx_mstate_global->__pyx_codeobj__5 #define __pyx_codeobj__7 __pyx_mstate_global->__pyx_codeobj__7 #define __pyx_codeobj__9 __pyx_mstate_global->__pyx_codeobj__9 #define __pyx_codeobj__11 __pyx_mstate_global->__pyx_codeobj__11 #define __pyx_codeobj__13 __pyx_mstate_global->__pyx_codeobj__13 #define __pyx_codeobj__15 __pyx_mstate_global->__pyx_codeobj__15 #define __pyx_codeobj__16 __pyx_mstate_global->__pyx_codeobj__16 #define __pyx_codeobj__18 __pyx_mstate_global->__pyx_codeobj__18 /* #### Code section: module_code ### */ /* "borg/algorithms/checksums.pyx":40 * * * cdef Py_buffer ro_buffer(object data) except *: # <<<<<<<<<<<<<< * cdef Py_buffer view * PyObject_GetBuffer(data, &view, PyBUF_SIMPLE) */ static Py_buffer __pyx_f_4borg_10algorithms_9checksums_ro_buffer(PyObject *__pyx_v_data) { Py_buffer __pyx_v_view; Py_buffer __pyx_r; int __pyx_t_1; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* "borg/algorithms/checksums.pyx":42 * cdef Py_buffer ro_buffer(object data) except *: * cdef Py_buffer view * PyObject_GetBuffer(data, &view, PyBUF_SIMPLE) # <<<<<<<<<<<<<< * return view * */ __pyx_t_1 = PyObject_GetBuffer(__pyx_v_data, (&__pyx_v_view), PyBUF_SIMPLE); if (unlikely(__pyx_t_1 == ((int)-1))) __PYX_ERR(1, 42, __pyx_L1_error) /* "borg/algorithms/checksums.pyx":43 * cdef Py_buffer view * PyObject_GetBuffer(data, &view, PyBUF_SIMPLE) * return view # <<<<<<<<<<<<<< * * */ __pyx_r = __pyx_v_view; goto __pyx_L0; /* "borg/algorithms/checksums.pyx":40 * * * cdef Py_buffer ro_buffer(object data) except *: # <<<<<<<<<<<<<< * cdef Py_buffer view * PyObject_GetBuffer(data, &view, PyBUF_SIMPLE) */ /* function exit code */ __pyx_L1_error:; __Pyx_AddTraceback("borg.algorithms.checksums.ro_buffer", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_pretend_to_initialize(&__pyx_r); __pyx_L0:; return __pyx_r; } /* "borg/algorithms/checksums.pyx":46 * * * def crc32_slice_by_8(data, value=0): # <<<<<<<<<<<<<< * cdef Py_buffer data_buf = ro_buffer(data) * cdef uint32_t val = value */ /* Python wrapper */ static PyObject *__pyx_pw_4borg_10algorithms_9checksums_1crc32_slice_by_8(PyObject *__pyx_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ); /*proto*/ static PyMethodDef __pyx_mdef_4borg_10algorithms_9checksums_1crc32_slice_by_8 = {"crc32_slice_by_8", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_1crc32_slice_by_8, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}; static PyObject *__pyx_pw_4borg_10algorithms_9checksums_1crc32_slice_by_8(PyObject *__pyx_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ) { PyObject *__pyx_v_data = 0; PyObject *__pyx_v_value = 0; #if !CYTHON_METH_FASTCALL CYTHON_UNUSED Py_ssize_t __pyx_nargs; #endif CYTHON_UNUSED PyObject *const *__pyx_kwvalues; PyObject* values[2] = {0,0}; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("crc32_slice_by_8 (wrapper)", 0); #if !CYTHON_METH_FASTCALL #if CYTHON_ASSUME_SAFE_MACROS __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); #else __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; #endif #endif __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); { PyObject **__pyx_pyargnames[] = {&__pyx_n_s_data,&__pyx_n_s_value,0}; values[1] = __Pyx_Arg_NewRef_FASTCALL(((PyObject *)((PyObject *)__pyx_int_0))); if (__pyx_kwds) { Py_ssize_t kw_args; switch (__pyx_nargs) { case 2: values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = __Pyx_NumKwargs_FASTCALL(__pyx_kwds); switch (__pyx_nargs) { case 0: if (likely((values[0] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_data)) != 0)) { (void)__Pyx_Arg_NewRef_FASTCALL(values[0]); kw_args--; } else if (unlikely(PyErr_Occurred())) __PYX_ERR(1, 46, __pyx_L3_error) else goto __pyx_L5_argtuple_error; CYTHON_FALLTHROUGH; case 1: if (kw_args > 0) { PyObject* value = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_value); if (value) { values[1] = __Pyx_Arg_NewRef_FASTCALL(value); kw_args--; } else if (unlikely(PyErr_Occurred())) __PYX_ERR(1, 46, __pyx_L3_error) } } if (unlikely(kw_args > 0)) { const Py_ssize_t kwd_pos_args = __pyx_nargs; if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "crc32_slice_by_8") < 0)) __PYX_ERR(1, 46, __pyx_L3_error) } } else { switch (__pyx_nargs) { case 2: values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); break; default: goto __pyx_L5_argtuple_error; } } __pyx_v_data = values[0]; __pyx_v_value = values[1]; } goto __pyx_L6_skip; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("crc32_slice_by_8", 0, 1, 2, __pyx_nargs); __PYX_ERR(1, 46, __pyx_L3_error) __pyx_L6_skip:; goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; { Py_ssize_t __pyx_temp; for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); } } __Pyx_AddTraceback("borg.algorithms.checksums.crc32_slice_by_8", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_4borg_10algorithms_9checksums_crc32_slice_by_8(__pyx_self, __pyx_v_data, __pyx_v_value); /* function exit code */ { Py_ssize_t __pyx_temp; for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); } } __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_4borg_10algorithms_9checksums_crc32_slice_by_8(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_data, PyObject *__pyx_v_value) { Py_buffer __pyx_v_data_buf; uint32_t __pyx_v_val; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations Py_buffer __pyx_t_1; uint32_t __pyx_t_2; PyObject *__pyx_t_3 = NULL; int __pyx_t_4; int __pyx_t_5; char const *__pyx_t_6; PyObject *__pyx_t_7 = NULL; PyObject *__pyx_t_8 = NULL; PyObject *__pyx_t_9 = NULL; PyObject *__pyx_t_10 = NULL; PyObject *__pyx_t_11 = NULL; PyObject *__pyx_t_12 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("crc32_slice_by_8", 1); /* "borg/algorithms/checksums.pyx":47 * * def crc32_slice_by_8(data, value=0): * cdef Py_buffer data_buf = ro_buffer(data) # <<<<<<<<<<<<<< * cdef uint32_t val = value * try: */ __pyx_t_1 = __pyx_f_4borg_10algorithms_9checksums_ro_buffer(__pyx_v_data); if (unlikely(PyErr_Occurred())) __PYX_ERR(1, 47, __pyx_L1_error) __pyx_v_data_buf = __pyx_t_1; /* "borg/algorithms/checksums.pyx":48 * def crc32_slice_by_8(data, value=0): * cdef Py_buffer data_buf = ro_buffer(data) * cdef uint32_t val = value # <<<<<<<<<<<<<< * try: * return _crc32_slice_by_8(data_buf.buf, data_buf.len, val) */ __pyx_t_2 = __Pyx_PyInt_As_uint32_t(__pyx_v_value); if (unlikely((__pyx_t_2 == ((uint32_t)-1)) && PyErr_Occurred())) __PYX_ERR(1, 48, __pyx_L1_error) __pyx_v_val = __pyx_t_2; /* "borg/algorithms/checksums.pyx":49 * cdef Py_buffer data_buf = ro_buffer(data) * cdef uint32_t val = value * try: # <<<<<<<<<<<<<< * return _crc32_slice_by_8(data_buf.buf, data_buf.len, val) * finally: */ /*try:*/ { /* "borg/algorithms/checksums.pyx":50 * cdef uint32_t val = value * try: * return _crc32_slice_by_8(data_buf.buf, data_buf.len, val) # <<<<<<<<<<<<<< * finally: * PyBuffer_Release(&data_buf) */ __Pyx_XDECREF(__pyx_r); __pyx_t_3 = __Pyx_PyInt_From_uint32_t(crc32_slice_by_8(__pyx_v_data_buf.buf, __pyx_v_data_buf.len, __pyx_v_val)); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 50, __pyx_L4_error) __Pyx_GOTREF(__pyx_t_3); __pyx_r = __pyx_t_3; __pyx_t_3 = 0; goto __pyx_L3_return; } /* "borg/algorithms/checksums.pyx":52 * return _crc32_slice_by_8(data_buf.buf, data_buf.len, val) * finally: * PyBuffer_Release(&data_buf) # <<<<<<<<<<<<<< * * */ /*finally:*/ { __pyx_L4_error:; /*exception exit:*/{ __Pyx_PyThreadState_declare __Pyx_PyThreadState_assign __pyx_t_7 = 0; __pyx_t_8 = 0; __pyx_t_9 = 0; __pyx_t_10 = 0; __pyx_t_11 = 0; __pyx_t_12 = 0; __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0; if (PY_MAJOR_VERSION >= 3) __Pyx_ExceptionSwap(&__pyx_t_10, &__pyx_t_11, &__pyx_t_12); if ((PY_MAJOR_VERSION < 3) || unlikely(__Pyx_GetException(&__pyx_t_7, &__pyx_t_8, &__pyx_t_9) < 0)) __Pyx_ErrFetch(&__pyx_t_7, &__pyx_t_8, &__pyx_t_9); __Pyx_XGOTREF(__pyx_t_7); __Pyx_XGOTREF(__pyx_t_8); __Pyx_XGOTREF(__pyx_t_9); __Pyx_XGOTREF(__pyx_t_10); __Pyx_XGOTREF(__pyx_t_11); __Pyx_XGOTREF(__pyx_t_12); __pyx_t_4 = __pyx_lineno; __pyx_t_5 = __pyx_clineno; __pyx_t_6 = __pyx_filename; { PyBuffer_Release((&__pyx_v_data_buf)); } if (PY_MAJOR_VERSION >= 3) { __Pyx_XGIVEREF(__pyx_t_10); __Pyx_XGIVEREF(__pyx_t_11); __Pyx_XGIVEREF(__pyx_t_12); __Pyx_ExceptionReset(__pyx_t_10, __pyx_t_11, __pyx_t_12); } __Pyx_XGIVEREF(__pyx_t_7); __Pyx_XGIVEREF(__pyx_t_8); __Pyx_XGIVEREF(__pyx_t_9); __Pyx_ErrRestore(__pyx_t_7, __pyx_t_8, __pyx_t_9); __pyx_t_7 = 0; __pyx_t_8 = 0; __pyx_t_9 = 0; __pyx_t_10 = 0; __pyx_t_11 = 0; __pyx_t_12 = 0; __pyx_lineno = __pyx_t_4; __pyx_clineno = __pyx_t_5; __pyx_filename = __pyx_t_6; goto __pyx_L1_error; } __pyx_L3_return: { __pyx_t_12 = __pyx_r; __pyx_r = 0; PyBuffer_Release((&__pyx_v_data_buf)); __pyx_r = __pyx_t_12; __pyx_t_12 = 0; goto __pyx_L0; } } /* "borg/algorithms/checksums.pyx":46 * * * def crc32_slice_by_8(data, value=0): # <<<<<<<<<<<<<< * cdef Py_buffer data_buf = ro_buffer(data) * cdef uint32_t val = value */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_3); __Pyx_AddTraceback("borg.algorithms.checksums.crc32_slice_by_8", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "borg/algorithms/checksums.pyx":55 * * * def crc32_clmul(data, value=0): # <<<<<<<<<<<<<< * cdef Py_buffer data_buf = ro_buffer(data) * cdef uint32_t val = value */ /* Python wrapper */ static PyObject *__pyx_pw_4borg_10algorithms_9checksums_3crc32_clmul(PyObject *__pyx_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ); /*proto*/ static PyMethodDef __pyx_mdef_4borg_10algorithms_9checksums_3crc32_clmul = {"crc32_clmul", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_3crc32_clmul, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}; static PyObject *__pyx_pw_4borg_10algorithms_9checksums_3crc32_clmul(PyObject *__pyx_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ) { PyObject *__pyx_v_data = 0; PyObject *__pyx_v_value = 0; #if !CYTHON_METH_FASTCALL CYTHON_UNUSED Py_ssize_t __pyx_nargs; #endif CYTHON_UNUSED PyObject *const *__pyx_kwvalues; PyObject* values[2] = {0,0}; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("crc32_clmul (wrapper)", 0); #if !CYTHON_METH_FASTCALL #if CYTHON_ASSUME_SAFE_MACROS __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); #else __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; #endif #endif __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); { PyObject **__pyx_pyargnames[] = {&__pyx_n_s_data,&__pyx_n_s_value,0}; values[1] = __Pyx_Arg_NewRef_FASTCALL(((PyObject *)((PyObject *)__pyx_int_0))); if (__pyx_kwds) { Py_ssize_t kw_args; switch (__pyx_nargs) { case 2: values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = __Pyx_NumKwargs_FASTCALL(__pyx_kwds); switch (__pyx_nargs) { case 0: if (likely((values[0] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_data)) != 0)) { (void)__Pyx_Arg_NewRef_FASTCALL(values[0]); kw_args--; } else if (unlikely(PyErr_Occurred())) __PYX_ERR(1, 55, __pyx_L3_error) else goto __pyx_L5_argtuple_error; CYTHON_FALLTHROUGH; case 1: if (kw_args > 0) { PyObject* value = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_value); if (value) { values[1] = __Pyx_Arg_NewRef_FASTCALL(value); kw_args--; } else if (unlikely(PyErr_Occurred())) __PYX_ERR(1, 55, __pyx_L3_error) } } if (unlikely(kw_args > 0)) { const Py_ssize_t kwd_pos_args = __pyx_nargs; if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "crc32_clmul") < 0)) __PYX_ERR(1, 55, __pyx_L3_error) } } else { switch (__pyx_nargs) { case 2: values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); break; default: goto __pyx_L5_argtuple_error; } } __pyx_v_data = values[0]; __pyx_v_value = values[1]; } goto __pyx_L6_skip; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("crc32_clmul", 0, 1, 2, __pyx_nargs); __PYX_ERR(1, 55, __pyx_L3_error) __pyx_L6_skip:; goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; { Py_ssize_t __pyx_temp; for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); } } __Pyx_AddTraceback("borg.algorithms.checksums.crc32_clmul", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_4borg_10algorithms_9checksums_2crc32_clmul(__pyx_self, __pyx_v_data, __pyx_v_value); /* function exit code */ { Py_ssize_t __pyx_temp; for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); } } __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_4borg_10algorithms_9checksums_2crc32_clmul(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_data, PyObject *__pyx_v_value) { Py_buffer __pyx_v_data_buf; uint32_t __pyx_v_val; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations Py_buffer __pyx_t_1; uint32_t __pyx_t_2; PyObject *__pyx_t_3 = NULL; int __pyx_t_4; int __pyx_t_5; char const *__pyx_t_6; PyObject *__pyx_t_7 = NULL; PyObject *__pyx_t_8 = NULL; PyObject *__pyx_t_9 = NULL; PyObject *__pyx_t_10 = NULL; PyObject *__pyx_t_11 = NULL; PyObject *__pyx_t_12 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("crc32_clmul", 1); /* "borg/algorithms/checksums.pyx":56 * * def crc32_clmul(data, value=0): * cdef Py_buffer data_buf = ro_buffer(data) # <<<<<<<<<<<<<< * cdef uint32_t val = value * try: */ __pyx_t_1 = __pyx_f_4borg_10algorithms_9checksums_ro_buffer(__pyx_v_data); if (unlikely(PyErr_Occurred())) __PYX_ERR(1, 56, __pyx_L1_error) __pyx_v_data_buf = __pyx_t_1; /* "borg/algorithms/checksums.pyx":57 * def crc32_clmul(data, value=0): * cdef Py_buffer data_buf = ro_buffer(data) * cdef uint32_t val = value # <<<<<<<<<<<<<< * try: * return _crc32_clmul(data_buf.buf, data_buf.len, val) */ __pyx_t_2 = __Pyx_PyInt_As_uint32_t(__pyx_v_value); if (unlikely((__pyx_t_2 == ((uint32_t)-1)) && PyErr_Occurred())) __PYX_ERR(1, 57, __pyx_L1_error) __pyx_v_val = __pyx_t_2; /* "borg/algorithms/checksums.pyx":58 * cdef Py_buffer data_buf = ro_buffer(data) * cdef uint32_t val = value * try: # <<<<<<<<<<<<<< * return _crc32_clmul(data_buf.buf, data_buf.len, val) * finally: */ /*try:*/ { /* "borg/algorithms/checksums.pyx":59 * cdef uint32_t val = value * try: * return _crc32_clmul(data_buf.buf, data_buf.len, val) # <<<<<<<<<<<<<< * finally: * PyBuffer_Release(&data_buf) */ __Pyx_XDECREF(__pyx_r); __pyx_t_3 = __Pyx_PyInt_From_uint32_t(crc32_clmul(__pyx_v_data_buf.buf, __pyx_v_data_buf.len, __pyx_v_val)); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 59, __pyx_L4_error) __Pyx_GOTREF(__pyx_t_3); __pyx_r = __pyx_t_3; __pyx_t_3 = 0; goto __pyx_L3_return; } /* "borg/algorithms/checksums.pyx":61 * return _crc32_clmul(data_buf.buf, data_buf.len, val) * finally: * PyBuffer_Release(&data_buf) # <<<<<<<<<<<<<< * * */ /*finally:*/ { __pyx_L4_error:; /*exception exit:*/{ __Pyx_PyThreadState_declare __Pyx_PyThreadState_assign __pyx_t_7 = 0; __pyx_t_8 = 0; __pyx_t_9 = 0; __pyx_t_10 = 0; __pyx_t_11 = 0; __pyx_t_12 = 0; __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0; if (PY_MAJOR_VERSION >= 3) __Pyx_ExceptionSwap(&__pyx_t_10, &__pyx_t_11, &__pyx_t_12); if ((PY_MAJOR_VERSION < 3) || unlikely(__Pyx_GetException(&__pyx_t_7, &__pyx_t_8, &__pyx_t_9) < 0)) __Pyx_ErrFetch(&__pyx_t_7, &__pyx_t_8, &__pyx_t_9); __Pyx_XGOTREF(__pyx_t_7); __Pyx_XGOTREF(__pyx_t_8); __Pyx_XGOTREF(__pyx_t_9); __Pyx_XGOTREF(__pyx_t_10); __Pyx_XGOTREF(__pyx_t_11); __Pyx_XGOTREF(__pyx_t_12); __pyx_t_4 = __pyx_lineno; __pyx_t_5 = __pyx_clineno; __pyx_t_6 = __pyx_filename; { PyBuffer_Release((&__pyx_v_data_buf)); } if (PY_MAJOR_VERSION >= 3) { __Pyx_XGIVEREF(__pyx_t_10); __Pyx_XGIVEREF(__pyx_t_11); __Pyx_XGIVEREF(__pyx_t_12); __Pyx_ExceptionReset(__pyx_t_10, __pyx_t_11, __pyx_t_12); } __Pyx_XGIVEREF(__pyx_t_7); __Pyx_XGIVEREF(__pyx_t_8); __Pyx_XGIVEREF(__pyx_t_9); __Pyx_ErrRestore(__pyx_t_7, __pyx_t_8, __pyx_t_9); __pyx_t_7 = 0; __pyx_t_8 = 0; __pyx_t_9 = 0; __pyx_t_10 = 0; __pyx_t_11 = 0; __pyx_t_12 = 0; __pyx_lineno = __pyx_t_4; __pyx_clineno = __pyx_t_5; __pyx_filename = __pyx_t_6; goto __pyx_L1_error; } __pyx_L3_return: { __pyx_t_12 = __pyx_r; __pyx_r = 0; PyBuffer_Release((&__pyx_v_data_buf)); __pyx_r = __pyx_t_12; __pyx_t_12 = 0; goto __pyx_L0; } } /* "borg/algorithms/checksums.pyx":55 * * * def crc32_clmul(data, value=0): # <<<<<<<<<<<<<< * cdef Py_buffer data_buf = ro_buffer(data) * cdef uint32_t val = value */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_3); __Pyx_AddTraceback("borg.algorithms.checksums.crc32_clmul", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "borg/algorithms/checksums.pyx":71 * * * def xxh64(data, seed=0): # <<<<<<<<<<<<<< * cdef unsigned long long _seed = seed * cdef XXH64_hash_t hash */ /* Python wrapper */ static PyObject *__pyx_pw_4borg_10algorithms_9checksums_5xxh64(PyObject *__pyx_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ); /*proto*/ static PyMethodDef __pyx_mdef_4borg_10algorithms_9checksums_5xxh64 = {"xxh64", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_5xxh64, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}; static PyObject *__pyx_pw_4borg_10algorithms_9checksums_5xxh64(PyObject *__pyx_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ) { PyObject *__pyx_v_data = 0; PyObject *__pyx_v_seed = 0; #if !CYTHON_METH_FASTCALL CYTHON_UNUSED Py_ssize_t __pyx_nargs; #endif CYTHON_UNUSED PyObject *const *__pyx_kwvalues; PyObject* values[2] = {0,0}; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("xxh64 (wrapper)", 0); #if !CYTHON_METH_FASTCALL #if CYTHON_ASSUME_SAFE_MACROS __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); #else __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; #endif #endif __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); { PyObject **__pyx_pyargnames[] = {&__pyx_n_s_data,&__pyx_n_s_seed,0}; values[1] = __Pyx_Arg_NewRef_FASTCALL(((PyObject *)((PyObject *)__pyx_int_0))); if (__pyx_kwds) { Py_ssize_t kw_args; switch (__pyx_nargs) { case 2: values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = __Pyx_NumKwargs_FASTCALL(__pyx_kwds); switch (__pyx_nargs) { case 0: if (likely((values[0] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_data)) != 0)) { (void)__Pyx_Arg_NewRef_FASTCALL(values[0]); kw_args--; } else if (unlikely(PyErr_Occurred())) __PYX_ERR(1, 71, __pyx_L3_error) else goto __pyx_L5_argtuple_error; CYTHON_FALLTHROUGH; case 1: if (kw_args > 0) { PyObject* value = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_seed); if (value) { values[1] = __Pyx_Arg_NewRef_FASTCALL(value); kw_args--; } else if (unlikely(PyErr_Occurred())) __PYX_ERR(1, 71, __pyx_L3_error) } } if (unlikely(kw_args > 0)) { const Py_ssize_t kwd_pos_args = __pyx_nargs; if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "xxh64") < 0)) __PYX_ERR(1, 71, __pyx_L3_error) } } else { switch (__pyx_nargs) { case 2: values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); break; default: goto __pyx_L5_argtuple_error; } } __pyx_v_data = values[0]; __pyx_v_seed = values[1]; } goto __pyx_L6_skip; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("xxh64", 0, 1, 2, __pyx_nargs); __PYX_ERR(1, 71, __pyx_L3_error) __pyx_L6_skip:; goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; { Py_ssize_t __pyx_temp; for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); } } __Pyx_AddTraceback("borg.algorithms.checksums.xxh64", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_4borg_10algorithms_9checksums_4xxh64(__pyx_self, __pyx_v_data, __pyx_v_seed); /* function exit code */ { Py_ssize_t __pyx_temp; for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); } } __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_4borg_10algorithms_9checksums_4xxh64(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_data, PyObject *__pyx_v_seed) { unsigned PY_LONG_LONG __pyx_v__seed; XXH64_hash_t __pyx_v_hash; XXH64_canonical_t __pyx_v_digest; Py_buffer __pyx_v_data_buf; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations unsigned PY_LONG_LONG __pyx_t_1; Py_buffer __pyx_t_2; PyObject *__pyx_t_3 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("xxh64", 1); /* "borg/algorithms/checksums.pyx":72 * * def xxh64(data, seed=0): * cdef unsigned long long _seed = seed # <<<<<<<<<<<<<< * cdef XXH64_hash_t hash * cdef XXH64_canonical_t digest */ __pyx_t_1 = __Pyx_PyInt_As_unsigned_PY_LONG_LONG(__pyx_v_seed); if (unlikely((__pyx_t_1 == (unsigned PY_LONG_LONG)-1) && PyErr_Occurred())) __PYX_ERR(1, 72, __pyx_L1_error) __pyx_v__seed = __pyx_t_1; /* "borg/algorithms/checksums.pyx":75 * cdef XXH64_hash_t hash * cdef XXH64_canonical_t digest * cdef Py_buffer data_buf = ro_buffer(data) # <<<<<<<<<<<<<< * try: * hash = XXH64(data_buf.buf, data_buf.len, _seed) */ __pyx_t_2 = __pyx_f_4borg_10algorithms_9checksums_ro_buffer(__pyx_v_data); if (unlikely(PyErr_Occurred())) __PYX_ERR(1, 75, __pyx_L1_error) __pyx_v_data_buf = __pyx_t_2; /* "borg/algorithms/checksums.pyx":76 * cdef XXH64_canonical_t digest * cdef Py_buffer data_buf = ro_buffer(data) * try: # <<<<<<<<<<<<<< * hash = XXH64(data_buf.buf, data_buf.len, _seed) * finally: */ /*try:*/ { /* "borg/algorithms/checksums.pyx":77 * cdef Py_buffer data_buf = ro_buffer(data) * try: * hash = XXH64(data_buf.buf, data_buf.len, _seed) # <<<<<<<<<<<<<< * finally: * PyBuffer_Release(&data_buf) */ __pyx_v_hash = XXH64(__pyx_v_data_buf.buf, __pyx_v_data_buf.len, __pyx_v__seed); } /* "borg/algorithms/checksums.pyx":79 * hash = XXH64(data_buf.buf, data_buf.len, _seed) * finally: * PyBuffer_Release(&data_buf) # <<<<<<<<<<<<<< * XXH64_canonicalFromHash(&digest, hash) * return PyBytes_FromStringAndSize( digest.digest, 8) */ /*finally:*/ { /*normal exit:*/{ PyBuffer_Release((&__pyx_v_data_buf)); goto __pyx_L5; } __pyx_L5:; } /* "borg/algorithms/checksums.pyx":80 * finally: * PyBuffer_Release(&data_buf) * XXH64_canonicalFromHash(&digest, hash) # <<<<<<<<<<<<<< * return PyBytes_FromStringAndSize( digest.digest, 8) * */ XXH64_canonicalFromHash((&__pyx_v_digest), __pyx_v_hash); /* "borg/algorithms/checksums.pyx":81 * PyBuffer_Release(&data_buf) * XXH64_canonicalFromHash(&digest, hash) * return PyBytes_FromStringAndSize( digest.digest, 8) # <<<<<<<<<<<<<< * * */ __Pyx_XDECREF(__pyx_r); __pyx_t_3 = PyBytes_FromStringAndSize(((char const *)__pyx_v_digest.digest), 8); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 81, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __pyx_r = __pyx_t_3; __pyx_t_3 = 0; goto __pyx_L0; /* "borg/algorithms/checksums.pyx":71 * * * def xxh64(data, seed=0): # <<<<<<<<<<<<<< * cdef unsigned long long _seed = seed * cdef XXH64_hash_t hash */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_3); __Pyx_AddTraceback("borg.algorithms.checksums.xxh64", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "borg/algorithms/checksums.pyx":87 * cdef XXH64_state_t* state * * def __cinit__(self, seed=0): # <<<<<<<<<<<<<< * self.state = XXH64_createState() * cdef unsigned long long _seed = seed */ /* Python wrapper */ static int __pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_1__cinit__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static int __pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_1__cinit__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { PyObject *__pyx_v_seed = 0; CYTHON_UNUSED Py_ssize_t __pyx_nargs; CYTHON_UNUSED PyObject *const *__pyx_kwvalues; PyObject* values[1] = {0}; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; int __pyx_r; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__cinit__ (wrapper)", 0); #if CYTHON_ASSUME_SAFE_MACROS __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); #else __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return -1; #endif __pyx_kwvalues = __Pyx_KwValues_VARARGS(__pyx_args, __pyx_nargs); { PyObject **__pyx_pyargnames[] = {&__pyx_n_s_seed,0}; values[0] = __Pyx_Arg_NewRef_VARARGS(((PyObject *)__pyx_int_0)); if (__pyx_kwds) { Py_ssize_t kw_args; switch (__pyx_nargs) { case 1: values[0] = __Pyx_Arg_VARARGS(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = __Pyx_NumKwargs_VARARGS(__pyx_kwds); switch (__pyx_nargs) { case 0: if (kw_args > 0) { PyObject* value = __Pyx_GetKwValue_VARARGS(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_seed); if (value) { values[0] = __Pyx_Arg_NewRef_VARARGS(value); kw_args--; } else if (unlikely(PyErr_Occurred())) __PYX_ERR(1, 87, __pyx_L3_error) } } if (unlikely(kw_args > 0)) { const Py_ssize_t kwd_pos_args = __pyx_nargs; if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "__cinit__") < 0)) __PYX_ERR(1, 87, __pyx_L3_error) } } else { switch (__pyx_nargs) { case 1: values[0] = __Pyx_Arg_VARARGS(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } } __pyx_v_seed = values[0]; } goto __pyx_L6_skip; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("__cinit__", 0, 0, 1, __pyx_nargs); __PYX_ERR(1, 87, __pyx_L3_error) __pyx_L6_skip:; goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; { Py_ssize_t __pyx_temp; for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { __Pyx_Arg_XDECREF_VARARGS(values[__pyx_temp]); } } __Pyx_AddTraceback("borg.algorithms.checksums.StreamingXXH64.__cinit__", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return -1; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64___cinit__(((struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *)__pyx_v_self), __pyx_v_seed); /* function exit code */ { Py_ssize_t __pyx_temp; for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { __Pyx_Arg_XDECREF_VARARGS(values[__pyx_temp]); } } __Pyx_RefNannyFinishContext(); return __pyx_r; } static int __pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64___cinit__(struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self, PyObject *__pyx_v_seed) { unsigned PY_LONG_LONG __pyx_v__seed; int __pyx_r; __Pyx_RefNannyDeclarations unsigned PY_LONG_LONG __pyx_t_1; int __pyx_t_2; PyObject *__pyx_t_3 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("__cinit__", 1); /* "borg/algorithms/checksums.pyx":88 * * def __cinit__(self, seed=0): * self.state = XXH64_createState() # <<<<<<<<<<<<<< * cdef unsigned long long _seed = seed * if XXH64_reset(self.state, _seed) != XXH_OK: */ __pyx_v_self->state = XXH64_createState(); /* "borg/algorithms/checksums.pyx":89 * def __cinit__(self, seed=0): * self.state = XXH64_createState() * cdef unsigned long long _seed = seed # <<<<<<<<<<<<<< * if XXH64_reset(self.state, _seed) != XXH_OK: * raise Exception('XXH64_reset failed') */ __pyx_t_1 = __Pyx_PyInt_As_unsigned_PY_LONG_LONG(__pyx_v_seed); if (unlikely((__pyx_t_1 == (unsigned PY_LONG_LONG)-1) && PyErr_Occurred())) __PYX_ERR(1, 89, __pyx_L1_error) __pyx_v__seed = __pyx_t_1; /* "borg/algorithms/checksums.pyx":90 * self.state = XXH64_createState() * cdef unsigned long long _seed = seed * if XXH64_reset(self.state, _seed) != XXH_OK: # <<<<<<<<<<<<<< * raise Exception('XXH64_reset failed') * */ __pyx_t_2 = (XXH64_reset(__pyx_v_self->state, __pyx_v__seed) != XXH_OK); if (unlikely(__pyx_t_2)) { /* "borg/algorithms/checksums.pyx":91 * cdef unsigned long long _seed = seed * if XXH64_reset(self.state, _seed) != XXH_OK: * raise Exception('XXH64_reset failed') # <<<<<<<<<<<<<< * * def __dealloc__(self): */ __pyx_t_3 = __Pyx_PyObject_Call(((PyObject *)(&((PyTypeObject*)PyExc_Exception)[0])), __pyx_tuple_, NULL); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 91, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_Raise(__pyx_t_3, 0, 0, 0); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __PYX_ERR(1, 91, __pyx_L1_error) /* "borg/algorithms/checksums.pyx":90 * self.state = XXH64_createState() * cdef unsigned long long _seed = seed * if XXH64_reset(self.state, _seed) != XXH_OK: # <<<<<<<<<<<<<< * raise Exception('XXH64_reset failed') * */ } /* "borg/algorithms/checksums.pyx":87 * cdef XXH64_state_t* state * * def __cinit__(self, seed=0): # <<<<<<<<<<<<<< * self.state = XXH64_createState() * cdef unsigned long long _seed = seed */ /* function exit code */ __pyx_r = 0; goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_3); __Pyx_AddTraceback("borg.algorithms.checksums.StreamingXXH64.__cinit__", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = -1; __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "borg/algorithms/checksums.pyx":93 * raise Exception('XXH64_reset failed') * * def __dealloc__(self): # <<<<<<<<<<<<<< * XXH64_freeState(self.state) * */ /* Python wrapper */ static void __pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_3__dealloc__(PyObject *__pyx_v_self); /*proto*/ static void __pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_3__dealloc__(PyObject *__pyx_v_self) { CYTHON_UNUSED PyObject *const *__pyx_kwvalues; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__dealloc__ (wrapper)", 0); __pyx_kwvalues = __Pyx_KwValues_VARARGS(__pyx_args, __pyx_nargs); __pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_2__dealloc__(((struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *)__pyx_v_self)); /* function exit code */ __Pyx_RefNannyFinishContext(); } static void __pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_2__dealloc__(struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self) { /* "borg/algorithms/checksums.pyx":94 * * def __dealloc__(self): * XXH64_freeState(self.state) # <<<<<<<<<<<<<< * * def update(self, data): */ (void)(XXH64_freeState(__pyx_v_self->state)); /* "borg/algorithms/checksums.pyx":93 * raise Exception('XXH64_reset failed') * * def __dealloc__(self): # <<<<<<<<<<<<<< * XXH64_freeState(self.state) * */ /* function exit code */ } /* "borg/algorithms/checksums.pyx":96 * XXH64_freeState(self.state) * * def update(self, data): # <<<<<<<<<<<<<< * cdef Py_buffer data_buf = ro_buffer(data) * try: */ /* Python wrapper */ static PyObject *__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_5update(PyObject *__pyx_v_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ); /*proto*/ static PyMethodDef __pyx_mdef_4borg_10algorithms_9checksums_14StreamingXXH64_5update = {"update", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_5update, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}; static PyObject *__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_5update(PyObject *__pyx_v_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ) { PyObject *__pyx_v_data = 0; #if !CYTHON_METH_FASTCALL CYTHON_UNUSED Py_ssize_t __pyx_nargs; #endif CYTHON_UNUSED PyObject *const *__pyx_kwvalues; PyObject* values[1] = {0}; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("update (wrapper)", 0); #if !CYTHON_METH_FASTCALL #if CYTHON_ASSUME_SAFE_MACROS __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); #else __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; #endif #endif __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); { PyObject **__pyx_pyargnames[] = {&__pyx_n_s_data,0}; if (__pyx_kwds) { Py_ssize_t kw_args; switch (__pyx_nargs) { case 1: values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = __Pyx_NumKwargs_FASTCALL(__pyx_kwds); switch (__pyx_nargs) { case 0: if (likely((values[0] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_data)) != 0)) { (void)__Pyx_Arg_NewRef_FASTCALL(values[0]); kw_args--; } else if (unlikely(PyErr_Occurred())) __PYX_ERR(1, 96, __pyx_L3_error) else goto __pyx_L5_argtuple_error; } if (unlikely(kw_args > 0)) { const Py_ssize_t kwd_pos_args = __pyx_nargs; if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "update") < 0)) __PYX_ERR(1, 96, __pyx_L3_error) } } else if (unlikely(__pyx_nargs != 1)) { goto __pyx_L5_argtuple_error; } else { values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); } __pyx_v_data = values[0]; } goto __pyx_L6_skip; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("update", 1, 1, 1, __pyx_nargs); __PYX_ERR(1, 96, __pyx_L3_error) __pyx_L6_skip:; goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; { Py_ssize_t __pyx_temp; for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); } } __Pyx_AddTraceback("borg.algorithms.checksums.StreamingXXH64.update", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_4update(((struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *)__pyx_v_self), __pyx_v_data); /* function exit code */ { Py_ssize_t __pyx_temp; for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); } } __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_4update(struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self, PyObject *__pyx_v_data) { Py_buffer __pyx_v_data_buf; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations Py_buffer __pyx_t_1; int __pyx_t_2; PyObject *__pyx_t_3 = NULL; int __pyx_t_4; int __pyx_t_5; char const *__pyx_t_6; PyObject *__pyx_t_7 = NULL; PyObject *__pyx_t_8 = NULL; PyObject *__pyx_t_9 = NULL; PyObject *__pyx_t_10 = NULL; PyObject *__pyx_t_11 = NULL; PyObject *__pyx_t_12 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("update", 1); /* "borg/algorithms/checksums.pyx":97 * * def update(self, data): * cdef Py_buffer data_buf = ro_buffer(data) # <<<<<<<<<<<<<< * try: * if XXH64_update(self.state, data_buf.buf, data_buf.len) != XXH_OK: */ __pyx_t_1 = __pyx_f_4borg_10algorithms_9checksums_ro_buffer(__pyx_v_data); if (unlikely(PyErr_Occurred())) __PYX_ERR(1, 97, __pyx_L1_error) __pyx_v_data_buf = __pyx_t_1; /* "borg/algorithms/checksums.pyx":98 * def update(self, data): * cdef Py_buffer data_buf = ro_buffer(data) * try: # <<<<<<<<<<<<<< * if XXH64_update(self.state, data_buf.buf, data_buf.len) != XXH_OK: * raise Exception('XXH64_update failed') */ /*try:*/ { /* "borg/algorithms/checksums.pyx":99 * cdef Py_buffer data_buf = ro_buffer(data) * try: * if XXH64_update(self.state, data_buf.buf, data_buf.len) != XXH_OK: # <<<<<<<<<<<<<< * raise Exception('XXH64_update failed') * finally: */ __pyx_t_2 = (XXH64_update(__pyx_v_self->state, __pyx_v_data_buf.buf, __pyx_v_data_buf.len) != XXH_OK); if (unlikely(__pyx_t_2)) { /* "borg/algorithms/checksums.pyx":100 * try: * if XXH64_update(self.state, data_buf.buf, data_buf.len) != XXH_OK: * raise Exception('XXH64_update failed') # <<<<<<<<<<<<<< * finally: * PyBuffer_Release(&data_buf) */ __pyx_t_3 = __Pyx_PyObject_Call(((PyObject *)(&((PyTypeObject*)PyExc_Exception)[0])), __pyx_tuple__2, NULL); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 100, __pyx_L4_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_Raise(__pyx_t_3, 0, 0, 0); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __PYX_ERR(1, 100, __pyx_L4_error) /* "borg/algorithms/checksums.pyx":99 * cdef Py_buffer data_buf = ro_buffer(data) * try: * if XXH64_update(self.state, data_buf.buf, data_buf.len) != XXH_OK: # <<<<<<<<<<<<<< * raise Exception('XXH64_update failed') * finally: */ } } /* "borg/algorithms/checksums.pyx":102 * raise Exception('XXH64_update failed') * finally: * PyBuffer_Release(&data_buf) # <<<<<<<<<<<<<< * * def digest(self): */ /*finally:*/ { /*normal exit:*/{ PyBuffer_Release((&__pyx_v_data_buf)); goto __pyx_L5; } __pyx_L4_error:; /*exception exit:*/{ __Pyx_PyThreadState_declare __Pyx_PyThreadState_assign __pyx_t_7 = 0; __pyx_t_8 = 0; __pyx_t_9 = 0; __pyx_t_10 = 0; __pyx_t_11 = 0; __pyx_t_12 = 0; __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0; if (PY_MAJOR_VERSION >= 3) __Pyx_ExceptionSwap(&__pyx_t_10, &__pyx_t_11, &__pyx_t_12); if ((PY_MAJOR_VERSION < 3) || unlikely(__Pyx_GetException(&__pyx_t_7, &__pyx_t_8, &__pyx_t_9) < 0)) __Pyx_ErrFetch(&__pyx_t_7, &__pyx_t_8, &__pyx_t_9); __Pyx_XGOTREF(__pyx_t_7); __Pyx_XGOTREF(__pyx_t_8); __Pyx_XGOTREF(__pyx_t_9); __Pyx_XGOTREF(__pyx_t_10); __Pyx_XGOTREF(__pyx_t_11); __Pyx_XGOTREF(__pyx_t_12); __pyx_t_4 = __pyx_lineno; __pyx_t_5 = __pyx_clineno; __pyx_t_6 = __pyx_filename; { PyBuffer_Release((&__pyx_v_data_buf)); } if (PY_MAJOR_VERSION >= 3) { __Pyx_XGIVEREF(__pyx_t_10); __Pyx_XGIVEREF(__pyx_t_11); __Pyx_XGIVEREF(__pyx_t_12); __Pyx_ExceptionReset(__pyx_t_10, __pyx_t_11, __pyx_t_12); } __Pyx_XGIVEREF(__pyx_t_7); __Pyx_XGIVEREF(__pyx_t_8); __Pyx_XGIVEREF(__pyx_t_9); __Pyx_ErrRestore(__pyx_t_7, __pyx_t_8, __pyx_t_9); __pyx_t_7 = 0; __pyx_t_8 = 0; __pyx_t_9 = 0; __pyx_t_10 = 0; __pyx_t_11 = 0; __pyx_t_12 = 0; __pyx_lineno = __pyx_t_4; __pyx_clineno = __pyx_t_5; __pyx_filename = __pyx_t_6; goto __pyx_L1_error; } __pyx_L5:; } /* "borg/algorithms/checksums.pyx":96 * XXH64_freeState(self.state) * * def update(self, data): # <<<<<<<<<<<<<< * cdef Py_buffer data_buf = ro_buffer(data) * try: */ /* function exit code */ __pyx_r = Py_None; __Pyx_INCREF(Py_None); goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_3); __Pyx_AddTraceback("borg.algorithms.checksums.StreamingXXH64.update", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "borg/algorithms/checksums.pyx":104 * PyBuffer_Release(&data_buf) * * def digest(self): # <<<<<<<<<<<<<< * cdef XXH64_hash_t hash * cdef XXH64_canonical_t digest */ /* Python wrapper */ static PyObject *__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_7digest(PyObject *__pyx_v_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ); /*proto*/ static PyMethodDef __pyx_mdef_4borg_10algorithms_9checksums_14StreamingXXH64_7digest = {"digest", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_7digest, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}; static PyObject *__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_7digest(PyObject *__pyx_v_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ) { #if !CYTHON_METH_FASTCALL CYTHON_UNUSED Py_ssize_t __pyx_nargs; #endif CYTHON_UNUSED PyObject *const *__pyx_kwvalues; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("digest (wrapper)", 0); #if !CYTHON_METH_FASTCALL #if CYTHON_ASSUME_SAFE_MACROS __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); #else __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; #endif #endif __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); if (unlikely(__pyx_nargs > 0)) { __Pyx_RaiseArgtupleInvalid("digest", 1, 0, 0, __pyx_nargs); return NULL;} if (unlikely(__pyx_kwds) && __Pyx_NumKwargs_FASTCALL(__pyx_kwds) && unlikely(!__Pyx_CheckKeywordStrings(__pyx_kwds, "digest", 0))) return NULL; __pyx_r = __pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_6digest(((struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *)__pyx_v_self)); /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_6digest(struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self) { XXH64_hash_t __pyx_v_hash; XXH64_canonical_t __pyx_v_digest; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations PyObject *__pyx_t_1 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("digest", 1); /* "borg/algorithms/checksums.pyx":107 * cdef XXH64_hash_t hash * cdef XXH64_canonical_t digest * hash = XXH64_digest(self.state) # <<<<<<<<<<<<<< * XXH64_canonicalFromHash(&digest, hash) * return PyBytes_FromStringAndSize( digest.digest, 8) */ __pyx_v_hash = XXH64_digest(__pyx_v_self->state); /* "borg/algorithms/checksums.pyx":108 * cdef XXH64_canonical_t digest * hash = XXH64_digest(self.state) * XXH64_canonicalFromHash(&digest, hash) # <<<<<<<<<<<<<< * return PyBytes_FromStringAndSize( digest.digest, 8) * */ XXH64_canonicalFromHash((&__pyx_v_digest), __pyx_v_hash); /* "borg/algorithms/checksums.pyx":109 * hash = XXH64_digest(self.state) * XXH64_canonicalFromHash(&digest, hash) * return PyBytes_FromStringAndSize( digest.digest, 8) # <<<<<<<<<<<<<< * * def hexdigest(self): */ __Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyBytes_FromStringAndSize(((char const *)__pyx_v_digest.digest), 8); if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 109, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; /* "borg/algorithms/checksums.pyx":104 * PyBuffer_Release(&data_buf) * * def digest(self): # <<<<<<<<<<<<<< * cdef XXH64_hash_t hash * cdef XXH64_canonical_t digest */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("borg.algorithms.checksums.StreamingXXH64.digest", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "borg/algorithms/checksums.pyx":111 * return PyBytes_FromStringAndSize( digest.digest, 8) * * def hexdigest(self): # <<<<<<<<<<<<<< * return bin_to_hex(self.digest()) */ /* Python wrapper */ static PyObject *__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_9hexdigest(PyObject *__pyx_v_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ); /*proto*/ static PyMethodDef __pyx_mdef_4borg_10algorithms_9checksums_14StreamingXXH64_9hexdigest = {"hexdigest", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_9hexdigest, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}; static PyObject *__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_9hexdigest(PyObject *__pyx_v_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ) { #if !CYTHON_METH_FASTCALL CYTHON_UNUSED Py_ssize_t __pyx_nargs; #endif CYTHON_UNUSED PyObject *const *__pyx_kwvalues; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("hexdigest (wrapper)", 0); #if !CYTHON_METH_FASTCALL #if CYTHON_ASSUME_SAFE_MACROS __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); #else __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; #endif #endif __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); if (unlikely(__pyx_nargs > 0)) { __Pyx_RaiseArgtupleInvalid("hexdigest", 1, 0, 0, __pyx_nargs); return NULL;} if (unlikely(__pyx_kwds) && __Pyx_NumKwargs_FASTCALL(__pyx_kwds) && unlikely(!__Pyx_CheckKeywordStrings(__pyx_kwds, "hexdigest", 0))) return NULL; __pyx_r = __pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_8hexdigest(((struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *)__pyx_v_self)); /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_8hexdigest(struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations PyObject *__pyx_t_1 = NULL; PyObject *__pyx_t_2 = NULL; PyObject *__pyx_t_3 = NULL; PyObject *__pyx_t_4 = NULL; PyObject *__pyx_t_5 = NULL; int __pyx_t_6; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("hexdigest", 1); /* "borg/algorithms/checksums.pyx":112 * * def hexdigest(self): * return bin_to_hex(self.digest()) # <<<<<<<<<<<<<< */ __Pyx_XDECREF(__pyx_r); __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_bin_to_hex); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 112, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); __pyx_t_4 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self), __pyx_n_s_digest); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 112, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __pyx_t_5 = NULL; __pyx_t_6 = 0; #if CYTHON_UNPACK_METHODS if (likely(PyMethod_Check(__pyx_t_4))) { __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_4); if (likely(__pyx_t_5)) { PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_4); __Pyx_INCREF(__pyx_t_5); __Pyx_INCREF(function); __Pyx_DECREF_SET(__pyx_t_4, function); __pyx_t_6 = 1; } } #endif { PyObject *__pyx_callargs[2] = {__pyx_t_5, NULL}; __pyx_t_3 = __Pyx_PyObject_FastCall(__pyx_t_4, __pyx_callargs+1-__pyx_t_6, 0+__pyx_t_6); __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0; if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 112, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; } __pyx_t_4 = NULL; __pyx_t_6 = 0; #if CYTHON_UNPACK_METHODS if (unlikely(PyMethod_Check(__pyx_t_2))) { __pyx_t_4 = PyMethod_GET_SELF(__pyx_t_2); if (likely(__pyx_t_4)) { PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_2); __Pyx_INCREF(__pyx_t_4); __Pyx_INCREF(function); __Pyx_DECREF_SET(__pyx_t_2, function); __pyx_t_6 = 1; } } #endif { PyObject *__pyx_callargs[2] = {__pyx_t_4, __pyx_t_3}; __pyx_t_1 = __Pyx_PyObject_FastCall(__pyx_t_2, __pyx_callargs+1-__pyx_t_6, 1+__pyx_t_6); __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 112, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; } __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; /* "borg/algorithms/checksums.pyx":111 * return PyBytes_FromStringAndSize( digest.digest, 8) * * def hexdigest(self): # <<<<<<<<<<<<<< * return bin_to_hex(self.digest()) */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_2); __Pyx_XDECREF(__pyx_t_3); __Pyx_XDECREF(__pyx_t_4); __Pyx_XDECREF(__pyx_t_5); __Pyx_AddTraceback("borg.algorithms.checksums.StreamingXXH64.hexdigest", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "(tree fragment)":1 * def __reduce_cython__(self): # <<<<<<<<<<<<<< * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" * def __setstate_cython__(self, __pyx_state): */ /* Python wrapper */ static PyObject *__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_11__reduce_cython__(PyObject *__pyx_v_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ); /*proto*/ static PyMethodDef __pyx_mdef_4borg_10algorithms_9checksums_14StreamingXXH64_11__reduce_cython__ = {"__reduce_cython__", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_11__reduce_cython__, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}; static PyObject *__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_11__reduce_cython__(PyObject *__pyx_v_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ) { #if !CYTHON_METH_FASTCALL CYTHON_UNUSED Py_ssize_t __pyx_nargs; #endif CYTHON_UNUSED PyObject *const *__pyx_kwvalues; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__reduce_cython__ (wrapper)", 0); #if !CYTHON_METH_FASTCALL #if CYTHON_ASSUME_SAFE_MACROS __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); #else __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; #endif #endif __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); if (unlikely(__pyx_nargs > 0)) { __Pyx_RaiseArgtupleInvalid("__reduce_cython__", 1, 0, 0, __pyx_nargs); return NULL;} if (unlikely(__pyx_kwds) && __Pyx_NumKwargs_FASTCALL(__pyx_kwds) && unlikely(!__Pyx_CheckKeywordStrings(__pyx_kwds, "__reduce_cython__", 0))) return NULL; __pyx_r = __pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_10__reduce_cython__(((struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *)__pyx_v_self)); /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_10__reduce_cython__(CYTHON_UNUSED struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("__reduce_cython__", 1); /* "(tree fragment)":2 * def __reduce_cython__(self): * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" # <<<<<<<<<<<<<< * def __setstate_cython__(self, __pyx_state): * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" */ __Pyx_Raise(__pyx_builtin_TypeError, __pyx_kp_s_no_default___reduce___due_to_non, 0, 0); __PYX_ERR(0, 2, __pyx_L1_error) /* "(tree fragment)":1 * def __reduce_cython__(self): # <<<<<<<<<<<<<< * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" * def __setstate_cython__(self, __pyx_state): */ /* function exit code */ __pyx_L1_error:; __Pyx_AddTraceback("borg.algorithms.checksums.StreamingXXH64.__reduce_cython__", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "(tree fragment)":3 * def __reduce_cython__(self): * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" * def __setstate_cython__(self, __pyx_state): # <<<<<<<<<<<<<< * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" */ /* Python wrapper */ static PyObject *__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_13__setstate_cython__(PyObject *__pyx_v_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ); /*proto*/ static PyMethodDef __pyx_mdef_4borg_10algorithms_9checksums_14StreamingXXH64_13__setstate_cython__ = {"__setstate_cython__", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_13__setstate_cython__, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}; static PyObject *__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_13__setstate_cython__(PyObject *__pyx_v_self, #if CYTHON_METH_FASTCALL PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds #else PyObject *__pyx_args, PyObject *__pyx_kwds #endif ) { CYTHON_UNUSED PyObject *__pyx_v___pyx_state = 0; #if !CYTHON_METH_FASTCALL CYTHON_UNUSED Py_ssize_t __pyx_nargs; #endif CYTHON_UNUSED PyObject *const *__pyx_kwvalues; PyObject* values[1] = {0}; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__setstate_cython__ (wrapper)", 0); #if !CYTHON_METH_FASTCALL #if CYTHON_ASSUME_SAFE_MACROS __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); #else __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; #endif #endif __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); { PyObject **__pyx_pyargnames[] = {&__pyx_n_s_pyx_state,0}; if (__pyx_kwds) { Py_ssize_t kw_args; switch (__pyx_nargs) { case 1: values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = __Pyx_NumKwargs_FASTCALL(__pyx_kwds); switch (__pyx_nargs) { case 0: if (likely((values[0] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_pyx_state)) != 0)) { (void)__Pyx_Arg_NewRef_FASTCALL(values[0]); kw_args--; } else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) else goto __pyx_L5_argtuple_error; } if (unlikely(kw_args > 0)) { const Py_ssize_t kwd_pos_args = __pyx_nargs; if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "__setstate_cython__") < 0)) __PYX_ERR(0, 3, __pyx_L3_error) } } else if (unlikely(__pyx_nargs != 1)) { goto __pyx_L5_argtuple_error; } else { values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); } __pyx_v___pyx_state = values[0]; } goto __pyx_L6_skip; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("__setstate_cython__", 1, 1, 1, __pyx_nargs); __PYX_ERR(0, 3, __pyx_L3_error) __pyx_L6_skip:; goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; { Py_ssize_t __pyx_temp; for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); } } __Pyx_AddTraceback("borg.algorithms.checksums.StreamingXXH64.__setstate_cython__", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_12__setstate_cython__(((struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *)__pyx_v_self), __pyx_v___pyx_state); /* function exit code */ { Py_ssize_t __pyx_temp; for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); } } __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_4borg_10algorithms_9checksums_14StreamingXXH64_12__setstate_cython__(CYTHON_UNUSED struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64 *__pyx_v_self, CYTHON_UNUSED PyObject *__pyx_v___pyx_state) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("__setstate_cython__", 1); /* "(tree fragment)":4 * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" * def __setstate_cython__(self, __pyx_state): * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" # <<<<<<<<<<<<<< */ __Pyx_Raise(__pyx_builtin_TypeError, __pyx_kp_s_no_default___reduce___due_to_non, 0, 0); __PYX_ERR(0, 4, __pyx_L1_error) /* "(tree fragment)":3 * def __reduce_cython__(self): * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" * def __setstate_cython__(self, __pyx_state): # <<<<<<<<<<<<<< * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" */ /* function exit code */ __pyx_L1_error:; __Pyx_AddTraceback("borg.algorithms.checksums.StreamingXXH64.__setstate_cython__", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_tp_new_4borg_10algorithms_9checksums_StreamingXXH64(PyTypeObject *t, PyObject *a, PyObject *k) { PyObject *o; #if CYTHON_COMPILING_IN_LIMITED_API allocfunc alloc_func = (allocfunc)PyType_GetSlot(t, Py_tp_alloc); o = alloc_func(t, 0); #else if (likely(!__Pyx_PyType_HasFeature(t, Py_TPFLAGS_IS_ABSTRACT))) { o = (*t->tp_alloc)(t, 0); } else { o = (PyObject *) PyBaseObject_Type.tp_new(t, __pyx_empty_tuple, 0); } if (unlikely(!o)) return 0; #endif if (unlikely(__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_1__cinit__(o, a, k) < 0)) goto bad; return o; bad: Py_DECREF(o); o = 0; return NULL; } static void __pyx_tp_dealloc_4borg_10algorithms_9checksums_StreamingXXH64(PyObject *o) { #if CYTHON_USE_TP_FINALIZE if (unlikely((PY_VERSION_HEX >= 0x03080000 || __Pyx_PyType_HasFeature(Py_TYPE(o), Py_TPFLAGS_HAVE_FINALIZE)) && __Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && (!PyType_IS_GC(Py_TYPE(o)) || !__Pyx_PyObject_GC_IsFinalized(o))) { if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_4borg_10algorithms_9checksums_StreamingXXH64) { if (PyObject_CallFinalizerFromDealloc(o)) return; } } #endif { PyObject *etype, *eval, *etb; PyErr_Fetch(&etype, &eval, &etb); __Pyx_SET_REFCNT(o, Py_REFCNT(o) + 1); __pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_3__dealloc__(o); __Pyx_SET_REFCNT(o, Py_REFCNT(o) - 1); PyErr_Restore(etype, eval, etb); } #if CYTHON_USE_TYPE_SLOTS || CYTHON_COMPILING_IN_PYPY (*Py_TYPE(o)->tp_free)(o); #else { freefunc tp_free = (freefunc)PyType_GetSlot(Py_TYPE(o), Py_tp_free); if (tp_free) tp_free(o); } #endif } static PyMethodDef __pyx_methods_4borg_10algorithms_9checksums_StreamingXXH64[] = { {"update", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_5update, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}, {"digest", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_7digest, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}, {"hexdigest", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_9hexdigest, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}, {"__reduce_cython__", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_11__reduce_cython__, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}, {"__setstate_cython__", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_4borg_10algorithms_9checksums_14StreamingXXH64_13__setstate_cython__, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0}, {0, 0, 0, 0} }; #if CYTHON_USE_TYPE_SPECS static PyType_Slot __pyx_type_4borg_10algorithms_9checksums_StreamingXXH64_slots[] = { {Py_tp_dealloc, (void *)__pyx_tp_dealloc_4borg_10algorithms_9checksums_StreamingXXH64}, {Py_tp_methods, (void *)__pyx_methods_4borg_10algorithms_9checksums_StreamingXXH64}, {Py_tp_new, (void *)__pyx_tp_new_4borg_10algorithms_9checksums_StreamingXXH64}, {0, 0}, }; static PyType_Spec __pyx_type_4borg_10algorithms_9checksums_StreamingXXH64_spec = { "borg.algorithms.checksums.StreamingXXH64", sizeof(struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64), 0, Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_BASETYPE, __pyx_type_4borg_10algorithms_9checksums_StreamingXXH64_slots, }; #else static PyTypeObject __pyx_type_4borg_10algorithms_9checksums_StreamingXXH64 = { PyVarObject_HEAD_INIT(0, 0) "borg.algorithms.checksums.""StreamingXXH64", /*tp_name*/ sizeof(struct __pyx_obj_4borg_10algorithms_9checksums_StreamingXXH64), /*tp_basicsize*/ 0, /*tp_itemsize*/ __pyx_tp_dealloc_4borg_10algorithms_9checksums_StreamingXXH64, /*tp_dealloc*/ #if PY_VERSION_HEX < 0x030800b4 0, /*tp_print*/ #endif #if PY_VERSION_HEX >= 0x030800b4 0, /*tp_vectorcall_offset*/ #endif 0, /*tp_getattr*/ 0, /*tp_setattr*/ #if PY_MAJOR_VERSION < 3 0, /*tp_compare*/ #endif #if PY_MAJOR_VERSION >= 3 0, /*tp_as_async*/ #endif 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_BASETYPE, /*tp_flags*/ 0, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ __pyx_methods_4borg_10algorithms_9checksums_StreamingXXH64, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ #if !CYTHON_USE_TYPE_SPECS 0, /*tp_dictoffset*/ #endif 0, /*tp_init*/ 0, /*tp_alloc*/ __pyx_tp_new_4borg_10algorithms_9checksums_StreamingXXH64, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ 0, /*tp_bases*/ 0, /*tp_mro*/ 0, /*tp_cache*/ 0, /*tp_subclasses*/ 0, /*tp_weaklist*/ 0, /*tp_del*/ 0, /*tp_version_tag*/ #if PY_VERSION_HEX >= 0x030400a1 #if CYTHON_USE_TP_FINALIZE 0, /*tp_finalize*/ #else NULL, /*tp_finalize*/ #endif #endif #if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800) 0, /*tp_vectorcall*/ #endif #if __PYX_NEED_TP_PRINT_SLOT == 1 0, /*tp_print*/ #endif #if PY_VERSION_HEX >= 0x030C0000 0, /*tp_watched*/ #endif #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000 0, /*tp_pypy_flags*/ #endif }; #endif static PyMethodDef __pyx_methods[] = { {0, 0, 0, 0} }; #ifndef CYTHON_SMALL_CODE #if defined(__clang__) #define CYTHON_SMALL_CODE #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) #define CYTHON_SMALL_CODE __attribute__((cold)) #else #define CYTHON_SMALL_CODE #endif #endif /* #### Code section: pystring_table ### */ static int __Pyx_CreateStringTabAndInitStrings(void) { __Pyx_StringTabEntry __pyx_string_tab[] = { {&__pyx_n_s_StreamingXXH64, __pyx_k_StreamingXXH64, sizeof(__pyx_k_StreamingXXH64), 0, 0, 1, 1}, {&__pyx_n_s_StreamingXXH64___reduce_cython, __pyx_k_StreamingXXH64___reduce_cython, sizeof(__pyx_k_StreamingXXH64___reduce_cython), 0, 0, 1, 1}, {&__pyx_n_s_StreamingXXH64___setstate_cython, __pyx_k_StreamingXXH64___setstate_cython, sizeof(__pyx_k_StreamingXXH64___setstate_cython), 0, 0, 1, 1}, {&__pyx_n_s_StreamingXXH64_digest, __pyx_k_StreamingXXH64_digest, sizeof(__pyx_k_StreamingXXH64_digest), 0, 0, 1, 1}, {&__pyx_n_s_StreamingXXH64_hexdigest, __pyx_k_StreamingXXH64_hexdigest, sizeof(__pyx_k_StreamingXXH64_hexdigest), 0, 0, 1, 1}, {&__pyx_n_s_StreamingXXH64_update, __pyx_k_StreamingXXH64_update, sizeof(__pyx_k_StreamingXXH64_update), 0, 0, 1, 1}, {&__pyx_n_s_TypeError, __pyx_k_TypeError, sizeof(__pyx_k_TypeError), 0, 0, 1, 1}, {&__pyx_kp_u_XXH64_reset_failed, __pyx_k_XXH64_reset_failed, sizeof(__pyx_k_XXH64_reset_failed), 0, 1, 0, 0}, {&__pyx_kp_u_XXH64_update_failed, __pyx_k_XXH64_update_failed, sizeof(__pyx_k_XXH64_update_failed), 0, 1, 0, 0}, {&__pyx_n_s__19, __pyx_k__19, sizeof(__pyx_k__19), 0, 0, 1, 1}, {&__pyx_kp_u__3, __pyx_k__3, sizeof(__pyx_k__3), 0, 1, 0, 0}, {&__pyx_n_s_asyncio_coroutines, __pyx_k_asyncio_coroutines, sizeof(__pyx_k_asyncio_coroutines), 0, 0, 1, 1}, {&__pyx_n_s_bin_to_hex, __pyx_k_bin_to_hex, sizeof(__pyx_k_bin_to_hex), 0, 0, 1, 1}, {&__pyx_n_s_borg_algorithms_checksums, __pyx_k_borg_algorithms_checksums, sizeof(__pyx_k_borg_algorithms_checksums), 0, 0, 1, 1}, {&__pyx_n_s_cline_in_traceback, __pyx_k_cline_in_traceback, sizeof(__pyx_k_cline_in_traceback), 0, 0, 1, 1}, {&__pyx_n_s_crc32, __pyx_k_crc32, sizeof(__pyx_k_crc32), 0, 0, 1, 1}, {&__pyx_n_s_crc32_clmul, __pyx_k_crc32_clmul, sizeof(__pyx_k_crc32_clmul), 0, 0, 1, 1}, {&__pyx_n_s_crc32_slice_by_8, __pyx_k_crc32_slice_by_8, sizeof(__pyx_k_crc32_slice_by_8), 0, 0, 1, 1}, {&__pyx_n_s_data, __pyx_k_data, sizeof(__pyx_k_data), 0, 0, 1, 1}, {&__pyx_n_s_data_buf, __pyx_k_data_buf, sizeof(__pyx_k_data_buf), 0, 0, 1, 1}, {&__pyx_n_s_digest, __pyx_k_digest, sizeof(__pyx_k_digest), 0, 0, 1, 1}, {&__pyx_kp_u_disable, __pyx_k_disable, sizeof(__pyx_k_disable), 0, 1, 0, 0}, {&__pyx_kp_u_enable, __pyx_k_enable, sizeof(__pyx_k_enable), 0, 1, 0, 0}, {&__pyx_kp_u_gc, __pyx_k_gc, sizeof(__pyx_k_gc), 0, 1, 0, 0}, {&__pyx_n_s_getstate, __pyx_k_getstate, sizeof(__pyx_k_getstate), 0, 0, 1, 1}, {&__pyx_n_s_hash, __pyx_k_hash, sizeof(__pyx_k_hash), 0, 0, 1, 1}, {&__pyx_n_s_have_clmul, __pyx_k_have_clmul, sizeof(__pyx_k_have_clmul), 0, 0, 1, 1}, {&__pyx_n_s_helpers, __pyx_k_helpers, sizeof(__pyx_k_helpers), 0, 0, 1, 1}, {&__pyx_n_s_hexdigest, __pyx_k_hexdigest, sizeof(__pyx_k_hexdigest), 0, 0, 1, 1}, {&__pyx_n_s_import, __pyx_k_import, sizeof(__pyx_k_import), 0, 0, 1, 1}, {&__pyx_n_s_is_coroutine, __pyx_k_is_coroutine, sizeof(__pyx_k_is_coroutine), 0, 0, 1, 1}, {&__pyx_kp_u_isenabled, __pyx_k_isenabled, sizeof(__pyx_k_isenabled), 0, 1, 0, 0}, {&__pyx_n_s_main, __pyx_k_main, sizeof(__pyx_k_main), 0, 0, 1, 1}, {&__pyx_n_s_name, __pyx_k_name, sizeof(__pyx_k_name), 0, 0, 1, 1}, {&__pyx_kp_s_no_default___reduce___due_to_non, __pyx_k_no_default___reduce___due_to_non, sizeof(__pyx_k_no_default___reduce___due_to_non), 0, 0, 1, 0}, {&__pyx_n_s_pyx_state, __pyx_k_pyx_state, sizeof(__pyx_k_pyx_state), 0, 0, 1, 1}, {&__pyx_n_s_reduce, __pyx_k_reduce, sizeof(__pyx_k_reduce), 0, 0, 1, 1}, {&__pyx_n_s_reduce_cython, __pyx_k_reduce_cython, sizeof(__pyx_k_reduce_cython), 0, 0, 1, 1}, {&__pyx_n_s_reduce_ex, __pyx_k_reduce_ex, sizeof(__pyx_k_reduce_ex), 0, 0, 1, 1}, {&__pyx_n_s_seed, __pyx_k_seed, sizeof(__pyx_k_seed), 0, 0, 1, 1}, {&__pyx_n_s_seed_2, __pyx_k_seed_2, sizeof(__pyx_k_seed_2), 0, 0, 1, 1}, {&__pyx_n_s_self, __pyx_k_self, sizeof(__pyx_k_self), 0, 0, 1, 1}, {&__pyx_n_s_setstate, __pyx_k_setstate, sizeof(__pyx_k_setstate), 0, 0, 1, 1}, {&__pyx_n_s_setstate_cython, __pyx_k_setstate_cython, sizeof(__pyx_k_setstate_cython), 0, 0, 1, 1}, {&__pyx_kp_s_src_borg_algorithms_checksums_py, __pyx_k_src_borg_algorithms_checksums_py, sizeof(__pyx_k_src_borg_algorithms_checksums_py), 0, 0, 1, 0}, {&__pyx_kp_s_stringsource, __pyx_k_stringsource, sizeof(__pyx_k_stringsource), 0, 0, 1, 0}, {&__pyx_n_s_test, __pyx_k_test, sizeof(__pyx_k_test), 0, 0, 1, 1}, {&__pyx_n_s_update, __pyx_k_update, sizeof(__pyx_k_update), 0, 0, 1, 1}, {&__pyx_n_s_val, __pyx_k_val, sizeof(__pyx_k_val), 0, 0, 1, 1}, {&__pyx_n_s_value, __pyx_k_value, sizeof(__pyx_k_value), 0, 0, 1, 1}, {&__pyx_n_s_xxh64, __pyx_k_xxh64, sizeof(__pyx_k_xxh64), 0, 0, 1, 1}, {0, 0, 0, 0, 0, 0, 0} }; return __Pyx_InitStrings(__pyx_string_tab); } /* #### Code section: cached_builtins ### */ static CYTHON_SMALL_CODE int __Pyx_InitCachedBuiltins(void) { __pyx_builtin_TypeError = __Pyx_GetBuiltinName(__pyx_n_s_TypeError); if (!__pyx_builtin_TypeError) __PYX_ERR(0, 2, __pyx_L1_error) return 0; __pyx_L1_error:; return -1; } /* #### Code section: cached_constants ### */ static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(void) { __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__Pyx_InitCachedConstants", 0); /* "borg/algorithms/checksums.pyx":91 * cdef unsigned long long _seed = seed * if XXH64_reset(self.state, _seed) != XXH_OK: * raise Exception('XXH64_reset failed') # <<<<<<<<<<<<<< * * def __dealloc__(self): */ __pyx_tuple_ = PyTuple_Pack(1, __pyx_kp_u_XXH64_reset_failed); if (unlikely(!__pyx_tuple_)) __PYX_ERR(1, 91, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple_); __Pyx_GIVEREF(__pyx_tuple_); /* "borg/algorithms/checksums.pyx":100 * try: * if XXH64_update(self.state, data_buf.buf, data_buf.len) != XXH_OK: * raise Exception('XXH64_update failed') # <<<<<<<<<<<<<< * finally: * PyBuffer_Release(&data_buf) */ __pyx_tuple__2 = PyTuple_Pack(1, __pyx_kp_u_XXH64_update_failed); if (unlikely(!__pyx_tuple__2)) __PYX_ERR(1, 100, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__2); __Pyx_GIVEREF(__pyx_tuple__2); /* "borg/algorithms/checksums.pyx":46 * * * def crc32_slice_by_8(data, value=0): # <<<<<<<<<<<<<< * cdef Py_buffer data_buf = ro_buffer(data) * cdef uint32_t val = value */ __pyx_tuple__4 = PyTuple_Pack(4, __pyx_n_s_data, __pyx_n_s_value, __pyx_n_s_data_buf, __pyx_n_s_val); if (unlikely(!__pyx_tuple__4)) __PYX_ERR(1, 46, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__4); __Pyx_GIVEREF(__pyx_tuple__4); __pyx_codeobj__5 = (PyObject*)__Pyx_PyCode_New(2, 0, 0, 4, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__4, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_src_borg_algorithms_checksums_py, __pyx_n_s_crc32_slice_by_8, 46, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__5)) __PYX_ERR(1, 46, __pyx_L1_error) __pyx_tuple__6 = PyTuple_Pack(1, ((PyObject *)__pyx_int_0)); if (unlikely(!__pyx_tuple__6)) __PYX_ERR(1, 46, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__6); __Pyx_GIVEREF(__pyx_tuple__6); /* "borg/algorithms/checksums.pyx":55 * * * def crc32_clmul(data, value=0): # <<<<<<<<<<<<<< * cdef Py_buffer data_buf = ro_buffer(data) * cdef uint32_t val = value */ __pyx_codeobj__7 = (PyObject*)__Pyx_PyCode_New(2, 0, 0, 4, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__4, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_src_borg_algorithms_checksums_py, __pyx_n_s_crc32_clmul, 55, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__7)) __PYX_ERR(1, 55, __pyx_L1_error) /* "borg/algorithms/checksums.pyx":71 * * * def xxh64(data, seed=0): # <<<<<<<<<<<<<< * cdef unsigned long long _seed = seed * cdef XXH64_hash_t hash */ __pyx_tuple__8 = PyTuple_Pack(6, __pyx_n_s_data, __pyx_n_s_seed, __pyx_n_s_seed_2, __pyx_n_s_hash, __pyx_n_s_digest, __pyx_n_s_data_buf); if (unlikely(!__pyx_tuple__8)) __PYX_ERR(1, 71, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__8); __Pyx_GIVEREF(__pyx_tuple__8); __pyx_codeobj__9 = (PyObject*)__Pyx_PyCode_New(2, 0, 0, 6, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__8, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_src_borg_algorithms_checksums_py, __pyx_n_s_xxh64, 71, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__9)) __PYX_ERR(1, 71, __pyx_L1_error) /* "borg/algorithms/checksums.pyx":96 * XXH64_freeState(self.state) * * def update(self, data): # <<<<<<<<<<<<<< * cdef Py_buffer data_buf = ro_buffer(data) * try: */ __pyx_tuple__10 = PyTuple_Pack(3, __pyx_n_s_self, __pyx_n_s_data, __pyx_n_s_data_buf); if (unlikely(!__pyx_tuple__10)) __PYX_ERR(1, 96, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__10); __Pyx_GIVEREF(__pyx_tuple__10); __pyx_codeobj__11 = (PyObject*)__Pyx_PyCode_New(2, 0, 0, 3, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__10, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_src_borg_algorithms_checksums_py, __pyx_n_s_update, 96, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__11)) __PYX_ERR(1, 96, __pyx_L1_error) /* "borg/algorithms/checksums.pyx":104 * PyBuffer_Release(&data_buf) * * def digest(self): # <<<<<<<<<<<<<< * cdef XXH64_hash_t hash * cdef XXH64_canonical_t digest */ __pyx_tuple__12 = PyTuple_Pack(3, __pyx_n_s_self, __pyx_n_s_hash, __pyx_n_s_digest); if (unlikely(!__pyx_tuple__12)) __PYX_ERR(1, 104, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__12); __Pyx_GIVEREF(__pyx_tuple__12); __pyx_codeobj__13 = (PyObject*)__Pyx_PyCode_New(1, 0, 0, 3, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__12, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_src_borg_algorithms_checksums_py, __pyx_n_s_digest, 104, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__13)) __PYX_ERR(1, 104, __pyx_L1_error) /* "borg/algorithms/checksums.pyx":111 * return PyBytes_FromStringAndSize( digest.digest, 8) * * def hexdigest(self): # <<<<<<<<<<<<<< * return bin_to_hex(self.digest()) */ __pyx_tuple__14 = PyTuple_Pack(1, __pyx_n_s_self); if (unlikely(!__pyx_tuple__14)) __PYX_ERR(1, 111, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__14); __Pyx_GIVEREF(__pyx_tuple__14); __pyx_codeobj__15 = (PyObject*)__Pyx_PyCode_New(1, 0, 0, 1, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__14, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_src_borg_algorithms_checksums_py, __pyx_n_s_hexdigest, 111, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__15)) __PYX_ERR(1, 111, __pyx_L1_error) /* "(tree fragment)":1 * def __reduce_cython__(self): # <<<<<<<<<<<<<< * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" * def __setstate_cython__(self, __pyx_state): */ __pyx_codeobj__16 = (PyObject*)__Pyx_PyCode_New(1, 0, 0, 1, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__14, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_stringsource, __pyx_n_s_reduce_cython, 1, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__16)) __PYX_ERR(0, 1, __pyx_L1_error) /* "(tree fragment)":3 * def __reduce_cython__(self): * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" * def __setstate_cython__(self, __pyx_state): # <<<<<<<<<<<<<< * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" */ __pyx_tuple__17 = PyTuple_Pack(2, __pyx_n_s_self, __pyx_n_s_pyx_state); if (unlikely(!__pyx_tuple__17)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__17); __Pyx_GIVEREF(__pyx_tuple__17); __pyx_codeobj__18 = (PyObject*)__Pyx_PyCode_New(2, 0, 0, 2, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__17, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_stringsource, __pyx_n_s_setstate_cython, 3, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__18)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_RefNannyFinishContext(); return 0; __pyx_L1_error:; __Pyx_RefNannyFinishContext(); return -1; } /* #### Code section: init_constants ### */ static CYTHON_SMALL_CODE int __Pyx_InitConstants(void) { if (__Pyx_CreateStringTabAndInitStrings() < 0) __PYX_ERR(1, 1, __pyx_L1_error); __pyx_int_0 = PyInt_FromLong(0); if (unlikely(!__pyx_int_0)) __PYX_ERR(1, 1, __pyx_L1_error) return 0; __pyx_L1_error:; return -1; } /* #### Code section: init_globals ### */ static CYTHON_SMALL_CODE int __Pyx_InitGlobals(void) { return 0; } /* #### Code section: init_module ### */ static CYTHON_SMALL_CODE int __Pyx_modinit_global_init_code(void); /*proto*/ static CYTHON_SMALL_CODE int __Pyx_modinit_variable_export_code(void); /*proto*/ static CYTHON_SMALL_CODE int __Pyx_modinit_function_export_code(void); /*proto*/ static CYTHON_SMALL_CODE int __Pyx_modinit_type_init_code(void); /*proto*/ static CYTHON_SMALL_CODE int __Pyx_modinit_type_import_code(void); /*proto*/ static CYTHON_SMALL_CODE int __Pyx_modinit_variable_import_code(void); /*proto*/ static CYTHON_SMALL_CODE int __Pyx_modinit_function_import_code(void); /*proto*/ static int __Pyx_modinit_global_init_code(void) { __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__Pyx_modinit_global_init_code", 0); /*--- Global init code ---*/ __Pyx_RefNannyFinishContext(); return 0; } static int __Pyx_modinit_variable_export_code(void) { __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__Pyx_modinit_variable_export_code", 0); /*--- Variable export code ---*/ __Pyx_RefNannyFinishContext(); return 0; } static int __Pyx_modinit_function_export_code(void) { __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__Pyx_modinit_function_export_code", 0); /*--- Function export code ---*/ __Pyx_RefNannyFinishContext(); return 0; } static int __Pyx_modinit_type_init_code(void) { __Pyx_RefNannyDeclarations int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("__Pyx_modinit_type_init_code", 0); /*--- Type init code ---*/ #if CYTHON_USE_TYPE_SPECS __pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64 = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_4borg_10algorithms_9checksums_StreamingXXH64_spec, NULL); if (unlikely(!__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64)) __PYX_ERR(1, 84, __pyx_L1_error) if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_4borg_10algorithms_9checksums_StreamingXXH64_spec, __pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64) < 0) __PYX_ERR(1, 84, __pyx_L1_error) #else __pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64 = &__pyx_type_4borg_10algorithms_9checksums_StreamingXXH64; #endif #if !CYTHON_COMPILING_IN_LIMITED_API #endif #if !CYTHON_USE_TYPE_SPECS if (__Pyx_PyType_Ready(__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64) < 0) __PYX_ERR(1, 84, __pyx_L1_error) #endif #if PY_MAJOR_VERSION < 3 __pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64->tp_print = 0; #endif #if !CYTHON_COMPILING_IN_LIMITED_API if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64->tp_dictoffset && __pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64->tp_getattro == PyObject_GenericGetAttr)) { __pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64->tp_getattro = __Pyx_PyObject_GenericGetAttr; } #endif if (PyObject_SetAttr(__pyx_m, __pyx_n_s_StreamingXXH64, (PyObject *) __pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64) < 0) __PYX_ERR(1, 84, __pyx_L1_error) #if !CYTHON_COMPILING_IN_LIMITED_API if (__Pyx_setup_reduce((PyObject *) __pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64) < 0) __PYX_ERR(1, 84, __pyx_L1_error) #endif __Pyx_RefNannyFinishContext(); return 0; __pyx_L1_error:; __Pyx_RefNannyFinishContext(); return -1; } static int __Pyx_modinit_type_import_code(void) { __Pyx_RefNannyDeclarations PyObject *__pyx_t_1 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannySetupContext("__Pyx_modinit_type_import_code", 0); /*--- Type import code ---*/ __pyx_t_1 = PyImport_ImportModule(__Pyx_BUILTIN_MODULE_NAME); if (unlikely(!__pyx_t_1)) __PYX_ERR(2, 9, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_ptype_7cpython_4type_type = __Pyx_ImportType_3_0_9(__pyx_t_1, __Pyx_BUILTIN_MODULE_NAME, "type", #if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x050B0000 sizeof(PyTypeObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_9(PyTypeObject), #elif CYTHON_COMPILING_IN_LIMITED_API sizeof(PyTypeObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_9(PyTypeObject), #else sizeof(PyHeapTypeObject), __PYX_GET_STRUCT_ALIGNMENT_3_0_9(PyHeapTypeObject), #endif __Pyx_ImportType_CheckSize_Warn_3_0_9); if (!__pyx_ptype_7cpython_4type_type) __PYX_ERR(2, 9, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_RefNannyFinishContext(); return 0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_RefNannyFinishContext(); return -1; } static int __Pyx_modinit_variable_import_code(void) { __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__Pyx_modinit_variable_import_code", 0); /*--- Variable import code ---*/ __Pyx_RefNannyFinishContext(); return 0; } static int __Pyx_modinit_function_import_code(void) { __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("__Pyx_modinit_function_import_code", 0); /*--- Function import code ---*/ __Pyx_RefNannyFinishContext(); return 0; } #if PY_MAJOR_VERSION >= 3 #if CYTHON_PEP489_MULTI_PHASE_INIT static PyObject* __pyx_pymod_create(PyObject *spec, PyModuleDef *def); /*proto*/ static int __pyx_pymod_exec_checksums(PyObject* module); /*proto*/ static PyModuleDef_Slot __pyx_moduledef_slots[] = { {Py_mod_create, (void*)__pyx_pymod_create}, {Py_mod_exec, (void*)__pyx_pymod_exec_checksums}, {0, NULL} }; #endif #ifdef __cplusplus namespace { struct PyModuleDef __pyx_moduledef = #else static struct PyModuleDef __pyx_moduledef = #endif { PyModuleDef_HEAD_INIT, "checksums", 0, /* m_doc */ #if CYTHON_PEP489_MULTI_PHASE_INIT 0, /* m_size */ #elif CYTHON_USE_MODULE_STATE sizeof(__pyx_mstate), /* m_size */ #else -1, /* m_size */ #endif __pyx_methods /* m_methods */, #if CYTHON_PEP489_MULTI_PHASE_INIT __pyx_moduledef_slots, /* m_slots */ #else NULL, /* m_reload */ #endif #if CYTHON_USE_MODULE_STATE __pyx_m_traverse, /* m_traverse */ __pyx_m_clear, /* m_clear */ NULL /* m_free */ #else NULL, /* m_traverse */ NULL, /* m_clear */ NULL /* m_free */ #endif }; #ifdef __cplusplus } /* anonymous namespace */ #endif #endif #ifndef CYTHON_NO_PYINIT_EXPORT #define __Pyx_PyMODINIT_FUNC PyMODINIT_FUNC #elif PY_MAJOR_VERSION < 3 #ifdef __cplusplus #define __Pyx_PyMODINIT_FUNC extern "C" void #else #define __Pyx_PyMODINIT_FUNC void #endif #else #ifdef __cplusplus #define __Pyx_PyMODINIT_FUNC extern "C" PyObject * #else #define __Pyx_PyMODINIT_FUNC PyObject * #endif #endif #if PY_MAJOR_VERSION < 3 __Pyx_PyMODINIT_FUNC initchecksums(void) CYTHON_SMALL_CODE; /*proto*/ __Pyx_PyMODINIT_FUNC initchecksums(void) #else __Pyx_PyMODINIT_FUNC PyInit_checksums(void) CYTHON_SMALL_CODE; /*proto*/ __Pyx_PyMODINIT_FUNC PyInit_checksums(void) #if CYTHON_PEP489_MULTI_PHASE_INIT { return PyModuleDef_Init(&__pyx_moduledef); } static CYTHON_SMALL_CODE int __Pyx_check_single_interpreter(void) { #if PY_VERSION_HEX >= 0x030700A1 static PY_INT64_T main_interpreter_id = -1; PY_INT64_T current_id = PyInterpreterState_GetID(PyThreadState_Get()->interp); if (main_interpreter_id == -1) { main_interpreter_id = current_id; return (unlikely(current_id == -1)) ? -1 : 0; } else if (unlikely(main_interpreter_id != current_id)) #else static PyInterpreterState *main_interpreter = NULL; PyInterpreterState *current_interpreter = PyThreadState_Get()->interp; if (!main_interpreter) { main_interpreter = current_interpreter; } else if (unlikely(main_interpreter != current_interpreter)) #endif { PyErr_SetString( PyExc_ImportError, "Interpreter change detected - this module can only be loaded into one interpreter per process."); return -1; } return 0; } #if CYTHON_COMPILING_IN_LIMITED_API static CYTHON_SMALL_CODE int __Pyx_copy_spec_to_module(PyObject *spec, PyObject *module, const char* from_name, const char* to_name, int allow_none) #else static CYTHON_SMALL_CODE int __Pyx_copy_spec_to_module(PyObject *spec, PyObject *moddict, const char* from_name, const char* to_name, int allow_none) #endif { PyObject *value = PyObject_GetAttrString(spec, from_name); int result = 0; if (likely(value)) { if (allow_none || value != Py_None) { #if CYTHON_COMPILING_IN_LIMITED_API result = PyModule_AddObject(module, to_name, value); #else result = PyDict_SetItemString(moddict, to_name, value); #endif } Py_DECREF(value); } else if (PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_Clear(); } else { result = -1; } return result; } static CYTHON_SMALL_CODE PyObject* __pyx_pymod_create(PyObject *spec, PyModuleDef *def) { PyObject *module = NULL, *moddict, *modname; CYTHON_UNUSED_VAR(def); if (__Pyx_check_single_interpreter()) return NULL; if (__pyx_m) return __Pyx_NewRef(__pyx_m); modname = PyObject_GetAttrString(spec, "name"); if (unlikely(!modname)) goto bad; module = PyModule_NewObject(modname); Py_DECREF(modname); if (unlikely(!module)) goto bad; #if CYTHON_COMPILING_IN_LIMITED_API moddict = module; #else moddict = PyModule_GetDict(module); if (unlikely(!moddict)) goto bad; #endif if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "loader", "__loader__", 1) < 0)) goto bad; if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "origin", "__file__", 1) < 0)) goto bad; if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "parent", "__package__", 1) < 0)) goto bad; if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "submodule_search_locations", "__path__", 0) < 0)) goto bad; return module; bad: Py_XDECREF(module); return NULL; } static CYTHON_SMALL_CODE int __pyx_pymod_exec_checksums(PyObject *__pyx_pyinit_module) #endif #endif { int stringtab_initialized = 0; #if CYTHON_USE_MODULE_STATE int pystate_addmodule_run = 0; #endif PyObject *__pyx_t_1 = NULL; PyObject *__pyx_t_2 = NULL; PyObject *__pyx_t_3 = NULL; int __pyx_t_4; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; __Pyx_RefNannyDeclarations #if CYTHON_PEP489_MULTI_PHASE_INIT if (__pyx_m) { if (__pyx_m == __pyx_pyinit_module) return 0; PyErr_SetString(PyExc_RuntimeError, "Module 'checksums' has already been imported. Re-initialisation is not supported."); return -1; } #elif PY_MAJOR_VERSION >= 3 if (__pyx_m) return __Pyx_NewRef(__pyx_m); #endif /*--- Module creation code ---*/ #if CYTHON_PEP489_MULTI_PHASE_INIT __pyx_m = __pyx_pyinit_module; Py_INCREF(__pyx_m); #else #if PY_MAJOR_VERSION < 3 __pyx_m = Py_InitModule4("checksums", __pyx_methods, 0, 0, PYTHON_API_VERSION); Py_XINCREF(__pyx_m); if (unlikely(!__pyx_m)) __PYX_ERR(1, 1, __pyx_L1_error) #elif CYTHON_USE_MODULE_STATE __pyx_t_1 = PyModule_Create(&__pyx_moduledef); if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 1, __pyx_L1_error) { int add_module_result = PyState_AddModule(__pyx_t_1, &__pyx_moduledef); __pyx_t_1 = 0; /* transfer ownership from __pyx_t_1 to "checksums" pseudovariable */ if (unlikely((add_module_result < 0))) __PYX_ERR(1, 1, __pyx_L1_error) pystate_addmodule_run = 1; } #else __pyx_m = PyModule_Create(&__pyx_moduledef); if (unlikely(!__pyx_m)) __PYX_ERR(1, 1, __pyx_L1_error) #endif #endif CYTHON_UNUSED_VAR(__pyx_t_1); __pyx_d = PyModule_GetDict(__pyx_m); if (unlikely(!__pyx_d)) __PYX_ERR(1, 1, __pyx_L1_error) Py_INCREF(__pyx_d); __pyx_b = __Pyx_PyImport_AddModuleRef(__Pyx_BUILTIN_MODULE_NAME); if (unlikely(!__pyx_b)) __PYX_ERR(1, 1, __pyx_L1_error) __pyx_cython_runtime = __Pyx_PyImport_AddModuleRef((const char *) "cython_runtime"); if (unlikely(!__pyx_cython_runtime)) __PYX_ERR(1, 1, __pyx_L1_error) if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) __PYX_ERR(1, 1, __pyx_L1_error) #if CYTHON_REFNANNY __Pyx_RefNanny = __Pyx_RefNannyImportAPI("refnanny"); if (!__Pyx_RefNanny) { PyErr_Clear(); __Pyx_RefNanny = __Pyx_RefNannyImportAPI("Cython.Runtime.refnanny"); if (!__Pyx_RefNanny) Py_FatalError("failed to import 'refnanny' module"); } #endif __Pyx_RefNannySetupContext("__Pyx_PyMODINIT_FUNC PyInit_checksums(void)", 0); if (__Pyx_check_binary_version(__PYX_LIMITED_VERSION_HEX, __Pyx_get_runtime_version(), CYTHON_COMPILING_IN_LIMITED_API) < 0) __PYX_ERR(1, 1, __pyx_L1_error) #ifdef __Pxy_PyFrame_Initialize_Offsets __Pxy_PyFrame_Initialize_Offsets(); #endif __pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_empty_tuple)) __PYX_ERR(1, 1, __pyx_L1_error) __pyx_empty_bytes = PyBytes_FromStringAndSize("", 0); if (unlikely(!__pyx_empty_bytes)) __PYX_ERR(1, 1, __pyx_L1_error) __pyx_empty_unicode = PyUnicode_FromStringAndSize("", 0); if (unlikely(!__pyx_empty_unicode)) __PYX_ERR(1, 1, __pyx_L1_error) #ifdef __Pyx_CyFunction_USED if (__pyx_CyFunction_init(__pyx_m) < 0) __PYX_ERR(1, 1, __pyx_L1_error) #endif #ifdef __Pyx_FusedFunction_USED if (__pyx_FusedFunction_init(__pyx_m) < 0) __PYX_ERR(1, 1, __pyx_L1_error) #endif #ifdef __Pyx_Coroutine_USED if (__pyx_Coroutine_init(__pyx_m) < 0) __PYX_ERR(1, 1, __pyx_L1_error) #endif #ifdef __Pyx_Generator_USED if (__pyx_Generator_init(__pyx_m) < 0) __PYX_ERR(1, 1, __pyx_L1_error) #endif #ifdef __Pyx_AsyncGen_USED if (__pyx_AsyncGen_init(__pyx_m) < 0) __PYX_ERR(1, 1, __pyx_L1_error) #endif #ifdef __Pyx_StopAsyncIteration_USED if (__pyx_StopAsyncIteration_init(__pyx_m) < 0) __PYX_ERR(1, 1, __pyx_L1_error) #endif /*--- Library function declarations ---*/ /*--- Threads initialization code ---*/ #if defined(WITH_THREAD) && PY_VERSION_HEX < 0x030700F0 && defined(__PYX_FORCE_INIT_THREADS) && __PYX_FORCE_INIT_THREADS PyEval_InitThreads(); #endif /*--- Initialize various global constants etc. ---*/ if (__Pyx_InitConstants() < 0) __PYX_ERR(1, 1, __pyx_L1_error) stringtab_initialized = 1; if (__Pyx_InitGlobals() < 0) __PYX_ERR(1, 1, __pyx_L1_error) #if PY_MAJOR_VERSION < 3 && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT) if (__Pyx_init_sys_getdefaultencoding_params() < 0) __PYX_ERR(1, 1, __pyx_L1_error) #endif if (__pyx_module_is_main_borg__algorithms__checksums) { if (PyObject_SetAttr(__pyx_m, __pyx_n_s_name, __pyx_n_s_main) < 0) __PYX_ERR(1, 1, __pyx_L1_error) } #if PY_MAJOR_VERSION >= 3 { PyObject *modules = PyImport_GetModuleDict(); if (unlikely(!modules)) __PYX_ERR(1, 1, __pyx_L1_error) if (!PyDict_GetItemString(modules, "borg.algorithms.checksums")) { if (unlikely((PyDict_SetItemString(modules, "borg.algorithms.checksums", __pyx_m) < 0))) __PYX_ERR(1, 1, __pyx_L1_error) } } #endif /*--- Builtin init code ---*/ if (__Pyx_InitCachedBuiltins() < 0) __PYX_ERR(1, 1, __pyx_L1_error) /*--- Constants init code ---*/ if (__Pyx_InitCachedConstants() < 0) __PYX_ERR(1, 1, __pyx_L1_error) /*--- Global type/function init code ---*/ (void)__Pyx_modinit_global_init_code(); (void)__Pyx_modinit_variable_export_code(); (void)__Pyx_modinit_function_export_code(); if (unlikely((__Pyx_modinit_type_init_code() < 0))) __PYX_ERR(1, 1, __pyx_L1_error) if (unlikely((__Pyx_modinit_type_import_code() < 0))) __PYX_ERR(1, 1, __pyx_L1_error) (void)__Pyx_modinit_variable_import_code(); (void)__Pyx_modinit_function_import_code(); /*--- Execution code ---*/ #if defined(__Pyx_Generator_USED) || defined(__Pyx_Coroutine_USED) if (__Pyx_patch_abc() < 0) __PYX_ERR(1, 1, __pyx_L1_error) #endif /* "borg/algorithms/checksums.pyx":1 * from ..helpers import bin_to_hex # <<<<<<<<<<<<<< * * from libc.stdint cimport uint32_t */ __pyx_t_2 = PyList_New(1); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 1, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); __Pyx_INCREF(__pyx_n_s_bin_to_hex); __Pyx_GIVEREF(__pyx_n_s_bin_to_hex); if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 0, __pyx_n_s_bin_to_hex)) __PYX_ERR(1, 1, __pyx_L1_error); __pyx_t_3 = __Pyx_Import(__pyx_n_s_helpers, __pyx_t_2, 2); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 1, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_t_2 = __Pyx_ImportFrom(__pyx_t_3, __pyx_n_s_bin_to_hex); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 1, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); if (PyDict_SetItem(__pyx_d, __pyx_n_s_bin_to_hex, __pyx_t_2) < 0) __PYX_ERR(1, 1, __pyx_L1_error) __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; /* "borg/algorithms/checksums.pyx":46 * * * def crc32_slice_by_8(data, value=0): # <<<<<<<<<<<<<< * cdef Py_buffer data_buf = ro_buffer(data) * cdef uint32_t val = value */ __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_4borg_10algorithms_9checksums_1crc32_slice_by_8, 0, __pyx_n_s_crc32_slice_by_8, NULL, __pyx_n_s_borg_algorithms_checksums, __pyx_d, ((PyObject *)__pyx_codeobj__5)); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 46, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_CyFunction_SetDefaultsTuple(__pyx_t_3, __pyx_tuple__6); if (PyDict_SetItem(__pyx_d, __pyx_n_s_crc32_slice_by_8, __pyx_t_3) < 0) __PYX_ERR(1, 46, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; /* "borg/algorithms/checksums.pyx":55 * * * def crc32_clmul(data, value=0): # <<<<<<<<<<<<<< * cdef Py_buffer data_buf = ro_buffer(data) * cdef uint32_t val = value */ __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_4borg_10algorithms_9checksums_3crc32_clmul, 0, __pyx_n_s_crc32_clmul, NULL, __pyx_n_s_borg_algorithms_checksums, __pyx_d, ((PyObject *)__pyx_codeobj__7)); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 55, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_CyFunction_SetDefaultsTuple(__pyx_t_3, __pyx_tuple__6); if (PyDict_SetItem(__pyx_d, __pyx_n_s_crc32_clmul, __pyx_t_3) < 0) __PYX_ERR(1, 55, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; /* "borg/algorithms/checksums.pyx":64 * * * have_clmul = _have_clmul() # <<<<<<<<<<<<<< * if have_clmul: * crc32 = crc32_clmul */ __pyx_t_3 = __Pyx_PyInt_From_int(have_clmul()); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 64, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); if (PyDict_SetItem(__pyx_d, __pyx_n_s_have_clmul, __pyx_t_3) < 0) __PYX_ERR(1, 64, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; /* "borg/algorithms/checksums.pyx":65 * * have_clmul = _have_clmul() * if have_clmul: # <<<<<<<<<<<<<< * crc32 = crc32_clmul * else: */ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_n_s_have_clmul); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 65, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(1, 65, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (__pyx_t_4) { /* "borg/algorithms/checksums.pyx":66 * have_clmul = _have_clmul() * if have_clmul: * crc32 = crc32_clmul # <<<<<<<<<<<<<< * else: * crc32 = crc32_slice_by_8 */ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_n_s_crc32_clmul); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 66, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); if (PyDict_SetItem(__pyx_d, __pyx_n_s_crc32, __pyx_t_3) < 0) __PYX_ERR(1, 66, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; /* "borg/algorithms/checksums.pyx":65 * * have_clmul = _have_clmul() * if have_clmul: # <<<<<<<<<<<<<< * crc32 = crc32_clmul * else: */ goto __pyx_L2; } /* "borg/algorithms/checksums.pyx":68 * crc32 = crc32_clmul * else: * crc32 = crc32_slice_by_8 # <<<<<<<<<<<<<< * * */ /*else*/ { __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_n_s_crc32_slice_by_8); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 68, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); if (PyDict_SetItem(__pyx_d, __pyx_n_s_crc32, __pyx_t_3) < 0) __PYX_ERR(1, 68, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; } __pyx_L2:; /* "borg/algorithms/checksums.pyx":71 * * * def xxh64(data, seed=0): # <<<<<<<<<<<<<< * cdef unsigned long long _seed = seed * cdef XXH64_hash_t hash */ __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_4borg_10algorithms_9checksums_5xxh64, 0, __pyx_n_s_xxh64, NULL, __pyx_n_s_borg_algorithms_checksums, __pyx_d, ((PyObject *)__pyx_codeobj__9)); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 71, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_CyFunction_SetDefaultsTuple(__pyx_t_3, __pyx_tuple__6); if (PyDict_SetItem(__pyx_d, __pyx_n_s_xxh64, __pyx_t_3) < 0) __PYX_ERR(1, 71, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; /* "borg/algorithms/checksums.pyx":96 * XXH64_freeState(self.state) * * def update(self, data): # <<<<<<<<<<<<<< * cdef Py_buffer data_buf = ro_buffer(data) * try: */ __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_4borg_10algorithms_9checksums_14StreamingXXH64_5update, __Pyx_CYFUNCTION_CCLASS, __pyx_n_s_StreamingXXH64_update, NULL, __pyx_n_s_borg_algorithms_checksums, __pyx_d, ((PyObject *)__pyx_codeobj__11)); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 96, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); if (__Pyx_SetItemOnTypeDict((PyObject *)__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64, __pyx_n_s_update, __pyx_t_3) < 0) __PYX_ERR(1, 96, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; PyType_Modified(__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64); /* "borg/algorithms/checksums.pyx":104 * PyBuffer_Release(&data_buf) * * def digest(self): # <<<<<<<<<<<<<< * cdef XXH64_hash_t hash * cdef XXH64_canonical_t digest */ __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_4borg_10algorithms_9checksums_14StreamingXXH64_7digest, __Pyx_CYFUNCTION_CCLASS, __pyx_n_s_StreamingXXH64_digest, NULL, __pyx_n_s_borg_algorithms_checksums, __pyx_d, ((PyObject *)__pyx_codeobj__13)); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 104, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); if (__Pyx_SetItemOnTypeDict((PyObject *)__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64, __pyx_n_s_digest, __pyx_t_3) < 0) __PYX_ERR(1, 104, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; PyType_Modified(__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64); /* "borg/algorithms/checksums.pyx":111 * return PyBytes_FromStringAndSize( digest.digest, 8) * * def hexdigest(self): # <<<<<<<<<<<<<< * return bin_to_hex(self.digest()) */ __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_4borg_10algorithms_9checksums_14StreamingXXH64_9hexdigest, __Pyx_CYFUNCTION_CCLASS, __pyx_n_s_StreamingXXH64_hexdigest, NULL, __pyx_n_s_borg_algorithms_checksums, __pyx_d, ((PyObject *)__pyx_codeobj__15)); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 111, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); if (__Pyx_SetItemOnTypeDict((PyObject *)__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64, __pyx_n_s_hexdigest, __pyx_t_3) < 0) __PYX_ERR(1, 111, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; PyType_Modified(__pyx_ptype_4borg_10algorithms_9checksums_StreamingXXH64); /* "(tree fragment)":1 * def __reduce_cython__(self): # <<<<<<<<<<<<<< * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" * def __setstate_cython__(self, __pyx_state): */ __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_4borg_10algorithms_9checksums_14StreamingXXH64_11__reduce_cython__, __Pyx_CYFUNCTION_CCLASS, __pyx_n_s_StreamingXXH64___reduce_cython, NULL, __pyx_n_s_borg_algorithms_checksums, __pyx_d, ((PyObject *)__pyx_codeobj__16)); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); if (PyDict_SetItem(__pyx_d, __pyx_n_s_reduce_cython, __pyx_t_3) < 0) __PYX_ERR(0, 1, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; /* "(tree fragment)":3 * def __reduce_cython__(self): * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" * def __setstate_cython__(self, __pyx_state): # <<<<<<<<<<<<<< * raise TypeError, "no default __reduce__ due to non-trivial __cinit__" */ __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_4borg_10algorithms_9checksums_14StreamingXXH64_13__setstate_cython__, __Pyx_CYFUNCTION_CCLASS, __pyx_n_s_StreamingXXH64___setstate_cython, NULL, __pyx_n_s_borg_algorithms_checksums, __pyx_d, ((PyObject *)__pyx_codeobj__18)); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); if (PyDict_SetItem(__pyx_d, __pyx_n_s_setstate_cython, __pyx_t_3) < 0) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; /* "borg/algorithms/checksums.pyx":1 * from ..helpers import bin_to_hex # <<<<<<<<<<<<<< * * from libc.stdint cimport uint32_t */ __pyx_t_3 = __Pyx_PyDict_NewPresized(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 1, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_3) < 0) __PYX_ERR(1, 1, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; /*--- Wrapped vars code ---*/ goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_2); __Pyx_XDECREF(__pyx_t_3); if (__pyx_m) { if (__pyx_d && stringtab_initialized) { __Pyx_AddTraceback("init borg.algorithms.checksums", __pyx_clineno, __pyx_lineno, __pyx_filename); } #if !CYTHON_USE_MODULE_STATE Py_CLEAR(__pyx_m); #else Py_DECREF(__pyx_m); if (pystate_addmodule_run) { PyObject *tp, *value, *tb; PyErr_Fetch(&tp, &value, &tb); PyState_RemoveModule(&__pyx_moduledef); PyErr_Restore(tp, value, tb); } #endif } else if (!PyErr_Occurred()) { PyErr_SetString(PyExc_ImportError, "init borg.algorithms.checksums"); } __pyx_L0:; __Pyx_RefNannyFinishContext(); #if CYTHON_PEP489_MULTI_PHASE_INIT return (__pyx_m != NULL) ? 0 : -1; #elif PY_MAJOR_VERSION >= 3 return __pyx_m; #else return; #endif } /* #### Code section: cleanup_globals ### */ /* #### Code section: cleanup_module ### */ /* #### Code section: main_method ### */ /* #### Code section: utility_code_pragmas ### */ #ifdef _MSC_VER #pragma warning( push ) /* Warning 4127: conditional expression is constant * Cython uses constant conditional expressions to allow in inline functions to be optimized at * compile-time, so this warning is not useful */ #pragma warning( disable : 4127 ) #endif /* #### Code section: utility_code_def ### */ /* --- Runtime support code --- */ /* Refnanny */ #if CYTHON_REFNANNY static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname) { PyObject *m = NULL, *p = NULL; void *r = NULL; m = PyImport_ImportModule(modname); if (!m) goto end; p = PyObject_GetAttrString(m, "RefNannyAPI"); if (!p) goto end; r = PyLong_AsVoidPtr(p); end: Py_XDECREF(p); Py_XDECREF(m); return (__Pyx_RefNannyAPIStruct *)r; } #endif /* PyErrExceptionMatches */ #if CYTHON_FAST_THREAD_STATE static int __Pyx_PyErr_ExceptionMatchesTuple(PyObject *exc_type, PyObject *tuple) { Py_ssize_t i, n; n = PyTuple_GET_SIZE(tuple); #if PY_MAJOR_VERSION >= 3 for (i=0; i= 0x030C00A6 PyObject *current_exception = tstate->current_exception; if (unlikely(!current_exception)) return 0; exc_type = (PyObject*) Py_TYPE(current_exception); if (exc_type == err) return 1; #else exc_type = tstate->curexc_type; if (exc_type == err) return 1; if (unlikely(!exc_type)) return 0; #endif #if CYTHON_AVOID_BORROWED_REFS Py_INCREF(exc_type); #endif if (unlikely(PyTuple_Check(err))) { result = __Pyx_PyErr_ExceptionMatchesTuple(exc_type, err); } else { result = __Pyx_PyErr_GivenExceptionMatches(exc_type, err); } #if CYTHON_AVOID_BORROWED_REFS Py_DECREF(exc_type); #endif return result; } #endif /* PyErrFetchRestore */ #if CYTHON_FAST_THREAD_STATE static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb) { #if PY_VERSION_HEX >= 0x030C00A6 PyObject *tmp_value; assert(type == NULL || (value != NULL && type == (PyObject*) Py_TYPE(value))); if (value) { #if CYTHON_COMPILING_IN_CPYTHON if (unlikely(((PyBaseExceptionObject*) value)->traceback != tb)) #endif PyException_SetTraceback(value, tb); } tmp_value = tstate->current_exception; tstate->current_exception = value; Py_XDECREF(tmp_value); Py_XDECREF(type); Py_XDECREF(tb); #else PyObject *tmp_type, *tmp_value, *tmp_tb; tmp_type = tstate->curexc_type; tmp_value = tstate->curexc_value; tmp_tb = tstate->curexc_traceback; tstate->curexc_type = type; tstate->curexc_value = value; tstate->curexc_traceback = tb; Py_XDECREF(tmp_type); Py_XDECREF(tmp_value); Py_XDECREF(tmp_tb); #endif } static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) { #if PY_VERSION_HEX >= 0x030C00A6 PyObject* exc_value; exc_value = tstate->current_exception; tstate->current_exception = 0; *value = exc_value; *type = NULL; *tb = NULL; if (exc_value) { *type = (PyObject*) Py_TYPE(exc_value); Py_INCREF(*type); #if CYTHON_COMPILING_IN_CPYTHON *tb = ((PyBaseExceptionObject*) exc_value)->traceback; Py_XINCREF(*tb); #else *tb = PyException_GetTraceback(exc_value); #endif } #else *type = tstate->curexc_type; *value = tstate->curexc_value; *tb = tstate->curexc_traceback; tstate->curexc_type = 0; tstate->curexc_value = 0; tstate->curexc_traceback = 0; #endif } #endif /* PyObjectGetAttrStr */ #if CYTHON_USE_TYPE_SLOTS static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name) { PyTypeObject* tp = Py_TYPE(obj); if (likely(tp->tp_getattro)) return tp->tp_getattro(obj, attr_name); #if PY_MAJOR_VERSION < 3 if (likely(tp->tp_getattr)) return tp->tp_getattr(obj, PyString_AS_STRING(attr_name)); #endif return PyObject_GetAttr(obj, attr_name); } #endif /* PyObjectGetAttrStrNoError */ #if __PYX_LIMITED_VERSION_HEX < 0x030d00A1 static void __Pyx_PyObject_GetAttrStr_ClearAttributeError(void) { __Pyx_PyThreadState_declare __Pyx_PyThreadState_assign if (likely(__Pyx_PyErr_ExceptionMatches(PyExc_AttributeError))) __Pyx_PyErr_Clear(); } #endif static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name) { PyObject *result; #if __PYX_LIMITED_VERSION_HEX >= 0x030d00A1 (void) PyObject_GetOptionalAttr(obj, attr_name, &result); return result; #else #if CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_TYPE_SLOTS && PY_VERSION_HEX >= 0x030700B1 PyTypeObject* tp = Py_TYPE(obj); if (likely(tp->tp_getattro == PyObject_GenericGetAttr)) { return _PyObject_GenericGetAttrWithDict(obj, attr_name, NULL, 1); } #endif result = __Pyx_PyObject_GetAttrStr(obj, attr_name); if (unlikely(!result)) { __Pyx_PyObject_GetAttrStr_ClearAttributeError(); } return result; #endif } /* GetBuiltinName */ static PyObject *__Pyx_GetBuiltinName(PyObject *name) { PyObject* result = __Pyx_PyObject_GetAttrStrNoError(__pyx_b, name); if (unlikely(!result) && !PyErr_Occurred()) { PyErr_Format(PyExc_NameError, #if PY_MAJOR_VERSION >= 3 "name '%U' is not defined", name); #else "name '%.200s' is not defined", PyString_AS_STRING(name)); #endif } return result; } /* TupleAndListFromArray */ #if CYTHON_COMPILING_IN_CPYTHON static CYTHON_INLINE void __Pyx_copy_object_array(PyObject *const *CYTHON_RESTRICT src, PyObject** CYTHON_RESTRICT dest, Py_ssize_t length) { PyObject *v; Py_ssize_t i; for (i = 0; i < length; i++) { v = dest[i] = src[i]; Py_INCREF(v); } } static CYTHON_INLINE PyObject * __Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) { PyObject *res; if (n <= 0) { Py_INCREF(__pyx_empty_tuple); return __pyx_empty_tuple; } res = PyTuple_New(n); if (unlikely(res == NULL)) return NULL; __Pyx_copy_object_array(src, ((PyTupleObject*)res)->ob_item, n); return res; } static CYTHON_INLINE PyObject * __Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n) { PyObject *res; if (n <= 0) { return PyList_New(0); } res = PyList_New(n); if (unlikely(res == NULL)) return NULL; __Pyx_copy_object_array(src, ((PyListObject*)res)->ob_item, n); return res; } #endif /* BytesEquals */ static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals) { #if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API return PyObject_RichCompareBool(s1, s2, equals); #else if (s1 == s2) { return (equals == Py_EQ); } else if (PyBytes_CheckExact(s1) & PyBytes_CheckExact(s2)) { const char *ps1, *ps2; Py_ssize_t length = PyBytes_GET_SIZE(s1); if (length != PyBytes_GET_SIZE(s2)) return (equals == Py_NE); ps1 = PyBytes_AS_STRING(s1); ps2 = PyBytes_AS_STRING(s2); if (ps1[0] != ps2[0]) { return (equals == Py_NE); } else if (length == 1) { return (equals == Py_EQ); } else { int result; #if CYTHON_USE_UNICODE_INTERNALS && (PY_VERSION_HEX < 0x030B0000) Py_hash_t hash1, hash2; hash1 = ((PyBytesObject*)s1)->ob_shash; hash2 = ((PyBytesObject*)s2)->ob_shash; if (hash1 != hash2 && hash1 != -1 && hash2 != -1) { return (equals == Py_NE); } #endif result = memcmp(ps1, ps2, (size_t)length); return (equals == Py_EQ) ? (result == 0) : (result != 0); } } else if ((s1 == Py_None) & PyBytes_CheckExact(s2)) { return (equals == Py_NE); } else if ((s2 == Py_None) & PyBytes_CheckExact(s1)) { return (equals == Py_NE); } else { int result; PyObject* py_result = PyObject_RichCompare(s1, s2, equals); if (!py_result) return -1; result = __Pyx_PyObject_IsTrue(py_result); Py_DECREF(py_result); return result; } #endif } /* UnicodeEquals */ static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals) { #if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API return PyObject_RichCompareBool(s1, s2, equals); #else #if PY_MAJOR_VERSION < 3 PyObject* owned_ref = NULL; #endif int s1_is_unicode, s2_is_unicode; if (s1 == s2) { goto return_eq; } s1_is_unicode = PyUnicode_CheckExact(s1); s2_is_unicode = PyUnicode_CheckExact(s2); #if PY_MAJOR_VERSION < 3 if ((s1_is_unicode & (!s2_is_unicode)) && PyString_CheckExact(s2)) { owned_ref = PyUnicode_FromObject(s2); if (unlikely(!owned_ref)) return -1; s2 = owned_ref; s2_is_unicode = 1; } else if ((s2_is_unicode & (!s1_is_unicode)) && PyString_CheckExact(s1)) { owned_ref = PyUnicode_FromObject(s1); if (unlikely(!owned_ref)) return -1; s1 = owned_ref; s1_is_unicode = 1; } else if (((!s2_is_unicode) & (!s1_is_unicode))) { return __Pyx_PyBytes_Equals(s1, s2, equals); } #endif if (s1_is_unicode & s2_is_unicode) { Py_ssize_t length; int kind; void *data1, *data2; if (unlikely(__Pyx_PyUnicode_READY(s1) < 0) || unlikely(__Pyx_PyUnicode_READY(s2) < 0)) return -1; length = __Pyx_PyUnicode_GET_LENGTH(s1); if (length != __Pyx_PyUnicode_GET_LENGTH(s2)) { goto return_ne; } #if CYTHON_USE_UNICODE_INTERNALS { Py_hash_t hash1, hash2; #if CYTHON_PEP393_ENABLED hash1 = ((PyASCIIObject*)s1)->hash; hash2 = ((PyASCIIObject*)s2)->hash; #else hash1 = ((PyUnicodeObject*)s1)->hash; hash2 = ((PyUnicodeObject*)s2)->hash; #endif if (hash1 != hash2 && hash1 != -1 && hash2 != -1) { goto return_ne; } } #endif kind = __Pyx_PyUnicode_KIND(s1); if (kind != __Pyx_PyUnicode_KIND(s2)) { goto return_ne; } data1 = __Pyx_PyUnicode_DATA(s1); data2 = __Pyx_PyUnicode_DATA(s2); if (__Pyx_PyUnicode_READ(kind, data1, 0) != __Pyx_PyUnicode_READ(kind, data2, 0)) { goto return_ne; } else if (length == 1) { goto return_eq; } else { int result = memcmp(data1, data2, (size_t)(length * kind)); #if PY_MAJOR_VERSION < 3 Py_XDECREF(owned_ref); #endif return (equals == Py_EQ) ? (result == 0) : (result != 0); } } else if ((s1 == Py_None) & s2_is_unicode) { goto return_ne; } else if ((s2 == Py_None) & s1_is_unicode) { goto return_ne; } else { int result; PyObject* py_result = PyObject_RichCompare(s1, s2, equals); #if PY_MAJOR_VERSION < 3 Py_XDECREF(owned_ref); #endif if (!py_result) return -1; result = __Pyx_PyObject_IsTrue(py_result); Py_DECREF(py_result); return result; } return_eq: #if PY_MAJOR_VERSION < 3 Py_XDECREF(owned_ref); #endif return (equals == Py_EQ); return_ne: #if PY_MAJOR_VERSION < 3 Py_XDECREF(owned_ref); #endif return (equals == Py_NE); #endif } /* fastcall */ #if CYTHON_METH_FASTCALL static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s) { Py_ssize_t i, n = PyTuple_GET_SIZE(kwnames); for (i = 0; i < n; i++) { if (s == PyTuple_GET_ITEM(kwnames, i)) return kwvalues[i]; } for (i = 0; i < n; i++) { int eq = __Pyx_PyUnicode_Equals(s, PyTuple_GET_ITEM(kwnames, i), Py_EQ); if (unlikely(eq != 0)) { if (unlikely(eq < 0)) return NULL; return kwvalues[i]; } } return NULL; } #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues) { Py_ssize_t i, nkwargs = PyTuple_GET_SIZE(kwnames); PyObject *dict; dict = PyDict_New(); if (unlikely(!dict)) return NULL; for (i=0; i= 3 "%s() got multiple values for keyword argument '%U'", func_name, kw_name); #else "%s() got multiple values for keyword argument '%s'", func_name, PyString_AsString(kw_name)); #endif } /* ParseKeywords */ static int __Pyx_ParseOptionalKeywords( PyObject *kwds, PyObject *const *kwvalues, PyObject **argnames[], PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args, const char* function_name) { PyObject *key = 0, *value = 0; Py_ssize_t pos = 0; PyObject*** name; PyObject*** first_kw_arg = argnames + num_pos_args; int kwds_is_tuple = CYTHON_METH_FASTCALL && likely(PyTuple_Check(kwds)); while (1) { Py_XDECREF(key); key = NULL; Py_XDECREF(value); value = NULL; if (kwds_is_tuple) { Py_ssize_t size; #if CYTHON_ASSUME_SAFE_MACROS size = PyTuple_GET_SIZE(kwds); #else size = PyTuple_Size(kwds); if (size < 0) goto bad; #endif if (pos >= size) break; #if CYTHON_AVOID_BORROWED_REFS key = __Pyx_PySequence_ITEM(kwds, pos); if (!key) goto bad; #elif CYTHON_ASSUME_SAFE_MACROS key = PyTuple_GET_ITEM(kwds, pos); #else key = PyTuple_GetItem(kwds, pos); if (!key) goto bad; #endif value = kwvalues[pos]; pos++; } else { if (!PyDict_Next(kwds, &pos, &key, &value)) break; #if CYTHON_AVOID_BORROWED_REFS Py_INCREF(key); #endif } name = first_kw_arg; while (*name && (**name != key)) name++; if (*name) { values[name-argnames] = value; #if CYTHON_AVOID_BORROWED_REFS Py_INCREF(value); Py_DECREF(key); #endif key = NULL; value = NULL; continue; } #if !CYTHON_AVOID_BORROWED_REFS Py_INCREF(key); #endif Py_INCREF(value); name = first_kw_arg; #if PY_MAJOR_VERSION < 3 if (likely(PyString_Check(key))) { while (*name) { if ((CYTHON_COMPILING_IN_PYPY || PyString_GET_SIZE(**name) == PyString_GET_SIZE(key)) && _PyString_Eq(**name, key)) { values[name-argnames] = value; #if CYTHON_AVOID_BORROWED_REFS value = NULL; #endif break; } name++; } if (*name) continue; else { PyObject*** argname = argnames; while (argname != first_kw_arg) { if ((**argname == key) || ( (CYTHON_COMPILING_IN_PYPY || PyString_GET_SIZE(**argname) == PyString_GET_SIZE(key)) && _PyString_Eq(**argname, key))) { goto arg_passed_twice; } argname++; } } } else #endif if (likely(PyUnicode_Check(key))) { while (*name) { int cmp = ( #if !CYTHON_COMPILING_IN_PYPY && PY_MAJOR_VERSION >= 3 (__Pyx_PyUnicode_GET_LENGTH(**name) != __Pyx_PyUnicode_GET_LENGTH(key)) ? 1 : #endif PyUnicode_Compare(**name, key) ); if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad; if (cmp == 0) { values[name-argnames] = value; #if CYTHON_AVOID_BORROWED_REFS value = NULL; #endif break; } name++; } if (*name) continue; else { PyObject*** argname = argnames; while (argname != first_kw_arg) { int cmp = (**argname == key) ? 0 : #if !CYTHON_COMPILING_IN_PYPY && PY_MAJOR_VERSION >= 3 (__Pyx_PyUnicode_GET_LENGTH(**argname) != __Pyx_PyUnicode_GET_LENGTH(key)) ? 1 : #endif PyUnicode_Compare(**argname, key); if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad; if (cmp == 0) goto arg_passed_twice; argname++; } } } else goto invalid_keyword_type; if (kwds2) { if (unlikely(PyDict_SetItem(kwds2, key, value))) goto bad; } else { goto invalid_keyword; } } Py_XDECREF(key); Py_XDECREF(value); return 0; arg_passed_twice: __Pyx_RaiseDoubleKeywordsError(function_name, key); goto bad; invalid_keyword_type: PyErr_Format(PyExc_TypeError, "%.200s() keywords must be strings", function_name); goto bad; invalid_keyword: #if PY_MAJOR_VERSION < 3 PyErr_Format(PyExc_TypeError, "%.200s() got an unexpected keyword argument '%.200s'", function_name, PyString_AsString(key)); #else PyErr_Format(PyExc_TypeError, "%s() got an unexpected keyword argument '%U'", function_name, key); #endif bad: Py_XDECREF(key); Py_XDECREF(value); return -1; } /* RaiseArgTupleInvalid */ static void __Pyx_RaiseArgtupleInvalid( const char* func_name, int exact, Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found) { Py_ssize_t num_expected; const char *more_or_less; if (num_found < num_min) { num_expected = num_min; more_or_less = "at least"; } else { num_expected = num_max; more_or_less = "at most"; } if (exact) { more_or_less = "exactly"; } PyErr_Format(PyExc_TypeError, "%.200s() takes %.8s %" CYTHON_FORMAT_SSIZE_T "d positional argument%.1s (%" CYTHON_FORMAT_SSIZE_T "d given)", func_name, more_or_less, num_expected, (num_expected == 1) ? "" : "s", num_found); } /* GetException */ #if CYTHON_FAST_THREAD_STATE static int __Pyx__GetException(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) #else static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb) #endif { PyObject *local_type = NULL, *local_value, *local_tb = NULL; #if CYTHON_FAST_THREAD_STATE PyObject *tmp_type, *tmp_value, *tmp_tb; #if PY_VERSION_HEX >= 0x030C00A6 local_value = tstate->current_exception; tstate->current_exception = 0; if (likely(local_value)) { local_type = (PyObject*) Py_TYPE(local_value); Py_INCREF(local_type); local_tb = PyException_GetTraceback(local_value); } #else local_type = tstate->curexc_type; local_value = tstate->curexc_value; local_tb = tstate->curexc_traceback; tstate->curexc_type = 0; tstate->curexc_value = 0; tstate->curexc_traceback = 0; #endif #else PyErr_Fetch(&local_type, &local_value, &local_tb); #endif PyErr_NormalizeException(&local_type, &local_value, &local_tb); #if CYTHON_FAST_THREAD_STATE && PY_VERSION_HEX >= 0x030C00A6 if (unlikely(tstate->current_exception)) #elif CYTHON_FAST_THREAD_STATE if (unlikely(tstate->curexc_type)) #else if (unlikely(PyErr_Occurred())) #endif goto bad; #if PY_MAJOR_VERSION >= 3 if (local_tb) { if (unlikely(PyException_SetTraceback(local_value, local_tb) < 0)) goto bad; } #endif Py_XINCREF(local_tb); Py_XINCREF(local_type); Py_XINCREF(local_value); *type = local_type; *value = local_value; *tb = local_tb; #if CYTHON_FAST_THREAD_STATE #if CYTHON_USE_EXC_INFO_STACK { _PyErr_StackItem *exc_info = tstate->exc_info; #if PY_VERSION_HEX >= 0x030B00a4 tmp_value = exc_info->exc_value; exc_info->exc_value = local_value; tmp_type = NULL; tmp_tb = NULL; Py_XDECREF(local_type); Py_XDECREF(local_tb); #else tmp_type = exc_info->exc_type; tmp_value = exc_info->exc_value; tmp_tb = exc_info->exc_traceback; exc_info->exc_type = local_type; exc_info->exc_value = local_value; exc_info->exc_traceback = local_tb; #endif } #else tmp_type = tstate->exc_type; tmp_value = tstate->exc_value; tmp_tb = tstate->exc_traceback; tstate->exc_type = local_type; tstate->exc_value = local_value; tstate->exc_traceback = local_tb; #endif Py_XDECREF(tmp_type); Py_XDECREF(tmp_value); Py_XDECREF(tmp_tb); #else PyErr_SetExcInfo(local_type, local_value, local_tb); #endif return 0; bad: *type = 0; *value = 0; *tb = 0; Py_XDECREF(local_type); Py_XDECREF(local_value); Py_XDECREF(local_tb); return -1; } /* SwapException */ #if CYTHON_FAST_THREAD_STATE static CYTHON_INLINE void __Pyx__ExceptionSwap(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) { PyObject *tmp_type, *tmp_value, *tmp_tb; #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4 _PyErr_StackItem *exc_info = tstate->exc_info; tmp_value = exc_info->exc_value; exc_info->exc_value = *value; if (tmp_value == NULL || tmp_value == Py_None) { Py_XDECREF(tmp_value); tmp_value = NULL; tmp_type = NULL; tmp_tb = NULL; } else { tmp_type = (PyObject*) Py_TYPE(tmp_value); Py_INCREF(tmp_type); #if CYTHON_COMPILING_IN_CPYTHON tmp_tb = ((PyBaseExceptionObject*) tmp_value)->traceback; Py_XINCREF(tmp_tb); #else tmp_tb = PyException_GetTraceback(tmp_value); #endif } #elif CYTHON_USE_EXC_INFO_STACK _PyErr_StackItem *exc_info = tstate->exc_info; tmp_type = exc_info->exc_type; tmp_value = exc_info->exc_value; tmp_tb = exc_info->exc_traceback; exc_info->exc_type = *type; exc_info->exc_value = *value; exc_info->exc_traceback = *tb; #else tmp_type = tstate->exc_type; tmp_value = tstate->exc_value; tmp_tb = tstate->exc_traceback; tstate->exc_type = *type; tstate->exc_value = *value; tstate->exc_traceback = *tb; #endif *type = tmp_type; *value = tmp_value; *tb = tmp_tb; } #else static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb) { PyObject *tmp_type, *tmp_value, *tmp_tb; PyErr_GetExcInfo(&tmp_type, &tmp_value, &tmp_tb); PyErr_SetExcInfo(*type, *value, *tb); *type = tmp_type; *value = tmp_value; *tb = tmp_tb; } #endif /* GetTopmostException */ #if CYTHON_USE_EXC_INFO_STACK && CYTHON_FAST_THREAD_STATE static _PyErr_StackItem * __Pyx_PyErr_GetTopmostException(PyThreadState *tstate) { _PyErr_StackItem *exc_info = tstate->exc_info; while ((exc_info->exc_value == NULL || exc_info->exc_value == Py_None) && exc_info->previous_item != NULL) { exc_info = exc_info->previous_item; } return exc_info; } #endif /* SaveResetException */ #if CYTHON_FAST_THREAD_STATE static CYTHON_INLINE void __Pyx__ExceptionSave(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) { #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4 _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate); PyObject *exc_value = exc_info->exc_value; if (exc_value == NULL || exc_value == Py_None) { *value = NULL; *type = NULL; *tb = NULL; } else { *value = exc_value; Py_INCREF(*value); *type = (PyObject*) Py_TYPE(exc_value); Py_INCREF(*type); *tb = PyException_GetTraceback(exc_value); } #elif CYTHON_USE_EXC_INFO_STACK _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate); *type = exc_info->exc_type; *value = exc_info->exc_value; *tb = exc_info->exc_traceback; Py_XINCREF(*type); Py_XINCREF(*value); Py_XINCREF(*tb); #else *type = tstate->exc_type; *value = tstate->exc_value; *tb = tstate->exc_traceback; Py_XINCREF(*type); Py_XINCREF(*value); Py_XINCREF(*tb); #endif } static CYTHON_INLINE void __Pyx__ExceptionReset(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb) { #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4 _PyErr_StackItem *exc_info = tstate->exc_info; PyObject *tmp_value = exc_info->exc_value; exc_info->exc_value = value; Py_XDECREF(tmp_value); Py_XDECREF(type); Py_XDECREF(tb); #else PyObject *tmp_type, *tmp_value, *tmp_tb; #if CYTHON_USE_EXC_INFO_STACK _PyErr_StackItem *exc_info = tstate->exc_info; tmp_type = exc_info->exc_type; tmp_value = exc_info->exc_value; tmp_tb = exc_info->exc_traceback; exc_info->exc_type = type; exc_info->exc_value = value; exc_info->exc_traceback = tb; #else tmp_type = tstate->exc_type; tmp_value = tstate->exc_value; tmp_tb = tstate->exc_traceback; tstate->exc_type = type; tstate->exc_value = value; tstate->exc_traceback = tb; #endif Py_XDECREF(tmp_type); Py_XDECREF(tmp_value); Py_XDECREF(tmp_tb); #endif } #endif /* PyObjectCall */ #if CYTHON_COMPILING_IN_CPYTHON static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) { PyObject *result; ternaryfunc call = Py_TYPE(func)->tp_call; if (unlikely(!call)) return PyObject_Call(func, arg, kw); #if PY_MAJOR_VERSION < 3 if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object"))) return NULL; #else if (unlikely(Py_EnterRecursiveCall(" while calling a Python object"))) return NULL; #endif result = (*call)(func, arg, kw); Py_LeaveRecursiveCall(); if (unlikely(!result) && unlikely(!PyErr_Occurred())) { PyErr_SetString( PyExc_SystemError, "NULL result without error in PyObject_Call"); } return result; } #endif /* RaiseException */ #if PY_MAJOR_VERSION < 3 static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause) { __Pyx_PyThreadState_declare CYTHON_UNUSED_VAR(cause); Py_XINCREF(type); if (!value || value == Py_None) value = NULL; else Py_INCREF(value); if (!tb || tb == Py_None) tb = NULL; else { Py_INCREF(tb); if (!PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "raise: arg 3 must be a traceback or None"); goto raise_error; } } if (PyType_Check(type)) { #if CYTHON_COMPILING_IN_PYPY if (!value) { Py_INCREF(Py_None); value = Py_None; } #endif PyErr_NormalizeException(&type, &value, &tb); } else { if (value) { PyErr_SetString(PyExc_TypeError, "instance exception may not have a separate value"); goto raise_error; } value = type; type = (PyObject*) Py_TYPE(type); Py_INCREF(type); if (!PyType_IsSubtype((PyTypeObject *)type, (PyTypeObject *)PyExc_BaseException)) { PyErr_SetString(PyExc_TypeError, "raise: exception class must be a subclass of BaseException"); goto raise_error; } } __Pyx_PyThreadState_assign __Pyx_ErrRestore(type, value, tb); return; raise_error: Py_XDECREF(value); Py_XDECREF(type); Py_XDECREF(tb); return; } #else static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause) { PyObject* owned_instance = NULL; if (tb == Py_None) { tb = 0; } else if (tb && !PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "raise: arg 3 must be a traceback or None"); goto bad; } if (value == Py_None) value = 0; if (PyExceptionInstance_Check(type)) { if (value) { PyErr_SetString(PyExc_TypeError, "instance exception may not have a separate value"); goto bad; } value = type; type = (PyObject*) Py_TYPE(value); } else if (PyExceptionClass_Check(type)) { PyObject *instance_class = NULL; if (value && PyExceptionInstance_Check(value)) { instance_class = (PyObject*) Py_TYPE(value); if (instance_class != type) { int is_subclass = PyObject_IsSubclass(instance_class, type); if (!is_subclass) { instance_class = NULL; } else if (unlikely(is_subclass == -1)) { goto bad; } else { type = instance_class; } } } if (!instance_class) { PyObject *args; if (!value) args = PyTuple_New(0); else if (PyTuple_Check(value)) { Py_INCREF(value); args = value; } else args = PyTuple_Pack(1, value); if (!args) goto bad; owned_instance = PyObject_Call(type, args, NULL); Py_DECREF(args); if (!owned_instance) goto bad; value = owned_instance; if (!PyExceptionInstance_Check(value)) { PyErr_Format(PyExc_TypeError, "calling %R should have returned an instance of " "BaseException, not %R", type, Py_TYPE(value)); goto bad; } } } else { PyErr_SetString(PyExc_TypeError, "raise: exception class must be a subclass of BaseException"); goto bad; } if (cause) { PyObject *fixed_cause; if (cause == Py_None) { fixed_cause = NULL; } else if (PyExceptionClass_Check(cause)) { fixed_cause = PyObject_CallObject(cause, NULL); if (fixed_cause == NULL) goto bad; } else if (PyExceptionInstance_Check(cause)) { fixed_cause = cause; Py_INCREF(fixed_cause); } else { PyErr_SetString(PyExc_TypeError, "exception causes must derive from " "BaseException"); goto bad; } PyException_SetCause(value, fixed_cause); } PyErr_SetObject(type, value); if (tb) { #if PY_VERSION_HEX >= 0x030C00A6 PyException_SetTraceback(value, tb); #elif CYTHON_FAST_THREAD_STATE PyThreadState *tstate = __Pyx_PyThreadState_Current; PyObject* tmp_tb = tstate->curexc_traceback; if (tb != tmp_tb) { Py_INCREF(tb); tstate->curexc_traceback = tb; Py_XDECREF(tmp_tb); } #else PyObject *tmp_type, *tmp_value, *tmp_tb; PyErr_Fetch(&tmp_type, &tmp_value, &tmp_tb); Py_INCREF(tb); PyErr_Restore(tmp_type, tmp_value, tb); Py_XDECREF(tmp_tb); #endif } bad: Py_XDECREF(owned_instance); return; } #endif /* KeywordStringCheck */ static int __Pyx_CheckKeywordStrings( PyObject *kw, const char* function_name, int kw_allowed) { PyObject* key = 0; Py_ssize_t pos = 0; #if CYTHON_COMPILING_IN_PYPY if (!kw_allowed && PyDict_Next(kw, &pos, &key, 0)) goto invalid_keyword; return 1; #else if (CYTHON_METH_FASTCALL && likely(PyTuple_Check(kw))) { Py_ssize_t kwsize; #if CYTHON_ASSUME_SAFE_MACROS kwsize = PyTuple_GET_SIZE(kw); #else kwsize = PyTuple_Size(kw); if (kwsize < 0) return 0; #endif if (unlikely(kwsize == 0)) return 1; if (!kw_allowed) { #if CYTHON_ASSUME_SAFE_MACROS key = PyTuple_GET_ITEM(kw, 0); #else key = PyTuple_GetItem(kw, pos); if (!key) return 0; #endif goto invalid_keyword; } #if PY_VERSION_HEX < 0x03090000 for (pos = 0; pos < kwsize; pos++) { #if CYTHON_ASSUME_SAFE_MACROS key = PyTuple_GET_ITEM(kw, pos); #else key = PyTuple_GetItem(kw, pos); if (!key) return 0; #endif if (unlikely(!PyUnicode_Check(key))) goto invalid_keyword_type; } #endif return 1; } while (PyDict_Next(kw, &pos, &key, 0)) { #if PY_MAJOR_VERSION < 3 if (unlikely(!PyString_Check(key))) #endif if (unlikely(!PyUnicode_Check(key))) goto invalid_keyword_type; } if (!kw_allowed && unlikely(key)) goto invalid_keyword; return 1; invalid_keyword_type: PyErr_Format(PyExc_TypeError, "%.200s() keywords must be strings", function_name); return 0; #endif invalid_keyword: #if PY_MAJOR_VERSION < 3 PyErr_Format(PyExc_TypeError, "%.200s() got an unexpected keyword argument '%.200s'", function_name, PyString_AsString(key)); #else PyErr_Format(PyExc_TypeError, "%s() got an unexpected keyword argument '%U'", function_name, key); #endif return 0; } /* PyDictVersioning */ #if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj) { PyObject *dict = Py_TYPE(obj)->tp_dict; return likely(dict) ? __PYX_GET_DICT_VERSION(dict) : 0; } static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj) { PyObject **dictptr = NULL; Py_ssize_t offset = Py_TYPE(obj)->tp_dictoffset; if (offset) { #if CYTHON_COMPILING_IN_CPYTHON dictptr = (likely(offset > 0)) ? (PyObject **) ((char *)obj + offset) : _PyObject_GetDictPtr(obj); #else dictptr = _PyObject_GetDictPtr(obj); #endif } return (dictptr && *dictptr) ? __PYX_GET_DICT_VERSION(*dictptr) : 0; } static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version) { PyObject *dict = Py_TYPE(obj)->tp_dict; if (unlikely(!dict) || unlikely(tp_dict_version != __PYX_GET_DICT_VERSION(dict))) return 0; return obj_dict_version == __Pyx_get_object_dict_version(obj); } #endif /* GetModuleGlobalName */ #if CYTHON_USE_DICT_VERSIONS static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value) #else static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name) #endif { PyObject *result; #if !CYTHON_AVOID_BORROWED_REFS #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030500A1 && PY_VERSION_HEX < 0x030d0000 result = _PyDict_GetItem_KnownHash(__pyx_d, name, ((PyASCIIObject *) name)->hash); __PYX_UPDATE_DICT_CACHE(__pyx_d, result, *dict_cached_value, *dict_version) if (likely(result)) { return __Pyx_NewRef(result); } else if (unlikely(PyErr_Occurred())) { return NULL; } #elif CYTHON_COMPILING_IN_LIMITED_API if (unlikely(!__pyx_m)) { return NULL; } result = PyObject_GetAttr(__pyx_m, name); if (likely(result)) { return result; } #else result = PyDict_GetItem(__pyx_d, name); __PYX_UPDATE_DICT_CACHE(__pyx_d, result, *dict_cached_value, *dict_version) if (likely(result)) { return __Pyx_NewRef(result); } #endif #else result = PyObject_GetItem(__pyx_d, name); __PYX_UPDATE_DICT_CACHE(__pyx_d, result, *dict_cached_value, *dict_version) if (likely(result)) { return __Pyx_NewRef(result); } PyErr_Clear(); #endif return __Pyx_GetBuiltinName(name); } /* PyFunctionFastCall */ #if CYTHON_FAST_PYCALL && !CYTHON_VECTORCALL static PyObject* __Pyx_PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t na, PyObject *globals) { PyFrameObject *f; PyThreadState *tstate = __Pyx_PyThreadState_Current; PyObject **fastlocals; Py_ssize_t i; PyObject *result; assert(globals != NULL); /* XXX Perhaps we should create a specialized PyFrame_New() that doesn't take locals, but does take builtins without sanity checking them. */ assert(tstate != NULL); f = PyFrame_New(tstate, co, globals, NULL); if (f == NULL) { return NULL; } fastlocals = __Pyx_PyFrame_GetLocalsplus(f); for (i = 0; i < na; i++) { Py_INCREF(*args); fastlocals[i] = *args++; } result = PyEval_EvalFrameEx(f,0); ++tstate->recursion_depth; Py_DECREF(f); --tstate->recursion_depth; return result; } static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, PyObject *kwargs) { PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); PyObject *closure; #if PY_MAJOR_VERSION >= 3 PyObject *kwdefs; #endif PyObject *kwtuple, **k; PyObject **d; Py_ssize_t nd; Py_ssize_t nk; PyObject *result; assert(kwargs == NULL || PyDict_Check(kwargs)); nk = kwargs ? PyDict_Size(kwargs) : 0; #if PY_MAJOR_VERSION < 3 if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object"))) { return NULL; } #else if (unlikely(Py_EnterRecursiveCall(" while calling a Python object"))) { return NULL; } #endif if ( #if PY_MAJOR_VERSION >= 3 co->co_kwonlyargcount == 0 && #endif likely(kwargs == NULL || nk == 0) && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { if (argdefs == NULL && co->co_argcount == nargs) { result = __Pyx_PyFunction_FastCallNoKw(co, args, nargs, globals); goto done; } else if (nargs == 0 && argdefs != NULL && co->co_argcount == Py_SIZE(argdefs)) { /* function called with no arguments, but all parameters have a default value: use default values as arguments .*/ args = &PyTuple_GET_ITEM(argdefs, 0); result =__Pyx_PyFunction_FastCallNoKw(co, args, Py_SIZE(argdefs), globals); goto done; } } if (kwargs != NULL) { Py_ssize_t pos, i; kwtuple = PyTuple_New(2 * nk); if (kwtuple == NULL) { result = NULL; goto done; } k = &PyTuple_GET_ITEM(kwtuple, 0); pos = i = 0; while (PyDict_Next(kwargs, &pos, &k[i], &k[i+1])) { Py_INCREF(k[i]); Py_INCREF(k[i+1]); i += 2; } nk = i / 2; } else { kwtuple = NULL; k = NULL; } closure = PyFunction_GET_CLOSURE(func); #if PY_MAJOR_VERSION >= 3 kwdefs = PyFunction_GET_KW_DEFAULTS(func); #endif if (argdefs != NULL) { d = &PyTuple_GET_ITEM(argdefs, 0); nd = Py_SIZE(argdefs); } else { d = NULL; nd = 0; } #if PY_MAJOR_VERSION >= 3 result = PyEval_EvalCodeEx((PyObject*)co, globals, (PyObject *)NULL, args, (int)nargs, k, (int)nk, d, (int)nd, kwdefs, closure); #else result = PyEval_EvalCodeEx(co, globals, (PyObject *)NULL, args, (int)nargs, k, (int)nk, d, (int)nd, closure); #endif Py_XDECREF(kwtuple); done: Py_LeaveRecursiveCall(); return result; } #endif /* PyObjectCallMethO */ #if CYTHON_COMPILING_IN_CPYTHON static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg) { PyObject *self, *result; PyCFunction cfunc; cfunc = __Pyx_CyOrPyCFunction_GET_FUNCTION(func); self = __Pyx_CyOrPyCFunction_GET_SELF(func); #if PY_MAJOR_VERSION < 3 if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object"))) return NULL; #else if (unlikely(Py_EnterRecursiveCall(" while calling a Python object"))) return NULL; #endif result = cfunc(self, arg); Py_LeaveRecursiveCall(); if (unlikely(!result) && unlikely(!PyErr_Occurred())) { PyErr_SetString( PyExc_SystemError, "NULL result without error in PyObject_Call"); } return result; } #endif /* PyObjectFastCall */ #if PY_VERSION_HEX < 0x03090000 || CYTHON_COMPILING_IN_LIMITED_API static PyObject* __Pyx_PyObject_FastCall_fallback(PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs) { PyObject *argstuple; PyObject *result = 0; size_t i; argstuple = PyTuple_New((Py_ssize_t)nargs); if (unlikely(!argstuple)) return NULL; for (i = 0; i < nargs; i++) { Py_INCREF(args[i]); if (__Pyx_PyTuple_SET_ITEM(argstuple, (Py_ssize_t)i, args[i]) < 0) goto bad; } result = __Pyx_PyObject_Call(func, argstuple, kwargs); bad: Py_DECREF(argstuple); return result; } #endif static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject **args, size_t _nargs, PyObject *kwargs) { Py_ssize_t nargs = __Pyx_PyVectorcall_NARGS(_nargs); #if CYTHON_COMPILING_IN_CPYTHON if (nargs == 0 && kwargs == NULL) { if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_NOARGS)) return __Pyx_PyObject_CallMethO(func, NULL); } else if (nargs == 1 && kwargs == NULL) { if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_O)) return __Pyx_PyObject_CallMethO(func, args[0]); } #endif #if PY_VERSION_HEX < 0x030800B1 #if CYTHON_FAST_PYCCALL if (PyCFunction_Check(func)) { if (kwargs) { return _PyCFunction_FastCallDict(func, args, nargs, kwargs); } else { return _PyCFunction_FastCallKeywords(func, args, nargs, NULL); } } #if PY_VERSION_HEX >= 0x030700A1 if (!kwargs && __Pyx_IS_TYPE(func, &PyMethodDescr_Type)) { return _PyMethodDescr_FastCallKeywords(func, args, nargs, NULL); } #endif #endif #if CYTHON_FAST_PYCALL if (PyFunction_Check(func)) { return __Pyx_PyFunction_FastCallDict(func, args, nargs, kwargs); } #endif #endif if (kwargs == NULL) { #if CYTHON_VECTORCALL #if PY_VERSION_HEX < 0x03090000 vectorcallfunc f = _PyVectorcall_Function(func); #else vectorcallfunc f = PyVectorcall_Function(func); #endif if (f) { return f(func, args, (size_t)nargs, NULL); } #elif defined(__Pyx_CyFunction_USED) && CYTHON_BACKPORT_VECTORCALL if (__Pyx_CyFunction_CheckExact(func)) { __pyx_vectorcallfunc f = __Pyx_CyFunction_func_vectorcall(func); if (f) return f(func, args, (size_t)nargs, NULL); } #endif } if (nargs == 0) { return __Pyx_PyObject_Call(func, __pyx_empty_tuple, kwargs); } #if PY_VERSION_HEX >= 0x03090000 && !CYTHON_COMPILING_IN_LIMITED_API return PyObject_VectorcallDict(func, args, (size_t)nargs, kwargs); #else return __Pyx_PyObject_FastCall_fallback(func, args, (size_t)nargs, kwargs); #endif } /* FixUpExtensionType */ #if CYTHON_USE_TYPE_SPECS static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject *type) { #if PY_VERSION_HEX > 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API CYTHON_UNUSED_VAR(spec); CYTHON_UNUSED_VAR(type); #else const PyType_Slot *slot = spec->slots; while (slot && slot->slot && slot->slot != Py_tp_members) slot++; if (slot && slot->slot == Py_tp_members) { int changed = 0; #if !(PY_VERSION_HEX <= 0x030900b1 && CYTHON_COMPILING_IN_CPYTHON) const #endif PyMemberDef *memb = (PyMemberDef*) slot->pfunc; while (memb && memb->name) { if (memb->name[0] == '_' && memb->name[1] == '_') { #if PY_VERSION_HEX < 0x030900b1 if (strcmp(memb->name, "__weaklistoffset__") == 0) { assert(memb->type == T_PYSSIZET); assert(memb->flags == READONLY); type->tp_weaklistoffset = memb->offset; changed = 1; } else if (strcmp(memb->name, "__dictoffset__") == 0) { assert(memb->type == T_PYSSIZET); assert(memb->flags == READONLY); type->tp_dictoffset = memb->offset; changed = 1; } #if CYTHON_METH_FASTCALL else if (strcmp(memb->name, "__vectorcalloffset__") == 0) { assert(memb->type == T_PYSSIZET); assert(memb->flags == READONLY); #if PY_VERSION_HEX >= 0x030800b4 type->tp_vectorcall_offset = memb->offset; #else type->tp_print = (printfunc) memb->offset; #endif changed = 1; } #endif #else if ((0)); #endif #if PY_VERSION_HEX <= 0x030900b1 && CYTHON_COMPILING_IN_CPYTHON else if (strcmp(memb->name, "__module__") == 0) { PyObject *descr; assert(memb->type == T_OBJECT); assert(memb->flags == 0 || memb->flags == READONLY); descr = PyDescr_NewMember(type, memb); if (unlikely(!descr)) return -1; if (unlikely(PyDict_SetItem(type->tp_dict, PyDescr_NAME(descr), descr) < 0)) { Py_DECREF(descr); return -1; } Py_DECREF(descr); changed = 1; } #endif } memb++; } if (changed) PyType_Modified(type); } #endif return 0; } #endif /* PyObjectCallNoArg */ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func) { PyObject *arg[2] = {NULL, NULL}; return __Pyx_PyObject_FastCall(func, arg + 1, 0 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); } /* PyObjectCallOneArg */ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg) { PyObject *args[2] = {NULL, arg}; return __Pyx_PyObject_FastCall(func, args+1, 1 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); } /* PyObjectGetMethod */ static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) { PyObject *attr; #if CYTHON_UNPACK_METHODS && CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_PYTYPE_LOOKUP __Pyx_TypeName type_name; PyTypeObject *tp = Py_TYPE(obj); PyObject *descr; descrgetfunc f = NULL; PyObject **dictptr, *dict; int meth_found = 0; assert (*method == NULL); if (unlikely(tp->tp_getattro != PyObject_GenericGetAttr)) { attr = __Pyx_PyObject_GetAttrStr(obj, name); goto try_unpack; } if (unlikely(tp->tp_dict == NULL) && unlikely(PyType_Ready(tp) < 0)) { return 0; } descr = _PyType_Lookup(tp, name); if (likely(descr != NULL)) { Py_INCREF(descr); #if defined(Py_TPFLAGS_METHOD_DESCRIPTOR) && Py_TPFLAGS_METHOD_DESCRIPTOR if (__Pyx_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)) #elif PY_MAJOR_VERSION >= 3 #ifdef __Pyx_CyFunction_USED if (likely(PyFunction_Check(descr) || __Pyx_IS_TYPE(descr, &PyMethodDescr_Type) || __Pyx_CyFunction_Check(descr))) #else if (likely(PyFunction_Check(descr) || __Pyx_IS_TYPE(descr, &PyMethodDescr_Type))) #endif #else #ifdef __Pyx_CyFunction_USED if (likely(PyFunction_Check(descr) || __Pyx_CyFunction_Check(descr))) #else if (likely(PyFunction_Check(descr))) #endif #endif { meth_found = 1; } else { f = Py_TYPE(descr)->tp_descr_get; if (f != NULL && PyDescr_IsData(descr)) { attr = f(descr, obj, (PyObject *)Py_TYPE(obj)); Py_DECREF(descr); goto try_unpack; } } } dictptr = _PyObject_GetDictPtr(obj); if (dictptr != NULL && (dict = *dictptr) != NULL) { Py_INCREF(dict); attr = __Pyx_PyDict_GetItemStr(dict, name); if (attr != NULL) { Py_INCREF(attr); Py_DECREF(dict); Py_XDECREF(descr); goto try_unpack; } Py_DECREF(dict); } if (meth_found) { *method = descr; return 1; } if (f != NULL) { attr = f(descr, obj, (PyObject *)Py_TYPE(obj)); Py_DECREF(descr); goto try_unpack; } if (likely(descr != NULL)) { *method = descr; return 0; } type_name = __Pyx_PyType_GetName(tp); PyErr_Format(PyExc_AttributeError, #if PY_MAJOR_VERSION >= 3 "'" __Pyx_FMT_TYPENAME "' object has no attribute '%U'", type_name, name); #else "'" __Pyx_FMT_TYPENAME "' object has no attribute '%.400s'", type_name, PyString_AS_STRING(name)); #endif __Pyx_DECREF_TypeName(type_name); return 0; #else attr = __Pyx_PyObject_GetAttrStr(obj, name); goto try_unpack; #endif try_unpack: #if CYTHON_UNPACK_METHODS if (likely(attr) && PyMethod_Check(attr) && likely(PyMethod_GET_SELF(attr) == obj)) { PyObject *function = PyMethod_GET_FUNCTION(attr); Py_INCREF(function); Py_DECREF(attr); *method = function; return 1; } #endif *method = attr; return 0; } /* PyObjectCallMethod0 */ static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name) { PyObject *method = NULL, *result = NULL; int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method); if (likely(is_method)) { result = __Pyx_PyObject_CallOneArg(method, obj); Py_DECREF(method); return result; } if (unlikely(!method)) goto bad; result = __Pyx_PyObject_CallNoArg(method); Py_DECREF(method); bad: return result; } /* ValidateBasesTuple */ #if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_USE_TYPE_SPECS static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffset, PyObject *bases) { Py_ssize_t i, n; #if CYTHON_ASSUME_SAFE_MACROS n = PyTuple_GET_SIZE(bases); #else n = PyTuple_Size(bases); if (n < 0) return -1; #endif for (i = 1; i < n; i++) { #if CYTHON_AVOID_BORROWED_REFS PyObject *b0 = PySequence_GetItem(bases, i); if (!b0) return -1; #elif CYTHON_ASSUME_SAFE_MACROS PyObject *b0 = PyTuple_GET_ITEM(bases, i); #else PyObject *b0 = PyTuple_GetItem(bases, i); if (!b0) return -1; #endif PyTypeObject *b; #if PY_MAJOR_VERSION < 3 if (PyClass_Check(b0)) { PyErr_Format(PyExc_TypeError, "base class '%.200s' is an old-style class", PyString_AS_STRING(((PyClassObject*)b0)->cl_name)); #if CYTHON_AVOID_BORROWED_REFS Py_DECREF(b0); #endif return -1; } #endif b = (PyTypeObject*) b0; if (!__Pyx_PyType_HasFeature(b, Py_TPFLAGS_HEAPTYPE)) { __Pyx_TypeName b_name = __Pyx_PyType_GetName(b); PyErr_Format(PyExc_TypeError, "base class '" __Pyx_FMT_TYPENAME "' is not a heap type", b_name); __Pyx_DECREF_TypeName(b_name); #if CYTHON_AVOID_BORROWED_REFS Py_DECREF(b0); #endif return -1; } if (dictoffset == 0) { Py_ssize_t b_dictoffset = 0; #if CYTHON_USE_TYPE_SLOTS || CYTHON_COMPILING_IN_PYPY b_dictoffset = b->tp_dictoffset; #else PyObject *py_b_dictoffset = PyObject_GetAttrString((PyObject*)b, "__dictoffset__"); if (!py_b_dictoffset) goto dictoffset_return; b_dictoffset = PyLong_AsSsize_t(py_b_dictoffset); Py_DECREF(py_b_dictoffset); if (b_dictoffset == -1 && PyErr_Occurred()) goto dictoffset_return; #endif if (b_dictoffset) { { __Pyx_TypeName b_name = __Pyx_PyType_GetName(b); PyErr_Format(PyExc_TypeError, "extension type '%.200s' has no __dict__ slot, " "but base type '" __Pyx_FMT_TYPENAME "' has: " "either add 'cdef dict __dict__' to the extension type " "or add '__slots__ = [...]' to the base type", type_name, b_name); __Pyx_DECREF_TypeName(b_name); } #if !(CYTHON_USE_TYPE_SLOTS || CYTHON_COMPILING_IN_PYPY) dictoffset_return: #endif #if CYTHON_AVOID_BORROWED_REFS Py_DECREF(b0); #endif return -1; } } #if CYTHON_AVOID_BORROWED_REFS Py_DECREF(b0); #endif } return 0; } #endif /* PyType_Ready */ static int __Pyx_PyType_Ready(PyTypeObject *t) { #if CYTHON_USE_TYPE_SPECS || !(CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API) || defined(PYSTON_MAJOR_VERSION) (void)__Pyx_PyObject_CallMethod0; #if CYTHON_USE_TYPE_SPECS (void)__Pyx_validate_bases_tuple; #endif return PyType_Ready(t); #else int r; PyObject *bases = __Pyx_PyType_GetSlot(t, tp_bases, PyObject*); if (bases && unlikely(__Pyx_validate_bases_tuple(t->tp_name, t->tp_dictoffset, bases) == -1)) return -1; #if PY_VERSION_HEX >= 0x03050000 && !defined(PYSTON_MAJOR_VERSION) { int gc_was_enabled; #if PY_VERSION_HEX >= 0x030A00b1 gc_was_enabled = PyGC_Disable(); (void)__Pyx_PyObject_CallMethod0; #else PyObject *ret, *py_status; PyObject *gc = NULL; #if PY_VERSION_HEX >= 0x030700a1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM+0 >= 0x07030400) gc = PyImport_GetModule(__pyx_kp_u_gc); #endif if (unlikely(!gc)) gc = PyImport_Import(__pyx_kp_u_gc); if (unlikely(!gc)) return -1; py_status = __Pyx_PyObject_CallMethod0(gc, __pyx_kp_u_isenabled); if (unlikely(!py_status)) { Py_DECREF(gc); return -1; } gc_was_enabled = __Pyx_PyObject_IsTrue(py_status); Py_DECREF(py_status); if (gc_was_enabled > 0) { ret = __Pyx_PyObject_CallMethod0(gc, __pyx_kp_u_disable); if (unlikely(!ret)) { Py_DECREF(gc); return -1; } Py_DECREF(ret); } else if (unlikely(gc_was_enabled == -1)) { Py_DECREF(gc); return -1; } #endif t->tp_flags |= Py_TPFLAGS_HEAPTYPE; #if PY_VERSION_HEX >= 0x030A0000 t->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE; #endif #else (void)__Pyx_PyObject_CallMethod0; #endif r = PyType_Ready(t); #if PY_VERSION_HEX >= 0x03050000 && !defined(PYSTON_MAJOR_VERSION) t->tp_flags &= ~Py_TPFLAGS_HEAPTYPE; #if PY_VERSION_HEX >= 0x030A00b1 if (gc_was_enabled) PyGC_Enable(); #else if (gc_was_enabled) { PyObject *tp, *v, *tb; PyErr_Fetch(&tp, &v, &tb); ret = __Pyx_PyObject_CallMethod0(gc, __pyx_kp_u_enable); if (likely(ret || r == -1)) { Py_XDECREF(ret); PyErr_Restore(tp, v, tb); } else { Py_XDECREF(tp); Py_XDECREF(v); Py_XDECREF(tb); r = -1; } } Py_DECREF(gc); #endif } #endif return r; #endif } /* PyObject_GenericGetAttrNoDict */ #if CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP && PY_VERSION_HEX < 0x03070000 static PyObject *__Pyx_RaiseGenericGetAttributeError(PyTypeObject *tp, PyObject *attr_name) { __Pyx_TypeName type_name = __Pyx_PyType_GetName(tp); PyErr_Format(PyExc_AttributeError, #if PY_MAJOR_VERSION >= 3 "'" __Pyx_FMT_TYPENAME "' object has no attribute '%U'", type_name, attr_name); #else "'" __Pyx_FMT_TYPENAME "' object has no attribute '%.400s'", type_name, PyString_AS_STRING(attr_name)); #endif __Pyx_DECREF_TypeName(type_name); return NULL; } static CYTHON_INLINE PyObject* __Pyx_PyObject_GenericGetAttrNoDict(PyObject* obj, PyObject* attr_name) { PyObject *descr; PyTypeObject *tp = Py_TYPE(obj); if (unlikely(!PyString_Check(attr_name))) { return PyObject_GenericGetAttr(obj, attr_name); } assert(!tp->tp_dictoffset); descr = _PyType_Lookup(tp, attr_name); if (unlikely(!descr)) { return __Pyx_RaiseGenericGetAttributeError(tp, attr_name); } Py_INCREF(descr); #if PY_MAJOR_VERSION < 3 if (likely(PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_HAVE_CLASS))) #endif { descrgetfunc f = Py_TYPE(descr)->tp_descr_get; if (unlikely(f)) { PyObject *res = f(descr, obj, (PyObject *)tp); Py_DECREF(descr); return res; } } return descr; } #endif /* PyObject_GenericGetAttr */ #if CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP && PY_VERSION_HEX < 0x03070000 static PyObject* __Pyx_PyObject_GenericGetAttr(PyObject* obj, PyObject* attr_name) { if (unlikely(Py_TYPE(obj)->tp_dictoffset)) { return PyObject_GenericGetAttr(obj, attr_name); } return __Pyx_PyObject_GenericGetAttrNoDict(obj, attr_name); } #endif /* SetupReduce */ #if !CYTHON_COMPILING_IN_LIMITED_API static int __Pyx_setup_reduce_is_named(PyObject* meth, PyObject* name) { int ret; PyObject *name_attr; name_attr = __Pyx_PyObject_GetAttrStrNoError(meth, __pyx_n_s_name); if (likely(name_attr)) { ret = PyObject_RichCompareBool(name_attr, name, Py_EQ); } else { ret = -1; } if (unlikely(ret < 0)) { PyErr_Clear(); ret = 0; } Py_XDECREF(name_attr); return ret; } static int __Pyx_setup_reduce(PyObject* type_obj) { int ret = 0; PyObject *object_reduce = NULL; PyObject *object_getstate = NULL; PyObject *object_reduce_ex = NULL; PyObject *reduce = NULL; PyObject *reduce_ex = NULL; PyObject *reduce_cython = NULL; PyObject *setstate = NULL; PyObject *setstate_cython = NULL; PyObject *getstate = NULL; #if CYTHON_USE_PYTYPE_LOOKUP getstate = _PyType_Lookup((PyTypeObject*)type_obj, __pyx_n_s_getstate); #else getstate = __Pyx_PyObject_GetAttrStrNoError(type_obj, __pyx_n_s_getstate); if (!getstate && PyErr_Occurred()) { goto __PYX_BAD; } #endif if (getstate) { #if CYTHON_USE_PYTYPE_LOOKUP object_getstate = _PyType_Lookup(&PyBaseObject_Type, __pyx_n_s_getstate); #else object_getstate = __Pyx_PyObject_GetAttrStrNoError((PyObject*)&PyBaseObject_Type, __pyx_n_s_getstate); if (!object_getstate && PyErr_Occurred()) { goto __PYX_BAD; } #endif if (object_getstate != getstate) { goto __PYX_GOOD; } } #if CYTHON_USE_PYTYPE_LOOKUP object_reduce_ex = _PyType_Lookup(&PyBaseObject_Type, __pyx_n_s_reduce_ex); if (!object_reduce_ex) goto __PYX_BAD; #else object_reduce_ex = __Pyx_PyObject_GetAttrStr((PyObject*)&PyBaseObject_Type, __pyx_n_s_reduce_ex); if (!object_reduce_ex) goto __PYX_BAD; #endif reduce_ex = __Pyx_PyObject_GetAttrStr(type_obj, __pyx_n_s_reduce_ex); if (unlikely(!reduce_ex)) goto __PYX_BAD; if (reduce_ex == object_reduce_ex) { #if CYTHON_USE_PYTYPE_LOOKUP object_reduce = _PyType_Lookup(&PyBaseObject_Type, __pyx_n_s_reduce); if (!object_reduce) goto __PYX_BAD; #else object_reduce = __Pyx_PyObject_GetAttrStr((PyObject*)&PyBaseObject_Type, __pyx_n_s_reduce); if (!object_reduce) goto __PYX_BAD; #endif reduce = __Pyx_PyObject_GetAttrStr(type_obj, __pyx_n_s_reduce); if (unlikely(!reduce)) goto __PYX_BAD; if (reduce == object_reduce || __Pyx_setup_reduce_is_named(reduce, __pyx_n_s_reduce_cython)) { reduce_cython = __Pyx_PyObject_GetAttrStrNoError(type_obj, __pyx_n_s_reduce_cython); if (likely(reduce_cython)) { ret = PyDict_SetItem(((PyTypeObject*)type_obj)->tp_dict, __pyx_n_s_reduce, reduce_cython); if (unlikely(ret < 0)) goto __PYX_BAD; ret = PyDict_DelItem(((PyTypeObject*)type_obj)->tp_dict, __pyx_n_s_reduce_cython); if (unlikely(ret < 0)) goto __PYX_BAD; } else if (reduce == object_reduce || PyErr_Occurred()) { goto __PYX_BAD; } setstate = __Pyx_PyObject_GetAttrStrNoError(type_obj, __pyx_n_s_setstate); if (!setstate) PyErr_Clear(); if (!setstate || __Pyx_setup_reduce_is_named(setstate, __pyx_n_s_setstate_cython)) { setstate_cython = __Pyx_PyObject_GetAttrStrNoError(type_obj, __pyx_n_s_setstate_cython); if (likely(setstate_cython)) { ret = PyDict_SetItem(((PyTypeObject*)type_obj)->tp_dict, __pyx_n_s_setstate, setstate_cython); if (unlikely(ret < 0)) goto __PYX_BAD; ret = PyDict_DelItem(((PyTypeObject*)type_obj)->tp_dict, __pyx_n_s_setstate_cython); if (unlikely(ret < 0)) goto __PYX_BAD; } else if (!setstate || PyErr_Occurred()) { goto __PYX_BAD; } } PyType_Modified((PyTypeObject*)type_obj); } } goto __PYX_GOOD; __PYX_BAD: if (!PyErr_Occurred()) { __Pyx_TypeName type_obj_name = __Pyx_PyType_GetName((PyTypeObject*)type_obj); PyErr_Format(PyExc_RuntimeError, "Unable to initialize pickling for " __Pyx_FMT_TYPENAME, type_obj_name); __Pyx_DECREF_TypeName(type_obj_name); } ret = -1; __PYX_GOOD: #if !CYTHON_USE_PYTYPE_LOOKUP Py_XDECREF(object_reduce); Py_XDECREF(object_reduce_ex); Py_XDECREF(object_getstate); Py_XDECREF(getstate); #endif Py_XDECREF(reduce); Py_XDECREF(reduce_ex); Py_XDECREF(reduce_cython); Py_XDECREF(setstate); Py_XDECREF(setstate_cython); return ret; } #endif /* TypeImport */ #ifndef __PYX_HAVE_RT_ImportType_3_0_9 #define __PYX_HAVE_RT_ImportType_3_0_9 static PyTypeObject *__Pyx_ImportType_3_0_9(PyObject *module, const char *module_name, const char *class_name, size_t size, size_t alignment, enum __Pyx_ImportType_CheckSize_3_0_9 check_size) { PyObject *result = 0; char warning[200]; Py_ssize_t basicsize; Py_ssize_t itemsize; #if CYTHON_COMPILING_IN_LIMITED_API PyObject *py_basicsize; PyObject *py_itemsize; #endif result = PyObject_GetAttrString(module, class_name); if (!result) goto bad; if (!PyType_Check(result)) { PyErr_Format(PyExc_TypeError, "%.200s.%.200s is not a type object", module_name, class_name); goto bad; } #if !CYTHON_COMPILING_IN_LIMITED_API basicsize = ((PyTypeObject *)result)->tp_basicsize; itemsize = ((PyTypeObject *)result)->tp_itemsize; #else py_basicsize = PyObject_GetAttrString(result, "__basicsize__"); if (!py_basicsize) goto bad; basicsize = PyLong_AsSsize_t(py_basicsize); Py_DECREF(py_basicsize); py_basicsize = 0; if (basicsize == (Py_ssize_t)-1 && PyErr_Occurred()) goto bad; py_itemsize = PyObject_GetAttrString(result, "__itemsize__"); if (!py_itemsize) goto bad; itemsize = PyLong_AsSsize_t(py_itemsize); Py_DECREF(py_itemsize); py_itemsize = 0; if (itemsize == (Py_ssize_t)-1 && PyErr_Occurred()) goto bad; #endif if (itemsize) { if (size % alignment) { alignment = size % alignment; } if (itemsize < (Py_ssize_t)alignment) itemsize = (Py_ssize_t)alignment; } if ((size_t)(basicsize + itemsize) < size) { PyErr_Format(PyExc_ValueError, "%.200s.%.200s size changed, may indicate binary incompatibility. " "Expected %zd from C header, got %zd from PyObject", module_name, class_name, size, basicsize+itemsize); goto bad; } if (check_size == __Pyx_ImportType_CheckSize_Error_3_0_9 && ((size_t)basicsize > size || (size_t)(basicsize + itemsize) < size)) { PyErr_Format(PyExc_ValueError, "%.200s.%.200s size changed, may indicate binary incompatibility. " "Expected %zd from C header, got %zd-%zd from PyObject", module_name, class_name, size, basicsize, basicsize+itemsize); goto bad; } else if (check_size == __Pyx_ImportType_CheckSize_Warn_3_0_9 && (size_t)basicsize > size) { PyOS_snprintf(warning, sizeof(warning), "%s.%s size changed, may indicate binary incompatibility. " "Expected %zd from C header, got %zd from PyObject", module_name, class_name, size, basicsize); if (PyErr_WarnEx(NULL, warning, 0) < 0) goto bad; } return (PyTypeObject *)result; bad: Py_XDECREF(result); return NULL; } #endif /* Import */ static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level) { PyObject *module = 0; PyObject *empty_dict = 0; PyObject *empty_list = 0; #if PY_MAJOR_VERSION < 3 PyObject *py_import; py_import = __Pyx_PyObject_GetAttrStr(__pyx_b, __pyx_n_s_import); if (unlikely(!py_import)) goto bad; if (!from_list) { empty_list = PyList_New(0); if (unlikely(!empty_list)) goto bad; from_list = empty_list; } #endif empty_dict = PyDict_New(); if (unlikely(!empty_dict)) goto bad; { #if PY_MAJOR_VERSION >= 3 if (level == -1) { if (strchr(__Pyx_MODULE_NAME, '.') != NULL) { module = PyImport_ImportModuleLevelObject( name, __pyx_d, empty_dict, from_list, 1); if (unlikely(!module)) { if (unlikely(!PyErr_ExceptionMatches(PyExc_ImportError))) goto bad; PyErr_Clear(); } } level = 0; } #endif if (!module) { #if PY_MAJOR_VERSION < 3 PyObject *py_level = PyInt_FromLong(level); if (unlikely(!py_level)) goto bad; module = PyObject_CallFunctionObjArgs(py_import, name, __pyx_d, empty_dict, from_list, py_level, (PyObject *)NULL); Py_DECREF(py_level); #else module = PyImport_ImportModuleLevelObject( name, __pyx_d, empty_dict, from_list, level); #endif } } bad: Py_XDECREF(empty_dict); Py_XDECREF(empty_list); #if PY_MAJOR_VERSION < 3 Py_XDECREF(py_import); #endif return module; } /* ImportFrom */ static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name) { PyObject* value = __Pyx_PyObject_GetAttrStr(module, name); if (unlikely(!value) && PyErr_ExceptionMatches(PyExc_AttributeError)) { const char* module_name_str = 0; PyObject* module_name = 0; PyObject* module_dot = 0; PyObject* full_name = 0; PyErr_Clear(); module_name_str = PyModule_GetName(module); if (unlikely(!module_name_str)) { goto modbad; } module_name = PyUnicode_FromString(module_name_str); if (unlikely(!module_name)) { goto modbad; } module_dot = PyUnicode_Concat(module_name, __pyx_kp_u__3); if (unlikely(!module_dot)) { goto modbad; } full_name = PyUnicode_Concat(module_dot, name); if (unlikely(!full_name)) { goto modbad; } #if PY_VERSION_HEX < 0x030700A1 || (CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030400) { PyObject *modules = PyImport_GetModuleDict(); if (unlikely(!modules)) goto modbad; value = PyObject_GetItem(modules, full_name); } #else value = PyImport_GetModule(full_name); #endif modbad: Py_XDECREF(full_name); Py_XDECREF(module_dot); Py_XDECREF(module_name); } if (unlikely(!value)) { PyErr_Format(PyExc_ImportError, #if PY_MAJOR_VERSION < 3 "cannot import name %.230s", PyString_AS_STRING(name)); #else "cannot import name %S", name); #endif } return value; } /* FetchSharedCythonModule */ static PyObject *__Pyx_FetchSharedCythonABIModule(void) { return __Pyx_PyImport_AddModuleRef((char*) __PYX_ABI_MODULE_NAME); } /* FetchCommonType */ static int __Pyx_VerifyCachedType(PyObject *cached_type, const char *name, Py_ssize_t basicsize, Py_ssize_t expected_basicsize) { if (!PyType_Check(cached_type)) { PyErr_Format(PyExc_TypeError, "Shared Cython type %.200s is not a type object", name); return -1; } if (basicsize != expected_basicsize) { PyErr_Format(PyExc_TypeError, "Shared Cython type %.200s has the wrong size, try recompiling", name); return -1; } return 0; } #if !CYTHON_USE_TYPE_SPECS static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type) { PyObject* abi_module; const char* object_name; PyTypeObject *cached_type = NULL; abi_module = __Pyx_FetchSharedCythonABIModule(); if (!abi_module) return NULL; object_name = strrchr(type->tp_name, '.'); object_name = object_name ? object_name+1 : type->tp_name; cached_type = (PyTypeObject*) PyObject_GetAttrString(abi_module, object_name); if (cached_type) { if (__Pyx_VerifyCachedType( (PyObject *)cached_type, object_name, cached_type->tp_basicsize, type->tp_basicsize) < 0) { goto bad; } goto done; } if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto bad; PyErr_Clear(); if (PyType_Ready(type) < 0) goto bad; if (PyObject_SetAttrString(abi_module, object_name, (PyObject *)type) < 0) goto bad; Py_INCREF(type); cached_type = type; done: Py_DECREF(abi_module); return cached_type; bad: Py_XDECREF(cached_type); cached_type = NULL; goto done; } #else static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) { PyObject *abi_module, *cached_type = NULL; const char* object_name = strrchr(spec->name, '.'); object_name = object_name ? object_name+1 : spec->name; abi_module = __Pyx_FetchSharedCythonABIModule(); if (!abi_module) return NULL; cached_type = PyObject_GetAttrString(abi_module, object_name); if (cached_type) { Py_ssize_t basicsize; #if CYTHON_COMPILING_IN_LIMITED_API PyObject *py_basicsize; py_basicsize = PyObject_GetAttrString(cached_type, "__basicsize__"); if (unlikely(!py_basicsize)) goto bad; basicsize = PyLong_AsSsize_t(py_basicsize); Py_DECREF(py_basicsize); py_basicsize = 0; if (unlikely(basicsize == (Py_ssize_t)-1) && PyErr_Occurred()) goto bad; #else basicsize = likely(PyType_Check(cached_type)) ? ((PyTypeObject*) cached_type)->tp_basicsize : -1; #endif if (__Pyx_VerifyCachedType( cached_type, object_name, basicsize, spec->basicsize) < 0) { goto bad; } goto done; } if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto bad; PyErr_Clear(); CYTHON_UNUSED_VAR(module); cached_type = __Pyx_PyType_FromModuleAndSpec(abi_module, spec, bases); if (unlikely(!cached_type)) goto bad; if (unlikely(__Pyx_fix_up_extension_type_from_spec(spec, (PyTypeObject *) cached_type) < 0)) goto bad; if (PyObject_SetAttrString(abi_module, object_name, cached_type) < 0) goto bad; done: Py_DECREF(abi_module); assert(cached_type == NULL || PyType_Check(cached_type)); return (PyTypeObject *) cached_type; bad: Py_XDECREF(cached_type); cached_type = NULL; goto done; } #endif /* PyVectorcallFastCallDict */ #if CYTHON_METH_FASTCALL static PyObject *__Pyx_PyVectorcall_FastCallDict_kw(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw) { PyObject *res = NULL; PyObject *kwnames; PyObject **newargs; PyObject **kwvalues; Py_ssize_t i, pos; size_t j; PyObject *key, *value; unsigned long keys_are_strings; Py_ssize_t nkw = PyDict_GET_SIZE(kw); newargs = (PyObject **)PyMem_Malloc((nargs + (size_t)nkw) * sizeof(args[0])); if (unlikely(newargs == NULL)) { PyErr_NoMemory(); return NULL; } for (j = 0; j < nargs; j++) newargs[j] = args[j]; kwnames = PyTuple_New(nkw); if (unlikely(kwnames == NULL)) { PyMem_Free(newargs); return NULL; } kwvalues = newargs + nargs; pos = i = 0; keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS; while (PyDict_Next(kw, &pos, &key, &value)) { keys_are_strings &= Py_TYPE(key)->tp_flags; Py_INCREF(key); Py_INCREF(value); PyTuple_SET_ITEM(kwnames, i, key); kwvalues[i] = value; i++; } if (unlikely(!keys_are_strings)) { PyErr_SetString(PyExc_TypeError, "keywords must be strings"); goto cleanup; } res = vc(func, newargs, nargs, kwnames); cleanup: Py_DECREF(kwnames); for (i = 0; i < nkw; i++) Py_DECREF(kwvalues[i]); PyMem_Free(newargs); return res; } static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw) { if (likely(kw == NULL) || PyDict_GET_SIZE(kw) == 0) { return vc(func, args, nargs, NULL); } return __Pyx_PyVectorcall_FastCallDict_kw(func, vc, args, nargs, kw); } #endif /* CythonFunctionShared */ #if CYTHON_COMPILING_IN_LIMITED_API static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) { if (__Pyx_CyFunction_Check(func)) { return PyCFunction_GetFunction(((__pyx_CyFunctionObject*)func)->func) == (PyCFunction) cfunc; } else if (PyCFunction_Check(func)) { return PyCFunction_GetFunction(func) == (PyCFunction) cfunc; } return 0; } #else static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) { return __Pyx_CyOrPyCFunction_Check(func) && __Pyx_CyOrPyCFunction_GET_FUNCTION(func) == (PyCFunction) cfunc; } #endif static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj) { #if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API __Pyx_Py_XDECREF_SET( __Pyx_CyFunction_GetClassObj(f), ((classobj) ? __Pyx_NewRef(classobj) : NULL)); #else __Pyx_Py_XDECREF_SET( ((PyCMethodObject *) (f))->mm_class, (PyTypeObject*)((classobj) ? __Pyx_NewRef(classobj) : NULL)); #endif } static PyObject * __Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, void *closure) { CYTHON_UNUSED_VAR(closure); if (unlikely(op->func_doc == NULL)) { #if CYTHON_COMPILING_IN_LIMITED_API op->func_doc = PyObject_GetAttrString(op->func, "__doc__"); if (unlikely(!op->func_doc)) return NULL; #else if (((PyCFunctionObject*)op)->m_ml->ml_doc) { #if PY_MAJOR_VERSION >= 3 op->func_doc = PyUnicode_FromString(((PyCFunctionObject*)op)->m_ml->ml_doc); #else op->func_doc = PyString_FromString(((PyCFunctionObject*)op)->m_ml->ml_doc); #endif if (unlikely(op->func_doc == NULL)) return NULL; } else { Py_INCREF(Py_None); return Py_None; } #endif } Py_INCREF(op->func_doc); return op->func_doc; } static int __Pyx_CyFunction_set_doc(__pyx_CyFunctionObject *op, PyObject *value, void *context) { CYTHON_UNUSED_VAR(context); if (value == NULL) { value = Py_None; } Py_INCREF(value); __Pyx_Py_XDECREF_SET(op->func_doc, value); return 0; } static PyObject * __Pyx_CyFunction_get_name(__pyx_CyFunctionObject *op, void *context) { CYTHON_UNUSED_VAR(context); if (unlikely(op->func_name == NULL)) { #if CYTHON_COMPILING_IN_LIMITED_API op->func_name = PyObject_GetAttrString(op->func, "__name__"); #elif PY_MAJOR_VERSION >= 3 op->func_name = PyUnicode_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name); #else op->func_name = PyString_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name); #endif if (unlikely(op->func_name == NULL)) return NULL; } Py_INCREF(op->func_name); return op->func_name; } static int __Pyx_CyFunction_set_name(__pyx_CyFunctionObject *op, PyObject *value, void *context) { CYTHON_UNUSED_VAR(context); #if PY_MAJOR_VERSION >= 3 if (unlikely(value == NULL || !PyUnicode_Check(value))) #else if (unlikely(value == NULL || !PyString_Check(value))) #endif { PyErr_SetString(PyExc_TypeError, "__name__ must be set to a string object"); return -1; } Py_INCREF(value); __Pyx_Py_XDECREF_SET(op->func_name, value); return 0; } static PyObject * __Pyx_CyFunction_get_qualname(__pyx_CyFunctionObject *op, void *context) { CYTHON_UNUSED_VAR(context); Py_INCREF(op->func_qualname); return op->func_qualname; } static int __Pyx_CyFunction_set_qualname(__pyx_CyFunctionObject *op, PyObject *value, void *context) { CYTHON_UNUSED_VAR(context); #if PY_MAJOR_VERSION >= 3 if (unlikely(value == NULL || !PyUnicode_Check(value))) #else if (unlikely(value == NULL || !PyString_Check(value))) #endif { PyErr_SetString(PyExc_TypeError, "__qualname__ must be set to a string object"); return -1; } Py_INCREF(value); __Pyx_Py_XDECREF_SET(op->func_qualname, value); return 0; } static PyObject * __Pyx_CyFunction_get_dict(__pyx_CyFunctionObject *op, void *context) { CYTHON_UNUSED_VAR(context); if (unlikely(op->func_dict == NULL)) { op->func_dict = PyDict_New(); if (unlikely(op->func_dict == NULL)) return NULL; } Py_INCREF(op->func_dict); return op->func_dict; } static int __Pyx_CyFunction_set_dict(__pyx_CyFunctionObject *op, PyObject *value, void *context) { CYTHON_UNUSED_VAR(context); if (unlikely(value == NULL)) { PyErr_SetString(PyExc_TypeError, "function's dictionary may not be deleted"); return -1; } if (unlikely(!PyDict_Check(value))) { PyErr_SetString(PyExc_TypeError, "setting function's dictionary to a non-dict"); return -1; } Py_INCREF(value); __Pyx_Py_XDECREF_SET(op->func_dict, value); return 0; } static PyObject * __Pyx_CyFunction_get_globals(__pyx_CyFunctionObject *op, void *context) { CYTHON_UNUSED_VAR(context); Py_INCREF(op->func_globals); return op->func_globals; } static PyObject * __Pyx_CyFunction_get_closure(__pyx_CyFunctionObject *op, void *context) { CYTHON_UNUSED_VAR(op); CYTHON_UNUSED_VAR(context); Py_INCREF(Py_None); return Py_None; } static PyObject * __Pyx_CyFunction_get_code(__pyx_CyFunctionObject *op, void *context) { PyObject* result = (op->func_code) ? op->func_code : Py_None; CYTHON_UNUSED_VAR(context); Py_INCREF(result); return result; } static int __Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) { int result = 0; PyObject *res = op->defaults_getter((PyObject *) op); if (unlikely(!res)) return -1; #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS op->defaults_tuple = PyTuple_GET_ITEM(res, 0); Py_INCREF(op->defaults_tuple); op->defaults_kwdict = PyTuple_GET_ITEM(res, 1); Py_INCREF(op->defaults_kwdict); #else op->defaults_tuple = __Pyx_PySequence_ITEM(res, 0); if (unlikely(!op->defaults_tuple)) result = -1; else { op->defaults_kwdict = __Pyx_PySequence_ITEM(res, 1); if (unlikely(!op->defaults_kwdict)) result = -1; } #endif Py_DECREF(res); return result; } static int __Pyx_CyFunction_set_defaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) { CYTHON_UNUSED_VAR(context); if (!value) { value = Py_None; } else if (unlikely(value != Py_None && !PyTuple_Check(value))) { PyErr_SetString(PyExc_TypeError, "__defaults__ must be set to a tuple object"); return -1; } PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__defaults__ will not " "currently affect the values used in function calls", 1); Py_INCREF(value); __Pyx_Py_XDECREF_SET(op->defaults_tuple, value); return 0; } static PyObject * __Pyx_CyFunction_get_defaults(__pyx_CyFunctionObject *op, void *context) { PyObject* result = op->defaults_tuple; CYTHON_UNUSED_VAR(context); if (unlikely(!result)) { if (op->defaults_getter) { if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; result = op->defaults_tuple; } else { result = Py_None; } } Py_INCREF(result); return result; } static int __Pyx_CyFunction_set_kwdefaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) { CYTHON_UNUSED_VAR(context); if (!value) { value = Py_None; } else if (unlikely(value != Py_None && !PyDict_Check(value))) { PyErr_SetString(PyExc_TypeError, "__kwdefaults__ must be set to a dict object"); return -1; } PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__kwdefaults__ will not " "currently affect the values used in function calls", 1); Py_INCREF(value); __Pyx_Py_XDECREF_SET(op->defaults_kwdict, value); return 0; } static PyObject * __Pyx_CyFunction_get_kwdefaults(__pyx_CyFunctionObject *op, void *context) { PyObject* result = op->defaults_kwdict; CYTHON_UNUSED_VAR(context); if (unlikely(!result)) { if (op->defaults_getter) { if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; result = op->defaults_kwdict; } else { result = Py_None; } } Py_INCREF(result); return result; } static int __Pyx_CyFunction_set_annotations(__pyx_CyFunctionObject *op, PyObject* value, void *context) { CYTHON_UNUSED_VAR(context); if (!value || value == Py_None) { value = NULL; } else if (unlikely(!PyDict_Check(value))) { PyErr_SetString(PyExc_TypeError, "__annotations__ must be set to a dict object"); return -1; } Py_XINCREF(value); __Pyx_Py_XDECREF_SET(op->func_annotations, value); return 0; } static PyObject * __Pyx_CyFunction_get_annotations(__pyx_CyFunctionObject *op, void *context) { PyObject* result = op->func_annotations; CYTHON_UNUSED_VAR(context); if (unlikely(!result)) { result = PyDict_New(); if (unlikely(!result)) return NULL; op->func_annotations = result; } Py_INCREF(result); return result; } static PyObject * __Pyx_CyFunction_get_is_coroutine(__pyx_CyFunctionObject *op, void *context) { int is_coroutine; CYTHON_UNUSED_VAR(context); if (op->func_is_coroutine) { return __Pyx_NewRef(op->func_is_coroutine); } is_coroutine = op->flags & __Pyx_CYFUNCTION_COROUTINE; #if PY_VERSION_HEX >= 0x03050000 if (is_coroutine) { PyObject *module, *fromlist, *marker = __pyx_n_s_is_coroutine; fromlist = PyList_New(1); if (unlikely(!fromlist)) return NULL; Py_INCREF(marker); #if CYTHON_ASSUME_SAFE_MACROS PyList_SET_ITEM(fromlist, 0, marker); #else if (unlikely(PyList_SetItem(fromlist, 0, marker) < 0)) { Py_DECREF(marker); Py_DECREF(fromlist); return NULL; } #endif module = PyImport_ImportModuleLevelObject(__pyx_n_s_asyncio_coroutines, NULL, NULL, fromlist, 0); Py_DECREF(fromlist); if (unlikely(!module)) goto ignore; op->func_is_coroutine = __Pyx_PyObject_GetAttrStr(module, marker); Py_DECREF(module); if (likely(op->func_is_coroutine)) { return __Pyx_NewRef(op->func_is_coroutine); } ignore: PyErr_Clear(); } #endif op->func_is_coroutine = __Pyx_PyBool_FromLong(is_coroutine); return __Pyx_NewRef(op->func_is_coroutine); } #if CYTHON_COMPILING_IN_LIMITED_API static PyObject * __Pyx_CyFunction_get_module(__pyx_CyFunctionObject *op, void *context) { CYTHON_UNUSED_VAR(context); return PyObject_GetAttrString(op->func, "__module__"); } static int __Pyx_CyFunction_set_module(__pyx_CyFunctionObject *op, PyObject* value, void *context) { CYTHON_UNUSED_VAR(context); return PyObject_SetAttrString(op->func, "__module__", value); } #endif static PyGetSetDef __pyx_CyFunction_getsets[] = { {(char *) "func_doc", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0}, {(char *) "__doc__", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0}, {(char *) "func_name", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0}, {(char *) "__name__", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0}, {(char *) "__qualname__", (getter)__Pyx_CyFunction_get_qualname, (setter)__Pyx_CyFunction_set_qualname, 0, 0}, {(char *) "func_dict", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0}, {(char *) "__dict__", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0}, {(char *) "func_globals", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0}, {(char *) "__globals__", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0}, {(char *) "func_closure", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0}, {(char *) "__closure__", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0}, {(char *) "func_code", (getter)__Pyx_CyFunction_get_code, 0, 0, 0}, {(char *) "__code__", (getter)__Pyx_CyFunction_get_code, 0, 0, 0}, {(char *) "func_defaults", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0}, {(char *) "__defaults__", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0}, {(char *) "__kwdefaults__", (getter)__Pyx_CyFunction_get_kwdefaults, (setter)__Pyx_CyFunction_set_kwdefaults, 0, 0}, {(char *) "__annotations__", (getter)__Pyx_CyFunction_get_annotations, (setter)__Pyx_CyFunction_set_annotations, 0, 0}, {(char *) "_is_coroutine", (getter)__Pyx_CyFunction_get_is_coroutine, 0, 0, 0}, #if CYTHON_COMPILING_IN_LIMITED_API {"__module__", (getter)__Pyx_CyFunction_get_module, (setter)__Pyx_CyFunction_set_module, 0, 0}, #endif {0, 0, 0, 0, 0} }; static PyMemberDef __pyx_CyFunction_members[] = { #if !CYTHON_COMPILING_IN_LIMITED_API {(char *) "__module__", T_OBJECT, offsetof(PyCFunctionObject, m_module), 0, 0}, #endif #if CYTHON_USE_TYPE_SPECS {(char *) "__dictoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_dict), READONLY, 0}, #if CYTHON_METH_FASTCALL #if CYTHON_BACKPORT_VECTORCALL {(char *) "__vectorcalloffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_vectorcall), READONLY, 0}, #else #if !CYTHON_COMPILING_IN_LIMITED_API {(char *) "__vectorcalloffset__", T_PYSSIZET, offsetof(PyCFunctionObject, vectorcall), READONLY, 0}, #endif #endif #endif #if PY_VERSION_HEX < 0x030500A0 || CYTHON_COMPILING_IN_LIMITED_API {(char *) "__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_weakreflist), READONLY, 0}, #else {(char *) "__weaklistoffset__", T_PYSSIZET, offsetof(PyCFunctionObject, m_weakreflist), READONLY, 0}, #endif #endif {0, 0, 0, 0, 0} }; static PyObject * __Pyx_CyFunction_reduce(__pyx_CyFunctionObject *m, PyObject *args) { CYTHON_UNUSED_VAR(args); #if PY_MAJOR_VERSION >= 3 Py_INCREF(m->func_qualname); return m->func_qualname; #else return PyString_FromString(((PyCFunctionObject*)m)->m_ml->ml_name); #endif } static PyMethodDef __pyx_CyFunction_methods[] = { {"__reduce__", (PyCFunction)__Pyx_CyFunction_reduce, METH_VARARGS, 0}, {0, 0, 0, 0} }; #if PY_VERSION_HEX < 0x030500A0 || CYTHON_COMPILING_IN_LIMITED_API #define __Pyx_CyFunction_weakreflist(cyfunc) ((cyfunc)->func_weakreflist) #else #define __Pyx_CyFunction_weakreflist(cyfunc) (((PyCFunctionObject*)cyfunc)->m_weakreflist) #endif static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef *ml, int flags, PyObject* qualname, PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) { #if !CYTHON_COMPILING_IN_LIMITED_API PyCFunctionObject *cf = (PyCFunctionObject*) op; #endif if (unlikely(op == NULL)) return NULL; #if CYTHON_COMPILING_IN_LIMITED_API op->func = PyCFunction_NewEx(ml, (PyObject*)op, module); if (unlikely(!op->func)) return NULL; #endif op->flags = flags; __Pyx_CyFunction_weakreflist(op) = NULL; #if !CYTHON_COMPILING_IN_LIMITED_API cf->m_ml = ml; cf->m_self = (PyObject *) op; #endif Py_XINCREF(closure); op->func_closure = closure; #if !CYTHON_COMPILING_IN_LIMITED_API Py_XINCREF(module); cf->m_module = module; #endif op->func_dict = NULL; op->func_name = NULL; Py_INCREF(qualname); op->func_qualname = qualname; op->func_doc = NULL; #if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API op->func_classobj = NULL; #else ((PyCMethodObject*)op)->mm_class = NULL; #endif op->func_globals = globals; Py_INCREF(op->func_globals); Py_XINCREF(code); op->func_code = code; op->defaults_pyobjects = 0; op->defaults_size = 0; op->defaults = NULL; op->defaults_tuple = NULL; op->defaults_kwdict = NULL; op->defaults_getter = NULL; op->func_annotations = NULL; op->func_is_coroutine = NULL; #if CYTHON_METH_FASTCALL switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS | METH_METHOD)) { case METH_NOARGS: __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_NOARGS; break; case METH_O: __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_O; break; case METH_METHOD | METH_FASTCALL | METH_KEYWORDS: __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD; break; case METH_FASTCALL | METH_KEYWORDS: __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS; break; case METH_VARARGS | METH_KEYWORDS: __Pyx_CyFunction_func_vectorcall(op) = NULL; break; default: PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction"); Py_DECREF(op); return NULL; } #endif return (PyObject *) op; } static int __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m) { Py_CLEAR(m->func_closure); #if CYTHON_COMPILING_IN_LIMITED_API Py_CLEAR(m->func); #else Py_CLEAR(((PyCFunctionObject*)m)->m_module); #endif Py_CLEAR(m->func_dict); Py_CLEAR(m->func_name); Py_CLEAR(m->func_qualname); Py_CLEAR(m->func_doc); Py_CLEAR(m->func_globals); Py_CLEAR(m->func_code); #if !CYTHON_COMPILING_IN_LIMITED_API #if PY_VERSION_HEX < 0x030900B1 Py_CLEAR(__Pyx_CyFunction_GetClassObj(m)); #else { PyObject *cls = (PyObject*) ((PyCMethodObject *) (m))->mm_class; ((PyCMethodObject *) (m))->mm_class = NULL; Py_XDECREF(cls); } #endif #endif Py_CLEAR(m->defaults_tuple); Py_CLEAR(m->defaults_kwdict); Py_CLEAR(m->func_annotations); Py_CLEAR(m->func_is_coroutine); if (m->defaults) { PyObject **pydefaults = __Pyx_CyFunction_Defaults(PyObject *, m); int i; for (i = 0; i < m->defaults_pyobjects; i++) Py_XDECREF(pydefaults[i]); PyObject_Free(m->defaults); m->defaults = NULL; } return 0; } static void __Pyx__CyFunction_dealloc(__pyx_CyFunctionObject *m) { if (__Pyx_CyFunction_weakreflist(m) != NULL) PyObject_ClearWeakRefs((PyObject *) m); __Pyx_CyFunction_clear(m); __Pyx_PyHeapTypeObject_GC_Del(m); } static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m) { PyObject_GC_UnTrack(m); __Pyx__CyFunction_dealloc(m); } static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, void *arg) { Py_VISIT(m->func_closure); #if CYTHON_COMPILING_IN_LIMITED_API Py_VISIT(m->func); #else Py_VISIT(((PyCFunctionObject*)m)->m_module); #endif Py_VISIT(m->func_dict); Py_VISIT(m->func_name); Py_VISIT(m->func_qualname); Py_VISIT(m->func_doc); Py_VISIT(m->func_globals); Py_VISIT(m->func_code); #if !CYTHON_COMPILING_IN_LIMITED_API Py_VISIT(__Pyx_CyFunction_GetClassObj(m)); #endif Py_VISIT(m->defaults_tuple); Py_VISIT(m->defaults_kwdict); Py_VISIT(m->func_is_coroutine); if (m->defaults) { PyObject **pydefaults = __Pyx_CyFunction_Defaults(PyObject *, m); int i; for (i = 0; i < m->defaults_pyobjects; i++) Py_VISIT(pydefaults[i]); } return 0; } static PyObject* __Pyx_CyFunction_repr(__pyx_CyFunctionObject *op) { #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromFormat("", op->func_qualname, (void *)op); #else return PyString_FromFormat("", PyString_AsString(op->func_qualname), (void *)op); #endif } static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, PyObject *arg, PyObject *kw) { #if CYTHON_COMPILING_IN_LIMITED_API PyObject *f = ((__pyx_CyFunctionObject*)func)->func; PyObject *py_name = NULL; PyCFunction meth; int flags; meth = PyCFunction_GetFunction(f); if (unlikely(!meth)) return NULL; flags = PyCFunction_GetFlags(f); if (unlikely(flags < 0)) return NULL; #else PyCFunctionObject* f = (PyCFunctionObject*)func; PyCFunction meth = f->m_ml->ml_meth; int flags = f->m_ml->ml_flags; #endif Py_ssize_t size; switch (flags & (METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O)) { case METH_VARARGS: if (likely(kw == NULL || PyDict_Size(kw) == 0)) return (*meth)(self, arg); break; case METH_VARARGS | METH_KEYWORDS: return (*(PyCFunctionWithKeywords)(void*)meth)(self, arg, kw); case METH_NOARGS: if (likely(kw == NULL || PyDict_Size(kw) == 0)) { #if CYTHON_ASSUME_SAFE_MACROS size = PyTuple_GET_SIZE(arg); #else size = PyTuple_Size(arg); if (unlikely(size < 0)) return NULL; #endif if (likely(size == 0)) return (*meth)(self, NULL); #if CYTHON_COMPILING_IN_LIMITED_API py_name = __Pyx_CyFunction_get_name((__pyx_CyFunctionObject*)func, NULL); if (!py_name) return NULL; PyErr_Format(PyExc_TypeError, "%.200S() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", py_name, size); Py_DECREF(py_name); #else PyErr_Format(PyExc_TypeError, "%.200s() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", f->m_ml->ml_name, size); #endif return NULL; } break; case METH_O: if (likely(kw == NULL || PyDict_Size(kw) == 0)) { #if CYTHON_ASSUME_SAFE_MACROS size = PyTuple_GET_SIZE(arg); #else size = PyTuple_Size(arg); if (unlikely(size < 0)) return NULL; #endif if (likely(size == 1)) { PyObject *result, *arg0; #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS arg0 = PyTuple_GET_ITEM(arg, 0); #else arg0 = __Pyx_PySequence_ITEM(arg, 0); if (unlikely(!arg0)) return NULL; #endif result = (*meth)(self, arg0); #if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS) Py_DECREF(arg0); #endif return result; } #if CYTHON_COMPILING_IN_LIMITED_API py_name = __Pyx_CyFunction_get_name((__pyx_CyFunctionObject*)func, NULL); if (!py_name) return NULL; PyErr_Format(PyExc_TypeError, "%.200S() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)", py_name, size); Py_DECREF(py_name); #else PyErr_Format(PyExc_TypeError, "%.200s() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)", f->m_ml->ml_name, size); #endif return NULL; } break; default: PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction"); return NULL; } #if CYTHON_COMPILING_IN_LIMITED_API py_name = __Pyx_CyFunction_get_name((__pyx_CyFunctionObject*)func, NULL); if (!py_name) return NULL; PyErr_Format(PyExc_TypeError, "%.200S() takes no keyword arguments", py_name); Py_DECREF(py_name); #else PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", f->m_ml->ml_name); #endif return NULL; } static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) { PyObject *self, *result; #if CYTHON_COMPILING_IN_LIMITED_API self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)func)->func); if (unlikely(!self) && PyErr_Occurred()) return NULL; #else self = ((PyCFunctionObject*)func)->m_self; #endif result = __Pyx_CyFunction_CallMethod(func, self, arg, kw); return result; } static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, PyObject *kw) { PyObject *result; __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func; #if CYTHON_METH_FASTCALL __pyx_vectorcallfunc vc = __Pyx_CyFunction_func_vectorcall(cyfunc); if (vc) { #if CYTHON_ASSUME_SAFE_MACROS return __Pyx_PyVectorcall_FastCallDict(func, vc, &PyTuple_GET_ITEM(args, 0), (size_t)PyTuple_GET_SIZE(args), kw); #else (void) &__Pyx_PyVectorcall_FastCallDict; return PyVectorcall_Call(func, args, kw); #endif } #endif if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) { Py_ssize_t argc; PyObject *new_args; PyObject *self; #if CYTHON_ASSUME_SAFE_MACROS argc = PyTuple_GET_SIZE(args); #else argc = PyTuple_Size(args); if (unlikely(!argc) < 0) return NULL; #endif new_args = PyTuple_GetSlice(args, 1, argc); if (unlikely(!new_args)) return NULL; self = PyTuple_GetItem(args, 0); if (unlikely(!self)) { Py_DECREF(new_args); #if PY_MAJOR_VERSION > 2 PyErr_Format(PyExc_TypeError, "unbound method %.200S() needs an argument", cyfunc->func_qualname); #else PyErr_SetString(PyExc_TypeError, "unbound method needs an argument"); #endif return NULL; } result = __Pyx_CyFunction_CallMethod(func, self, new_args, kw); Py_DECREF(new_args); } else { result = __Pyx_CyFunction_Call(func, args, kw); } return result; } #if CYTHON_METH_FASTCALL static CYTHON_INLINE int __Pyx_CyFunction_Vectorcall_CheckArgs(__pyx_CyFunctionObject *cyfunc, Py_ssize_t nargs, PyObject *kwnames) { int ret = 0; if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) { if (unlikely(nargs < 1)) { PyErr_Format(PyExc_TypeError, "%.200s() needs an argument", ((PyCFunctionObject*)cyfunc)->m_ml->ml_name); return -1; } ret = 1; } if (unlikely(kwnames) && unlikely(PyTuple_GET_SIZE(kwnames))) { PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", ((PyCFunctionObject*)cyfunc)->m_ml->ml_name); return -1; } return ret; } static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; PyMethodDef* def = ((PyCFunctionObject*)cyfunc)->m_ml; #if CYTHON_BACKPORT_VECTORCALL Py_ssize_t nargs = (Py_ssize_t)nargsf; #else Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); #endif PyObject *self; switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, kwnames)) { case 1: self = args[0]; args += 1; nargs -= 1; break; case 0: self = ((PyCFunctionObject*)cyfunc)->m_self; break; default: return NULL; } if (unlikely(nargs != 0)) { PyErr_Format(PyExc_TypeError, "%.200s() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", def->ml_name, nargs); return NULL; } return def->ml_meth(self, NULL); } static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; PyMethodDef* def = ((PyCFunctionObject*)cyfunc)->m_ml; #if CYTHON_BACKPORT_VECTORCALL Py_ssize_t nargs = (Py_ssize_t)nargsf; #else Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); #endif PyObject *self; switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, kwnames)) { case 1: self = args[0]; args += 1; nargs -= 1; break; case 0: self = ((PyCFunctionObject*)cyfunc)->m_self; break; default: return NULL; } if (unlikely(nargs != 1)) { PyErr_Format(PyExc_TypeError, "%.200s() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)", def->ml_name, nargs); return NULL; } return def->ml_meth(self, args[0]); } static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; PyMethodDef* def = ((PyCFunctionObject*)cyfunc)->m_ml; #if CYTHON_BACKPORT_VECTORCALL Py_ssize_t nargs = (Py_ssize_t)nargsf; #else Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); #endif PyObject *self; switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, NULL)) { case 1: self = args[0]; args += 1; nargs -= 1; break; case 0: self = ((PyCFunctionObject*)cyfunc)->m_self; break; default: return NULL; } return ((__Pyx_PyCFunctionFastWithKeywords)(void(*)(void))def->ml_meth)(self, args, nargs, kwnames); } static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; PyMethodDef* def = ((PyCFunctionObject*)cyfunc)->m_ml; PyTypeObject *cls = (PyTypeObject *) __Pyx_CyFunction_GetClassObj(cyfunc); #if CYTHON_BACKPORT_VECTORCALL Py_ssize_t nargs = (Py_ssize_t)nargsf; #else Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); #endif PyObject *self; switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, NULL)) { case 1: self = args[0]; args += 1; nargs -= 1; break; case 0: self = ((PyCFunctionObject*)cyfunc)->m_self; break; default: return NULL; } return ((__Pyx_PyCMethod)(void(*)(void))def->ml_meth)(self, cls, args, (size_t)nargs, kwnames); } #endif #if CYTHON_USE_TYPE_SPECS static PyType_Slot __pyx_CyFunctionType_slots[] = { {Py_tp_dealloc, (void *)__Pyx_CyFunction_dealloc}, {Py_tp_repr, (void *)__Pyx_CyFunction_repr}, {Py_tp_call, (void *)__Pyx_CyFunction_CallAsMethod}, {Py_tp_traverse, (void *)__Pyx_CyFunction_traverse}, {Py_tp_clear, (void *)__Pyx_CyFunction_clear}, {Py_tp_methods, (void *)__pyx_CyFunction_methods}, {Py_tp_members, (void *)__pyx_CyFunction_members}, {Py_tp_getset, (void *)__pyx_CyFunction_getsets}, {Py_tp_descr_get, (void *)__Pyx_PyMethod_New}, {0, 0}, }; static PyType_Spec __pyx_CyFunctionType_spec = { __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", sizeof(__pyx_CyFunctionObject), 0, #ifdef Py_TPFLAGS_METHOD_DESCRIPTOR Py_TPFLAGS_METHOD_DESCRIPTOR | #endif #if (defined(_Py_TPFLAGS_HAVE_VECTORCALL) && CYTHON_METH_FASTCALL) _Py_TPFLAGS_HAVE_VECTORCALL | #endif Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, __pyx_CyFunctionType_slots }; #else static PyTypeObject __pyx_CyFunctionType_type = { PyVarObject_HEAD_INIT(0, 0) __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", sizeof(__pyx_CyFunctionObject), 0, (destructor) __Pyx_CyFunction_dealloc, #if !CYTHON_METH_FASTCALL 0, #elif CYTHON_BACKPORT_VECTORCALL (printfunc)offsetof(__pyx_CyFunctionObject, func_vectorcall), #else offsetof(PyCFunctionObject, vectorcall), #endif 0, 0, #if PY_MAJOR_VERSION < 3 0, #else 0, #endif (reprfunc) __Pyx_CyFunction_repr, 0, 0, 0, 0, __Pyx_CyFunction_CallAsMethod, 0, 0, 0, 0, #ifdef Py_TPFLAGS_METHOD_DESCRIPTOR Py_TPFLAGS_METHOD_DESCRIPTOR | #endif #if defined(_Py_TPFLAGS_HAVE_VECTORCALL) && CYTHON_METH_FASTCALL _Py_TPFLAGS_HAVE_VECTORCALL | #endif Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, 0, (traverseproc) __Pyx_CyFunction_traverse, (inquiry) __Pyx_CyFunction_clear, 0, #if PY_VERSION_HEX < 0x030500A0 offsetof(__pyx_CyFunctionObject, func_weakreflist), #else offsetof(PyCFunctionObject, m_weakreflist), #endif 0, 0, __pyx_CyFunction_methods, __pyx_CyFunction_members, __pyx_CyFunction_getsets, 0, 0, __Pyx_PyMethod_New, 0, offsetof(__pyx_CyFunctionObject, func_dict), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, #if PY_VERSION_HEX >= 0x030400a1 0, #endif #if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800) 0, #endif #if __PYX_NEED_TP_PRINT_SLOT 0, #endif #if PY_VERSION_HEX >= 0x030C0000 0, #endif #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000 0, #endif }; #endif static int __pyx_CyFunction_init(PyObject *module) { #if CYTHON_USE_TYPE_SPECS __pyx_CyFunctionType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_CyFunctionType_spec, NULL); #else CYTHON_UNUSED_VAR(module); __pyx_CyFunctionType = __Pyx_FetchCommonType(&__pyx_CyFunctionType_type); #endif if (unlikely(__pyx_CyFunctionType == NULL)) { return -1; } return 0; } static CYTHON_INLINE void *__Pyx_CyFunction_InitDefaults(PyObject *func, size_t size, int pyobjects) { __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; m->defaults = PyObject_Malloc(size); if (unlikely(!m->defaults)) return PyErr_NoMemory(); memset(m->defaults, 0, size); m->defaults_pyobjects = pyobjects; m->defaults_size = size; return m->defaults; } static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *func, PyObject *tuple) { __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; m->defaults_tuple = tuple; Py_INCREF(tuple); } static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *func, PyObject *dict) { __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; m->defaults_kwdict = dict; Py_INCREF(dict); } static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *func, PyObject *dict) { __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; m->func_annotations = dict; Py_INCREF(dict); } /* CythonFunction */ static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, int flags, PyObject* qualname, PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) { PyObject *op = __Pyx_CyFunction_Init( PyObject_GC_New(__pyx_CyFunctionObject, __pyx_CyFunctionType), ml, flags, qualname, closure, module, globals, code ); if (likely(op)) { PyObject_GC_Track(op); } return op; } /* CLineInTraceback */ #ifndef CYTHON_CLINE_IN_TRACEBACK static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line) { PyObject *use_cline; PyObject *ptype, *pvalue, *ptraceback; #if CYTHON_COMPILING_IN_CPYTHON PyObject **cython_runtime_dict; #endif CYTHON_MAYBE_UNUSED_VAR(tstate); if (unlikely(!__pyx_cython_runtime)) { return c_line; } __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback); #if CYTHON_COMPILING_IN_CPYTHON cython_runtime_dict = _PyObject_GetDictPtr(__pyx_cython_runtime); if (likely(cython_runtime_dict)) { __PYX_PY_DICT_LOOKUP_IF_MODIFIED( use_cline, *cython_runtime_dict, __Pyx_PyDict_GetItemStr(*cython_runtime_dict, __pyx_n_s_cline_in_traceback)) } else #endif { PyObject *use_cline_obj = __Pyx_PyObject_GetAttrStrNoError(__pyx_cython_runtime, __pyx_n_s_cline_in_traceback); if (use_cline_obj) { use_cline = PyObject_Not(use_cline_obj) ? Py_False : Py_True; Py_DECREF(use_cline_obj); } else { PyErr_Clear(); use_cline = NULL; } } if (!use_cline) { c_line = 0; (void) PyObject_SetAttr(__pyx_cython_runtime, __pyx_n_s_cline_in_traceback, Py_False); } else if (use_cline == Py_False || (use_cline != Py_True && PyObject_Not(use_cline) != 0)) { c_line = 0; } __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback); return c_line; } #endif /* CodeObjectCache */ #if !CYTHON_COMPILING_IN_LIMITED_API static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line) { int start = 0, mid = 0, end = count - 1; if (end >= 0 && code_line > entries[end].code_line) { return count; } while (start < end) { mid = start + (end - start) / 2; if (code_line < entries[mid].code_line) { end = mid; } else if (code_line > entries[mid].code_line) { start = mid + 1; } else { return mid; } } if (code_line <= entries[mid].code_line) { return mid; } else { return mid + 1; } } static PyCodeObject *__pyx_find_code_object(int code_line) { PyCodeObject* code_object; int pos; if (unlikely(!code_line) || unlikely(!__pyx_code_cache.entries)) { return NULL; } pos = __pyx_bisect_code_objects(__pyx_code_cache.entries, __pyx_code_cache.count, code_line); if (unlikely(pos >= __pyx_code_cache.count) || unlikely(__pyx_code_cache.entries[pos].code_line != code_line)) { return NULL; } code_object = __pyx_code_cache.entries[pos].code_object; Py_INCREF(code_object); return code_object; } static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object) { int pos, i; __Pyx_CodeObjectCacheEntry* entries = __pyx_code_cache.entries; if (unlikely(!code_line)) { return; } if (unlikely(!entries)) { entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Malloc(64*sizeof(__Pyx_CodeObjectCacheEntry)); if (likely(entries)) { __pyx_code_cache.entries = entries; __pyx_code_cache.max_count = 64; __pyx_code_cache.count = 1; entries[0].code_line = code_line; entries[0].code_object = code_object; Py_INCREF(code_object); } return; } pos = __pyx_bisect_code_objects(__pyx_code_cache.entries, __pyx_code_cache.count, code_line); if ((pos < __pyx_code_cache.count) && unlikely(__pyx_code_cache.entries[pos].code_line == code_line)) { PyCodeObject* tmp = entries[pos].code_object; entries[pos].code_object = code_object; Py_DECREF(tmp); return; } if (__pyx_code_cache.count == __pyx_code_cache.max_count) { int new_max = __pyx_code_cache.max_count + 64; entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Realloc( __pyx_code_cache.entries, ((size_t)new_max) * sizeof(__Pyx_CodeObjectCacheEntry)); if (unlikely(!entries)) { return; } __pyx_code_cache.entries = entries; __pyx_code_cache.max_count = new_max; } for (i=__pyx_code_cache.count; i>pos; i--) { entries[i] = entries[i-1]; } entries[pos].code_line = code_line; entries[pos].code_object = code_object; __pyx_code_cache.count++; Py_INCREF(code_object); } #endif /* AddTraceback */ #include "compile.h" #include "frameobject.h" #include "traceback.h" #if PY_VERSION_HEX >= 0x030b00a6 && !CYTHON_COMPILING_IN_LIMITED_API #ifndef Py_BUILD_CORE #define Py_BUILD_CORE 1 #endif #include "internal/pycore_frame.h" #endif #if CYTHON_COMPILING_IN_LIMITED_API static PyObject *__Pyx_PyCode_Replace_For_AddTraceback(PyObject *code, PyObject *scratch_dict, PyObject *firstlineno, PyObject *name) { PyObject *replace = NULL; if (unlikely(PyDict_SetItemString(scratch_dict, "co_firstlineno", firstlineno))) return NULL; if (unlikely(PyDict_SetItemString(scratch_dict, "co_name", name))) return NULL; replace = PyObject_GetAttrString(code, "replace"); if (likely(replace)) { PyObject *result; result = PyObject_Call(replace, __pyx_empty_tuple, scratch_dict); Py_DECREF(replace); return result; } PyErr_Clear(); #if __PYX_LIMITED_VERSION_HEX < 0x030780000 { PyObject *compiled = NULL, *result = NULL; if (unlikely(PyDict_SetItemString(scratch_dict, "code", code))) return NULL; if (unlikely(PyDict_SetItemString(scratch_dict, "type", (PyObject*)(&PyType_Type)))) return NULL; compiled = Py_CompileString( "out = type(code)(\n" " code.co_argcount, code.co_kwonlyargcount, code.co_nlocals, code.co_stacksize,\n" " code.co_flags, code.co_code, code.co_consts, code.co_names,\n" " code.co_varnames, code.co_filename, co_name, co_firstlineno,\n" " code.co_lnotab)\n", "", Py_file_input); if (!compiled) return NULL; result = PyEval_EvalCode(compiled, scratch_dict, scratch_dict); Py_DECREF(compiled); if (!result) PyErr_Print(); Py_DECREF(result); result = PyDict_GetItemString(scratch_dict, "out"); if (result) Py_INCREF(result); return result; } #else return NULL; #endif } static void __Pyx_AddTraceback(const char *funcname, int c_line, int py_line, const char *filename) { PyObject *code_object = NULL, *py_py_line = NULL, *py_funcname = NULL, *dict = NULL; PyObject *replace = NULL, *getframe = NULL, *frame = NULL; PyObject *exc_type, *exc_value, *exc_traceback; int success = 0; if (c_line) { (void) __pyx_cfilenm; (void) __Pyx_CLineForTraceback(__Pyx_PyThreadState_Current, c_line); } PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); code_object = Py_CompileString("_getframe()", filename, Py_eval_input); if (unlikely(!code_object)) goto bad; py_py_line = PyLong_FromLong(py_line); if (unlikely(!py_py_line)) goto bad; py_funcname = PyUnicode_FromString(funcname); if (unlikely(!py_funcname)) goto bad; dict = PyDict_New(); if (unlikely(!dict)) goto bad; { PyObject *old_code_object = code_object; code_object = __Pyx_PyCode_Replace_For_AddTraceback(code_object, dict, py_py_line, py_funcname); Py_DECREF(old_code_object); } if (unlikely(!code_object)) goto bad; getframe = PySys_GetObject("_getframe"); if (unlikely(!getframe)) goto bad; if (unlikely(PyDict_SetItemString(dict, "_getframe", getframe))) goto bad; frame = PyEval_EvalCode(code_object, dict, dict); if (unlikely(!frame) || frame == Py_None) goto bad; success = 1; bad: PyErr_Restore(exc_type, exc_value, exc_traceback); Py_XDECREF(code_object); Py_XDECREF(py_py_line); Py_XDECREF(py_funcname); Py_XDECREF(dict); Py_XDECREF(replace); if (success) { PyTraceBack_Here( (struct _frame*)frame); } Py_XDECREF(frame); } #else static PyCodeObject* __Pyx_CreateCodeObjectForTraceback( const char *funcname, int c_line, int py_line, const char *filename) { PyCodeObject *py_code = NULL; PyObject *py_funcname = NULL; #if PY_MAJOR_VERSION < 3 PyObject *py_srcfile = NULL; py_srcfile = PyString_FromString(filename); if (!py_srcfile) goto bad; #endif if (c_line) { #if PY_MAJOR_VERSION < 3 py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); if (!py_funcname) goto bad; #else py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); if (!py_funcname) goto bad; funcname = PyUnicode_AsUTF8(py_funcname); if (!funcname) goto bad; #endif } else { #if PY_MAJOR_VERSION < 3 py_funcname = PyString_FromString(funcname); if (!py_funcname) goto bad; #endif } #if PY_MAJOR_VERSION < 3 py_code = __Pyx_PyCode_New( 0, 0, 0, 0, 0, 0, __pyx_empty_bytes, /*PyObject *code,*/ __pyx_empty_tuple, /*PyObject *consts,*/ __pyx_empty_tuple, /*PyObject *names,*/ __pyx_empty_tuple, /*PyObject *varnames,*/ __pyx_empty_tuple, /*PyObject *freevars,*/ __pyx_empty_tuple, /*PyObject *cellvars,*/ py_srcfile, /*PyObject *filename,*/ py_funcname, /*PyObject *name,*/ py_line, __pyx_empty_bytes /*PyObject *lnotab*/ ); Py_DECREF(py_srcfile); #else py_code = PyCode_NewEmpty(filename, funcname, py_line); #endif Py_XDECREF(py_funcname); return py_code; bad: Py_XDECREF(py_funcname); #if PY_MAJOR_VERSION < 3 Py_XDECREF(py_srcfile); #endif return NULL; } static void __Pyx_AddTraceback(const char *funcname, int c_line, int py_line, const char *filename) { PyCodeObject *py_code = 0; PyFrameObject *py_frame = 0; PyThreadState *tstate = __Pyx_PyThreadState_Current; PyObject *ptype, *pvalue, *ptraceback; if (c_line) { c_line = __Pyx_CLineForTraceback(tstate, c_line); } py_code = __pyx_find_code_object(c_line ? -c_line : py_line); if (!py_code) { __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback); py_code = __Pyx_CreateCodeObjectForTraceback( funcname, c_line, py_line, filename); if (!py_code) { /* If the code object creation fails, then we should clear the fetched exception references and propagate the new exception */ Py_XDECREF(ptype); Py_XDECREF(pvalue); Py_XDECREF(ptraceback); goto bad; } __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback); __pyx_insert_code_object(c_line ? -c_line : py_line, py_code); } py_frame = PyFrame_New( tstate, /*PyThreadState *tstate,*/ py_code, /*PyCodeObject *code,*/ __pyx_d, /*PyObject *globals,*/ 0 /*PyObject *locals*/ ); if (!py_frame) goto bad; __Pyx_PyFrame_SetLineNumber(py_frame, py_line); PyTraceBack_Here(py_frame); bad: Py_XDECREF(py_code); Py_XDECREF(py_frame); } #endif /* CIntFromPyVerify */ #define __PYX_VERIFY_RETURN_INT(target_type, func_type, func_value)\ __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 0) #define __PYX_VERIFY_RETURN_INT_EXC(target_type, func_type, func_value)\ __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 1) #define __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, exc)\ {\ func_type value = func_value;\ if (sizeof(target_type) < sizeof(func_type)) {\ if (unlikely(value != (func_type) (target_type) value)) {\ func_type zero = 0;\ if (exc && unlikely(value == (func_type)-1 && PyErr_Occurred()))\ return (target_type) -1;\ if (is_unsigned && unlikely(value < zero))\ goto raise_neg_overflow;\ else\ goto raise_overflow;\ }\ }\ return (target_type) value;\ } /* CIntToPy */ static CYTHON_INLINE PyObject* __Pyx_PyInt_From_int(int value) { #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #endif const int neg_one = (int) -1, const_zero = (int) 0; #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic pop #endif const int is_unsigned = neg_one > const_zero; if (is_unsigned) { if (sizeof(int) < sizeof(long)) { return PyInt_FromLong((long) value); } else if (sizeof(int) <= sizeof(unsigned long)) { return PyLong_FromUnsignedLong((unsigned long) value); #ifdef HAVE_LONG_LONG } else if (sizeof(int) <= sizeof(unsigned PY_LONG_LONG)) { return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value); #endif } } else { if (sizeof(int) <= sizeof(long)) { return PyInt_FromLong((long) value); #ifdef HAVE_LONG_LONG } else if (sizeof(int) <= sizeof(PY_LONG_LONG)) { return PyLong_FromLongLong((PY_LONG_LONG) value); #endif } } { int one = 1; int little = (int)*(unsigned char *)&one; unsigned char *bytes = (unsigned char *)&value; #if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030d0000 return _PyLong_FromByteArray(bytes, sizeof(int), little, !is_unsigned); #else PyObject *from_bytes, *result = NULL; PyObject *py_bytes = NULL, *arg_tuple = NULL, *kwds = NULL, *order_str = NULL; from_bytes = PyObject_GetAttrString((PyObject*)&PyLong_Type, "from_bytes"); if (!from_bytes) return NULL; py_bytes = PyBytes_FromStringAndSize((char*)bytes, sizeof(int)); if (!py_bytes) goto limited_bad; order_str = PyUnicode_FromString(little ? "little" : "big"); if (!order_str) goto limited_bad; arg_tuple = PyTuple_Pack(2, py_bytes, order_str); if (!arg_tuple) goto limited_bad; if (!is_unsigned) { kwds = PyDict_New(); if (!kwds) goto limited_bad; if (PyDict_SetItemString(kwds, "signed", __Pyx_NewRef(Py_True))) goto limited_bad; } result = PyObject_Call(from_bytes, arg_tuple, kwds); limited_bad: Py_XDECREF(kwds); Py_XDECREF(arg_tuple); Py_XDECREF(order_str); Py_XDECREF(py_bytes); Py_XDECREF(from_bytes); return result; #endif } } /* CIntFromPy */ static CYTHON_INLINE uint32_t __Pyx_PyInt_As_uint32_t(PyObject *x) { #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #endif const uint32_t neg_one = (uint32_t) -1, const_zero = (uint32_t) 0; #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic pop #endif const int is_unsigned = neg_one > const_zero; #if PY_MAJOR_VERSION < 3 if (likely(PyInt_Check(x))) { if ((sizeof(uint32_t) < sizeof(long))) { __PYX_VERIFY_RETURN_INT(uint32_t, long, PyInt_AS_LONG(x)) } else { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { goto raise_neg_overflow; } return (uint32_t) val; } } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { #if CYTHON_USE_PYLONG_INTERNALS if (unlikely(__Pyx_PyLong_IsNeg(x))) { goto raise_neg_overflow; } else if (__Pyx_PyLong_IsCompact(x)) { __PYX_VERIFY_RETURN_INT(uint32_t, __Pyx_compact_upylong, __Pyx_PyLong_CompactValueUnsigned(x)) } else { const digit* digits = __Pyx_PyLong_Digits(x); assert(__Pyx_PyLong_DigitCount(x) > 1); switch (__Pyx_PyLong_DigitCount(x)) { case 2: if ((8 * sizeof(uint32_t) > 1 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(uint32_t, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(uint32_t) >= 2 * PyLong_SHIFT)) { return (uint32_t) (((((uint32_t)digits[1]) << PyLong_SHIFT) | (uint32_t)digits[0])); } } break; case 3: if ((8 * sizeof(uint32_t) > 2 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(uint32_t, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(uint32_t) >= 3 * PyLong_SHIFT)) { return (uint32_t) (((((((uint32_t)digits[2]) << PyLong_SHIFT) | (uint32_t)digits[1]) << PyLong_SHIFT) | (uint32_t)digits[0])); } } break; case 4: if ((8 * sizeof(uint32_t) > 3 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(uint32_t, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(uint32_t) >= 4 * PyLong_SHIFT)) { return (uint32_t) (((((((((uint32_t)digits[3]) << PyLong_SHIFT) | (uint32_t)digits[2]) << PyLong_SHIFT) | (uint32_t)digits[1]) << PyLong_SHIFT) | (uint32_t)digits[0])); } } break; } } #endif #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7 if (unlikely(Py_SIZE(x) < 0)) { goto raise_neg_overflow; } #else { int result = PyObject_RichCompareBool(x, Py_False, Py_LT); if (unlikely(result < 0)) return (uint32_t) -1; if (unlikely(result == 1)) goto raise_neg_overflow; } #endif if ((sizeof(uint32_t) <= sizeof(unsigned long))) { __PYX_VERIFY_RETURN_INT_EXC(uint32_t, unsigned long, PyLong_AsUnsignedLong(x)) #ifdef HAVE_LONG_LONG } else if ((sizeof(uint32_t) <= sizeof(unsigned PY_LONG_LONG))) { __PYX_VERIFY_RETURN_INT_EXC(uint32_t, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x)) #endif } } else { #if CYTHON_USE_PYLONG_INTERNALS if (__Pyx_PyLong_IsCompact(x)) { __PYX_VERIFY_RETURN_INT(uint32_t, __Pyx_compact_pylong, __Pyx_PyLong_CompactValue(x)) } else { const digit* digits = __Pyx_PyLong_Digits(x); assert(__Pyx_PyLong_DigitCount(x) > 1); switch (__Pyx_PyLong_SignedDigitCount(x)) { case -2: if ((8 * sizeof(uint32_t) - 1 > 1 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(uint32_t, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(uint32_t) - 1 > 2 * PyLong_SHIFT)) { return (uint32_t) (((uint32_t)-1)*(((((uint32_t)digits[1]) << PyLong_SHIFT) | (uint32_t)digits[0]))); } } break; case 2: if ((8 * sizeof(uint32_t) > 1 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(uint32_t, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(uint32_t) - 1 > 2 * PyLong_SHIFT)) { return (uint32_t) ((((((uint32_t)digits[1]) << PyLong_SHIFT) | (uint32_t)digits[0]))); } } break; case -3: if ((8 * sizeof(uint32_t) - 1 > 2 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(uint32_t, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(uint32_t) - 1 > 3 * PyLong_SHIFT)) { return (uint32_t) (((uint32_t)-1)*(((((((uint32_t)digits[2]) << PyLong_SHIFT) | (uint32_t)digits[1]) << PyLong_SHIFT) | (uint32_t)digits[0]))); } } break; case 3: if ((8 * sizeof(uint32_t) > 2 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(uint32_t, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(uint32_t) - 1 > 3 * PyLong_SHIFT)) { return (uint32_t) ((((((((uint32_t)digits[2]) << PyLong_SHIFT) | (uint32_t)digits[1]) << PyLong_SHIFT) | (uint32_t)digits[0]))); } } break; case -4: if ((8 * sizeof(uint32_t) - 1 > 3 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(uint32_t, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(uint32_t) - 1 > 4 * PyLong_SHIFT)) { return (uint32_t) (((uint32_t)-1)*(((((((((uint32_t)digits[3]) << PyLong_SHIFT) | (uint32_t)digits[2]) << PyLong_SHIFT) | (uint32_t)digits[1]) << PyLong_SHIFT) | (uint32_t)digits[0]))); } } break; case 4: if ((8 * sizeof(uint32_t) > 3 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(uint32_t, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(uint32_t) - 1 > 4 * PyLong_SHIFT)) { return (uint32_t) ((((((((((uint32_t)digits[3]) << PyLong_SHIFT) | (uint32_t)digits[2]) << PyLong_SHIFT) | (uint32_t)digits[1]) << PyLong_SHIFT) | (uint32_t)digits[0]))); } } break; } } #endif if ((sizeof(uint32_t) <= sizeof(long))) { __PYX_VERIFY_RETURN_INT_EXC(uint32_t, long, PyLong_AsLong(x)) #ifdef HAVE_LONG_LONG } else if ((sizeof(uint32_t) <= sizeof(PY_LONG_LONG))) { __PYX_VERIFY_RETURN_INT_EXC(uint32_t, PY_LONG_LONG, PyLong_AsLongLong(x)) #endif } } { uint32_t val; PyObject *v = __Pyx_PyNumber_IntOrLong(x); #if PY_MAJOR_VERSION < 3 if (likely(v) && !PyLong_Check(v)) { PyObject *tmp = v; v = PyNumber_Long(tmp); Py_DECREF(tmp); } #endif if (likely(v)) { int ret = -1; #if PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray) int one = 1; int is_little = (int)*(unsigned char *)&one; unsigned char *bytes = (unsigned char *)&val; ret = _PyLong_AsByteArray((PyLongObject *)v, bytes, sizeof(val), is_little, !is_unsigned); #else PyObject *stepval = NULL, *mask = NULL, *shift = NULL; int bits, remaining_bits, is_negative = 0; long idigit; int chunk_size = (sizeof(long) < 8) ? 30 : 62; if (unlikely(!PyLong_CheckExact(v))) { PyObject *tmp = v; v = PyNumber_Long(v); assert(PyLong_CheckExact(v)); Py_DECREF(tmp); if (unlikely(!v)) return (uint32_t) -1; } #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000 if (Py_SIZE(x) == 0) return (uint32_t) 0; is_negative = Py_SIZE(x) < 0; #else { int result = PyObject_RichCompareBool(x, Py_False, Py_LT); if (unlikely(result < 0)) return (uint32_t) -1; is_negative = result == 1; } #endif if (is_unsigned && unlikely(is_negative)) { goto raise_neg_overflow; } else if (is_negative) { stepval = PyNumber_Invert(v); if (unlikely(!stepval)) return (uint32_t) -1; } else { stepval = __Pyx_NewRef(v); } val = (uint32_t) 0; mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done; shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done; for (bits = 0; bits < (int) sizeof(uint32_t) * 8 - chunk_size; bits += chunk_size) { PyObject *tmp, *digit; digit = PyNumber_And(stepval, mask); if (unlikely(!digit)) goto done; idigit = PyLong_AsLong(digit); Py_DECREF(digit); if (unlikely(idigit < 0)) goto done; tmp = PyNumber_Rshift(stepval, shift); if (unlikely(!tmp)) goto done; Py_DECREF(stepval); stepval = tmp; val |= ((uint32_t) idigit) << bits; #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000 if (Py_SIZE(stepval) == 0) goto unpacking_done; #endif } idigit = PyLong_AsLong(stepval); if (unlikely(idigit < 0)) goto done; remaining_bits = ((int) sizeof(uint32_t) * 8) - bits - (is_unsigned ? 0 : 1); if (unlikely(idigit >= (1L << remaining_bits))) goto raise_overflow; val |= ((uint32_t) idigit) << bits; #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000 unpacking_done: #endif if (!is_unsigned) { if (unlikely(val & (((uint32_t) 1) << (sizeof(uint32_t) * 8 - 1)))) goto raise_overflow; if (is_negative) val = ~val; } ret = 0; done: Py_XDECREF(shift); Py_XDECREF(mask); Py_XDECREF(stepval); #endif Py_DECREF(v); if (likely(!ret)) return val; } return (uint32_t) -1; } } else { uint32_t val; PyObject *tmp = __Pyx_PyNumber_IntOrLong(x); if (!tmp) return (uint32_t) -1; val = __Pyx_PyInt_As_uint32_t(tmp); Py_DECREF(tmp); return val; } raise_overflow: PyErr_SetString(PyExc_OverflowError, "value too large to convert to uint32_t"); return (uint32_t) -1; raise_neg_overflow: PyErr_SetString(PyExc_OverflowError, "can't convert negative value to uint32_t"); return (uint32_t) -1; } /* CIntToPy */ static CYTHON_INLINE PyObject* __Pyx_PyInt_From_uint32_t(uint32_t value) { #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #endif const uint32_t neg_one = (uint32_t) -1, const_zero = (uint32_t) 0; #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic pop #endif const int is_unsigned = neg_one > const_zero; if (is_unsigned) { if (sizeof(uint32_t) < sizeof(long)) { return PyInt_FromLong((long) value); } else if (sizeof(uint32_t) <= sizeof(unsigned long)) { return PyLong_FromUnsignedLong((unsigned long) value); #ifdef HAVE_LONG_LONG } else if (sizeof(uint32_t) <= sizeof(unsigned PY_LONG_LONG)) { return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value); #endif } } else { if (sizeof(uint32_t) <= sizeof(long)) { return PyInt_FromLong((long) value); #ifdef HAVE_LONG_LONG } else if (sizeof(uint32_t) <= sizeof(PY_LONG_LONG)) { return PyLong_FromLongLong((PY_LONG_LONG) value); #endif } } { int one = 1; int little = (int)*(unsigned char *)&one; unsigned char *bytes = (unsigned char *)&value; #if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030d0000 return _PyLong_FromByteArray(bytes, sizeof(uint32_t), little, !is_unsigned); #else PyObject *from_bytes, *result = NULL; PyObject *py_bytes = NULL, *arg_tuple = NULL, *kwds = NULL, *order_str = NULL; from_bytes = PyObject_GetAttrString((PyObject*)&PyLong_Type, "from_bytes"); if (!from_bytes) return NULL; py_bytes = PyBytes_FromStringAndSize((char*)bytes, sizeof(uint32_t)); if (!py_bytes) goto limited_bad; order_str = PyUnicode_FromString(little ? "little" : "big"); if (!order_str) goto limited_bad; arg_tuple = PyTuple_Pack(2, py_bytes, order_str); if (!arg_tuple) goto limited_bad; if (!is_unsigned) { kwds = PyDict_New(); if (!kwds) goto limited_bad; if (PyDict_SetItemString(kwds, "signed", __Pyx_NewRef(Py_True))) goto limited_bad; } result = PyObject_Call(from_bytes, arg_tuple, kwds); limited_bad: Py_XDECREF(kwds); Py_XDECREF(arg_tuple); Py_XDECREF(order_str); Py_XDECREF(py_bytes); Py_XDECREF(from_bytes); return result; #endif } } /* CIntFromPy */ static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_PyInt_As_unsigned_PY_LONG_LONG(PyObject *x) { #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #endif const unsigned PY_LONG_LONG neg_one = (unsigned PY_LONG_LONG) -1, const_zero = (unsigned PY_LONG_LONG) 0; #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic pop #endif const int is_unsigned = neg_one > const_zero; #if PY_MAJOR_VERSION < 3 if (likely(PyInt_Check(x))) { if ((sizeof(unsigned PY_LONG_LONG) < sizeof(long))) { __PYX_VERIFY_RETURN_INT(unsigned PY_LONG_LONG, long, PyInt_AS_LONG(x)) } else { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { goto raise_neg_overflow; } return (unsigned PY_LONG_LONG) val; } } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { #if CYTHON_USE_PYLONG_INTERNALS if (unlikely(__Pyx_PyLong_IsNeg(x))) { goto raise_neg_overflow; } else if (__Pyx_PyLong_IsCompact(x)) { __PYX_VERIFY_RETURN_INT(unsigned PY_LONG_LONG, __Pyx_compact_upylong, __Pyx_PyLong_CompactValueUnsigned(x)) } else { const digit* digits = __Pyx_PyLong_Digits(x); assert(__Pyx_PyLong_DigitCount(x) > 1); switch (__Pyx_PyLong_DigitCount(x)) { case 2: if ((8 * sizeof(unsigned PY_LONG_LONG) > 1 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(unsigned PY_LONG_LONG, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(unsigned PY_LONG_LONG) >= 2 * PyLong_SHIFT)) { return (unsigned PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); } } break; case 3: if ((8 * sizeof(unsigned PY_LONG_LONG) > 2 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(unsigned PY_LONG_LONG, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(unsigned PY_LONG_LONG) >= 3 * PyLong_SHIFT)) { return (unsigned PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); } } break; case 4: if ((8 * sizeof(unsigned PY_LONG_LONG) > 3 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(unsigned PY_LONG_LONG, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(unsigned PY_LONG_LONG) >= 4 * PyLong_SHIFT)) { return (unsigned PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); } } break; } } #endif #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7 if (unlikely(Py_SIZE(x) < 0)) { goto raise_neg_overflow; } #else { int result = PyObject_RichCompareBool(x, Py_False, Py_LT); if (unlikely(result < 0)) return (unsigned PY_LONG_LONG) -1; if (unlikely(result == 1)) goto raise_neg_overflow; } #endif if ((sizeof(unsigned PY_LONG_LONG) <= sizeof(unsigned long))) { __PYX_VERIFY_RETURN_INT_EXC(unsigned PY_LONG_LONG, unsigned long, PyLong_AsUnsignedLong(x)) #ifdef HAVE_LONG_LONG } else if ((sizeof(unsigned PY_LONG_LONG) <= sizeof(unsigned PY_LONG_LONG))) { __PYX_VERIFY_RETURN_INT_EXC(unsigned PY_LONG_LONG, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x)) #endif } } else { #if CYTHON_USE_PYLONG_INTERNALS if (__Pyx_PyLong_IsCompact(x)) { __PYX_VERIFY_RETURN_INT(unsigned PY_LONG_LONG, __Pyx_compact_pylong, __Pyx_PyLong_CompactValue(x)) } else { const digit* digits = __Pyx_PyLong_Digits(x); assert(__Pyx_PyLong_DigitCount(x) > 1); switch (__Pyx_PyLong_SignedDigitCount(x)) { case -2: if ((8 * sizeof(unsigned PY_LONG_LONG) - 1 > 1 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(unsigned PY_LONG_LONG, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(unsigned PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT)) { return (unsigned PY_LONG_LONG) (((unsigned PY_LONG_LONG)-1)*(((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]))); } } break; case 2: if ((8 * sizeof(unsigned PY_LONG_LONG) > 1 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(unsigned PY_LONG_LONG, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(unsigned PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT)) { return (unsigned PY_LONG_LONG) ((((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]))); } } break; case -3: if ((8 * sizeof(unsigned PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(unsigned PY_LONG_LONG, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(unsigned PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT)) { return (unsigned PY_LONG_LONG) (((unsigned PY_LONG_LONG)-1)*(((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]))); } } break; case 3: if ((8 * sizeof(unsigned PY_LONG_LONG) > 2 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(unsigned PY_LONG_LONG, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(unsigned PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT)) { return (unsigned PY_LONG_LONG) ((((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]))); } } break; case -4: if ((8 * sizeof(unsigned PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(unsigned PY_LONG_LONG, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(unsigned PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT)) { return (unsigned PY_LONG_LONG) (((unsigned PY_LONG_LONG)-1)*(((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]))); } } break; case 4: if ((8 * sizeof(unsigned PY_LONG_LONG) > 3 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(unsigned PY_LONG_LONG, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(unsigned PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT)) { return (unsigned PY_LONG_LONG) ((((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]))); } } break; } } #endif if ((sizeof(unsigned PY_LONG_LONG) <= sizeof(long))) { __PYX_VERIFY_RETURN_INT_EXC(unsigned PY_LONG_LONG, long, PyLong_AsLong(x)) #ifdef HAVE_LONG_LONG } else if ((sizeof(unsigned PY_LONG_LONG) <= sizeof(PY_LONG_LONG))) { __PYX_VERIFY_RETURN_INT_EXC(unsigned PY_LONG_LONG, PY_LONG_LONG, PyLong_AsLongLong(x)) #endif } } { unsigned PY_LONG_LONG val; PyObject *v = __Pyx_PyNumber_IntOrLong(x); #if PY_MAJOR_VERSION < 3 if (likely(v) && !PyLong_Check(v)) { PyObject *tmp = v; v = PyNumber_Long(tmp); Py_DECREF(tmp); } #endif if (likely(v)) { int ret = -1; #if PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray) int one = 1; int is_little = (int)*(unsigned char *)&one; unsigned char *bytes = (unsigned char *)&val; ret = _PyLong_AsByteArray((PyLongObject *)v, bytes, sizeof(val), is_little, !is_unsigned); #else PyObject *stepval = NULL, *mask = NULL, *shift = NULL; int bits, remaining_bits, is_negative = 0; long idigit; int chunk_size = (sizeof(long) < 8) ? 30 : 62; if (unlikely(!PyLong_CheckExact(v))) { PyObject *tmp = v; v = PyNumber_Long(v); assert(PyLong_CheckExact(v)); Py_DECREF(tmp); if (unlikely(!v)) return (unsigned PY_LONG_LONG) -1; } #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000 if (Py_SIZE(x) == 0) return (unsigned PY_LONG_LONG) 0; is_negative = Py_SIZE(x) < 0; #else { int result = PyObject_RichCompareBool(x, Py_False, Py_LT); if (unlikely(result < 0)) return (unsigned PY_LONG_LONG) -1; is_negative = result == 1; } #endif if (is_unsigned && unlikely(is_negative)) { goto raise_neg_overflow; } else if (is_negative) { stepval = PyNumber_Invert(v); if (unlikely(!stepval)) return (unsigned PY_LONG_LONG) -1; } else { stepval = __Pyx_NewRef(v); } val = (unsigned PY_LONG_LONG) 0; mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done; shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done; for (bits = 0; bits < (int) sizeof(unsigned PY_LONG_LONG) * 8 - chunk_size; bits += chunk_size) { PyObject *tmp, *digit; digit = PyNumber_And(stepval, mask); if (unlikely(!digit)) goto done; idigit = PyLong_AsLong(digit); Py_DECREF(digit); if (unlikely(idigit < 0)) goto done; tmp = PyNumber_Rshift(stepval, shift); if (unlikely(!tmp)) goto done; Py_DECREF(stepval); stepval = tmp; val |= ((unsigned PY_LONG_LONG) idigit) << bits; #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000 if (Py_SIZE(stepval) == 0) goto unpacking_done; #endif } idigit = PyLong_AsLong(stepval); if (unlikely(idigit < 0)) goto done; remaining_bits = ((int) sizeof(unsigned PY_LONG_LONG) * 8) - bits - (is_unsigned ? 0 : 1); if (unlikely(idigit >= (1L << remaining_bits))) goto raise_overflow; val |= ((unsigned PY_LONG_LONG) idigit) << bits; #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000 unpacking_done: #endif if (!is_unsigned) { if (unlikely(val & (((unsigned PY_LONG_LONG) 1) << (sizeof(unsigned PY_LONG_LONG) * 8 - 1)))) goto raise_overflow; if (is_negative) val = ~val; } ret = 0; done: Py_XDECREF(shift); Py_XDECREF(mask); Py_XDECREF(stepval); #endif Py_DECREF(v); if (likely(!ret)) return val; } return (unsigned PY_LONG_LONG) -1; } } else { unsigned PY_LONG_LONG val; PyObject *tmp = __Pyx_PyNumber_IntOrLong(x); if (!tmp) return (unsigned PY_LONG_LONG) -1; val = __Pyx_PyInt_As_unsigned_PY_LONG_LONG(tmp); Py_DECREF(tmp); return val; } raise_overflow: PyErr_SetString(PyExc_OverflowError, "value too large to convert to unsigned PY_LONG_LONG"); return (unsigned PY_LONG_LONG) -1; raise_neg_overflow: PyErr_SetString(PyExc_OverflowError, "can't convert negative value to unsigned PY_LONG_LONG"); return (unsigned PY_LONG_LONG) -1; } /* FormatTypeName */ #if CYTHON_COMPILING_IN_LIMITED_API static __Pyx_TypeName __Pyx_PyType_GetName(PyTypeObject* tp) { PyObject *name = __Pyx_PyObject_GetAttrStr((PyObject *)tp, __pyx_n_s_name); if (unlikely(name == NULL) || unlikely(!PyUnicode_Check(name))) { PyErr_Clear(); Py_XDECREF(name); name = __Pyx_NewRef(__pyx_n_s__19); } return name; } #endif /* CIntToPy */ static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value) { #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #endif const long neg_one = (long) -1, const_zero = (long) 0; #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic pop #endif const int is_unsigned = neg_one > const_zero; if (is_unsigned) { if (sizeof(long) < sizeof(long)) { return PyInt_FromLong((long) value); } else if (sizeof(long) <= sizeof(unsigned long)) { return PyLong_FromUnsignedLong((unsigned long) value); #ifdef HAVE_LONG_LONG } else if (sizeof(long) <= sizeof(unsigned PY_LONG_LONG)) { return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value); #endif } } else { if (sizeof(long) <= sizeof(long)) { return PyInt_FromLong((long) value); #ifdef HAVE_LONG_LONG } else if (sizeof(long) <= sizeof(PY_LONG_LONG)) { return PyLong_FromLongLong((PY_LONG_LONG) value); #endif } } { int one = 1; int little = (int)*(unsigned char *)&one; unsigned char *bytes = (unsigned char *)&value; #if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030d0000 return _PyLong_FromByteArray(bytes, sizeof(long), little, !is_unsigned); #else PyObject *from_bytes, *result = NULL; PyObject *py_bytes = NULL, *arg_tuple = NULL, *kwds = NULL, *order_str = NULL; from_bytes = PyObject_GetAttrString((PyObject*)&PyLong_Type, "from_bytes"); if (!from_bytes) return NULL; py_bytes = PyBytes_FromStringAndSize((char*)bytes, sizeof(long)); if (!py_bytes) goto limited_bad; order_str = PyUnicode_FromString(little ? "little" : "big"); if (!order_str) goto limited_bad; arg_tuple = PyTuple_Pack(2, py_bytes, order_str); if (!arg_tuple) goto limited_bad; if (!is_unsigned) { kwds = PyDict_New(); if (!kwds) goto limited_bad; if (PyDict_SetItemString(kwds, "signed", __Pyx_NewRef(Py_True))) goto limited_bad; } result = PyObject_Call(from_bytes, arg_tuple, kwds); limited_bad: Py_XDECREF(kwds); Py_XDECREF(arg_tuple); Py_XDECREF(order_str); Py_XDECREF(py_bytes); Py_XDECREF(from_bytes); return result; #endif } } /* CIntFromPy */ static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *x) { #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #endif const long neg_one = (long) -1, const_zero = (long) 0; #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic pop #endif const int is_unsigned = neg_one > const_zero; #if PY_MAJOR_VERSION < 3 if (likely(PyInt_Check(x))) { if ((sizeof(long) < sizeof(long))) { __PYX_VERIFY_RETURN_INT(long, long, PyInt_AS_LONG(x)) } else { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { goto raise_neg_overflow; } return (long) val; } } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { #if CYTHON_USE_PYLONG_INTERNALS if (unlikely(__Pyx_PyLong_IsNeg(x))) { goto raise_neg_overflow; } else if (__Pyx_PyLong_IsCompact(x)) { __PYX_VERIFY_RETURN_INT(long, __Pyx_compact_upylong, __Pyx_PyLong_CompactValueUnsigned(x)) } else { const digit* digits = __Pyx_PyLong_Digits(x); assert(__Pyx_PyLong_DigitCount(x) > 1); switch (__Pyx_PyLong_DigitCount(x)) { case 2: if ((8 * sizeof(long) > 1 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(long) >= 2 * PyLong_SHIFT)) { return (long) (((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0])); } } break; case 3: if ((8 * sizeof(long) > 2 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(long) >= 3 * PyLong_SHIFT)) { return (long) (((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])); } } break; case 4: if ((8 * sizeof(long) > 3 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(long) >= 4 * PyLong_SHIFT)) { return (long) (((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])); } } break; } } #endif #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7 if (unlikely(Py_SIZE(x) < 0)) { goto raise_neg_overflow; } #else { int result = PyObject_RichCompareBool(x, Py_False, Py_LT); if (unlikely(result < 0)) return (long) -1; if (unlikely(result == 1)) goto raise_neg_overflow; } #endif if ((sizeof(long) <= sizeof(unsigned long))) { __PYX_VERIFY_RETURN_INT_EXC(long, unsigned long, PyLong_AsUnsignedLong(x)) #ifdef HAVE_LONG_LONG } else if ((sizeof(long) <= sizeof(unsigned PY_LONG_LONG))) { __PYX_VERIFY_RETURN_INT_EXC(long, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x)) #endif } } else { #if CYTHON_USE_PYLONG_INTERNALS if (__Pyx_PyLong_IsCompact(x)) { __PYX_VERIFY_RETURN_INT(long, __Pyx_compact_pylong, __Pyx_PyLong_CompactValue(x)) } else { const digit* digits = __Pyx_PyLong_Digits(x); assert(__Pyx_PyLong_DigitCount(x) > 1); switch (__Pyx_PyLong_SignedDigitCount(x)) { case -2: if ((8 * sizeof(long) - 1 > 1 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) { return (long) (((long)-1)*(((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); } } break; case 2: if ((8 * sizeof(long) > 1 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) { return (long) ((((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); } } break; case -3: if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) { return (long) (((long)-1)*(((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); } } break; case 3: if ((8 * sizeof(long) > 2 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) { return (long) ((((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); } } break; case -4: if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(long) - 1 > 4 * PyLong_SHIFT)) { return (long) (((long)-1)*(((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); } } break; case 4: if ((8 * sizeof(long) > 3 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(long) - 1 > 4 * PyLong_SHIFT)) { return (long) ((((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); } } break; } } #endif if ((sizeof(long) <= sizeof(long))) { __PYX_VERIFY_RETURN_INT_EXC(long, long, PyLong_AsLong(x)) #ifdef HAVE_LONG_LONG } else if ((sizeof(long) <= sizeof(PY_LONG_LONG))) { __PYX_VERIFY_RETURN_INT_EXC(long, PY_LONG_LONG, PyLong_AsLongLong(x)) #endif } } { long val; PyObject *v = __Pyx_PyNumber_IntOrLong(x); #if PY_MAJOR_VERSION < 3 if (likely(v) && !PyLong_Check(v)) { PyObject *tmp = v; v = PyNumber_Long(tmp); Py_DECREF(tmp); } #endif if (likely(v)) { int ret = -1; #if PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray) int one = 1; int is_little = (int)*(unsigned char *)&one; unsigned char *bytes = (unsigned char *)&val; ret = _PyLong_AsByteArray((PyLongObject *)v, bytes, sizeof(val), is_little, !is_unsigned); #else PyObject *stepval = NULL, *mask = NULL, *shift = NULL; int bits, remaining_bits, is_negative = 0; long idigit; int chunk_size = (sizeof(long) < 8) ? 30 : 62; if (unlikely(!PyLong_CheckExact(v))) { PyObject *tmp = v; v = PyNumber_Long(v); assert(PyLong_CheckExact(v)); Py_DECREF(tmp); if (unlikely(!v)) return (long) -1; } #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000 if (Py_SIZE(x) == 0) return (long) 0; is_negative = Py_SIZE(x) < 0; #else { int result = PyObject_RichCompareBool(x, Py_False, Py_LT); if (unlikely(result < 0)) return (long) -1; is_negative = result == 1; } #endif if (is_unsigned && unlikely(is_negative)) { goto raise_neg_overflow; } else if (is_negative) { stepval = PyNumber_Invert(v); if (unlikely(!stepval)) return (long) -1; } else { stepval = __Pyx_NewRef(v); } val = (long) 0; mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done; shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done; for (bits = 0; bits < (int) sizeof(long) * 8 - chunk_size; bits += chunk_size) { PyObject *tmp, *digit; digit = PyNumber_And(stepval, mask); if (unlikely(!digit)) goto done; idigit = PyLong_AsLong(digit); Py_DECREF(digit); if (unlikely(idigit < 0)) goto done; tmp = PyNumber_Rshift(stepval, shift); if (unlikely(!tmp)) goto done; Py_DECREF(stepval); stepval = tmp; val |= ((long) idigit) << bits; #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000 if (Py_SIZE(stepval) == 0) goto unpacking_done; #endif } idigit = PyLong_AsLong(stepval); if (unlikely(idigit < 0)) goto done; remaining_bits = ((int) sizeof(long) * 8) - bits - (is_unsigned ? 0 : 1); if (unlikely(idigit >= (1L << remaining_bits))) goto raise_overflow; val |= ((long) idigit) << bits; #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000 unpacking_done: #endif if (!is_unsigned) { if (unlikely(val & (((long) 1) << (sizeof(long) * 8 - 1)))) goto raise_overflow; if (is_negative) val = ~val; } ret = 0; done: Py_XDECREF(shift); Py_XDECREF(mask); Py_XDECREF(stepval); #endif Py_DECREF(v); if (likely(!ret)) return val; } return (long) -1; } } else { long val; PyObject *tmp = __Pyx_PyNumber_IntOrLong(x); if (!tmp) return (long) -1; val = __Pyx_PyInt_As_long(tmp); Py_DECREF(tmp); return val; } raise_overflow: PyErr_SetString(PyExc_OverflowError, "value too large to convert to long"); return (long) -1; raise_neg_overflow: PyErr_SetString(PyExc_OverflowError, "can't convert negative value to long"); return (long) -1; } /* CIntFromPy */ static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *x) { #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #endif const int neg_one = (int) -1, const_zero = (int) 0; #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic pop #endif const int is_unsigned = neg_one > const_zero; #if PY_MAJOR_VERSION < 3 if (likely(PyInt_Check(x))) { if ((sizeof(int) < sizeof(long))) { __PYX_VERIFY_RETURN_INT(int, long, PyInt_AS_LONG(x)) } else { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { goto raise_neg_overflow; } return (int) val; } } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { #if CYTHON_USE_PYLONG_INTERNALS if (unlikely(__Pyx_PyLong_IsNeg(x))) { goto raise_neg_overflow; } else if (__Pyx_PyLong_IsCompact(x)) { __PYX_VERIFY_RETURN_INT(int, __Pyx_compact_upylong, __Pyx_PyLong_CompactValueUnsigned(x)) } else { const digit* digits = __Pyx_PyLong_Digits(x); assert(__Pyx_PyLong_DigitCount(x) > 1); switch (__Pyx_PyLong_DigitCount(x)) { case 2: if ((8 * sizeof(int) > 1 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(int) >= 2 * PyLong_SHIFT)) { return (int) (((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0])); } } break; case 3: if ((8 * sizeof(int) > 2 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(int) >= 3 * PyLong_SHIFT)) { return (int) (((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])); } } break; case 4: if ((8 * sizeof(int) > 3 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(int) >= 4 * PyLong_SHIFT)) { return (int) (((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])); } } break; } } #endif #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7 if (unlikely(Py_SIZE(x) < 0)) { goto raise_neg_overflow; } #else { int result = PyObject_RichCompareBool(x, Py_False, Py_LT); if (unlikely(result < 0)) return (int) -1; if (unlikely(result == 1)) goto raise_neg_overflow; } #endif if ((sizeof(int) <= sizeof(unsigned long))) { __PYX_VERIFY_RETURN_INT_EXC(int, unsigned long, PyLong_AsUnsignedLong(x)) #ifdef HAVE_LONG_LONG } else if ((sizeof(int) <= sizeof(unsigned PY_LONG_LONG))) { __PYX_VERIFY_RETURN_INT_EXC(int, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x)) #endif } } else { #if CYTHON_USE_PYLONG_INTERNALS if (__Pyx_PyLong_IsCompact(x)) { __PYX_VERIFY_RETURN_INT(int, __Pyx_compact_pylong, __Pyx_PyLong_CompactValue(x)) } else { const digit* digits = __Pyx_PyLong_Digits(x); assert(__Pyx_PyLong_DigitCount(x) > 1); switch (__Pyx_PyLong_SignedDigitCount(x)) { case -2: if ((8 * sizeof(int) - 1 > 1 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) { return (int) (((int)-1)*(((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); } } break; case 2: if ((8 * sizeof(int) > 1 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) { return (int) ((((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); } } break; case -3: if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) { return (int) (((int)-1)*(((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); } } break; case 3: if ((8 * sizeof(int) > 2 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) { return (int) ((((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); } } break; case -4: if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(int) - 1 > 4 * PyLong_SHIFT)) { return (int) (((int)-1)*(((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); } } break; case 4: if ((8 * sizeof(int) > 3 * PyLong_SHIFT)) { if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) } else if ((8 * sizeof(int) - 1 > 4 * PyLong_SHIFT)) { return (int) ((((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); } } break; } } #endif if ((sizeof(int) <= sizeof(long))) { __PYX_VERIFY_RETURN_INT_EXC(int, long, PyLong_AsLong(x)) #ifdef HAVE_LONG_LONG } else if ((sizeof(int) <= sizeof(PY_LONG_LONG))) { __PYX_VERIFY_RETURN_INT_EXC(int, PY_LONG_LONG, PyLong_AsLongLong(x)) #endif } } { int val; PyObject *v = __Pyx_PyNumber_IntOrLong(x); #if PY_MAJOR_VERSION < 3 if (likely(v) && !PyLong_Check(v)) { PyObject *tmp = v; v = PyNumber_Long(tmp); Py_DECREF(tmp); } #endif if (likely(v)) { int ret = -1; #if PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray) int one = 1; int is_little = (int)*(unsigned char *)&one; unsigned char *bytes = (unsigned char *)&val; ret = _PyLong_AsByteArray((PyLongObject *)v, bytes, sizeof(val), is_little, !is_unsigned); #else PyObject *stepval = NULL, *mask = NULL, *shift = NULL; int bits, remaining_bits, is_negative = 0; long idigit; int chunk_size = (sizeof(long) < 8) ? 30 : 62; if (unlikely(!PyLong_CheckExact(v))) { PyObject *tmp = v; v = PyNumber_Long(v); assert(PyLong_CheckExact(v)); Py_DECREF(tmp); if (unlikely(!v)) return (int) -1; } #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000 if (Py_SIZE(x) == 0) return (int) 0; is_negative = Py_SIZE(x) < 0; #else { int result = PyObject_RichCompareBool(x, Py_False, Py_LT); if (unlikely(result < 0)) return (int) -1; is_negative = result == 1; } #endif if (is_unsigned && unlikely(is_negative)) { goto raise_neg_overflow; } else if (is_negative) { stepval = PyNumber_Invert(v); if (unlikely(!stepval)) return (int) -1; } else { stepval = __Pyx_NewRef(v); } val = (int) 0; mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done; shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done; for (bits = 0; bits < (int) sizeof(int) * 8 - chunk_size; bits += chunk_size) { PyObject *tmp, *digit; digit = PyNumber_And(stepval, mask); if (unlikely(!digit)) goto done; idigit = PyLong_AsLong(digit); Py_DECREF(digit); if (unlikely(idigit < 0)) goto done; tmp = PyNumber_Rshift(stepval, shift); if (unlikely(!tmp)) goto done; Py_DECREF(stepval); stepval = tmp; val |= ((int) idigit) << bits; #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000 if (Py_SIZE(stepval) == 0) goto unpacking_done; #endif } idigit = PyLong_AsLong(stepval); if (unlikely(idigit < 0)) goto done; remaining_bits = ((int) sizeof(int) * 8) - bits - (is_unsigned ? 0 : 1); if (unlikely(idigit >= (1L << remaining_bits))) goto raise_overflow; val |= ((int) idigit) << bits; #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000 unpacking_done: #endif if (!is_unsigned) { if (unlikely(val & (((int) 1) << (sizeof(int) * 8 - 1)))) goto raise_overflow; if (is_negative) val = ~val; } ret = 0; done: Py_XDECREF(shift); Py_XDECREF(mask); Py_XDECREF(stepval); #endif Py_DECREF(v); if (likely(!ret)) return val; } return (int) -1; } } else { int val; PyObject *tmp = __Pyx_PyNumber_IntOrLong(x); if (!tmp) return (int) -1; val = __Pyx_PyInt_As_int(tmp); Py_DECREF(tmp); return val; } raise_overflow: PyErr_SetString(PyExc_OverflowError, "value too large to convert to int"); return (int) -1; raise_neg_overflow: PyErr_SetString(PyExc_OverflowError, "can't convert negative value to int"); return (int) -1; } /* FastTypeChecks */ #if CYTHON_COMPILING_IN_CPYTHON static int __Pyx_InBases(PyTypeObject *a, PyTypeObject *b) { while (a) { a = __Pyx_PyType_GetSlot(a, tp_base, PyTypeObject*); if (a == b) return 1; } return b == &PyBaseObject_Type; } static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b) { PyObject *mro; if (a == b) return 1; mro = a->tp_mro; if (likely(mro)) { Py_ssize_t i, n; n = PyTuple_GET_SIZE(mro); for (i = 0; i < n; i++) { if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b) return 1; } return 0; } return __Pyx_InBases(a, b); } static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, PyTypeObject *b) { PyObject *mro; if (cls == a || cls == b) return 1; mro = cls->tp_mro; if (likely(mro)) { Py_ssize_t i, n; n = PyTuple_GET_SIZE(mro); for (i = 0; i < n; i++) { PyObject *base = PyTuple_GET_ITEM(mro, i); if (base == (PyObject *)a || base == (PyObject *)b) return 1; } return 0; } return __Pyx_InBases(cls, a) || __Pyx_InBases(cls, b); } #if PY_MAJOR_VERSION == 2 static int __Pyx_inner_PyErr_GivenExceptionMatches2(PyObject *err, PyObject* exc_type1, PyObject* exc_type2) { PyObject *exception, *value, *tb; int res; __Pyx_PyThreadState_declare __Pyx_PyThreadState_assign __Pyx_ErrFetch(&exception, &value, &tb); res = exc_type1 ? PyObject_IsSubclass(err, exc_type1) : 0; if (unlikely(res == -1)) { PyErr_WriteUnraisable(err); res = 0; } if (!res) { res = PyObject_IsSubclass(err, exc_type2); if (unlikely(res == -1)) { PyErr_WriteUnraisable(err); res = 0; } } __Pyx_ErrRestore(exception, value, tb); return res; } #else static CYTHON_INLINE int __Pyx_inner_PyErr_GivenExceptionMatches2(PyObject *err, PyObject* exc_type1, PyObject *exc_type2) { if (exc_type1) { return __Pyx_IsAnySubtype2((PyTypeObject*)err, (PyTypeObject*)exc_type1, (PyTypeObject*)exc_type2); } else { return __Pyx_IsSubtype((PyTypeObject*)err, (PyTypeObject*)exc_type2); } } #endif static int __Pyx_PyErr_GivenExceptionMatchesTuple(PyObject *exc_type, PyObject *tuple) { Py_ssize_t i, n; assert(PyExceptionClass_Check(exc_type)); n = PyTuple_GET_SIZE(tuple); #if PY_MAJOR_VERSION >= 3 for (i=0; i= 0x030B00A4 return Py_Version & ~0xFFUL; #else const char* rt_version = Py_GetVersion(); unsigned long version = 0; unsigned long factor = 0x01000000UL; unsigned int digit = 0; int i = 0; while (factor) { while ('0' <= rt_version[i] && rt_version[i] <= '9') { digit = digit * 10 + (unsigned int) (rt_version[i] - '0'); ++i; } version += factor * digit; if (rt_version[i] != '.') break; digit = 0; factor >>= 8; ++i; } return version; #endif } static int __Pyx_check_binary_version(unsigned long ct_version, unsigned long rt_version, int allow_newer) { const unsigned long MAJOR_MINOR = 0xFFFF0000UL; if ((rt_version & MAJOR_MINOR) == (ct_version & MAJOR_MINOR)) return 0; if (likely(allow_newer && (rt_version & MAJOR_MINOR) > (ct_version & MAJOR_MINOR))) return 1; { char message[200]; PyOS_snprintf(message, sizeof(message), "compile time Python version %d.%d " "of module '%.100s' " "%s " "runtime version %d.%d", (int) (ct_version >> 24), (int) ((ct_version >> 16) & 0xFF), __Pyx_MODULE_NAME, (allow_newer) ? "was newer than" : "does not match", (int) (rt_version >> 24), (int) ((rt_version >> 16) & 0xFF) ); return PyErr_WarnEx(NULL, message, 1); } } /* InitStrings */ #if PY_MAJOR_VERSION >= 3 static int __Pyx_InitString(__Pyx_StringTabEntry t, PyObject **str) { if (t.is_unicode | t.is_str) { if (t.intern) { *str = PyUnicode_InternFromString(t.s); } else if (t.encoding) { *str = PyUnicode_Decode(t.s, t.n - 1, t.encoding, NULL); } else { *str = PyUnicode_FromStringAndSize(t.s, t.n - 1); } } else { *str = PyBytes_FromStringAndSize(t.s, t.n - 1); } if (!*str) return -1; if (PyObject_Hash(*str) == -1) return -1; return 0; } #endif static int __Pyx_InitStrings(__Pyx_StringTabEntry *t) { while (t->p) { #if PY_MAJOR_VERSION >= 3 __Pyx_InitString(*t, t->p); #else if (t->is_unicode) { *t->p = PyUnicode_DecodeUTF8(t->s, t->n - 1, NULL); } else if (t->intern) { *t->p = PyString_InternFromString(t->s); } else { *t->p = PyString_FromStringAndSize(t->s, t->n - 1); } if (!*t->p) return -1; if (PyObject_Hash(*t->p) == -1) return -1; #endif ++t; } return 0; } #include static CYTHON_INLINE Py_ssize_t __Pyx_ssize_strlen(const char *s) { size_t len = strlen(s); if (unlikely(len > (size_t) PY_SSIZE_T_MAX)) { PyErr_SetString(PyExc_OverflowError, "byte string is too long"); return -1; } return (Py_ssize_t) len; } static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char* c_str) { Py_ssize_t len = __Pyx_ssize_strlen(c_str); if (unlikely(len < 0)) return NULL; return __Pyx_PyUnicode_FromStringAndSize(c_str, len); } static CYTHON_INLINE PyObject* __Pyx_PyByteArray_FromString(const char* c_str) { Py_ssize_t len = __Pyx_ssize_strlen(c_str); if (unlikely(len < 0)) return NULL; return PyByteArray_FromStringAndSize(c_str, len); } static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject* o) { Py_ssize_t ignore; return __Pyx_PyObject_AsStringAndSize(o, &ignore); } #if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT #if !CYTHON_PEP393_ENABLED static const char* __Pyx_PyUnicode_AsStringAndSize(PyObject* o, Py_ssize_t *length) { char* defenc_c; PyObject* defenc = _PyUnicode_AsDefaultEncodedString(o, NULL); if (!defenc) return NULL; defenc_c = PyBytes_AS_STRING(defenc); #if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII { char* end = defenc_c + PyBytes_GET_SIZE(defenc); char* c; for (c = defenc_c; c < end; c++) { if ((unsigned char) (*c) >= 128) { PyUnicode_AsASCIIString(o); return NULL; } } } #endif *length = PyBytes_GET_SIZE(defenc); return defenc_c; } #else static CYTHON_INLINE const char* __Pyx_PyUnicode_AsStringAndSize(PyObject* o, Py_ssize_t *length) { if (unlikely(__Pyx_PyUnicode_READY(o) == -1)) return NULL; #if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII if (likely(PyUnicode_IS_ASCII(o))) { *length = PyUnicode_GET_LENGTH(o); return PyUnicode_AsUTF8(o); } else { PyUnicode_AsASCIIString(o); return NULL; } #else return PyUnicode_AsUTF8AndSize(o, length); #endif } #endif #endif static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) { #if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT if ( #if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII __Pyx_sys_getdefaultencoding_not_ascii && #endif PyUnicode_Check(o)) { return __Pyx_PyUnicode_AsStringAndSize(o, length); } else #endif #if (!CYTHON_COMPILING_IN_PYPY && !CYTHON_COMPILING_IN_LIMITED_API) || (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE)) if (PyByteArray_Check(o)) { *length = PyByteArray_GET_SIZE(o); return PyByteArray_AS_STRING(o); } else #endif { char* result; int r = PyBytes_AsStringAndSize(o, &result, length); if (unlikely(r < 0)) { return NULL; } else { return result; } } } static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { int is_true = x == Py_True; if (is_true | (x == Py_False) | (x == Py_None)) return is_true; else return PyObject_IsTrue(x); } static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject* x) { int retval; if (unlikely(!x)) return -1; retval = __Pyx_PyObject_IsTrue(x); Py_DECREF(x); return retval; } static PyObject* __Pyx_PyNumber_IntOrLongWrongResultType(PyObject* result, const char* type_name) { __Pyx_TypeName result_type_name = __Pyx_PyType_GetName(Py_TYPE(result)); #if PY_MAJOR_VERSION >= 3 if (PyLong_Check(result)) { if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, "__int__ returned non-int (type " __Pyx_FMT_TYPENAME "). " "The ability to return an instance of a strict subclass of int is deprecated, " "and may be removed in a future version of Python.", result_type_name)) { __Pyx_DECREF_TypeName(result_type_name); Py_DECREF(result); return NULL; } __Pyx_DECREF_TypeName(result_type_name); return result; } #endif PyErr_Format(PyExc_TypeError, "__%.4s__ returned non-%.4s (type " __Pyx_FMT_TYPENAME ")", type_name, type_name, result_type_name); __Pyx_DECREF_TypeName(result_type_name); Py_DECREF(result); return NULL; } static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x) { #if CYTHON_USE_TYPE_SLOTS PyNumberMethods *m; #endif const char *name = NULL; PyObject *res = NULL; #if PY_MAJOR_VERSION < 3 if (likely(PyInt_Check(x) || PyLong_Check(x))) #else if (likely(PyLong_Check(x))) #endif return __Pyx_NewRef(x); #if CYTHON_USE_TYPE_SLOTS m = Py_TYPE(x)->tp_as_number; #if PY_MAJOR_VERSION < 3 if (m && m->nb_int) { name = "int"; res = m->nb_int(x); } else if (m && m->nb_long) { name = "long"; res = m->nb_long(x); } #else if (likely(m && m->nb_int)) { name = "int"; res = m->nb_int(x); } #endif #else if (!PyBytes_CheckExact(x) && !PyUnicode_CheckExact(x)) { res = PyNumber_Int(x); } #endif if (likely(res)) { #if PY_MAJOR_VERSION < 3 if (unlikely(!PyInt_Check(res) && !PyLong_Check(res))) { #else if (unlikely(!PyLong_CheckExact(res))) { #endif return __Pyx_PyNumber_IntOrLongWrongResultType(res, name); } } else if (!PyErr_Occurred()) { PyErr_SetString(PyExc_TypeError, "an integer is required"); } return res; } static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) { Py_ssize_t ival; PyObject *x; #if PY_MAJOR_VERSION < 3 if (likely(PyInt_CheckExact(b))) { if (sizeof(Py_ssize_t) >= sizeof(long)) return PyInt_AS_LONG(b); else return PyInt_AsSsize_t(b); } #endif if (likely(PyLong_CheckExact(b))) { #if CYTHON_USE_PYLONG_INTERNALS if (likely(__Pyx_PyLong_IsCompact(b))) { return __Pyx_PyLong_CompactValue(b); } else { const digit* digits = __Pyx_PyLong_Digits(b); const Py_ssize_t size = __Pyx_PyLong_SignedDigitCount(b); switch (size) { case 2: if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) { return (Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); } break; case -2: if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) { return -(Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); } break; case 3: if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) { return (Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); } break; case -3: if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) { return -(Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); } break; case 4: if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) { return (Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); } break; case -4: if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) { return -(Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); } break; } } #endif return PyLong_AsSsize_t(b); } x = PyNumber_Index(b); if (!x) return -1; ival = PyInt_AsSsize_t(x); Py_DECREF(x); return ival; } static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject* o) { if (sizeof(Py_hash_t) == sizeof(Py_ssize_t)) { return (Py_hash_t) __Pyx_PyIndex_AsSsize_t(o); #if PY_MAJOR_VERSION < 3 } else if (likely(PyInt_CheckExact(o))) { return PyInt_AS_LONG(o); #endif } else { Py_ssize_t ival; PyObject *x; x = PyNumber_Index(o); if (!x) return -1; ival = PyInt_AsLong(x); Py_DECREF(x); return ival; } } static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) { return b ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False); } static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) { return PyInt_FromSize_t(ival); } /* #### Code section: utility_code_pragmas_end ### */ #ifdef _MSC_VER #pragma warning( pop ) #endif /* #### Code section: end ### */ #endif /* Py_PYTHON_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/checksums.pyx0000644000076500000240000000645414601576577020671 0ustar00twstafffrom ..helpers import bin_to_hex from libc.stdint cimport uint32_t from cpython.buffer cimport PyBUF_SIMPLE, PyObject_GetBuffer, PyBuffer_Release from cpython.bytes cimport PyBytes_FromStringAndSize cdef extern from "crc32_dispatch.c": uint32_t _crc32_slice_by_8 "crc32_slice_by_8"(const void* data, size_t length, uint32_t initial_crc) uint32_t _crc32_clmul "crc32_clmul"(const void* data, size_t length, uint32_t initial_crc) int _have_clmul "have_clmul"() cdef extern from "xxhash-libselect.h": ctypedef struct XXH64_canonical_t: char digest[8] ctypedef struct XXH64_state_t: pass # opaque ctypedef unsigned long long XXH64_hash_t ctypedef enum XXH_errorcode: XXH_OK, XXH_ERROR XXH64_state_t* XXH64_createState() XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) XXH64_hash_t XXH64(const void* input, size_t length, unsigned long long seed) XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed) XXH_errorcode XXH64_update(XXH64_state_t* statePtr, const void* input, size_t length) XXH64_hash_t XXH64_digest(const XXH64_state_t* statePtr) void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) cdef Py_buffer ro_buffer(object data) except *: cdef Py_buffer view PyObject_GetBuffer(data, &view, PyBUF_SIMPLE) return view def crc32_slice_by_8(data, value=0): cdef Py_buffer data_buf = ro_buffer(data) cdef uint32_t val = value try: return _crc32_slice_by_8(data_buf.buf, data_buf.len, val) finally: PyBuffer_Release(&data_buf) def crc32_clmul(data, value=0): cdef Py_buffer data_buf = ro_buffer(data) cdef uint32_t val = value try: return _crc32_clmul(data_buf.buf, data_buf.len, val) finally: PyBuffer_Release(&data_buf) have_clmul = _have_clmul() if have_clmul: crc32 = crc32_clmul else: crc32 = crc32_slice_by_8 def xxh64(data, seed=0): cdef unsigned long long _seed = seed cdef XXH64_hash_t hash cdef XXH64_canonical_t digest cdef Py_buffer data_buf = ro_buffer(data) try: hash = XXH64(data_buf.buf, data_buf.len, _seed) finally: PyBuffer_Release(&data_buf) XXH64_canonicalFromHash(&digest, hash) return PyBytes_FromStringAndSize( digest.digest, 8) cdef class StreamingXXH64: cdef XXH64_state_t* state def __cinit__(self, seed=0): self.state = XXH64_createState() cdef unsigned long long _seed = seed if XXH64_reset(self.state, _seed) != XXH_OK: raise Exception('XXH64_reset failed') def __dealloc__(self): XXH64_freeState(self.state) def update(self, data): cdef Py_buffer data_buf = ro_buffer(data) try: if XXH64_update(self.state, data_buf.buf, data_buf.len) != XXH_OK: raise Exception('XXH64_update failed') finally: PyBuffer_Release(&data_buf) def digest(self): cdef XXH64_hash_t hash cdef XXH64_canonical_t digest hash = XXH64_digest(self.state) XXH64_canonicalFromHash(&digest, hash) return PyBytes_FromStringAndSize( digest.digest, 8) def hexdigest(self): return bin_to_hex(self.digest()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/crc32_clmul.c0000644000076500000240000004225014601576577020410 0ustar00twstaff/* * Compute the CRC32 using a parallelized folding approach with the PCLMULQDQ * instruction. * * A white paper describing this algorithm can be found at: * http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf * * Copyright (C) 2013 Intel Corporation. All rights reserved. * Authors: * Wajdi Feghali * Jim Guilford * Vinodh Gopal * Erdinc Ozturk * Jim Kukunas * * Copyright (c) 2016 Marian Beermann (add support for initial value, restructuring) * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include #include #include #ifdef _MSC_VER #include #else /* * Newer versions of GCC and clang come with cpuid.h * (ftr GCC 4.7 in Debian Wheezy has this) */ #include #endif static void cpuid(int info, unsigned* eax, unsigned* ebx, unsigned* ecx, unsigned* edx) { #ifdef _MSC_VER unsigned int registers[4]; __cpuid(registers, info); *eax = registers[0]; *ebx = registers[1]; *ecx = registers[2]; *edx = registers[3]; #else /* GCC, clang */ unsigned int _eax; unsigned int _ebx; unsigned int _ecx; unsigned int _edx; __cpuid(info, _eax, _ebx, _ecx, _edx); *eax = _eax; *ebx = _ebx; *ecx = _ecx; *edx = _edx; #endif } static int have_clmul(void) { unsigned eax, ebx, ecx, edx; int has_pclmulqdq; int has_sse41; cpuid(1 /* feature bits */, &eax, &ebx, &ecx, &edx); has_pclmulqdq = ecx & 0x2; /* bit 1 */ has_sse41 = ecx & 0x80000; /* bit 19 */ return has_pclmulqdq && has_sse41; } CLMUL static void fold_1(__m128i *xmm_crc0, __m128i *xmm_crc1, __m128i *xmm_crc2, __m128i *xmm_crc3) { const __m128i xmm_fold4 = _mm_set_epi32( 0x00000001, 0x54442bd4, 0x00000001, 0xc6e41596); __m128i x_tmp3; __m128 ps_crc0, ps_crc3, ps_res; x_tmp3 = *xmm_crc3; *xmm_crc3 = *xmm_crc0; *xmm_crc0 = _mm_clmulepi64_si128(*xmm_crc0, xmm_fold4, 0x01); *xmm_crc3 = _mm_clmulepi64_si128(*xmm_crc3, xmm_fold4, 0x10); ps_crc0 = _mm_castsi128_ps(*xmm_crc0); ps_crc3 = _mm_castsi128_ps(*xmm_crc3); ps_res = _mm_xor_ps(ps_crc0, ps_crc3); *xmm_crc0 = *xmm_crc1; *xmm_crc1 = *xmm_crc2; *xmm_crc2 = x_tmp3; *xmm_crc3 = _mm_castps_si128(ps_res); } CLMUL static void fold_2(__m128i *xmm_crc0, __m128i *xmm_crc1, __m128i *xmm_crc2, __m128i *xmm_crc3) { const __m128i xmm_fold4 = _mm_set_epi32( 0x00000001, 0x54442bd4, 0x00000001, 0xc6e41596); __m128i x_tmp3, x_tmp2; __m128 ps_crc0, ps_crc1, ps_crc2, ps_crc3, ps_res31, ps_res20; x_tmp3 = *xmm_crc3; x_tmp2 = *xmm_crc2; *xmm_crc3 = *xmm_crc1; *xmm_crc1 = _mm_clmulepi64_si128(*xmm_crc1, xmm_fold4, 0x01); *xmm_crc3 = _mm_clmulepi64_si128(*xmm_crc3, xmm_fold4, 0x10); ps_crc3 = _mm_castsi128_ps(*xmm_crc3); ps_crc1 = _mm_castsi128_ps(*xmm_crc1); ps_res31 = _mm_xor_ps(ps_crc3, ps_crc1); *xmm_crc2 = *xmm_crc0; *xmm_crc0 = _mm_clmulepi64_si128(*xmm_crc0, xmm_fold4, 0x01); *xmm_crc2 = _mm_clmulepi64_si128(*xmm_crc2, xmm_fold4, 0x10); ps_crc0 = _mm_castsi128_ps(*xmm_crc0); ps_crc2 = _mm_castsi128_ps(*xmm_crc2); ps_res20 = _mm_xor_ps(ps_crc0, ps_crc2); *xmm_crc0 = x_tmp2; *xmm_crc1 = x_tmp3; *xmm_crc2 = _mm_castps_si128(ps_res20); *xmm_crc3 = _mm_castps_si128(ps_res31); } CLMUL static void fold_3(__m128i *xmm_crc0, __m128i *xmm_crc1, __m128i *xmm_crc2, __m128i *xmm_crc3) { const __m128i xmm_fold4 = _mm_set_epi32( 0x00000001, 0x54442bd4, 0x00000001, 0xc6e41596); __m128i x_tmp3; __m128 ps_crc0, ps_crc1, ps_crc2, ps_crc3, ps_res32, ps_res21, ps_res10; x_tmp3 = *xmm_crc3; *xmm_crc3 = *xmm_crc2; *xmm_crc2 = _mm_clmulepi64_si128(*xmm_crc2, xmm_fold4, 0x01); *xmm_crc3 = _mm_clmulepi64_si128(*xmm_crc3, xmm_fold4, 0x10); ps_crc2 = _mm_castsi128_ps(*xmm_crc2); ps_crc3 = _mm_castsi128_ps(*xmm_crc3); ps_res32 = _mm_xor_ps(ps_crc2, ps_crc3); *xmm_crc2 = *xmm_crc1; *xmm_crc1 = _mm_clmulepi64_si128(*xmm_crc1, xmm_fold4, 0x01); *xmm_crc2 = _mm_clmulepi64_si128(*xmm_crc2, xmm_fold4, 0x10); ps_crc1 = _mm_castsi128_ps(*xmm_crc1); ps_crc2 = _mm_castsi128_ps(*xmm_crc2); ps_res21 = _mm_xor_ps(ps_crc1, ps_crc2); *xmm_crc1 = *xmm_crc0; *xmm_crc0 = _mm_clmulepi64_si128(*xmm_crc0, xmm_fold4, 0x01); *xmm_crc1 = _mm_clmulepi64_si128(*xmm_crc1, xmm_fold4, 0x10); ps_crc0 = _mm_castsi128_ps(*xmm_crc0); ps_crc1 = _mm_castsi128_ps(*xmm_crc1); ps_res10 = _mm_xor_ps(ps_crc0, ps_crc1); *xmm_crc0 = x_tmp3; *xmm_crc1 = _mm_castps_si128(ps_res10); *xmm_crc2 = _mm_castps_si128(ps_res21); *xmm_crc3 = _mm_castps_si128(ps_res32); } CLMUL static void fold_4(__m128i *xmm_crc0, __m128i *xmm_crc1, __m128i *xmm_crc2, __m128i *xmm_crc3) { const __m128i xmm_fold4 = _mm_set_epi32( 0x00000001, 0x54442bd4, 0x00000001, 0xc6e41596); __m128i x_tmp0, x_tmp1, x_tmp2, x_tmp3; __m128 ps_crc0, ps_crc1, ps_crc2, ps_crc3; __m128 ps_t0, ps_t1, ps_t2, ps_t3; __m128 ps_res0, ps_res1, ps_res2, ps_res3; x_tmp0 = *xmm_crc0; x_tmp1 = *xmm_crc1; x_tmp2 = *xmm_crc2; x_tmp3 = *xmm_crc3; *xmm_crc0 = _mm_clmulepi64_si128(*xmm_crc0, xmm_fold4, 0x01); x_tmp0 = _mm_clmulepi64_si128(x_tmp0, xmm_fold4, 0x10); ps_crc0 = _mm_castsi128_ps(*xmm_crc0); ps_t0 = _mm_castsi128_ps(x_tmp0); ps_res0 = _mm_xor_ps(ps_crc0, ps_t0); *xmm_crc1 = _mm_clmulepi64_si128(*xmm_crc1, xmm_fold4, 0x01); x_tmp1 = _mm_clmulepi64_si128(x_tmp1, xmm_fold4, 0x10); ps_crc1 = _mm_castsi128_ps(*xmm_crc1); ps_t1 = _mm_castsi128_ps(x_tmp1); ps_res1 = _mm_xor_ps(ps_crc1, ps_t1); *xmm_crc2 = _mm_clmulepi64_si128(*xmm_crc2, xmm_fold4, 0x01); x_tmp2 = _mm_clmulepi64_si128(x_tmp2, xmm_fold4, 0x10); ps_crc2 = _mm_castsi128_ps(*xmm_crc2); ps_t2 = _mm_castsi128_ps(x_tmp2); ps_res2 = _mm_xor_ps(ps_crc2, ps_t2); *xmm_crc3 = _mm_clmulepi64_si128(*xmm_crc3, xmm_fold4, 0x01); x_tmp3 = _mm_clmulepi64_si128(x_tmp3, xmm_fold4, 0x10); ps_crc3 = _mm_castsi128_ps(*xmm_crc3); ps_t3 = _mm_castsi128_ps(x_tmp3); ps_res3 = _mm_xor_ps(ps_crc3, ps_t3); *xmm_crc0 = _mm_castps_si128(ps_res0); *xmm_crc1 = _mm_castps_si128(ps_res1); *xmm_crc2 = _mm_castps_si128(ps_res2); *xmm_crc3 = _mm_castps_si128(ps_res3); } static const unsigned ALIGNED_(32) pshufb_shf_table[60] = { 0x84838281, 0x88878685, 0x8c8b8a89, 0x008f8e8d, /* shl 15 (16 - 1)/shr1 */ 0x85848382, 0x89888786, 0x8d8c8b8a, 0x01008f8e, /* shl 14 (16 - 3)/shr2 */ 0x86858483, 0x8a898887, 0x8e8d8c8b, 0x0201008f, /* shl 13 (16 - 4)/shr3 */ 0x87868584, 0x8b8a8988, 0x8f8e8d8c, 0x03020100, /* shl 12 (16 - 4)/shr4 */ 0x88878685, 0x8c8b8a89, 0x008f8e8d, 0x04030201, /* shl 11 (16 - 5)/shr5 */ 0x89888786, 0x8d8c8b8a, 0x01008f8e, 0x05040302, /* shl 10 (16 - 6)/shr6 */ 0x8a898887, 0x8e8d8c8b, 0x0201008f, 0x06050403, /* shl 9 (16 - 7)/shr7 */ 0x8b8a8988, 0x8f8e8d8c, 0x03020100, 0x07060504, /* shl 8 (16 - 8)/shr8 */ 0x8c8b8a89, 0x008f8e8d, 0x04030201, 0x08070605, /* shl 7 (16 - 9)/shr9 */ 0x8d8c8b8a, 0x01008f8e, 0x05040302, 0x09080706, /* shl 6 (16 -10)/shr10*/ 0x8e8d8c8b, 0x0201008f, 0x06050403, 0x0a090807, /* shl 5 (16 -11)/shr11*/ 0x8f8e8d8c, 0x03020100, 0x07060504, 0x0b0a0908, /* shl 4 (16 -12)/shr12*/ 0x008f8e8d, 0x04030201, 0x08070605, 0x0c0b0a09, /* shl 3 (16 -13)/shr13*/ 0x01008f8e, 0x05040302, 0x09080706, 0x0d0c0b0a, /* shl 2 (16 -14)/shr14*/ 0x0201008f, 0x06050403, 0x0a090807, 0x0e0d0c0b /* shl 1 (16 -15)/shr15*/ }; CLMUL static void partial_fold(const size_t len, __m128i *xmm_crc0, __m128i *xmm_crc1, __m128i *xmm_crc2, __m128i *xmm_crc3, __m128i *xmm_crc_part) { const __m128i xmm_fold4 = _mm_set_epi32( 0x00000001, 0x54442bd4, 0x00000001, 0xc6e41596); const __m128i xmm_mask3 = _mm_set1_epi32(0x80808080); __m128i xmm_shl, xmm_shr, xmm_tmp1, xmm_tmp2, xmm_tmp3; __m128i xmm_a0_0, xmm_a0_1; __m128 ps_crc3, psa0_0, psa0_1, ps_res; xmm_shl = _mm_load_si128((__m128i *)pshufb_shf_table + (len - 1)); xmm_shr = xmm_shl; xmm_shr = _mm_xor_si128(xmm_shr, xmm_mask3); xmm_a0_0 = _mm_shuffle_epi8(*xmm_crc0, xmm_shl); *xmm_crc0 = _mm_shuffle_epi8(*xmm_crc0, xmm_shr); xmm_tmp1 = _mm_shuffle_epi8(*xmm_crc1, xmm_shl); *xmm_crc0 = _mm_or_si128(*xmm_crc0, xmm_tmp1); *xmm_crc1 = _mm_shuffle_epi8(*xmm_crc1, xmm_shr); xmm_tmp2 = _mm_shuffle_epi8(*xmm_crc2, xmm_shl); *xmm_crc1 = _mm_or_si128(*xmm_crc1, xmm_tmp2); *xmm_crc2 = _mm_shuffle_epi8(*xmm_crc2, xmm_shr); xmm_tmp3 = _mm_shuffle_epi8(*xmm_crc3, xmm_shl); *xmm_crc2 = _mm_or_si128(*xmm_crc2, xmm_tmp3); *xmm_crc3 = _mm_shuffle_epi8(*xmm_crc3, xmm_shr); *xmm_crc_part = _mm_shuffle_epi8(*xmm_crc_part, xmm_shl); *xmm_crc3 = _mm_or_si128(*xmm_crc3, *xmm_crc_part); xmm_a0_1 = _mm_clmulepi64_si128(xmm_a0_0, xmm_fold4, 0x10); xmm_a0_0 = _mm_clmulepi64_si128(xmm_a0_0, xmm_fold4, 0x01); ps_crc3 = _mm_castsi128_ps(*xmm_crc3); psa0_0 = _mm_castsi128_ps(xmm_a0_0); psa0_1 = _mm_castsi128_ps(xmm_a0_1); ps_res = _mm_xor_ps(ps_crc3, psa0_0); ps_res = _mm_xor_ps(ps_res, psa0_1); *xmm_crc3 = _mm_castps_si128(ps_res); } static const unsigned ALIGNED_(16) crc_k[] = { 0xccaa009e, 0x00000000, /* rk1 */ 0x751997d0, 0x00000001, /* rk2 */ 0xccaa009e, 0x00000000, /* rk5 */ 0x63cd6124, 0x00000001, /* rk6 */ 0xf7011640, 0x00000001, /* rk7 */ 0xdb710640, 0x00000001 /* rk8 */ }; static const unsigned ALIGNED_(16) crc_mask[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000 }; static const unsigned ALIGNED_(16) crc_mask2[4] = { 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; #define ONCE(op) if(first) { \ first = 0; \ (op); \ } /* * somewhat surprisingly the "naive" way of doing this, ie. with a flag and a cond. branch, * is consistently ~5 % faster on average than the implied-recommended branchless way (always xor, * always zero xmm_initial). Guess speculative execution and branch prediction got the better of * yet another "optimization tip". */ #define XOR_INITIAL(where) ONCE(where = _mm_xor_si128(where, xmm_initial)) CLMUL static uint32_t crc32_clmul(const uint8_t *src, long len, uint32_t initial_crc) { unsigned long algn_diff; __m128i xmm_t0, xmm_t1, xmm_t2, xmm_t3; __m128i xmm_initial = _mm_cvtsi32_si128(initial_crc); __m128i xmm_crc0 = _mm_cvtsi32_si128(0x9db42487); __m128i xmm_crc1 = _mm_setzero_si128(); __m128i xmm_crc2 = _mm_setzero_si128(); __m128i xmm_crc3 = _mm_setzero_si128(); __m128i xmm_crc_part; int first = 1; /* fold 512 to 32 step variable declarations for ISO-C90 compat. */ const __m128i xmm_mask = _mm_load_si128((__m128i *)crc_mask); const __m128i xmm_mask2 = _mm_load_si128((__m128i *)crc_mask2); uint32_t crc; __m128i x_tmp0, x_tmp1, x_tmp2, crc_fold; if (len < 16) { if (len == 0) return initial_crc; if (len < 4) { /* * no idea how to do this for <4 bytes, delegate to classic impl. */ uint32_t crc = ~initial_crc; switch (len) { case 3: crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *src++]; // fallthrough case 2: crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *src++]; // fallthrough case 1: crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *src++]; } return ~crc; } xmm_crc_part = _mm_loadu_si128((__m128i *)src); XOR_INITIAL(xmm_crc_part); goto partial; } /* this alignment computation would be wrong for len<16 handled above */ algn_diff = (0 - (uintptr_t)src) & 0xF; if (algn_diff) { xmm_crc_part = _mm_loadu_si128((__m128i *)src); XOR_INITIAL(xmm_crc_part); src += algn_diff; len -= algn_diff; partial_fold(algn_diff, &xmm_crc0, &xmm_crc1, &xmm_crc2, &xmm_crc3, &xmm_crc_part); } while ((len -= 64) >= 0) { xmm_t0 = _mm_load_si128((__m128i *)src); xmm_t1 = _mm_load_si128((__m128i *)src + 1); xmm_t2 = _mm_load_si128((__m128i *)src + 2); xmm_t3 = _mm_load_si128((__m128i *)src + 3); XOR_INITIAL(xmm_t0); fold_4(&xmm_crc0, &xmm_crc1, &xmm_crc2, &xmm_crc3); xmm_crc0 = _mm_xor_si128(xmm_crc0, xmm_t0); xmm_crc1 = _mm_xor_si128(xmm_crc1, xmm_t1); xmm_crc2 = _mm_xor_si128(xmm_crc2, xmm_t2); xmm_crc3 = _mm_xor_si128(xmm_crc3, xmm_t3); src += 64; } /* * len = num bytes left - 64 */ if (len + 16 >= 0) { len += 16; xmm_t0 = _mm_load_si128((__m128i *)src); xmm_t1 = _mm_load_si128((__m128i *)src + 1); xmm_t2 = _mm_load_si128((__m128i *)src + 2); XOR_INITIAL(xmm_t0); fold_3(&xmm_crc0, &xmm_crc1, &xmm_crc2, &xmm_crc3); xmm_crc1 = _mm_xor_si128(xmm_crc1, xmm_t0); xmm_crc2 = _mm_xor_si128(xmm_crc2, xmm_t1); xmm_crc3 = _mm_xor_si128(xmm_crc3, xmm_t2); if (len == 0) goto done; xmm_crc_part = _mm_load_si128((__m128i *)src + 3); } else if (len + 32 >= 0) { len += 32; xmm_t0 = _mm_load_si128((__m128i *)src); xmm_t1 = _mm_load_si128((__m128i *)src + 1); XOR_INITIAL(xmm_t0); fold_2(&xmm_crc0, &xmm_crc1, &xmm_crc2, &xmm_crc3); xmm_crc2 = _mm_xor_si128(xmm_crc2, xmm_t0); xmm_crc3 = _mm_xor_si128(xmm_crc3, xmm_t1); if (len == 0) goto done; xmm_crc_part = _mm_load_si128((__m128i *)src + 2); } else if (len + 48 >= 0) { len += 48; xmm_t0 = _mm_load_si128((__m128i *)src); XOR_INITIAL(xmm_t0); fold_1(&xmm_crc0, &xmm_crc1, &xmm_crc2, &xmm_crc3); xmm_crc3 = _mm_xor_si128(xmm_crc3, xmm_t0); if (len == 0) goto done; xmm_crc_part = _mm_load_si128((__m128i *)src + 1); } else { len += 64; if (len == 0) goto done; xmm_crc_part = _mm_load_si128((__m128i *)src); XOR_INITIAL(xmm_crc_part); } partial: partial_fold(len, &xmm_crc0, &xmm_crc1, &xmm_crc2, &xmm_crc3, &xmm_crc_part); done: (void)0; /* fold 512 to 32 */ /* * k1 */ crc_fold = _mm_load_si128((__m128i *)crc_k); x_tmp0 = _mm_clmulepi64_si128(xmm_crc0, crc_fold, 0x10); xmm_crc0 = _mm_clmulepi64_si128(xmm_crc0, crc_fold, 0x01); xmm_crc1 = _mm_xor_si128(xmm_crc1, x_tmp0); xmm_crc1 = _mm_xor_si128(xmm_crc1, xmm_crc0); x_tmp1 = _mm_clmulepi64_si128(xmm_crc1, crc_fold, 0x10); xmm_crc1 = _mm_clmulepi64_si128(xmm_crc1, crc_fold, 0x01); xmm_crc2 = _mm_xor_si128(xmm_crc2, x_tmp1); xmm_crc2 = _mm_xor_si128(xmm_crc2, xmm_crc1); x_tmp2 = _mm_clmulepi64_si128(xmm_crc2, crc_fold, 0x10); xmm_crc2 = _mm_clmulepi64_si128(xmm_crc2, crc_fold, 0x01); xmm_crc3 = _mm_xor_si128(xmm_crc3, x_tmp2); xmm_crc3 = _mm_xor_si128(xmm_crc3, xmm_crc2); /* * k5 */ crc_fold = _mm_load_si128((__m128i *)crc_k + 1); xmm_crc0 = xmm_crc3; xmm_crc3 = _mm_clmulepi64_si128(xmm_crc3, crc_fold, 0); xmm_crc0 = _mm_srli_si128(xmm_crc0, 8); xmm_crc3 = _mm_xor_si128(xmm_crc3, xmm_crc0); xmm_crc0 = xmm_crc3; xmm_crc3 = _mm_slli_si128(xmm_crc3, 4); xmm_crc3 = _mm_clmulepi64_si128(xmm_crc3, crc_fold, 0x10); xmm_crc3 = _mm_xor_si128(xmm_crc3, xmm_crc0); xmm_crc3 = _mm_and_si128(xmm_crc3, xmm_mask2); /* * k7 */ xmm_crc1 = xmm_crc3; xmm_crc2 = xmm_crc3; crc_fold = _mm_load_si128((__m128i *)crc_k + 2); xmm_crc3 = _mm_clmulepi64_si128(xmm_crc3, crc_fold, 0); xmm_crc3 = _mm_xor_si128(xmm_crc3, xmm_crc2); xmm_crc3 = _mm_and_si128(xmm_crc3, xmm_mask); xmm_crc2 = xmm_crc3; xmm_crc3 = _mm_clmulepi64_si128(xmm_crc3, crc_fold, 0x10); xmm_crc3 = _mm_xor_si128(xmm_crc3, xmm_crc2); xmm_crc3 = _mm_xor_si128(xmm_crc3, xmm_crc1); /* * could just as well write xmm_crc3[2], doing a movaps and truncating, but * no real advantage - it's a tiny bit slower per call, while no additional CPUs * would be supported by only requiring SSSE3 and CLMUL instead of SSE4.1 + CLMUL */ crc = _mm_extract_epi32(xmm_crc3, 2); return ~crc; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/crc32_dispatch.c0000644000076500000240000000542214601576577021073 0ustar00twstaff /* always compile slice by 8 as a runtime fallback */ #include "crc32_slice_by_8.c" #ifdef __GNUC__ /* * GCC 4.4(.7) has a bug that causes it to recurse infinitely if an unknown option * is pushed onto the options stack. GCC 4.5 was not tested, so is excluded as well. * GCC 4.6 is known good. */ #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) /* * clang also has or had GCC bug #56298 explained below, but doesn't support * target attributes or the options stack. So we disable this faster code path for clang. */ #ifndef __clang__ /* * While OpenBSD uses GCC, they don't have Intel intrinsics, so we can't compile this code * on OpenBSD. */ #ifndef __OpenBSD__ #if __x86_64__ /* * Because we don't want a configure script we need compiler-dependent pre-defined macros for detecting this, * also some compiler-dependent stuff to invoke SSE modes and align things. */ #define FOLDING_CRC /* * SSE2 misses _mm_shuffle_epi32, and _mm_extract_epi32 * SSSE3 added _mm_shuffle_epi32 * SSE4.1 added _mm_extract_epi32 * Also requires CLMUL of course (all AES-NI CPUs have it) * Note that there are no CPUs with AES-NI/CLMUL but without SSE4.1 */ #define CLMUL __attribute__ ((target ("pclmul,sse4.1"))) #define ALIGNED_(n) __attribute__ ((aligned(n))) /* * Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56298 * These are taken from GCC 6.x, so apparently the above bug has been resolved in that version, * but it still affects widely used GCC 4.x. * Part 2 of 2 follows below. */ #ifndef __PCLMUL__ #pragma GCC push_options #pragma GCC target("pclmul") #define __BORG_DISABLE_PCLMUL__ #endif #ifndef __SSE3__ #pragma GCC push_options #pragma GCC target("sse3") #define __BORG_DISABLE_SSE3__ #endif #ifndef __SSSE3__ #pragma GCC push_options #pragma GCC target("ssse3") #define __BORG_DISABLE_SSSE3__ #endif #ifndef __SSE4_1__ #pragma GCC push_options #pragma GCC target("sse4.1") #define __BORG_DISABLE_SSE4_1__ #endif #endif /* if __x86_64__ */ #endif /* ifndef __OpenBSD__ */ #endif /* ifndef __clang__ */ #endif /* __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) */ #endif /* ifdef __GNUC__ */ #ifdef FOLDING_CRC #include "crc32_clmul.c" #else static uint32_t crc32_clmul(const uint8_t *src, long len, uint32_t initial_crc) { (void)src; (void)len; (void)initial_crc; assert(0); return 0; } static int have_clmul(void) { return 0; } #endif /* * Part 2 of 2 of the GCC workaround. */ #ifdef __BORG_DISABLE_PCLMUL__ #undef __BORG_DISABLE_PCLMUL__ #pragma GCC pop_options #endif #ifdef __BORG_DISABLE_SSE3__ #undef __BORG_DISABLE_SSE3__ #pragma GCC pop_options #endif #ifdef __BORG_DISABLE_SSSE3__ #undef __BORG_DISABLE_SSSE3__ #pragma GCC pop_options #endif #ifdef __BORG_DISABLE_SSE4_1__ #undef __BORG_DISABLE_SSE4_1__ #pragma GCC pop_options #endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/crc32_slice_by_8.c0000644000076500000240000006634414601576577021326 0ustar00twstaff// ////////////////////////////////////////////////////////// // Crc32.h // Copyright (c) 2011-2016 Stephan Brumme. All rights reserved. // see http://create.stephan-brumme.com/disclaimer.html // // uint8_t, uint32_t, int32_t #include // size_t #include #include "../_endian.h" /// compute CRC32 (Slicing-by-8 algorithm), unroll inner loop 4 times uint32_t crc32_4x8bytes(const void* data, size_t length, uint32_t previousCrc32); // ////////////////////////////////////////////////////////// // Crc32.cpp // Copyright (c) 2011-2016 Stephan Brumme. All rights reserved. // Slicing-by-16 contributed by Bulat Ziganshin // Tableless bytewise CRC contributed by Hagai Gold // see http://create.stephan-brumme.com/disclaimer.html // /// zlib's CRC32 polynomial const uint32_t Polynomial = 0xEDB88320; // ////////////////////////////////////////////////////////// // constants /// look-up table, already declared above const uint32_t Crc32Lookup[8][256] = { //// same algorithm as crc32_bitwise //for (int i = 0; i <= 0xFF; i++) //{ // uint32_t crc = i; // for (int j = 0; j < 8; j++) // crc = (crc >> 1) ^ ((crc & 1) * Polynomial); // Crc32Lookup[0][i] = crc; //} //// ... and the following slicing-by-8 algorithm (from Intel): //// http://www.intel.com/technology/comms/perfnet/download/CRC_generators.pdf //// http://sourceforge.net/projects/slicing-by-8/ //for (int slice = 1; slice < MaxSlice; slice++) // Crc32Lookup[slice][i] = (Crc32Lookup[slice - 1][i] >> 8) ^ Crc32Lookup[0][Crc32Lookup[slice - 1][i] & 0xFF]; { // note: the first number of every second row corresponds to the half-byte look-up table ! 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3, 0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7, 0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B, 0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F, 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433, 0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457, 0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB, 0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F, 0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683, 0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7, 0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B, 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F, 0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713, 0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777, 0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB, 0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF, 0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D, } ,{ 0x00000000,0x191B3141,0x32366282,0x2B2D53C3,0x646CC504,0x7D77F445,0x565AA786,0x4F4196C7, 0xC8D98A08,0xD1C2BB49,0xFAEFE88A,0xE3F4D9CB,0xACB54F0C,0xB5AE7E4D,0x9E832D8E,0x87981CCF, 0x4AC21251,0x53D92310,0x78F470D3,0x61EF4192,0x2EAED755,0x37B5E614,0x1C98B5D7,0x05838496, 0x821B9859,0x9B00A918,0xB02DFADB,0xA936CB9A,0xE6775D5D,0xFF6C6C1C,0xD4413FDF,0xCD5A0E9E, 0x958424A2,0x8C9F15E3,0xA7B24620,0xBEA97761,0xF1E8E1A6,0xE8F3D0E7,0xC3DE8324,0xDAC5B265, 0x5D5DAEAA,0x44469FEB,0x6F6BCC28,0x7670FD69,0x39316BAE,0x202A5AEF,0x0B07092C,0x121C386D, 0xDF4636F3,0xC65D07B2,0xED705471,0xF46B6530,0xBB2AF3F7,0xA231C2B6,0x891C9175,0x9007A034, 0x179FBCFB,0x0E848DBA,0x25A9DE79,0x3CB2EF38,0x73F379FF,0x6AE848BE,0x41C51B7D,0x58DE2A3C, 0xF0794F05,0xE9627E44,0xC24F2D87,0xDB541CC6,0x94158A01,0x8D0EBB40,0xA623E883,0xBF38D9C2, 0x38A0C50D,0x21BBF44C,0x0A96A78F,0x138D96CE,0x5CCC0009,0x45D73148,0x6EFA628B,0x77E153CA, 0xBABB5D54,0xA3A06C15,0x888D3FD6,0x91960E97,0xDED79850,0xC7CCA911,0xECE1FAD2,0xF5FACB93, 0x7262D75C,0x6B79E61D,0x4054B5DE,0x594F849F,0x160E1258,0x0F152319,0x243870DA,0x3D23419B, 0x65FD6BA7,0x7CE65AE6,0x57CB0925,0x4ED03864,0x0191AEA3,0x188A9FE2,0x33A7CC21,0x2ABCFD60, 0xAD24E1AF,0xB43FD0EE,0x9F12832D,0x8609B26C,0xC94824AB,0xD05315EA,0xFB7E4629,0xE2657768, 0x2F3F79F6,0x362448B7,0x1D091B74,0x04122A35,0x4B53BCF2,0x52488DB3,0x7965DE70,0x607EEF31, 0xE7E6F3FE,0xFEFDC2BF,0xD5D0917C,0xCCCBA03D,0x838A36FA,0x9A9107BB,0xB1BC5478,0xA8A76539, 0x3B83984B,0x2298A90A,0x09B5FAC9,0x10AECB88,0x5FEF5D4F,0x46F46C0E,0x6DD93FCD,0x74C20E8C, 0xF35A1243,0xEA412302,0xC16C70C1,0xD8774180,0x9736D747,0x8E2DE606,0xA500B5C5,0xBC1B8484, 0x71418A1A,0x685ABB5B,0x4377E898,0x5A6CD9D9,0x152D4F1E,0x0C367E5F,0x271B2D9C,0x3E001CDD, 0xB9980012,0xA0833153,0x8BAE6290,0x92B553D1,0xDDF4C516,0xC4EFF457,0xEFC2A794,0xF6D996D5, 0xAE07BCE9,0xB71C8DA8,0x9C31DE6B,0x852AEF2A,0xCA6B79ED,0xD37048AC,0xF85D1B6F,0xE1462A2E, 0x66DE36E1,0x7FC507A0,0x54E85463,0x4DF36522,0x02B2F3E5,0x1BA9C2A4,0x30849167,0x299FA026, 0xE4C5AEB8,0xFDDE9FF9,0xD6F3CC3A,0xCFE8FD7B,0x80A96BBC,0x99B25AFD,0xB29F093E,0xAB84387F, 0x2C1C24B0,0x350715F1,0x1E2A4632,0x07317773,0x4870E1B4,0x516BD0F5,0x7A468336,0x635DB277, 0xCBFAD74E,0xD2E1E60F,0xF9CCB5CC,0xE0D7848D,0xAF96124A,0xB68D230B,0x9DA070C8,0x84BB4189, 0x03235D46,0x1A386C07,0x31153FC4,0x280E0E85,0x674F9842,0x7E54A903,0x5579FAC0,0x4C62CB81, 0x8138C51F,0x9823F45E,0xB30EA79D,0xAA1596DC,0xE554001B,0xFC4F315A,0xD7626299,0xCE7953D8, 0x49E14F17,0x50FA7E56,0x7BD72D95,0x62CC1CD4,0x2D8D8A13,0x3496BB52,0x1FBBE891,0x06A0D9D0, 0x5E7EF3EC,0x4765C2AD,0x6C48916E,0x7553A02F,0x3A1236E8,0x230907A9,0x0824546A,0x113F652B, 0x96A779E4,0x8FBC48A5,0xA4911B66,0xBD8A2A27,0xF2CBBCE0,0xEBD08DA1,0xC0FDDE62,0xD9E6EF23, 0x14BCE1BD,0x0DA7D0FC,0x268A833F,0x3F91B27E,0x70D024B9,0x69CB15F8,0x42E6463B,0x5BFD777A, 0xDC656BB5,0xC57E5AF4,0xEE530937,0xF7483876,0xB809AEB1,0xA1129FF0,0x8A3FCC33,0x9324FD72, }, { 0x00000000,0x01C26A37,0x0384D46E,0x0246BE59,0x0709A8DC,0x06CBC2EB,0x048D7CB2,0x054F1685, 0x0E1351B8,0x0FD13B8F,0x0D9785D6,0x0C55EFE1,0x091AF964,0x08D89353,0x0A9E2D0A,0x0B5C473D, 0x1C26A370,0x1DE4C947,0x1FA2771E,0x1E601D29,0x1B2F0BAC,0x1AED619B,0x18ABDFC2,0x1969B5F5, 0x1235F2C8,0x13F798FF,0x11B126A6,0x10734C91,0x153C5A14,0x14FE3023,0x16B88E7A,0x177AE44D, 0x384D46E0,0x398F2CD7,0x3BC9928E,0x3A0BF8B9,0x3F44EE3C,0x3E86840B,0x3CC03A52,0x3D025065, 0x365E1758,0x379C7D6F,0x35DAC336,0x3418A901,0x3157BF84,0x3095D5B3,0x32D36BEA,0x331101DD, 0x246BE590,0x25A98FA7,0x27EF31FE,0x262D5BC9,0x23624D4C,0x22A0277B,0x20E69922,0x2124F315, 0x2A78B428,0x2BBADE1F,0x29FC6046,0x283E0A71,0x2D711CF4,0x2CB376C3,0x2EF5C89A,0x2F37A2AD, 0x709A8DC0,0x7158E7F7,0x731E59AE,0x72DC3399,0x7793251C,0x76514F2B,0x7417F172,0x75D59B45, 0x7E89DC78,0x7F4BB64F,0x7D0D0816,0x7CCF6221,0x798074A4,0x78421E93,0x7A04A0CA,0x7BC6CAFD, 0x6CBC2EB0,0x6D7E4487,0x6F38FADE,0x6EFA90E9,0x6BB5866C,0x6A77EC5B,0x68315202,0x69F33835, 0x62AF7F08,0x636D153F,0x612BAB66,0x60E9C151,0x65A6D7D4,0x6464BDE3,0x662203BA,0x67E0698D, 0x48D7CB20,0x4915A117,0x4B531F4E,0x4A917579,0x4FDE63FC,0x4E1C09CB,0x4C5AB792,0x4D98DDA5, 0x46C49A98,0x4706F0AF,0x45404EF6,0x448224C1,0x41CD3244,0x400F5873,0x4249E62A,0x438B8C1D, 0x54F16850,0x55330267,0x5775BC3E,0x56B7D609,0x53F8C08C,0x523AAABB,0x507C14E2,0x51BE7ED5, 0x5AE239E8,0x5B2053DF,0x5966ED86,0x58A487B1,0x5DEB9134,0x5C29FB03,0x5E6F455A,0x5FAD2F6D, 0xE1351B80,0xE0F771B7,0xE2B1CFEE,0xE373A5D9,0xE63CB35C,0xE7FED96B,0xE5B86732,0xE47A0D05, 0xEF264A38,0xEEE4200F,0xECA29E56,0xED60F461,0xE82FE2E4,0xE9ED88D3,0xEBAB368A,0xEA695CBD, 0xFD13B8F0,0xFCD1D2C7,0xFE976C9E,0xFF5506A9,0xFA1A102C,0xFBD87A1B,0xF99EC442,0xF85CAE75, 0xF300E948,0xF2C2837F,0xF0843D26,0xF1465711,0xF4094194,0xF5CB2BA3,0xF78D95FA,0xF64FFFCD, 0xD9785D60,0xD8BA3757,0xDAFC890E,0xDB3EE339,0xDE71F5BC,0xDFB39F8B,0xDDF521D2,0xDC374BE5, 0xD76B0CD8,0xD6A966EF,0xD4EFD8B6,0xD52DB281,0xD062A404,0xD1A0CE33,0xD3E6706A,0xD2241A5D, 0xC55EFE10,0xC49C9427,0xC6DA2A7E,0xC7184049,0xC25756CC,0xC3953CFB,0xC1D382A2,0xC011E895, 0xCB4DAFA8,0xCA8FC59F,0xC8C97BC6,0xC90B11F1,0xCC440774,0xCD866D43,0xCFC0D31A,0xCE02B92D, 0x91AF9640,0x906DFC77,0x922B422E,0x93E92819,0x96A63E9C,0x976454AB,0x9522EAF2,0x94E080C5, 0x9FBCC7F8,0x9E7EADCF,0x9C381396,0x9DFA79A1,0x98B56F24,0x99770513,0x9B31BB4A,0x9AF3D17D, 0x8D893530,0x8C4B5F07,0x8E0DE15E,0x8FCF8B69,0x8A809DEC,0x8B42F7DB,0x89044982,0x88C623B5, 0x839A6488,0x82580EBF,0x801EB0E6,0x81DCDAD1,0x8493CC54,0x8551A663,0x8717183A,0x86D5720D, 0xA9E2D0A0,0xA820BA97,0xAA6604CE,0xABA46EF9,0xAEEB787C,0xAF29124B,0xAD6FAC12,0xACADC625, 0xA7F18118,0xA633EB2F,0xA4755576,0xA5B73F41,0xA0F829C4,0xA13A43F3,0xA37CFDAA,0xA2BE979D, 0xB5C473D0,0xB40619E7,0xB640A7BE,0xB782CD89,0xB2CDDB0C,0xB30FB13B,0xB1490F62,0xB08B6555, 0xBBD72268,0xBA15485F,0xB853F606,0xB9919C31,0xBCDE8AB4,0xBD1CE083,0xBF5A5EDA,0xBE9834ED, }, { 0x00000000,0xB8BC6765,0xAA09C88B,0x12B5AFEE,0x8F629757,0x37DEF032,0x256B5FDC,0x9DD738B9, 0xC5B428EF,0x7D084F8A,0x6FBDE064,0xD7018701,0x4AD6BFB8,0xF26AD8DD,0xE0DF7733,0x58631056, 0x5019579F,0xE8A530FA,0xFA109F14,0x42ACF871,0xDF7BC0C8,0x67C7A7AD,0x75720843,0xCDCE6F26, 0x95AD7F70,0x2D111815,0x3FA4B7FB,0x8718D09E,0x1ACFE827,0xA2738F42,0xB0C620AC,0x087A47C9, 0xA032AF3E,0x188EC85B,0x0A3B67B5,0xB28700D0,0x2F503869,0x97EC5F0C,0x8559F0E2,0x3DE59787, 0x658687D1,0xDD3AE0B4,0xCF8F4F5A,0x7733283F,0xEAE41086,0x525877E3,0x40EDD80D,0xF851BF68, 0xF02BF8A1,0x48979FC4,0x5A22302A,0xE29E574F,0x7F496FF6,0xC7F50893,0xD540A77D,0x6DFCC018, 0x359FD04E,0x8D23B72B,0x9F9618C5,0x272A7FA0,0xBAFD4719,0x0241207C,0x10F48F92,0xA848E8F7, 0x9B14583D,0x23A83F58,0x311D90B6,0x89A1F7D3,0x1476CF6A,0xACCAA80F,0xBE7F07E1,0x06C36084, 0x5EA070D2,0xE61C17B7,0xF4A9B859,0x4C15DF3C,0xD1C2E785,0x697E80E0,0x7BCB2F0E,0xC377486B, 0xCB0D0FA2,0x73B168C7,0x6104C729,0xD9B8A04C,0x446F98F5,0xFCD3FF90,0xEE66507E,0x56DA371B, 0x0EB9274D,0xB6054028,0xA4B0EFC6,0x1C0C88A3,0x81DBB01A,0x3967D77F,0x2BD27891,0x936E1FF4, 0x3B26F703,0x839A9066,0x912F3F88,0x299358ED,0xB4446054,0x0CF80731,0x1E4DA8DF,0xA6F1CFBA, 0xFE92DFEC,0x462EB889,0x549B1767,0xEC277002,0x71F048BB,0xC94C2FDE,0xDBF98030,0x6345E755, 0x6B3FA09C,0xD383C7F9,0xC1366817,0x798A0F72,0xE45D37CB,0x5CE150AE,0x4E54FF40,0xF6E89825, 0xAE8B8873,0x1637EF16,0x048240F8,0xBC3E279D,0x21E91F24,0x99557841,0x8BE0D7AF,0x335CB0CA, 0xED59B63B,0x55E5D15E,0x47507EB0,0xFFEC19D5,0x623B216C,0xDA874609,0xC832E9E7,0x708E8E82, 0x28ED9ED4,0x9051F9B1,0x82E4565F,0x3A58313A,0xA78F0983,0x1F336EE6,0x0D86C108,0xB53AA66D, 0xBD40E1A4,0x05FC86C1,0x1749292F,0xAFF54E4A,0x322276F3,0x8A9E1196,0x982BBE78,0x2097D91D, 0x78F4C94B,0xC048AE2E,0xD2FD01C0,0x6A4166A5,0xF7965E1C,0x4F2A3979,0x5D9F9697,0xE523F1F2, 0x4D6B1905,0xF5D77E60,0xE762D18E,0x5FDEB6EB,0xC2098E52,0x7AB5E937,0x680046D9,0xD0BC21BC, 0x88DF31EA,0x3063568F,0x22D6F961,0x9A6A9E04,0x07BDA6BD,0xBF01C1D8,0xADB46E36,0x15080953, 0x1D724E9A,0xA5CE29FF,0xB77B8611,0x0FC7E174,0x9210D9CD,0x2AACBEA8,0x38191146,0x80A57623, 0xD8C66675,0x607A0110,0x72CFAEFE,0xCA73C99B,0x57A4F122,0xEF189647,0xFDAD39A9,0x45115ECC, 0x764DEE06,0xCEF18963,0xDC44268D,0x64F841E8,0xF92F7951,0x41931E34,0x5326B1DA,0xEB9AD6BF, 0xB3F9C6E9,0x0B45A18C,0x19F00E62,0xA14C6907,0x3C9B51BE,0x842736DB,0x96929935,0x2E2EFE50, 0x2654B999,0x9EE8DEFC,0x8C5D7112,0x34E11677,0xA9362ECE,0x118A49AB,0x033FE645,0xBB838120, 0xE3E09176,0x5B5CF613,0x49E959FD,0xF1553E98,0x6C820621,0xD43E6144,0xC68BCEAA,0x7E37A9CF, 0xD67F4138,0x6EC3265D,0x7C7689B3,0xC4CAEED6,0x591DD66F,0xE1A1B10A,0xF3141EE4,0x4BA87981, 0x13CB69D7,0xAB770EB2,0xB9C2A15C,0x017EC639,0x9CA9FE80,0x241599E5,0x36A0360B,0x8E1C516E, 0x866616A7,0x3EDA71C2,0x2C6FDE2C,0x94D3B949,0x090481F0,0xB1B8E695,0xA30D497B,0x1BB12E1E, 0x43D23E48,0xFB6E592D,0xE9DBF6C3,0x516791A6,0xCCB0A91F,0x740CCE7A,0x66B96194,0xDE0506F1, } ,{ 0x00000000,0x3D6029B0,0x7AC05360,0x47A07AD0,0xF580A6C0,0xC8E08F70,0x8F40F5A0,0xB220DC10, 0x30704BC1,0x0D106271,0x4AB018A1,0x77D03111,0xC5F0ED01,0xF890C4B1,0xBF30BE61,0x825097D1, 0x60E09782,0x5D80BE32,0x1A20C4E2,0x2740ED52,0x95603142,0xA80018F2,0xEFA06222,0xD2C04B92, 0x5090DC43,0x6DF0F5F3,0x2A508F23,0x1730A693,0xA5107A83,0x98705333,0xDFD029E3,0xE2B00053, 0xC1C12F04,0xFCA106B4,0xBB017C64,0x866155D4,0x344189C4,0x0921A074,0x4E81DAA4,0x73E1F314, 0xF1B164C5,0xCCD14D75,0x8B7137A5,0xB6111E15,0x0431C205,0x3951EBB5,0x7EF19165,0x4391B8D5, 0xA121B886,0x9C419136,0xDBE1EBE6,0xE681C256,0x54A11E46,0x69C137F6,0x2E614D26,0x13016496, 0x9151F347,0xAC31DAF7,0xEB91A027,0xD6F18997,0x64D15587,0x59B17C37,0x1E1106E7,0x23712F57, 0x58F35849,0x659371F9,0x22330B29,0x1F532299,0xAD73FE89,0x9013D739,0xD7B3ADE9,0xEAD38459, 0x68831388,0x55E33A38,0x124340E8,0x2F236958,0x9D03B548,0xA0639CF8,0xE7C3E628,0xDAA3CF98, 0x3813CFCB,0x0573E67B,0x42D39CAB,0x7FB3B51B,0xCD93690B,0xF0F340BB,0xB7533A6B,0x8A3313DB, 0x0863840A,0x3503ADBA,0x72A3D76A,0x4FC3FEDA,0xFDE322CA,0xC0830B7A,0x872371AA,0xBA43581A, 0x9932774D,0xA4525EFD,0xE3F2242D,0xDE920D9D,0x6CB2D18D,0x51D2F83D,0x167282ED,0x2B12AB5D, 0xA9423C8C,0x9422153C,0xD3826FEC,0xEEE2465C,0x5CC29A4C,0x61A2B3FC,0x2602C92C,0x1B62E09C, 0xF9D2E0CF,0xC4B2C97F,0x8312B3AF,0xBE729A1F,0x0C52460F,0x31326FBF,0x7692156F,0x4BF23CDF, 0xC9A2AB0E,0xF4C282BE,0xB362F86E,0x8E02D1DE,0x3C220DCE,0x0142247E,0x46E25EAE,0x7B82771E, 0xB1E6B092,0x8C869922,0xCB26E3F2,0xF646CA42,0x44661652,0x79063FE2,0x3EA64532,0x03C66C82, 0x8196FB53,0xBCF6D2E3,0xFB56A833,0xC6368183,0x74165D93,0x49767423,0x0ED60EF3,0x33B62743, 0xD1062710,0xEC660EA0,0xABC67470,0x96A65DC0,0x248681D0,0x19E6A860,0x5E46D2B0,0x6326FB00, 0xE1766CD1,0xDC164561,0x9BB63FB1,0xA6D61601,0x14F6CA11,0x2996E3A1,0x6E369971,0x5356B0C1, 0x70279F96,0x4D47B626,0x0AE7CCF6,0x3787E546,0x85A73956,0xB8C710E6,0xFF676A36,0xC2074386, 0x4057D457,0x7D37FDE7,0x3A978737,0x07F7AE87,0xB5D77297,0x88B75B27,0xCF1721F7,0xF2770847, 0x10C70814,0x2DA721A4,0x6A075B74,0x576772C4,0xE547AED4,0xD8278764,0x9F87FDB4,0xA2E7D404, 0x20B743D5,0x1DD76A65,0x5A7710B5,0x67173905,0xD537E515,0xE857CCA5,0xAFF7B675,0x92979FC5, 0xE915E8DB,0xD475C16B,0x93D5BBBB,0xAEB5920B,0x1C954E1B,0x21F567AB,0x66551D7B,0x5B3534CB, 0xD965A31A,0xE4058AAA,0xA3A5F07A,0x9EC5D9CA,0x2CE505DA,0x11852C6A,0x562556BA,0x6B457F0A, 0x89F57F59,0xB49556E9,0xF3352C39,0xCE550589,0x7C75D999,0x4115F029,0x06B58AF9,0x3BD5A349, 0xB9853498,0x84E51D28,0xC34567F8,0xFE254E48,0x4C059258,0x7165BBE8,0x36C5C138,0x0BA5E888, 0x28D4C7DF,0x15B4EE6F,0x521494BF,0x6F74BD0F,0xDD54611F,0xE03448AF,0xA794327F,0x9AF41BCF, 0x18A48C1E,0x25C4A5AE,0x6264DF7E,0x5F04F6CE,0xED242ADE,0xD044036E,0x97E479BE,0xAA84500E, 0x4834505D,0x755479ED,0x32F4033D,0x0F942A8D,0xBDB4F69D,0x80D4DF2D,0xC774A5FD,0xFA148C4D, 0x78441B9C,0x4524322C,0x028448FC,0x3FE4614C,0x8DC4BD5C,0xB0A494EC,0xF704EE3C,0xCA64C78C, }, { 0x00000000,0xCB5CD3A5,0x4DC8A10B,0x869472AE,0x9B914216,0x50CD91B3,0xD659E31D,0x1D0530B8, 0xEC53826D,0x270F51C8,0xA19B2366,0x6AC7F0C3,0x77C2C07B,0xBC9E13DE,0x3A0A6170,0xF156B2D5, 0x03D6029B,0xC88AD13E,0x4E1EA390,0x85427035,0x9847408D,0x531B9328,0xD58FE186,0x1ED33223, 0xEF8580F6,0x24D95353,0xA24D21FD,0x6911F258,0x7414C2E0,0xBF481145,0x39DC63EB,0xF280B04E, 0x07AC0536,0xCCF0D693,0x4A64A43D,0x81387798,0x9C3D4720,0x57619485,0xD1F5E62B,0x1AA9358E, 0xEBFF875B,0x20A354FE,0xA6372650,0x6D6BF5F5,0x706EC54D,0xBB3216E8,0x3DA66446,0xF6FAB7E3, 0x047A07AD,0xCF26D408,0x49B2A6A6,0x82EE7503,0x9FEB45BB,0x54B7961E,0xD223E4B0,0x197F3715, 0xE82985C0,0x23755665,0xA5E124CB,0x6EBDF76E,0x73B8C7D6,0xB8E41473,0x3E7066DD,0xF52CB578, 0x0F580A6C,0xC404D9C9,0x4290AB67,0x89CC78C2,0x94C9487A,0x5F959BDF,0xD901E971,0x125D3AD4, 0xE30B8801,0x28575BA4,0xAEC3290A,0x659FFAAF,0x789ACA17,0xB3C619B2,0x35526B1C,0xFE0EB8B9, 0x0C8E08F7,0xC7D2DB52,0x4146A9FC,0x8A1A7A59,0x971F4AE1,0x5C439944,0xDAD7EBEA,0x118B384F, 0xE0DD8A9A,0x2B81593F,0xAD152B91,0x6649F834,0x7B4CC88C,0xB0101B29,0x36846987,0xFDD8BA22, 0x08F40F5A,0xC3A8DCFF,0x453CAE51,0x8E607DF4,0x93654D4C,0x58399EE9,0xDEADEC47,0x15F13FE2, 0xE4A78D37,0x2FFB5E92,0xA96F2C3C,0x6233FF99,0x7F36CF21,0xB46A1C84,0x32FE6E2A,0xF9A2BD8F, 0x0B220DC1,0xC07EDE64,0x46EAACCA,0x8DB67F6F,0x90B34FD7,0x5BEF9C72,0xDD7BEEDC,0x16273D79, 0xE7718FAC,0x2C2D5C09,0xAAB92EA7,0x61E5FD02,0x7CE0CDBA,0xB7BC1E1F,0x31286CB1,0xFA74BF14, 0x1EB014D8,0xD5ECC77D,0x5378B5D3,0x98246676,0x852156CE,0x4E7D856B,0xC8E9F7C5,0x03B52460, 0xF2E396B5,0x39BF4510,0xBF2B37BE,0x7477E41B,0x6972D4A3,0xA22E0706,0x24BA75A8,0xEFE6A60D, 0x1D661643,0xD63AC5E6,0x50AEB748,0x9BF264ED,0x86F75455,0x4DAB87F0,0xCB3FF55E,0x006326FB, 0xF135942E,0x3A69478B,0xBCFD3525,0x77A1E680,0x6AA4D638,0xA1F8059D,0x276C7733,0xEC30A496, 0x191C11EE,0xD240C24B,0x54D4B0E5,0x9F886340,0x828D53F8,0x49D1805D,0xCF45F2F3,0x04192156, 0xF54F9383,0x3E134026,0xB8873288,0x73DBE12D,0x6EDED195,0xA5820230,0x2316709E,0xE84AA33B, 0x1ACA1375,0xD196C0D0,0x5702B27E,0x9C5E61DB,0x815B5163,0x4A0782C6,0xCC93F068,0x07CF23CD, 0xF6999118,0x3DC542BD,0xBB513013,0x700DE3B6,0x6D08D30E,0xA65400AB,0x20C07205,0xEB9CA1A0, 0x11E81EB4,0xDAB4CD11,0x5C20BFBF,0x977C6C1A,0x8A795CA2,0x41258F07,0xC7B1FDA9,0x0CED2E0C, 0xFDBB9CD9,0x36E74F7C,0xB0733DD2,0x7B2FEE77,0x662ADECF,0xAD760D6A,0x2BE27FC4,0xE0BEAC61, 0x123E1C2F,0xD962CF8A,0x5FF6BD24,0x94AA6E81,0x89AF5E39,0x42F38D9C,0xC467FF32,0x0F3B2C97, 0xFE6D9E42,0x35314DE7,0xB3A53F49,0x78F9ECEC,0x65FCDC54,0xAEA00FF1,0x28347D5F,0xE368AEFA, 0x16441B82,0xDD18C827,0x5B8CBA89,0x90D0692C,0x8DD55994,0x46898A31,0xC01DF89F,0x0B412B3A, 0xFA1799EF,0x314B4A4A,0xB7DF38E4,0x7C83EB41,0x6186DBF9,0xAADA085C,0x2C4E7AF2,0xE712A957, 0x15921919,0xDECECABC,0x585AB812,0x93066BB7,0x8E035B0F,0x455F88AA,0xC3CBFA04,0x089729A1, 0xF9C19B74,0x329D48D1,0xB4093A7F,0x7F55E9DA,0x6250D962,0xA90C0AC7,0x2F987869,0xE4C4ABCC, }, { 0x00000000,0xA6770BB4,0x979F1129,0x31E81A9D,0xF44F2413,0x52382FA7,0x63D0353A,0xC5A73E8E, 0x33EF4E67,0x959845D3,0xA4705F4E,0x020754FA,0xC7A06A74,0x61D761C0,0x503F7B5D,0xF64870E9, 0x67DE9CCE,0xC1A9977A,0xF0418DE7,0x56368653,0x9391B8DD,0x35E6B369,0x040EA9F4,0xA279A240, 0x5431D2A9,0xF246D91D,0xC3AEC380,0x65D9C834,0xA07EF6BA,0x0609FD0E,0x37E1E793,0x9196EC27, 0xCFBD399C,0x69CA3228,0x582228B5,0xFE552301,0x3BF21D8F,0x9D85163B,0xAC6D0CA6,0x0A1A0712, 0xFC5277FB,0x5A257C4F,0x6BCD66D2,0xCDBA6D66,0x081D53E8,0xAE6A585C,0x9F8242C1,0x39F54975, 0xA863A552,0x0E14AEE6,0x3FFCB47B,0x998BBFCF,0x5C2C8141,0xFA5B8AF5,0xCBB39068,0x6DC49BDC, 0x9B8CEB35,0x3DFBE081,0x0C13FA1C,0xAA64F1A8,0x6FC3CF26,0xC9B4C492,0xF85CDE0F,0x5E2BD5BB, 0x440B7579,0xE27C7ECD,0xD3946450,0x75E36FE4,0xB044516A,0x16335ADE,0x27DB4043,0x81AC4BF7, 0x77E43B1E,0xD19330AA,0xE07B2A37,0x460C2183,0x83AB1F0D,0x25DC14B9,0x14340E24,0xB2430590, 0x23D5E9B7,0x85A2E203,0xB44AF89E,0x123DF32A,0xD79ACDA4,0x71EDC610,0x4005DC8D,0xE672D739, 0x103AA7D0,0xB64DAC64,0x87A5B6F9,0x21D2BD4D,0xE47583C3,0x42028877,0x73EA92EA,0xD59D995E, 0x8BB64CE5,0x2DC14751,0x1C295DCC,0xBA5E5678,0x7FF968F6,0xD98E6342,0xE86679DF,0x4E11726B, 0xB8590282,0x1E2E0936,0x2FC613AB,0x89B1181F,0x4C162691,0xEA612D25,0xDB8937B8,0x7DFE3C0C, 0xEC68D02B,0x4A1FDB9F,0x7BF7C102,0xDD80CAB6,0x1827F438,0xBE50FF8C,0x8FB8E511,0x29CFEEA5, 0xDF879E4C,0x79F095F8,0x48188F65,0xEE6F84D1,0x2BC8BA5F,0x8DBFB1EB,0xBC57AB76,0x1A20A0C2, 0x8816EAF2,0x2E61E146,0x1F89FBDB,0xB9FEF06F,0x7C59CEE1,0xDA2EC555,0xEBC6DFC8,0x4DB1D47C, 0xBBF9A495,0x1D8EAF21,0x2C66B5BC,0x8A11BE08,0x4FB68086,0xE9C18B32,0xD82991AF,0x7E5E9A1B, 0xEFC8763C,0x49BF7D88,0x78576715,0xDE206CA1,0x1B87522F,0xBDF0599B,0x8C184306,0x2A6F48B2, 0xDC27385B,0x7A5033EF,0x4BB82972,0xEDCF22C6,0x28681C48,0x8E1F17FC,0xBFF70D61,0x198006D5, 0x47ABD36E,0xE1DCD8DA,0xD034C247,0x7643C9F3,0xB3E4F77D,0x1593FCC9,0x247BE654,0x820CEDE0, 0x74449D09,0xD23396BD,0xE3DB8C20,0x45AC8794,0x800BB91A,0x267CB2AE,0x1794A833,0xB1E3A387, 0x20754FA0,0x86024414,0xB7EA5E89,0x119D553D,0xD43A6BB3,0x724D6007,0x43A57A9A,0xE5D2712E, 0x139A01C7,0xB5ED0A73,0x840510EE,0x22721B5A,0xE7D525D4,0x41A22E60,0x704A34FD,0xD63D3F49, 0xCC1D9F8B,0x6A6A943F,0x5B828EA2,0xFDF58516,0x3852BB98,0x9E25B02C,0xAFCDAAB1,0x09BAA105, 0xFFF2D1EC,0x5985DA58,0x686DC0C5,0xCE1ACB71,0x0BBDF5FF,0xADCAFE4B,0x9C22E4D6,0x3A55EF62, 0xABC30345,0x0DB408F1,0x3C5C126C,0x9A2B19D8,0x5F8C2756,0xF9FB2CE2,0xC813367F,0x6E643DCB, 0x982C4D22,0x3E5B4696,0x0FB35C0B,0xA9C457BF,0x6C636931,0xCA146285,0xFBFC7818,0x5D8B73AC, 0x03A0A617,0xA5D7ADA3,0x943FB73E,0x3248BC8A,0xF7EF8204,0x519889B0,0x6070932D,0xC6079899, 0x304FE870,0x9638E3C4,0xA7D0F959,0x01A7F2ED,0xC400CC63,0x6277C7D7,0x539FDD4A,0xF5E8D6FE, 0x647E3AD9,0xC209316D,0xF3E12BF0,0x55962044,0x90311ECA,0x3646157E,0x07AE0FE3,0xA1D90457, 0x579174BE,0xF1E67F0A,0xC00E6597,0x66796E23,0xA3DE50AD,0x05A95B19,0x34414184,0x92364A30, }, { 0x00000000,0xCCAA009E,0x4225077D,0x8E8F07E3,0x844A0EFA,0x48E00E64,0xC66F0987,0x0AC50919, 0xD3E51BB5,0x1F4F1B2B,0x91C01CC8,0x5D6A1C56,0x57AF154F,0x9B0515D1,0x158A1232,0xD92012AC, 0x7CBB312B,0xB01131B5,0x3E9E3656,0xF23436C8,0xF8F13FD1,0x345B3F4F,0xBAD438AC,0x767E3832, 0xAF5E2A9E,0x63F42A00,0xED7B2DE3,0x21D12D7D,0x2B142464,0xE7BE24FA,0x69312319,0xA59B2387, 0xF9766256,0x35DC62C8,0xBB53652B,0x77F965B5,0x7D3C6CAC,0xB1966C32,0x3F196BD1,0xF3B36B4F, 0x2A9379E3,0xE639797D,0x68B67E9E,0xA41C7E00,0xAED97719,0x62737787,0xECFC7064,0x205670FA, 0x85CD537D,0x496753E3,0xC7E85400,0x0B42549E,0x01875D87,0xCD2D5D19,0x43A25AFA,0x8F085A64, 0x562848C8,0x9A824856,0x140D4FB5,0xD8A74F2B,0xD2624632,0x1EC846AC,0x9047414F,0x5CED41D1, 0x299DC2ED,0xE537C273,0x6BB8C590,0xA712C50E,0xADD7CC17,0x617DCC89,0xEFF2CB6A,0x2358CBF4, 0xFA78D958,0x36D2D9C6,0xB85DDE25,0x74F7DEBB,0x7E32D7A2,0xB298D73C,0x3C17D0DF,0xF0BDD041, 0x5526F3C6,0x998CF358,0x1703F4BB,0xDBA9F425,0xD16CFD3C,0x1DC6FDA2,0x9349FA41,0x5FE3FADF, 0x86C3E873,0x4A69E8ED,0xC4E6EF0E,0x084CEF90,0x0289E689,0xCE23E617,0x40ACE1F4,0x8C06E16A, 0xD0EBA0BB,0x1C41A025,0x92CEA7C6,0x5E64A758,0x54A1AE41,0x980BAEDF,0x1684A93C,0xDA2EA9A2, 0x030EBB0E,0xCFA4BB90,0x412BBC73,0x8D81BCED,0x8744B5F4,0x4BEEB56A,0xC561B289,0x09CBB217, 0xAC509190,0x60FA910E,0xEE7596ED,0x22DF9673,0x281A9F6A,0xE4B09FF4,0x6A3F9817,0xA6959889, 0x7FB58A25,0xB31F8ABB,0x3D908D58,0xF13A8DC6,0xFBFF84DF,0x37558441,0xB9DA83A2,0x7570833C, 0x533B85DA,0x9F918544,0x111E82A7,0xDDB48239,0xD7718B20,0x1BDB8BBE,0x95548C5D,0x59FE8CC3, 0x80DE9E6F,0x4C749EF1,0xC2FB9912,0x0E51998C,0x04949095,0xC83E900B,0x46B197E8,0x8A1B9776, 0x2F80B4F1,0xE32AB46F,0x6DA5B38C,0xA10FB312,0xABCABA0B,0x6760BA95,0xE9EFBD76,0x2545BDE8, 0xFC65AF44,0x30CFAFDA,0xBE40A839,0x72EAA8A7,0x782FA1BE,0xB485A120,0x3A0AA6C3,0xF6A0A65D, 0xAA4DE78C,0x66E7E712,0xE868E0F1,0x24C2E06F,0x2E07E976,0xE2ADE9E8,0x6C22EE0B,0xA088EE95, 0x79A8FC39,0xB502FCA7,0x3B8DFB44,0xF727FBDA,0xFDE2F2C3,0x3148F25D,0xBFC7F5BE,0x736DF520, 0xD6F6D6A7,0x1A5CD639,0x94D3D1DA,0x5879D144,0x52BCD85D,0x9E16D8C3,0x1099DF20,0xDC33DFBE, 0x0513CD12,0xC9B9CD8C,0x4736CA6F,0x8B9CCAF1,0x8159C3E8,0x4DF3C376,0xC37CC495,0x0FD6C40B, 0x7AA64737,0xB60C47A9,0x3883404A,0xF42940D4,0xFEEC49CD,0x32464953,0xBCC94EB0,0x70634E2E, 0xA9435C82,0x65E95C1C,0xEB665BFF,0x27CC5B61,0x2D095278,0xE1A352E6,0x6F2C5505,0xA386559B, 0x061D761C,0xCAB77682,0x44387161,0x889271FF,0x825778E6,0x4EFD7878,0xC0727F9B,0x0CD87F05, 0xD5F86DA9,0x19526D37,0x97DD6AD4,0x5B776A4A,0x51B26353,0x9D1863CD,0x1397642E,0xDF3D64B0, 0x83D02561,0x4F7A25FF,0xC1F5221C,0x0D5F2282,0x079A2B9B,0xCB302B05,0x45BF2CE6,0x89152C78, 0x50353ED4,0x9C9F3E4A,0x121039A9,0xDEBA3937,0xD47F302E,0x18D530B0,0x965A3753,0x5AF037CD, 0xFF6B144A,0x33C114D4,0xBD4E1337,0x71E413A9,0x7B211AB0,0xB78B1A2E,0x39041DCD,0xF5AE1D53, 0x2C8E0FFF,0xE0240F61,0x6EAB0882,0xA201081C,0xA8C40105,0x646E019B,0xEAE10678,0x264B06E6, } }; /// compute CRC32 (Slicing-by-8 algorithm), unroll inner loop 4 times uint32_t crc32_slice_by_8(const void* data, size_t length, uint32_t previousCrc32) { uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF const uint32_t* current; const uint8_t* currentChar = (const uint8_t*) data; // enabling optimization (at least -O2) automatically unrolls the inner for-loop const size_t Unroll = 4; const size_t BytesAtOnce = 8 * Unroll; // wanted: 32 bit / 4 Byte alignment, compute leading, unaligned bytes length uintptr_t unaligned_length = (4 - (((uintptr_t) currentChar) & 3)) & 3; // process unaligned bytes, if any (standard algorithm) while ((length != 0) && (unaligned_length != 0)) { crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *currentChar++]; length--; unaligned_length--; } // pointer points to 32bit aligned address now current = (const uint32_t*) currentChar; // process 4x eight bytes at once (Slicing-by-8) while (length >= BytesAtOnce) { size_t unrolling; for (unrolling = 0; unrolling < Unroll; unrolling++) { #if BORG_BIG_ENDIAN uint32_t one = *current++ ^ _le32toh(crc); uint32_t two = *current++; crc = Crc32Lookup[0][ two & 0xFF] ^ Crc32Lookup[1][(two>> 8) & 0xFF] ^ Crc32Lookup[2][(two>>16) & 0xFF] ^ Crc32Lookup[3][(two>>24) & 0xFF] ^ Crc32Lookup[4][ one & 0xFF] ^ Crc32Lookup[5][(one>> 8) & 0xFF] ^ Crc32Lookup[6][(one>>16) & 0xFF] ^ Crc32Lookup[7][(one>>24) & 0xFF]; #else uint32_t one = *current++ ^ crc; uint32_t two = *current++; crc = Crc32Lookup[0][(two>>24) & 0xFF] ^ Crc32Lookup[1][(two>>16) & 0xFF] ^ Crc32Lookup[2][(two>> 8) & 0xFF] ^ Crc32Lookup[3][ two & 0xFF] ^ Crc32Lookup[4][(one>>24) & 0xFF] ^ Crc32Lookup[5][(one>>16) & 0xFF] ^ Crc32Lookup[6][(one>> 8) & 0xFF] ^ Crc32Lookup[7][ one & 0xFF]; #endif } length -= BytesAtOnce; } currentChar = (const uint8_t*) current; // remaining 1 to 31 bytes (standard algorithm) while (length-- != 0) crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *currentChar++]; return ~crc; // same as crc ^ 0xFFFFFFFF } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.0929744 borgbackup-1.2.8/src/borg/algorithms/lz4/0000755000076500000240000000000014601577064016632 5ustar00twstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1687849 borgbackup-1.2.8/src/borg/algorithms/lz4/lib/0000755000076500000240000000000014601577064017400 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/lz4/lib/lz4.c0000644000076500000240000030564414601576577020301 0ustar00twstaff/* LZ4 - Fast LZ compression algorithm Copyright (C) 2011-present, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 homepage : http://www.lz4.org - LZ4 source repository : https://github.com/lz4/lz4 */ /*-************************************ * Tuning parameters **************************************/ /* * LZ4_HEAPMODE : * Select how default compression functions will allocate memory for their hash table, * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). */ #ifndef LZ4_HEAPMODE # define LZ4_HEAPMODE 0 #endif /* * ACCELERATION_DEFAULT : * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 */ #define ACCELERATION_DEFAULT 1 /*-************************************ * CPU Feature Detection **************************************/ /* LZ4_FORCE_MEMORY_ACCESS * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. * The below switch allow to select different access method for improved performance. * Method 0 (default) : use `memcpy()`. Safe and portable. * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. * Method 2 : direct access. This method is portable but violate C standard. * It can generate buggy code on targets which assembly generation depends on alignment. * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. * Prefer these methods in priority order (0 > 1 > 2) */ #ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */ # if defined(__GNUC__) && \ ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \ || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) # define LZ4_FORCE_MEMORY_ACCESS 2 # elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) # define LZ4_FORCE_MEMORY_ACCESS 1 # endif #endif /* * LZ4_FORCE_SW_BITCOUNT * Define this parameter if your target system or compiler does not support hardware bit count */ #if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */ # define LZ4_FORCE_SW_BITCOUNT #endif /*-************************************ * Dependency **************************************/ /* * LZ4_SRC_INCLUDED: * Amalgamation flag, whether lz4.c is included */ #ifndef LZ4_SRC_INCLUDED # define LZ4_SRC_INCLUDED 1 #endif #ifndef LZ4_STATIC_LINKING_ONLY #define LZ4_STATIC_LINKING_ONLY #endif #ifndef LZ4_DISABLE_DEPRECATE_WARNINGS #define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */ #endif #define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */ #include "lz4.h" /* see also "memory routines" below */ /*-************************************ * Compiler Options **************************************/ #ifdef _MSC_VER /* Visual Studio */ # include # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */ #endif /* _MSC_VER */ #ifndef LZ4_FORCE_INLINE # ifdef _MSC_VER /* Visual Studio */ # define LZ4_FORCE_INLINE static __forceinline # else # if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ # ifdef __GNUC__ # define LZ4_FORCE_INLINE static inline __attribute__((always_inline)) # else # define LZ4_FORCE_INLINE static inline # endif # else # define LZ4_FORCE_INLINE static # endif /* __STDC_VERSION__ */ # endif /* _MSC_VER */ #endif /* LZ4_FORCE_INLINE */ /* LZ4_FORCE_O2_GCC_PPC64LE and LZ4_FORCE_O2_INLINE_GCC_PPC64LE * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8, * together with a simple 8-byte copy loop as a fall-back path. * However, this optimization hurts the decompression speed by >30%, * because the execution does not go to the optimized loop * for typical compressible data, and all of the preamble checks * before going to the fall-back path become useless overhead. * This optimization happens only with the -O3 flag, and -O2 generates * a simple 8-byte copy loop. * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8 * functions are annotated with __attribute__((optimize("O2"))), * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute * of LZ4_wildCopy8 does not affect the compression speed. */ #if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__) # define LZ4_FORCE_O2_GCC_PPC64LE __attribute__((optimize("O2"))) # define LZ4_FORCE_O2_INLINE_GCC_PPC64LE __attribute__((optimize("O2"))) LZ4_FORCE_INLINE #else # define LZ4_FORCE_O2_GCC_PPC64LE # define LZ4_FORCE_O2_INLINE_GCC_PPC64LE static #endif #if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) # define expect(expr,value) (__builtin_expect ((expr),(value)) ) #else # define expect(expr,value) (expr) #endif #ifndef likely #define likely(expr) expect((expr) != 0, 1) #endif #ifndef unlikely #define unlikely(expr) expect((expr) != 0, 0) #endif /*-************************************ * Memory routines **************************************/ #include /* malloc, calloc, free */ #define ALLOC(s) malloc(s) #define ALLOC_AND_ZERO(s) calloc(1,s) #define FREEMEM(p) free(p) #include /* memset, memcpy */ #define MEM_INIT(p,v,s) memset((p),(v),(s)) /*-************************************ * Common Constants **************************************/ #define MINMATCH 4 #define WILDCOPYLENGTH 8 #define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ #define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ #define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */ #define FASTLOOP_SAFE_DISTANCE 64 static const int LZ4_minLength = (MFLIMIT+1); #define KB *(1 <<10) #define MB *(1 <<20) #define GB *(1U<<30) #define LZ4_DISTANCE_ABSOLUTE_MAX 65535 #if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */ # error "LZ4_DISTANCE_MAX is too big : must be <= 65535" #endif #define ML_BITS 4 #define ML_MASK ((1U<=1) # include #else # ifndef assert # define assert(condition) ((void)0) # endif #endif #define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */ #if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) # include static int g_debuglog_enable = 1; # define DEBUGLOG(l, ...) { \ if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ fprintf(stderr, __FILE__ ": "); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, " \n"); \ } } #else # define DEBUGLOG(l, ...) {} /* disabled */ #endif /*-************************************ * Types **************************************/ #if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # include typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; typedef uintptr_t uptrval; #else typedef unsigned char BYTE; typedef unsigned short U16; typedef unsigned int U32; typedef signed int S32; typedef unsigned long long U64; typedef size_t uptrval; /* generally true, except OpenVMS-64 */ #endif #if defined(__x86_64__) typedef U64 reg_t; /* 64-bits in x32 mode */ #else typedef size_t reg_t; /* 32-bits in x32 mode */ #endif typedef enum { notLimited = 0, limitedOutput = 1, fillOutput = 2 } limitedOutput_directive; /*-************************************ * Reading and writing into memory **************************************/ static unsigned LZ4_isLittleEndian(void) { const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ return one.c[0]; } #if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) /* lie to the compiler about data alignment; use with caution */ static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; } static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; } static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; } static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } #elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ /* currently only defined for gcc and icc */ typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) unalign; static U16 LZ4_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } static U32 LZ4_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArch; } static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; } #else /* safe and portable access using memcpy() */ static U16 LZ4_read16(const void* memPtr) { U16 val; memcpy(&val, memPtr, sizeof(val)); return val; } static U32 LZ4_read32(const void* memPtr) { U32 val; memcpy(&val, memPtr, sizeof(val)); return val; } static reg_t LZ4_read_ARCH(const void* memPtr) { reg_t val; memcpy(&val, memPtr, sizeof(val)); return val; } static void LZ4_write16(void* memPtr, U16 value) { memcpy(memPtr, &value, sizeof(value)); } static void LZ4_write32(void* memPtr, U32 value) { memcpy(memPtr, &value, sizeof(value)); } #endif /* LZ4_FORCE_MEMORY_ACCESS */ static U16 LZ4_readLE16(const void* memPtr) { if (LZ4_isLittleEndian()) { return LZ4_read16(memPtr); } else { const BYTE* p = (const BYTE*)memPtr; return (U16)((U16)p[0] + (p[1]<<8)); } } static void LZ4_writeLE16(void* memPtr, U16 value) { if (LZ4_isLittleEndian()) { LZ4_write16(memPtr, value); } else { BYTE* p = (BYTE*)memPtr; p[0] = (BYTE) value; p[1] = (BYTE)(value>>8); } } /* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ LZ4_FORCE_O2_INLINE_GCC_PPC64LE void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd) { BYTE* d = (BYTE*)dstPtr; const BYTE* s = (const BYTE*)srcPtr; BYTE* const e = (BYTE*)dstEnd; do { memcpy(d,s,8); d+=8; s+=8; } while (d= 16. */ LZ4_FORCE_O2_INLINE_GCC_PPC64LE void LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd) { BYTE* d = (BYTE*)dstPtr; const BYTE* s = (const BYTE*)srcPtr; BYTE* const e = (BYTE*)dstEnd; do { memcpy(d,s,16); memcpy(d+16,s+16,16); d+=32; s+=32; } while (d= dstPtr + MINMATCH * - there is at least 8 bytes available to write after dstEnd */ LZ4_FORCE_O2_INLINE_GCC_PPC64LE void LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) { BYTE v[8]; assert(dstEnd >= dstPtr + MINMATCH); LZ4_write32(dstPtr, 0); /* silence an msan warning when offset==0 */ switch(offset) { case 1: memset(v, *srcPtr, 8); break; case 2: memcpy(v, srcPtr, 2); memcpy(&v[2], srcPtr, 2); memcpy(&v[4], &v[0], 4); break; case 4: memcpy(v, srcPtr, 4); memcpy(&v[4], srcPtr, 4); break; default: LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset); return; } memcpy(dstPtr, v, 8); dstPtr += 8; while (dstPtr < dstEnd) { memcpy(dstPtr, v, 8); dstPtr += 8; } } #endif /*-************************************ * Common functions **************************************/ static unsigned LZ4_NbCommonBytes (reg_t val) { if (LZ4_isLittleEndian()) { if (sizeof(val)==8) { # if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanForward64( &r, (U64)val ); return (int)(r>>3); # elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) return (unsigned)__builtin_ctzll((U64)val) >> 3; # else static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; # endif } else /* 32 bits */ { # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r; _BitScanForward( &r, (U32)val ); return (int)(r>>3); # elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) return (unsigned)__builtin_ctz((U32)val) >> 3; # else static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; # endif } } else /* Big Endian CPU */ { if (sizeof(val)==8) { /* 64-bits */ # if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanReverse64( &r, val ); return (unsigned)(r>>3); # elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) return (unsigned)__builtin_clzll((U64)val) >> 3; # else static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits. Just to avoid some static analyzer complaining about shift by 32 on 32-bits target. Note that this code path is never triggered in 32-bits mode. */ unsigned r; if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; } if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } r += (!val); return r; # endif } else /* 32 bits */ { # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanReverse( &r, (unsigned long)val ); return (unsigned)(r>>3); # elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) return (unsigned)__builtin_clz((U32)val) >> 3; # else unsigned r; if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } r += (!val); return r; # endif } } } #define STEPSIZE sizeof(reg_t) LZ4_FORCE_INLINE unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) { const BYTE* const pStart = pIn; if (likely(pIn < pInLimit-(STEPSIZE-1))) { reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; } else { return LZ4_NbCommonBytes(diff); } } while (likely(pIn < pInLimit-(STEPSIZE-1))) { reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; } pIn += LZ4_NbCommonBytes(diff); return (unsigned)(pIn - pStart); } if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; } if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; } if ((pIn compression run slower on incompressible data */ /*-************************************ * Local Structures and types **************************************/ typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t; /** * This enum distinguishes several different modes of accessing previous * content in the stream. * * - noDict : There is no preceding content. * - withPrefix64k : Table entries up to ctx->dictSize before the current blob * blob being compressed are valid and refer to the preceding * content (of length ctx->dictSize), which is available * contiguously preceding in memory the content currently * being compressed. * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere * else in memory, starting at ctx->dictionary with length * ctx->dictSize. * - usingDictCtx : Like usingExtDict, but everything concerning the preceding * content is in a separate context, pointed to by * ctx->dictCtx. ctx->dictionary, ctx->dictSize, and table * entries in the current context that refer to positions * preceding the beginning of the current compression are * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx * ->dictSize describe the location and size of the preceding * content, and matches are found by looking in the ctx * ->dictCtx->hashTable. */ typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive; typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; /*-************************************ * Local Utils **************************************/ int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } int LZ4_sizeofState() { return LZ4_STREAMSIZE; } /*-************************************ * Internal Definitions used in Tests **************************************/ #if defined (__cplusplus) extern "C" { #endif int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize); int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const void* dictStart, size_t dictSize); #if defined (__cplusplus) } #endif /*-****************************** * Compression functions ********************************/ static U32 LZ4_hash4(U32 sequence, tableType_t const tableType) { if (tableType == byU16) return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); else return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); } static U32 LZ4_hash5(U64 sequence, tableType_t const tableType) { const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; if (LZ4_isLittleEndian()) { const U64 prime5bytes = 889523592379ULL; return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); } else { const U64 prime8bytes = 11400714785074694791ULL; return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); } } LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) { if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); return LZ4_hash4(LZ4_read32(p), tableType); } static void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) { switch (tableType) { default: /* fallthrough */ case clearedTable: { /* illegal! */ assert(0); return; } case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; } case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; } case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; } } } static void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType) { switch (tableType) { default: /* fallthrough */ case clearedTable: /* fallthrough */ case byPtr: { /* illegal! */ assert(0); return; } case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; } case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; } } } static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase) { switch (tableType) { case clearedTable: { /* illegal! */ assert(0); return; } case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } } } LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) { U32 const h = LZ4_hashPosition(p, tableType); LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); } /* LZ4_getIndexOnHash() : * Index of match position registered in hash table. * hash position must be calculated by using base+index, or dictBase+index. * Assumption 1 : only valid if tableType == byU32 or byU16. * Assumption 2 : h is presumed valid (within limits of hash table) */ static U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType) { LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2); if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; assert(h < (1U << (LZ4_MEMORY_USAGE-2))); return hashTable[h]; } if (tableType == byU16) { const U16* const hashTable = (const U16*) tableBase; assert(h < (1U << (LZ4_MEMORY_USAGE-1))); return hashTable[h]; } assert(0); return 0; /* forbidden case */ } static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase) { if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; } if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; } { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ } LZ4_FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, const void* tableBase, tableType_t tableType, const BYTE* srcBase) { U32 const h = LZ4_hashPosition(p, tableType); return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); } LZ4_FORCE_INLINE void LZ4_prepareTable(LZ4_stream_t_internal* const cctx, const int inputSize, const tableType_t tableType) { /* If compression failed during the previous step, then the context * is marked as dirty, therefore, it has to be fully reset. */ if (cctx->dirty) { DEBUGLOG(5, "LZ4_prepareTable: Full reset for %p", cctx); MEM_INIT(cctx, 0, sizeof(LZ4_stream_t_internal)); return; } /* If the table hasn't been used, it's guaranteed to be zeroed out, and is * therefore safe to use no matter what mode we're in. Otherwise, we figure * out if it's safe to leave as is or whether it needs to be reset. */ if (cctx->tableType != clearedTable) { assert(inputSize >= 0); if (cctx->tableType != tableType || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU) || ((tableType == byU32) && cctx->currentOffset > 1 GB) || tableType == byPtr || inputSize >= 4 KB) { DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx); MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE); cctx->currentOffset = 0; cctx->tableType = clearedTable; } else { DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)"); } } /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, is faster * than compressing without a gap. However, compressing with * currentOffset == 0 is faster still, so we preserve that case. */ if (cctx->currentOffset != 0 && tableType == byU32) { DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset"); cctx->currentOffset += 64 KB; } /* Finally, clear history */ cctx->dictCtx = NULL; cctx->dictionary = NULL; cctx->dictSize = 0; } /** LZ4_compress_generic() : inlined, to ensure branches are decided at compilation time */ LZ4_FORCE_INLINE int LZ4_compress_generic( LZ4_stream_t_internal* const cctx, const char* const source, char* const dest, const int inputSize, int *inputConsumed, /* only written when outputDirective == fillOutput */ const int maxOutputSize, const limitedOutput_directive outputDirective, const tableType_t tableType, const dict_directive dictDirective, const dictIssue_directive dictIssue, const int acceleration) { int result; const BYTE* ip = (const BYTE*) source; U32 const startIndex = cctx->currentOffset; const BYTE* base = (const BYTE*) source - startIndex; const BYTE* lowLimit; const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx; const BYTE* const dictionary = dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary; const U32 dictSize = dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize; const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */ int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx); U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */ const BYTE* const dictEnd = dictionary + dictSize; const BYTE* anchor = (const BYTE*) source; const BYTE* const iend = ip + inputSize; const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1; const BYTE* const matchlimit = iend - LASTLITERALS; /* the dictCtx currentOffset is indexed on the start of the dictionary, * while a dictionary in the current context precedes the currentOffset */ const BYTE* dictBase = (dictDirective == usingDictCtx) ? dictionary + dictSize - dictCtx->currentOffset : dictionary + dictSize - startIndex; BYTE* op = (BYTE*) dest; BYTE* const olimit = op + maxOutputSize; U32 offset = 0; U32 forwardH; DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, tableType=%u", inputSize, tableType); /* If init conditions are not met, we don't have to mark stream * as having dirty context, since no action was taken yet */ if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported inputSize, too large (or negative) */ if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */ if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */ assert(acceleration >= 1); lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0); /* Update context state */ if (dictDirective == usingDictCtx) { /* Subsequent linked blocks can't use the dictionary. */ /* Instead, they use the block we just compressed. */ cctx->dictCtx = NULL; cctx->dictSize = (U32)inputSize; } else { cctx->dictSize += (U32)inputSize; } cctx->currentOffset += (U32)inputSize; cctx->tableType = (U16)tableType; if (inputSizehashTable, tableType, base); ip++; forwardH = LZ4_hashPosition(ip, tableType); /* Main Loop */ for ( ; ; ) { const BYTE* match; BYTE* token; const BYTE* filledIp; /* Find a match */ if (tableType == byPtr) { const BYTE* forwardIp = ip; int step = 1; int searchMatchNb = acceleration << LZ4_skipTrigger; do { U32 const h = forwardH; ip = forwardIp; forwardIp += step; step = (searchMatchNb++ >> LZ4_skipTrigger); if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; assert(ip < mflimitPlusOne); match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base); forwardH = LZ4_hashPosition(forwardIp, tableType); LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base); } while ( (match+LZ4_DISTANCE_MAX < ip) || (LZ4_read32(match) != LZ4_read32(ip)) ); } else { /* byU32, byU16 */ const BYTE* forwardIp = ip; int step = 1; int searchMatchNb = acceleration << LZ4_skipTrigger; do { U32 const h = forwardH; U32 const current = (U32)(forwardIp - base); U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); assert(matchIndex <= current); assert(forwardIp - base < (ptrdiff_t)(2 GB - 1)); ip = forwardIp; forwardIp += step; step = (searchMatchNb++ >> LZ4_skipTrigger); if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; assert(ip < mflimitPlusOne); if (dictDirective == usingDictCtx) { if (matchIndex < startIndex) { /* there was no match, try the dictionary */ assert(tableType == byU32); matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); match = dictBase + matchIndex; matchIndex += dictDelta; /* make dictCtx index comparable with current context */ lowLimit = dictionary; } else { match = base + matchIndex; lowLimit = (const BYTE*)source; } } else if (dictDirective==usingExtDict) { if (matchIndex < startIndex) { DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex); assert(startIndex - matchIndex >= MINMATCH); match = dictBase + matchIndex; lowLimit = dictionary; } else { match = base + matchIndex; lowLimit = (const BYTE*)source; } } else { /* single continuous memory segment */ match = base + matchIndex; } forwardH = LZ4_hashPosition(forwardIp, tableType); LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex); if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */ assert(matchIndex < current); if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX)) && (matchIndex+LZ4_DISTANCE_MAX < current)) { continue; } /* too far */ assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */ if (LZ4_read32(match) == LZ4_read32(ip)) { if (maybe_extMem) offset = current - matchIndex; break; /* match found */ } } while(1); } /* Catch up */ filledIp = ip; while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } /* Encode Literals */ { unsigned const litLength = (unsigned)(ip - anchor); token = op++; if ((outputDirective == limitedOutput) && /* Check output buffer overflow */ (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) { return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ } if ((outputDirective == fillOutput) && (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) { op--; goto _last_literals; } if (litLength >= RUN_MASK) { int len = (int)(litLength - RUN_MASK); *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255; *op++ = (BYTE)len; } else *token = (BYTE)(litLength< olimit)) { /* the match was too close to the end, rewind and go to last literals */ op = token; goto _last_literals; } /* Encode Offset */ if (maybe_extMem) { /* static test */ DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source)); assert(offset <= LZ4_DISTANCE_MAX && offset > 0); LZ4_writeLE16(op, (U16)offset); op+=2; } else { DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match)); assert(ip-match <= LZ4_DISTANCE_MAX); LZ4_writeLE16(op, (U16)(ip - match)); op+=2; } /* Encode MatchLength */ { unsigned matchCode; if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx) && (lowLimit==dictionary) /* match within extDict */ ) { const BYTE* limit = ip + (dictEnd-match); assert(dictEnd > match); if (limit > matchlimit) limit = matchlimit; matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); ip += (size_t)matchCode + MINMATCH; if (ip==limit) { unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit); matchCode += more; ip += more; } DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH); } else { matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); ip += (size_t)matchCode + MINMATCH; DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH); } if ((outputDirective) && /* Check output buffer overflow */ (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) { if (outputDirective == fillOutput) { /* Match description too long : reduce it */ U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255; ip -= matchCode - newMatchCode; assert(newMatchCode < matchCode); matchCode = newMatchCode; if (unlikely(ip <= filledIp)) { /* We have already filled up to filledIp so if ip ends up less than filledIp * we have positions in the hash table beyond the current position. This is * a problem if we reuse the hash table. So we have to remove these positions * from the hash table. */ const BYTE* ptr; DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip)); for (ptr = ip; ptr <= filledIp; ++ptr) { U32 const h = LZ4_hashPosition(ptr, tableType); LZ4_clearHash(h, cctx->hashTable, tableType); } } } else { assert(outputDirective == limitedOutput); return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ } } if (matchCode >= ML_MASK) { *token += ML_MASK; matchCode -= ML_MASK; LZ4_write32(op, 0xFFFFFFFF); while (matchCode >= 4*255) { op+=4; LZ4_write32(op, 0xFFFFFFFF); matchCode -= 4*255; } op += matchCode / 255; *op++ = (BYTE)(matchCode % 255); } else *token += (BYTE)(matchCode); } /* Ensure we have enough space for the last literals. */ assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit)); anchor = ip; /* Test end of chunk */ if (ip >= mflimitPlusOne) break; /* Fill table */ LZ4_putPosition(ip-2, cctx->hashTable, tableType, base); /* Test next position */ if (tableType == byPtr) { match = LZ4_getPosition(ip, cctx->hashTable, tableType, base); LZ4_putPosition(ip, cctx->hashTable, tableType, base); if ( (match+LZ4_DISTANCE_MAX >= ip) && (LZ4_read32(match) == LZ4_read32(ip)) ) { token=op++; *token=0; goto _next_match; } } else { /* byU32, byU16 */ U32 const h = LZ4_hashPosition(ip, tableType); U32 const current = (U32)(ip-base); U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); assert(matchIndex < current); if (dictDirective == usingDictCtx) { if (matchIndex < startIndex) { /* there was no match, try the dictionary */ matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); match = dictBase + matchIndex; lowLimit = dictionary; /* required for match length counter */ matchIndex += dictDelta; } else { match = base + matchIndex; lowLimit = (const BYTE*)source; /* required for match length counter */ } } else if (dictDirective==usingExtDict) { if (matchIndex < startIndex) { match = dictBase + matchIndex; lowLimit = dictionary; /* required for match length counter */ } else { match = base + matchIndex; lowLimit = (const BYTE*)source; /* required for match length counter */ } } else { /* single memory segment */ match = base + matchIndex; } LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); assert(matchIndex < current); if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1) && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current)) && (LZ4_read32(match) == LZ4_read32(ip)) ) { token=op++; *token=0; if (maybe_extMem) offset = current - matchIndex; DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i", (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source)); goto _next_match; } } /* Prepare next loop */ forwardH = LZ4_hashPosition(++ip, tableType); } _last_literals: /* Encode Last Literals */ { size_t lastRun = (size_t)(iend - anchor); if ( (outputDirective) && /* Check output buffer overflow */ (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) { if (outputDirective == fillOutput) { /* adapt lastRun to fill 'dst' */ assert(olimit >= op); lastRun = (size_t)(olimit-op) - 1; lastRun -= (lastRun+240)/255; } else { assert(outputDirective == limitedOutput); return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ } } if (lastRun >= RUN_MASK) { size_t accumulator = lastRun - RUN_MASK; *op++ = RUN_MASK << ML_BITS; for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; *op++ = (BYTE) accumulator; } else { *op++ = (BYTE)(lastRun< 0); return result; } int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse; assert(ctx != NULL); if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; if (maxOutputSize >= LZ4_compressBound(inputSize)) { if (inputSize < LZ4_64Klimit) { return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration); } else { const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); } } else { if (inputSize < LZ4_64Klimit) { return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); } else { const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration); } } } /** * LZ4_compress_fast_extState_fastReset() : * A variant of LZ4_compress_fast_extState(). * * Using this variant avoids an expensive initialization step. It is only safe * to call if the state buffer is known to be correctly initialized already * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of * "correctly initialized"). */ int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) { LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse; if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; if (dstCapacity >= LZ4_compressBound(srcSize)) { if (srcSize < LZ4_64Klimit) { const tableType_t tableType = byU16; LZ4_prepareTable(ctx, srcSize, tableType); if (ctx->currentOffset) { return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration); } else { return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); } } else { const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; LZ4_prepareTable(ctx, srcSize, tableType); return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); } } else { if (srcSize < LZ4_64Klimit) { const tableType_t tableType = byU16; LZ4_prepareTable(ctx, srcSize, tableType); if (ctx->currentOffset) { return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration); } else { return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); } } else { const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; LZ4_prepareTable(ctx, srcSize, tableType); return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); } } } int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { int result; #if (LZ4_HEAPMODE) LZ4_stream_t* ctxPtr = ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ if (ctxPtr == NULL) return 0; #else LZ4_stream_t ctx; LZ4_stream_t* const ctxPtr = &ctx; #endif result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); #if (LZ4_HEAPMODE) FREEMEM(ctxPtr); #endif return result; } int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize) { return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1); } /* hidden debug function */ /* strangely enough, gcc generates faster code when this function is uncommented, even if unused */ int LZ4_compress_fast_force(const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) { LZ4_stream_t ctx; LZ4_initStream(&ctx, sizeof(ctx)); if (srcSize < LZ4_64Klimit) { return LZ4_compress_generic(&ctx.internal_donotuse, src, dst, srcSize, NULL, dstCapacity, limitedOutput, byU16, noDict, noDictIssue, acceleration); } else { tableType_t const addrMode = (sizeof(void*) > 4) ? byU32 : byPtr; return LZ4_compress_generic(&ctx.internal_donotuse, src, dst, srcSize, NULL, dstCapacity, limitedOutput, addrMode, noDict, noDictIssue, acceleration); } } /* Note!: This function leaves the stream in an unclean/broken state! * It is not safe to subsequently use the same state with a _fastReset() or * _continue() call without resetting it. */ static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize) { void* const s = LZ4_initStream(state, sizeof (*state)); assert(s != NULL); (void)s; if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */ return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); } else { if (*srcSizePtr < LZ4_64Klimit) { return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1); } else { tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1); } } } int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) { #if (LZ4_HEAPMODE) LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ if (ctx == NULL) return 0; #else LZ4_stream_t ctxBody; LZ4_stream_t* ctx = &ctxBody; #endif int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); #if (LZ4_HEAPMODE) FREEMEM(ctx); #endif return result; } /*-****************************** * Streaming functions ********************************/ LZ4_stream_t* LZ4_createStream(void) { LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */ DEBUGLOG(4, "LZ4_createStream %p", lz4s); if (lz4s == NULL) return NULL; LZ4_initStream(lz4s, sizeof(*lz4s)); return lz4s; } #ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 : it reports an aligment of 8-bytes, while actually aligning LZ4_stream_t on 4 bytes. */ static size_t LZ4_stream_t_alignment(void) { struct { char c; LZ4_stream_t t; } t_a; return sizeof(t_a) - sizeof(t_a.t); } #endif LZ4_stream_t* LZ4_initStream (void* buffer, size_t size) { DEBUGLOG(5, "LZ4_initStream"); if (buffer == NULL) { return NULL; } if (size < sizeof(LZ4_stream_t)) { return NULL; } #ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 : it reports an aligment of 8-bytes, while actually aligning LZ4_stream_t on 4 bytes. */ if (((size_t)buffer) & (LZ4_stream_t_alignment() - 1)) { return NULL; } /* alignment check */ #endif MEM_INIT(buffer, 0, sizeof(LZ4_stream_t)); return (LZ4_stream_t*)buffer; } /* resetStream is now deprecated, * prefer initStream() which is more general */ void LZ4_resetStream (LZ4_stream_t* LZ4_stream) { DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream); MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t)); } void LZ4_resetStream_fast(LZ4_stream_t* ctx) { LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32); } int LZ4_freeStream (LZ4_stream_t* LZ4_stream) { if (!LZ4_stream) return 0; /* support free on NULL */ DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream); FREEMEM(LZ4_stream); return (0); } #define HASH_UNIT sizeof(reg_t) int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) { LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse; const tableType_t tableType = byU32; const BYTE* p = (const BYTE*)dictionary; const BYTE* const dictEnd = p + dictSize; const BYTE* base; DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict); /* It's necessary to reset the context, * and not just continue it with prepareTable() * to avoid any risk of generating overflowing matchIndex * when compressing using this dictionary */ LZ4_resetStream(LZ4_dict); /* We always increment the offset by 64 KB, since, if the dict is longer, * we truncate it to the last 64k, and if it's shorter, we still want to * advance by a whole window length so we can provide the guarantee that * there are only valid offsets in the window, which allows an optimization * in LZ4_compress_fast_continue() where it uses noDictIssue even when the * dictionary isn't a full 64k. */ dict->currentOffset += 64 KB; if (dictSize < (int)HASH_UNIT) { return 0; } if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; base = dictEnd - dict->currentOffset; dict->dictionary = p; dict->dictSize = (U32)(dictEnd - p); dict->tableType = tableType; while (p <= dictEnd-HASH_UNIT) { LZ4_putPosition(p, dict->hashTable, tableType, base); p+=3; } return (int)dict->dictSize; } void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) { const LZ4_stream_t_internal* dictCtx = dictionaryStream == NULL ? NULL : &(dictionaryStream->internal_donotuse); DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)", workingStream, dictionaryStream, dictCtx != NULL ? dictCtx->dictSize : 0); /* Calling LZ4_resetStream_fast() here makes sure that changes will not be * erased by subsequent calls to LZ4_resetStream_fast() in case stream was * marked as having dirty context, e.g. requiring full reset. */ LZ4_resetStream_fast(workingStream); if (dictCtx != NULL) { /* If the current offset is zero, we will never look in the * external dictionary context, since there is no value a table * entry can take that indicate a miss. In that case, we need * to bump the offset to something non-zero. */ if (workingStream->internal_donotuse.currentOffset == 0) { workingStream->internal_donotuse.currentOffset = 64 KB; } /* Don't actually attach an empty dictionary. */ if (dictCtx->dictSize == 0) { dictCtx = NULL; } } workingStream->internal_donotuse.dictCtx = dictCtx; } static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize) { assert(nextSize >= 0); if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */ /* rescale hash table */ U32 const delta = LZ4_dict->currentOffset - 64 KB; const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; int i; DEBUGLOG(4, "LZ4_renormDictT"); for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0; else LZ4_dict->hashTable[i] -= delta; } LZ4_dict->currentOffset = 64 KB; if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; } } int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { const tableType_t tableType = byU32; LZ4_stream_t_internal* streamPtr = &LZ4_stream->internal_donotuse; const BYTE* dictEnd = streamPtr->dictionary + streamPtr->dictSize; DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i)", inputSize); if (streamPtr->dirty) { return 0; } /* Uninitialized structure detected */ LZ4_renormDictT(streamPtr, inputSize); /* avoid index overflow */ if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; /* invalidate tiny dictionaries */ if ( (streamPtr->dictSize-1 < 4-1) /* intentional underflow */ && (dictEnd != (const BYTE*)source) ) { DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary); streamPtr->dictSize = 0; streamPtr->dictionary = (const BYTE*)source; dictEnd = (const BYTE*)source; } /* Check overlapping input/dictionary space */ { const BYTE* sourceEnd = (const BYTE*) source + inputSize; if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) { streamPtr->dictSize = (U32)(dictEnd - sourceEnd); if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; streamPtr->dictionary = dictEnd - streamPtr->dictSize; } } /* prefix mode : source data follows dictionary */ if (dictEnd == (const BYTE*)source) { if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration); else return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration); } /* external dictionary mode */ { int result; if (streamPtr->dictCtx) { /* We depend here on the fact that dictCtx'es (produced by * LZ4_loadDict) guarantee that their tables contain no references * to offsets between dictCtx->currentOffset - 64 KB and * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe * to use noDictIssue even when the dict isn't a full 64 KB. */ if (inputSize > 4 KB) { /* For compressing large blobs, it is faster to pay the setup * cost to copy the dictionary's tables into the active context, * so that the compression loop is only looking into one table. */ memcpy(streamPtr, streamPtr->dictCtx, sizeof(LZ4_stream_t)); result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); } else { result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration); } } else { if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration); } else { result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); } } streamPtr->dictionary = (const BYTE*)source; streamPtr->dictSize = (U32)inputSize; return result; } } /* Hidden debug function, to force-test external dictionary mode */ int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize) { LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse; int result; LZ4_renormDictT(streamPtr, srcSize); if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1); } else { result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); } streamPtr->dictionary = (const BYTE*)source; streamPtr->dictSize = (U32)srcSize; return result; } /*! LZ4_saveDict() : * If previously compressed data block is not guaranteed to remain available at its memory location, * save it into a safer place (char* safeBuffer). * Note : you don't need to call LZ4_loadDict() afterwards, * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue(). * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. */ int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) { LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } memmove(safeBuffer, previousDictEnd - dictSize, dictSize); dict->dictionary = (const BYTE*)safeBuffer; dict->dictSize = (U32)dictSize; return dictSize; } /*-******************************* * Decompression functions ********************************/ typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive; #undef MIN #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) /* Read the variable-length literal or match length. * * ip - pointer to use as input. * lencheck - end ip. Return an error if ip advances >= lencheck. * loop_check - check ip >= lencheck in body of loop. Returns loop_error if so. * initial_check - check ip >= lencheck before start of loop. Returns initial_error if so. * error (output) - error code. Should be set to 0 before call. */ typedef enum { loop_error = -2, initial_error = -1, ok = 0 } variable_length_error; LZ4_FORCE_INLINE unsigned read_variable_length(const BYTE**ip, const BYTE* lencheck, int loop_check, int initial_check, variable_length_error* error) { unsigned length = 0; unsigned s; if (initial_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ *error = initial_error; return length; } do { s = **ip; (*ip)++; length += s; if (loop_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ *error = loop_error; return length; } } while (s==255); return length; } /*! LZ4_decompress_generic() : * This generic decompression function covers all use cases. * It shall be instantiated several times, using different sets of directives. * Note that it is important for performance that this function really get inlined, * in order to remove useless branches during compilation optimization. */ LZ4_FORCE_INLINE int LZ4_decompress_generic( const char* const src, char* const dst, int srcSize, int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */ endCondition_directive endOnInput, /* endOnOutputSize, endOnInputSize */ earlyEnd_directive partialDecoding, /* full, partial */ dict_directive dict, /* noDict, withPrefix64k, usingExtDict */ const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */ const BYTE* const dictStart, /* only if dict==usingExtDict */ const size_t dictSize /* note : = 0 if noDict */ ) { if (src == NULL) { return -1; } { const BYTE* ip = (const BYTE*) src; const BYTE* const iend = ip + srcSize; BYTE* op = (BYTE*) dst; BYTE* const oend = op + outputSize; BYTE* cpy; const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize; const int safeDecode = (endOnInput==endOnInputSize); const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); /* Set up the "end" pointers for the shortcut. */ const BYTE* const shortiend = iend - (endOnInput ? 14 : 8) /*maxLL*/ - 2 /*offset*/; const BYTE* const shortoend = oend - (endOnInput ? 14 : 8) /*maxLL*/ - 18 /*maxML*/; const BYTE* match; size_t offset; unsigned token; size_t length; DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize); /* Special cases */ assert(lowPrefix <= op); if ((endOnInput) && (unlikely(outputSize==0))) { /* Empty output buffer */ if (partialDecoding) return 0; return ((srcSize==1) && (*ip==0)) ? 0 : -1; } if ((!endOnInput) && (unlikely(outputSize==0))) { return (*ip==0 ? 1 : -1); } if ((endOnInput) && unlikely(srcSize==0)) { return -1; } /* Currently the fast loop shows a regression on qualcomm arm chips. */ #if LZ4_FAST_DEC_LOOP if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { DEBUGLOG(6, "skip fast decode loop"); goto safe_decode; } /* Fast loop : decode sequences as long as output < iend-FASTLOOP_SAFE_DISTANCE */ while (1) { /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */ assert(oend - op >= FASTLOOP_SAFE_DISTANCE); if (endOnInput) { assert(ip < iend); } token = *ip++; length = token >> ML_BITS; /* literal length */ assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ /* decode literal length */ if (length == RUN_MASK) { variable_length_error error = ok; length += read_variable_length(&ip, iend-RUN_MASK, endOnInput, endOnInput, &error); if (error == initial_error) { goto _output_error; } if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ /* copy literals */ cpy = op+length; LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); if (endOnInput) { /* LZ4_decompress_safe() */ if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } LZ4_wildCopy32(op, ip, cpy); } else { /* LZ4_decompress_fast() */ if (cpy>oend-8) { goto safe_literal_copy; } LZ4_wildCopy8(op, ip, cpy); /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : * it doesn't know input length, and only relies on end-of-block properties */ } ip += length; op = cpy; } else { cpy = op+length; if (endOnInput) { /* LZ4_decompress_safe() */ DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); /* We don't need to check oend, since we check it once for each loop below */ if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } /* Literals can only be 14, but hope compilers optimize if we copy by a register size */ memcpy(op, ip, 16); } else { /* LZ4_decompress_fast() */ /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : * it doesn't know input length, and relies on end-of-block properties */ memcpy(op, ip, 8); if (length > 8) { memcpy(op+8, ip+8, 8); } } ip += length; op = cpy; } /* get offset */ offset = LZ4_readLE16(ip); ip+=2; match = op - offset; assert(match <= op); /* get matchlength */ length = token & ML_MASK; if (length == ML_MASK) { variable_length_error error = ok; if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ length += read_variable_length(&ip, iend - LASTLITERALS + 1, endOnInput, 0, &error); if (error != ok) { goto _output_error; } if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ length += MINMATCH; if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { goto safe_match_copy; } } else { length += MINMATCH; if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { goto safe_match_copy; } /* Fastpath check: Avoids a branch in LZ4_wildCopy32 if true */ if ((dict == withPrefix64k) || (match >= lowPrefix)) { if (offset >= 8) { assert(match >= lowPrefix); assert(match <= op); assert(op + 18 <= oend); memcpy(op, match, 8); memcpy(op+8, match+8, 8); memcpy(op+16, match+16, 2); op += length; continue; } } } if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ /* match starting within external dictionary */ if ((dict==usingExtDict) && (match < lowPrefix)) { if (unlikely(op+length > oend-LASTLITERALS)) { if (partialDecoding) { length = MIN(length, (size_t)(oend-op)); /* reach end of buffer */ } else { goto _output_error; /* end-of-block condition violated */ } } if (length <= (size_t)(lowPrefix-match)) { /* match fits entirely within external dictionary : just copy */ memmove(op, dictEnd - (lowPrefix-match), length); op += length; } else { /* match stretches into both external dictionary and current block */ size_t const copySize = (size_t)(lowPrefix - match); size_t const restSize = length - copySize; memcpy(op, dictEnd - copySize, copySize); op += copySize; if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ BYTE* const endOfMatch = op + restSize; const BYTE* copyFrom = lowPrefix; while (op < endOfMatch) { *op++ = *copyFrom++; } } else { memcpy(op, lowPrefix, restSize); op += restSize; } } continue; } /* copy match within block */ cpy = op + length; assert((op <= oend) && (oend-op >= 32)); if (unlikely(offset<16)) { LZ4_memcpy_using_offset(op, match, cpy, offset); } else { LZ4_wildCopy32(op, match, cpy); } op = cpy; /* wildcopy correction */ } safe_decode: #endif /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */ while (1) { token = *ip++; length = token >> ML_BITS; /* literal length */ assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ /* A two-stage shortcut for the most common case: * 1) If the literal length is 0..14, and there is enough space, * enter the shortcut and copy 16 bytes on behalf of the literals * (in the fast mode, only 8 bytes can be safely copied this way). * 2) Further if the match length is 4..18, copy 18 bytes in a similar * manner; but we ensure that there's enough space in the output for * those 18 bytes earlier, upon entering the shortcut (in other words, * there is a combined check for both stages). */ if ( (endOnInput ? length != RUN_MASK : length <= 8) /* strictly "less than" on input, to re-enter the loop with at least one byte */ && likely((endOnInput ? ip < shortiend : 1) & (op <= shortoend)) ) { /* Copy the literals */ memcpy(op, ip, endOnInput ? 16 : 8); op += length; ip += length; /* The second stage: prepare for match copying, decode full info. * If it doesn't work out, the info won't be wasted. */ length = token & ML_MASK; /* match length */ offset = LZ4_readLE16(ip); ip += 2; match = op - offset; assert(match <= op); /* check overflow */ /* Do not deal with overlapping matches. */ if ( (length != ML_MASK) && (offset >= 8) && (dict==withPrefix64k || match >= lowPrefix) ) { /* Copy the match. */ memcpy(op + 0, match + 0, 8); memcpy(op + 8, match + 8, 8); memcpy(op +16, match +16, 2); op += length + MINMATCH; /* Both stages worked, load the next token. */ continue; } /* The second stage didn't work out, but the info is ready. * Propel it right to the point of match copying. */ goto _copy_match; } /* decode literal length */ if (length == RUN_MASK) { variable_length_error error = ok; length += read_variable_length(&ip, iend-RUN_MASK, endOnInput, endOnInput, &error); if (error == initial_error) { goto _output_error; } if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ } /* copy literals */ cpy = op+length; #if LZ4_FAST_DEC_LOOP safe_literal_copy: #endif LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); if ( ((endOnInput) && ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) ) || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) ) { /* We've either hit the input parsing restriction or the output parsing restriction. * If we've hit the input parsing condition then this must be the last sequence. * If we've hit the output parsing condition then we are either using partialDecoding * or we've hit the output parsing condition. */ if (partialDecoding) { /* Since we are partial decoding we may be in this block because of the output parsing * restriction, which is not valid since the output buffer is allowed to be undersized. */ assert(endOnInput); /* If we're in this block because of the input parsing condition, then we must be on the * last sequence (or invalid), so we must check that we exactly consume the input. */ if ((ip+length>iend-(2+1+LASTLITERALS)) && (ip+length != iend)) { goto _output_error; } assert(ip+length <= iend); /* We are finishing in the middle of a literals segment. * Break after the copy. */ if (cpy > oend) { cpy = oend; assert(op<=oend); length = (size_t)(oend-op); } assert(ip+length <= iend); } else { /* We must be on the last sequence because of the parsing limitations so check * that we exactly regenerate the original size (must be exact when !endOnInput). */ if ((!endOnInput) && (cpy != oend)) { goto _output_error; } /* We must be on the last sequence (or invalid) because of the parsing limitations * so check that we exactly consume the input and don't overrun the output buffer. */ if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) { goto _output_error; } } memmove(op, ip, length); /* supports overlapping memory regions, which only matters for in-place decompression scenarios */ ip += length; op += length; /* Necessarily EOF when !partialDecoding. When partialDecoding * it is EOF if we've either filled the output buffer or hit * the input parsing restriction. */ if (!partialDecoding || (cpy == oend) || (ip == iend)) { break; } } else { LZ4_wildCopy8(op, ip, cpy); /* may overwrite up to WILDCOPYLENGTH beyond cpy */ ip += length; op = cpy; } /* get offset */ offset = LZ4_readLE16(ip); ip+=2; match = op - offset; /* get matchlength */ length = token & ML_MASK; _copy_match: if (length == ML_MASK) { variable_length_error error = ok; length += read_variable_length(&ip, iend - LASTLITERALS + 1, endOnInput, 0, &error); if (error != ok) goto _output_error; if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ } length += MINMATCH; #if LZ4_FAST_DEC_LOOP safe_match_copy: #endif if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */ /* match starting within external dictionary */ if ((dict==usingExtDict) && (match < lowPrefix)) { if (unlikely(op+length > oend-LASTLITERALS)) { if (partialDecoding) length = MIN(length, (size_t)(oend-op)); else goto _output_error; /* doesn't respect parsing restriction */ } if (length <= (size_t)(lowPrefix-match)) { /* match fits entirely within external dictionary : just copy */ memmove(op, dictEnd - (lowPrefix-match), length); op += length; } else { /* match stretches into both external dictionary and current block */ size_t const copySize = (size_t)(lowPrefix - match); size_t const restSize = length - copySize; memcpy(op, dictEnd - copySize, copySize); op += copySize; if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ BYTE* const endOfMatch = op + restSize; const BYTE* copyFrom = lowPrefix; while (op < endOfMatch) *op++ = *copyFrom++; } else { memcpy(op, lowPrefix, restSize); op += restSize; } } continue; } assert(match >= lowPrefix); /* copy match within block */ cpy = op + length; /* partialDecoding : may end anywhere within the block */ assert(op<=oend); if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { size_t const mlen = MIN(length, (size_t)(oend-op)); const BYTE* const matchEnd = match + mlen; BYTE* const copyEnd = op + mlen; if (matchEnd > op) { /* overlap copy */ while (op < copyEnd) { *op++ = *match++; } } else { memcpy(op, match, mlen); } op = copyEnd; if (op == oend) { break; } continue; } if (unlikely(offset<8)) { LZ4_write32(op, 0); /* silence msan warning when offset==0 */ op[0] = match[0]; op[1] = match[1]; op[2] = match[2]; op[3] = match[3]; match += inc32table[offset]; memcpy(op+4, match, 4); match -= dec64table[offset]; } else { memcpy(op, match, 8); match += 8; } op += 8; if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1); if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ if (op < oCopyLimit) { LZ4_wildCopy8(op, match, oCopyLimit); match += oCopyLimit - op; op = oCopyLimit; } while (op < cpy) { *op++ = *match++; } } else { memcpy(op, match, 8); if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } } op = cpy; /* wildcopy correction */ } /* end of decoding */ if (endOnInput) { return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ } else { return (int) (((const char*)ip)-src); /* Nb of input bytes read */ } /* Overflow error detected */ _output_error: return (int) (-(((const char*)ip)-src))-1; } } /*===== Instantiate the API decoding functions. =====*/ LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, decode_full_block, noDict, (BYTE*)dest, NULL, 0); } LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity) { dstCapacity = MIN(targetOutputSize, dstCapacity); return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity, endOnInputSize, partial_decode, noDict, (BYTE*)dst, NULL, 0); } LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_fast(const char* source, char* dest, int originalSize) { return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, decode_full_block, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 0); } /*===== Instantiate a few more decoding cases, used more than once. =====*/ LZ4_FORCE_O2_GCC_PPC64LE /* Exported, an obsolete API function. */ int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, decode_full_block, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 0); } /* Another obsolete API function, paired with the previous one. */ int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) { /* LZ4_decompress_fast doesn't validate match offsets, * and thus serves well with any prefixed dictionary. */ return LZ4_decompress_fast(source, dest, originalSize); } LZ4_FORCE_O2_GCC_PPC64LE static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize, size_t prefixSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, decode_full_block, noDict, (BYTE*)dest-prefixSize, NULL, 0); } LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const void* dictStart, size_t dictSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, decode_full_block, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); } LZ4_FORCE_O2_GCC_PPC64LE static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize, const void* dictStart, size_t dictSize) { return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, decode_full_block, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); } /* The "double dictionary" mode, for use with e.g. ring buffers: the first part * of the dictionary is passed as prefix, and the second via dictStart + dictSize. * These routines are used only once, in LZ4_decompress_*_continue(). */ LZ4_FORCE_INLINE int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize, size_t prefixSize, const void* dictStart, size_t dictSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, decode_full_block, usingExtDict, (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); } LZ4_FORCE_INLINE int LZ4_decompress_fast_doubleDict(const char* source, char* dest, int originalSize, size_t prefixSize, const void* dictStart, size_t dictSize) { return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, decode_full_block, usingExtDict, (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); } /*===== streaming decompression functions =====*/ LZ4_streamDecode_t* LZ4_createStreamDecode(void) { LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t)); LZ4_STATIC_ASSERT(LZ4_STREAMDECODESIZE >= sizeof(LZ4_streamDecode_t_internal)); /* A compilation error here means LZ4_STREAMDECODESIZE is not large enough */ return lz4s; } int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) { if (LZ4_stream == NULL) { return 0; } /* support free on NULL */ FREEMEM(LZ4_stream); return 0; } /*! LZ4_setStreamDecode() : * Use this function to instruct where to find the dictionary. * This function is not necessary if previous data is still available where it was decoded. * Loading a size of 0 is allowed (same effect as no dictionary). * @return : 1 if OK, 0 if error */ int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize) { LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; lz4sd->prefixSize = (size_t) dictSize; lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; lz4sd->externalDict = NULL; lz4sd->extDictSize = 0; return 1; } /*! LZ4_decoderRingBufferSize() : * when setting a ring buffer for streaming decompression (optional scenario), * provides the minimum size of this ring buffer * to be compatible with any source respecting maxBlockSize condition. * Note : in a ring buffer scenario, * blocks are presumed decompressed next to each other. * When not enough space remains for next block (remainingSize < maxBlockSize), * decoding resumes from beginning of ring buffer. * @return : minimum ring buffer size, * or 0 if there is an error (invalid maxBlockSize). */ int LZ4_decoderRingBufferSize(int maxBlockSize) { if (maxBlockSize < 0) return 0; if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0; if (maxBlockSize < 16) maxBlockSize = 16; return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize); } /* *_continue() : These decoding functions allow decompression of multiple blocks in "streaming" mode. Previously decoded blocks must still be available at the memory position where they were decoded. If it's not possible, save the relevant part of decoded data into a safe buffer, and indicate where it stands using LZ4_setStreamDecode() */ LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) { LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; int result; if (lz4sd->prefixSize == 0) { /* The first call, no dictionary yet. */ assert(lz4sd->extDictSize == 0); result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); if (result <= 0) return result; lz4sd->prefixSize = (size_t)result; lz4sd->prefixEnd = (BYTE*)dest + result; } else if (lz4sd->prefixEnd == (BYTE*)dest) { /* They're rolling the current segment. */ if (lz4sd->prefixSize >= 64 KB - 1) result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); else if (lz4sd->extDictSize == 0) result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, lz4sd->prefixSize); else result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize, lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize += (size_t)result; lz4sd->prefixEnd += result; } else { /* The buffer wraps around, or they're switching to another buffer. */ lz4sd->extDictSize = lz4sd->prefixSize; lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize = (size_t)result; lz4sd->prefixEnd = (BYTE*)dest + result; } return result; } LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize) { LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; int result; assert(originalSize >= 0); if (lz4sd->prefixSize == 0) { assert(lz4sd->extDictSize == 0); result = LZ4_decompress_fast(source, dest, originalSize); if (result <= 0) return result; lz4sd->prefixSize = (size_t)originalSize; lz4sd->prefixEnd = (BYTE*)dest + originalSize; } else if (lz4sd->prefixEnd == (BYTE*)dest) { if (lz4sd->prefixSize >= 64 KB - 1 || lz4sd->extDictSize == 0) result = LZ4_decompress_fast(source, dest, originalSize); else result = LZ4_decompress_fast_doubleDict(source, dest, originalSize, lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize += (size_t)originalSize; lz4sd->prefixEnd += originalSize; } else { lz4sd->extDictSize = lz4sd->prefixSize; lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; result = LZ4_decompress_fast_extDict(source, dest, originalSize, lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize = (size_t)originalSize; lz4sd->prefixEnd = (BYTE*)dest + originalSize; } return result; } /* Advanced decoding functions : *_usingDict() : These decoding functions work the same as "_continue" ones, the dictionary must be explicitly provided within parameters */ int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) { if (dictSize==0) return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); if (dictStart+dictSize == dest) { if (dictSize >= 64 KB - 1) { return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); } assert(dictSize >= 0); return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize); } assert(dictSize >= 0); return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize); } int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) { if (dictSize==0 || dictStart+dictSize == dest) return LZ4_decompress_fast(source, dest, originalSize); assert(dictSize >= 0); return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize); } /*=************************************************* * Obsolete Functions ***************************************************/ /* obsolete compression functions */ int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); } int LZ4_compress(const char* src, char* dest, int srcSize) { return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize)); } int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); } int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); } int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1); } int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); } /* These decompression functions are deprecated and should no longer be used. They are only provided here for compatibility with older user programs. - LZ4_uncompress is totally equivalent to LZ4_decompress_fast - LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe */ int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); } int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); } /* Obsolete Streaming functions */ int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; } int LZ4_resetStreamState(void* state, char* inputBuffer) { (void)inputBuffer; LZ4_resetStream((LZ4_stream_t*)state); return 0; } void* LZ4_create (char* inputBuffer) { (void)inputBuffer; return LZ4_createStream(); } char* LZ4_slideInputBuffer (void* state) { /* avoid const char * -> char * conversion warning */ return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary; } #endif /* LZ4_COMMONDEFS_ONLY */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/lz4/lib/lz4.h0000644000076500000240000011600714601576577020277 0ustar00twstaff/* * LZ4 - Fast LZ compression algorithm * Header File * Copyright (C) 2011-present, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 homepage : http://www.lz4.org - LZ4 source repository : https://github.com/lz4/lz4 */ #if defined (__cplusplus) extern "C" { #endif #ifndef LZ4_H_2983827168210 #define LZ4_H_2983827168210 /* --- Dependency --- */ #include /* size_t */ /** Introduction LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core, scalable with multi-cores CPU. It features an extremely fast decoder, with speed in multiple GB/s per core, typically reaching RAM speed limits on multi-core systems. The LZ4 compression library provides in-memory compression and decompression functions. It gives full buffer control to user. Compression can be done in: - a single step (described as Simple Functions) - a single step, reusing a context (described in Advanced Functions) - unbounded multiple steps (described as Streaming compression) lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md). Decompressing such a compressed block requires additional metadata. Exact metadata depends on exact decompression function. For the typical case of LZ4_decompress_safe(), metadata includes block's compressed size, and maximum bound of decompressed size. Each application is free to encode and pass such metadata in whichever way it wants. lz4.h only handle blocks, it can not generate Frames. Blocks are different from Frames (doc/lz4_Frame_format.md). Frames bundle both blocks and metadata in a specified manner. Embedding metadata is required for compressed data to be self-contained and portable. Frame format is delivered through a companion API, declared in lz4frame.h. The `lz4` CLI can only manage frames. */ /*^*************************************************************** * Export parameters *****************************************************************/ /* * LZ4_DLL_EXPORT : * Enable exporting of functions when building a Windows DLL * LZ4LIB_VISIBILITY : * Control library symbols visibility. */ #ifndef LZ4LIB_VISIBILITY # if defined(__GNUC__) && (__GNUC__ >= 4) # define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default"))) # else # define LZ4LIB_VISIBILITY # endif #endif #if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) # define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY #elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) # define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ #else # define LZ4LIB_API LZ4LIB_VISIBILITY #endif /*------ Version ------*/ #define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ #define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ #define LZ4_VERSION_RELEASE 2 /* for tweaks, bug-fixes, or development */ #define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) #define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE #define LZ4_QUOTE(str) #str #define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) #define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version */ LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version */ /*-************************************ * Tuning parameter **************************************/ /*! * LZ4_MEMORY_USAGE : * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) * Increasing memory usage improves compression ratio. * Reduced memory usage may improve speed, thanks to better cache locality. * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ #ifndef LZ4_MEMORY_USAGE # define LZ4_MEMORY_USAGE 14 #endif /*-************************************ * Simple Functions **************************************/ /*! LZ4_compress_default() : * Compresses 'srcSize' bytes from buffer 'src' * into already allocated 'dst' buffer of size 'dstCapacity'. * Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize). * It also runs faster, so it's a recommended setting. * If the function cannot compress 'src' into a more limited 'dst' budget, * compression stops *immediately*, and the function result is zero. * In which case, 'dst' content is undefined (invalid). * srcSize : max supported value is LZ4_MAX_INPUT_SIZE. * dstCapacity : size of buffer 'dst' (which must be already allocated) * @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity) * or 0 if compression fails * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer). */ LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity); /*! LZ4_decompress_safe() : * compressedSize : is the exact complete size of the compressed block. * dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size. * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) * If destination buffer is not large enough, decoding will stop and output an error code (negative value). * If the source stream is detected malformed, the function will stop decoding and return a negative result. * Note 1 : This function is protected against malicious data packets : * it will never writes outside 'dst' buffer, nor read outside 'source' buffer, * even if the compressed block is maliciously modified to order the decoder to do these actions. * In such case, the decoder stops immediately, and considers the compressed block malformed. * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them. * The implementation is free to send / store / derive this information in whichever way is most beneficial. * If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead. */ LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity); /*-************************************ * Advanced Functions **************************************/ #define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ #define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) /*! LZ4_compressBound() : Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) This function is primarily useful for memory allocation purposes (destination buffer size). Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize) inputSize : max supported value is LZ4_MAX_INPUT_SIZE return : maximum output size in a "worst case" scenario or 0, if input size is incorrect (too large or negative) */ LZ4LIB_API int LZ4_compressBound(int inputSize); /*! LZ4_compress_fast() : Same as LZ4_compress_default(), but allows selection of "acceleration" factor. The larger the acceleration value, the faster the algorithm, but also the lesser the compression. It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. An acceleration value of "1" is the same as regular LZ4_compress_default() Values <= 0 will be replaced by ACCELERATION_DEFAULT (currently == 1, see lz4.c). */ LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); /*! LZ4_compress_fast_extState() : * Same as LZ4_compress_fast(), using an externally allocated memory space for its state. * Use LZ4_sizeofState() to know how much memory must be allocated, * and allocate it on 8-bytes boundaries (using `malloc()` typically). * Then, provide this buffer as `void* state` to compression function. */ LZ4LIB_API int LZ4_sizeofState(void); LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); /*! LZ4_compress_destSize() : * Reverse the logic : compresses as much data as possible from 'src' buffer * into already allocated buffer 'dst', of size >= 'targetDestSize'. * This function either compresses the entire 'src' content into 'dst' if it's large enough, * or fill 'dst' buffer completely with as much data as possible from 'src'. * note: acceleration parameter is fixed to "default". * * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'. * New value is necessarily <= input value. * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize) * or 0 if compression fails. */ LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); /*! LZ4_decompress_safe_partial() : * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', * into destination buffer 'dst' of size 'dstCapacity'. * Up to 'targetOutputSize' bytes will be decoded. * The function stops decoding on reaching this objective, * which can boost performance when only the beginning of a block is required. * * @return : the number of bytes decoded in `dst` (necessarily <= dstCapacity) * If source stream is detected malformed, function returns a negative result. * * Note : @return can be < targetOutputSize, if compressed block contains less data. * * Note 2 : this function features 2 parameters, targetOutputSize and dstCapacity, * and expects targetOutputSize <= dstCapacity. * It effectively stops decoding on reaching targetOutputSize, * so dstCapacity is kind of redundant. * This is because in a previous version of this function, * decoding operation would not "break" a sequence in the middle. * As a consequence, there was no guarantee that decoding would stop at exactly targetOutputSize, * it could write more bytes, though only up to dstCapacity. * Some "margin" used to be required for this operation to work properly. * This is no longer necessary. * The function nonetheless keeps its signature, in an effort to not break API. */ LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity); /*-********************************************* * Streaming Compression Functions ***********************************************/ typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr); /*! LZ4_resetStream_fast() : v1.9.0+ * Use this to prepare an LZ4_stream_t for a new chain of dependent blocks * (e.g., LZ4_compress_fast_continue()). * * An LZ4_stream_t must be initialized once before usage. * This is automatically done when created by LZ4_createStream(). * However, should the LZ4_stream_t be simply declared on stack (for example), * it's necessary to initialize it first, using LZ4_initStream(). * * After init, start any new stream with LZ4_resetStream_fast(). * A same LZ4_stream_t can be re-used multiple times consecutively * and compress multiple streams, * provided that it starts each new stream with LZ4_resetStream_fast(). * * LZ4_resetStream_fast() is much faster than LZ4_initStream(), * but is not compatible with memory regions containing garbage data. * * Note: it's only useful to call LZ4_resetStream_fast() * in the context of streaming compression. * The *extState* functions perform their own resets. * Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive. */ LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr); /*! LZ4_loadDict() : * Use this function to reference a static dictionary into LZ4_stream_t. * The dictionary must remain available during compression. * LZ4_loadDict() triggers a reset, so any previous data will be forgotten. * The same dictionary will have to be loaded on decompression side for successful decoding. * Dictionary are useful for better compression of small data (KB range). * While LZ4 accept any input as dictionary, * results are generally better when using Zstandard's Dictionary Builder. * Loading a size of 0 is allowed, and is the same as reset. * @return : loaded dictionary size, in bytes (necessarily <= 64 KB) */ LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); /*! LZ4_compress_fast_continue() : * Compress 'src' content using data from previously compressed blocks, for better compression ratio. * 'dst' buffer must be already allocated. * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. * * @return : size of compressed block * or 0 if there is an error (typically, cannot fit into 'dst'). * * Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block. * Each block has precise boundaries. * Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata. * It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together. * * Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory ! * * Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB. * Make sure that buffers are separated, by at least one byte. * This construction ensures that each block only depends on previous block. * * Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB. * * Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed. */ LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); /*! LZ4_saveDict() : * If last 64KB data cannot be guaranteed to remain available at its current memory location, * save it into a safer place (char* safeBuffer). * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(), * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables. * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error. */ LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize); /*-********************************************** * Streaming Decompression Functions * Bufferless synchronous API ************************************************/ typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */ /*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() : * creation / destruction of streaming decompression tracking context. * A tracking context can be re-used multiple times. */ LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); /*! LZ4_setStreamDecode() : * An LZ4_streamDecode_t context can be allocated once and re-used multiple times. * Use this function to start decompression of a new stream of blocks. * A dictionary can optionally be set. Use NULL or size 0 for a reset order. * Dictionary is presumed stable : it must remain accessible and unmodified during next decompression. * @return : 1 if OK, 0 if error */ LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); /*! LZ4_decoderRingBufferSize() : v1.8.2+ * Note : in a ring buffer scenario (optional), * blocks are presumed decompressed next to each other * up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize), * at which stage it resumes from beginning of ring buffer. * When setting such a ring buffer for streaming decompression, * provides the minimum size of this ring buffer * to be compatible with any source respecting maxBlockSize condition. * @return : minimum ring buffer size, * or 0 if there is an error (invalid maxBlockSize). */ LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize); #define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */ /*! LZ4_decompress_*_continue() : * These decoding functions allow decompression of consecutive blocks in "streaming" mode. * A block is an unsplittable entity, it must be presented entirely to a decompression function. * Decompression functions only accepts one block at a time. * The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded. * If less than 64KB of data has been decoded, all the data must be present. * * Special : if decompression side sets a ring buffer, it must respect one of the following conditions : * - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize). * maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes. * In which case, encoding and decoding buffers do not need to be synchronized. * Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize. * - Synchronized mode : * Decompression buffer size is _exactly_ the same as compression buffer size, * and follows exactly same update rule (block boundaries at same positions), * and decoding function is provided with exact decompressed size of each block (exception for last block of the stream), * _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB). * - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes. * In which case, encoding and decoding buffers do not need to be synchronized, * and encoding ring buffer can have any size, including small ones ( < 64 KB). * * Whenever these conditions are not possible, * save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression, * then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block. */ LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity); /*! LZ4_decompress_*_usingDict() : * These decoding functions work the same as * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue() * They are stand-alone, and don't need an LZ4_streamDecode_t structure. * Dictionary is presumed stable : it must remain accessible and unmodified during decompression. * Performance tip : Decompression speed can be substantially increased * when dst == dictStart + dictSize. */ LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize); #endif /* LZ4_H_2983827168210 */ /*^************************************* * !!!!!! STATIC LINKING ONLY !!!!!! ***************************************/ /*-**************************************************************************** * Experimental section * * Symbols declared in this section must be considered unstable. Their * signatures or semantics may change, or they may be removed altogether in the * future. They are therefore only safe to depend on when the caller is * statically linked against the library. * * To protect against unsafe usage, not only are the declarations guarded, * the definitions are hidden by default * when building LZ4 as a shared/dynamic library. * * In order to access these declarations, * define LZ4_STATIC_LINKING_ONLY in your application * before including LZ4's headers. * * In order to make their implementations accessible dynamically, you must * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library. ******************************************************************************/ #ifdef LZ4_STATIC_LINKING_ONLY #ifndef LZ4_STATIC_3504398509 #define LZ4_STATIC_3504398509 #ifdef LZ4_PUBLISH_STATIC_FUNCTIONS #define LZ4LIB_STATIC_API LZ4LIB_API #else #define LZ4LIB_STATIC_API #endif /*! LZ4_compress_fast_extState_fastReset() : * A variant of LZ4_compress_fast_extState(). * * Using this variant avoids an expensive initialization step. * It is only safe to call if the state buffer is known to be correctly initialized already * (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized"). * From a high level, the difference is that * this function initializes the provided state with a call to something like LZ4_resetStream_fast() * while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream(). */ LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); /*! LZ4_attach_dictionary() : * This is an experimental API that allows * efficient use of a static dictionary many times. * * Rather than re-loading the dictionary buffer into a working context before * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a * working LZ4_stream_t, this function introduces a no-copy setup mechanism, * in which the working stream references the dictionary stream in-place. * * Several assumptions are made about the state of the dictionary stream. * Currently, only streams which have been prepared by LZ4_loadDict() should * be expected to work. * * Alternatively, the provided dictionaryStream may be NULL, * in which case any existing dictionary stream is unset. * * If a dictionary is provided, it replaces any pre-existing stream history. * The dictionary contents are the only history that can be referenced and * logically immediately precede the data compressed in the first subsequent * compression call. * * The dictionary will only remain attached to the working stream through the * first compression call, at the end of which it is cleared. The dictionary * stream (and source buffer) must remain in-place / accessible / unchanged * through the completion of the first compression call on the stream. */ LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream); /*! In-place compression and decompression * * It's possible to have input and output sharing the same buffer, * for highly contrained memory environments. * In both cases, it requires input to lay at the end of the buffer, * and decompression to start at beginning of the buffer. * Buffer size must feature some margin, hence be larger than final size. * * |<------------------------buffer--------------------------------->| * |<-----------compressed data--------->| * |<-----------decompressed size------------------>| * |<----margin---->| * * This technique is more useful for decompression, * since decompressed size is typically larger, * and margin is short. * * In-place decompression will work inside any buffer * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize). * This presumes that decompressedSize > compressedSize. * Otherwise, it means compression actually expanded data, * and it would be more efficient to store such data with a flag indicating it's not compressed. * This can happen when data is not compressible (already compressed, or encrypted). * * For in-place compression, margin is larger, as it must be able to cope with both * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX, * and data expansion, which can happen when input is not compressible. * As a consequence, buffer size requirements are much higher, * and memory savings offered by in-place compression are more limited. * * There are ways to limit this cost for compression : * - Reduce history size, by modifying LZ4_DISTANCE_MAX. * Note that it is a compile-time constant, so all compressions will apply this limit. * Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX, * so it's a reasonable trick when inputs are known to be small. * - Require the compressor to deliver a "maximum compressed size". * This is the `dstCapacity` parameter in `LZ4_compress*()`. * When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail, * in which case, the return code will be 0 (zero). * The caller must be ready for these cases to happen, * and typically design a backup scheme to send data uncompressed. * The combination of both techniques can significantly reduce * the amount of margin required for in-place compression. * * In-place compression can work in any buffer * which size is >= (maxCompressedSize) * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success. * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX, * so it's possible to reduce memory requirements by playing with them. */ #define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32) #define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */ #ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */ # define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */ #endif #define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */ #define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */ #endif /* LZ4_STATIC_3504398509 */ #endif /* LZ4_STATIC_LINKING_ONLY */ #ifndef LZ4_H_98237428734687 #define LZ4_H_98237428734687 /*-************************************************************ * PRIVATE DEFINITIONS ************************************************************** * Do not use these definitions directly. * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. * Accessing members will expose code to API and/or ABI break in future versions of the library. **************************************************************/ #define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) #define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) #define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ #if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) #include typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; struct LZ4_stream_t_internal { uint32_t hashTable[LZ4_HASH_SIZE_U32]; uint32_t currentOffset; uint16_t dirty; uint16_t tableType; const uint8_t* dictionary; const LZ4_stream_t_internal* dictCtx; uint32_t dictSize; }; typedef struct { const uint8_t* externalDict; size_t extDictSize; const uint8_t* prefixEnd; size_t prefixSize; } LZ4_streamDecode_t_internal; #else typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; struct LZ4_stream_t_internal { unsigned int hashTable[LZ4_HASH_SIZE_U32]; unsigned int currentOffset; unsigned short dirty; unsigned short tableType; const unsigned char* dictionary; const LZ4_stream_t_internal* dictCtx; unsigned int dictSize; }; typedef struct { const unsigned char* externalDict; const unsigned char* prefixEnd; size_t extDictSize; size_t prefixSize; } LZ4_streamDecode_t_internal; #endif /*! LZ4_stream_t : * information structure to track an LZ4 stream. * LZ4_stream_t can also be created using LZ4_createStream(), which is recommended. * The structure definition can be convenient for static allocation * (on stack, or as part of larger structure). * Init this structure with LZ4_initStream() before first use. * note : only use this definition in association with static linking ! * this definition is not API/ABI safe, and may change in a future version. */ #define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4 + ((sizeof(void*)==16) ? 4 : 0) /*AS-400*/ ) #define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long)) union LZ4_stream_u { unsigned long long table[LZ4_STREAMSIZE_U64]; LZ4_stream_t_internal internal_donotuse; } ; /* previously typedef'd to LZ4_stream_t */ /*! LZ4_initStream() : v1.9.0+ * An LZ4_stream_t structure must be initialized at least once. * This is automatically done when invoking LZ4_createStream(), * but it's not when the structure is simply declared on stack (for example). * * Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t. * It can also initialize any arbitrary buffer of sufficient size, * and will @return a pointer of proper type upon initialization. * * Note : initialization fails if size and alignment conditions are not respected. * In which case, the function will @return NULL. * Note2: An LZ4_stream_t structure guarantees correct alignment and size. * Note3: Before v1.9.0, use LZ4_resetStream() instead */ LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size); /*! LZ4_streamDecode_t : * information structure to track an LZ4 stream during decompression. * init this structure using LZ4_setStreamDecode() before first use. * note : only use in association with static linking ! * this definition is not API/ABI safe, * and may change in a future version ! */ #define LZ4_STREAMDECODESIZE_U64 (4 + ((sizeof(void*)==16) ? 2 : 0) /*AS-400*/ ) #define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) union LZ4_streamDecode_u { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; LZ4_streamDecode_t_internal internal_donotuse; } ; /* previously typedef'd to LZ4_streamDecode_t */ /*-************************************ * Obsolete Functions **************************************/ /*! Deprecation warnings * * Deprecated functions make the compiler generate a warning when invoked. * This is meant to invite users to update their source code. * Should deprecation warnings be a problem, it is generally possible to disable them, * typically with -Wno-deprecated-declarations for gcc * or _CRT_SECURE_NO_WARNINGS in Visual. * * Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS * before including the header file. */ #ifdef LZ4_DISABLE_DEPRECATE_WARNINGS # define LZ4_DEPRECATED(message) /* disable deprecation warnings */ #else # define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ # define LZ4_DEPRECATED(message) [[deprecated(message)]] # elif (LZ4_GCC_VERSION >= 405) || defined(__clang__) # define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) # elif (LZ4_GCC_VERSION >= 301) # define LZ4_DEPRECATED(message) __attribute__((deprecated)) # elif defined(_MSC_VER) # define LZ4_DEPRECATED(message) __declspec(deprecated(message)) # else # pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler") # define LZ4_DEPRECATED(message) # endif #endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ /* Obsolete compression functions */ LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize); LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize); LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); /* Obsolete decompression functions */ LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize); LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); /* Obsolete streaming functions; degraded functionality; do not use! * * In order to perform streaming compression, these functions depended on data * that is no longer tracked in the state. They have been preserved as well as * possible: using them will still produce a correct output. However, they don't * actually retain any history between compression calls. The compression ratio * achieved will therefore be no better than compressing each chunk * independently. */ LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer); LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void); LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer); LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state); /* Obsolete streaming decoding functions */ LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); /*! LZ4_decompress_fast() : **unsafe!** * These functions used to be faster than LZ4_decompress_safe(), * but it has changed, and they are now slower than LZ4_decompress_safe(). * This is because LZ4_decompress_fast() doesn't know the input size, * and therefore must progress more cautiously in the input buffer to not read beyond the end of block. * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability. * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated. * * The last remaining LZ4_decompress_fast() specificity is that * it can decompress a block without knowing its compressed size. * Such functionality could be achieved in a more secure manner, * by also providing the maximum size of input buffer, * but it would require new prototypes, and adaptation of the implementation to this new use case. * * Parameters: * originalSize : is the uncompressed size to regenerate. * `dst` must be already allocated, its size must be >= 'originalSize' bytes. * @return : number of bytes read from source buffer (== compressed size). * The function expects to finish at block's end exactly. * If the source stream is detected malformed, the function stops decoding and returns a negative result. * note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer. * However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds. * Also, since match offsets are not validated, match reads from 'src' may underflow too. * These issues never happen if input (compressed) data is correct. * But they may happen if input data is invalid (error or intentional tampering). * As a consequence, use these functions in trusted environments with trusted data **only**. */ LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead") LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize); LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize); /*! LZ4_resetStream() : * An LZ4_stream_t structure must be initialized at least once. * This is done with LZ4_initStream(), or LZ4_resetStream(). * Consider switching to LZ4_initStream(), * invoking LZ4_resetStream() will trigger deprecation warnings in the future. */ LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr); #endif /* LZ4_H_98237428734687 */ #if defined (__cplusplus) } #endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/lz4-libselect.h0000644000076500000240000000012314601576577020753 0ustar00twstaff#ifdef BORG_USE_BUNDLED_LZ4 #include "lz4/lib/lz4.h" #else #include #endif ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1690786 borgbackup-1.2.8/src/borg/algorithms/xxh64/0000755000076500000240000000000014601577064017102 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/xxh64/xxhash.c0000644000076500000240000000347714601576577020574 0ustar00twstaff/* * xxHash - Extremely Fast Hash algorithm * Copyright (C) 2012-2020 Yann Collet * * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You can contact the author at: * - xxHash homepage: https://www.xxhash.com * - xxHash source repository: https://github.com/Cyan4973/xxHash */ /* * xxhash.c instantiates functions defined in xxhash.h */ #define XXH_STATIC_LINKING_ONLY /* access advanced declarations */ #define XXH_IMPLEMENTATION /* access definitions */ #include "xxhash.h" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/xxh64/xxhash.h0000644000076500000240000063135614601576577020604 0ustar00twstaff/* * xxHash - Extremely Fast Hash algorithm * Header File * Copyright (C) 2012-2020 Yann Collet * * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You can contact the author at: * - xxHash homepage: https://www.xxhash.com * - xxHash source repository: https://github.com/Cyan4973/xxHash */ /*! * @mainpage xxHash * * @file xxhash.h * xxHash prototypes and implementation */ /* TODO: update */ /* Notice extracted from xxHash homepage: xxHash is an extremely fast hash algorithm, running at RAM speed limits. It also successfully passes all tests from the SMHasher suite. Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) Name Speed Q.Score Author xxHash 5.4 GB/s 10 CrapWow 3.2 GB/s 2 Andrew MurmurHash 3a 2.7 GB/s 10 Austin Appleby SpookyHash 2.0 GB/s 10 Bob Jenkins SBox 1.4 GB/s 9 Bret Mulvey Lookup3 1.2 GB/s 9 Bob Jenkins SuperFastHash 1.2 GB/s 1 Paul Hsieh CityHash64 1.05 GB/s 10 Pike & Alakuijala FNV 0.55 GB/s 5 Fowler, Noll, Vo CRC32 0.43 GB/s 9 MD5-32 0.33 GB/s 10 Ronald L. Rivest SHA1-32 0.28 GB/s 10 Q.Score is a measure of quality of the hash function. It depends on successfully passing SMHasher test set. 10 is a perfect score. Note: SMHasher's CRC32 implementation is not the fastest one. Other speed-oriented implementations can be faster, especially in combination with PCLMUL instruction: https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html?showComment=1552696407071#c3490092340461170735 A 64-bit version, named XXH64, is available since r35. It offers much better speed, but for 64-bit applications only. Name Speed on 64 bits Speed on 32 bits XXH64 13.8 GB/s 1.9 GB/s XXH32 6.8 GB/s 6.0 GB/s */ #if defined (__cplusplus) extern "C" { #endif /* **************************** * INLINE mode ******************************/ /*! * XXH_INLINE_ALL (and XXH_PRIVATE_API) * Use these build macros to inline xxhash into the target unit. * Inlining improves performance on small inputs, especially when the length is * expressed as a compile-time constant: * * https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html * * It also keeps xxHash symbols private to the unit, so they are not exported. * * Usage: * #define XXH_INLINE_ALL * #include "xxhash.h" * * Do not compile and link xxhash.o as a separate object, as it is not useful. */ #if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)) \ && !defined(XXH_INLINE_ALL_31684351384) /* this section should be traversed only once */ # define XXH_INLINE_ALL_31684351384 /* give access to the advanced API, required to compile implementations */ # undef XXH_STATIC_LINKING_ONLY /* avoid macro redef */ # define XXH_STATIC_LINKING_ONLY /* make all functions private */ # undef XXH_PUBLIC_API # if defined(__GNUC__) # define XXH_PUBLIC_API static __inline __attribute__((unused)) # elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # define XXH_PUBLIC_API static inline # elif defined(_MSC_VER) # define XXH_PUBLIC_API static __inline # else /* note: this version may generate warnings for unused static functions */ # define XXH_PUBLIC_API static # endif /* * This part deals with the special case where a unit wants to inline xxHash, * but "xxhash.h" has previously been included without XXH_INLINE_ALL, * such as part of some previously included *.h header file. * Without further action, the new include would just be ignored, * and functions would effectively _not_ be inlined (silent failure). * The following macros solve this situation by prefixing all inlined names, * avoiding naming collision with previous inclusions. */ /* Before that, we unconditionally #undef all symbols, * in case they were already defined with XXH_NAMESPACE. * They will then be redefined for XXH_INLINE_ALL */ # undef XXH_versionNumber /* XXH32 */ # undef XXH32 # undef XXH32_createState # undef XXH32_freeState # undef XXH32_reset # undef XXH32_update # undef XXH32_digest # undef XXH32_copyState # undef XXH32_canonicalFromHash # undef XXH32_hashFromCanonical /* XXH64 */ # undef XXH64 # undef XXH64_createState # undef XXH64_freeState # undef XXH64_reset # undef XXH64_update # undef XXH64_digest # undef XXH64_copyState # undef XXH64_canonicalFromHash # undef XXH64_hashFromCanonical /* XXH3_64bits */ # undef XXH3_64bits # undef XXH3_64bits_withSecret # undef XXH3_64bits_withSeed # undef XXH3_64bits_withSecretandSeed # undef XXH3_createState # undef XXH3_freeState # undef XXH3_copyState # undef XXH3_64bits_reset # undef XXH3_64bits_reset_withSeed # undef XXH3_64bits_reset_withSecret # undef XXH3_64bits_update # undef XXH3_64bits_digest # undef XXH3_generateSecret /* XXH3_128bits */ # undef XXH128 # undef XXH3_128bits # undef XXH3_128bits_withSeed # undef XXH3_128bits_withSecret # undef XXH3_128bits_reset # undef XXH3_128bits_reset_withSeed # undef XXH3_128bits_reset_withSecret # undef XXH3_128bits_reset_withSecretandSeed # undef XXH3_128bits_update # undef XXH3_128bits_digest # undef XXH128_isEqual # undef XXH128_cmp # undef XXH128_canonicalFromHash # undef XXH128_hashFromCanonical /* Finally, free the namespace itself */ # undef XXH_NAMESPACE /* employ the namespace for XXH_INLINE_ALL */ # define XXH_NAMESPACE XXH_INLINE_ /* * Some identifiers (enums, type names) are not symbols, * but they must nonetheless be renamed to avoid redeclaration. * Alternative solution: do not redeclare them. * However, this requires some #ifdefs, and has a more dispersed impact. * Meanwhile, renaming can be achieved in a single place. */ # define XXH_IPREF(Id) XXH_NAMESPACE ## Id # define XXH_OK XXH_IPREF(XXH_OK) # define XXH_ERROR XXH_IPREF(XXH_ERROR) # define XXH_errorcode XXH_IPREF(XXH_errorcode) # define XXH32_canonical_t XXH_IPREF(XXH32_canonical_t) # define XXH64_canonical_t XXH_IPREF(XXH64_canonical_t) # define XXH128_canonical_t XXH_IPREF(XXH128_canonical_t) # define XXH32_state_s XXH_IPREF(XXH32_state_s) # define XXH32_state_t XXH_IPREF(XXH32_state_t) # define XXH64_state_s XXH_IPREF(XXH64_state_s) # define XXH64_state_t XXH_IPREF(XXH64_state_t) # define XXH3_state_s XXH_IPREF(XXH3_state_s) # define XXH3_state_t XXH_IPREF(XXH3_state_t) # define XXH128_hash_t XXH_IPREF(XXH128_hash_t) /* Ensure the header is parsed again, even if it was previously included */ # undef XXHASH_H_5627135585666179 # undef XXHASH_H_STATIC_13879238742 #endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ /* **************************************************************** * Stable API *****************************************************************/ #ifndef XXHASH_H_5627135585666179 #define XXHASH_H_5627135585666179 1 /*! * @defgroup public Public API * Contains details on the public xxHash functions. * @{ */ /* specific declaration modes for Windows */ #if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API) # if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) # ifdef XXH_EXPORT # define XXH_PUBLIC_API __declspec(dllexport) # elif XXH_IMPORT # define XXH_PUBLIC_API __declspec(dllimport) # endif # else # define XXH_PUBLIC_API /* do nothing */ # endif #endif #ifdef XXH_DOXYGEN /*! * @brief Emulate a namespace by transparently prefixing all symbols. * * If you want to include _and expose_ xxHash functions from within your own * library, but also want to avoid symbol collisions with other libraries which * may also include xxHash, you can use XXH_NAMESPACE to automatically prefix * any public symbol from xxhash library with the value of XXH_NAMESPACE * (therefore, avoid empty or numeric values). * * Note that no change is required within the calling program as long as it * includes `xxhash.h`: Regular symbol names will be automatically translated * by this header. */ # define XXH_NAMESPACE /* YOUR NAME HERE */ # undef XXH_NAMESPACE #endif #ifdef XXH_NAMESPACE # define XXH_CAT(A,B) A##B # define XXH_NAME2(A,B) XXH_CAT(A,B) # define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) /* XXH32 */ # define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) # define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) # define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) # define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) # define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) # define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) # define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) # define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) # define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) /* XXH64 */ # define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) # define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) # define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) # define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) # define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) # define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) # define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) # define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) # define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) /* XXH3_64bits */ # define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits) # define XXH3_64bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret) # define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed) # define XXH3_64bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecretandSeed) # define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState) # define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState) # define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState) # define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset) # define XXH3_64bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed) # define XXH3_64bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret) # define XXH3_64bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecretandSeed) # define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update) # define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest) # define XXH3_generateSecret XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret) # define XXH3_generateSecret_fromSeed XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret_fromSeed) /* XXH3_128bits */ # define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128) # define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits) # define XXH3_128bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed) # define XXH3_128bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret) # define XXH3_128bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecretandSeed) # define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset) # define XXH3_128bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed) # define XXH3_128bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret) # define XXH3_128bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecretandSeed) # define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update) # define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest) # define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual) # define XXH128_cmp XXH_NAME2(XXH_NAMESPACE, XXH128_cmp) # define XXH128_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH128_canonicalFromHash) # define XXH128_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH128_hashFromCanonical) #endif /* ************************************* * Version ***************************************/ #define XXH_VERSION_MAJOR 0 #define XXH_VERSION_MINOR 8 #define XXH_VERSION_RELEASE 1 #define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) /*! * @brief Obtains the xxHash version. * * This is mostly useful when xxHash is compiled as a shared library, * since the returned value comes from the library, as opposed to header file. * * @return `XXH_VERSION_NUMBER` of the invoked library. */ XXH_PUBLIC_API unsigned XXH_versionNumber (void); /* **************************** * Common basic types ******************************/ #include /* size_t */ typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; /*-********************************************************************** * 32-bit hash ************************************************************************/ #if defined(XXH_DOXYGEN) /* Don't show include */ /*! * @brief An unsigned 32-bit integer. * * Not necessarily defined to `uint32_t` but functionally equivalent. */ typedef uint32_t XXH32_hash_t; #elif !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include typedef uint32_t XXH32_hash_t; #else # include # if UINT_MAX == 0xFFFFFFFFUL typedef unsigned int XXH32_hash_t; # else # if ULONG_MAX == 0xFFFFFFFFUL typedef unsigned long XXH32_hash_t; # else # error "unsupported platform: need a 32-bit type" # endif # endif #endif /*! * @} * * @defgroup xxh32_family XXH32 family * @ingroup public * Contains functions used in the classic 32-bit xxHash algorithm. * * @note * XXH32 is useful for older platforms, with no or poor 64-bit performance. * Note that @ref xxh3_family provides competitive speed * for both 32-bit and 64-bit systems, and offers true 64/128 bit hash results. * * @see @ref xxh64_family, @ref xxh3_family : Other xxHash families * @see @ref xxh32_impl for implementation details * @{ */ /*! * @brief Calculates the 32-bit hash of @p input using xxHash32. * * Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark): 5.4 GB/s * * @param input The block of data to be hashed, at least @p length bytes in size. * @param length The length of @p input, in bytes. * @param seed The 32-bit seed to alter the hash's output predictably. * * @pre * The memory between @p input and @p input + @p length must be valid, * readable, contiguous memory. However, if @p length is `0`, @p input may be * `NULL`. In C++, this also must be *TriviallyCopyable*. * * @return The calculated 32-bit hash value. * * @see * XXH64(), XXH3_64bits_withSeed(), XXH3_128bits_withSeed(), XXH128(): * Direct equivalents for the other variants of xxHash. * @see * XXH32_createState(), XXH32_update(), XXH32_digest(): Streaming version. */ XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed); /*! * Streaming functions generate the xxHash value from an incremental input. * This method is slower than single-call functions, due to state management. * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. * * An XXH state must first be allocated using `XXH*_createState()`. * * Start a new hash by initializing the state with a seed using `XXH*_reset()`. * * Then, feed the hash state by calling `XXH*_update()` as many times as necessary. * * The function returns an error code, with 0 meaning OK, and any other value * meaning there is an error. * * Finally, a hash value can be produced anytime, by using `XXH*_digest()`. * This function returns the nn-bits hash as an int or long long. * * It's still possible to continue inserting input into the hash state after a * digest, and generate new hash values later on by invoking `XXH*_digest()`. * * When done, release the state using `XXH*_freeState()`. * * Example code for incrementally hashing a file: * @code{.c} * #include * #include * #define BUFFER_SIZE 256 * * // Note: XXH64 and XXH3 use the same interface. * XXH32_hash_t * hashFile(FILE* stream) * { * XXH32_state_t* state; * unsigned char buf[BUFFER_SIZE]; * size_t amt; * XXH32_hash_t hash; * * state = XXH32_createState(); // Create a state * assert(state != NULL); // Error check here * XXH32_reset(state, 0xbaad5eed); // Reset state with our seed * while ((amt = fread(buf, 1, sizeof(buf), stream)) != 0) { * XXH32_update(state, buf, amt); // Hash the file in chunks * } * hash = XXH32_digest(state); // Finalize the hash * XXH32_freeState(state); // Clean up * return hash; * } * @endcode */ /*! * @typedef struct XXH32_state_s XXH32_state_t * @brief The opaque state struct for the XXH32 streaming API. * * @see XXH32_state_s for details. */ typedef struct XXH32_state_s XXH32_state_t; /*! * @brief Allocates an @ref XXH32_state_t. * * Must be freed with XXH32_freeState(). * @return An allocated XXH32_state_t on success, `NULL` on failure. */ XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); /*! * @brief Frees an @ref XXH32_state_t. * * Must be allocated with XXH32_createState(). * @param statePtr A pointer to an @ref XXH32_state_t allocated with @ref XXH32_createState(). * @return XXH_OK. */ XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); /*! * @brief Copies one @ref XXH32_state_t to another. * * @param dst_state The state to copy to. * @param src_state The state to copy from. * @pre * @p dst_state and @p src_state must not be `NULL` and must not overlap. */ XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); /*! * @brief Resets an @ref XXH32_state_t to begin a new hash. * * This function resets and seeds a state. Call it before @ref XXH32_update(). * * @param statePtr The state struct to reset. * @param seed The 32-bit seed to alter the hash result predictably. * * @pre * @p statePtr must not be `NULL`. * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. */ XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, XXH32_hash_t seed); /*! * @brief Consumes a block of @p input to an @ref XXH32_state_t. * * Call this to incrementally consume blocks of data. * * @param statePtr The state struct to update. * @param input The block of data to be hashed, at least @p length bytes in size. * @param length The length of @p input, in bytes. * * @pre * @p statePtr must not be `NULL`. * @pre * The memory between @p input and @p input + @p length must be valid, * readable, contiguous memory. However, if @p length is `0`, @p input may be * `NULL`. In C++, this also must be *TriviallyCopyable*. * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. */ XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); /*! * @brief Returns the calculated hash value from an @ref XXH32_state_t. * * @note * Calling XXH32_digest() will not affect @p statePtr, so you can update, * digest, and update again. * * @param statePtr The state struct to calculate the hash from. * * @pre * @p statePtr must not be `NULL`. * * @return The calculated xxHash32 value from that state. */ XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); /******* Canonical representation *******/ /* * The default return values from XXH functions are unsigned 32 and 64 bit * integers. * This the simplest and fastest format for further post-processing. * * However, this leaves open the question of what is the order on the byte level, * since little and big endian conventions will store the same number differently. * * The canonical representation settles this issue by mandating big-endian * convention, the same convention as human-readable numbers (large digits first). * * When writing hash values to storage, sending them over a network, or printing * them, it's highly recommended to use the canonical representation to ensure * portability across a wider range of systems, present and future. * * The following functions allow transformation of hash values to and from * canonical format. */ /*! * @brief Canonical (big endian) representation of @ref XXH32_hash_t. */ typedef struct { unsigned char digest[4]; /*!< Hash bytes, big endian */ } XXH32_canonical_t; /*! * @brief Converts an @ref XXH32_hash_t to a big endian @ref XXH32_canonical_t. * * @param dst The @ref XXH32_canonical_t pointer to be stored to. * @param hash The @ref XXH32_hash_t to be converted. * * @pre * @p dst must not be `NULL`. */ XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); /*! * @brief Converts an @ref XXH32_canonical_t to a native @ref XXH32_hash_t. * * @param src The @ref XXH32_canonical_t to convert. * * @pre * @p src must not be `NULL`. * * @return The converted hash. */ XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); #ifdef __has_attribute # define XXH_HAS_ATTRIBUTE(x) __has_attribute(x) #else # define XXH_HAS_ATTRIBUTE(x) 0 #endif /* C-language Attributes are added in C23. */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ > 201710L) && defined(__has_c_attribute) # define XXH_HAS_C_ATTRIBUTE(x) __has_c_attribute(x) #else # define XXH_HAS_C_ATTRIBUTE(x) 0 #endif #if defined(__cplusplus) && defined(__has_cpp_attribute) # define XXH_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) #else # define XXH_HAS_CPP_ATTRIBUTE(x) 0 #endif /* Define XXH_FALLTHROUGH macro for annotating switch case with the 'fallthrough' attribute introduced in CPP17 and C23. CPP17 : https://en.cppreference.com/w/cpp/language/attributes/fallthrough C23 : https://en.cppreference.com/w/c/language/attributes/fallthrough */ #if XXH_HAS_C_ATTRIBUTE(x) # define XXH_FALLTHROUGH [[fallthrough]] #elif XXH_HAS_CPP_ATTRIBUTE(x) # define XXH_FALLTHROUGH [[fallthrough]] #elif XXH_HAS_ATTRIBUTE(__fallthrough__) # define XXH_FALLTHROUGH __attribute__ ((fallthrough)) #else # define XXH_FALLTHROUGH #endif /*! * @} * @ingroup public * @{ */ #ifndef XXH_NO_LONG_LONG /*-********************************************************************** * 64-bit hash ************************************************************************/ #if defined(XXH_DOXYGEN) /* don't include */ /*! * @brief An unsigned 64-bit integer. * * Not necessarily defined to `uint64_t` but functionally equivalent. */ typedef uint64_t XXH64_hash_t; #elif !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include typedef uint64_t XXH64_hash_t; #else # include # if defined(__LP64__) && ULONG_MAX == 0xFFFFFFFFFFFFFFFFULL /* LP64 ABI says uint64_t is unsigned long */ typedef unsigned long XXH64_hash_t; # else /* the following type must have a width of 64-bit */ typedef unsigned long long XXH64_hash_t; # endif #endif /*! * @} * * @defgroup xxh64_family XXH64 family * @ingroup public * @{ * Contains functions used in the classic 64-bit xxHash algorithm. * * @note * XXH3 provides competitive speed for both 32-bit and 64-bit systems, * and offers true 64/128 bit hash results. * It provides better speed for systems with vector processing capabilities. */ /*! * @brief Calculates the 64-bit hash of @p input using xxHash64. * * This function usually runs faster on 64-bit systems, but slower on 32-bit * systems (see benchmark). * * @param input The block of data to be hashed, at least @p length bytes in size. * @param length The length of @p input, in bytes. * @param seed The 64-bit seed to alter the hash's output predictably. * * @pre * The memory between @p input and @p input + @p length must be valid, * readable, contiguous memory. However, if @p length is `0`, @p input may be * `NULL`. In C++, this also must be *TriviallyCopyable*. * * @return The calculated 64-bit hash. * * @see * XXH32(), XXH3_64bits_withSeed(), XXH3_128bits_withSeed(), XXH128(): * Direct equivalents for the other variants of xxHash. * @see * XXH64_createState(), XXH64_update(), XXH64_digest(): Streaming version. */ XXH_PUBLIC_API XXH64_hash_t XXH64(const void* input, size_t length, XXH64_hash_t seed); /******* Streaming *******/ /*! * @brief The opaque state struct for the XXH64 streaming API. * * @see XXH64_state_s for details. */ typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, XXH64_hash_t seed); XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); /******* Canonical representation *******/ typedef struct { unsigned char digest[sizeof(XXH64_hash_t)]; } XXH64_canonical_t; XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); /*! * @} * ************************************************************************ * @defgroup xxh3_family XXH3 family * @ingroup public * @{ * * XXH3 is a more recent hash algorithm featuring: * - Improved speed for both small and large inputs * - True 64-bit and 128-bit outputs * - SIMD acceleration * - Improved 32-bit viability * * Speed analysis methodology is explained here: * * https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html * * Compared to XXH64, expect XXH3 to run approximately * ~2x faster on large inputs and >3x faster on small ones, * exact differences vary depending on platform. * * XXH3's speed benefits greatly from SIMD and 64-bit arithmetic, * but does not require it. * Any 32-bit and 64-bit targets that can run XXH32 smoothly * can run XXH3 at competitive speeds, even without vector support. * Further details are explained in the implementation. * * Optimized implementations are provided for AVX512, AVX2, SSE2, NEON, POWER8, * ZVector and scalar targets. This can be controlled via the XXH_VECTOR macro. * * XXH3 implementation is portable: * it has a generic C90 formulation that can be compiled on any platform, * all implementations generage exactly the same hash value on all platforms. * Starting from v0.8.0, it's also labelled "stable", meaning that * any future version will also generate the same hash value. * * XXH3 offers 2 variants, _64bits and _128bits. * * When only 64 bits are needed, prefer invoking the _64bits variant, as it * reduces the amount of mixing, resulting in faster speed on small inputs. * It's also generally simpler to manipulate a scalar return type than a struct. * * The API supports one-shot hashing, streaming mode, and custom secrets. */ /*-********************************************************************** * XXH3 64-bit variant ************************************************************************/ /* XXH3_64bits(): * default 64-bit variant, using default secret and default seed of 0. * It's the fastest variant. */ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len); /* * XXH3_64bits_withSeed(): * This variant generates a custom secret on the fly * based on default secret altered using the `seed` value. * While this operation is decently fast, note that it's not completely free. * Note: seed==0 produces the same results as XXH3_64bits(). */ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); /*! * The bare minimum size for a custom secret. * * @see * XXH3_64bits_withSecret(), XXH3_64bits_reset_withSecret(), * XXH3_128bits_withSecret(), XXH3_128bits_reset_withSecret(). */ #define XXH3_SECRET_SIZE_MIN 136 /* * XXH3_64bits_withSecret(): * It's possible to provide any blob of bytes as a "secret" to generate the hash. * This makes it more difficult for an external actor to prepare an intentional collision. * The main condition is that secretSize *must* be large enough (>= XXH3_SECRET_SIZE_MIN). * However, the quality of the secret impacts the dispersion of the hash algorithm. * Therefore, the secret _must_ look like a bunch of random bytes. * Avoid "trivial" or structured data such as repeated sequences or a text document. * Whenever in doubt about the "randomness" of the blob of bytes, * consider employing "XXH3_generateSecret()" instead (see below). * It will generate a proper high entropy secret derived from the blob of bytes. * Another advantage of using XXH3_generateSecret() is that * it guarantees that all bits within the initial blob of bytes * will impact every bit of the output. * This is not necessarily the case when using the blob of bytes directly * because, when hashing _small_ inputs, only a portion of the secret is employed. */ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); /******* Streaming *******/ /* * Streaming requires state maintenance. * This operation costs memory and CPU. * As a consequence, streaming is slower than one-shot hashing. * For better performance, prefer one-shot functions whenever applicable. */ /*! * @brief The state struct for the XXH3 streaming API. * * @see XXH3_state_s for details. */ typedef struct XXH3_state_s XXH3_state_t; XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void); XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr); XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state); /* * XXH3_64bits_reset(): * Initialize with default parameters. * digest will be equivalent to `XXH3_64bits()`. */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t* statePtr); /* * XXH3_64bits_reset_withSeed(): * Generate a custom secret from `seed`, and store it into `statePtr`. * digest will be equivalent to `XXH3_64bits_withSeed()`. */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); /* * XXH3_64bits_reset_withSecret(): * `secret` is referenced, it _must outlive_ the hash streaming session. * Similar to one-shot API, `secretSize` must be >= `XXH3_SECRET_SIZE_MIN`, * and the quality of produced hash values depends on secret's entropy * (secret's content should look like a bunch of random bytes). * When in doubt about the randomness of a candidate `secret`, * consider employing `XXH3_generateSecret()` instead (see below). */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH3_state_t* statePtr, const void* input, size_t length); XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* statePtr); /* note : canonical representation of XXH3 is the same as XXH64 * since they both produce XXH64_hash_t values */ /*-********************************************************************** * XXH3 128-bit variant ************************************************************************/ /*! * @brief The return value from 128-bit hashes. * * Stored in little endian order, although the fields themselves are in native * endianness. */ typedef struct { XXH64_hash_t low64; /*!< `value & 0xFFFFFFFFFFFFFFFF` */ XXH64_hash_t high64; /*!< `value >> 64` */ } XXH128_hash_t; XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* data, size_t len); XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); /******* Streaming *******/ /* * Streaming requires state maintenance. * This operation costs memory and CPU. * As a consequence, streaming is slower than one-shot hashing. * For better performance, prefer one-shot functions whenever applicable. * * XXH3_128bits uses the same XXH3_state_t as XXH3_64bits(). * Use already declared XXH3_createState() and XXH3_freeState(). * * All reset and streaming functions have same meaning as their 64-bit counterpart. */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t* statePtr); XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH3_state_t* statePtr, const void* input, size_t length); XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* statePtr); /* Following helper functions make it possible to compare XXH128_hast_t values. * Since XXH128_hash_t is a structure, this capability is not offered by the language. * Note: For better performance, these functions can be inlined using XXH_INLINE_ALL */ /*! * XXH128_isEqual(): * Return: 1 if `h1` and `h2` are equal, 0 if they are not. */ XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2); /*! * XXH128_cmp(): * * This comparator is compatible with stdlib's `qsort()`/`bsearch()`. * * return: >0 if *h128_1 > *h128_2 * =0 if *h128_1 == *h128_2 * <0 if *h128_1 < *h128_2 */ XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2); /******* Canonical representation *******/ typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t; XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash); XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src); #endif /* XXH_NO_LONG_LONG */ /*! * @} */ #endif /* XXHASH_H_5627135585666179 */ #if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) #define XXHASH_H_STATIC_13879238742 /* **************************************************************************** * This section contains declarations which are not guaranteed to remain stable. * They may change in future versions, becoming incompatible with a different * version of the library. * These declarations should only be used with static linking. * Never use them in association with dynamic linking! ***************************************************************************** */ /* * These definitions are only present to allow static allocation * of XXH states, on stack or in a struct, for example. * Never **ever** access their members directly. */ /*! * @internal * @brief Structure for XXH32 streaming API. * * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is * an opaque type. This allows fields to safely be changed. * * Typedef'd to @ref XXH32_state_t. * Do not access the members of this struct directly. * @see XXH64_state_s, XXH3_state_s */ struct XXH32_state_s { XXH32_hash_t total_len_32; /*!< Total length hashed, modulo 2^32 */ XXH32_hash_t large_len; /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */ XXH32_hash_t v[4]; /*!< Accumulator lanes */ XXH32_hash_t mem32[4]; /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */ XXH32_hash_t memsize; /*!< Amount of data in @ref mem32 */ XXH32_hash_t reserved; /*!< Reserved field. Do not read or write to it, it may be removed. */ }; /* typedef'd to XXH32_state_t */ #ifndef XXH_NO_LONG_LONG /* defined when there is no 64-bit support */ /*! * @internal * @brief Structure for XXH64 streaming API. * * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is * an opaque type. This allows fields to safely be changed. * * Typedef'd to @ref XXH64_state_t. * Do not access the members of this struct directly. * @see XXH32_state_s, XXH3_state_s */ struct XXH64_state_s { XXH64_hash_t total_len; /*!< Total length hashed. This is always 64-bit. */ XXH64_hash_t v[4]; /*!< Accumulator lanes */ XXH64_hash_t mem64[4]; /*!< Internal buffer for partial reads. Treated as unsigned char[32]. */ XXH32_hash_t memsize; /*!< Amount of data in @ref mem64 */ XXH32_hash_t reserved32; /*!< Reserved field, needed for padding anyways*/ XXH64_hash_t reserved64; /*!< Reserved field. Do not read or write to it, it may be removed. */ }; /* typedef'd to XXH64_state_t */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* >= C11 */ # include # define XXH_ALIGN(n) alignas(n) #elif defined(__cplusplus) && (__cplusplus >= 201103L) /* >= C++11 */ /* In C++ alignas() is a keyword */ # define XXH_ALIGN(n) alignas(n) #elif defined(__GNUC__) # define XXH_ALIGN(n) __attribute__ ((aligned(n))) #elif defined(_MSC_VER) # define XXH_ALIGN(n) __declspec(align(n)) #else # define XXH_ALIGN(n) /* disabled */ #endif /* Old GCC versions only accept the attribute after the type in structures. */ #if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) /* C11+ */ \ && ! (defined(__cplusplus) && (__cplusplus >= 201103L)) /* >= C++11 */ \ && defined(__GNUC__) # define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align) #else # define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type #endif /*! * @brief The size of the internal XXH3 buffer. * * This is the optimal update size for incremental hashing. * * @see XXH3_64b_update(), XXH3_128b_update(). */ #define XXH3_INTERNALBUFFER_SIZE 256 /*! * @brief Default size of the secret buffer (and @ref XXH3_kSecret). * * This is the size used in @ref XXH3_kSecret and the seeded functions. * * Not to be confused with @ref XXH3_SECRET_SIZE_MIN. */ #define XXH3_SECRET_DEFAULT_SIZE 192 /*! * @internal * @brief Structure for XXH3 streaming API. * * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. * Otherwise it is an opaque type. * Never use this definition in combination with dynamic library. * This allows fields to safely be changed in the future. * * @note ** This structure has a strict alignment requirement of 64 bytes!! ** * Do not allocate this with `malloc()` or `new`, * it will not be sufficiently aligned. * Use @ref XXH3_createState() and @ref XXH3_freeState(), or stack allocation. * * Typedef'd to @ref XXH3_state_t. * Do never access the members of this struct directly. * * @see XXH3_INITSTATE() for stack initialization. * @see XXH3_createState(), XXH3_freeState(). * @see XXH32_state_s, XXH64_state_s */ struct XXH3_state_s { XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]); /*!< The 8 accumulators. Similar to `vN` in @ref XXH32_state_s::v1 and @ref XXH64_state_s */ XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]); /*!< Used to store a custom secret generated from a seed. */ XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]); /*!< The internal buffer. @see XXH32_state_s::mem32 */ XXH32_hash_t bufferedSize; /*!< The amount of memory in @ref buffer, @see XXH32_state_s::memsize */ XXH32_hash_t useSeed; /*!< Reserved field. Needed for padding on 64-bit. */ size_t nbStripesSoFar; /*!< Number or stripes processed. */ XXH64_hash_t totalLen; /*!< Total length hashed. 64-bit even on 32-bit targets. */ size_t nbStripesPerBlock; /*!< Number of stripes per block. */ size_t secretLimit; /*!< Size of @ref customSecret or @ref extSecret */ XXH64_hash_t seed; /*!< Seed for _withSeed variants. Must be zero otherwise, @see XXH3_INITSTATE() */ XXH64_hash_t reserved64; /*!< Reserved field. */ const unsigned char* extSecret; /*!< Reference to an external secret for the _withSecret variants, NULL * for other variants. */ /* note: there may be some padding at the end due to alignment on 64 bytes */ }; /* typedef'd to XXH3_state_t */ #undef XXH_ALIGN_MEMBER /*! * @brief Initializes a stack-allocated `XXH3_state_s`. * * When the @ref XXH3_state_t structure is merely emplaced on stack, * it should be initialized with XXH3_INITSTATE() or a memset() * in case its first reset uses XXH3_NNbits_reset_withSeed(). * This init can be omitted if the first reset uses default or _withSecret mode. * This operation isn't necessary when the state is created with XXH3_createState(). * Note that this doesn't prepare the state for a streaming operation, * it's still necessary to use XXH3_NNbits_reset*() afterwards. */ #define XXH3_INITSTATE(XXH3_state_ptr) { (XXH3_state_ptr)->seed = 0; } /* XXH128() : * simple alias to pre-selected XXH3_128bits variant */ XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t seed); /* === Experimental API === */ /* Symbols defined below must be considered tied to a specific library version. */ /* * XXH3_generateSecret(): * * Derive a high-entropy secret from any user-defined content, named customSeed. * The generated secret can be used in combination with `*_withSecret()` functions. * The `_withSecret()` variants are useful to provide a higher level of protection than 64-bit seed, * as it becomes much more difficult for an external actor to guess how to impact the calculation logic. * * The function accepts as input a custom seed of any length and any content, * and derives from it a high-entropy secret of length @secretSize * into an already allocated buffer @secretBuffer. * @secretSize must be >= XXH3_SECRET_SIZE_MIN * * The generated secret can then be used with any `*_withSecret()` variant. * Functions `XXH3_128bits_withSecret()`, `XXH3_64bits_withSecret()`, * `XXH3_128bits_reset_withSecret()` and `XXH3_64bits_reset_withSecret()` * are part of this list. They all accept a `secret` parameter * which must be large enough for implementation reasons (>= XXH3_SECRET_SIZE_MIN) * _and_ feature very high entropy (consist of random-looking bytes). * These conditions can be a high bar to meet, so * XXH3_generateSecret() can be employed to ensure proper quality. * * customSeed can be anything. It can have any size, even small ones, * and its content can be anything, even "poor entropy" sources such as a bunch of zeroes. * The resulting `secret` will nonetheless provide all required qualities. * * When customSeedSize > 0, supplying NULL as customSeed is undefined behavior. */ XXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(void* secretBuffer, size_t secretSize, const void* customSeed, size_t customSeedSize); /* * XXH3_generateSecret_fromSeed(): * * Generate the same secret as the _withSeed() variants. * * The resulting secret has a length of XXH3_SECRET_DEFAULT_SIZE (necessarily). * @secretBuffer must be already allocated, of size at least XXH3_SECRET_DEFAULT_SIZE bytes. * * The generated secret can be used in combination with *`*_withSecret()` and `_withSecretandSeed()` variants. * This generator is notably useful in combination with `_withSecretandSeed()`, * as a way to emulate a faster `_withSeed()` variant. */ XXH_PUBLIC_API void XXH3_generateSecret_fromSeed(void* secretBuffer, XXH64_hash_t seed); /* * *_withSecretandSeed() : * These variants generate hash values using either * @seed for "short" keys (< XXH3_MIDSIZE_MAX = 240 bytes) * or @secret for "large" keys (>= XXH3_MIDSIZE_MAX). * * This generally benefits speed, compared to `_withSeed()` or `_withSecret()`. * `_withSeed()` has to generate the secret on the fly for "large" keys. * It's fast, but can be perceptible for "not so large" keys (< 1 KB). * `_withSecret()` has to generate the masks on the fly for "small" keys, * which requires more instructions than _withSeed() variants. * Therefore, _withSecretandSeed variant combines the best of both worlds. * * When @secret has been generated by XXH3_generateSecret_fromSeed(), * this variant produces *exactly* the same results as `_withSeed()` variant, * hence offering only a pure speed benefit on "large" input, * by skipping the need to regenerate the secret for every large input. * * Another usage scenario is to hash the secret to a 64-bit hash value, * for example with XXH3_64bits(), which then becomes the seed, * and then employ both the seed and the secret in _withSecretandSeed(). * On top of speed, an added benefit is that each bit in the secret * has a 50% chance to swap each bit in the output, * via its impact to the seed. * This is not guaranteed when using the secret directly in "small data" scenarios, * because only portions of the secret are employed for small data. */ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecretandSeed(const void* data, size_t len, const void* secret, size_t secretSize, XXH64_hash_t seed); XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecretandSeed(const void* data, size_t len, const void* secret, size_t secretSize, XXH64_hash_t seed64); XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecretandSeed(XXH3_state_t* statePtr, const void* secret, size_t secretSize, XXH64_hash_t seed64); XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr, const void* secret, size_t secretSize, XXH64_hash_t seed64); #endif /* XXH_NO_LONG_LONG */ #if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) # define XXH_IMPLEMENTATION #endif #endif /* defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) */ /* ======================================================================== */ /* ======================================================================== */ /* ======================================================================== */ /*-********************************************************************** * xxHash implementation *-********************************************************************** * xxHash's implementation used to be hosted inside xxhash.c. * * However, inlining requires implementation to be visible to the compiler, * hence be included alongside the header. * Previously, implementation was hosted inside xxhash.c, * which was then #included when inlining was activated. * This construction created issues with a few build and install systems, * as it required xxhash.c to be stored in /include directory. * * xxHash implementation is now directly integrated within xxhash.h. * As a consequence, xxhash.c is no longer needed in /include. * * xxhash.c is still available and is still useful. * In a "normal" setup, when xxhash is not inlined, * xxhash.h only exposes the prototypes and public symbols, * while xxhash.c can be built into an object file xxhash.o * which can then be linked into the final binary. ************************************************************************/ #if ( defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) \ || defined(XXH_IMPLEMENTATION) ) && !defined(XXH_IMPLEM_13a8737387) # define XXH_IMPLEM_13a8737387 /* ************************************* * Tuning parameters ***************************************/ /*! * @defgroup tuning Tuning parameters * @{ * * Various macros to control xxHash's behavior. */ #ifdef XXH_DOXYGEN /*! * @brief Define this to disable 64-bit code. * * Useful if only using the @ref xxh32_family and you have a strict C90 compiler. */ # define XXH_NO_LONG_LONG # undef XXH_NO_LONG_LONG /* don't actually */ /*! * @brief Controls how unaligned memory is accessed. * * By default, access to unaligned memory is controlled by `memcpy()`, which is * safe and portable. * * Unfortunately, on some target/compiler combinations, the generated assembly * is sub-optimal. * * The below switch allow selection of a different access method * in the search for improved performance. * * @par Possible options: * * - `XXH_FORCE_MEMORY_ACCESS=0` (default): `memcpy` * @par * Use `memcpy()`. Safe and portable. Note that most modern compilers will * eliminate the function call and treat it as an unaligned access. * * - `XXH_FORCE_MEMORY_ACCESS=1`: `__attribute__((packed))` * @par * Depends on compiler extensions and is therefore not portable. * This method is safe _if_ your compiler supports it, * and *generally* as fast or faster than `memcpy`. * * - `XXH_FORCE_MEMORY_ACCESS=2`: Direct cast * @par * Casts directly and dereferences. This method doesn't depend on the * compiler, but it violates the C standard as it directly dereferences an * unaligned pointer. It can generate buggy code on targets which do not * support unaligned memory accesses, but in some circumstances, it's the * only known way to get the most performance. * * - `XXH_FORCE_MEMORY_ACCESS=3`: Byteshift * @par * Also portable. This can generate the best code on old compilers which don't * inline small `memcpy()` calls, and it might also be faster on big-endian * systems which lack a native byteswap instruction. However, some compilers * will emit literal byteshifts even if the target supports unaligned access. * . * * @warning * Methods 1 and 2 rely on implementation-defined behavior. Use these with * care, as what works on one compiler/platform/optimization level may cause * another to read garbage data or even crash. * * See http://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html for details. * * Prefer these methods in priority order (0 > 3 > 1 > 2) */ # define XXH_FORCE_MEMORY_ACCESS 0 /*! * @def XXH_FORCE_ALIGN_CHECK * @brief If defined to non-zero, adds a special path for aligned inputs (XXH32() * and XXH64() only). * * This is an important performance trick for architectures without decent * unaligned memory access performance. * * It checks for input alignment, and when conditions are met, uses a "fast * path" employing direct 32-bit/64-bit reads, resulting in _dramatically * faster_ read speed. * * The check costs one initial branch per hash, which is generally negligible, * but not zero. * * Moreover, it's not useful to generate an additional code path if memory * access uses the same instruction for both aligned and unaligned * addresses (e.g. x86 and aarch64). * * In these cases, the alignment check can be removed by setting this macro to 0. * Then the code will always use unaligned memory access. * Align check is automatically disabled on x86, x64 & arm64, * which are platforms known to offer good unaligned memory accesses performance. * * This option does not affect XXH3 (only XXH32 and XXH64). */ # define XXH_FORCE_ALIGN_CHECK 0 /*! * @def XXH_NO_INLINE_HINTS * @brief When non-zero, sets all functions to `static`. * * By default, xxHash tries to force the compiler to inline almost all internal * functions. * * This can usually improve performance due to reduced jumping and improved * constant folding, but significantly increases the size of the binary which * might not be favorable. * * Additionally, sometimes the forced inlining can be detrimental to performance, * depending on the architecture. * * XXH_NO_INLINE_HINTS marks all internal functions as static, giving the * compiler full control on whether to inline or not. * * When not optimizing (-O0), optimizing for size (-Os, -Oz), or using * -fno-inline with GCC or Clang, this will automatically be defined. */ # define XXH_NO_INLINE_HINTS 0 /*! * @def XXH32_ENDJMP * @brief Whether to use a jump for `XXH32_finalize`. * * For performance, `XXH32_finalize` uses multiple branches in the finalizer. * This is generally preferable for performance, * but depending on exact architecture, a jmp may be preferable. * * This setting is only possibly making a difference for very small inputs. */ # define XXH32_ENDJMP 0 /*! * @internal * @brief Redefines old internal names. * * For compatibility with code that uses xxHash's internals before the names * were changed to improve namespacing. There is no other reason to use this. */ # define XXH_OLD_NAMES # undef XXH_OLD_NAMES /* don't actually use, it is ugly. */ #endif /* XXH_DOXYGEN */ /*! * @} */ #ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ /* prefer __packed__ structures (method 1) for gcc on armv7+ and mips */ # if !defined(__clang__) && \ ( \ (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \ ( \ defined(__GNUC__) && ( \ (defined(__ARM_ARCH) && __ARM_ARCH >= 7) || \ ( \ defined(__mips__) && \ (__mips <= 5 || __mips_isa_rev < 6) && \ (!defined(__mips16) || defined(__mips_mips16e2)) \ ) \ ) \ ) \ ) # define XXH_FORCE_MEMORY_ACCESS 1 # endif #endif #ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ # if defined(__i386) || defined(__x86_64__) || defined(__aarch64__) \ || defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64) /* visual */ # define XXH_FORCE_ALIGN_CHECK 0 # else # define XXH_FORCE_ALIGN_CHECK 1 # endif #endif #ifndef XXH_NO_INLINE_HINTS # if defined(__OPTIMIZE_SIZE__) /* -Os, -Oz */ \ || defined(__NO_INLINE__) /* -O0, -fno-inline */ # define XXH_NO_INLINE_HINTS 1 # else # define XXH_NO_INLINE_HINTS 0 # endif #endif #ifndef XXH32_ENDJMP /* generally preferable for performance */ # define XXH32_ENDJMP 0 #endif /*! * @defgroup impl Implementation * @{ */ /* ************************************* * Includes & Memory related functions ***************************************/ /* * Modify the local functions below should you wish to use * different memory routines for malloc() and free() */ #include /*! * @internal * @brief Modify this function to use a different routine than malloc(). */ static void* XXH_malloc(size_t s) { return malloc(s); } /*! * @internal * @brief Modify this function to use a different routine than free(). */ static void XXH_free(void* p) { free(p); } #include /*! * @internal * @brief Modify this function to use a different routine than memcpy(). */ static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } #include /* ULLONG_MAX */ /* ************************************* * Compiler Specific Options ***************************************/ #ifdef _MSC_VER /* Visual Studio warning fix */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #endif #if XXH_NO_INLINE_HINTS /* disable inlining hints */ # if defined(__GNUC__) || defined(__clang__) # define XXH_FORCE_INLINE static __attribute__((unused)) # else # define XXH_FORCE_INLINE static # endif # define XXH_NO_INLINE static /* enable inlining hints */ #elif defined(__GNUC__) || defined(__clang__) # define XXH_FORCE_INLINE static __inline__ __attribute__((always_inline, unused)) # define XXH_NO_INLINE static __attribute__((noinline)) #elif defined(_MSC_VER) /* Visual Studio */ # define XXH_FORCE_INLINE static __forceinline # define XXH_NO_INLINE static __declspec(noinline) #elif defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) /* C99 */ # define XXH_FORCE_INLINE static inline # define XXH_NO_INLINE static #else # define XXH_FORCE_INLINE static # define XXH_NO_INLINE static #endif /* ************************************* * Debug ***************************************/ /*! * @ingroup tuning * @def XXH_DEBUGLEVEL * @brief Sets the debugging level. * * XXH_DEBUGLEVEL is expected to be defined externally, typically via the * compiler's command line options. The value must be a number. */ #ifndef XXH_DEBUGLEVEL # ifdef DEBUGLEVEL /* backwards compat */ # define XXH_DEBUGLEVEL DEBUGLEVEL # else # define XXH_DEBUGLEVEL 0 # endif #endif #if (XXH_DEBUGLEVEL>=1) # include /* note: can still be disabled with NDEBUG */ # define XXH_ASSERT(c) assert(c) #else # define XXH_ASSERT(c) ((void)0) #endif /* note: use after variable declarations */ #ifndef XXH_STATIC_ASSERT # if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ # include # define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0) # elif defined(__cplusplus) && (__cplusplus >= 201103L) /* C++11 */ # define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0) # else # define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { struct xxh_sa { char x[(c) ? 1 : -1]; }; } while(0) # endif # define XXH_STATIC_ASSERT(c) XXH_STATIC_ASSERT_WITH_MESSAGE((c),#c) #endif /*! * @internal * @def XXH_COMPILER_GUARD(var) * @brief Used to prevent unwanted optimizations for @p var. * * It uses an empty GCC inline assembly statement with a register constraint * which forces @p var into a general purpose register (eg eax, ebx, ecx * on x86) and marks it as modified. * * This is used in a few places to avoid unwanted autovectorization (e.g. * XXH32_round()). All vectorization we want is explicit via intrinsics, * and _usually_ isn't wanted elsewhere. * * We also use it to prevent unwanted constant folding for AArch64 in * XXH3_initCustomSecret_scalar(). */ #if defined(__GNUC__) || defined(__clang__) # define XXH_COMPILER_GUARD(var) __asm__ __volatile__("" : "+r" (var)) #else # define XXH_COMPILER_GUARD(var) ((void)0) #endif /* ************************************* * Basic Types ***************************************/ #if !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include typedef uint8_t xxh_u8; #else typedef unsigned char xxh_u8; #endif typedef XXH32_hash_t xxh_u32; #ifdef XXH_OLD_NAMES # define BYTE xxh_u8 # define U8 xxh_u8 # define U32 xxh_u32 #endif /* *** Memory access *** */ /*! * @internal * @fn xxh_u32 XXH_read32(const void* ptr) * @brief Reads an unaligned 32-bit integer from @p ptr in native endianness. * * Affected by @ref XXH_FORCE_MEMORY_ACCESS. * * @param ptr The pointer to read from. * @return The 32-bit native endian integer from the bytes at @p ptr. */ /*! * @internal * @fn xxh_u32 XXH_readLE32(const void* ptr) * @brief Reads an unaligned 32-bit little endian integer from @p ptr. * * Affected by @ref XXH_FORCE_MEMORY_ACCESS. * * @param ptr The pointer to read from. * @return The 32-bit little endian integer from the bytes at @p ptr. */ /*! * @internal * @fn xxh_u32 XXH_readBE32(const void* ptr) * @brief Reads an unaligned 32-bit big endian integer from @p ptr. * * Affected by @ref XXH_FORCE_MEMORY_ACCESS. * * @param ptr The pointer to read from. * @return The 32-bit big endian integer from the bytes at @p ptr. */ /*! * @internal * @fn xxh_u32 XXH_readLE32_align(const void* ptr, XXH_alignment align) * @brief Like @ref XXH_readLE32(), but has an option for aligned reads. * * Affected by @ref XXH_FORCE_MEMORY_ACCESS. * Note that when @ref XXH_FORCE_ALIGN_CHECK == 0, the @p align parameter is * always @ref XXH_alignment::XXH_unaligned. * * @param ptr The pointer to read from. * @param align Whether @p ptr is aligned. * @pre * If @p align == @ref XXH_alignment::XXH_aligned, @p ptr must be 4 byte * aligned. * @return The 32-bit little endian integer from the bytes at @p ptr. */ #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) /* * Manual byteshift. Best for old compilers which don't inline memcpy. * We actually directly use XXH_readLE32 and XXH_readBE32. */ #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) /* * Force direct memory access. Only works on CPU which support unaligned memory * access in hardware. */ static xxh_u32 XXH_read32(const void* memPtr) { return *(const xxh_u32*) memPtr; } #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) /* * __pack instructions are safer but compiler specific, hence potentially * problematic for some compilers. * * Currently only defined for GCC and ICC. */ #ifdef XXH_OLD_NAMES typedef union { xxh_u32 u32; } __attribute__((packed)) unalign; #endif static xxh_u32 XXH_read32(const void* ptr) { typedef union { xxh_u32 u32; } __attribute__((packed)) xxh_unalign; return ((const xxh_unalign*)ptr)->u32; } #else /* * Portable and safe solution. Generally efficient. * see: http://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html */ static xxh_u32 XXH_read32(const void* memPtr) { xxh_u32 val; XXH_memcpy(&val, memPtr, sizeof(val)); return val; } #endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ /* *** Endianness *** */ /*! * @ingroup tuning * @def XXH_CPU_LITTLE_ENDIAN * @brief Whether the target is little endian. * * Defined to 1 if the target is little endian, or 0 if it is big endian. * It can be defined externally, for example on the compiler command line. * * If it is not defined, * a runtime check (which is usually constant folded) is used instead. * * @note * This is not necessarily defined to an integer constant. * * @see XXH_isLittleEndian() for the runtime check. */ #ifndef XXH_CPU_LITTLE_ENDIAN /* * Try to detect endianness automatically, to avoid the nonstandard behavior * in `XXH_isLittleEndian()` */ # if defined(_WIN32) /* Windows is always little endian */ \ || defined(__LITTLE_ENDIAN__) \ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) # define XXH_CPU_LITTLE_ENDIAN 1 # elif defined(__BIG_ENDIAN__) \ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) # define XXH_CPU_LITTLE_ENDIAN 0 # else /*! * @internal * @brief Runtime check for @ref XXH_CPU_LITTLE_ENDIAN. * * Most compilers will constant fold this. */ static int XXH_isLittleEndian(void) { /* * Portable and well-defined behavior. * Don't use static: it is detrimental to performance. */ const union { xxh_u32 u; xxh_u8 c[4]; } one = { 1 }; return one.c[0]; } # define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() # endif #endif /* **************************************** * Compiler-specific Functions and Macros ******************************************/ #define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) #ifdef __has_builtin # define XXH_HAS_BUILTIN(x) __has_builtin(x) #else # define XXH_HAS_BUILTIN(x) 0 #endif /*! * @internal * @def XXH_rotl32(x,r) * @brief 32-bit rotate left. * * @param x The 32-bit integer to be rotated. * @param r The number of bits to rotate. * @pre * @p r > 0 && @p r < 32 * @note * @p x and @p r may be evaluated multiple times. * @return The rotated result. */ #if !defined(NO_CLANG_BUILTIN) && XXH_HAS_BUILTIN(__builtin_rotateleft32) \ && XXH_HAS_BUILTIN(__builtin_rotateleft64) # define XXH_rotl32 __builtin_rotateleft32 # define XXH_rotl64 __builtin_rotateleft64 /* Note: although _rotl exists for minGW (GCC under windows), performance seems poor */ #elif defined(_MSC_VER) # define XXH_rotl32(x,r) _rotl(x,r) # define XXH_rotl64(x,r) _rotl64(x,r) #else # define XXH_rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) # define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r)))) #endif /*! * @internal * @fn xxh_u32 XXH_swap32(xxh_u32 x) * @brief A 32-bit byteswap. * * @param x The 32-bit integer to byteswap. * @return @p x, byteswapped. */ #if defined(_MSC_VER) /* Visual Studio */ # define XXH_swap32 _byteswap_ulong #elif XXH_GCC_VERSION >= 403 # define XXH_swap32 __builtin_bswap32 #else static xxh_u32 XXH_swap32 (xxh_u32 x) { return ((x << 24) & 0xff000000 ) | ((x << 8) & 0x00ff0000 ) | ((x >> 8) & 0x0000ff00 ) | ((x >> 24) & 0x000000ff ); } #endif /* *************************** * Memory reads *****************************/ /*! * @internal * @brief Enum to indicate whether a pointer is aligned. */ typedef enum { XXH_aligned, /*!< Aligned */ XXH_unaligned /*!< Possibly unaligned */ } XXH_alignment; /* * XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. * * This is ideal for older compilers which don't inline memcpy. */ #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* memPtr) { const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; return bytePtr[0] | ((xxh_u32)bytePtr[1] << 8) | ((xxh_u32)bytePtr[2] << 16) | ((xxh_u32)bytePtr[3] << 24); } XXH_FORCE_INLINE xxh_u32 XXH_readBE32(const void* memPtr) { const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; return bytePtr[3] | ((xxh_u32)bytePtr[2] << 8) | ((xxh_u32)bytePtr[1] << 16) | ((xxh_u32)bytePtr[0] << 24); } #else XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* ptr) { return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); } static xxh_u32 XXH_readBE32(const void* ptr) { return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); } #endif XXH_FORCE_INLINE xxh_u32 XXH_readLE32_align(const void* ptr, XXH_alignment align) { if (align==XXH_unaligned) { return XXH_readLE32(ptr); } else { return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u32*)ptr : XXH_swap32(*(const xxh_u32*)ptr); } } /* ************************************* * Misc ***************************************/ /*! @ingroup public */ XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } /* ******************************************************************* * 32-bit hash functions *********************************************************************/ /*! * @} * @defgroup xxh32_impl XXH32 implementation * @ingroup impl * @{ */ /* #define instead of static const, to be used as initializers */ #define XXH_PRIME32_1 0x9E3779B1U /*!< 0b10011110001101110111100110110001 */ #define XXH_PRIME32_2 0x85EBCA77U /*!< 0b10000101111010111100101001110111 */ #define XXH_PRIME32_3 0xC2B2AE3DU /*!< 0b11000010101100101010111000111101 */ #define XXH_PRIME32_4 0x27D4EB2FU /*!< 0b00100111110101001110101100101111 */ #define XXH_PRIME32_5 0x165667B1U /*!< 0b00010110010101100110011110110001 */ #ifdef XXH_OLD_NAMES # define PRIME32_1 XXH_PRIME32_1 # define PRIME32_2 XXH_PRIME32_2 # define PRIME32_3 XXH_PRIME32_3 # define PRIME32_4 XXH_PRIME32_4 # define PRIME32_5 XXH_PRIME32_5 #endif /*! * @internal * @brief Normal stripe processing routine. * * This shuffles the bits so that any bit from @p input impacts several bits in * @p acc. * * @param acc The accumulator lane. * @param input The stripe of input to mix. * @return The mixed accumulator lane. */ static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input) { acc += input * XXH_PRIME32_2; acc = XXH_rotl32(acc, 13); acc *= XXH_PRIME32_1; #if (defined(__SSE4_1__) || defined(__aarch64__)) && !defined(XXH_ENABLE_AUTOVECTORIZE) /* * UGLY HACK: * A compiler fence is the only thing that prevents GCC and Clang from * autovectorizing the XXH32 loop (pragmas and attributes don't work for some * reason) without globally disabling SSE4.1. * * The reason we want to avoid vectorization is because despite working on * 4 integers at a time, there are multiple factors slowing XXH32 down on * SSE4: * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on * newer chips!) making it slightly slower to multiply four integers at * once compared to four integers independently. Even when pmulld was * fastest, Sandy/Ivy Bridge, it is still not worth it to go into SSE * just to multiply unless doing a long operation. * * - Four instructions are required to rotate, * movqda tmp, v // not required with VEX encoding * pslld tmp, 13 // tmp <<= 13 * psrld v, 19 // x >>= 19 * por v, tmp // x |= tmp * compared to one for scalar: * roll v, 13 // reliably fast across the board * shldl v, v, 13 // Sandy Bridge and later prefer this for some reason * * - Instruction level parallelism is actually more beneficial here because * the SIMD actually serializes this operation: While v1 is rotating, v2 * can load data, while v3 can multiply. SSE forces them to operate * together. * * This is also enabled on AArch64, as Clang autovectorizes it incorrectly * and it is pointless writing a NEON implementation that is basically the * same speed as scalar for XXH32. */ XXH_COMPILER_GUARD(acc); #endif return acc; } /*! * @internal * @brief Mixes all bits to finalize the hash. * * The final mix ensures that all input bits have a chance to impact any bit in * the output digest, resulting in an unbiased distribution. * * @param h32 The hash to avalanche. * @return The avalanched hash. */ static xxh_u32 XXH32_avalanche(xxh_u32 h32) { h32 ^= h32 >> 15; h32 *= XXH_PRIME32_2; h32 ^= h32 >> 13; h32 *= XXH_PRIME32_3; h32 ^= h32 >> 16; return(h32); } #define XXH_get32bits(p) XXH_readLE32_align(p, align) /*! * @internal * @brief Processes the last 0-15 bytes of @p ptr. * * There may be up to 15 bytes remaining to consume from the input. * This final stage will digest them to ensure that all input bytes are present * in the final mix. * * @param h32 The hash to finalize. * @param ptr The pointer to the remaining input. * @param len The remaining length, modulo 16. * @param align Whether @p ptr is aligned. * @return The finalized hash. */ static xxh_u32 XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align) { #define XXH_PROCESS1 do { \ h32 += (*ptr++) * XXH_PRIME32_5; \ h32 = XXH_rotl32(h32, 11) * XXH_PRIME32_1; \ } while (0) #define XXH_PROCESS4 do { \ h32 += XXH_get32bits(ptr) * XXH_PRIME32_3; \ ptr += 4; \ h32 = XXH_rotl32(h32, 17) * XXH_PRIME32_4; \ } while (0) if (ptr==NULL) XXH_ASSERT(len == 0); /* Compact rerolled version; generally faster */ if (!XXH32_ENDJMP) { len &= 15; while (len >= 4) { XXH_PROCESS4; len -= 4; } while (len > 0) { XXH_PROCESS1; --len; } return XXH32_avalanche(h32); } else { switch(len&15) /* or switch(bEnd - p) */ { case 12: XXH_PROCESS4; XXH_FALLTHROUGH; case 8: XXH_PROCESS4; XXH_FALLTHROUGH; case 4: XXH_PROCESS4; return XXH32_avalanche(h32); case 13: XXH_PROCESS4; XXH_FALLTHROUGH; case 9: XXH_PROCESS4; XXH_FALLTHROUGH; case 5: XXH_PROCESS4; XXH_PROCESS1; return XXH32_avalanche(h32); case 14: XXH_PROCESS4; XXH_FALLTHROUGH; case 10: XXH_PROCESS4; XXH_FALLTHROUGH; case 6: XXH_PROCESS4; XXH_PROCESS1; XXH_PROCESS1; return XXH32_avalanche(h32); case 15: XXH_PROCESS4; XXH_FALLTHROUGH; case 11: XXH_PROCESS4; XXH_FALLTHROUGH; case 7: XXH_PROCESS4; XXH_FALLTHROUGH; case 3: XXH_PROCESS1; XXH_FALLTHROUGH; case 2: XXH_PROCESS1; XXH_FALLTHROUGH; case 1: XXH_PROCESS1; XXH_FALLTHROUGH; case 0: return XXH32_avalanche(h32); } XXH_ASSERT(0); return h32; /* reaching this point is deemed impossible */ } } #ifdef XXH_OLD_NAMES # define PROCESS1 XXH_PROCESS1 # define PROCESS4 XXH_PROCESS4 #else # undef XXH_PROCESS1 # undef XXH_PROCESS4 #endif /*! * @internal * @brief The implementation for @ref XXH32(). * * @param input , len , seed Directly passed from @ref XXH32(). * @param align Whether @p input is aligned. * @return The calculated hash. */ XXH_FORCE_INLINE xxh_u32 XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment align) { xxh_u32 h32; if (input==NULL) XXH_ASSERT(len == 0); if (len>=16) { const xxh_u8* const bEnd = input + len; const xxh_u8* const limit = bEnd - 15; xxh_u32 v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; xxh_u32 v2 = seed + XXH_PRIME32_2; xxh_u32 v3 = seed + 0; xxh_u32 v4 = seed - XXH_PRIME32_1; do { v1 = XXH32_round(v1, XXH_get32bits(input)); input += 4; v2 = XXH32_round(v2, XXH_get32bits(input)); input += 4; v3 = XXH32_round(v3, XXH_get32bits(input)); input += 4; v4 = XXH32_round(v4, XXH_get32bits(input)); input += 4; } while (input < limit); h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); } else { h32 = seed + XXH_PRIME32_5; } h32 += (xxh_u32)len; return XXH32_finalize(h32, input, len&15, align); } /*! @ingroup xxh32_family */ XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t seed) { #if 0 /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ XXH32_state_t state; XXH32_reset(&state, seed); XXH32_update(&state, (const xxh_u8*)input, len); return XXH32_digest(&state); #else if (XXH_FORCE_ALIGN_CHECK) { if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); } } return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned); #endif } /******* Hash streaming *******/ /*! * @ingroup xxh32_family */ XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) { return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); } /*! @ingroup xxh32_family */ XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) { XXH_free(statePtr); return XXH_OK; } /*! @ingroup xxh32_family */ XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) { XXH_memcpy(dstState, srcState, sizeof(*dstState)); } /*! @ingroup xxh32_family */ XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, XXH32_hash_t seed) { XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ memset(&state, 0, sizeof(state)); state.v[0] = seed + XXH_PRIME32_1 + XXH_PRIME32_2; state.v[1] = seed + XXH_PRIME32_2; state.v[2] = seed + 0; state.v[3] = seed - XXH_PRIME32_1; /* do not write into reserved, planned to be removed in a future version */ XXH_memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); return XXH_OK; } /*! @ingroup xxh32_family */ XXH_PUBLIC_API XXH_errorcode XXH32_update(XXH32_state_t* state, const void* input, size_t len) { if (input==NULL) { XXH_ASSERT(len == 0); return XXH_OK; } { const xxh_u8* p = (const xxh_u8*)input; const xxh_u8* const bEnd = p + len; state->total_len_32 += (XXH32_hash_t)len; state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16)); if (state->memsize + len < 16) { /* fill in tmp buffer */ XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, len); state->memsize += (XXH32_hash_t)len; return XXH_OK; } if (state->memsize) { /* some data left from previous update */ XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, 16-state->memsize); { const xxh_u32* p32 = state->mem32; state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p32)); p32++; state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p32)); p32++; state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p32)); p32++; state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p32)); } p += 16-state->memsize; state->memsize = 0; } if (p <= bEnd-16) { const xxh_u8* const limit = bEnd - 16; do { state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p)); p+=4; state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p)); p+=4; state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p)); p+=4; state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p)); p+=4; } while (p<=limit); } if (p < bEnd) { XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); state->memsize = (unsigned)(bEnd-p); } } return XXH_OK; } /*! @ingroup xxh32_family */ XXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t* state) { xxh_u32 h32; if (state->large_len) { h32 = XXH_rotl32(state->v[0], 1) + XXH_rotl32(state->v[1], 7) + XXH_rotl32(state->v[2], 12) + XXH_rotl32(state->v[3], 18); } else { h32 = state->v[2] /* == seed */ + XXH_PRIME32_5; } h32 += state->total_len_32; return XXH32_finalize(h32, (const xxh_u8*)state->mem32, state->memsize, XXH_aligned); } /******* Canonical representation *******/ /*! * @ingroup xxh32_family * The default return values from XXH functions are unsigned 32 and 64 bit * integers. * * The canonical representation uses big endian convention, the same convention * as human-readable numbers (large digits first). * * This way, hash values can be written into a file or buffer, remaining * comparable across different systems. * * The following functions allow transformation of hash values to and from their * canonical format. */ XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); XXH_memcpy(dst, &hash, sizeof(*dst)); } /*! @ingroup xxh32_family */ XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) { return XXH_readBE32(src); } #ifndef XXH_NO_LONG_LONG /* ******************************************************************* * 64-bit hash functions *********************************************************************/ /*! * @} * @ingroup impl * @{ */ /******* Memory access *******/ typedef XXH64_hash_t xxh_u64; #ifdef XXH_OLD_NAMES # define U64 xxh_u64 #endif #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) /* * Manual byteshift. Best for old compilers which don't inline memcpy. * We actually directly use XXH_readLE64 and XXH_readBE64. */ #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) /* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ static xxh_u64 XXH_read64(const void* memPtr) { return *(const xxh_u64*) memPtr; } #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) /* * __pack instructions are safer, but compiler specific, hence potentially * problematic for some compilers. * * Currently only defined for GCC and ICC. */ #ifdef XXH_OLD_NAMES typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) unalign64; #endif static xxh_u64 XXH_read64(const void* ptr) { typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) xxh_unalign64; return ((const xxh_unalign64*)ptr)->u64; } #else /* * Portable and safe solution. Generally efficient. * see: http://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html */ static xxh_u64 XXH_read64(const void* memPtr) { xxh_u64 val; XXH_memcpy(&val, memPtr, sizeof(val)); return val; } #endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ #if defined(_MSC_VER) /* Visual Studio */ # define XXH_swap64 _byteswap_uint64 #elif XXH_GCC_VERSION >= 403 # define XXH_swap64 __builtin_bswap64 #else static xxh_u64 XXH_swap64(xxh_u64 x) { return ((x << 56) & 0xff00000000000000ULL) | ((x << 40) & 0x00ff000000000000ULL) | ((x << 24) & 0x0000ff0000000000ULL) | ((x << 8) & 0x000000ff00000000ULL) | ((x >> 8) & 0x00000000ff000000ULL) | ((x >> 24) & 0x0000000000ff0000ULL) | ((x >> 40) & 0x000000000000ff00ULL) | ((x >> 56) & 0x00000000000000ffULL); } #endif /* XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. */ #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* memPtr) { const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; return bytePtr[0] | ((xxh_u64)bytePtr[1] << 8) | ((xxh_u64)bytePtr[2] << 16) | ((xxh_u64)bytePtr[3] << 24) | ((xxh_u64)bytePtr[4] << 32) | ((xxh_u64)bytePtr[5] << 40) | ((xxh_u64)bytePtr[6] << 48) | ((xxh_u64)bytePtr[7] << 56); } XXH_FORCE_INLINE xxh_u64 XXH_readBE64(const void* memPtr) { const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; return bytePtr[7] | ((xxh_u64)bytePtr[6] << 8) | ((xxh_u64)bytePtr[5] << 16) | ((xxh_u64)bytePtr[4] << 24) | ((xxh_u64)bytePtr[3] << 32) | ((xxh_u64)bytePtr[2] << 40) | ((xxh_u64)bytePtr[1] << 48) | ((xxh_u64)bytePtr[0] << 56); } #else XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* ptr) { return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); } static xxh_u64 XXH_readBE64(const void* ptr) { return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); } #endif XXH_FORCE_INLINE xxh_u64 XXH_readLE64_align(const void* ptr, XXH_alignment align) { if (align==XXH_unaligned) return XXH_readLE64(ptr); else return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u64*)ptr : XXH_swap64(*(const xxh_u64*)ptr); } /******* xxh64 *******/ /*! * @} * @defgroup xxh64_impl XXH64 implementation * @ingroup impl * @{ */ /* #define rather that static const, to be used as initializers */ #define XXH_PRIME64_1 0x9E3779B185EBCA87ULL /*!< 0b1001111000110111011110011011000110000101111010111100101010000111 */ #define XXH_PRIME64_2 0xC2B2AE3D27D4EB4FULL /*!< 0b1100001010110010101011100011110100100111110101001110101101001111 */ #define XXH_PRIME64_3 0x165667B19E3779F9ULL /*!< 0b0001011001010110011001111011000110011110001101110111100111111001 */ #define XXH_PRIME64_4 0x85EBCA77C2B2AE63ULL /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */ #define XXH_PRIME64_5 0x27D4EB2F165667C5ULL /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */ #ifdef XXH_OLD_NAMES # define PRIME64_1 XXH_PRIME64_1 # define PRIME64_2 XXH_PRIME64_2 # define PRIME64_3 XXH_PRIME64_3 # define PRIME64_4 XXH_PRIME64_4 # define PRIME64_5 XXH_PRIME64_5 #endif static xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input) { acc += input * XXH_PRIME64_2; acc = XXH_rotl64(acc, 31); acc *= XXH_PRIME64_1; return acc; } static xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val) { val = XXH64_round(0, val); acc ^= val; acc = acc * XXH_PRIME64_1 + XXH_PRIME64_4; return acc; } static xxh_u64 XXH64_avalanche(xxh_u64 h64) { h64 ^= h64 >> 33; h64 *= XXH_PRIME64_2; h64 ^= h64 >> 29; h64 *= XXH_PRIME64_3; h64 ^= h64 >> 32; return h64; } #define XXH_get64bits(p) XXH_readLE64_align(p, align) static xxh_u64 XXH64_finalize(xxh_u64 h64, const xxh_u8* ptr, size_t len, XXH_alignment align) { if (ptr==NULL) XXH_ASSERT(len == 0); len &= 31; while (len >= 8) { xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr)); ptr += 8; h64 ^= k1; h64 = XXH_rotl64(h64,27) * XXH_PRIME64_1 + XXH_PRIME64_4; len -= 8; } if (len >= 4) { h64 ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1; ptr += 4; h64 = XXH_rotl64(h64, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; len -= 4; } while (len > 0) { h64 ^= (*ptr++) * XXH_PRIME64_5; h64 = XXH_rotl64(h64, 11) * XXH_PRIME64_1; --len; } return XXH64_avalanche(h64); } #ifdef XXH_OLD_NAMES # define PROCESS1_64 XXH_PROCESS1_64 # define PROCESS4_64 XXH_PROCESS4_64 # define PROCESS8_64 XXH_PROCESS8_64 #else # undef XXH_PROCESS1_64 # undef XXH_PROCESS4_64 # undef XXH_PROCESS8_64 #endif XXH_FORCE_INLINE xxh_u64 XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment align) { xxh_u64 h64; if (input==NULL) XXH_ASSERT(len == 0); if (len>=32) { const xxh_u8* const bEnd = input + len; const xxh_u8* const limit = bEnd - 31; xxh_u64 v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; xxh_u64 v2 = seed + XXH_PRIME64_2; xxh_u64 v3 = seed + 0; xxh_u64 v4 = seed - XXH_PRIME64_1; do { v1 = XXH64_round(v1, XXH_get64bits(input)); input+=8; v2 = XXH64_round(v2, XXH_get64bits(input)); input+=8; v3 = XXH64_round(v3, XXH_get64bits(input)); input+=8; v4 = XXH64_round(v4, XXH_get64bits(input)); input+=8; } while (inputtotal_len += len; if (state->memsize + len < 32) { /* fill in tmp buffer */ XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, len); state->memsize += (xxh_u32)len; return XXH_OK; } if (state->memsize) { /* tmp buffer is full */ XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, 32-state->memsize); state->v[0] = XXH64_round(state->v[0], XXH_readLE64(state->mem64+0)); state->v[1] = XXH64_round(state->v[1], XXH_readLE64(state->mem64+1)); state->v[2] = XXH64_round(state->v[2], XXH_readLE64(state->mem64+2)); state->v[3] = XXH64_round(state->v[3], XXH_readLE64(state->mem64+3)); p += 32 - state->memsize; state->memsize = 0; } if (p+32 <= bEnd) { const xxh_u8* const limit = bEnd - 32; do { state->v[0] = XXH64_round(state->v[0], XXH_readLE64(p)); p+=8; state->v[1] = XXH64_round(state->v[1], XXH_readLE64(p)); p+=8; state->v[2] = XXH64_round(state->v[2], XXH_readLE64(p)); p+=8; state->v[3] = XXH64_round(state->v[3], XXH_readLE64(p)); p+=8; } while (p<=limit); } if (p < bEnd) { XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); state->memsize = (unsigned)(bEnd-p); } } return XXH_OK; } /*! @ingroup xxh64_family */ XXH_PUBLIC_API XXH64_hash_t XXH64_digest(const XXH64_state_t* state) { xxh_u64 h64; if (state->total_len >= 32) { h64 = XXH_rotl64(state->v[0], 1) + XXH_rotl64(state->v[1], 7) + XXH_rotl64(state->v[2], 12) + XXH_rotl64(state->v[3], 18); h64 = XXH64_mergeRound(h64, state->v[0]); h64 = XXH64_mergeRound(h64, state->v[1]); h64 = XXH64_mergeRound(h64, state->v[2]); h64 = XXH64_mergeRound(h64, state->v[3]); } else { h64 = state->v[2] /*seed*/ + XXH_PRIME64_5; } h64 += (xxh_u64) state->total_len; return XXH64_finalize(h64, (const xxh_u8*)state->mem64, (size_t)state->total_len, XXH_aligned); } /******* Canonical representation *******/ /*! @ingroup xxh64_family */ XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); XXH_memcpy(dst, &hash, sizeof(*dst)); } /*! @ingroup xxh64_family */ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) { return XXH_readBE64(src); } #ifndef XXH_NO_XXH3 /* ********************************************************************* * XXH3 * New generation hash designed for speed on small keys and vectorization ************************************************************************ */ /*! * @} * @defgroup xxh3_impl XXH3 implementation * @ingroup impl * @{ */ /* === Compiler specifics === */ #if ((defined(sun) || defined(__sun)) && __cplusplus) /* Solaris includes __STDC_VERSION__ with C++. Tested with GCC 5.5 */ # define XXH_RESTRICT /* disable */ #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */ # define XXH_RESTRICT restrict #else /* Note: it might be useful to define __restrict or __restrict__ for some C++ compilers */ # define XXH_RESTRICT /* disable */ #endif #if (defined(__GNUC__) && (__GNUC__ >= 3)) \ || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) \ || defined(__clang__) # define XXH_likely(x) __builtin_expect(x, 1) # define XXH_unlikely(x) __builtin_expect(x, 0) #else # define XXH_likely(x) (x) # define XXH_unlikely(x) (x) #endif #if defined(__GNUC__) # if defined(__AVX2__) # include # elif defined(__SSE2__) # include # elif defined(__ARM_NEON__) || defined(__ARM_NEON) # define inline __inline__ /* circumvent a clang bug */ # include # undef inline # endif #elif defined(_MSC_VER) # include #endif /* * One goal of XXH3 is to make it fast on both 32-bit and 64-bit, while * remaining a true 64-bit/128-bit hash function. * * This is done by prioritizing a subset of 64-bit operations that can be * emulated without too many steps on the average 32-bit machine. * * For example, these two lines seem similar, and run equally fast on 64-bit: * * xxh_u64 x; * x ^= (x >> 47); // good * x ^= (x >> 13); // bad * * However, to a 32-bit machine, there is a major difference. * * x ^= (x >> 47) looks like this: * * x.lo ^= (x.hi >> (47 - 32)); * * while x ^= (x >> 13) looks like this: * * // note: funnel shifts are not usually cheap. * x.lo ^= (x.lo >> 13) | (x.hi << (32 - 13)); * x.hi ^= (x.hi >> 13); * * The first one is significantly faster than the second, simply because the * shift is larger than 32. This means: * - All the bits we need are in the upper 32 bits, so we can ignore the lower * 32 bits in the shift. * - The shift result will always fit in the lower 32 bits, and therefore, * we can ignore the upper 32 bits in the xor. * * Thanks to this optimization, XXH3 only requires these features to be efficient: * * - Usable unaligned access * - A 32-bit or 64-bit ALU * - If 32-bit, a decent ADC instruction * - A 32 or 64-bit multiply with a 64-bit result * - For the 128-bit variant, a decent byteswap helps short inputs. * * The first two are already required by XXH32, and almost all 32-bit and 64-bit * platforms which can run XXH32 can run XXH3 efficiently. * * Thumb-1, the classic 16-bit only subset of ARM's instruction set, is one * notable exception. * * First of all, Thumb-1 lacks support for the UMULL instruction which * performs the important long multiply. This means numerous __aeabi_lmul * calls. * * Second of all, the 8 functional registers are just not enough. * Setup for __aeabi_lmul, byteshift loads, pointers, and all arithmetic need * Lo registers, and this shuffling results in thousands more MOVs than A32. * * A32 and T32 don't have this limitation. They can access all 14 registers, * do a 32->64 multiply with UMULL, and the flexible operand allowing free * shifts is helpful, too. * * Therefore, we do a quick sanity check. * * If compiling Thumb-1 for a target which supports ARM instructions, we will * emit a warning, as it is not a "sane" platform to compile for. * * Usually, if this happens, it is because of an accident and you probably need * to specify -march, as you likely meant to compile for a newer architecture. * * Credit: large sections of the vectorial and asm source code paths * have been contributed by @easyaspi314 */ #if defined(__thumb__) && !defined(__thumb2__) && defined(__ARM_ARCH_ISA_ARM) # warning "XXH3 is highly inefficient without ARM or Thumb-2." #endif /* ========================================== * Vectorization detection * ========================================== */ #ifdef XXH_DOXYGEN /*! * @ingroup tuning * @brief Overrides the vectorization implementation chosen for XXH3. * * Can be defined to 0 to disable SIMD or any of the values mentioned in * @ref XXH_VECTOR_TYPE. * * If this is not defined, it uses predefined macros to determine the best * implementation. */ # define XXH_VECTOR XXH_SCALAR /*! * @ingroup tuning * @brief Possible values for @ref XXH_VECTOR. * * Note that these are actually implemented as macros. * * If this is not defined, it is detected automatically. * @ref XXH_X86DISPATCH overrides this. */ enum XXH_VECTOR_TYPE /* fake enum */ { XXH_SCALAR = 0, /*!< Portable scalar version */ XXH_SSE2 = 1, /*!< * SSE2 for Pentium 4, Opteron, all x86_64. * * @note SSE2 is also guaranteed on Windows 10, macOS, and * Android x86. */ XXH_AVX2 = 2, /*!< AVX2 for Haswell and Bulldozer */ XXH_AVX512 = 3, /*!< AVX512 for Skylake and Icelake */ XXH_NEON = 4, /*!< NEON for most ARMv7-A and all AArch64 */ XXH_VSX = 5, /*!< VSX and ZVector for POWER8/z13 (64-bit) */ }; /*! * @ingroup tuning * @brief Selects the minimum alignment for XXH3's accumulators. * * When using SIMD, this should match the alignment reqired for said vector * type, so, for example, 32 for AVX2. * * Default: Auto detected. */ # define XXH_ACC_ALIGN 8 #endif /* Actual definition */ #ifndef XXH_DOXYGEN # define XXH_SCALAR 0 # define XXH_SSE2 1 # define XXH_AVX2 2 # define XXH_AVX512 3 # define XXH_NEON 4 # define XXH_VSX 5 #endif #ifndef XXH_VECTOR /* can be defined on command line */ # if defined(__AVX512F__) # define XXH_VECTOR XXH_AVX512 # elif defined(__AVX2__) # define XXH_VECTOR XXH_AVX2 # elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2)) # define XXH_VECTOR XXH_SSE2 # elif ( \ defined(__ARM_NEON__) || defined(__ARM_NEON) /* gcc */ \ || defined(_M_ARM64) || defined(_M_ARM_ARMV7VE) /* msvc */ \ ) && ( \ defined(_WIN32) || defined(__LITTLE_ENDIAN__) /* little endian only */ \ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \ ) # define XXH_VECTOR XXH_NEON # elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) \ || (defined(__s390x__) && defined(__VEC__)) \ && defined(__GNUC__) /* TODO: IBM XL */ # define XXH_VECTOR XXH_VSX # else # define XXH_VECTOR XXH_SCALAR # endif #endif /* * Controls the alignment of the accumulator, * for compatibility with aligned vector loads, which are usually faster. */ #ifndef XXH_ACC_ALIGN # if defined(XXH_X86DISPATCH) # define XXH_ACC_ALIGN 64 /* for compatibility with avx512 */ # elif XXH_VECTOR == XXH_SCALAR /* scalar */ # define XXH_ACC_ALIGN 8 # elif XXH_VECTOR == XXH_SSE2 /* sse2 */ # define XXH_ACC_ALIGN 16 # elif XXH_VECTOR == XXH_AVX2 /* avx2 */ # define XXH_ACC_ALIGN 32 # elif XXH_VECTOR == XXH_NEON /* neon */ # define XXH_ACC_ALIGN 16 # elif XXH_VECTOR == XXH_VSX /* vsx */ # define XXH_ACC_ALIGN 16 # elif XXH_VECTOR == XXH_AVX512 /* avx512 */ # define XXH_ACC_ALIGN 64 # endif #endif #if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 \ || XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512 # define XXH_SEC_ALIGN XXH_ACC_ALIGN #else # define XXH_SEC_ALIGN 8 #endif /* * UGLY HACK: * GCC usually generates the best code with -O3 for xxHash. * * However, when targeting AVX2, it is overzealous in its unrolling resulting * in code roughly 3/4 the speed of Clang. * * There are other issues, such as GCC splitting _mm256_loadu_si256 into * _mm_loadu_si128 + _mm256_inserti128_si256. This is an optimization which * only applies to Sandy and Ivy Bridge... which don't even support AVX2. * * That is why when compiling the AVX2 version, it is recommended to use either * -O2 -mavx2 -march=haswell * or * -O2 -mavx2 -mno-avx256-split-unaligned-load * for decent performance, or to use Clang instead. * * Fortunately, we can control the first one with a pragma that forces GCC into * -O2, but the other one we can't control without "failed to inline always * inline function due to target mismatch" warnings. */ #if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \ && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ && defined(__OPTIMIZE__) && !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */ # pragma GCC push_options # pragma GCC optimize("-O2") #endif #if XXH_VECTOR == XXH_NEON /* * NEON's setup for vmlal_u32 is a little more complicated than it is on * SSE2, AVX2, and VSX. * * While PMULUDQ and VMULEUW both perform a mask, VMLAL.U32 performs an upcast. * * To do the same operation, the 128-bit 'Q' register needs to be split into * two 64-bit 'D' registers, performing this operation:: * * [ a | b ] * | '---------. .--------' | * | x | * | .---------' '--------. | * [ a & 0xFFFFFFFF | b & 0xFFFFFFFF ],[ a >> 32 | b >> 32 ] * * Due to significant changes in aarch64, the fastest method for aarch64 is * completely different than the fastest method for ARMv7-A. * * ARMv7-A treats D registers as unions overlaying Q registers, so modifying * D11 will modify the high half of Q5. This is similar to how modifying AH * will only affect bits 8-15 of AX on x86. * * VZIP takes two registers, and puts even lanes in one register and odd lanes * in the other. * * On ARMv7-A, this strangely modifies both parameters in place instead of * taking the usual 3-operand form. * * Therefore, if we want to do this, we can simply use a D-form VZIP.32 on the * lower and upper halves of the Q register to end up with the high and low * halves where we want - all in one instruction. * * vzip.32 d10, d11 @ d10 = { d10[0], d11[0] }; d11 = { d10[1], d11[1] } * * Unfortunately we need inline assembly for this: Instructions modifying two * registers at once is not possible in GCC or Clang's IR, and they have to * create a copy. * * aarch64 requires a different approach. * * In order to make it easier to write a decent compiler for aarch64, many * quirks were removed, such as conditional execution. * * NEON was also affected by this. * * aarch64 cannot access the high bits of a Q-form register, and writes to a * D-form register zero the high bits, similar to how writes to W-form scalar * registers (or DWORD registers on x86_64) work. * * The formerly free vget_high intrinsics now require a vext (with a few * exceptions) * * Additionally, VZIP was replaced by ZIP1 and ZIP2, which are the equivalent * of PUNPCKL* and PUNPCKH* in SSE, respectively, in order to only modify one * operand. * * The equivalent of the VZIP.32 on the lower and upper halves would be this * mess: * * ext v2.4s, v0.4s, v0.4s, #2 // v2 = { v0[2], v0[3], v0[0], v0[1] } * zip1 v1.2s, v0.2s, v2.2s // v1 = { v0[0], v2[0] } * zip2 v0.2s, v0.2s, v1.2s // v0 = { v0[1], v2[1] } * * Instead, we use a literal downcast, vmovn_u64 (XTN), and vshrn_n_u64 (SHRN): * * shrn v1.2s, v0.2d, #32 // v1 = (uint32x2_t)(v0 >> 32); * xtn v0.2s, v0.2d // v0 = (uint32x2_t)(v0 & 0xFFFFFFFF); * * This is available on ARMv7-A, but is less efficient than a single VZIP.32. */ /*! * Function-like macro: * void XXH_SPLIT_IN_PLACE(uint64x2_t &in, uint32x2_t &outLo, uint32x2_t &outHi) * { * outLo = (uint32x2_t)(in & 0xFFFFFFFF); * outHi = (uint32x2_t)(in >> 32); * in = UNDEFINED; * } */ # if !defined(XXH_NO_VZIP_HACK) /* define to disable */ \ && defined(__GNUC__) \ && !defined(__aarch64__) && !defined(__arm64__) && !defined(_M_ARM64) # define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ do { \ /* Undocumented GCC/Clang operand modifier: %e0 = lower D half, %f0 = upper D half */ \ /* https://github.com/gcc-mirror/gcc/blob/38cf91e5/gcc/config/arm/arm.c#L22486 */ \ /* https://github.com/llvm-mirror/llvm/blob/2c4ca683/lib/Target/ARM/ARMAsmPrinter.cpp#L399 */ \ __asm__("vzip.32 %e0, %f0" : "+w" (in)); \ (outLo) = vget_low_u32 (vreinterpretq_u32_u64(in)); \ (outHi) = vget_high_u32(vreinterpretq_u32_u64(in)); \ } while (0) # else # define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ do { \ (outLo) = vmovn_u64 (in); \ (outHi) = vshrn_n_u64 ((in), 32); \ } while (0) # endif #endif /* XXH_VECTOR == XXH_NEON */ /* * VSX and Z Vector helpers. * * This is very messy, and any pull requests to clean this up are welcome. * * There are a lot of problems with supporting VSX and s390x, due to * inconsistent intrinsics, spotty coverage, and multiple endiannesses. */ #if XXH_VECTOR == XXH_VSX # if defined(__s390x__) # include # else /* gcc's altivec.h can have the unwanted consequence to unconditionally * #define bool, vector, and pixel keywords, * with bad consequences for programs already using these keywords for other purposes. * The paragraph defining these macros is skipped when __APPLE_ALTIVEC__ is defined. * __APPLE_ALTIVEC__ is _generally_ defined automatically by the compiler, * but it seems that, in some cases, it isn't. * Force the build macro to be defined, so that keywords are not altered. */ # if defined(__GNUC__) && !defined(__APPLE_ALTIVEC__) # define __APPLE_ALTIVEC__ # endif # include # endif typedef __vector unsigned long long xxh_u64x2; typedef __vector unsigned char xxh_u8x16; typedef __vector unsigned xxh_u32x4; # ifndef XXH_VSX_BE # if defined(__BIG_ENDIAN__) \ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) # define XXH_VSX_BE 1 # elif defined(__VEC_ELEMENT_REG_ORDER__) && __VEC_ELEMENT_REG_ORDER__ == __ORDER_BIG_ENDIAN__ # warning "-maltivec=be is not recommended. Please use native endianness." # define XXH_VSX_BE 1 # else # define XXH_VSX_BE 0 # endif # endif /* !defined(XXH_VSX_BE) */ # if XXH_VSX_BE # if defined(__POWER9_VECTOR__) || (defined(__clang__) && defined(__s390x__)) # define XXH_vec_revb vec_revb # else /*! * A polyfill for POWER9's vec_revb(). */ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val) { xxh_u8x16 const vByteSwap = { 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08 }; return vec_perm(val, val, vByteSwap); } # endif # endif /* XXH_VSX_BE */ /*! * Performs an unaligned vector load and byte swaps it on big endian. */ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr) { xxh_u64x2 ret; XXH_memcpy(&ret, ptr, sizeof(xxh_u64x2)); # if XXH_VSX_BE ret = XXH_vec_revb(ret); # endif return ret; } /* * vec_mulo and vec_mule are very problematic intrinsics on PowerPC * * These intrinsics weren't added until GCC 8, despite existing for a while, * and they are endian dependent. Also, their meaning swap depending on version. * */ # if defined(__s390x__) /* s390x is always big endian, no issue on this platform */ # define XXH_vec_mulo vec_mulo # define XXH_vec_mule vec_mule # elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw) /* Clang has a better way to control this, we can just use the builtin which doesn't swap. */ # define XXH_vec_mulo __builtin_altivec_vmulouw # define XXH_vec_mule __builtin_altivec_vmuleuw # else /* gcc needs inline assembly */ /* Adapted from https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mulo(xxh_u32x4 a, xxh_u32x4 b) { xxh_u64x2 result; __asm__("vmulouw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b)); return result; } XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b) { xxh_u64x2 result; __asm__("vmuleuw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b)); return result; } # endif /* XXH_vec_mulo, XXH_vec_mule */ #endif /* XXH_VECTOR == XXH_VSX */ /* prefetch * can be disabled, by declaring XXH_NO_PREFETCH build macro */ #if defined(XXH_NO_PREFETCH) # define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ #else # if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) /* _mm_prefetch() not defined outside of x86/x64 */ # include /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ # define XXH_PREFETCH(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) # elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) ) # define XXH_PREFETCH(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */) # else # define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ # endif #endif /* XXH_NO_PREFETCH */ /* ========================================== * XXH3 default settings * ========================================== */ #define XXH_SECRET_DEFAULT_SIZE 192 /* minimum XXH3_SECRET_SIZE_MIN */ #if (XXH_SECRET_DEFAULT_SIZE < XXH3_SECRET_SIZE_MIN) # error "default keyset is not large enough" #endif /*! Pseudorandom secret taken directly from FARSH. */ XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = { 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, }; #ifdef XXH_OLD_NAMES # define kSecret XXH3_kSecret #endif #ifdef XXH_DOXYGEN /*! * @brief Calculates a 32-bit to 64-bit long multiply. * * Implemented as a macro. * * Wraps `__emulu` on MSVC x86 because it tends to call `__allmul` when it doesn't * need to (but it shouldn't need to anyways, it is about 7 instructions to do * a 64x64 multiply...). Since we know that this will _always_ emit `MULL`, we * use that instead of the normal method. * * If you are compiling for platforms like Thumb-1 and don't have a better option, * you may also want to write your own long multiply routine here. * * @param x, y Numbers to be multiplied * @return 64-bit product of the low 32 bits of @p x and @p y. */ XXH_FORCE_INLINE xxh_u64 XXH_mult32to64(xxh_u64 x, xxh_u64 y) { return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF); } #elif defined(_MSC_VER) && defined(_M_IX86) # include # define XXH_mult32to64(x, y) __emulu((unsigned)(x), (unsigned)(y)) #else /* * Downcast + upcast is usually better than masking on older compilers like * GCC 4.2 (especially 32-bit ones), all without affecting newer compilers. * * The other method, (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF), will AND both operands * and perform a full 64x64 multiply -- entirely redundant on 32-bit. */ # define XXH_mult32to64(x, y) ((xxh_u64)(xxh_u32)(x) * (xxh_u64)(xxh_u32)(y)) #endif /*! * @brief Calculates a 64->128-bit long multiply. * * Uses `__uint128_t` and `_umul128` if available, otherwise uses a scalar * version. * * @param lhs , rhs The 64-bit integers to be multiplied * @return The 128-bit result represented in an @ref XXH128_hash_t. */ static XXH128_hash_t XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) { /* * GCC/Clang __uint128_t method. * * On most 64-bit targets, GCC and Clang define a __uint128_t type. * This is usually the best way as it usually uses a native long 64-bit * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64. * * Usually. * * Despite being a 32-bit platform, Clang (and emscripten) define this type * despite not having the arithmetic for it. This results in a laggy * compiler builtin call which calculates a full 128-bit multiply. * In that case it is best to use the portable one. * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 */ #if defined(__GNUC__) && !defined(__wasm__) \ && defined(__SIZEOF_INT128__) \ || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128) __uint128_t const product = (__uint128_t)lhs * (__uint128_t)rhs; XXH128_hash_t r128; r128.low64 = (xxh_u64)(product); r128.high64 = (xxh_u64)(product >> 64); return r128; /* * MSVC for x64's _umul128 method. * * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 *HighProduct); * * This compiles to single operand MUL on x64. */ #elif defined(_M_X64) || defined(_M_IA64) #ifndef _MSC_VER # pragma intrinsic(_umul128) #endif xxh_u64 product_high; xxh_u64 const product_low = _umul128(lhs, rhs, &product_high); XXH128_hash_t r128; r128.low64 = product_low; r128.high64 = product_high; return r128; /* * MSVC for ARM64's __umulh method. * * This compiles to the same MUL + UMULH as GCC/Clang's __uint128_t method. */ #elif defined(_M_ARM64) #ifndef _MSC_VER # pragma intrinsic(__umulh) #endif XXH128_hash_t r128; r128.low64 = lhs * rhs; r128.high64 = __umulh(lhs, rhs); return r128; #else /* * Portable scalar method. Optimized for 32-bit and 64-bit ALUs. * * This is a fast and simple grade school multiply, which is shown below * with base 10 arithmetic instead of base 0x100000000. * * 9 3 // D2 lhs = 93 * x 7 5 // D2 rhs = 75 * ---------- * 1 5 // D2 lo_lo = (93 % 10) * (75 % 10) = 15 * 4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) = 45 * 2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) = 21 * + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) = 63 * --------- * 2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 = 27 * + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 = 67 * --------- * 6 9 7 5 // D4 res = (27 * 10) + (15 % 10) + (67 * 100) = 6975 * * The reasons for adding the products like this are: * 1. It avoids manual carry tracking. Just like how * (9 * 9) + 9 + 9 = 99, the same applies with this for UINT64_MAX. * This avoids a lot of complexity. * * 2. It hints for, and on Clang, compiles to, the powerful UMAAL * instruction available in ARM's Digital Signal Processing extension * in 32-bit ARMv6 and later, which is shown below: * * void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm) * { * xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm; * *RdLo = (xxh_u32)(product & 0xFFFFFFFF); * *RdHi = (xxh_u32)(product >> 32); * } * * This instruction was designed for efficient long multiplication, and * allows this to be calculated in only 4 instructions at speeds * comparable to some 64-bit ALUs. * * 3. It isn't terrible on other platforms. Usually this will be a couple * of 32-bit ADD/ADCs. */ /* First calculate all of the cross products. */ xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF); xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32, rhs & 0xFFFFFFFF); xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32); xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32, rhs >> 32); /* Now add the products together. These will never overflow. */ xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi; xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32) + hi_hi; xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF); XXH128_hash_t r128; r128.low64 = lower; r128.high64 = upper; return r128; #endif } /*! * @brief Calculates a 64-bit to 128-bit multiply, then XOR folds it. * * The reason for the separate function is to prevent passing too many structs * around by value. This will hopefully inline the multiply, but we don't force it. * * @param lhs , rhs The 64-bit integers to multiply * @return The low 64 bits of the product XOR'd by the high 64 bits. * @see XXH_mult64to128() */ static xxh_u64 XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) { XXH128_hash_t product = XXH_mult64to128(lhs, rhs); return product.low64 ^ product.high64; } /*! Seems to produce slightly better code on GCC for some reason. */ XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) { XXH_ASSERT(0 <= shift && shift < 64); return v64 ^ (v64 >> shift); } /* * This is a fast avalanche stage, * suitable when input bits are already partially mixed */ static XXH64_hash_t XXH3_avalanche(xxh_u64 h64) { h64 = XXH_xorshift64(h64, 37); h64 *= 0x165667919E3779F9ULL; h64 = XXH_xorshift64(h64, 32); return h64; } /* * This is a stronger avalanche, * inspired by Pelle Evensen's rrmxmx * preferable when input has not been previously mixed */ static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) { /* this mix is inspired by Pelle Evensen's rrmxmx */ h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24); h64 *= 0x9FB21C651E98DF25ULL; h64 ^= (h64 >> 35) + len ; h64 *= 0x9FB21C651E98DF25ULL; return XXH_xorshift64(h64, 28); } /* ========================================== * Short keys * ========================================== * One of the shortcomings of XXH32 and XXH64 was that their performance was * sub-optimal on short lengths. It used an iterative algorithm which strongly * favored lengths that were a multiple of 4 or 8. * * Instead of iterating over individual inputs, we use a set of single shot * functions which piece together a range of lengths and operate in constant time. * * Additionally, the number of multiplies has been significantly reduced. This * reduces latency, especially when emulating 64-bit multiplies on 32-bit. * * Depending on the platform, this may or may not be faster than XXH32, but it * is almost guaranteed to be faster than XXH64. */ /* * At very short lengths, there isn't enough input to fully hide secrets, or use * the entire secret. * * There is also only a limited amount of mixing we can do before significantly * impacting performance. * * Therefore, we use different sections of the secret and always mix two secret * samples with an XOR. This should have no effect on performance on the * seedless or withSeed variants because everything _should_ be constant folded * by modern compilers. * * The XOR mixing hides individual parts of the secret and increases entropy. * * This adds an extra layer of strength for custom secrets. */ XXH_FORCE_INLINE XXH64_hash_t XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(1 <= len && len <= 3); XXH_ASSERT(secret != NULL); /* * len = 1: combined = { input[0], 0x01, input[0], input[0] } * len = 2: combined = { input[1], 0x02, input[0], input[1] } * len = 3: combined = { input[2], 0x03, input[0], input[1] } */ { xxh_u8 const c1 = input[0]; xxh_u8 const c2 = input[len >> 1]; xxh_u8 const c3 = input[len - 1]; xxh_u32 const combined = ((xxh_u32)c1 << 16) | ((xxh_u32)c2 << 24) | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); xxh_u64 const bitflip = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed; xxh_u64 const keyed = (xxh_u64)combined ^ bitflip; return XXH64_avalanche(keyed); } } XXH_FORCE_INLINE XXH64_hash_t XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(secret != NULL); XXH_ASSERT(4 <= len && len <= 8); seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; { xxh_u32 const input1 = XXH_readLE32(input); xxh_u32 const input2 = XXH_readLE32(input + len - 4); xxh_u64 const bitflip = (XXH_readLE64(secret+8) ^ XXH_readLE64(secret+16)) - seed; xxh_u64 const input64 = input2 + (((xxh_u64)input1) << 32); xxh_u64 const keyed = input64 ^ bitflip; return XXH3_rrmxmx(keyed, len); } } XXH_FORCE_INLINE XXH64_hash_t XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(secret != NULL); XXH_ASSERT(9 <= len && len <= 16); { xxh_u64 const bitflip1 = (XXH_readLE64(secret+24) ^ XXH_readLE64(secret+32)) + seed; xxh_u64 const bitflip2 = (XXH_readLE64(secret+40) ^ XXH_readLE64(secret+48)) - seed; xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1; xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2; xxh_u64 const acc = len + XXH_swap64(input_lo) + input_hi + XXH3_mul128_fold64(input_lo, input_hi); return XXH3_avalanche(acc); } } XXH_FORCE_INLINE XXH64_hash_t XXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(len <= 16); { if (XXH_likely(len > 8)) return XXH3_len_9to16_64b(input, len, secret, seed); if (XXH_likely(len >= 4)) return XXH3_len_4to8_64b(input, len, secret, seed); if (len) return XXH3_len_1to3_64b(input, len, secret, seed); return XXH64_avalanche(seed ^ (XXH_readLE64(secret+56) ^ XXH_readLE64(secret+64))); } } /* * DISCLAIMER: There are known *seed-dependent* multicollisions here due to * multiplication by zero, affecting hashes of lengths 17 to 240. * * However, they are very unlikely. * * Keep this in mind when using the unseeded XXH3_64bits() variant: As with all * unseeded non-cryptographic hashes, it does not attempt to defend itself * against specially crafted inputs, only random inputs. * * Compared to classic UMAC where a 1 in 2^31 chance of 4 consecutive bytes * cancelling out the secret is taken an arbitrary number of times (addressed * in XXH3_accumulate_512), this collision is very unlikely with random inputs * and/or proper seeding: * * This only has a 1 in 2^63 chance of 8 consecutive bytes cancelling out, in a * function that is only called up to 16 times per hash with up to 240 bytes of * input. * * This is not too bad for a non-cryptographic hash function, especially with * only 64 bit outputs. * * The 128-bit variant (which trades some speed for strength) is NOT affected * by this, although it is always a good idea to use a proper seed if you care * about strength. */ XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input, const xxh_u8* XXH_RESTRICT secret, xxh_u64 seed64) { #if defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ && defined(__i386__) && defined(__SSE2__) /* x86 + SSE2 */ \ && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable like XXH32 hack */ /* * UGLY HACK: * GCC for x86 tends to autovectorize the 128-bit multiply, resulting in * slower code. * * By forcing seed64 into a register, we disrupt the cost model and * cause it to scalarize. See `XXH32_round()` * * FIXME: Clang's output is still _much_ faster -- On an AMD Ryzen 3600, * XXH3_64bits @ len=240 runs at 4.6 GB/s with Clang 9, but 3.3 GB/s on * GCC 9.2, despite both emitting scalar code. * * GCC generates much better scalar code than Clang for the rest of XXH3, * which is why finding a more optimal codepath is an interest. */ XXH_COMPILER_GUARD(seed64); #endif { xxh_u64 const input_lo = XXH_readLE64(input); xxh_u64 const input_hi = XXH_readLE64(input+8); return XXH3_mul128_fold64( input_lo ^ (XXH_readLE64(secret) + seed64), input_hi ^ (XXH_readLE64(secret+8) - seed64) ); } } /* For mid range keys, XXH3 uses a Mum-hash variant. */ XXH_FORCE_INLINE XXH64_hash_t XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; XXH_ASSERT(16 < len && len <= 128); { xxh_u64 acc = len * XXH_PRIME64_1; if (len > 32) { if (len > 64) { if (len > 96) { acc += XXH3_mix16B(input+48, secret+96, seed); acc += XXH3_mix16B(input+len-64, secret+112, seed); } acc += XXH3_mix16B(input+32, secret+64, seed); acc += XXH3_mix16B(input+len-48, secret+80, seed); } acc += XXH3_mix16B(input+16, secret+32, seed); acc += XXH3_mix16B(input+len-32, secret+48, seed); } acc += XXH3_mix16B(input+0, secret+0, seed); acc += XXH3_mix16B(input+len-16, secret+16, seed); return XXH3_avalanche(acc); } } #define XXH3_MIDSIZE_MAX 240 XXH_NO_INLINE XXH64_hash_t XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); #define XXH3_MIDSIZE_STARTOFFSET 3 #define XXH3_MIDSIZE_LASTOFFSET 17 { xxh_u64 acc = len * XXH_PRIME64_1; int const nbRounds = (int)len / 16; int i; for (i=0; i<8; i++) { acc += XXH3_mix16B(input+(16*i), secret+(16*i), seed); } acc = XXH3_avalanche(acc); XXH_ASSERT(nbRounds >= 8); #if defined(__clang__) /* Clang */ \ && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ /* * UGLY HACK: * Clang for ARMv7-A tries to vectorize this loop, similar to GCC x86. * In everywhere else, it uses scalar code. * * For 64->128-bit multiplies, even if the NEON was 100% optimal, it * would still be slower than UMAAL (see XXH_mult64to128). * * Unfortunately, Clang doesn't handle the long multiplies properly and * converts them to the nonexistent "vmulq_u64" intrinsic, which is then * scalarized into an ugly mess of VMOV.32 instructions. * * This mess is difficult to avoid without turning autovectorization * off completely, but they are usually relatively minor and/or not * worth it to fix. * * This loop is the easiest to fix, as unlike XXH32, this pragma * _actually works_ because it is a loop vectorization instead of an * SLP vectorization. */ #pragma clang loop vectorize(disable) #endif for (i=8 ; i < nbRounds; i++) { acc += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed); } /* last bytes */ acc += XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed); return XXH3_avalanche(acc); } } /* ======= Long Keys ======= */ #define XXH_STRIPE_LEN 64 #define XXH_SECRET_CONSUME_RATE 8 /* nb of secret bytes consumed at each accumulation */ #define XXH_ACC_NB (XXH_STRIPE_LEN / sizeof(xxh_u64)) #ifdef XXH_OLD_NAMES # define STRIPE_LEN XXH_STRIPE_LEN # define ACC_NB XXH_ACC_NB #endif XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) { if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64); XXH_memcpy(dst, &v64, sizeof(v64)); } /* Several intrinsic functions below are supposed to accept __int64 as argument, * as documented in https://software.intel.com/sites/landingpage/IntrinsicsGuide/ . * However, several environments do not define __int64 type, * requiring a workaround. */ #if !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) typedef int64_t xxh_i64; #else /* the following type must have a width of 64-bit */ typedef long long xxh_i64; #endif /* * XXH3_accumulate_512 is the tightest loop for long inputs, and it is the most optimized. * * It is a hardened version of UMAC, based off of FARSH's implementation. * * This was chosen because it adapts quite well to 32-bit, 64-bit, and SIMD * implementations, and it is ridiculously fast. * * We harden it by mixing the original input to the accumulators as well as the product. * * This means that in the (relatively likely) case of a multiply by zero, the * original input is preserved. * * On 128-bit inputs, we swap 64-bit pairs when we add the input to improve * cross-pollination, as otherwise the upper and lower halves would be * essentially independent. * * This doesn't matter on 64-bit hashes since they all get merged together in * the end, so we skip the extra step. * * Both XXH3_64bits and XXH3_128bits use this subroutine. */ #if (XXH_VECTOR == XXH_AVX512) \ || (defined(XXH_DISPATCH_AVX512) && XXH_DISPATCH_AVX512 != 0) #ifndef XXH_TARGET_AVX512 # define XXH_TARGET_AVX512 /* disable attribute target */ #endif XXH_FORCE_INLINE XXH_TARGET_AVX512 void XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { __m512i* const xacc = (__m512i *) acc; XXH_ASSERT((((size_t)acc) & 63) == 0); XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); { /* data_vec = input[0]; */ __m512i const data_vec = _mm512_loadu_si512 (input); /* key_vec = secret[0]; */ __m512i const key_vec = _mm512_loadu_si512 (secret); /* data_key = data_vec ^ key_vec; */ __m512i const data_key = _mm512_xor_si512 (data_vec, key_vec); /* data_key_lo = data_key >> 32; */ __m512i const data_key_lo = _mm512_shuffle_epi32 (data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1)); /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ __m512i const product = _mm512_mul_epu32 (data_key, data_key_lo); /* xacc[0] += swap(data_vec); */ __m512i const data_swap = _mm512_shuffle_epi32(data_vec, (_MM_PERM_ENUM)_MM_SHUFFLE(1, 0, 3, 2)); __m512i const sum = _mm512_add_epi64(*xacc, data_swap); /* xacc[0] += product; */ *xacc = _mm512_add_epi64(product, sum); } } /* * XXH3_scrambleAcc: Scrambles the accumulators to improve mixing. * * Multiplication isn't perfect, as explained by Google in HighwayHash: * * // Multiplication mixes/scrambles bytes 0-7 of the 64-bit result to * // varying degrees. In descending order of goodness, bytes * // 3 4 2 5 1 6 0 7 have quality 228 224 164 160 100 96 36 32. * // As expected, the upper and lower bytes are much worse. * * Source: https://github.com/google/highwayhash/blob/0aaf66b/highwayhash/hh_avx2.h#L291 * * Since our algorithm uses a pseudorandom secret to add some variance into the * mix, we don't need to (or want to) mix as often or as much as HighwayHash does. * * This isn't as tight as XXH3_accumulate, but still written in SIMD to avoid * extraction. * * Both XXH3_64bits and XXH3_128bits use this subroutine. */ XXH_FORCE_INLINE XXH_TARGET_AVX512 void XXH3_scrambleAcc_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 63) == 0); XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); { __m512i* const xacc = (__m512i*) acc; const __m512i prime32 = _mm512_set1_epi32((int)XXH_PRIME32_1); /* xacc[0] ^= (xacc[0] >> 47) */ __m512i const acc_vec = *xacc; __m512i const shifted = _mm512_srli_epi64 (acc_vec, 47); __m512i const data_vec = _mm512_xor_si512 (acc_vec, shifted); /* xacc[0] ^= secret; */ __m512i const key_vec = _mm512_loadu_si512 (secret); __m512i const data_key = _mm512_xor_si512 (data_vec, key_vec); /* xacc[0] *= XXH_PRIME32_1; */ __m512i const data_key_hi = _mm512_shuffle_epi32 (data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1)); __m512i const prod_lo = _mm512_mul_epu32 (data_key, prime32); __m512i const prod_hi = _mm512_mul_epu32 (data_key_hi, prime32); *xacc = _mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32)); } } XXH_FORCE_INLINE XXH_TARGET_AVX512 void XXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64) { XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 63) == 0); XXH_STATIC_ASSERT(XXH_SEC_ALIGN == 64); XXH_ASSERT(((size_t)customSecret & 63) == 0); (void)(&XXH_writeLE64); { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m512i); __m512i const seed = _mm512_mask_set1_epi64(_mm512_set1_epi64((xxh_i64)seed64), 0xAA, (xxh_i64)(0U - seed64)); const __m512i* const src = (const __m512i*) ((const void*) XXH3_kSecret); __m512i* const dest = ( __m512i*) customSecret; int i; XXH_ASSERT(((size_t)src & 63) == 0); /* control alignment */ XXH_ASSERT(((size_t)dest & 63) == 0); for (i=0; i < nbRounds; ++i) { /* GCC has a bug, _mm512_stream_load_si512 accepts 'void*', not 'void const*', * this will warn "discards 'const' qualifier". */ union { const __m512i* cp; void* p; } remote_const_void; remote_const_void.cp = src + i; dest[i] = _mm512_add_epi64(_mm512_stream_load_si512(remote_const_void.p), seed); } } } #endif #if (XXH_VECTOR == XXH_AVX2) \ || (defined(XXH_DISPATCH_AVX2) && XXH_DISPATCH_AVX2 != 0) #ifndef XXH_TARGET_AVX2 # define XXH_TARGET_AVX2 /* disable attribute target */ #endif XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 31) == 0); { __m256i* const xacc = (__m256i *) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ const __m256i* const xinput = (const __m256i *) input; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ const __m256i* const xsecret = (const __m256i *) secret; size_t i; for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) { /* data_vec = xinput[i]; */ __m256i const data_vec = _mm256_loadu_si256 (xinput+i); /* key_vec = xsecret[i]; */ __m256i const key_vec = _mm256_loadu_si256 (xsecret+i); /* data_key = data_vec ^ key_vec; */ __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); /* data_key_lo = data_key >> 32; */ __m256i const data_key_lo = _mm256_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ __m256i const product = _mm256_mul_epu32 (data_key, data_key_lo); /* xacc[i] += swap(data_vec); */ __m256i const data_swap = _mm256_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2)); __m256i const sum = _mm256_add_epi64(xacc[i], data_swap); /* xacc[i] += product; */ xacc[i] = _mm256_add_epi64(product, sum); } } } XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_scrambleAcc_avx2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 31) == 0); { __m256i* const xacc = (__m256i*) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ const __m256i* const xsecret = (const __m256i *) secret; const __m256i prime32 = _mm256_set1_epi32((int)XXH_PRIME32_1); size_t i; for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) { /* xacc[i] ^= (xacc[i] >> 47) */ __m256i const acc_vec = xacc[i]; __m256i const shifted = _mm256_srli_epi64 (acc_vec, 47); __m256i const data_vec = _mm256_xor_si256 (acc_vec, shifted); /* xacc[i] ^= xsecret; */ __m256i const key_vec = _mm256_loadu_si256 (xsecret+i); __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); /* xacc[i] *= XXH_PRIME32_1; */ __m256i const data_key_hi = _mm256_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); __m256i const prod_lo = _mm256_mul_epu32 (data_key, prime32); __m256i const prod_hi = _mm256_mul_epu32 (data_key_hi, prime32); xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32)); } } } XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2(void* XXH_RESTRICT customSecret, xxh_u64 seed64) { XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 31) == 0); XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE / sizeof(__m256i)) == 6); XXH_STATIC_ASSERT(XXH_SEC_ALIGN <= 64); (void)(&XXH_writeLE64); XXH_PREFETCH(customSecret); { __m256i const seed = _mm256_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64, (xxh_i64)(0U - seed64), (xxh_i64)seed64); const __m256i* const src = (const __m256i*) ((const void*) XXH3_kSecret); __m256i* dest = ( __m256i*) customSecret; # if defined(__GNUC__) || defined(__clang__) /* * On GCC & Clang, marking 'dest' as modified will cause the compiler: * - do not extract the secret from sse registers in the internal loop * - use less common registers, and avoid pushing these reg into stack */ XXH_COMPILER_GUARD(dest); # endif XXH_ASSERT(((size_t)src & 31) == 0); /* control alignment */ XXH_ASSERT(((size_t)dest & 31) == 0); /* GCC -O2 need unroll loop manually */ dest[0] = _mm256_add_epi64(_mm256_stream_load_si256(src+0), seed); dest[1] = _mm256_add_epi64(_mm256_stream_load_si256(src+1), seed); dest[2] = _mm256_add_epi64(_mm256_stream_load_si256(src+2), seed); dest[3] = _mm256_add_epi64(_mm256_stream_load_si256(src+3), seed); dest[4] = _mm256_add_epi64(_mm256_stream_load_si256(src+4), seed); dest[5] = _mm256_add_epi64(_mm256_stream_load_si256(src+5), seed); } } #endif /* x86dispatch always generates SSE2 */ #if (XXH_VECTOR == XXH_SSE2) || defined(XXH_X86DISPATCH) #ifndef XXH_TARGET_SSE2 # define XXH_TARGET_SSE2 /* disable attribute target */ #endif XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_accumulate_512_sse2( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { /* SSE2 is just a half-scale version of the AVX2 version. */ XXH_ASSERT((((size_t)acc) & 15) == 0); { __m128i* const xacc = (__m128i *) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ const __m128i* const xinput = (const __m128i *) input; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ const __m128i* const xsecret = (const __m128i *) secret; size_t i; for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) { /* data_vec = xinput[i]; */ __m128i const data_vec = _mm_loadu_si128 (xinput+i); /* key_vec = xsecret[i]; */ __m128i const key_vec = _mm_loadu_si128 (xsecret+i); /* data_key = data_vec ^ key_vec; */ __m128i const data_key = _mm_xor_si128 (data_vec, key_vec); /* data_key_lo = data_key >> 32; */ __m128i const data_key_lo = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ __m128i const product = _mm_mul_epu32 (data_key, data_key_lo); /* xacc[i] += swap(data_vec); */ __m128i const data_swap = _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1,0,3,2)); __m128i const sum = _mm_add_epi64(xacc[i], data_swap); /* xacc[i] += product; */ xacc[i] = _mm_add_epi64(product, sum); } } } XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_scrambleAcc_sse2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); { __m128i* const xacc = (__m128i*) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ const __m128i* const xsecret = (const __m128i *) secret; const __m128i prime32 = _mm_set1_epi32((int)XXH_PRIME32_1); size_t i; for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) { /* xacc[i] ^= (xacc[i] >> 47) */ __m128i const acc_vec = xacc[i]; __m128i const shifted = _mm_srli_epi64 (acc_vec, 47); __m128i const data_vec = _mm_xor_si128 (acc_vec, shifted); /* xacc[i] ^= xsecret[i]; */ __m128i const key_vec = _mm_loadu_si128 (xsecret+i); __m128i const data_key = _mm_xor_si128 (data_vec, key_vec); /* xacc[i] *= XXH_PRIME32_1; */ __m128i const data_key_hi = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); __m128i const prod_lo = _mm_mul_epu32 (data_key, prime32); __m128i const prod_hi = _mm_mul_epu32 (data_key_hi, prime32); xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32)); } } } XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTRICT customSecret, xxh_u64 seed64) { XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); (void)(&XXH_writeLE64); { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i); # if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER < 1900 /* MSVC 32bit mode does not support _mm_set_epi64x before 2015 */ XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, (xxh_i64)(0U - seed64) }; __m128i const seed = _mm_load_si128((__m128i const*)seed64x2); # else __m128i const seed = _mm_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64); # endif int i; const void* const src16 = XXH3_kSecret; __m128i* dst16 = (__m128i*) customSecret; # if defined(__GNUC__) || defined(__clang__) /* * On GCC & Clang, marking 'dest' as modified will cause the compiler: * - do not extract the secret from sse registers in the internal loop * - use less common registers, and avoid pushing these reg into stack */ XXH_COMPILER_GUARD(dst16); # endif XXH_ASSERT(((size_t)src16 & 15) == 0); /* control alignment */ XXH_ASSERT(((size_t)dst16 & 15) == 0); for (i=0; i < nbRounds; ++i) { dst16[i] = _mm_add_epi64(_mm_load_si128((const __m128i *)src16+i), seed); } } } #endif #if (XXH_VECTOR == XXH_NEON) XXH_FORCE_INLINE void XXH3_accumulate_512_neon( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); { uint64x2_t* const xacc = (uint64x2_t *) acc; /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */ uint8_t const* const xinput = (const uint8_t *) input; uint8_t const* const xsecret = (const uint8_t *) secret; size_t i; for (i=0; i < XXH_STRIPE_LEN / sizeof(uint64x2_t); i++) { /* data_vec = xinput[i]; */ uint8x16_t data_vec = vld1q_u8(xinput + (i * 16)); /* key_vec = xsecret[i]; */ uint8x16_t key_vec = vld1q_u8(xsecret + (i * 16)); uint64x2_t data_key; uint32x2_t data_key_lo, data_key_hi; /* xacc[i] += swap(data_vec); */ uint64x2_t const data64 = vreinterpretq_u64_u8(data_vec); uint64x2_t const swapped = vextq_u64(data64, data64, 1); xacc[i] = vaddq_u64 (xacc[i], swapped); /* data_key = data_vec ^ key_vec; */ data_key = vreinterpretq_u64_u8(veorq_u8(data_vec, key_vec)); /* data_key_lo = (uint32x2_t) (data_key & 0xFFFFFFFF); * data_key_hi = (uint32x2_t) (data_key >> 32); * data_key = UNDEFINED; */ XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi); /* xacc[i] += (uint64x2_t) data_key_lo * (uint64x2_t) data_key_hi; */ xacc[i] = vmlal_u32 (xacc[i], data_key_lo, data_key_hi); } } } XXH_FORCE_INLINE void XXH3_scrambleAcc_neon(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); { uint64x2_t* xacc = (uint64x2_t*) acc; uint8_t const* xsecret = (uint8_t const*) secret; uint32x2_t prime = vdup_n_u32 (XXH_PRIME32_1); size_t i; for (i=0; i < XXH_STRIPE_LEN/sizeof(uint64x2_t); i++) { /* xacc[i] ^= (xacc[i] >> 47); */ uint64x2_t acc_vec = xacc[i]; uint64x2_t shifted = vshrq_n_u64 (acc_vec, 47); uint64x2_t data_vec = veorq_u64 (acc_vec, shifted); /* xacc[i] ^= xsecret[i]; */ uint8x16_t key_vec = vld1q_u8 (xsecret + (i * 16)); uint64x2_t data_key = veorq_u64 (data_vec, vreinterpretq_u64_u8(key_vec)); /* xacc[i] *= XXH_PRIME32_1 */ uint32x2_t data_key_lo, data_key_hi; /* data_key_lo = (uint32x2_t) (xacc[i] & 0xFFFFFFFF); * data_key_hi = (uint32x2_t) (xacc[i] >> 32); * xacc[i] = UNDEFINED; */ XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi); { /* * prod_hi = (data_key >> 32) * XXH_PRIME32_1; * * Avoid vmul_u32 + vshll_n_u32 since Clang 6 and 7 will * incorrectly "optimize" this: * tmp = vmul_u32(vmovn_u64(a), vmovn_u64(b)); * shifted = vshll_n_u32(tmp, 32); * to this: * tmp = "vmulq_u64"(a, b); // no such thing! * shifted = vshlq_n_u64(tmp, 32); * * However, unlike SSE, Clang lacks a 64-bit multiply routine * for NEON, and it scalarizes two 64-bit multiplies instead. * * vmull_u32 has the same timing as vmul_u32, and it avoids * this bug completely. * See https://bugs.llvm.org/show_bug.cgi?id=39967 */ uint64x2_t prod_hi = vmull_u32 (data_key_hi, prime); /* xacc[i] = prod_hi << 32; */ xacc[i] = vshlq_n_u64(prod_hi, 32); /* xacc[i] += (prod_hi & 0xFFFFFFFF) * XXH_PRIME32_1; */ xacc[i] = vmlal_u32(xacc[i], data_key_lo, prime); } } } } #endif #if (XXH_VECTOR == XXH_VSX) XXH_FORCE_INLINE void XXH3_accumulate_512_vsx( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { /* presumed aligned */ unsigned long long* const xacc = (unsigned long long*) acc; xxh_u64x2 const* const xinput = (xxh_u64x2 const*) input; /* no alignment restriction */ xxh_u64x2 const* const xsecret = (xxh_u64x2 const*) secret; /* no alignment restriction */ xxh_u64x2 const v32 = { 32, 32 }; size_t i; for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { /* data_vec = xinput[i]; */ xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + i); /* key_vec = xsecret[i]; */ xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i); xxh_u64x2 const data_key = data_vec ^ key_vec; /* shuffled = (data_key << 32) | (data_key >> 32); */ xxh_u32x4 const shuffled = (xxh_u32x4)vec_rl(data_key, v32); /* product = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)shuffled & 0xFFFFFFFF); */ xxh_u64x2 const product = XXH_vec_mulo((xxh_u32x4)data_key, shuffled); /* acc_vec = xacc[i]; */ xxh_u64x2 acc_vec = vec_xl(0, xacc + 2 * i); acc_vec += product; /* swap high and low halves */ #ifdef __s390x__ acc_vec += vec_permi(data_vec, data_vec, 2); #else acc_vec += vec_xxpermdi(data_vec, data_vec, 2); #endif /* xacc[i] = acc_vec; */ vec_xst(acc_vec, 0, xacc + 2 * i); } } XXH_FORCE_INLINE void XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); { xxh_u64x2* const xacc = (xxh_u64x2*) acc; const xxh_u64x2* const xsecret = (const xxh_u64x2*) secret; /* constants */ xxh_u64x2 const v32 = { 32, 32 }; xxh_u64x2 const v47 = { 47, 47 }; xxh_u32x4 const prime = { XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1 }; size_t i; for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { /* xacc[i] ^= (xacc[i] >> 47); */ xxh_u64x2 const acc_vec = xacc[i]; xxh_u64x2 const data_vec = acc_vec ^ (acc_vec >> v47); /* xacc[i] ^= xsecret[i]; */ xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i); xxh_u64x2 const data_key = data_vec ^ key_vec; /* xacc[i] *= XXH_PRIME32_1 */ /* prod_lo = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)prime & 0xFFFFFFFF); */ xxh_u64x2 const prod_even = XXH_vec_mule((xxh_u32x4)data_key, prime); /* prod_hi = ((xxh_u64x2)data_key >> 32) * ((xxh_u64x2)prime >> 32); */ xxh_u64x2 const prod_odd = XXH_vec_mulo((xxh_u32x4)data_key, prime); xacc[i] = prod_odd + (prod_even << v32); } } } #endif /* scalar variants - universal */ XXH_FORCE_INLINE void XXH3_accumulate_512_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ const xxh_u8* const xinput = (const xxh_u8*) input; /* no alignment restriction */ const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ size_t i; XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0); for (i=0; i < XXH_ACC_NB; i++) { xxh_u64 const data_val = XXH_readLE64(xinput + 8*i); xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + i*8); xacc[i ^ 1] += data_val; /* swap adjacent lanes */ xacc[i] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32); } } XXH_FORCE_INLINE void XXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ size_t i; XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0); for (i=0; i < XXH_ACC_NB; i++) { xxh_u64 const key64 = XXH_readLE64(xsecret + 8*i); xxh_u64 acc64 = xacc[i]; acc64 = XXH_xorshift64(acc64, 47); acc64 ^= key64; acc64 *= XXH_PRIME32_1; xacc[i] = acc64; } } XXH_FORCE_INLINE void XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) { /* * We need a separate pointer for the hack below, * which requires a non-const pointer. * Any decent compiler will optimize this out otherwise. */ const xxh_u8* kSecretPtr = XXH3_kSecret; XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); #if defined(__clang__) && defined(__aarch64__) /* * UGLY HACK: * Clang generates a bunch of MOV/MOVK pairs for aarch64, and they are * placed sequentially, in order, at the top of the unrolled loop. * * While MOVK is great for generating constants (2 cycles for a 64-bit * constant compared to 4 cycles for LDR), long MOVK chains stall the * integer pipelines: * I L S * MOVK * MOVK * MOVK * MOVK * ADD * SUB STR * STR * By forcing loads from memory (as the asm line causes Clang to assume * that XXH3_kSecretPtr has been changed), the pipelines are used more * efficiently: * I L S * LDR * ADD LDR * SUB STR * STR * XXH3_64bits_withSeed, len == 256, Snapdragon 835 * without hack: 2654.4 MB/s * with hack: 3202.9 MB/s */ XXH_COMPILER_GUARD(kSecretPtr); #endif /* * Note: in debug mode, this overrides the asm optimization * and Clang will emit MOVK chains again. */ XXH_ASSERT(kSecretPtr == XXH3_kSecret); { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; int i; for (i=0; i < nbRounds; i++) { /* * The asm hack causes Clang to assume that kSecretPtr aliases with * customSecret, and on aarch64, this prevented LDP from merging two * loads together for free. Putting the loads together before the stores * properly generates LDP. */ xxh_u64 lo = XXH_readLE64(kSecretPtr + 16*i) + seed64; xxh_u64 hi = XXH_readLE64(kSecretPtr + 16*i + 8) - seed64; XXH_writeLE64((xxh_u8*)customSecret + 16*i, lo); XXH_writeLE64((xxh_u8*)customSecret + 16*i + 8, hi); } } } typedef void (*XXH3_f_accumulate_512)(void* XXH_RESTRICT, const void*, const void*); typedef void (*XXH3_f_scrambleAcc)(void* XXH_RESTRICT, const void*); typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64); #if (XXH_VECTOR == XXH_AVX512) #define XXH3_accumulate_512 XXH3_accumulate_512_avx512 #define XXH3_scrambleAcc XXH3_scrambleAcc_avx512 #define XXH3_initCustomSecret XXH3_initCustomSecret_avx512 #elif (XXH_VECTOR == XXH_AVX2) #define XXH3_accumulate_512 XXH3_accumulate_512_avx2 #define XXH3_scrambleAcc XXH3_scrambleAcc_avx2 #define XXH3_initCustomSecret XXH3_initCustomSecret_avx2 #elif (XXH_VECTOR == XXH_SSE2) #define XXH3_accumulate_512 XXH3_accumulate_512_sse2 #define XXH3_scrambleAcc XXH3_scrambleAcc_sse2 #define XXH3_initCustomSecret XXH3_initCustomSecret_sse2 #elif (XXH_VECTOR == XXH_NEON) #define XXH3_accumulate_512 XXH3_accumulate_512_neon #define XXH3_scrambleAcc XXH3_scrambleAcc_neon #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar #elif (XXH_VECTOR == XXH_VSX) #define XXH3_accumulate_512 XXH3_accumulate_512_vsx #define XXH3_scrambleAcc XXH3_scrambleAcc_vsx #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar #else /* scalar */ #define XXH3_accumulate_512 XXH3_accumulate_512_scalar #define XXH3_scrambleAcc XXH3_scrambleAcc_scalar #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar #endif #ifndef XXH_PREFETCH_DIST # ifdef __clang__ # define XXH_PREFETCH_DIST 320 # else # if (XXH_VECTOR == XXH_AVX512) # define XXH_PREFETCH_DIST 512 # else # define XXH_PREFETCH_DIST 384 # endif # endif /* __clang__ */ #endif /* XXH_PREFETCH_DIST */ /* * XXH3_accumulate() * Loops over XXH3_accumulate_512(). * Assumption: nbStripes will not overflow the secret size */ XXH_FORCE_INLINE void XXH3_accumulate( xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT input, const xxh_u8* XXH_RESTRICT secret, size_t nbStripes, XXH3_f_accumulate_512 f_acc512) { size_t n; for (n = 0; n < nbStripes; n++ ) { const xxh_u8* const in = input + n*XXH_STRIPE_LEN; XXH_PREFETCH(in + XXH_PREFETCH_DIST); f_acc512(acc, in, secret + n*XXH_SECRET_CONSUME_RATE); } } XXH_FORCE_INLINE void XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble) { size_t const nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; size_t const block_len = XXH_STRIPE_LEN * nbStripesPerBlock; size_t const nb_blocks = (len - 1) / block_len; size_t n; XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); for (n = 0; n < nb_blocks; n++) { XXH3_accumulate(acc, input + n*block_len, secret, nbStripesPerBlock, f_acc512); f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN); } /* last partial block */ XXH_ASSERT(len > XXH_STRIPE_LEN); { size_t const nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN; XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE)); XXH3_accumulate(acc, input + nb_blocks*block_len, secret, nbStripes, f_acc512); /* last stripe */ { const xxh_u8* const p = input + len - XXH_STRIPE_LEN; #define XXH_SECRET_LASTACC_START 7 /* not aligned on 8, last secret is different from acc & scrambler */ f_acc512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START); } } } XXH_FORCE_INLINE xxh_u64 XXH3_mix2Accs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret) { return XXH3_mul128_fold64( acc[0] ^ XXH_readLE64(secret), acc[1] ^ XXH_readLE64(secret+8) ); } static XXH64_hash_t XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 start) { xxh_u64 result64 = start; size_t i = 0; for (i = 0; i < 4; i++) { result64 += XXH3_mix2Accs(acc+2*i, secret + 16*i); #if defined(__clang__) /* Clang */ \ && (defined(__arm__) || defined(__thumb__)) /* ARMv7 */ \ && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ /* * UGLY HACK: * Prevent autovectorization on Clang ARMv7-a. Exact same problem as * the one in XXH3_len_129to240_64b. Speeds up shorter keys > 240b. * XXH3_64bits, len == 256, Snapdragon 835: * without hack: 2063.7 MB/s * with hack: 2560.7 MB/s */ XXH_COMPILER_GUARD(result64); #endif } return XXH3_avalanche(result64); } #define XXH3_INIT_ACC { XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, \ XXH_PRIME64_4, XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 } XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len, const void* XXH_RESTRICT secret, size_t secretSize, XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble) { XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc512, f_scramble); /* converge into final hash */ XXH_STATIC_ASSERT(sizeof(acc) == 64); /* do not align on 8, so that the secret is different from the accumulator */ #define XXH_SECRET_MERGEACCS_START 11 XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); return XXH3_mergeAccs(acc, (const xxh_u8*)secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * XXH_PRIME64_1); } /* * It's important for performance to transmit secret's size (when it's static) * so that the compiler can properly optimize the vectorized loop. * This makes a big performance difference for "medium" keys (<1 KB) when using AVX instruction set. */ XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate_512, XXH3_scrambleAcc); } /* * It's preferable for performance that XXH3_hashLong is not inlined, * as it results in a smaller function for small data, easier to the instruction cache. * Note that inside this no_inline function, we do inline the internal loop, * and provide a statically defined secret size to allow optimization of vector loop. */ XXH_NO_INLINE XXH64_hash_t XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; (void)secret; (void)secretLen; return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512, XXH3_scrambleAcc); } /* * XXH3_hashLong_64b_withSeed(): * Generate a custom key based on alteration of default XXH3_kSecret with the seed, * and then use this key for long mode hashing. * * This operation is decently fast but nonetheless costs a little bit of time. * Try to avoid it whenever possible (typically when seed==0). * * It's important for performance that XXH3_hashLong is not inlined. Not sure * why (uop cache maybe?), but the difference is large and easily measurable. */ XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_withSeed_internal(const void* input, size_t len, XXH64_hash_t seed, XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble, XXH3_f_initCustomSecret f_initSec) { if (seed == 0) return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), f_acc512, f_scramble); { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; f_initSec(secret, seed); return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret), f_acc512, f_scramble); } } /* * It's important for performance that XXH3_hashLong is not inlined. */ XXH_NO_INLINE XXH64_hash_t XXH3_hashLong_64b_withSeed(const void* input, size_t len, XXH64_hash_t seed, const xxh_u8* secret, size_t secretLen) { (void)secret; (void)secretLen; return XXH3_hashLong_64b_withSeed_internal(input, len, seed, XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret); } typedef XXH64_hash_t (*XXH3_hashLong64_f)(const void* XXH_RESTRICT, size_t, XXH64_hash_t, const xxh_u8* XXH_RESTRICT, size_t); XXH_FORCE_INLINE XXH64_hash_t XXH3_64bits_internal(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen, XXH3_hashLong64_f f_hashLong) { XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); /* * If an action is to be taken if `secretLen` condition is not respected, * it should be done here. * For now, it's a contract pre-condition. * Adding a check and a branch here would cost performance at every hash. * Also, note that function signature doesn't offer room to return an error. */ if (len <= 16) return XXH3_len_0to16_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64); if (len <= 128) return XXH3_len_17to128_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); return f_hashLong(input, len, seed64, (const xxh_u8*)secret, secretLen); } /* === Public entry point === */ /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* input, size_t len) { return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize) { return XXH3_64bits_internal(input, len, 0, secret, secretSize, XXH3_hashLong_64b_withSecret); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) { return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed); } XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecretandSeed(const void* input, size_t len, const void* secret, size_t secretSize, XXH64_hash_t seed) { if (len <= XXH3_MIDSIZE_MAX) return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); return XXH3_hashLong_64b_withSecret(input, len, seed, (const xxh_u8*)secret, secretSize); } /* === XXH3 streaming === */ /* * Malloc's a pointer that is always aligned to align. * * This must be freed with `XXH_alignedFree()`. * * malloc typically guarantees 16 byte alignment on 64-bit systems and 8 byte * alignment on 32-bit. This isn't enough for the 32 byte aligned loads in AVX2 * or on 32-bit, the 16 byte aligned loads in SSE2 and NEON. * * This underalignment previously caused a rather obvious crash which went * completely unnoticed due to XXH3_createState() not actually being tested. * Credit to RedSpah for noticing this bug. * * The alignment is done manually: Functions like posix_memalign or _mm_malloc * are avoided: To maintain portability, we would have to write a fallback * like this anyways, and besides, testing for the existence of library * functions without relying on external build tools is impossible. * * The method is simple: Overallocate, manually align, and store the offset * to the original behind the returned pointer. * * Align must be a power of 2 and 8 <= align <= 128. */ static void* XXH_alignedMalloc(size_t s, size_t align) { XXH_ASSERT(align <= 128 && align >= 8); /* range check */ XXH_ASSERT((align & (align-1)) == 0); /* power of 2 */ XXH_ASSERT(s != 0 && s < (s + align)); /* empty/overflow */ { /* Overallocate to make room for manual realignment and an offset byte */ xxh_u8* base = (xxh_u8*)XXH_malloc(s + align); if (base != NULL) { /* * Get the offset needed to align this pointer. * * Even if the returned pointer is aligned, there will always be * at least one byte to store the offset to the original pointer. */ size_t offset = align - ((size_t)base & (align - 1)); /* base % align */ /* Add the offset for the now-aligned pointer */ xxh_u8* ptr = base + offset; XXH_ASSERT((size_t)ptr % align == 0); /* Store the offset immediately before the returned pointer. */ ptr[-1] = (xxh_u8)offset; return ptr; } return NULL; } } /* * Frees an aligned pointer allocated by XXH_alignedMalloc(). Don't pass * normal malloc'd pointers, XXH_alignedMalloc has a specific data layout. */ static void XXH_alignedFree(void* p) { if (p != NULL) { xxh_u8* ptr = (xxh_u8*)p; /* Get the offset byte we added in XXH_malloc. */ xxh_u8 offset = ptr[-1]; /* Free the original malloc'd pointer */ xxh_u8* base = ptr - offset; XXH_free(base); } } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void) { XXH3_state_t* const state = (XXH3_state_t*)XXH_alignedMalloc(sizeof(XXH3_state_t), 64); if (state==NULL) return NULL; XXH3_INITSTATE(state); return state; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr) { XXH_alignedFree(statePtr); return XXH_OK; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state) { XXH_memcpy(dst_state, src_state, sizeof(*dst_state)); } static void XXH3_reset_internal(XXH3_state_t* statePtr, XXH64_hash_t seed, const void* secret, size_t secretSize) { size_t const initStart = offsetof(XXH3_state_t, bufferedSize); size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart; XXH_ASSERT(offsetof(XXH3_state_t, nbStripesPerBlock) > initStart); XXH_ASSERT(statePtr != NULL); /* set members from bufferedSize to nbStripesPerBlock (excluded) to 0 */ memset((char*)statePtr + initStart, 0, initLength); statePtr->acc[0] = XXH_PRIME32_3; statePtr->acc[1] = XXH_PRIME64_1; statePtr->acc[2] = XXH_PRIME64_2; statePtr->acc[3] = XXH_PRIME64_3; statePtr->acc[4] = XXH_PRIME64_4; statePtr->acc[5] = XXH_PRIME32_2; statePtr->acc[6] = XXH_PRIME64_5; statePtr->acc[7] = XXH_PRIME32_1; statePtr->seed = seed; statePtr->useSeed = (seed != 0); statePtr->extSecret = (const unsigned char*)secret; XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); statePtr->secretLimit = secretSize - XXH_STRIPE_LEN; statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t* statePtr) { if (statePtr == NULL) return XXH_ERROR; XXH3_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); return XXH_OK; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) { if (statePtr == NULL) return XXH_ERROR; XXH3_reset_internal(statePtr, 0, secret, secretSize); if (secret == NULL) return XXH_ERROR; if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; return XXH_OK; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) { if (statePtr == NULL) return XXH_ERROR; if (seed==0) return XXH3_64bits_reset(statePtr); if ((seed != statePtr->seed) || (statePtr->extSecret != NULL)) XXH3_initCustomSecret(statePtr->customSecret, seed); XXH3_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); return XXH_OK; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecretandSeed(XXH3_state_t* statePtr, const void* secret, size_t secretSize, XXH64_hash_t seed64) { if (statePtr == NULL) return XXH_ERROR; if (secret == NULL) return XXH_ERROR; if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; XXH3_reset_internal(statePtr, seed64, secret, secretSize); statePtr->useSeed = 1; /* always, even if seed64==0 */ return XXH_OK; } /* Note : when XXH3_consumeStripes() is invoked, * there must be a guarantee that at least one more byte must be consumed from input * so that the function can blindly consume all stripes using the "normal" secret segment */ XXH_FORCE_INLINE void XXH3_consumeStripes(xxh_u64* XXH_RESTRICT acc, size_t* XXH_RESTRICT nbStripesSoFarPtr, size_t nbStripesPerBlock, const xxh_u8* XXH_RESTRICT input, size_t nbStripes, const xxh_u8* XXH_RESTRICT secret, size_t secretLimit, XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble) { XXH_ASSERT(nbStripes <= nbStripesPerBlock); /* can handle max 1 scramble per invocation */ XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock); if (nbStripesPerBlock - *nbStripesSoFarPtr <= nbStripes) { /* need a scrambling operation */ size_t const nbStripesToEndofBlock = nbStripesPerBlock - *nbStripesSoFarPtr; size_t const nbStripesAfterBlock = nbStripes - nbStripesToEndofBlock; XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripesToEndofBlock, f_acc512); f_scramble(acc, secret + secretLimit); XXH3_accumulate(acc, input + nbStripesToEndofBlock * XXH_STRIPE_LEN, secret, nbStripesAfterBlock, f_acc512); *nbStripesSoFarPtr = nbStripesAfterBlock; } else { XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripes, f_acc512); *nbStripesSoFarPtr += nbStripes; } } #ifndef XXH3_STREAM_USE_STACK # ifndef __clang__ /* clang doesn't need additional stack space */ # define XXH3_STREAM_USE_STACK 1 # endif #endif /* * Both XXH3_64bits_update and XXH3_128bits_update use this routine. */ XXH_FORCE_INLINE XXH_errorcode XXH3_update(XXH3_state_t* XXH_RESTRICT const state, const xxh_u8* XXH_RESTRICT input, size_t len, XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble) { if (input==NULL) { XXH_ASSERT(len == 0); return XXH_OK; } XXH_ASSERT(state != NULL); { const xxh_u8* const bEnd = input + len; const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; #if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 /* For some reason, gcc and MSVC seem to suffer greatly * when operating accumulators directly into state. * Operating into stack space seems to enable proper optimization. * clang, on the other hand, doesn't seem to need this trick */ XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[8]; memcpy(acc, state->acc, sizeof(acc)); #else xxh_u64* XXH_RESTRICT const acc = state->acc; #endif state->totalLen += len; XXH_ASSERT(state->bufferedSize <= XXH3_INTERNALBUFFER_SIZE); /* small input : just fill in tmp buffer */ if (state->bufferedSize + len <= XXH3_INTERNALBUFFER_SIZE) { XXH_memcpy(state->buffer + state->bufferedSize, input, len); state->bufferedSize += (XXH32_hash_t)len; return XXH_OK; } /* total input is now > XXH3_INTERNALBUFFER_SIZE */ #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN) XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == 0); /* clean multiple */ /* * Internal buffer is partially filled (always, except at beginning) * Complete it, then consume it. */ if (state->bufferedSize) { size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize; XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize); input += loadSize; XXH3_consumeStripes(acc, &state->nbStripesSoFar, state->nbStripesPerBlock, state->buffer, XXH3_INTERNALBUFFER_STRIPES, secret, state->secretLimit, f_acc512, f_scramble); state->bufferedSize = 0; } XXH_ASSERT(input < bEnd); /* large input to consume : ingest per full block */ if ((size_t)(bEnd - input) > state->nbStripesPerBlock * XXH_STRIPE_LEN) { size_t nbStripes = (size_t)(bEnd - 1 - input) / XXH_STRIPE_LEN; XXH_ASSERT(state->nbStripesPerBlock >= state->nbStripesSoFar); /* join to current block's end */ { size_t const nbStripesToEnd = state->nbStripesPerBlock - state->nbStripesSoFar; XXH_ASSERT(nbStripes <= nbStripes); XXH3_accumulate(acc, input, secret + state->nbStripesSoFar * XXH_SECRET_CONSUME_RATE, nbStripesToEnd, f_acc512); f_scramble(acc, secret + state->secretLimit); state->nbStripesSoFar = 0; input += nbStripesToEnd * XXH_STRIPE_LEN; nbStripes -= nbStripesToEnd; } /* consume per entire blocks */ while(nbStripes >= state->nbStripesPerBlock) { XXH3_accumulate(acc, input, secret, state->nbStripesPerBlock, f_acc512); f_scramble(acc, secret + state->secretLimit); input += state->nbStripesPerBlock * XXH_STRIPE_LEN; nbStripes -= state->nbStripesPerBlock; } /* consume last partial block */ XXH3_accumulate(acc, input, secret, nbStripes, f_acc512); input += nbStripes * XXH_STRIPE_LEN; XXH_ASSERT(input < bEnd); /* at least some bytes left */ state->nbStripesSoFar = nbStripes; /* buffer predecessor of last partial stripe */ XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); XXH_ASSERT(bEnd - input <= XXH_STRIPE_LEN); } else { /* content to consume <= block size */ /* Consume input by a multiple of internal buffer size */ if (bEnd - input > XXH3_INTERNALBUFFER_SIZE) { const xxh_u8* const limit = bEnd - XXH3_INTERNALBUFFER_SIZE; do { XXH3_consumeStripes(acc, &state->nbStripesSoFar, state->nbStripesPerBlock, input, XXH3_INTERNALBUFFER_STRIPES, secret, state->secretLimit, f_acc512, f_scramble); input += XXH3_INTERNALBUFFER_SIZE; } while (inputbuffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); } } /* Some remaining input (always) : buffer it */ XXH_ASSERT(input < bEnd); XXH_ASSERT(bEnd - input <= XXH3_INTERNALBUFFER_SIZE); XXH_ASSERT(state->bufferedSize == 0); XXH_memcpy(state->buffer, input, (size_t)(bEnd-input)); state->bufferedSize = (XXH32_hash_t)(bEnd-input); #if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 /* save stack accumulators into state */ memcpy(state->acc, acc, sizeof(acc)); #endif } return XXH_OK; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update(XXH3_state_t* state, const void* input, size_t len) { return XXH3_update(state, (const xxh_u8*)input, len, XXH3_accumulate_512, XXH3_scrambleAcc); } XXH_FORCE_INLINE void XXH3_digest_long (XXH64_hash_t* acc, const XXH3_state_t* state, const unsigned char* secret) { /* * Digest on a local copy. This way, the state remains unaltered, and it can * continue ingesting more input afterwards. */ XXH_memcpy(acc, state->acc, sizeof(state->acc)); if (state->bufferedSize >= XXH_STRIPE_LEN) { size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN; size_t nbStripesSoFar = state->nbStripesSoFar; XXH3_consumeStripes(acc, &nbStripesSoFar, state->nbStripesPerBlock, state->buffer, nbStripes, secret, state->secretLimit, XXH3_accumulate_512, XXH3_scrambleAcc); /* last stripe */ XXH3_accumulate_512(acc, state->buffer + state->bufferedSize - XXH_STRIPE_LEN, secret + state->secretLimit - XXH_SECRET_LASTACC_START); } else { /* bufferedSize < XXH_STRIPE_LEN */ xxh_u8 lastStripe[XXH_STRIPE_LEN]; size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize; XXH_ASSERT(state->bufferedSize > 0); /* there is always some input buffered */ XXH_memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize); XXH_memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); XXH3_accumulate_512(acc, lastStripe, secret + state->secretLimit - XXH_SECRET_LASTACC_START); } } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* state) { const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; if (state->totalLen > XXH3_MIDSIZE_MAX) { XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; XXH3_digest_long(acc, state, secret); return XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)state->totalLen * XXH_PRIME64_1); } /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */ if (state->useSeed) return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen), secret, state->secretLimit + XXH_STRIPE_LEN); } /* ========================================== * XXH3 128 bits (a.k.a XXH128) * ========================================== * XXH3's 128-bit variant has better mixing and strength than the 64-bit variant, * even without counting the significantly larger output size. * * For example, extra steps are taken to avoid the seed-dependent collisions * in 17-240 byte inputs (See XXH3_mix16B and XXH128_mix32B). * * This strength naturally comes at the cost of some speed, especially on short * lengths. Note that longer hashes are about as fast as the 64-bit version * due to it using only a slight modification of the 64-bit loop. * * XXH128 is also more oriented towards 64-bit machines. It is still extremely * fast for a _128-bit_ hash on 32-bit (it usually clears XXH64). */ XXH_FORCE_INLINE XXH128_hash_t XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { /* A doubled version of 1to3_64b with different constants. */ XXH_ASSERT(input != NULL); XXH_ASSERT(1 <= len && len <= 3); XXH_ASSERT(secret != NULL); /* * len = 1: combinedl = { input[0], 0x01, input[0], input[0] } * len = 2: combinedl = { input[1], 0x02, input[0], input[1] } * len = 3: combinedl = { input[2], 0x03, input[0], input[1] } */ { xxh_u8 const c1 = input[0]; xxh_u8 const c2 = input[len >> 1]; xxh_u8 const c3 = input[len - 1]; xxh_u32 const combinedl = ((xxh_u32)c1 <<16) | ((xxh_u32)c2 << 24) | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); xxh_u32 const combinedh = XXH_rotl32(XXH_swap32(combinedl), 13); xxh_u64 const bitflipl = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed; xxh_u64 const bitfliph = (XXH_readLE32(secret+8) ^ XXH_readLE32(secret+12)) - seed; xxh_u64 const keyed_lo = (xxh_u64)combinedl ^ bitflipl; xxh_u64 const keyed_hi = (xxh_u64)combinedh ^ bitfliph; XXH128_hash_t h128; h128.low64 = XXH64_avalanche(keyed_lo); h128.high64 = XXH64_avalanche(keyed_hi); return h128; } } XXH_FORCE_INLINE XXH128_hash_t XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(secret != NULL); XXH_ASSERT(4 <= len && len <= 8); seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; { xxh_u32 const input_lo = XXH_readLE32(input); xxh_u32 const input_hi = XXH_readLE32(input + len - 4); xxh_u64 const input_64 = input_lo + ((xxh_u64)input_hi << 32); xxh_u64 const bitflip = (XXH_readLE64(secret+16) ^ XXH_readLE64(secret+24)) + seed; xxh_u64 const keyed = input_64 ^ bitflip; /* Shift len to the left to ensure it is even, this avoids even multiplies. */ XXH128_hash_t m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + (len << 2)); m128.high64 += (m128.low64 << 1); m128.low64 ^= (m128.high64 >> 3); m128.low64 = XXH_xorshift64(m128.low64, 35); m128.low64 *= 0x9FB21C651E98DF25ULL; m128.low64 = XXH_xorshift64(m128.low64, 28); m128.high64 = XXH3_avalanche(m128.high64); return m128; } } XXH_FORCE_INLINE XXH128_hash_t XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(secret != NULL); XXH_ASSERT(9 <= len && len <= 16); { xxh_u64 const bitflipl = (XXH_readLE64(secret+32) ^ XXH_readLE64(secret+40)) - seed; xxh_u64 const bitfliph = (XXH_readLE64(secret+48) ^ XXH_readLE64(secret+56)) + seed; xxh_u64 const input_lo = XXH_readLE64(input); xxh_u64 input_hi = XXH_readLE64(input + len - 8); XXH128_hash_t m128 = XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1); /* * Put len in the middle of m128 to ensure that the length gets mixed to * both the low and high bits in the 128x64 multiply below. */ m128.low64 += (xxh_u64)(len - 1) << 54; input_hi ^= bitfliph; /* * Add the high 32 bits of input_hi to the high 32 bits of m128, then * add the long product of the low 32 bits of input_hi and XXH_PRIME32_2 to * the high 64 bits of m128. * * The best approach to this operation is different on 32-bit and 64-bit. */ if (sizeof(void *) < sizeof(xxh_u64)) { /* 32-bit */ /* * 32-bit optimized version, which is more readable. * * On 32-bit, it removes an ADC and delays a dependency between the two * halves of m128.high64, but it generates an extra mask on 64-bit. */ m128.high64 += (input_hi & 0xFFFFFFFF00000000ULL) + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2); } else { /* * 64-bit optimized (albeit more confusing) version. * * Uses some properties of addition and multiplication to remove the mask: * * Let: * a = input_hi.lo = (input_hi & 0x00000000FFFFFFFF) * b = input_hi.hi = (input_hi & 0xFFFFFFFF00000000) * c = XXH_PRIME32_2 * * a + (b * c) * Inverse Property: x + y - x == y * a + (b * (1 + c - 1)) * Distributive Property: x * (y + z) == (x * y) + (x * z) * a + (b * 1) + (b * (c - 1)) * Identity Property: x * 1 == x * a + b + (b * (c - 1)) * * Substitute a, b, and c: * input_hi.hi + input_hi.lo + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) * * Since input_hi.hi + input_hi.lo == input_hi, we get this: * input_hi + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) */ m128.high64 += input_hi + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2 - 1); } /* m128 ^= XXH_swap64(m128 >> 64); */ m128.low64 ^= XXH_swap64(m128.high64); { /* 128x64 multiply: h128 = m128 * XXH_PRIME64_2; */ XXH128_hash_t h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2); h128.high64 += m128.high64 * XXH_PRIME64_2; h128.low64 = XXH3_avalanche(h128.low64); h128.high64 = XXH3_avalanche(h128.high64); return h128; } } } /* * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN */ XXH_FORCE_INLINE XXH128_hash_t XXH3_len_0to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(len <= 16); { if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed); if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed); if (len) return XXH3_len_1to3_128b(input, len, secret, seed); { XXH128_hash_t h128; xxh_u64 const bitflipl = XXH_readLE64(secret+64) ^ XXH_readLE64(secret+72); xxh_u64 const bitfliph = XXH_readLE64(secret+80) ^ XXH_readLE64(secret+88); h128.low64 = XXH64_avalanche(seed ^ bitflipl); h128.high64 = XXH64_avalanche( seed ^ bitfliph); return h128; } } } /* * A bit slower than XXH3_mix16B, but handles multiply by zero better. */ XXH_FORCE_INLINE XXH128_hash_t XXH128_mix32B(XXH128_hash_t acc, const xxh_u8* input_1, const xxh_u8* input_2, const xxh_u8* secret, XXH64_hash_t seed) { acc.low64 += XXH3_mix16B (input_1, secret+0, seed); acc.low64 ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8); acc.high64 += XXH3_mix16B (input_2, secret+16, seed); acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8); return acc; } XXH_FORCE_INLINE XXH128_hash_t XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; XXH_ASSERT(16 < len && len <= 128); { XXH128_hash_t acc; acc.low64 = len * XXH_PRIME64_1; acc.high64 = 0; if (len > 32) { if (len > 64) { if (len > 96) { acc = XXH128_mix32B(acc, input+48, input+len-64, secret+96, seed); } acc = XXH128_mix32B(acc, input+32, input+len-48, secret+64, seed); } acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed); } acc = XXH128_mix32B(acc, input, input+len-16, secret, seed); { XXH128_hash_t h128; h128.low64 = acc.low64 + acc.high64; h128.high64 = (acc.low64 * XXH_PRIME64_1) + (acc.high64 * XXH_PRIME64_4) + ((len - seed) * XXH_PRIME64_2); h128.low64 = XXH3_avalanche(h128.low64); h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); return h128; } } } XXH_NO_INLINE XXH128_hash_t XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); { XXH128_hash_t acc; int const nbRounds = (int)len / 32; int i; acc.low64 = len * XXH_PRIME64_1; acc.high64 = 0; for (i=0; i<4; i++) { acc = XXH128_mix32B(acc, input + (32 * i), input + (32 * i) + 16, secret + (32 * i), seed); } acc.low64 = XXH3_avalanche(acc.low64); acc.high64 = XXH3_avalanche(acc.high64); XXH_ASSERT(nbRounds >= 4); for (i=4 ; i < nbRounds; i++) { acc = XXH128_mix32B(acc, input + (32 * i), input + (32 * i) + 16, secret + XXH3_MIDSIZE_STARTOFFSET + (32 * (i - 4)), seed); } /* last bytes */ acc = XXH128_mix32B(acc, input + len - 16, input + len - 32, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, 0ULL - seed); { XXH128_hash_t h128; h128.low64 = acc.low64 + acc.high64; h128.high64 = (acc.low64 * XXH_PRIME64_1) + (acc.high64 * XXH_PRIME64_4) + ((len - seed) * XXH_PRIME64_2); h128.low64 = XXH3_avalanche(h128.low64); h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); return h128; } } } XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_internal(const void* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble) { XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc512, f_scramble); /* converge into final hash */ XXH_STATIC_ASSERT(sizeof(acc) == 64); XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); { XXH128_hash_t h128; h128.low64 = XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * XXH_PRIME64_1); h128.high64 = XXH3_mergeAccs(acc, secret + secretSize - sizeof(acc) - XXH_SECRET_MERGEACCS_START, ~((xxh_u64)len * XXH_PRIME64_2)); return h128; } } /* * It's important for performance that XXH3_hashLong is not inlined. */ XXH_NO_INLINE XXH128_hash_t XXH3_hashLong_128b_default(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; (void)secret; (void)secretLen; return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512, XXH3_scrambleAcc); } /* * It's important for performance to pass @secretLen (when it's static) * to the compiler, so that it can properly optimize the vectorized loop. */ XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_withSecret(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen, XXH3_accumulate_512, XXH3_scrambleAcc); } XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_withSeed_internal(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble, XXH3_f_initCustomSecret f_initSec) { if (seed64 == 0) return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), f_acc512, f_scramble); { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; f_initSec(secret, seed64); return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, sizeof(secret), f_acc512, f_scramble); } } /* * It's important for performance that XXH3_hashLong is not inlined. */ XXH_NO_INLINE XXH128_hash_t XXH3_hashLong_128b_withSeed(const void* input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) { (void)secret; (void)secretLen; return XXH3_hashLong_128b_withSeed_internal(input, len, seed64, XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret); } typedef XXH128_hash_t (*XXH3_hashLong128_f)(const void* XXH_RESTRICT, size_t, XXH64_hash_t, const void* XXH_RESTRICT, size_t); XXH_FORCE_INLINE XXH128_hash_t XXH3_128bits_internal(const void* input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen, XXH3_hashLong128_f f_hl128) { XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); /* * If an action is to be taken if `secret` conditions are not respected, * it should be done here. * For now, it's a contract pre-condition. * Adding a check and a branch here would cost performance at every hash. */ if (len <= 16) return XXH3_len_0to16_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64); if (len <= 128) return XXH3_len_17to128_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); return f_hl128(input, len, seed64, secret, secretLen); } /* === Public XXH128 API === */ /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* input, size_t len) { return XXH3_128bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_default); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize) { return XXH3_128bits_internal(input, len, 0, (const xxh_u8*)secret, secretSize, XXH3_hashLong_128b_withSecret); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) { return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_withSeed); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecretandSeed(const void* input, size_t len, const void* secret, size_t secretSize, XXH64_hash_t seed) { if (len <= XXH3_MIDSIZE_MAX) return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); return XXH3_hashLong_128b_withSecret(input, len, seed, secret, secretSize); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH128_hash_t XXH128(const void* input, size_t len, XXH64_hash_t seed) { return XXH3_128bits_withSeed(input, len, seed); } /* === XXH3 128-bit streaming === */ /* * All initialization and update functions are identical to 64-bit streaming variant. * The only difference is the finalization routine. */ /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t* statePtr) { return XXH3_64bits_reset(statePtr); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) { return XXH3_64bits_reset_withSecret(statePtr, secret, secretSize); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) { return XXH3_64bits_reset_withSeed(statePtr, seed); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr, const void* secret, size_t secretSize, XXH64_hash_t seed) { return XXH3_64bits_reset_withSecretandSeed(statePtr, secret, secretSize, seed); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update(XXH3_state_t* state, const void* input, size_t len) { return XXH3_update(state, (const xxh_u8*)input, len, XXH3_accumulate_512, XXH3_scrambleAcc); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* state) { const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; if (state->totalLen > XXH3_MIDSIZE_MAX) { XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; XXH3_digest_long(acc, state, secret); XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); { XXH128_hash_t h128; h128.low64 = XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)state->totalLen * XXH_PRIME64_1); h128.high64 = XXH3_mergeAccs(acc, secret + state->secretLimit + XXH_STRIPE_LEN - sizeof(acc) - XXH_SECRET_MERGEACCS_START, ~((xxh_u64)state->totalLen * XXH_PRIME64_2)); return h128; } } /* len <= XXH3_MIDSIZE_MAX : short code */ if (state->seed) return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen), secret, state->secretLimit + XXH_STRIPE_LEN); } /* 128-bit utility functions */ #include /* memcmp, memcpy */ /* return : 1 is equal, 0 if different */ /*! @ingroup xxh3_family */ XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) { /* note : XXH128_hash_t is compact, it has no padding byte */ return !(memcmp(&h1, &h2, sizeof(h1))); } /* This prototype is compatible with stdlib's qsort(). * return : >0 if *h128_1 > *h128_2 * <0 if *h128_1 < *h128_2 * =0 if *h128_1 == *h128_2 */ /*! @ingroup xxh3_family */ XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2) { XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1; XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2; int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64); /* note : bets that, in most cases, hash values are different */ if (hcmp) return hcmp; return (h1.low64 > h2.low64) - (h2.low64 > h1.low64); } /*====== Canonical representation ======*/ /*! @ingroup xxh3_family */ XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) { hash.high64 = XXH_swap64(hash.high64); hash.low64 = XXH_swap64(hash.low64); } XXH_memcpy(dst, &hash.high64, sizeof(hash.high64)); XXH_memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src) { XXH128_hash_t h; h.high64 = XXH_readBE64(src); h.low64 = XXH_readBE64(src->digest + 8); return h; } /* ========================================== * Secret generators * ========================================== */ #define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x)) static void XXH3_combine16(void* dst, XXH128_hash_t h128) { XXH_writeLE64( dst, XXH_readLE64(dst) ^ h128.low64 ); XXH_writeLE64( (char*)dst+8, XXH_readLE64((char*)dst+8) ^ h128.high64 ); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(void* secretBuffer, size_t secretSize, const void* customSeed, size_t customSeedSize) { XXH_ASSERT(secretBuffer != NULL); if (secretBuffer == NULL) return XXH_ERROR; XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; if (customSeedSize == 0) { customSeed = XXH3_kSecret; customSeedSize = XXH_SECRET_DEFAULT_SIZE; } XXH_ASSERT(customSeed != NULL); if (customSeed == NULL) return XXH_ERROR; /* Fill secretBuffer with a copy of customSeed - repeat as needed */ { size_t pos = 0; while (pos < secretSize) { size_t const toCopy = XXH_MIN((secretSize - pos), customSeedSize); memcpy((char*)secretBuffer + pos, customSeed, toCopy); pos += toCopy; } } { size_t const nbSeg16 = secretSize / 16; size_t n; XXH128_canonical_t scrambler; XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0)); for (n=0; n #endif ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1711734324.093097 borgbackup-1.2.8/src/borg/algorithms/zstd/0000755000076500000240000000000014601577064017105 5ustar00twstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1699004 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/0000755000076500000240000000000014601577064017653 5ustar00twstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1747377 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/0000755000076500000240000000000014601577064021143 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/bitstream.h0000644000076500000240000004271114601576577023323 0ustar00twstaff/* ****************************************************************** * bitstream * Part of FSE library * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. ****************************************************************** */ #ifndef BITSTREAM_H_MODULE #define BITSTREAM_H_MODULE #if defined (__cplusplus) extern "C" { #endif /* * This API consists of small unitary functions, which must be inlined for best performance. * Since link-time-optimization is not available for all compilers, * these functions are defined into a .h to be included. */ /*-**************************************** * Dependencies ******************************************/ #include "mem.h" /* unaligned access routines */ #include "compiler.h" /* UNLIKELY() */ #include "debug.h" /* assert(), DEBUGLOG(), RAWLOG() */ #include "error_private.h" /* error codes and messages */ /*========================================= * Target specific =========================================*/ #if defined(__BMI__) && defined(__GNUC__) # include /* support for bextr (experimental) */ #elif defined(__ICCARM__) # include #endif #define STREAM_ACCUMULATOR_MIN_32 25 #define STREAM_ACCUMULATOR_MIN_64 57 #define STREAM_ACCUMULATOR_MIN ((U32)(MEM_32bits() ? STREAM_ACCUMULATOR_MIN_32 : STREAM_ACCUMULATOR_MIN_64)) /*-****************************************** * bitStream encoding API (write forward) ********************************************/ /* bitStream can mix input from multiple sources. * A critical property of these streams is that they encode and decode in **reverse** direction. * So the first bit sequence you add will be the last to be read, like a LIFO stack. */ typedef struct { size_t bitContainer; unsigned bitPos; char* startPtr; char* ptr; char* endPtr; } BIT_CStream_t; MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC, void* dstBuffer, size_t dstCapacity); MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC, size_t value, unsigned nbBits); MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC); MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC); /* Start with initCStream, providing the size of buffer to write into. * bitStream will never write outside of this buffer. * `dstCapacity` must be >= sizeof(bitD->bitContainer), otherwise @return will be an error code. * * bits are first added to a local register. * Local register is size_t, hence 64-bits on 64-bits systems, or 32-bits on 32-bits systems. * Writing data into memory is an explicit operation, performed by the flushBits function. * Hence keep track how many bits are potentially stored into local register to avoid register overflow. * After a flushBits, a maximum of 7 bits might still be stored into local register. * * Avoid storing elements of more than 24 bits if you want compatibility with 32-bits bitstream readers. * * Last operation is to close the bitStream. * The function returns the final size of CStream in bytes. * If data couldn't fit into `dstBuffer`, it will return a 0 ( == not storable) */ /*-******************************************** * bitStream decoding API (read backward) **********************************************/ typedef struct { size_t bitContainer; unsigned bitsConsumed; const char* ptr; const char* start; const char* limitPtr; } BIT_DStream_t; typedef enum { BIT_DStream_unfinished = 0, BIT_DStream_endOfBuffer = 1, BIT_DStream_completed = 2, BIT_DStream_overflow = 3 } BIT_DStream_status; /* result of BIT_reloadDStream() */ /* 1,2,4,8 would be better for bitmap combinations, but slows down performance a bit ... :( */ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize); MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits); MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD); MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* bitD); /* Start by invoking BIT_initDStream(). * A chunk of the bitStream is then stored into a local register. * Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t). * You can then retrieve bitFields stored into the local register, **in reverse order**. * Local register is explicitly reloaded from memory by the BIT_reloadDStream() method. * A reload guarantee a minimum of ((8*sizeof(bitD->bitContainer))-7) bits when its result is BIT_DStream_unfinished. * Otherwise, it can be less than that, so proceed accordingly. * Checking if DStream has reached its end can be performed with BIT_endOfDStream(). */ /*-**************************************** * unsafe API ******************************************/ MEM_STATIC void BIT_addBitsFast(BIT_CStream_t* bitC, size_t value, unsigned nbBits); /* faster, but works only if value is "clean", meaning all high bits above nbBits are 0 */ MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC); /* unsafe version; does not check buffer overflow */ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits); /* faster, but works only if nbBits >= 1 */ /*-************************************************************** * Internal functions ****************************************************************/ MEM_STATIC unsigned BIT_highbit32 (U32 val) { assert(val != 0); { # if defined(_MSC_VER) /* Visual */ unsigned long r=0; return _BitScanReverse ( &r, val ) ? (unsigned)r : 0; # elif defined(__GNUC__) && (__GNUC__ >= 3) /* Use GCC Intrinsic */ return __builtin_clz (val) ^ 31; # elif defined(__ICCARM__) /* IAR Intrinsic */ return 31 - __CLZ(val); # else /* Software version */ static const unsigned DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; U32 v = val; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; return DeBruijnClz[ (U32) (v * 0x07C4ACDDU) >> 27]; # endif } } /*===== Local Constants =====*/ static const unsigned BIT_mask[] = { 0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF}; /* up to 31 bits */ #define BIT_MASK_SIZE (sizeof(BIT_mask) / sizeof(BIT_mask[0])) /*-************************************************************** * bitStream encoding ****************************************************************/ /*! BIT_initCStream() : * `dstCapacity` must be > sizeof(size_t) * @return : 0 if success, * otherwise an error code (can be tested using ERR_isError()) */ MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC, void* startPtr, size_t dstCapacity) { bitC->bitContainer = 0; bitC->bitPos = 0; bitC->startPtr = (char*)startPtr; bitC->ptr = bitC->startPtr; bitC->endPtr = bitC->startPtr + dstCapacity - sizeof(bitC->bitContainer); if (dstCapacity <= sizeof(bitC->bitContainer)) return ERROR(dstSize_tooSmall); return 0; } /*! BIT_addBits() : * can add up to 31 bits into `bitC`. * Note : does not check for register overflow ! */ MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC, size_t value, unsigned nbBits) { MEM_STATIC_ASSERT(BIT_MASK_SIZE == 32); assert(nbBits < BIT_MASK_SIZE); assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8); bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos; bitC->bitPos += nbBits; } /*! BIT_addBitsFast() : * works only if `value` is _clean_, * meaning all high bits above nbBits are 0 */ MEM_STATIC void BIT_addBitsFast(BIT_CStream_t* bitC, size_t value, unsigned nbBits) { assert((value>>nbBits) == 0); assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8); bitC->bitContainer |= value << bitC->bitPos; bitC->bitPos += nbBits; } /*! BIT_flushBitsFast() : * assumption : bitContainer has not overflowed * unsafe version; does not check buffer overflow */ MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC) { size_t const nbBytes = bitC->bitPos >> 3; assert(bitC->bitPos < sizeof(bitC->bitContainer) * 8); assert(bitC->ptr <= bitC->endPtr); MEM_writeLEST(bitC->ptr, bitC->bitContainer); bitC->ptr += nbBytes; bitC->bitPos &= 7; bitC->bitContainer >>= nbBytes*8; } /*! BIT_flushBits() : * assumption : bitContainer has not overflowed * safe version; check for buffer overflow, and prevents it. * note : does not signal buffer overflow. * overflow will be revealed later on using BIT_closeCStream() */ MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC) { size_t const nbBytes = bitC->bitPos >> 3; assert(bitC->bitPos < sizeof(bitC->bitContainer) * 8); assert(bitC->ptr <= bitC->endPtr); MEM_writeLEST(bitC->ptr, bitC->bitContainer); bitC->ptr += nbBytes; if (bitC->ptr > bitC->endPtr) bitC->ptr = bitC->endPtr; bitC->bitPos &= 7; bitC->bitContainer >>= nbBytes*8; } /*! BIT_closeCStream() : * @return : size of CStream, in bytes, * or 0 if it could not fit into dstBuffer */ MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC) { BIT_addBitsFast(bitC, 1, 1); /* endMark */ BIT_flushBits(bitC); if (bitC->ptr >= bitC->endPtr) return 0; /* overflow detected */ return (bitC->ptr - bitC->startPtr) + (bitC->bitPos > 0); } /*-******************************************************** * bitStream decoding **********************************************************/ /*! BIT_initDStream() : * Initialize a BIT_DStream_t. * `bitD` : a pointer to an already allocated BIT_DStream_t structure. * `srcSize` must be the *exact* size of the bitStream, in bytes. * @return : size of stream (== srcSize), or an errorCode if a problem is detected */ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize) { if (srcSize < 1) { memset(bitD, 0, sizeof(*bitD)); return ERROR(srcSize_wrong); } bitD->start = (const char*)srcBuffer; bitD->limitPtr = bitD->start + sizeof(bitD->bitContainer); if (srcSize >= sizeof(bitD->bitContainer)) { /* normal case */ bitD->ptr = (const char*)srcBuffer + srcSize - sizeof(bitD->bitContainer); bitD->bitContainer = MEM_readLEST(bitD->ptr); { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1]; bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */ if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ } } else { bitD->ptr = bitD->start; bitD->bitContainer = *(const BYTE*)(bitD->start); switch(srcSize) { case 7: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[6]) << (sizeof(bitD->bitContainer)*8 - 16); /* fall-through */ case 6: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[5]) << (sizeof(bitD->bitContainer)*8 - 24); /* fall-through */ case 5: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[4]) << (sizeof(bitD->bitContainer)*8 - 32); /* fall-through */ case 4: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[3]) << 24; /* fall-through */ case 3: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[2]) << 16; /* fall-through */ case 2: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[1]) << 8; /* fall-through */ default: break; } { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1]; bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; if (lastByte == 0) return ERROR(corruption_detected); /* endMark not present */ } bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize)*8; } return srcSize; } MEM_STATIC size_t BIT_getUpperBits(size_t bitContainer, U32 const start) { return bitContainer >> start; } MEM_STATIC size_t BIT_getMiddleBits(size_t bitContainer, U32 const start, U32 const nbBits) { U32 const regMask = sizeof(bitContainer)*8 - 1; /* if start > regMask, bitstream is corrupted, and result is undefined */ assert(nbBits < BIT_MASK_SIZE); return (bitContainer >> (start & regMask)) & BIT_mask[nbBits]; } MEM_STATIC size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits) { assert(nbBits < BIT_MASK_SIZE); return bitContainer & BIT_mask[nbBits]; } /*! BIT_lookBits() : * Provides next n bits from local register. * local register is not modified. * On 32-bits, maxNbBits==24. * On 64-bits, maxNbBits==56. * @return : value extracted */ MEM_STATIC size_t BIT_lookBits(const BIT_DStream_t* bitD, U32 nbBits) { /* arbitrate between double-shift and shift+mask */ #if 1 /* if bitD->bitsConsumed + nbBits > sizeof(bitD->bitContainer)*8, * bitstream is likely corrupted, and result is undefined */ return BIT_getMiddleBits(bitD->bitContainer, (sizeof(bitD->bitContainer)*8) - bitD->bitsConsumed - nbBits, nbBits); #else /* this code path is slower on my os-x laptop */ U32 const regMask = sizeof(bitD->bitContainer)*8 - 1; return ((bitD->bitContainer << (bitD->bitsConsumed & regMask)) >> 1) >> ((regMask-nbBits) & regMask); #endif } /*! BIT_lookBitsFast() : * unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BIT_lookBitsFast(const BIT_DStream_t* bitD, U32 nbBits) { U32 const regMask = sizeof(bitD->bitContainer)*8 - 1; assert(nbBits >= 1); return (bitD->bitContainer << (bitD->bitsConsumed & regMask)) >> (((regMask+1)-nbBits) & regMask); } MEM_STATIC void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits) { bitD->bitsConsumed += nbBits; } /*! BIT_readBits() : * Read (consume) next n bits from local register and update. * Pay attention to not read more than nbBits contained into local register. * @return : extracted value. */ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits) { size_t const value = BIT_lookBits(bitD, nbBits); BIT_skipBits(bitD, nbBits); return value; } /*! BIT_readBitsFast() : * unsafe version; only works only if nbBits >= 1 */ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits) { size_t const value = BIT_lookBitsFast(bitD, nbBits); assert(nbBits >= 1); BIT_skipBits(bitD, nbBits); return value; } /*! BIT_reloadDStreamFast() : * Similar to BIT_reloadDStream(), but with two differences: * 1. bitsConsumed <= sizeof(bitD->bitContainer)*8 must hold! * 2. Returns BIT_DStream_overflow when bitD->ptr < bitD->limitPtr, at this * point you must use BIT_reloadDStream() to reload. */ MEM_STATIC BIT_DStream_status BIT_reloadDStreamFast(BIT_DStream_t* bitD) { if (UNLIKELY(bitD->ptr < bitD->limitPtr)) return BIT_DStream_overflow; assert(bitD->bitsConsumed <= sizeof(bitD->bitContainer)*8); bitD->ptr -= bitD->bitsConsumed >> 3; bitD->bitsConsumed &= 7; bitD->bitContainer = MEM_readLEST(bitD->ptr); return BIT_DStream_unfinished; } /*! BIT_reloadDStream() : * Refill `bitD` from buffer previously set in BIT_initDStream() . * This function is safe, it guarantees it will not read beyond src buffer. * @return : status of `BIT_DStream_t` internal register. * when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */ MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD) { if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* overflow detected, like end of stream */ return BIT_DStream_overflow; if (bitD->ptr >= bitD->limitPtr) { return BIT_reloadDStreamFast(bitD); } if (bitD->ptr == bitD->start) { if (bitD->bitsConsumed < sizeof(bitD->bitContainer)*8) return BIT_DStream_endOfBuffer; return BIT_DStream_completed; } /* start < ptr < limitPtr */ { U32 nbBytes = bitD->bitsConsumed >> 3; BIT_DStream_status result = BIT_DStream_unfinished; if (bitD->ptr - nbBytes < bitD->start) { nbBytes = (U32)(bitD->ptr - bitD->start); /* ptr > start */ result = BIT_DStream_endOfBuffer; } bitD->ptr -= nbBytes; bitD->bitsConsumed -= nbBytes*8; bitD->bitContainer = MEM_readLEST(bitD->ptr); /* reminder : srcSize > sizeof(bitD->bitContainer), otherwise bitD->ptr == bitD->start */ return result; } } /*! BIT_endOfDStream() : * @return : 1 if DStream has _exactly_ reached its end (all bits consumed). */ MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* DStream) { return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer)*8)); } #if defined (__cplusplus) } #endif #endif /* BITSTREAM_H_MODULE */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/compiler.h0000644000076500000240000001412114601576577023135 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_COMPILER_H #define ZSTD_COMPILER_H /*-******************************************************* * Compiler specifics *********************************************************/ /* force inlining */ #if !defined(ZSTD_NO_INLINE) #if (defined(__GNUC__) && !defined(__STRICT_ANSI__)) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ # define INLINE_KEYWORD inline #else # define INLINE_KEYWORD #endif #if defined(__GNUC__) || defined(__ICCARM__) # define FORCE_INLINE_ATTR __attribute__((always_inline)) #elif defined(_MSC_VER) # define FORCE_INLINE_ATTR __forceinline #else # define FORCE_INLINE_ATTR #endif #else #define INLINE_KEYWORD #define FORCE_INLINE_ATTR #endif /** * FORCE_INLINE_TEMPLATE is used to define C "templates", which take constant * parameters. They must be inlined for the compiler to eliminate the constant * branches. */ #define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR /** * HINT_INLINE is used to help the compiler generate better code. It is *not* * used for "templates", so it can be tweaked based on the compilers * performance. * * gcc-4.8 and gcc-4.9 have been shown to benefit from leaving off the * always_inline attribute. * * clang up to 5.0.0 (trunk) benefit tremendously from the always_inline * attribute. */ #if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 && __GNUC__ < 5 # define HINT_INLINE static INLINE_KEYWORD #else # define HINT_INLINE static INLINE_KEYWORD FORCE_INLINE_ATTR #endif /* UNUSED_ATTR tells the compiler it is okay if the function is unused. */ #if defined(__GNUC__) # define UNUSED_ATTR __attribute__((unused)) #else # define UNUSED_ATTR #endif /* force no inlining */ #ifdef _MSC_VER # define FORCE_NOINLINE static __declspec(noinline) #else # if defined(__GNUC__) || defined(__ICCARM__) # define FORCE_NOINLINE static __attribute__((__noinline__)) # else # define FORCE_NOINLINE static # endif #endif /* target attribute */ #ifndef __has_attribute #define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ #endif #if defined(__GNUC__) || defined(__ICCARM__) # define TARGET_ATTRIBUTE(target) __attribute__((__target__(target))) #else # define TARGET_ATTRIBUTE(target) #endif /* Enable runtime BMI2 dispatch based on the CPU. * Enabled for clang & gcc >=4.8 on x86 when BMI2 isn't enabled by default. */ #ifndef DYNAMIC_BMI2 #if ((defined(__clang__) && __has_attribute(__target__)) \ || (defined(__GNUC__) \ && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))) \ && (defined(__x86_64__) || defined(_M_X86)) \ && !defined(__BMI2__) # define DYNAMIC_BMI2 1 #else # define DYNAMIC_BMI2 0 #endif #endif /* prefetch * can be disabled, by declaring NO_PREFETCH build macro */ #if defined(NO_PREFETCH) # define PREFETCH_L1(ptr) (void)(ptr) /* disabled */ # define PREFETCH_L2(ptr) (void)(ptr) /* disabled */ #else # if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) /* _mm_prefetch() is not defined outside of x86/x64 */ # include /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ # define PREFETCH_L1(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) # define PREFETCH_L2(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T1) # elif defined(__aarch64__) # define PREFETCH_L1(ptr) __asm__ __volatile__("prfm pldl1keep, %0" ::"Q"(*(ptr))) # define PREFETCH_L2(ptr) __asm__ __volatile__("prfm pldl2keep, %0" ::"Q"(*(ptr))) # elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) ) # define PREFETCH_L1(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */) # define PREFETCH_L2(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 2 /* locality */) # else # define PREFETCH_L1(ptr) (void)(ptr) /* disabled */ # define PREFETCH_L2(ptr) (void)(ptr) /* disabled */ # endif #endif /* NO_PREFETCH */ #define CACHELINE_SIZE 64 #define PREFETCH_AREA(p, s) { \ const char* const _ptr = (const char*)(p); \ size_t const _size = (size_t)(s); \ size_t _pos; \ for (_pos=0; _pos<_size; _pos+=CACHELINE_SIZE) { \ PREFETCH_L2(_ptr + _pos); \ } \ } /* vectorization * older GCC (pre gcc-4.3 picked as the cutoff) uses a different syntax */ #if !defined(__INTEL_COMPILER) && !defined(__clang__) && defined(__GNUC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ > 3) || (__GNUC__ >= 5) # define DONT_VECTORIZE __attribute__((optimize("no-tree-vectorize"))) # else # define DONT_VECTORIZE _Pragma("GCC optimize(\"no-tree-vectorize\")") # endif #else # define DONT_VECTORIZE #endif /* Tell the compiler that a branch is likely or unlikely. * Only use these macros if it causes the compiler to generate better code. * If you can remove a LIKELY/UNLIKELY annotation without speed changes in gcc * and clang, please do. */ #if defined(__GNUC__) #define LIKELY(x) (__builtin_expect((x), 1)) #define UNLIKELY(x) (__builtin_expect((x), 0)) #else #define LIKELY(x) (x) #define UNLIKELY(x) (x) #endif /* disable warnings */ #ifdef _MSC_VER /* Visual Studio */ # include /* For Visual 2005 */ # pragma warning(disable : 4100) /* disable: C4100: unreferenced formal parameter */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */ # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ # pragma warning(disable : 4324) /* disable: C4324: padded structure */ #endif #endif /* ZSTD_COMPILER_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/cpu.h0000644000076500000240000001057414601576577022122 0ustar00twstaff/* * Copyright (c) 2018-2020, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_COMMON_CPU_H #define ZSTD_COMMON_CPU_H /** * Implementation taken from folly/CpuId.h * https://github.com/facebook/folly/blob/master/folly/CpuId.h */ #include #include "mem.h" #ifdef _MSC_VER #include #endif typedef struct { U32 f1c; U32 f1d; U32 f7b; U32 f7c; } ZSTD_cpuid_t; MEM_STATIC ZSTD_cpuid_t ZSTD_cpuid(void) { U32 f1c = 0; U32 f1d = 0; U32 f7b = 0; U32 f7c = 0; #if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) int reg[4]; __cpuid((int*)reg, 0); { int const n = reg[0]; if (n >= 1) { __cpuid((int*)reg, 1); f1c = (U32)reg[2]; f1d = (U32)reg[3]; } if (n >= 7) { __cpuidex((int*)reg, 7, 0); f7b = (U32)reg[1]; f7c = (U32)reg[2]; } } #elif defined(__i386__) && defined(__PIC__) && !defined(__clang__) && defined(__GNUC__) /* The following block like the normal cpuid branch below, but gcc * reserves ebx for use of its pic register so we must specially * handle the save and restore to avoid clobbering the register */ U32 n; __asm__( "pushl %%ebx\n\t" "cpuid\n\t" "popl %%ebx\n\t" : "=a"(n) : "a"(0) : "ecx", "edx"); if (n >= 1) { U32 f1a; __asm__( "pushl %%ebx\n\t" "cpuid\n\t" "popl %%ebx\n\t" : "=a"(f1a), "=c"(f1c), "=d"(f1d) : "a"(1)); } if (n >= 7) { __asm__( "pushl %%ebx\n\t" "cpuid\n\t" "movl %%ebx, %%eax\n\t" "popl %%ebx" : "=a"(f7b), "=c"(f7c) : "a"(7), "c"(0) : "edx"); } #elif defined(__x86_64__) || defined(_M_X64) || defined(__i386__) U32 n; __asm__("cpuid" : "=a"(n) : "a"(0) : "ebx", "ecx", "edx"); if (n >= 1) { U32 f1a; __asm__("cpuid" : "=a"(f1a), "=c"(f1c), "=d"(f1d) : "a"(1) : "ebx"); } if (n >= 7) { U32 f7a; __asm__("cpuid" : "=a"(f7a), "=b"(f7b), "=c"(f7c) : "a"(7), "c"(0) : "edx"); } #endif { ZSTD_cpuid_t cpuid; cpuid.f1c = f1c; cpuid.f1d = f1d; cpuid.f7b = f7b; cpuid.f7c = f7c; return cpuid; } } #define X(name, r, bit) \ MEM_STATIC int ZSTD_cpuid_##name(ZSTD_cpuid_t const cpuid) { \ return ((cpuid.r) & (1U << bit)) != 0; \ } /* cpuid(1): Processor Info and Feature Bits. */ #define C(name, bit) X(name, f1c, bit) C(sse3, 0) C(pclmuldq, 1) C(dtes64, 2) C(monitor, 3) C(dscpl, 4) C(vmx, 5) C(smx, 6) C(eist, 7) C(tm2, 8) C(ssse3, 9) C(cnxtid, 10) C(fma, 12) C(cx16, 13) C(xtpr, 14) C(pdcm, 15) C(pcid, 17) C(dca, 18) C(sse41, 19) C(sse42, 20) C(x2apic, 21) C(movbe, 22) C(popcnt, 23) C(tscdeadline, 24) C(aes, 25) C(xsave, 26) C(osxsave, 27) C(avx, 28) C(f16c, 29) C(rdrand, 30) #undef C #define D(name, bit) X(name, f1d, bit) D(fpu, 0) D(vme, 1) D(de, 2) D(pse, 3) D(tsc, 4) D(msr, 5) D(pae, 6) D(mce, 7) D(cx8, 8) D(apic, 9) D(sep, 11) D(mtrr, 12) D(pge, 13) D(mca, 14) D(cmov, 15) D(pat, 16) D(pse36, 17) D(psn, 18) D(clfsh, 19) D(ds, 21) D(acpi, 22) D(mmx, 23) D(fxsr, 24) D(sse, 25) D(sse2, 26) D(ss, 27) D(htt, 28) D(tm, 29) D(pbe, 31) #undef D /* cpuid(7): Extended Features. */ #define B(name, bit) X(name, f7b, bit) B(bmi1, 3) B(hle, 4) B(avx2, 5) B(smep, 7) B(bmi2, 8) B(erms, 9) B(invpcid, 10) B(rtm, 11) B(mpx, 14) B(avx512f, 16) B(avx512dq, 17) B(rdseed, 18) B(adx, 19) B(smap, 20) B(avx512ifma, 21) B(pcommit, 22) B(clflushopt, 23) B(clwb, 24) B(avx512pf, 26) B(avx512er, 27) B(avx512cd, 28) B(sha, 29) B(avx512bw, 30) B(avx512vl, 31) #undef B #define C(name, bit) X(name, f7c, bit) C(prefetchwt1, 0) C(avx512vbmi, 1) #undef C #undef X #endif /* ZSTD_COMMON_CPU_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/debug.c0000644000076500000240000000151514601576577022407 0ustar00twstaff/* ****************************************************************** * debug * Part of FSE library * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. ****************************************************************** */ /* * This module only hosts one global variable * which can be used to dynamically influence the verbosity of traces, * such as DEBUGLOG and RAWLOG */ #include "debug.h" int g_debuglevel = DEBUGLEVEL; ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/debug.h0000644000076500000240000000746514601576577022426 0ustar00twstaff/* ****************************************************************** * debug * Part of FSE library * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. ****************************************************************** */ /* * The purpose of this header is to enable debug functions. * They regroup assert(), DEBUGLOG() and RAWLOG() for run-time, * and DEBUG_STATIC_ASSERT() for compile-time. * * By default, DEBUGLEVEL==0, which means run-time debug is disabled. * * Level 1 enables assert() only. * Starting level 2, traces can be generated and pushed to stderr. * The higher the level, the more verbose the traces. * * It's possible to dynamically adjust level using variable g_debug_level, * which is only declared if DEBUGLEVEL>=2, * and is a global variable, not multi-thread protected (use with care) */ #ifndef DEBUG_H_12987983217 #define DEBUG_H_12987983217 #if defined (__cplusplus) extern "C" { #endif /* static assert is triggered at compile time, leaving no runtime artefact. * static assert only works with compile-time constants. * Also, this variant can only be used inside a function. */ #define DEBUG_STATIC_ASSERT(c) (void)sizeof(char[(c) ? 1 : -1]) /* DEBUGLEVEL is expected to be defined externally, * typically through compiler command line. * Value must be a number. */ #ifndef DEBUGLEVEL # define DEBUGLEVEL 0 #endif /* DEBUGFILE can be defined externally, * typically through compiler command line. * note : currently useless. * Value must be stderr or stdout */ #ifndef DEBUGFILE # define DEBUGFILE stderr #endif /* recommended values for DEBUGLEVEL : * 0 : release mode, no debug, all run-time checks disabled * 1 : enables assert() only, no display * 2 : reserved, for currently active debug path * 3 : events once per object lifetime (CCtx, CDict, etc.) * 4 : events once per frame * 5 : events once per block * 6 : events once per sequence (verbose) * 7+: events at every position (*very* verbose) * * It's generally inconvenient to output traces > 5. * In which case, it's possible to selectively trigger high verbosity levels * by modifying g_debug_level. */ #if (DEBUGLEVEL>=1) # include #else # ifndef assert /* assert may be already defined, due to prior #include */ # define assert(condition) ((void)0) /* disable assert (default) */ # endif #endif #if (DEBUGLEVEL>=2) # include extern int g_debuglevel; /* the variable is only declared, it actually lives in debug.c, and is shared by the whole process. It's not thread-safe. It's useful when enabling very verbose levels on selective conditions (such as position in src) */ # define RAWLOG(l, ...) { \ if (l<=g_debuglevel) { \ fprintf(stderr, __VA_ARGS__); \ } } # define DEBUGLOG(l, ...) { \ if (l<=g_debuglevel) { \ fprintf(stderr, __FILE__ ": " __VA_ARGS__); \ fprintf(stderr, " \n"); \ } } #else # define RAWLOG(l, ...) {} /* disabled */ # define DEBUGLOG(l, ...) {} /* disabled */ #endif #if defined (__cplusplus) } #endif #endif /* DEBUG_H_12987983217 */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/entropy_common.c0000644000076500000240000002055114601576577024372 0ustar00twstaff/* ****************************************************************** * Common functions of New Generation Entropy library * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy * - Public forum : https://groups.google.com/forum/#!forum/lz4c * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. ****************************************************************** */ /* ************************************* * Dependencies ***************************************/ #include "mem.h" #include "error_private.h" /* ERR_*, ERROR */ #define FSE_STATIC_LINKING_ONLY /* FSE_MIN_TABLELOG */ #include "fse.h" #define HUF_STATIC_LINKING_ONLY /* HUF_TABLELOG_ABSOLUTEMAX */ #include "huf.h" /*=== Version ===*/ unsigned FSE_versionNumber(void) { return FSE_VERSION_NUMBER; } /*=== Error Management ===*/ unsigned FSE_isError(size_t code) { return ERR_isError(code); } const char* FSE_getErrorName(size_t code) { return ERR_getErrorName(code); } unsigned HUF_isError(size_t code) { return ERR_isError(code); } const char* HUF_getErrorName(size_t code) { return ERR_getErrorName(code); } /*-************************************************************** * FSE NCount encoding-decoding ****************************************************************/ size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr, const void* headerBuffer, size_t hbSize) { const BYTE* const istart = (const BYTE*) headerBuffer; const BYTE* const iend = istart + hbSize; const BYTE* ip = istart; int nbBits; int remaining; int threshold; U32 bitStream; int bitCount; unsigned charnum = 0; int previous0 = 0; if (hbSize < 4) { /* This function only works when hbSize >= 4 */ char buffer[4]; memset(buffer, 0, sizeof(buffer)); memcpy(buffer, headerBuffer, hbSize); { size_t const countSize = FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr, buffer, sizeof(buffer)); if (FSE_isError(countSize)) return countSize; if (countSize > hbSize) return ERROR(corruption_detected); return countSize; } } assert(hbSize >= 4); /* init */ memset(normalizedCounter, 0, (*maxSVPtr+1) * sizeof(normalizedCounter[0])); /* all symbols not present in NCount have a frequency of 0 */ bitStream = MEM_readLE32(ip); nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */ if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX) return ERROR(tableLog_tooLarge); bitStream >>= 4; bitCount = 4; *tableLogPtr = nbBits; remaining = (1<1) & (charnum<=*maxSVPtr)) { if (previous0) { unsigned n0 = charnum; while ((bitStream & 0xFFFF) == 0xFFFF) { n0 += 24; if (ip < iend-5) { ip += 2; bitStream = MEM_readLE32(ip) >> bitCount; } else { bitStream >>= 16; bitCount += 16; } } while ((bitStream & 3) == 3) { n0 += 3; bitStream >>= 2; bitCount += 2; } n0 += bitStream & 3; bitCount += 2; if (n0 > *maxSVPtr) return ERROR(maxSymbolValue_tooSmall); while (charnum < n0) normalizedCounter[charnum++] = 0; if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) { assert((bitCount >> 3) <= 3); /* For first condition to work */ ip += bitCount>>3; bitCount &= 7; bitStream = MEM_readLE32(ip) >> bitCount; } else { bitStream >>= 2; } } { int const max = (2*threshold-1) - remaining; int count; if ((bitStream & (threshold-1)) < (U32)max) { count = bitStream & (threshold-1); bitCount += nbBits-1; } else { count = bitStream & (2*threshold-1); if (count >= threshold) count -= max; bitCount += nbBits; } count--; /* extra accuracy */ remaining -= count < 0 ? -count : count; /* -1 means +1 */ normalizedCounter[charnum++] = (short)count; previous0 = !count; while (remaining < threshold) { nbBits--; threshold >>= 1; } if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) { ip += bitCount>>3; bitCount &= 7; } else { bitCount -= (int)(8 * (iend - 4 - ip)); ip = iend - 4; } bitStream = MEM_readLE32(ip) >> (bitCount & 31); } } /* while ((remaining>1) & (charnum<=*maxSVPtr)) */ if (remaining != 1) return ERROR(corruption_detected); if (bitCount > 32) return ERROR(corruption_detected); *maxSVPtr = charnum-1; ip += (bitCount+7)>>3; return ip-istart; } /*! HUF_readStats() : Read compact Huffman tree, saved by HUF_writeCTable(). `huffWeight` is destination buffer. `rankStats` is assumed to be a table of at least HUF_TABLELOG_MAX U32. @return : size read from `src` , or an error Code . Note : Needed by HUF_readCTable() and HUF_readDTableX?() . */ size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr, const void* src, size_t srcSize) { U32 weightTotal; const BYTE* ip = (const BYTE*) src; size_t iSize; size_t oSize; if (!srcSize) return ERROR(srcSize_wrong); iSize = ip[0]; /* memset(huffWeight, 0, hwSize); *//* is not necessary, even though some analyzer complain ... */ if (iSize >= 128) { /* special header */ oSize = iSize - 127; iSize = ((oSize+1)/2); if (iSize+1 > srcSize) return ERROR(srcSize_wrong); if (oSize >= hwSize) return ERROR(corruption_detected); ip += 1; { U32 n; for (n=0; n> 4; huffWeight[n+1] = ip[n/2] & 15; } } } else { /* header compressed with FSE (normal case) */ FSE_DTable fseWorkspace[FSE_DTABLE_SIZE_U32(6)]; /* 6 is max possible tableLog for HUF header (maybe even 5, to be tested) */ if (iSize+1 > srcSize) return ERROR(srcSize_wrong); oSize = FSE_decompress_wksp(huffWeight, hwSize-1, ip+1, iSize, fseWorkspace, 6); /* max (hwSize-1) values decoded, as last one is implied */ if (FSE_isError(oSize)) return oSize; } /* collect weight stats */ memset(rankStats, 0, (HUF_TABLELOG_MAX + 1) * sizeof(U32)); weightTotal = 0; { U32 n; for (n=0; n= HUF_TABLELOG_MAX) return ERROR(corruption_detected); rankStats[huffWeight[n]]++; weightTotal += (1 << huffWeight[n]) >> 1; } } if (weightTotal == 0) return ERROR(corruption_detected); /* get last non-null symbol weight (implied, total must be 2^n) */ { U32 const tableLog = BIT_highbit32(weightTotal) + 1; if (tableLog > HUF_TABLELOG_MAX) return ERROR(corruption_detected); *tableLogPtr = tableLog; /* determine last weight */ { U32 const total = 1 << tableLog; U32 const rest = total - weightTotal; U32 const verif = 1 << BIT_highbit32(rest); U32 const lastWeight = BIT_highbit32(rest) + 1; if (verif != rest) return ERROR(corruption_detected); /* last value must be a clean power of 2 */ huffWeight[oSize] = (BYTE)lastWeight; rankStats[lastWeight]++; } } /* check tree construction validity */ if ((rankStats[1] < 2) || (rankStats[1] & 1)) return ERROR(corruption_detected); /* by construction : at least 2 elts of rank 1, must be even */ /* results */ *nbSymbolsPtr = (U32)(oSize+1); return iSize+1; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/error_private.c0000644000076500000240000000557614601576577024217 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* The purpose of this file is to have a single list of error strings embedded in binary */ #include "error_private.h" const char* ERR_getErrorString(ERR_enum code) { #ifdef ZSTD_STRIP_ERROR_STRINGS (void)code; return "Error strings stripped"; #else static const char* const notErrorCode = "Unspecified error code"; switch( code ) { case PREFIX(no_error): return "No error detected"; case PREFIX(GENERIC): return "Error (generic)"; case PREFIX(prefix_unknown): return "Unknown frame descriptor"; case PREFIX(version_unsupported): return "Version not supported"; case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter"; case PREFIX(frameParameter_windowTooLarge): return "Frame requires too much memory for decoding"; case PREFIX(corruption_detected): return "Corrupted block detected"; case PREFIX(checksum_wrong): return "Restored data doesn't match checksum"; case PREFIX(parameter_unsupported): return "Unsupported parameter"; case PREFIX(parameter_outOfBound): return "Parameter is out of bound"; case PREFIX(init_missing): return "Context should be init first"; case PREFIX(memory_allocation): return "Allocation error : not enough memory"; case PREFIX(workSpace_tooSmall): return "workSpace buffer is not large enough"; case PREFIX(stage_wrong): return "Operation not authorized at current processing stage"; case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory : unsupported"; case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max Symbol Value : too large"; case PREFIX(maxSymbolValue_tooSmall): return "Specified maxSymbolValue is too small"; case PREFIX(dictionary_corrupted): return "Dictionary is corrupted"; case PREFIX(dictionary_wrong): return "Dictionary mismatch"; case PREFIX(dictionaryCreation_failed): return "Cannot create Dictionary from provided samples"; case PREFIX(dstSize_tooSmall): return "Destination buffer is too small"; case PREFIX(srcSize_wrong): return "Src size is incorrect"; case PREFIX(dstBuffer_null): return "Operation on NULL destination buffer"; /* following error codes are not stable and may be removed or changed in a future version */ case PREFIX(frameIndex_tooLarge): return "Frame index is too large"; case PREFIX(seekableIO): return "An I/O error occurred when reading/seeking"; case PREFIX(dstBuffer_wrong): return "Destination buffer is wrong"; case PREFIX(maxCode): default: return notErrorCode; } #endif } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/error_private.h0000644000076500000240000000461214601576577024212 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* Note : this module is expected to remain private, do not expose it */ #ifndef ERROR_H_MODULE #define ERROR_H_MODULE #if defined (__cplusplus) extern "C" { #endif /* **************************************** * Dependencies ******************************************/ #include /* size_t */ #include "zstd_errors.h" /* enum list */ /* **************************************** * Compiler-specific ******************************************/ #if defined(__GNUC__) # define ERR_STATIC static __attribute__((unused)) #elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # define ERR_STATIC static inline #elif defined(_MSC_VER) # define ERR_STATIC static __inline #else # define ERR_STATIC static /* this version may generate warnings for unused static functions; disable the relevant warning */ #endif /*-**************************************** * Customization (error_public.h) ******************************************/ typedef ZSTD_ErrorCode ERR_enum; #define PREFIX(name) ZSTD_error_##name /*-**************************************** * Error codes handling ******************************************/ #undef ERROR /* already defined on Visual Studio */ #define ERROR(name) ZSTD_ERROR(name) #define ZSTD_ERROR(name) ((size_t)-PREFIX(name)) ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); } ERR_STATIC ERR_enum ERR_getErrorCode(size_t code) { if (!ERR_isError(code)) return (ERR_enum)0; return (ERR_enum) (0-code); } /* check and forward error code */ #define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return e #define CHECK_F(f) { CHECK_V_F(_var_err__, f); } /*-**************************************** * Error Strings ******************************************/ const char* ERR_getErrorString(ERR_enum code); /* error_private.c */ ERR_STATIC const char* ERR_getErrorName(size_t code) { return ERR_getErrorString(ERR_getErrorCode(code)); } #if defined (__cplusplus) } #endif #endif /* ERROR_H_MODULE */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/fse.h0000644000076500000240000007633714601576577022121 0ustar00twstaff/* ****************************************************************** * FSE : Finite State Entropy codec * Public Prototypes declaration * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. ****************************************************************** */ #if defined (__cplusplus) extern "C" { #endif #ifndef FSE_H #define FSE_H /*-***************************************** * Dependencies ******************************************/ #include /* size_t, ptrdiff_t */ /*-***************************************** * FSE_PUBLIC_API : control library symbols visibility ******************************************/ #if defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) && defined(__GNUC__) && (__GNUC__ >= 4) # define FSE_PUBLIC_API __attribute__ ((visibility ("default"))) #elif defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) /* Visual expected */ # define FSE_PUBLIC_API __declspec(dllexport) #elif defined(FSE_DLL_IMPORT) && (FSE_DLL_IMPORT==1) # define FSE_PUBLIC_API __declspec(dllimport) /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ #else # define FSE_PUBLIC_API #endif /*------ Version ------*/ #define FSE_VERSION_MAJOR 0 #define FSE_VERSION_MINOR 9 #define FSE_VERSION_RELEASE 0 #define FSE_LIB_VERSION FSE_VERSION_MAJOR.FSE_VERSION_MINOR.FSE_VERSION_RELEASE #define FSE_QUOTE(str) #str #define FSE_EXPAND_AND_QUOTE(str) FSE_QUOTE(str) #define FSE_VERSION_STRING FSE_EXPAND_AND_QUOTE(FSE_LIB_VERSION) #define FSE_VERSION_NUMBER (FSE_VERSION_MAJOR *100*100 + FSE_VERSION_MINOR *100 + FSE_VERSION_RELEASE) FSE_PUBLIC_API unsigned FSE_versionNumber(void); /**< library version number; to be used when checking dll version */ /*-**************************************** * FSE simple functions ******************************************/ /*! FSE_compress() : Compress content of buffer 'src', of size 'srcSize', into destination buffer 'dst'. 'dst' buffer must be already allocated. Compression runs faster is dstCapacity >= FSE_compressBound(srcSize). @return : size of compressed data (<= dstCapacity). Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!! if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression instead. if FSE_isError(return), compression failed (more details using FSE_getErrorName()) */ FSE_PUBLIC_API size_t FSE_compress(void* dst, size_t dstCapacity, const void* src, size_t srcSize); /*! FSE_decompress(): Decompress FSE data from buffer 'cSrc', of size 'cSrcSize', into already allocated destination buffer 'dst', of size 'dstCapacity'. @return : size of regenerated data (<= maxDstSize), or an error code, which can be tested using FSE_isError() . ** Important ** : FSE_decompress() does not decompress non-compressible nor RLE data !!! Why ? : making this distinction requires a header. Header management is intentionally delegated to the user layer, which can better manage special cases. */ FSE_PUBLIC_API size_t FSE_decompress(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize); /*-***************************************** * Tool functions ******************************************/ FSE_PUBLIC_API size_t FSE_compressBound(size_t size); /* maximum compressed size */ /* Error Management */ FSE_PUBLIC_API unsigned FSE_isError(size_t code); /* tells if a return value is an error code */ FSE_PUBLIC_API const char* FSE_getErrorName(size_t code); /* provides error code string (useful for debugging) */ /*-***************************************** * FSE advanced functions ******************************************/ /*! FSE_compress2() : Same as FSE_compress(), but allows the selection of 'maxSymbolValue' and 'tableLog' Both parameters can be defined as '0' to mean : use default value @return : size of compressed data Special values : if return == 0, srcData is not compressible => Nothing is stored within cSrc !!! if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression. if FSE_isError(return), it's an error code. */ FSE_PUBLIC_API size_t FSE_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); /*-***************************************** * FSE detailed API ******************************************/ /*! FSE_compress() does the following: 1. count symbol occurrence from source[] into table count[] (see hist.h) 2. normalize counters so that sum(count[]) == Power_of_2 (2^tableLog) 3. save normalized counters to memory buffer using writeNCount() 4. build encoding table 'CTable' from normalized counters 5. encode the data stream using encoding table 'CTable' FSE_decompress() does the following: 1. read normalized counters with readNCount() 2. build decoding table 'DTable' from normalized counters 3. decode the data stream using decoding table 'DTable' The following API allows targeting specific sub-functions for advanced tasks. For example, it's possible to compress several blocks using the same 'CTable', or to save and provide normalized distribution using external method. */ /* *** COMPRESSION *** */ /*! FSE_optimalTableLog(): dynamically downsize 'tableLog' when conditions are met. It saves CPU time, by using smaller tables, while preserving or even improving compression ratio. @return : recommended tableLog (necessarily <= 'maxTableLog') */ FSE_PUBLIC_API unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); /*! FSE_normalizeCount(): normalize counts so that sum(count[]) == Power_of_2 (2^tableLog) 'normalizedCounter' is a table of short, of minimum size (maxSymbolValue+1). @return : tableLog, or an errorCode, which can be tested using FSE_isError() */ FSE_PUBLIC_API size_t FSE_normalizeCount(short* normalizedCounter, unsigned tableLog, const unsigned* count, size_t srcSize, unsigned maxSymbolValue); /*! FSE_NCountWriteBound(): Provides the maximum possible size of an FSE normalized table, given 'maxSymbolValue' and 'tableLog'. Typically useful for allocation purpose. */ FSE_PUBLIC_API size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog); /*! FSE_writeNCount(): Compactly save 'normalizedCounter' into 'buffer'. @return : size of the compressed table, or an errorCode, which can be tested using FSE_isError(). */ FSE_PUBLIC_API size_t FSE_writeNCount (void* buffer, size_t bufferSize, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); /*! Constructor and Destructor of FSE_CTable. Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */ typedef unsigned FSE_CTable; /* don't allocate that. It's only meant to be more restrictive than void* */ FSE_PUBLIC_API FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog); FSE_PUBLIC_API void FSE_freeCTable (FSE_CTable* ct); /*! FSE_buildCTable(): Builds `ct`, which must be already allocated, using FSE_createCTable(). @return : 0, or an errorCode, which can be tested using FSE_isError() */ FSE_PUBLIC_API size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); /*! FSE_compress_usingCTable(): Compress `src` using `ct` into `dst` which must be already allocated. @return : size of compressed data (<= `dstCapacity`), or 0 if compressed data could not fit into `dst`, or an errorCode, which can be tested using FSE_isError() */ FSE_PUBLIC_API size_t FSE_compress_usingCTable (void* dst, size_t dstCapacity, const void* src, size_t srcSize, const FSE_CTable* ct); /*! Tutorial : ---------- The first step is to count all symbols. FSE_count() does this job very fast. Result will be saved into 'count', a table of unsigned int, which must be already allocated, and have 'maxSymbolValuePtr[0]+1' cells. 'src' is a table of bytes of size 'srcSize'. All values within 'src' MUST be <= maxSymbolValuePtr[0] maxSymbolValuePtr[0] will be updated, with its real value (necessarily <= original value) FSE_count() will return the number of occurrence of the most frequent symbol. This can be used to know if there is a single symbol within 'src', and to quickly evaluate its compressibility. If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()). The next step is to normalize the frequencies. FSE_normalizeCount() will ensure that sum of frequencies is == 2 ^'tableLog'. It also guarantees a minimum of 1 to any Symbol with frequency >= 1. You can use 'tableLog'==0 to mean "use default tableLog value". If you are unsure of which tableLog value to use, you can ask FSE_optimalTableLog(), which will provide the optimal valid tableLog given sourceSize, maxSymbolValue, and a user-defined maximum (0 means "default"). The result of FSE_normalizeCount() will be saved into a table, called 'normalizedCounter', which is a table of signed short. 'normalizedCounter' must be already allocated, and have at least 'maxSymbolValue+1' cells. The return value is tableLog if everything proceeded as expected. It is 0 if there is a single symbol within distribution. If there is an error (ex: invalid tableLog value), the function will return an ErrorCode (which can be tested using FSE_isError()). 'normalizedCounter' can be saved in a compact manner to a memory area using FSE_writeNCount(). 'buffer' must be already allocated. For guaranteed success, buffer size must be at least FSE_headerBound(). The result of the function is the number of bytes written into 'buffer'. If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError(); ex : buffer size too small). 'normalizedCounter' can then be used to create the compression table 'CTable'. The space required by 'CTable' must be already allocated, using FSE_createCTable(). You can then use FSE_buildCTable() to fill 'CTable'. If there is an error, both functions will return an ErrorCode (which can be tested using FSE_isError()). 'CTable' can then be used to compress 'src', with FSE_compress_usingCTable(). Similar to FSE_count(), the convention is that 'src' is assumed to be a table of char of size 'srcSize' The function returns the size of compressed data (without header), necessarily <= `dstCapacity`. If it returns '0', compressed data could not fit into 'dst'. If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()). */ /* *** DECOMPRESSION *** */ /*! FSE_readNCount(): Read compactly saved 'normalizedCounter' from 'rBuffer'. @return : size read from 'rBuffer', or an errorCode, which can be tested using FSE_isError(). maxSymbolValuePtr[0] and tableLogPtr[0] will also be updated with their respective values */ FSE_PUBLIC_API size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSymbolValuePtr, unsigned* tableLogPtr, const void* rBuffer, size_t rBuffSize); /*! Constructor and Destructor of FSE_DTable. Note that its size depends on 'tableLog' */ typedef unsigned FSE_DTable; /* don't allocate that. It's just a way to be more restrictive than void* */ FSE_PUBLIC_API FSE_DTable* FSE_createDTable(unsigned tableLog); FSE_PUBLIC_API void FSE_freeDTable(FSE_DTable* dt); /*! FSE_buildDTable(): Builds 'dt', which must be already allocated, using FSE_createDTable(). return : 0, or an errorCode, which can be tested using FSE_isError() */ FSE_PUBLIC_API size_t FSE_buildDTable (FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); /*! FSE_decompress_usingDTable(): Decompress compressed source `cSrc` of size `cSrcSize` using `dt` into `dst` which must be already allocated. @return : size of regenerated data (necessarily <= `dstCapacity`), or an errorCode, which can be tested using FSE_isError() */ FSE_PUBLIC_API size_t FSE_decompress_usingDTable(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt); /*! Tutorial : ---------- (Note : these functions only decompress FSE-compressed blocks. If block is uncompressed, use memcpy() instead If block is a single repeated byte, use memset() instead ) The first step is to obtain the normalized frequencies of symbols. This can be performed by FSE_readNCount() if it was saved using FSE_writeNCount(). 'normalizedCounter' must be already allocated, and have at least 'maxSymbolValuePtr[0]+1' cells of signed short. In practice, that means it's necessary to know 'maxSymbolValue' beforehand, or size the table to handle worst case situations (typically 256). FSE_readNCount() will provide 'tableLog' and 'maxSymbolValue'. The result of FSE_readNCount() is the number of bytes read from 'rBuffer'. Note that 'rBufferSize' must be at least 4 bytes, even if useful information is less than that. If there is an error, the function will return an error code, which can be tested using FSE_isError(). The next step is to build the decompression tables 'FSE_DTable' from 'normalizedCounter'. This is performed by the function FSE_buildDTable(). The space required by 'FSE_DTable' must be already allocated using FSE_createDTable(). If there is an error, the function will return an error code, which can be tested using FSE_isError(). `FSE_DTable` can then be used to decompress `cSrc`, with FSE_decompress_usingDTable(). `cSrcSize` must be strictly correct, otherwise decompression will fail. FSE_decompress_usingDTable() result will tell how many bytes were regenerated (<=`dstCapacity`). If there is an error, the function will return an error code, which can be tested using FSE_isError(). (ex: dst buffer too small) */ #endif /* FSE_H */ #if defined(FSE_STATIC_LINKING_ONLY) && !defined(FSE_H_FSE_STATIC_LINKING_ONLY) #define FSE_H_FSE_STATIC_LINKING_ONLY /* *** Dependency *** */ #include "bitstream.h" /* ***************************************** * Static allocation *******************************************/ /* FSE buffer bounds */ #define FSE_NCOUNTBOUND 512 #define FSE_BLOCKBOUND(size) (size + (size>>7) + 4 /* fse states */ + sizeof(size_t) /* bitContainer */) #define FSE_COMPRESSBOUND(size) (FSE_NCOUNTBOUND + FSE_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ /* It is possible to statically allocate FSE CTable/DTable as a table of FSE_CTable/FSE_DTable using below macros */ #define FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) (1 + (1<<(maxTableLog-1)) + ((maxSymbolValue+1)*2)) #define FSE_DTABLE_SIZE_U32(maxTableLog) (1 + (1< 12) ? (1 << (maxTableLog - 2)) : 1024) ) size_t FSE_compress_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits); /**< build a fake FSE_CTable, designed for a flat distribution, where each symbol uses nbBits */ size_t FSE_buildCTable_rle (FSE_CTable* ct, unsigned char symbolValue); /**< build a fake FSE_CTable, designed to compress always the same symbolValue */ /* FSE_buildCTable_wksp() : * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`). * `wkspSize` must be >= `(1<= BIT_DStream_completed When it's done, verify decompression is fully completed, by checking both DStream and the relevant states. Checking if DStream has reached its end is performed by : BIT_endOfDStream(&DStream); Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible. FSE_endOfDState(&DState); */ /* ***************************************** * FSE unsafe API *******************************************/ static unsigned char FSE_decodeSymbolFast(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD); /* faster, but works only if nbBits is always >= 1 (otherwise, result will be corrupted) */ /* ***************************************** * Implementation of inlined functions *******************************************/ typedef struct { int deltaFindState; U32 deltaNbBits; } FSE_symbolCompressionTransform; /* total 8 bytes */ MEM_STATIC void FSE_initCState(FSE_CState_t* statePtr, const FSE_CTable* ct) { const void* ptr = ct; const U16* u16ptr = (const U16*) ptr; const U32 tableLog = MEM_read16(ptr); statePtr->value = (ptrdiff_t)1<stateTable = u16ptr+2; statePtr->symbolTT = ct + 1 + (tableLog ? (1<<(tableLog-1)) : 1); statePtr->stateLog = tableLog; } /*! FSE_initCState2() : * Same as FSE_initCState(), but the first symbol to include (which will be the last to be read) * uses the smallest state value possible, saving the cost of this symbol */ MEM_STATIC void FSE_initCState2(FSE_CState_t* statePtr, const FSE_CTable* ct, U32 symbol) { FSE_initCState(statePtr, ct); { const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform*)(statePtr->symbolTT))[symbol]; const U16* stateTable = (const U16*)(statePtr->stateTable); U32 nbBitsOut = (U32)((symbolTT.deltaNbBits + (1<<15)) >> 16); statePtr->value = (nbBitsOut << 16) - symbolTT.deltaNbBits; statePtr->value = stateTable[(statePtr->value >> nbBitsOut) + symbolTT.deltaFindState]; } } MEM_STATIC void FSE_encodeSymbol(BIT_CStream_t* bitC, FSE_CState_t* statePtr, unsigned symbol) { FSE_symbolCompressionTransform const symbolTT = ((const FSE_symbolCompressionTransform*)(statePtr->symbolTT))[symbol]; const U16* const stateTable = (const U16*)(statePtr->stateTable); U32 const nbBitsOut = (U32)((statePtr->value + symbolTT.deltaNbBits) >> 16); BIT_addBits(bitC, statePtr->value, nbBitsOut); statePtr->value = stateTable[ (statePtr->value >> nbBitsOut) + symbolTT.deltaFindState]; } MEM_STATIC void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* statePtr) { BIT_addBits(bitC, statePtr->value, statePtr->stateLog); BIT_flushBits(bitC); } /* FSE_getMaxNbBits() : * Approximate maximum cost of a symbol, in bits. * Fractional get rounded up (i.e : a symbol with a normalized frequency of 3 gives the same result as a frequency of 2) * note 1 : assume symbolValue is valid (<= maxSymbolValue) * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */ MEM_STATIC U32 FSE_getMaxNbBits(const void* symbolTTPtr, U32 symbolValue) { const FSE_symbolCompressionTransform* symbolTT = (const FSE_symbolCompressionTransform*) symbolTTPtr; return (symbolTT[symbolValue].deltaNbBits + ((1<<16)-1)) >> 16; } /* FSE_bitCost() : * Approximate symbol cost, as fractional value, using fixed-point format (accuracyLog fractional bits) * note 1 : assume symbolValue is valid (<= maxSymbolValue) * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */ MEM_STATIC U32 FSE_bitCost(const void* symbolTTPtr, U32 tableLog, U32 symbolValue, U32 accuracyLog) { const FSE_symbolCompressionTransform* symbolTT = (const FSE_symbolCompressionTransform*) symbolTTPtr; U32 const minNbBits = symbolTT[symbolValue].deltaNbBits >> 16; U32 const threshold = (minNbBits+1) << 16; assert(tableLog < 16); assert(accuracyLog < 31-tableLog); /* ensure enough room for renormalization double shift */ { U32 const tableSize = 1 << tableLog; U32 const deltaFromThreshold = threshold - (symbolTT[symbolValue].deltaNbBits + tableSize); U32 const normalizedDeltaFromThreshold = (deltaFromThreshold << accuracyLog) >> tableLog; /* linear interpolation (very approximate) */ U32 const bitMultiplier = 1 << accuracyLog; assert(symbolTT[symbolValue].deltaNbBits + tableSize <= threshold); assert(normalizedDeltaFromThreshold <= bitMultiplier); return (minNbBits+1)*bitMultiplier - normalizedDeltaFromThreshold; } } /* ====== Decompression ====== */ typedef struct { U16 tableLog; U16 fastMode; } FSE_DTableHeader; /* sizeof U32 */ typedef struct { unsigned short newState; unsigned char symbol; unsigned char nbBits; } FSE_decode_t; /* size == U32 */ MEM_STATIC void FSE_initDState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD, const FSE_DTable* dt) { const void* ptr = dt; const FSE_DTableHeader* const DTableH = (const FSE_DTableHeader*)ptr; DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog); BIT_reloadDStream(bitD); DStatePtr->table = dt + 1; } MEM_STATIC BYTE FSE_peekSymbol(const FSE_DState_t* DStatePtr) { FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state]; return DInfo.symbol; } MEM_STATIC void FSE_updateState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD) { FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state]; U32 const nbBits = DInfo.nbBits; size_t const lowBits = BIT_readBits(bitD, nbBits); DStatePtr->state = DInfo.newState + lowBits; } MEM_STATIC BYTE FSE_decodeSymbol(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD) { FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state]; U32 const nbBits = DInfo.nbBits; BYTE const symbol = DInfo.symbol; size_t const lowBits = BIT_readBits(bitD, nbBits); DStatePtr->state = DInfo.newState + lowBits; return symbol; } /*! FSE_decodeSymbolFast() : unsafe, only works if no symbol has a probability > 50% */ MEM_STATIC BYTE FSE_decodeSymbolFast(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD) { FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state]; U32 const nbBits = DInfo.nbBits; BYTE const symbol = DInfo.symbol; size_t const lowBits = BIT_readBitsFast(bitD, nbBits); DStatePtr->state = DInfo.newState + lowBits; return symbol; } MEM_STATIC unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr) { return DStatePtr->state == 0; } #ifndef FSE_COMMONDEFS_ONLY /* ************************************************************** * Tuning parameters ****************************************************************/ /*!MEMORY_USAGE : * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) * Increasing memory usage improves compression ratio * Reduced memory usage can improve speed, due to cache effect * Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ #ifndef FSE_MAX_MEMORY_USAGE # define FSE_MAX_MEMORY_USAGE 14 #endif #ifndef FSE_DEFAULT_MEMORY_USAGE # define FSE_DEFAULT_MEMORY_USAGE 13 #endif /*!FSE_MAX_SYMBOL_VALUE : * Maximum symbol value authorized. * Required for proper stack allocation */ #ifndef FSE_MAX_SYMBOL_VALUE # define FSE_MAX_SYMBOL_VALUE 255 #endif /* ************************************************************** * template functions type & suffix ****************************************************************/ #define FSE_FUNCTION_TYPE BYTE #define FSE_FUNCTION_EXTENSION #define FSE_DECODE_TYPE FSE_decode_t #endif /* !FSE_COMMONDEFS_ONLY */ /* *************************************************************** * Constants *****************************************************************/ #define FSE_MAX_TABLELOG (FSE_MAX_MEMORY_USAGE-2) #define FSE_MAX_TABLESIZE (1U< FSE_TABLELOG_ABSOLUTE_MAX # error "FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX is not supported" #endif #define FSE_TABLESTEP(tableSize) ((tableSize>>1) + (tableSize>>3) + 3) #endif /* FSE_STATIC_LINKING_ONLY */ #if defined (__cplusplus) } #endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/fse_decompress.c0000644000076500000240000002343114601576577024323 0ustar00twstaff/* ****************************************************************** * FSE : Finite State Entropy decoder * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy * - Public forum : https://groups.google.com/forum/#!forum/lz4c * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. ****************************************************************** */ /* ************************************************************** * Includes ****************************************************************/ #include /* malloc, free, qsort */ #include /* memcpy, memset */ #include "bitstream.h" #include "compiler.h" #define FSE_STATIC_LINKING_ONLY #include "fse.h" #include "error_private.h" /* ************************************************************** * Error Management ****************************************************************/ #define FSE_isError ERR_isError #define FSE_STATIC_ASSERT(c) DEBUG_STATIC_ASSERT(c) /* use only *after* variable declarations */ /* ************************************************************** * Templates ****************************************************************/ /* designed to be included for type-specific functions (template emulation in C) Objective is to write these functions only once, for improved maintenance */ /* safety checks */ #ifndef FSE_FUNCTION_EXTENSION # error "FSE_FUNCTION_EXTENSION must be defined" #endif #ifndef FSE_FUNCTION_TYPE # error "FSE_FUNCTION_TYPE must be defined" #endif /* Function names */ #define FSE_CAT(X,Y) X##Y #define FSE_FUNCTION_NAME(X,Y) FSE_CAT(X,Y) #define FSE_TYPE_NAME(X,Y) FSE_CAT(X,Y) /* Function templates */ FSE_DTable* FSE_createDTable (unsigned tableLog) { if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX; return (FSE_DTable*)malloc( FSE_DTABLE_SIZE_U32(tableLog) * sizeof (U32) ); } void FSE_freeDTable (FSE_DTable* dt) { free(dt); } size_t FSE_buildDTable(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) { void* const tdPtr = dt+1; /* because *dt is unsigned, 32-bits aligned on 32-bits */ FSE_DECODE_TYPE* const tableDecode = (FSE_DECODE_TYPE*) (tdPtr); U16 symbolNext[FSE_MAX_SYMBOL_VALUE+1]; U32 const maxSV1 = maxSymbolValue + 1; U32 const tableSize = 1 << tableLog; U32 highThreshold = tableSize-1; /* Sanity Checks */ if (maxSymbolValue > FSE_MAX_SYMBOL_VALUE) return ERROR(maxSymbolValue_tooLarge); if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); /* Init, lay down lowprob symbols */ { FSE_DTableHeader DTableH; DTableH.tableLog = (U16)tableLog; DTableH.fastMode = 1; { S16 const largeLimit= (S16)(1 << (tableLog-1)); U32 s; for (s=0; s= largeLimit) DTableH.fastMode=0; symbolNext[s] = normalizedCounter[s]; } } } memcpy(dt, &DTableH, sizeof(DTableH)); } /* Spread symbols */ { U32 const tableMask = tableSize-1; U32 const step = FSE_TABLESTEP(tableSize); U32 s, position = 0; for (s=0; s highThreshold) position = (position + step) & tableMask; /* lowprob area */ } } if (position!=0) return ERROR(GENERIC); /* position must reach all cells once, otherwise normalizedCounter is incorrect */ } /* Build Decoding table */ { U32 u; for (u=0; utableLog = 0; DTableH->fastMode = 0; cell->newState = 0; cell->symbol = symbolValue; cell->nbBits = 0; return 0; } size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits) { void* ptr = dt; FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr; void* dPtr = dt + 1; FSE_decode_t* const dinfo = (FSE_decode_t*)dPtr; const unsigned tableSize = 1 << nbBits; const unsigned tableMask = tableSize - 1; const unsigned maxSV1 = tableMask+1; unsigned s; /* Sanity checks */ if (nbBits < 1) return ERROR(GENERIC); /* min size */ /* Build Decoding Table */ DTableH->tableLog = (U16)nbBits; DTableH->fastMode = 1; for (s=0; s sizeof(bitD.bitContainer)*8) /* This test must be static */ BIT_reloadDStream(&bitD); op[1] = FSE_GETSYMBOL(&state2); if (FSE_MAX_TABLELOG*4+7 > sizeof(bitD.bitContainer)*8) /* This test must be static */ { if (BIT_reloadDStream(&bitD) > BIT_DStream_unfinished) { op+=2; break; } } op[2] = FSE_GETSYMBOL(&state1); if (FSE_MAX_TABLELOG*2+7 > sizeof(bitD.bitContainer)*8) /* This test must be static */ BIT_reloadDStream(&bitD); op[3] = FSE_GETSYMBOL(&state2); } /* tail */ /* note : BIT_reloadDStream(&bitD) >= FSE_DStream_partiallyFilled; Ends at exactly BIT_DStream_completed */ while (1) { if (op>(omax-2)) return ERROR(dstSize_tooSmall); *op++ = FSE_GETSYMBOL(&state1); if (BIT_reloadDStream(&bitD)==BIT_DStream_overflow) { *op++ = FSE_GETSYMBOL(&state2); break; } if (op>(omax-2)) return ERROR(dstSize_tooSmall); *op++ = FSE_GETSYMBOL(&state2); if (BIT_reloadDStream(&bitD)==BIT_DStream_overflow) { *op++ = FSE_GETSYMBOL(&state1); break; } } return op-ostart; } size_t FSE_decompress_usingDTable(void* dst, size_t originalSize, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt) { const void* ptr = dt; const FSE_DTableHeader* DTableH = (const FSE_DTableHeader*)ptr; const U32 fastMode = DTableH->fastMode; /* select fast mode (static) */ if (fastMode) return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 1); return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 0); } size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, FSE_DTable* workSpace, unsigned maxLog) { const BYTE* const istart = (const BYTE*)cSrc; const BYTE* ip = istart; short counting[FSE_MAX_SYMBOL_VALUE+1]; unsigned tableLog; unsigned maxSymbolValue = FSE_MAX_SYMBOL_VALUE; /* normal FSE decoding mode */ size_t const NCountLength = FSE_readNCount (counting, &maxSymbolValue, &tableLog, istart, cSrcSize); if (FSE_isError(NCountLength)) return NCountLength; /* if (NCountLength >= cSrcSize) return ERROR(srcSize_wrong); */ /* too small input size; supposed to be already checked in NCountLength, only remaining case : NCountLength==cSrcSize */ if (tableLog > maxLog) return ERROR(tableLog_tooLarge); ip += NCountLength; cSrcSize -= NCountLength; CHECK_F( FSE_buildDTable (workSpace, counting, maxSymbolValue, tableLog) ); return FSE_decompress_usingDTable (dst, dstCapacity, ip, cSrcSize, workSpace); /* always return, even if it is an error code */ } typedef FSE_DTable DTable_max_t[FSE_DTABLE_SIZE_U32(FSE_MAX_TABLELOG)]; size_t FSE_decompress(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize) { DTable_max_t dt; /* Static analyzer seems unable to understand this table will be properly initialized later */ return FSE_decompress_wksp(dst, dstCapacity, cSrc, cSrcSize, dt, FSE_MAX_TABLELOG); } #endif /* FSE_COMMONDEFS_ONLY */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/huf.h0000644000076500000240000004526214601576577022117 0ustar00twstaff/* ****************************************************************** * huff0 huffman codec, * part of Finite State Entropy library * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. ****************************************************************** */ #if defined (__cplusplus) extern "C" { #endif #ifndef HUF_H_298734234 #define HUF_H_298734234 /* *** Dependencies *** */ #include /* size_t */ /* *** library symbols visibility *** */ /* Note : when linking with -fvisibility=hidden on gcc, or by default on Visual, * HUF symbols remain "private" (internal symbols for library only). * Set macro FSE_DLL_EXPORT to 1 if you want HUF symbols visible on DLL interface */ #if defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) && defined(__GNUC__) && (__GNUC__ >= 4) # define HUF_PUBLIC_API __attribute__ ((visibility ("default"))) #elif defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) /* Visual expected */ # define HUF_PUBLIC_API __declspec(dllexport) #elif defined(FSE_DLL_IMPORT) && (FSE_DLL_IMPORT==1) # define HUF_PUBLIC_API __declspec(dllimport) /* not required, just to generate faster code (saves a function pointer load from IAT and an indirect jump) */ #else # define HUF_PUBLIC_API #endif /* ========================== */ /* *** simple functions *** */ /* ========================== */ /** HUF_compress() : * Compress content from buffer 'src', of size 'srcSize', into buffer 'dst'. * 'dst' buffer must be already allocated. * Compression runs faster if `dstCapacity` >= HUF_compressBound(srcSize). * `srcSize` must be <= `HUF_BLOCKSIZE_MAX` == 128 KB. * @return : size of compressed data (<= `dstCapacity`). * Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!! * if HUF_isError(return), compression failed (more details using HUF_getErrorName()) */ HUF_PUBLIC_API size_t HUF_compress(void* dst, size_t dstCapacity, const void* src, size_t srcSize); /** HUF_decompress() : * Decompress HUF data from buffer 'cSrc', of size 'cSrcSize', * into already allocated buffer 'dst', of minimum size 'dstSize'. * `originalSize` : **must** be the ***exact*** size of original (uncompressed) data. * Note : in contrast with FSE, HUF_decompress can regenerate * RLE (cSrcSize==1) and uncompressed (cSrcSize==dstSize) data, * because it knows size to regenerate (originalSize). * @return : size of regenerated data (== originalSize), * or an error code, which can be tested using HUF_isError() */ HUF_PUBLIC_API size_t HUF_decompress(void* dst, size_t originalSize, const void* cSrc, size_t cSrcSize); /* *** Tool functions *** */ #define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */ HUF_PUBLIC_API size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */ /* Error Management */ HUF_PUBLIC_API unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */ HUF_PUBLIC_API const char* HUF_getErrorName(size_t code); /**< provides error code string (useful for debugging) */ /* *** Advanced function *** */ /** HUF_compress2() : * Same as HUF_compress(), but offers control over `maxSymbolValue` and `tableLog`. * `maxSymbolValue` must be <= HUF_SYMBOLVALUE_MAX . * `tableLog` must be `<= HUF_TABLELOG_MAX` . */ HUF_PUBLIC_API size_t HUF_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); /** HUF_compress4X_wksp() : * Same as HUF_compress2(), but uses externally allocated `workSpace`. * `workspace` must have minimum alignment of 4, and be at least as large as HUF_WORKSPACE_SIZE */ #define HUF_WORKSPACE_SIZE ((6 << 10) + 256) #define HUF_WORKSPACE_SIZE_U32 (HUF_WORKSPACE_SIZE / sizeof(U32)) HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); #endif /* HUF_H_298734234 */ /* ****************************************************************** * WARNING !! * The following section contains advanced and experimental definitions * which shall never be used in the context of a dynamic library, * because they are not guaranteed to remain stable in the future. * Only consider them in association with static linking. * *****************************************************************/ #if defined(HUF_STATIC_LINKING_ONLY) && !defined(HUF_H_HUF_STATIC_LINKING_ONLY) #define HUF_H_HUF_STATIC_LINKING_ONLY /* *** Dependencies *** */ #include "mem.h" /* U32 */ /* *** Constants *** */ #define HUF_TABLELOG_MAX 12 /* max runtime value of tableLog (due to static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */ #define HUF_TABLELOG_DEFAULT 11 /* default tableLog value when none specified */ #define HUF_SYMBOLVALUE_MAX 255 #define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ #if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX) # error "HUF_TABLELOG_MAX is too large !" #endif /* **************************************** * Static allocation ******************************************/ /* HUF buffer bounds */ #define HUF_CTABLEBOUND 129 #define HUF_BLOCKBOUND(size) (size + (size>>8) + 8) /* only true when incompressible is pre-filtered with fast heuristic */ #define HUF_COMPRESSBOUND(size) (HUF_CTABLEBOUND + HUF_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ /* static allocation of HUF's Compression Table */ #define HUF_CTABLE_SIZE_U32(maxSymbolValue) ((maxSymbolValue)+1) /* Use tables of U32, for proper alignment */ #define HUF_CTABLE_SIZE(maxSymbolValue) (HUF_CTABLE_SIZE_U32(maxSymbolValue) * sizeof(U32)) #define HUF_CREATE_STATIC_CTABLE(name, maxSymbolValue) \ U32 name##hb[HUF_CTABLE_SIZE_U32(maxSymbolValue)]; \ void* name##hv = &(name##hb); \ HUF_CElt* name = (HUF_CElt*)(name##hv) /* no final ; */ /* static allocation of HUF's DTable */ typedef U32 HUF_DTable; #define HUF_DTABLE_SIZE(maxTableLog) (1 + (1<<(maxTableLog))) #define HUF_CREATE_STATIC_DTABLEX1(DTable, maxTableLog) \ HUF_DTable DTable[HUF_DTABLE_SIZE((maxTableLog)-1)] = { ((U32)((maxTableLog)-1) * 0x01000001) } #define HUF_CREATE_STATIC_DTABLEX2(DTable, maxTableLog) \ HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = { ((U32)(maxTableLog) * 0x01000001) } /* **************************************** * Advanced decompression functions ******************************************/ size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ #ifndef HUF_FORCE_DECOMPRESS_X1 size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ #endif size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< decodes RLE and uncompressed */ size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< considers RLE and uncompressed as errors */ size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< considers RLE and uncompressed as errors */ size_t HUF_decompress4X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */ #ifndef HUF_FORCE_DECOMPRESS_X1 size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */ #endif /* **************************************** * HUF detailed API * ****************************************/ /*! HUF_compress() does the following: * 1. count symbol occurrence from source[] into table count[] using FSE_count() (exposed within "fse.h") * 2. (optional) refine tableLog using HUF_optimalTableLog() * 3. build Huffman table from count using HUF_buildCTable() * 4. save Huffman table to memory buffer using HUF_writeCTable() * 5. encode the data stream using HUF_compress4X_usingCTable() * * The following API allows targeting specific sub-functions for advanced tasks. * For example, it's possible to compress several blocks using the same 'CTable', * or to save and regenerate 'CTable' using external methods. */ unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); typedef struct HUF_CElt_s HUF_CElt; /* incomplete type */ size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits); /* @return : maxNbBits; CTable and count can overlap. In which case, CTable will overwrite count content */ size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog); size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); size_t HUF_estimateCompressedSize(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue); int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue); typedef enum { HUF_repeat_none, /**< Cannot use the previous table */ HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, 4}X_repeat */ HUF_repeat_valid /**< Can use the previous table and it is assumed to be valid */ } HUF_repeat; /** HUF_compress4X_repeat() : * Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. * If it uses hufTable it does not modify hufTable or repeat. * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. * If preferRepeat then the old table will always be used if valid. */ size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */ HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2); /** HUF_buildCTable_wksp() : * Same as HUF_buildCTable(), but using externally allocated scratch buffer. * `workSpace` must be aligned on 4-bytes boundaries, and its size must be >= HUF_CTABLE_WORKSPACE_SIZE. */ #define HUF_CTABLE_WORKSPACE_SIZE_U32 (2*HUF_SYMBOLVALUE_MAX +1 +1) #define HUF_CTABLE_WORKSPACE_SIZE (HUF_CTABLE_WORKSPACE_SIZE_U32 * sizeof(unsigned)) size_t HUF_buildCTable_wksp (HUF_CElt* tree, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize); /*! HUF_readStats() : * Read compact Huffman tree, saved by HUF_writeCTable(). * `huffWeight` is destination buffer. * @return : size read from `src` , or an error Code . * Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */ size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr, const void* src, size_t srcSize); /** HUF_readCTable() : * Loading a CTable saved with HUF_writeCTable() */ size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned *hasZeroWeights); /** HUF_getNbBits() : * Read nbBits from CTable symbolTable, for symbol `symbolValue` presumed <= HUF_SYMBOLVALUE_MAX * Note 1 : is not inlined, as HUF_CElt definition is private * Note 2 : const void* used, so that it can provide a statically allocated table as argument (which uses type U32) */ U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue); /* * HUF_decompress() does the following: * 1. select the decompression algorithm (X1, X2) based on pre-computed heuristics * 2. build Huffman table from save, using HUF_readDTableX?() * 3. decode 1 or 4 segments in parallel using HUF_decompress?X?_usingDTable() */ /** HUF_selectDecoder() : * Tells which decoder is likely to decode faster, * based on a set of pre-computed metrics. * @return : 0==HUF_decompress4X1, 1==HUF_decompress4X2 . * Assumption : 0 < dstSize <= 128 KB */ U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize); /** * The minimum workspace size for the `workSpace` used in * HUF_readDTableX1_wksp() and HUF_readDTableX2_wksp(). * * The space used depends on HUF_TABLELOG_MAX, ranging from ~1500 bytes when * HUF_TABLE_LOG_MAX=12 to ~1850 bytes when HUF_TABLE_LOG_MAX=15. * Buffer overflow errors may potentially occur if code modifications result in * a required workspace size greater than that specified in the following * macro. */ #define HUF_DECOMPRESS_WORKSPACE_SIZE (2 << 10) #define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32)) #ifndef HUF_FORCE_DECOMPRESS_X2 size_t HUF_readDTableX1 (HUF_DTable* DTable, const void* src, size_t srcSize); size_t HUF_readDTableX1_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize); #endif #ifndef HUF_FORCE_DECOMPRESS_X1 size_t HUF_readDTableX2 (HUF_DTable* DTable, const void* src, size_t srcSize); size_t HUF_readDTableX2_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize); #endif size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); #ifndef HUF_FORCE_DECOMPRESS_X2 size_t HUF_decompress4X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); #endif #ifndef HUF_FORCE_DECOMPRESS_X1 size_t HUF_decompress4X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); #endif /* ====================== */ /* single stream variants */ /* ====================== */ size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); /** HUF_compress1X_repeat() : * Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. * If it uses hufTable it does not modify hufTable or repeat. * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. * If preferRepeat then the old table will always be used if valid. */ size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */ HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2); size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */ #ifndef HUF_FORCE_DECOMPRESS_X1 size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */ #endif size_t HUF_decompress1X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); size_t HUF_decompress1X_DCtx_wksp (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); #ifndef HUF_FORCE_DECOMPRESS_X2 size_t HUF_decompress1X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */ #endif #ifndef HUF_FORCE_DECOMPRESS_X1 size_t HUF_decompress1X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */ #endif size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); /**< automatic selection of sing or double symbol decoder, based on DTable */ #ifndef HUF_FORCE_DECOMPRESS_X2 size_t HUF_decompress1X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); #endif #ifndef HUF_FORCE_DECOMPRESS_X1 size_t HUF_decompress1X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); #endif /* BMI2 variants. * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0. */ size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2); #ifndef HUF_FORCE_DECOMPRESS_X2 size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2); #endif size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2); size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2); #endif /* HUF_STATIC_LINKING_ONLY */ #if defined (__cplusplus) } #endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/mem.h0000644000076500000240000003512214601576577022105 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef MEM_H_MODULE #define MEM_H_MODULE #if defined (__cplusplus) extern "C" { #endif /*-**************************************** * Dependencies ******************************************/ #include /* size_t, ptrdiff_t */ #include /* memcpy */ /*-**************************************** * Compiler specifics ******************************************/ #if defined(_MSC_VER) /* Visual Studio */ # include /* _byteswap_ulong */ # include /* _byteswap_* */ #endif #if defined(__GNUC__) # define MEM_STATIC static __inline __attribute__((unused)) #elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # define MEM_STATIC static inline #elif defined(_MSC_VER) # define MEM_STATIC static __inline #else # define MEM_STATIC static /* this version may generate warnings for unused static functions; disable the relevant warning */ #endif #ifndef __has_builtin # define __has_builtin(x) 0 /* compat. with non-clang compilers */ #endif /* code only tested on 32 and 64 bits systems */ #define MEM_STATIC_ASSERT(c) { enum { MEM_static_assert = 1/(int)(!!(c)) }; } MEM_STATIC void MEM_check(void) { MEM_STATIC_ASSERT((sizeof(size_t)==4) || (sizeof(size_t)==8)); } /* detects whether we are being compiled under msan */ #if defined (__has_feature) # if __has_feature(memory_sanitizer) # define MEMORY_SANITIZER 1 # endif #endif #if defined (MEMORY_SANITIZER) /* Not all platforms that support msan provide sanitizers/msan_interface.h. * We therefore declare the functions we need ourselves, rather than trying to * include the header file... */ #include /* intptr_t */ /* Make memory region fully initialized (without changing its contents). */ void __msan_unpoison(const volatile void *a, size_t size); /* Make memory region fully uninitialized (without changing its contents). This is a legacy interface that does not update origin information. Use __msan_allocated_memory() instead. */ void __msan_poison(const volatile void *a, size_t size); /* Returns the offset of the first (at least partially) poisoned byte in the memory range, or -1 if the whole range is good. */ intptr_t __msan_test_shadow(const volatile void *x, size_t size); #endif /* detects whether we are being compiled under asan */ #if defined (__has_feature) # if __has_feature(address_sanitizer) # define ADDRESS_SANITIZER 1 # endif #elif defined(__SANITIZE_ADDRESS__) # define ADDRESS_SANITIZER 1 #endif #if defined (ADDRESS_SANITIZER) /* Not all platforms that support asan provide sanitizers/asan_interface.h. * We therefore declare the functions we need ourselves, rather than trying to * include the header file... */ /** * Marks a memory region ([addr, addr+size)) as unaddressable. * * This memory must be previously allocated by your program. Instrumented * code is forbidden from accessing addresses in this region until it is * unpoisoned. This function is not guaranteed to poison the entire region - * it could poison only a subregion of [addr, addr+size) due to ASan * alignment restrictions. * * \note This function is not thread-safe because no two threads can poison or * unpoison memory in the same memory region simultaneously. * * \param addr Start of memory region. * \param size Size of memory region. */ void __asan_poison_memory_region(void const volatile *addr, size_t size); /** * Marks a memory region ([addr, addr+size)) as addressable. * * This memory must be previously allocated by your program. Accessing * addresses in this region is allowed until this region is poisoned again. * This function could unpoison a super-region of [addr, addr+size) due * to ASan alignment restrictions. * * \note This function is not thread-safe because no two threads can * poison or unpoison memory in the same memory region simultaneously. * * \param addr Start of memory region. * \param size Size of memory region. */ void __asan_unpoison_memory_region(void const volatile *addr, size_t size); #endif /*-************************************************************** * Basic Types *****************************************************************/ #if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include typedef uint8_t BYTE; typedef uint16_t U16; typedef int16_t S16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; typedef int64_t S64; #else # include #if CHAR_BIT != 8 # error "this implementation requires char to be exactly 8-bit type" #endif typedef unsigned char BYTE; #if USHRT_MAX != 65535 # error "this implementation requires short to be exactly 16-bit type" #endif typedef unsigned short U16; typedef signed short S16; #if UINT_MAX != 4294967295 # error "this implementation requires int to be exactly 32-bit type" #endif typedef unsigned int U32; typedef signed int S32; /* note : there are no limits defined for long long type in C90. * limits exist in C99, however, in such case, is preferred */ typedef unsigned long long U64; typedef signed long long S64; #endif /*-************************************************************** * Memory I/O *****************************************************************/ /* MEM_FORCE_MEMORY_ACCESS : * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. * The below switch allow to select different access method for improved performance. * Method 0 (default) : use `memcpy()`. Safe and portable. * Method 1 : `__packed` statement. It depends on compiler extension (i.e., not portable). * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. * Method 2 : direct access. This method is portable but violate C standard. * It can generate buggy code on targets depending on alignment. * In some circumstances, it's the only known way to get the most performance (i.e. GCC + ARMv6) * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. * Prefer these methods in priority order (0 > 1 > 2) */ #ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ # if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) # define MEM_FORCE_MEMORY_ACCESS 2 # elif defined(__INTEL_COMPILER) || defined(__GNUC__) || defined(__ICCARM__) # define MEM_FORCE_MEMORY_ACCESS 1 # endif #endif MEM_STATIC unsigned MEM_32bits(void) { return sizeof(size_t)==4; } MEM_STATIC unsigned MEM_64bits(void) { return sizeof(size_t)==8; } MEM_STATIC unsigned MEM_isLittleEndian(void) { const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ return one.c[0]; } #if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2) /* violates C standard, by lying on structure alignment. Only use if no other choice to achieve best performance on target platform */ MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; } MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; } MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; } MEM_STATIC size_t MEM_readST(const void* memPtr) { return *(const size_t*) memPtr; } MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } MEM_STATIC void MEM_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } MEM_STATIC void MEM_write64(void* memPtr, U64 value) { *(U64*)memPtr = value; } #elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1) /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ /* currently only defined for gcc and icc */ #if defined(_MSC_VER) || (defined(__INTEL_COMPILER) && defined(WIN32)) __pragma( pack(push, 1) ) typedef struct { U16 v; } unalign16; typedef struct { U32 v; } unalign32; typedef struct { U64 v; } unalign64; typedef struct { size_t v; } unalignArch; __pragma( pack(pop) ) #else typedef struct { U16 v; } __attribute__((packed)) unalign16; typedef struct { U32 v; } __attribute__((packed)) unalign32; typedef struct { U64 v; } __attribute__((packed)) unalign64; typedef struct { size_t v; } __attribute__((packed)) unalignArch; #endif MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign16*)ptr)->v; } MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign32*)ptr)->v; } MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign64*)ptr)->v; } MEM_STATIC size_t MEM_readST(const void* ptr) { return ((const unalignArch*)ptr)->v; } MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign16*)memPtr)->v = value; } MEM_STATIC void MEM_write32(void* memPtr, U32 value) { ((unalign32*)memPtr)->v = value; } MEM_STATIC void MEM_write64(void* memPtr, U64 value) { ((unalign64*)memPtr)->v = value; } #else /* default method, safe and standard. can sometimes prove slower */ MEM_STATIC U16 MEM_read16(const void* memPtr) { U16 val; memcpy(&val, memPtr, sizeof(val)); return val; } MEM_STATIC U32 MEM_read32(const void* memPtr) { U32 val; memcpy(&val, memPtr, sizeof(val)); return val; } MEM_STATIC U64 MEM_read64(const void* memPtr) { U64 val; memcpy(&val, memPtr, sizeof(val)); return val; } MEM_STATIC size_t MEM_readST(const void* memPtr) { size_t val; memcpy(&val, memPtr, sizeof(val)); return val; } MEM_STATIC void MEM_write16(void* memPtr, U16 value) { memcpy(memPtr, &value, sizeof(value)); } MEM_STATIC void MEM_write32(void* memPtr, U32 value) { memcpy(memPtr, &value, sizeof(value)); } MEM_STATIC void MEM_write64(void* memPtr, U64 value) { memcpy(memPtr, &value, sizeof(value)); } #endif /* MEM_FORCE_MEMORY_ACCESS */ MEM_STATIC U32 MEM_swap32(U32 in) { #if defined(_MSC_VER) /* Visual Studio */ return _byteswap_ulong(in); #elif (defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403)) \ || (defined(__clang__) && __has_builtin(__builtin_bswap32)) return __builtin_bswap32(in); #else return ((in << 24) & 0xff000000 ) | ((in << 8) & 0x00ff0000 ) | ((in >> 8) & 0x0000ff00 ) | ((in >> 24) & 0x000000ff ); #endif } MEM_STATIC U64 MEM_swap64(U64 in) { #if defined(_MSC_VER) /* Visual Studio */ return _byteswap_uint64(in); #elif (defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403)) \ || (defined(__clang__) && __has_builtin(__builtin_bswap64)) return __builtin_bswap64(in); #else return ((in << 56) & 0xff00000000000000ULL) | ((in << 40) & 0x00ff000000000000ULL) | ((in << 24) & 0x0000ff0000000000ULL) | ((in << 8) & 0x000000ff00000000ULL) | ((in >> 8) & 0x00000000ff000000ULL) | ((in >> 24) & 0x0000000000ff0000ULL) | ((in >> 40) & 0x000000000000ff00ULL) | ((in >> 56) & 0x00000000000000ffULL); #endif } MEM_STATIC size_t MEM_swapST(size_t in) { if (MEM_32bits()) return (size_t)MEM_swap32((U32)in); else return (size_t)MEM_swap64((U64)in); } /*=== Little endian r/w ===*/ MEM_STATIC U16 MEM_readLE16(const void* memPtr) { if (MEM_isLittleEndian()) return MEM_read16(memPtr); else { const BYTE* p = (const BYTE*)memPtr; return (U16)(p[0] + (p[1]<<8)); } } MEM_STATIC void MEM_writeLE16(void* memPtr, U16 val) { if (MEM_isLittleEndian()) { MEM_write16(memPtr, val); } else { BYTE* p = (BYTE*)memPtr; p[0] = (BYTE)val; p[1] = (BYTE)(val>>8); } } MEM_STATIC U32 MEM_readLE24(const void* memPtr) { return MEM_readLE16(memPtr) + (((const BYTE*)memPtr)[2] << 16); } MEM_STATIC void MEM_writeLE24(void* memPtr, U32 val) { MEM_writeLE16(memPtr, (U16)val); ((BYTE*)memPtr)[2] = (BYTE)(val>>16); } MEM_STATIC U32 MEM_readLE32(const void* memPtr) { if (MEM_isLittleEndian()) return MEM_read32(memPtr); else return MEM_swap32(MEM_read32(memPtr)); } MEM_STATIC void MEM_writeLE32(void* memPtr, U32 val32) { if (MEM_isLittleEndian()) MEM_write32(memPtr, val32); else MEM_write32(memPtr, MEM_swap32(val32)); } MEM_STATIC U64 MEM_readLE64(const void* memPtr) { if (MEM_isLittleEndian()) return MEM_read64(memPtr); else return MEM_swap64(MEM_read64(memPtr)); } MEM_STATIC void MEM_writeLE64(void* memPtr, U64 val64) { if (MEM_isLittleEndian()) MEM_write64(memPtr, val64); else MEM_write64(memPtr, MEM_swap64(val64)); } MEM_STATIC size_t MEM_readLEST(const void* memPtr) { if (MEM_32bits()) return (size_t)MEM_readLE32(memPtr); else return (size_t)MEM_readLE64(memPtr); } MEM_STATIC void MEM_writeLEST(void* memPtr, size_t val) { if (MEM_32bits()) MEM_writeLE32(memPtr, (U32)val); else MEM_writeLE64(memPtr, (U64)val); } /*=== Big endian r/w ===*/ MEM_STATIC U32 MEM_readBE32(const void* memPtr) { if (MEM_isLittleEndian()) return MEM_swap32(MEM_read32(memPtr)); else return MEM_read32(memPtr); } MEM_STATIC void MEM_writeBE32(void* memPtr, U32 val32) { if (MEM_isLittleEndian()) MEM_write32(memPtr, MEM_swap32(val32)); else MEM_write32(memPtr, val32); } MEM_STATIC U64 MEM_readBE64(const void* memPtr) { if (MEM_isLittleEndian()) return MEM_swap64(MEM_read64(memPtr)); else return MEM_read64(memPtr); } MEM_STATIC void MEM_writeBE64(void* memPtr, U64 val64) { if (MEM_isLittleEndian()) MEM_write64(memPtr, MEM_swap64(val64)); else MEM_write64(memPtr, val64); } MEM_STATIC size_t MEM_readBEST(const void* memPtr) { if (MEM_32bits()) return (size_t)MEM_readBE32(memPtr); else return (size_t)MEM_readBE64(memPtr); } MEM_STATIC void MEM_writeBEST(void* memPtr, size_t val) { if (MEM_32bits()) MEM_writeBE32(memPtr, (U32)val); else MEM_writeBE64(memPtr, (U64)val); } #if defined (__cplusplus) } #endif #endif /* MEM_H_MODULE */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/pool.c0000644000076500000240000002546014601576577022277 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* ====== Dependencies ======= */ #include /* size_t */ #include "debug.h" /* assert */ #include "zstd_internal.h" /* ZSTD_malloc, ZSTD_free */ #include "pool.h" /* ====== Compiler specifics ====== */ #if defined(_MSC_VER) # pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */ #endif #ifdef ZSTD_MULTITHREAD #include "threading.h" /* pthread adaptation */ /* A job is a function and an opaque argument */ typedef struct POOL_job_s { POOL_function function; void *opaque; } POOL_job; struct POOL_ctx_s { ZSTD_customMem customMem; /* Keep track of the threads */ ZSTD_pthread_t* threads; size_t threadCapacity; size_t threadLimit; /* The queue is a circular buffer */ POOL_job *queue; size_t queueHead; size_t queueTail; size_t queueSize; /* The number of threads working on jobs */ size_t numThreadsBusy; /* Indicates if the queue is empty */ int queueEmpty; /* The mutex protects the queue */ ZSTD_pthread_mutex_t queueMutex; /* Condition variable for pushers to wait on when the queue is full */ ZSTD_pthread_cond_t queuePushCond; /* Condition variables for poppers to wait on when the queue is empty */ ZSTD_pthread_cond_t queuePopCond; /* Indicates if the queue is shutting down */ int shutdown; }; /* POOL_thread() : * Work thread for the thread pool. * Waits for jobs and executes them. * @returns : NULL on failure else non-null. */ static void* POOL_thread(void* opaque) { POOL_ctx* const ctx = (POOL_ctx*)opaque; if (!ctx) { return NULL; } for (;;) { /* Lock the mutex and wait for a non-empty queue or until shutdown */ ZSTD_pthread_mutex_lock(&ctx->queueMutex); while ( ctx->queueEmpty || (ctx->numThreadsBusy >= ctx->threadLimit) ) { if (ctx->shutdown) { /* even if !queueEmpty, (possible if numThreadsBusy >= threadLimit), * a few threads will be shutdown while !queueEmpty, * but enough threads will remain active to finish the queue */ ZSTD_pthread_mutex_unlock(&ctx->queueMutex); return opaque; } ZSTD_pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex); } /* Pop a job off the queue */ { POOL_job const job = ctx->queue[ctx->queueHead]; ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize; ctx->numThreadsBusy++; ctx->queueEmpty = ctx->queueHead == ctx->queueTail; /* Unlock the mutex, signal a pusher, and run the job */ ZSTD_pthread_cond_signal(&ctx->queuePushCond); ZSTD_pthread_mutex_unlock(&ctx->queueMutex); job.function(job.opaque); /* If the intended queue size was 0, signal after finishing job */ ZSTD_pthread_mutex_lock(&ctx->queueMutex); ctx->numThreadsBusy--; if (ctx->queueSize == 1) { ZSTD_pthread_cond_signal(&ctx->queuePushCond); } ZSTD_pthread_mutex_unlock(&ctx->queueMutex); } } /* for (;;) */ assert(0); /* Unreachable */ } POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) { return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem); } POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem) { POOL_ctx* ctx; /* Check parameters */ if (!numThreads) { return NULL; } /* Allocate the context and zero initialize */ ctx = (POOL_ctx*)ZSTD_calloc(sizeof(POOL_ctx), customMem); if (!ctx) { return NULL; } /* Initialize the job queue. * It needs one extra space since one space is wasted to differentiate * empty and full queues. */ ctx->queueSize = queueSize + 1; ctx->queue = (POOL_job*)ZSTD_malloc(ctx->queueSize * sizeof(POOL_job), customMem); ctx->queueHead = 0; ctx->queueTail = 0; ctx->numThreadsBusy = 0; ctx->queueEmpty = 1; { int error = 0; error |= ZSTD_pthread_mutex_init(&ctx->queueMutex, NULL); error |= ZSTD_pthread_cond_init(&ctx->queuePushCond, NULL); error |= ZSTD_pthread_cond_init(&ctx->queuePopCond, NULL); if (error) { POOL_free(ctx); return NULL; } } ctx->shutdown = 0; /* Allocate space for the thread handles */ ctx->threads = (ZSTD_pthread_t*)ZSTD_malloc(numThreads * sizeof(ZSTD_pthread_t), customMem); ctx->threadCapacity = 0; ctx->customMem = customMem; /* Check for errors */ if (!ctx->threads || !ctx->queue) { POOL_free(ctx); return NULL; } /* Initialize the threads */ { size_t i; for (i = 0; i < numThreads; ++i) { if (ZSTD_pthread_create(&ctx->threads[i], NULL, &POOL_thread, ctx)) { ctx->threadCapacity = i; POOL_free(ctx); return NULL; } } ctx->threadCapacity = numThreads; ctx->threadLimit = numThreads; } return ctx; } /*! POOL_join() : Shutdown the queue, wake any sleeping threads, and join all of the threads. */ static void POOL_join(POOL_ctx* ctx) { /* Shut down the queue */ ZSTD_pthread_mutex_lock(&ctx->queueMutex); ctx->shutdown = 1; ZSTD_pthread_mutex_unlock(&ctx->queueMutex); /* Wake up sleeping threads */ ZSTD_pthread_cond_broadcast(&ctx->queuePushCond); ZSTD_pthread_cond_broadcast(&ctx->queuePopCond); /* Join all of the threads */ { size_t i; for (i = 0; i < ctx->threadCapacity; ++i) { ZSTD_pthread_join(ctx->threads[i], NULL); /* note : could fail */ } } } void POOL_free(POOL_ctx *ctx) { if (!ctx) { return; } POOL_join(ctx); ZSTD_pthread_mutex_destroy(&ctx->queueMutex); ZSTD_pthread_cond_destroy(&ctx->queuePushCond); ZSTD_pthread_cond_destroy(&ctx->queuePopCond); ZSTD_free(ctx->queue, ctx->customMem); ZSTD_free(ctx->threads, ctx->customMem); ZSTD_free(ctx, ctx->customMem); } size_t POOL_sizeof(POOL_ctx *ctx) { if (ctx==NULL) return 0; /* supports sizeof NULL */ return sizeof(*ctx) + ctx->queueSize * sizeof(POOL_job) + ctx->threadCapacity * sizeof(ZSTD_pthread_t); } /* @return : 0 on success, 1 on error */ static int POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) { if (numThreads <= ctx->threadCapacity) { if (!numThreads) return 1; ctx->threadLimit = numThreads; return 0; } /* numThreads > threadCapacity */ { ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_malloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem); if (!threadPool) return 1; /* replace existing thread pool */ memcpy(threadPool, ctx->threads, ctx->threadCapacity * sizeof(*threadPool)); ZSTD_free(ctx->threads, ctx->customMem); ctx->threads = threadPool; /* Initialize additional threads */ { size_t threadId; for (threadId = ctx->threadCapacity; threadId < numThreads; ++threadId) { if (ZSTD_pthread_create(&threadPool[threadId], NULL, &POOL_thread, ctx)) { ctx->threadCapacity = threadId; return 1; } } } } /* successfully expanded */ ctx->threadCapacity = numThreads; ctx->threadLimit = numThreads; return 0; } /* @return : 0 on success, 1 on error */ int POOL_resize(POOL_ctx* ctx, size_t numThreads) { int result; if (ctx==NULL) return 1; ZSTD_pthread_mutex_lock(&ctx->queueMutex); result = POOL_resize_internal(ctx, numThreads); ZSTD_pthread_cond_broadcast(&ctx->queuePopCond); ZSTD_pthread_mutex_unlock(&ctx->queueMutex); return result; } /** * Returns 1 if the queue is full and 0 otherwise. * * When queueSize is 1 (pool was created with an intended queueSize of 0), * then a queue is empty if there is a thread free _and_ no job is waiting. */ static int isQueueFull(POOL_ctx const* ctx) { if (ctx->queueSize > 1) { return ctx->queueHead == ((ctx->queueTail + 1) % ctx->queueSize); } else { return (ctx->numThreadsBusy == ctx->threadLimit) || !ctx->queueEmpty; } } static void POOL_add_internal(POOL_ctx* ctx, POOL_function function, void *opaque) { POOL_job const job = {function, opaque}; assert(ctx != NULL); if (ctx->shutdown) return; ctx->queueEmpty = 0; ctx->queue[ctx->queueTail] = job; ctx->queueTail = (ctx->queueTail + 1) % ctx->queueSize; ZSTD_pthread_cond_signal(&ctx->queuePopCond); } void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque) { assert(ctx != NULL); ZSTD_pthread_mutex_lock(&ctx->queueMutex); /* Wait until there is space in the queue for the new job */ while (isQueueFull(ctx) && (!ctx->shutdown)) { ZSTD_pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex); } POOL_add_internal(ctx, function, opaque); ZSTD_pthread_mutex_unlock(&ctx->queueMutex); } int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque) { assert(ctx != NULL); ZSTD_pthread_mutex_lock(&ctx->queueMutex); if (isQueueFull(ctx)) { ZSTD_pthread_mutex_unlock(&ctx->queueMutex); return 0; } POOL_add_internal(ctx, function, opaque); ZSTD_pthread_mutex_unlock(&ctx->queueMutex); return 1; } #else /* ZSTD_MULTITHREAD not defined */ /* ========================== */ /* No multi-threading support */ /* ========================== */ /* We don't need any data, but if it is empty, malloc() might return NULL. */ struct POOL_ctx_s { int dummy; }; static POOL_ctx g_ctx; POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) { return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem); } POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem) { (void)numThreads; (void)queueSize; (void)customMem; return &g_ctx; } void POOL_free(POOL_ctx* ctx) { assert(!ctx || ctx == &g_ctx); (void)ctx; } int POOL_resize(POOL_ctx* ctx, size_t numThreads) { (void)ctx; (void)numThreads; return 0; } void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque) { (void)ctx; function(opaque); } int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque) { (void)ctx; function(opaque); return 1; } size_t POOL_sizeof(POOL_ctx* ctx) { if (ctx==NULL) return 0; /* supports sizeof NULL */ assert(ctx == &g_ctx); return sizeof(*ctx); } #endif /* ZSTD_MULTITHREAD */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/pool.h0000644000076500000240000000476014601576577022304 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef POOL_H #define POOL_H #if defined (__cplusplus) extern "C" { #endif #include /* size_t */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_customMem */ #include "../zstd.h" typedef struct POOL_ctx_s POOL_ctx; /*! POOL_create() : * Create a thread pool with at most `numThreads` threads. * `numThreads` must be at least 1. * The maximum number of queued jobs before blocking is `queueSize`. * @return : POOL_ctx pointer on success, else NULL. */ POOL_ctx* POOL_create(size_t numThreads, size_t queueSize); POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem); /*! POOL_free() : * Free a thread pool returned by POOL_create(). */ void POOL_free(POOL_ctx* ctx); /*! POOL_resize() : * Expands or shrinks pool's number of threads. * This is more efficient than releasing + creating a new context, * since it tries to preserve and re-use existing threads. * `numThreads` must be at least 1. * @return : 0 when resize was successful, * !0 (typically 1) if there is an error. * note : only numThreads can be resized, queueSize remains unchanged. */ int POOL_resize(POOL_ctx* ctx, size_t numThreads); /*! POOL_sizeof() : * @return threadpool memory usage * note : compatible with NULL (returns 0 in this case) */ size_t POOL_sizeof(POOL_ctx* ctx); /*! POOL_function : * The function type that can be added to a thread pool. */ typedef void (*POOL_function)(void*); /*! POOL_add() : * Add the job `function(opaque)` to the thread pool. `ctx` must be valid. * Possibly blocks until there is room in the queue. * Note : The function may be executed asynchronously, * therefore, `opaque` must live until function has been completed. */ void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque); /*! POOL_tryAdd() : * Add the job `function(opaque)` to thread pool _if_ a worker is available. * Returns immediately even if not (does not block). * @return : 1 if successful, 0 if not. */ int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque); #if defined (__cplusplus) } #endif #endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/threading.c0000644000076500000240000000551014601576577023265 0ustar00twstaff/** * Copyright (c) 2016 Tino Reichardt * All rights reserved. * * You can contact the author at: * - zstdmt source repository: https://github.com/mcmilk/zstdmt * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /** * This file will hold wrapper for systems, which do not support pthreads */ #include "threading.h" /* create fake symbol to avoid empty translation unit warning */ int g_ZSTD_threading_useless_symbol; #if defined(ZSTD_MULTITHREAD) && defined(_WIN32) /** * Windows minimalist Pthread Wrapper, based on : * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html */ /* === Dependencies === */ #include #include /* === Implementation === */ static unsigned __stdcall worker(void *arg) { ZSTD_pthread_t* const thread = (ZSTD_pthread_t*) arg; thread->arg = thread->start_routine(thread->arg); return 0; } int ZSTD_pthread_create(ZSTD_pthread_t* thread, const void* unused, void* (*start_routine) (void*), void* arg) { (void)unused; thread->arg = arg; thread->start_routine = start_routine; thread->handle = (HANDLE) _beginthreadex(NULL, 0, worker, thread, 0, NULL); if (!thread->handle) return errno; else return 0; } int ZSTD_pthread_join(ZSTD_pthread_t thread, void **value_ptr) { DWORD result; if (!thread.handle) return 0; result = WaitForSingleObject(thread.handle, INFINITE); switch (result) { case WAIT_OBJECT_0: if (value_ptr) *value_ptr = thread.arg; return 0; case WAIT_ABANDONED: return EINVAL; default: return GetLastError(); } } #endif /* ZSTD_MULTITHREAD */ #if defined(ZSTD_MULTITHREAD) && DEBUGLEVEL >= 1 && !defined(_WIN32) #include int ZSTD_pthread_mutex_init(ZSTD_pthread_mutex_t* mutex, pthread_mutexattr_t const* attr) { *mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); if (!*mutex) return 1; return pthread_mutex_init(*mutex, attr); } int ZSTD_pthread_mutex_destroy(ZSTD_pthread_mutex_t* mutex) { if (!*mutex) return 0; { int const ret = pthread_mutex_destroy(*mutex); free(*mutex); return ret; } } int ZSTD_pthread_cond_init(ZSTD_pthread_cond_t* cond, pthread_condattr_t const* attr) { *cond = (pthread_cond_t*)malloc(sizeof(pthread_cond_t)); if (!*cond) return 1; return pthread_cond_init(*cond, attr); } int ZSTD_pthread_cond_destroy(ZSTD_pthread_cond_t* cond) { if (!*cond) return 0; { int const ret = pthread_cond_destroy(*cond); free(*cond); return ret; } } #endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/threading.h0000644000076500000240000001235314601576577023275 0ustar00twstaff/** * Copyright (c) 2016 Tino Reichardt * All rights reserved. * * You can contact the author at: * - zstdmt source repository: https://github.com/mcmilk/zstdmt * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef THREADING_H_938743 #define THREADING_H_938743 #include "debug.h" #if defined (__cplusplus) extern "C" { #endif #if defined(ZSTD_MULTITHREAD) && defined(_WIN32) /** * Windows minimalist Pthread Wrapper, based on : * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html */ #ifdef WINVER # undef WINVER #endif #define WINVER 0x0600 #ifdef _WIN32_WINNT # undef _WIN32_WINNT #endif #define _WIN32_WINNT 0x0600 #ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif #undef ERROR /* reported already defined on VS 2015 (Rich Geldreich) */ #include #undef ERROR #define ERROR(name) ZSTD_ERROR(name) /* mutex */ #define ZSTD_pthread_mutex_t CRITICAL_SECTION #define ZSTD_pthread_mutex_init(a, b) ((void)(b), InitializeCriticalSection((a)), 0) #define ZSTD_pthread_mutex_destroy(a) DeleteCriticalSection((a)) #define ZSTD_pthread_mutex_lock(a) EnterCriticalSection((a)) #define ZSTD_pthread_mutex_unlock(a) LeaveCriticalSection((a)) /* condition variable */ #define ZSTD_pthread_cond_t CONDITION_VARIABLE #define ZSTD_pthread_cond_init(a, b) ((void)(b), InitializeConditionVariable((a)), 0) #define ZSTD_pthread_cond_destroy(a) ((void)(a)) #define ZSTD_pthread_cond_wait(a, b) SleepConditionVariableCS((a), (b), INFINITE) #define ZSTD_pthread_cond_signal(a) WakeConditionVariable((a)) #define ZSTD_pthread_cond_broadcast(a) WakeAllConditionVariable((a)) /* ZSTD_pthread_create() and ZSTD_pthread_join() */ typedef struct { HANDLE handle; void* (*start_routine)(void*); void* arg; } ZSTD_pthread_t; int ZSTD_pthread_create(ZSTD_pthread_t* thread, const void* unused, void* (*start_routine) (void*), void* arg); int ZSTD_pthread_join(ZSTD_pthread_t thread, void** value_ptr); /** * add here more wrappers as required */ #elif defined(ZSTD_MULTITHREAD) /* posix assumed ; need a better detection method */ /* === POSIX Systems === */ # include #if DEBUGLEVEL < 1 #define ZSTD_pthread_mutex_t pthread_mutex_t #define ZSTD_pthread_mutex_init(a, b) pthread_mutex_init((a), (b)) #define ZSTD_pthread_mutex_destroy(a) pthread_mutex_destroy((a)) #define ZSTD_pthread_mutex_lock(a) pthread_mutex_lock((a)) #define ZSTD_pthread_mutex_unlock(a) pthread_mutex_unlock((a)) #define ZSTD_pthread_cond_t pthread_cond_t #define ZSTD_pthread_cond_init(a, b) pthread_cond_init((a), (b)) #define ZSTD_pthread_cond_destroy(a) pthread_cond_destroy((a)) #define ZSTD_pthread_cond_wait(a, b) pthread_cond_wait((a), (b)) #define ZSTD_pthread_cond_signal(a) pthread_cond_signal((a)) #define ZSTD_pthread_cond_broadcast(a) pthread_cond_broadcast((a)) #define ZSTD_pthread_t pthread_t #define ZSTD_pthread_create(a, b, c, d) pthread_create((a), (b), (c), (d)) #define ZSTD_pthread_join(a, b) pthread_join((a),(b)) #else /* DEBUGLEVEL >= 1 */ /* Debug implementation of threading. * In this implementation we use pointers for mutexes and condition variables. * This way, if we forget to init/destroy them the program will crash or ASAN * will report leaks. */ #define ZSTD_pthread_mutex_t pthread_mutex_t* int ZSTD_pthread_mutex_init(ZSTD_pthread_mutex_t* mutex, pthread_mutexattr_t const* attr); int ZSTD_pthread_mutex_destroy(ZSTD_pthread_mutex_t* mutex); #define ZSTD_pthread_mutex_lock(a) pthread_mutex_lock(*(a)) #define ZSTD_pthread_mutex_unlock(a) pthread_mutex_unlock(*(a)) #define ZSTD_pthread_cond_t pthread_cond_t* int ZSTD_pthread_cond_init(ZSTD_pthread_cond_t* cond, pthread_condattr_t const* attr); int ZSTD_pthread_cond_destroy(ZSTD_pthread_cond_t* cond); #define ZSTD_pthread_cond_wait(a, b) pthread_cond_wait(*(a), *(b)) #define ZSTD_pthread_cond_signal(a) pthread_cond_signal(*(a)) #define ZSTD_pthread_cond_broadcast(a) pthread_cond_broadcast(*(a)) #define ZSTD_pthread_t pthread_t #define ZSTD_pthread_create(a, b, c, d) pthread_create((a), (b), (c), (d)) #define ZSTD_pthread_join(a, b) pthread_join((a),(b)) #endif #else /* ZSTD_MULTITHREAD not defined */ /* No multithreading support */ typedef int ZSTD_pthread_mutex_t; #define ZSTD_pthread_mutex_init(a, b) ((void)(a), (void)(b), 0) #define ZSTD_pthread_mutex_destroy(a) ((void)(a)) #define ZSTD_pthread_mutex_lock(a) ((void)(a)) #define ZSTD_pthread_mutex_unlock(a) ((void)(a)) typedef int ZSTD_pthread_cond_t; #define ZSTD_pthread_cond_init(a, b) ((void)(a), (void)(b), 0) #define ZSTD_pthread_cond_destroy(a) ((void)(a)) #define ZSTD_pthread_cond_wait(a, b) ((void)(a), (void)(b)) #define ZSTD_pthread_cond_signal(a) ((void)(a)) #define ZSTD_pthread_cond_broadcast(a) ((void)(a)) /* do not use ZSTD_pthread_t */ #endif /* ZSTD_MULTITHREAD */ #if defined (__cplusplus) } #endif #endif /* THREADING_H_938743 */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/xxhash.c0000644000076500000240000006672614601576577022643 0ustar00twstaff/* * xxHash - Fast Hash algorithm * Copyright (c) 2012-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - xxHash homepage: http://www.xxhash.com * - xxHash source repository : https://github.com/Cyan4973/xxHash * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* ************************************* * Tuning parameters ***************************************/ /*!XXH_FORCE_MEMORY_ACCESS : * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. * The below switch allow to select different access method for improved performance. * Method 0 (default) : use `memcpy()`. Safe and portable. * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. * Method 2 : direct access. This method doesn't depend on compiler but violate C standard. * It can generate buggy code on targets which do not support unaligned memory accesses. * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) * See http://stackoverflow.com/a/32095106/646947 for details. * Prefer these methods in priority order (0 > 1 > 2) */ #ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ # if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) # define XXH_FORCE_MEMORY_ACCESS 2 # elif (defined(__INTEL_COMPILER) && !defined(WIN32)) || \ (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) )) || \ defined(__ICCARM__) # define XXH_FORCE_MEMORY_ACCESS 1 # endif #endif /*!XXH_ACCEPT_NULL_INPUT_POINTER : * If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer. * When this option is enabled, xxHash output for null input pointers will be the same as a null-length input. * By default, this option is disabled. To enable it, uncomment below define : */ /* #define XXH_ACCEPT_NULL_INPUT_POINTER 1 */ /*!XXH_FORCE_NATIVE_FORMAT : * By default, xxHash library provides endian-independent Hash values, based on little-endian convention. * Results are therefore identical for little-endian and big-endian CPU. * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. * Should endian-independence be of no importance for your application, you may set the #define below to 1, * to improve speed for Big-endian CPU. * This option has no impact on Little_Endian CPU. */ #ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */ # define XXH_FORCE_NATIVE_FORMAT 0 #endif /*!XXH_FORCE_ALIGN_CHECK : * This is a minor performance trick, only useful with lots of very small keys. * It means : check for aligned/unaligned input. * The check costs one initial branch per hash; set to 0 when the input data * is guaranteed to be aligned. */ #ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ # if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) # define XXH_FORCE_ALIGN_CHECK 0 # else # define XXH_FORCE_ALIGN_CHECK 1 # endif #endif /* ************************************* * Includes & Memory related functions ***************************************/ /* Modify the local functions below should you wish to use some other memory routines */ /* for malloc(), free() */ #include #include /* size_t */ static void* XXH_malloc(size_t s) { return malloc(s); } static void XXH_free (void* p) { free(p); } /* for memcpy() */ #include static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } #ifndef XXH_STATIC_LINKING_ONLY # define XXH_STATIC_LINKING_ONLY #endif #include "xxhash.h" /* ************************************* * Compiler Specific Options ***************************************/ #if (defined(__GNUC__) && !defined(__STRICT_ANSI__)) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ # define INLINE_KEYWORD inline #else # define INLINE_KEYWORD #endif #if defined(__GNUC__) || defined(__ICCARM__) # define FORCE_INLINE_ATTR __attribute__((always_inline)) #elif defined(_MSC_VER) # define FORCE_INLINE_ATTR __forceinline #else # define FORCE_INLINE_ATTR #endif #define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR #ifdef _MSC_VER # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #endif /* ************************************* * Basic Types ***************************************/ #ifndef MEM_MODULE # define MEM_MODULE # if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; # else typedef unsigned char BYTE; typedef unsigned short U16; typedef unsigned int U32; typedef signed int S32; typedef unsigned long long U64; /* if your compiler doesn't support unsigned long long, replace by another 64-bit type here. Note that xxhash.h will also need to be updated. */ # endif #endif #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) /* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; } static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; } #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ /* currently only defined for gcc and icc */ typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign; static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } static U64 XXH_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } #else /* portable and safe solution. Generally efficient. * see : http://stackoverflow.com/a/32095106/646947 */ static U32 XXH_read32(const void* memPtr) { U32 val; memcpy(&val, memPtr, sizeof(val)); return val; } static U64 XXH_read64(const void* memPtr) { U64 val; memcpy(&val, memPtr, sizeof(val)); return val; } #endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ /* **************************************** * Compiler-specific Functions and Macros ******************************************/ #define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) /* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ #if defined(_MSC_VER) # define XXH_rotl32(x,r) _rotl(x,r) # define XXH_rotl64(x,r) _rotl64(x,r) #else #if defined(__ICCARM__) # include # define XXH_rotl32(x,r) __ROR(x,(32 - r)) #else # define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) #endif # define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) #endif #if defined(_MSC_VER) /* Visual Studio */ # define XXH_swap32 _byteswap_ulong # define XXH_swap64 _byteswap_uint64 #elif GCC_VERSION >= 403 # define XXH_swap32 __builtin_bswap32 # define XXH_swap64 __builtin_bswap64 #else static U32 XXH_swap32 (U32 x) { return ((x << 24) & 0xff000000 ) | ((x << 8) & 0x00ff0000 ) | ((x >> 8) & 0x0000ff00 ) | ((x >> 24) & 0x000000ff ); } static U64 XXH_swap64 (U64 x) { return ((x << 56) & 0xff00000000000000ULL) | ((x << 40) & 0x00ff000000000000ULL) | ((x << 24) & 0x0000ff0000000000ULL) | ((x << 8) & 0x000000ff00000000ULL) | ((x >> 8) & 0x00000000ff000000ULL) | ((x >> 24) & 0x0000000000ff0000ULL) | ((x >> 40) & 0x000000000000ff00ULL) | ((x >> 56) & 0x00000000000000ffULL); } #endif /* ************************************* * Architecture Macros ***************************************/ typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; /* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ #ifndef XXH_CPU_LITTLE_ENDIAN static const int g_one = 1; # define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&g_one)) #endif /* *************************** * Memory reads *****************************/ typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; FORCE_INLINE_TEMPLATE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) { if (align==XXH_unaligned) return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); else return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr); } FORCE_INLINE_TEMPLATE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); } static U32 XXH_readBE32(const void* ptr) { return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); } FORCE_INLINE_TEMPLATE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) { if (align==XXH_unaligned) return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); else return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr); } FORCE_INLINE_TEMPLATE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) { return XXH_readLE64_align(ptr, endian, XXH_unaligned); } static U64 XXH_readBE64(const void* ptr) { return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); } /* ************************************* * Macros ***************************************/ #define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ /* ************************************* * Constants ***************************************/ static const U32 PRIME32_1 = 2654435761U; static const U32 PRIME32_2 = 2246822519U; static const U32 PRIME32_3 = 3266489917U; static const U32 PRIME32_4 = 668265263U; static const U32 PRIME32_5 = 374761393U; static const U64 PRIME64_1 = 11400714785074694791ULL; static const U64 PRIME64_2 = 14029467366897019727ULL; static const U64 PRIME64_3 = 1609587929392839161ULL; static const U64 PRIME64_4 = 9650029242287828579ULL; static const U64 PRIME64_5 = 2870177450012600261ULL; XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } /* ************************** * Utils ****************************/ XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* restrict dstState, const XXH32_state_t* restrict srcState) { memcpy(dstState, srcState, sizeof(*dstState)); } XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* restrict dstState, const XXH64_state_t* restrict srcState) { memcpy(dstState, srcState, sizeof(*dstState)); } /* *************************** * Simple Hash Functions *****************************/ static U32 XXH32_round(U32 seed, U32 input) { seed += input * PRIME32_2; seed = XXH_rotl32(seed, 13); seed *= PRIME32_1; return seed; } FORCE_INLINE_TEMPLATE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align) { const BYTE* p = (const BYTE*)input; const BYTE* bEnd = p + len; U32 h32; #define XXH_get32bits(p) XXH_readLE32_align(p, endian, align) #ifdef XXH_ACCEPT_NULL_INPUT_POINTER if (p==NULL) { len=0; bEnd=p=(const BYTE*)(size_t)16; } #endif if (len>=16) { const BYTE* const limit = bEnd - 16; U32 v1 = seed + PRIME32_1 + PRIME32_2; U32 v2 = seed + PRIME32_2; U32 v3 = seed + 0; U32 v4 = seed - PRIME32_1; do { v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4; v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4; v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4; v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4; } while (p<=limit); h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); } else { h32 = seed + PRIME32_5; } h32 += (U32) len; while (p+4<=bEnd) { h32 += XXH_get32bits(p) * PRIME32_3; h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; p+=4; } while (p> 15; h32 *= PRIME32_2; h32 ^= h32 >> 13; h32 *= PRIME32_3; h32 ^= h32 >> 16; return h32; } XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed) { #if 0 /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ XXH32_CREATESTATE_STATIC(state); XXH32_reset(state, seed); XXH32_update(state, input, len); return XXH32_digest(state); #else XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if (XXH_FORCE_ALIGN_CHECK) { if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); else return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); } } if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); else return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); #endif } static U64 XXH64_round(U64 acc, U64 input) { acc += input * PRIME64_2; acc = XXH_rotl64(acc, 31); acc *= PRIME64_1; return acc; } static U64 XXH64_mergeRound(U64 acc, U64 val) { val = XXH64_round(0, val); acc ^= val; acc = acc * PRIME64_1 + PRIME64_4; return acc; } FORCE_INLINE_TEMPLATE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align) { const BYTE* p = (const BYTE*)input; const BYTE* const bEnd = p + len; U64 h64; #define XXH_get64bits(p) XXH_readLE64_align(p, endian, align) #ifdef XXH_ACCEPT_NULL_INPUT_POINTER if (p==NULL) { len=0; bEnd=p=(const BYTE*)(size_t)32; } #endif if (len>=32) { const BYTE* const limit = bEnd - 32; U64 v1 = seed + PRIME64_1 + PRIME64_2; U64 v2 = seed + PRIME64_2; U64 v3 = seed + 0; U64 v4 = seed - PRIME64_1; do { v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8; v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8; v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8; v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8; } while (p<=limit); h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); h64 = XXH64_mergeRound(h64, v1); h64 = XXH64_mergeRound(h64, v2); h64 = XXH64_mergeRound(h64, v3); h64 = XXH64_mergeRound(h64, v4); } else { h64 = seed + PRIME64_5; } h64 += (U64) len; while (p+8<=bEnd) { U64 const k1 = XXH64_round(0, XXH_get64bits(p)); h64 ^= k1; h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; p+=8; } if (p+4<=bEnd) { h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; p+=4; } while (p> 33; h64 *= PRIME64_2; h64 ^= h64 >> 29; h64 *= PRIME64_3; h64 ^= h64 >> 32; return h64; } XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed) { #if 0 /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ XXH64_CREATESTATE_STATIC(state); XXH64_reset(state, seed); XXH64_update(state, input, len); return XXH64_digest(state); #else XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if (XXH_FORCE_ALIGN_CHECK) { if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); else return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); } } if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); else return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); #endif } /* ************************************************** * Advanced Hash Functions ****************************************************/ XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) { return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); } XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) { XXH_free(statePtr); return XXH_OK; } XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) { return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); } XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) { XXH_free(statePtr); return XXH_OK; } /*** Hash feed ***/ XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed) { XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ memset(&state, 0, sizeof(state)-4); /* do not write into reserved, for future removal */ state.v1 = seed + PRIME32_1 + PRIME32_2; state.v2 = seed + PRIME32_2; state.v3 = seed + 0; state.v4 = seed - PRIME32_1; memcpy(statePtr, &state, sizeof(state)); return XXH_OK; } XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed) { XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ memset(&state, 0, sizeof(state)-8); /* do not write into reserved, for future removal */ state.v1 = seed + PRIME64_1 + PRIME64_2; state.v2 = seed + PRIME64_2; state.v3 = seed + 0; state.v4 = seed - PRIME64_1; memcpy(statePtr, &state, sizeof(state)); return XXH_OK; } FORCE_INLINE_TEMPLATE XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian) { const BYTE* p = (const BYTE*)input; const BYTE* const bEnd = p + len; #ifdef XXH_ACCEPT_NULL_INPUT_POINTER if (input==NULL) return XXH_ERROR; #endif state->total_len_32 += (unsigned)len; state->large_len |= (len>=16) | (state->total_len_32>=16); if (state->memsize + len < 16) { /* fill in tmp buffer */ XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len); state->memsize += (unsigned)len; return XXH_OK; } if (state->memsize) { /* some data left from previous update */ XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize); { const U32* p32 = state->mem32; state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++; state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++; state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++; state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); p32++; } p += 16-state->memsize; state->memsize = 0; } if (p <= bEnd-16) { const BYTE* const limit = bEnd - 16; U32 v1 = state->v1; U32 v2 = state->v2; U32 v3 = state->v3; U32 v4 = state->v4; do { v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4; v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4; v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4; v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4; } while (p<=limit); state->v1 = v1; state->v2 = v2; state->v3 = v3; state->v4 = v4; } if (p < bEnd) { XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); state->memsize = (unsigned)(bEnd-p); } return XXH_OK; } XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len) { XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH32_update_endian(state_in, input, len, XXH_littleEndian); else return XXH32_update_endian(state_in, input, len, XXH_bigEndian); } FORCE_INLINE_TEMPLATE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian) { const BYTE * p = (const BYTE*)state->mem32; const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize; U32 h32; if (state->large_len) { h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18); } else { h32 = state->v3 /* == seed */ + PRIME32_5; } h32 += state->total_len_32; while (p+4<=bEnd) { h32 += XXH_readLE32(p, endian) * PRIME32_3; h32 = XXH_rotl32(h32, 17) * PRIME32_4; p+=4; } while (p> 15; h32 *= PRIME32_2; h32 ^= h32 >> 13; h32 *= PRIME32_3; h32 ^= h32 >> 16; return h32; } XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in) { XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH32_digest_endian(state_in, XXH_littleEndian); else return XXH32_digest_endian(state_in, XXH_bigEndian); } /* **** XXH64 **** */ FORCE_INLINE_TEMPLATE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian) { const BYTE* p = (const BYTE*)input; const BYTE* const bEnd = p + len; #ifdef XXH_ACCEPT_NULL_INPUT_POINTER if (input==NULL) return XXH_ERROR; #endif state->total_len += len; if (state->memsize + len < 32) { /* fill in tmp buffer */ if (input != NULL) { XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len); } state->memsize += (U32)len; return XXH_OK; } if (state->memsize) { /* tmp buffer is full */ XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize); state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian)); state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian)); state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian)); state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian)); p += 32-state->memsize; state->memsize = 0; } if (p+32 <= bEnd) { const BYTE* const limit = bEnd - 32; U64 v1 = state->v1; U64 v2 = state->v2; U64 v3 = state->v3; U64 v4 = state->v4; do { v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8; v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8; v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8; v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8; } while (p<=limit); state->v1 = v1; state->v2 = v2; state->v3 = v3; state->v4 = v4; } if (p < bEnd) { XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); state->memsize = (unsigned)(bEnd-p); } return XXH_OK; } XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len) { XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH64_update_endian(state_in, input, len, XXH_littleEndian); else return XXH64_update_endian(state_in, input, len, XXH_bigEndian); } FORCE_INLINE_TEMPLATE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian) { const BYTE * p = (const BYTE*)state->mem64; const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize; U64 h64; if (state->total_len >= 32) { U64 const v1 = state->v1; U64 const v2 = state->v2; U64 const v3 = state->v3; U64 const v4 = state->v4; h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); h64 = XXH64_mergeRound(h64, v1); h64 = XXH64_mergeRound(h64, v2); h64 = XXH64_mergeRound(h64, v3); h64 = XXH64_mergeRound(h64, v4); } else { h64 = state->v3 + PRIME64_5; } h64 += (U64) state->total_len; while (p+8<=bEnd) { U64 const k1 = XXH64_round(0, XXH_readLE64(p, endian)); h64 ^= k1; h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; p+=8; } if (p+4<=bEnd) { h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1; h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; p+=4; } while (p> 33; h64 *= PRIME64_2; h64 ^= h64 >> 29; h64 *= PRIME64_3; h64 ^= h64 >> 32; return h64; } XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in) { XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH64_digest_endian(state_in, XXH_littleEndian); else return XXH64_digest_endian(state_in, XXH_bigEndian); } /* ************************** * Canonical representation ****************************/ /*! Default XXH result types are basic unsigned 32 and 64 bits. * The canonical representation follows human-readable write convention, aka big-endian (large digits first). * These functions allow transformation of hash result into and from its canonical format. * This way, hash values can be written into a file or buffer, and remain comparable across different systems and programs. */ XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); memcpy(dst, &hash, sizeof(*dst)); } XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); memcpy(dst, &hash, sizeof(*dst)); } XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) { return XXH_readBE32(src); } XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) { return XXH_readBE64(src); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/xxhash.h0000644000076500000240000002670614601576577022642 0ustar00twstaff/* * xxHash - Extremely Fast Hash algorithm * Header File * Copyright (c) 2012-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - xxHash source repository : https://github.com/Cyan4973/xxHash * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* Notice extracted from xxHash homepage : xxHash is an extremely fast Hash algorithm, running at RAM speed limits. It also successfully passes all tests from the SMHasher suite. Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) Name Speed Q.Score Author xxHash 5.4 GB/s 10 CrapWow 3.2 GB/s 2 Andrew MumurHash 3a 2.7 GB/s 10 Austin Appleby SpookyHash 2.0 GB/s 10 Bob Jenkins SBox 1.4 GB/s 9 Bret Mulvey Lookup3 1.2 GB/s 9 Bob Jenkins SuperFastHash 1.2 GB/s 1 Paul Hsieh CityHash64 1.05 GB/s 10 Pike & Alakuijala FNV 0.55 GB/s 5 Fowler, Noll, Vo CRC32 0.43 GB/s 9 MD5-32 0.33 GB/s 10 Ronald L. Rivest SHA1-32 0.28 GB/s 10 Q.Score is a measure of quality of the hash function. It depends on successfully passing SMHasher test set. 10 is a perfect score. A 64-bits version, named XXH64, is available since r35. It offers much better speed, but for 64-bits applications only. Name Speed on 64 bits Speed on 32 bits XXH64 13.8 GB/s 1.9 GB/s XXH32 6.8 GB/s 6.0 GB/s */ #if defined (__cplusplus) extern "C" { #endif #ifndef XXHASH_H_5627135585666179 #define XXHASH_H_5627135585666179 1 /* **************************** * Definitions ******************************/ #include /* size_t */ typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; /* **************************** * API modifier ******************************/ /** XXH_PRIVATE_API * This is useful if you want to include xxhash functions in `static` mode * in order to inline them, and remove their symbol from the public list. * Methodology : * #define XXH_PRIVATE_API * #include "xxhash.h" * `xxhash.c` is automatically included. * It's not useful to compile and link it as a separate module anymore. */ #ifdef XXH_PRIVATE_API # ifndef XXH_STATIC_LINKING_ONLY # define XXH_STATIC_LINKING_ONLY # endif # if defined(__GNUC__) # define XXH_PUBLIC_API static __inline __attribute__((unused)) # elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # define XXH_PUBLIC_API static inline # elif defined(_MSC_VER) # define XXH_PUBLIC_API static __inline # else # define XXH_PUBLIC_API static /* this version may generate warnings for unused static functions; disable the relevant warning */ # endif #else # define XXH_PUBLIC_API /* do nothing */ #endif /* XXH_PRIVATE_API */ /*!XXH_NAMESPACE, aka Namespace Emulation : If you want to include _and expose_ xxHash functions from within your own library, but also want to avoid symbol collisions with another library which also includes xxHash, you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library with the value of XXH_NAMESPACE (so avoid to keep it NULL and avoid numeric values). Note that no change is required within the calling program as long as it includes `xxhash.h` : regular symbol name will be automatically translated by this header. */ #ifdef XXH_NAMESPACE # define XXH_CAT(A,B) A##B # define XXH_NAME2(A,B) XXH_CAT(A,B) # define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) # define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) # define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) # define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) # define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) # define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) # define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) # define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) # define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) # define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) # define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) # define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) # define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) # define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) # define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) # define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) # define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) # define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) # define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) #endif /* ************************************* * Version ***************************************/ #define XXH_VERSION_MAJOR 0 #define XXH_VERSION_MINOR 6 #define XXH_VERSION_RELEASE 2 #define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) XXH_PUBLIC_API unsigned XXH_versionNumber (void); /* **************************** * Simple Hash Functions ******************************/ typedef unsigned int XXH32_hash_t; typedef unsigned long long XXH64_hash_t; XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed); XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed); /*! XXH32() : Calculate the 32-bits hash of sequence "length" bytes stored at memory address "input". The memory between input & input+length must be valid (allocated and read-accessible). "seed" can be used to alter the result predictably. Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s XXH64() : Calculate the 64-bits hash of sequence of length "len" stored at memory address "input". "seed" can be used to alter the result predictably. This function runs 2x faster on 64-bits systems, but slower on 32-bits systems (see benchmark). */ /* **************************** * Streaming Hash Functions ******************************/ typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ /*! State allocation, compatible with dynamic libraries */ XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); /* hash streaming */ XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed); XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed); XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); /* These functions generate the xxHash of an input provided in multiple segments. Note that, for small input, they are slower than single-call functions, due to state management. For small input, prefer `XXH32()` and `XXH64()` . XXH state must first be allocated, using XXH*_createState() . Start a new hash by initializing state with a seed, using XXH*_reset(). Then, feed the hash state by calling XXH*_update() as many times as necessary. Obviously, input must be allocated and read accessible. The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. Finally, a hash value can be produced anytime, by using XXH*_digest(). This function returns the nn-bits hash as an int or long long. It's still possible to continue inserting input into the hash state after a digest, and generate some new hashes later on, by calling again XXH*_digest(). When done, free XXH state space if it was allocated dynamically. */ /* ************************** * Utils ****************************/ #if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) /* ! C99 */ # define restrict /* disable restrict */ #endif XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* restrict dst_state, const XXH32_state_t* restrict src_state); XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* restrict dst_state, const XXH64_state_t* restrict src_state); /* ************************** * Canonical representation ****************************/ /* Default result type for XXH functions are primitive unsigned 32 and 64 bits. * The canonical representation uses human-readable write convention, aka big-endian (large digits first). * These functions allow transformation of hash result into and from its canonical format. * This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. */ typedef struct { unsigned char digest[4]; } XXH32_canonical_t; typedef struct { unsigned char digest[8]; } XXH64_canonical_t; XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); #endif /* XXHASH_H_5627135585666179 */ /* ================================================================================================ This section contains definitions which are not guaranteed to remain stable. They may change in future versions, becoming incompatible with a different version of the library. They shall only be used with static linking. Never use these definitions in association with dynamic linking ! =================================================================================================== */ #if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXH_STATIC_H_3543687687345) #define XXH_STATIC_H_3543687687345 /* These definitions are only meant to allow allocation of XXH state statically, on stack, or in a struct for example. Do not use members directly. */ struct XXH32_state_s { unsigned total_len_32; unsigned large_len; unsigned v1; unsigned v2; unsigned v3; unsigned v4; unsigned mem32[4]; /* buffer defined as U32 for alignment */ unsigned memsize; unsigned reserved; /* never read nor write, will be removed in a future version */ }; /* typedef'd to XXH32_state_t */ struct XXH64_state_s { unsigned long long total_len; unsigned long long v1; unsigned long long v2; unsigned long long v3; unsigned long long v4; unsigned long long mem64[4]; /* buffer defined as U64 for alignment */ unsigned memsize; unsigned reserved[2]; /* never read nor write, will be removed in a future version */ }; /* typedef'd to XXH64_state_t */ # ifdef XXH_PRIVATE_API # include "xxhash.c" /* include xxhash functions as `static`, for inlining */ # endif #endif /* XXH_STATIC_LINKING_ONLY && XXH_STATIC_H_3543687687345 */ #if defined (__cplusplus) } #endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/zstd_common.c0000644000076500000240000000515614601576577023662 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /*-************************************* * Dependencies ***************************************/ #include /* malloc, calloc, free */ #include /* memset */ #include "error_private.h" #include "zstd_internal.h" /*-**************************************** * Version ******************************************/ unsigned ZSTD_versionNumber(void) { return ZSTD_VERSION_NUMBER; } const char* ZSTD_versionString(void) { return ZSTD_VERSION_STRING; } /*-**************************************** * ZSTD Error Management ******************************************/ #undef ZSTD_isError /* defined within zstd_internal.h */ /*! ZSTD_isError() : * tells if a return value is an error code * symbol is required for external callers */ unsigned ZSTD_isError(size_t code) { return ERR_isError(code); } /*! ZSTD_getErrorName() : * provides error code string from function result (useful for debugging) */ const char* ZSTD_getErrorName(size_t code) { return ERR_getErrorName(code); } /*! ZSTD_getError() : * convert a `size_t` function result into a proper ZSTD_errorCode enum */ ZSTD_ErrorCode ZSTD_getErrorCode(size_t code) { return ERR_getErrorCode(code); } /*! ZSTD_getErrorString() : * provides error code string from enum */ const char* ZSTD_getErrorString(ZSTD_ErrorCode code) { return ERR_getErrorString(code); } /*=************************************************************** * Custom allocator ****************************************************************/ void* ZSTD_malloc(size_t size, ZSTD_customMem customMem) { if (customMem.customAlloc) return customMem.customAlloc(customMem.opaque, size); return malloc(size); } void* ZSTD_calloc(size_t size, ZSTD_customMem customMem) { if (customMem.customAlloc) { /* calloc implemented as malloc+memset; * not as efficient as calloc, but next best guess for custom malloc */ void* const ptr = customMem.customAlloc(customMem.opaque, size); memset(ptr, 0, size); return ptr; } return calloc(1, size); } void ZSTD_free(void* ptr, ZSTD_customMem customMem) { if (ptr!=NULL) { if (customMem.customFree) customMem.customFree(customMem.opaque, ptr); else free(ptr); } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/zstd_errors.h0000644000076500000240000000731414601576577023711 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_ERRORS_H_398273423 #define ZSTD_ERRORS_H_398273423 #if defined (__cplusplus) extern "C" { #endif /*===== dependency =====*/ #include /* size_t */ /* ===== ZSTDERRORLIB_API : control library symbols visibility ===== */ #ifndef ZSTDERRORLIB_VISIBILITY # if defined(__GNUC__) && (__GNUC__ >= 4) # define ZSTDERRORLIB_VISIBILITY __attribute__ ((visibility ("default"))) # else # define ZSTDERRORLIB_VISIBILITY # endif #endif #if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) # define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBILITY #elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) # define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ #else # define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBILITY #endif /*-********************************************* * Error codes list *-********************************************* * Error codes _values_ are pinned down since v1.3.1 only. * Therefore, don't rely on values if you may link to any version < v1.3.1. * * Only values < 100 are considered stable. * * note 1 : this API shall be used with static linking only. * dynamic linking is not yet officially supported. * note 2 : Prefer relying on the enum than on its value whenever possible * This is the only supported way to use the error list < v1.3.1 * note 3 : ZSTD_isError() is always correct, whatever the library version. **********************************************/ typedef enum { ZSTD_error_no_error = 0, ZSTD_error_GENERIC = 1, ZSTD_error_prefix_unknown = 10, ZSTD_error_version_unsupported = 12, ZSTD_error_frameParameter_unsupported = 14, ZSTD_error_frameParameter_windowTooLarge = 16, ZSTD_error_corruption_detected = 20, ZSTD_error_checksum_wrong = 22, ZSTD_error_dictionary_corrupted = 30, ZSTD_error_dictionary_wrong = 32, ZSTD_error_dictionaryCreation_failed = 34, ZSTD_error_parameter_unsupported = 40, ZSTD_error_parameter_outOfBound = 42, ZSTD_error_tableLog_tooLarge = 44, ZSTD_error_maxSymbolValue_tooLarge = 46, ZSTD_error_maxSymbolValue_tooSmall = 48, ZSTD_error_stage_wrong = 60, ZSTD_error_init_missing = 62, ZSTD_error_memory_allocation = 64, ZSTD_error_workSpace_tooSmall= 66, ZSTD_error_dstSize_tooSmall = 70, ZSTD_error_srcSize_wrong = 72, ZSTD_error_dstBuffer_null = 74, /* following error codes are __NOT STABLE__, they can be removed or changed in future versions */ ZSTD_error_frameIndex_tooLarge = 100, ZSTD_error_seekableIO = 102, ZSTD_error_dstBuffer_wrong = 104, ZSTD_error_maxCode = 120 /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */ } ZSTD_ErrorCode; /*! ZSTD_getErrorCode() : convert a `size_t` function result into a `ZSTD_ErrorCode` enum type, which can be used to compare with enum list published above */ ZSTDERRORLIB_API ZSTD_ErrorCode ZSTD_getErrorCode(size_t functionResult); ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code); /**< Same as ZSTD_getErrorName, but using a `ZSTD_ErrorCode` enum argument */ #if defined (__cplusplus) } #endif #endif /* ZSTD_ERRORS_H_398273423 */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/common/zstd_internal.h0000644000076500000240000003571414601576577024216 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_CCOMMON_H_MODULE #define ZSTD_CCOMMON_H_MODULE /* this module contains definitions which must be identical * across compression, decompression and dictBuilder. * It also contains a few functions useful to at least 2 of them * and which benefit from being inlined */ /*-************************************* * Dependencies ***************************************/ #ifdef __aarch64__ #include #endif #include "compiler.h" #include "mem.h" #include "debug.h" /* assert, DEBUGLOG, RAWLOG, g_debuglevel */ #include "error_private.h" #define ZSTD_STATIC_LINKING_ONLY #include "../zstd.h" #define FSE_STATIC_LINKING_ONLY #include "fse.h" #define HUF_STATIC_LINKING_ONLY #include "huf.h" #ifndef XXH_STATIC_LINKING_ONLY # define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ #endif #include "xxhash.h" /* XXH_reset, update, digest */ #if defined (__cplusplus) extern "C" { #endif /* ---- static assert (debug) --- */ #define ZSTD_STATIC_ASSERT(c) DEBUG_STATIC_ASSERT(c) #define ZSTD_isError ERR_isError /* for inlining */ #define FSE_isError ERR_isError #define HUF_isError ERR_isError /*-************************************* * shared macros ***************************************/ #undef MIN #undef MAX #define MIN(a,b) ((a)<(b) ? (a) : (b)) #define MAX(a,b) ((a)>(b) ? (a) : (b)) /** * Ignore: this is an internal helper. * * This is a helper function to help force C99-correctness during compilation. * Under strict compilation modes, variadic macro arguments can't be empty. * However, variadic function arguments can be. Using a function therefore lets * us statically check that at least one (string) argument was passed, * independent of the compilation flags. */ static INLINE_KEYWORD UNUSED_ATTR void _force_has_format_string(const char *format, ...) { (void)format; } /** * Ignore: this is an internal helper. * * We want to force this function invocation to be syntactically correct, but * we don't want to force runtime evaluation of its arguments. */ #define _FORCE_HAS_FORMAT_STRING(...) \ if (0) { \ _force_has_format_string(__VA_ARGS__); \ } /** * Return the specified error if the condition evaluates to true. * * In debug modes, prints additional information. * In order to do that (particularly, printing the conditional that failed), * this can't just wrap RETURN_ERROR(). */ #define RETURN_ERROR_IF(cond, err, ...) \ if (cond) { \ RAWLOG(3, "%s:%d: ERROR!: check %s failed, returning %s", \ __FILE__, __LINE__, ZSTD_QUOTE(cond), ZSTD_QUOTE(ERROR(err))); \ _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ RAWLOG(3, ": " __VA_ARGS__); \ RAWLOG(3, "\n"); \ return ERROR(err); \ } /** * Unconditionally return the specified error. * * In debug modes, prints additional information. */ #define RETURN_ERROR(err, ...) \ do { \ RAWLOG(3, "%s:%d: ERROR!: unconditional check failed, returning %s", \ __FILE__, __LINE__, ZSTD_QUOTE(ERROR(err))); \ _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ RAWLOG(3, ": " __VA_ARGS__); \ RAWLOG(3, "\n"); \ return ERROR(err); \ } while(0); /** * If the provided expression evaluates to an error code, returns that error code. * * In debug modes, prints additional information. */ #define FORWARD_IF_ERROR(err, ...) \ do { \ size_t const err_code = (err); \ if (ERR_isError(err_code)) { \ RAWLOG(3, "%s:%d: ERROR!: forwarding error in %s: %s", \ __FILE__, __LINE__, ZSTD_QUOTE(err), ERR_getErrorName(err_code)); \ _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ RAWLOG(3, ": " __VA_ARGS__); \ RAWLOG(3, "\n"); \ return err_code; \ } \ } while(0); /*-************************************* * Common constants ***************************************/ #define ZSTD_OPT_NUM (1<<12) #define ZSTD_REP_NUM 3 /* number of repcodes */ #define ZSTD_REP_MOVE (ZSTD_REP_NUM-1) static const U32 repStartValue[ZSTD_REP_NUM] = { 1, 4, 8 }; #define KB *(1 <<10) #define MB *(1 <<20) #define GB *(1U<<30) #define BIT7 128 #define BIT6 64 #define BIT5 32 #define BIT4 16 #define BIT1 2 #define BIT0 1 #define ZSTD_WINDOWLOG_ABSOLUTEMIN 10 static const size_t ZSTD_fcs_fieldSize[4] = { 0, 2, 4, 8 }; static const size_t ZSTD_did_fieldSize[4] = { 0, 1, 2, 4 }; #define ZSTD_FRAMEIDSIZE 4 /* magic number size */ #define ZSTD_BLOCKHEADERSIZE 3 /* C standard doesn't allow `static const` variable to be init using another `static const` variable */ static const size_t ZSTD_blockHeaderSize = ZSTD_BLOCKHEADERSIZE; typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e; #define ZSTD_FRAMECHECKSUMSIZE 4 #define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */ #define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */ #define HufLog 12 typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingType_e; #define LONGNBSEQ 0x7F00 #define MINMATCH 3 #define Litbits 8 #define MaxLit ((1<= 8 || (ovtype == ZSTD_no_overlap && diff <= -WILDCOPY_VECLEN)); if (ovtype == ZSTD_overlap_src_before_dst && diff < WILDCOPY_VECLEN) { /* Handle short offset copies. */ do { COPY8(op, ip) } while (op < oend); } else { assert(diff >= WILDCOPY_VECLEN || diff <= -WILDCOPY_VECLEN); /* Separate out the first COPY16() call because the copy length is * almost certain to be short, so the branches have different * probabilities. Since it is almost certain to be short, only do * one COPY16() in the first call. Then, do two calls per loop since * at that point it is more likely to have a high trip count. */ #ifndef __aarch64__ do { COPY16(op, ip); } while (op < oend); #else COPY16(op, ip); if (op >= oend) return; do { COPY16(op, ip); COPY16(op, ip); } while (op < oend); #endif } } MEM_STATIC size_t ZSTD_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize) { size_t const length = MIN(dstCapacity, srcSize); if (length > 0) { memcpy(dst, src, length); } return length; } /* define "workspace is too large" as this number of times larger than needed */ #define ZSTD_WORKSPACETOOLARGE_FACTOR 3 /* when workspace is continuously too large * during at least this number of times, * context's memory usage is considered wasteful, * because it's sized to handle a worst case scenario which rarely happens. * In which case, resize it down to free some memory */ #define ZSTD_WORKSPACETOOLARGE_MAXDURATION 128 /*-******************************************* * Private declarations *********************************************/ typedef struct seqDef_s { U32 offset; U16 litLength; U16 matchLength; } seqDef; typedef struct { seqDef* sequencesStart; seqDef* sequences; BYTE* litStart; BYTE* lit; BYTE* llCode; BYTE* mlCode; BYTE* ofCode; size_t maxNbSeq; size_t maxNbLit; U32 longLengthID; /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */ U32 longLengthPos; } seqStore_t; typedef struct { U32 litLength; U32 matchLength; } ZSTD_sequenceLength; /** * Returns the ZSTD_sequenceLength for the given sequences. It handles the decoding of long sequences * indicated by longLengthPos and longLengthID, and adds MINMATCH back to matchLength. */ MEM_STATIC ZSTD_sequenceLength ZSTD_getSequenceLength(seqStore_t const* seqStore, seqDef const* seq) { ZSTD_sequenceLength seqLen; seqLen.litLength = seq->litLength; seqLen.matchLength = seq->matchLength + MINMATCH; if (seqStore->longLengthPos == (U32)(seq - seqStore->sequencesStart)) { if (seqStore->longLengthID == 1) { seqLen.litLength += 0xFFFF; } if (seqStore->longLengthID == 2) { seqLen.matchLength += 0xFFFF; } } return seqLen; } /** * Contains the compressed frame size and an upper-bound for the decompressed frame size. * Note: before using `compressedSize`, check for errors using ZSTD_isError(). * similarly, before using `decompressedBound`, check for errors using: * `decompressedBound != ZSTD_CONTENTSIZE_ERROR` */ typedef struct { size_t compressedSize; unsigned long long decompressedBound; } ZSTD_frameSizeInfo; /* decompress & legacy */ const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx); /* compress & dictBuilder */ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr); /* compress, dictBuilder, decodeCorpus (shouldn't get its definition from here) */ /* custom memory allocation functions */ void* ZSTD_malloc(size_t size, ZSTD_customMem customMem); void* ZSTD_calloc(size_t size, ZSTD_customMem customMem); void ZSTD_free(void* ptr, ZSTD_customMem customMem); MEM_STATIC U32 ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus */ { assert(val != 0); { # if defined(_MSC_VER) /* Visual */ unsigned long r=0; return _BitScanReverse(&r, val) ? (unsigned)r : 0; # elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */ return __builtin_clz (val) ^ 31; # elif defined(__ICCARM__) /* IAR Intrinsic */ return 31 - __CLZ(val); # else /* Software version */ static const U32 DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; U32 v = val; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; return DeBruijnClz[(v * 0x07C4ACDDU) >> 27]; # endif } } /* ZSTD_invalidateRepCodes() : * ensures next compression will not use repcodes from previous block. * Note : only works with regular variant; * do not use with extDict variant ! */ void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx); /* zstdmt, adaptive_compression (shouldn't get this definition from here) */ typedef struct { blockType_e blockType; U32 lastBlock; U32 origSize; } blockProperties_t; /* declared here for decompress and fullbench */ /*! ZSTD_getcBlockSize() : * Provides the size of compressed block from block header `src` */ /* Used by: decompress, fullbench (does not get its definition from here) */ size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* bpPtr); /*! ZSTD_decodeSeqHeaders() : * decode sequence header from src */ /* Used by: decompress, fullbench (does not get its definition from here) */ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, const void* src, size_t srcSize); #if defined (__cplusplus) } #endif #endif /* ZSTD_CCOMMON_H_MODULE */ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1810966 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/0000755000076500000240000000000014601577064021506 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/fse_compress.c0000644000076500000240000006345214601576577024364 0ustar00twstaff/* ****************************************************************** * FSE : Finite State Entropy encoder * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy * - Public forum : https://groups.google.com/forum/#!forum/lz4c * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. ****************************************************************** */ /* ************************************************************** * Includes ****************************************************************/ #include /* malloc, free, qsort */ #include /* memcpy, memset */ #include "../common/compiler.h" #include "../common/mem.h" /* U32, U16, etc. */ #include "../common/debug.h" /* assert, DEBUGLOG */ #include "hist.h" /* HIST_count_wksp */ #include "../common/bitstream.h" #define FSE_STATIC_LINKING_ONLY #include "../common/fse.h" #include "../common/error_private.h" /* ************************************************************** * Error Management ****************************************************************/ #define FSE_isError ERR_isError /* ************************************************************** * Templates ****************************************************************/ /* designed to be included for type-specific functions (template emulation in C) Objective is to write these functions only once, for improved maintenance */ /* safety checks */ #ifndef FSE_FUNCTION_EXTENSION # error "FSE_FUNCTION_EXTENSION must be defined" #endif #ifndef FSE_FUNCTION_TYPE # error "FSE_FUNCTION_TYPE must be defined" #endif /* Function names */ #define FSE_CAT(X,Y) X##Y #define FSE_FUNCTION_NAME(X,Y) FSE_CAT(X,Y) #define FSE_TYPE_NAME(X,Y) FSE_CAT(X,Y) /* Function templates */ /* FSE_buildCTable_wksp() : * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`). * wkspSize should be sized to handle worst case situation, which is `1<>1 : 1) ; FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT); U32 const step = FSE_TABLESTEP(tableSize); U32 cumul[FSE_MAX_SYMBOL_VALUE+2]; FSE_FUNCTION_TYPE* const tableSymbol = (FSE_FUNCTION_TYPE*)workSpace; U32 highThreshold = tableSize-1; /* CTable header */ if (((size_t)1 << tableLog) * sizeof(FSE_FUNCTION_TYPE) > wkspSize) return ERROR(tableLog_tooLarge); tableU16[-2] = (U16) tableLog; tableU16[-1] = (U16) maxSymbolValue; assert(tableLog < 16); /* required for threshold strategy to work */ /* For explanations on how to distribute symbol values over the table : * http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */ #ifdef __clang_analyzer__ memset(tableSymbol, 0, sizeof(*tableSymbol) * tableSize); /* useless initialization, just to keep scan-build happy */ #endif /* symbol start positions */ { U32 u; cumul[0] = 0; for (u=1; u <= maxSymbolValue+1; u++) { if (normalizedCounter[u-1]==-1) { /* Low proba symbol */ cumul[u] = cumul[u-1] + 1; tableSymbol[highThreshold--] = (FSE_FUNCTION_TYPE)(u-1); } else { cumul[u] = cumul[u-1] + normalizedCounter[u-1]; } } cumul[maxSymbolValue+1] = tableSize+1; } /* Spread symbols */ { U32 position = 0; U32 symbol; for (symbol=0; symbol<=maxSymbolValue; symbol++) { int nbOccurrences; int const freq = normalizedCounter[symbol]; for (nbOccurrences=0; nbOccurrences highThreshold) position = (position + step) & tableMask; /* Low proba area */ } } assert(position==0); /* Must have initialized all positions */ } /* Build table */ { U32 u; for (u=0; u> 3) + 3; return maxSymbolValue ? maxHeaderSize : FSE_NCOUNTBOUND; /* maxSymbolValue==0 ? use default */ } static size_t FSE_writeNCount_generic (void* header, size_t headerBufferSize, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, unsigned writeIsSafe) { BYTE* const ostart = (BYTE*) header; BYTE* out = ostart; BYTE* const oend = ostart + headerBufferSize; int nbBits; const int tableSize = 1 << tableLog; int remaining; int threshold; U32 bitStream = 0; int bitCount = 0; unsigned symbol = 0; unsigned const alphabetSize = maxSymbolValue + 1; int previousIs0 = 0; /* Table Size */ bitStream += (tableLog-FSE_MIN_TABLELOG) << bitCount; bitCount += 4; /* Init */ remaining = tableSize+1; /* +1 for extra accuracy */ threshold = tableSize; nbBits = tableLog+1; while ((symbol < alphabetSize) && (remaining>1)) { /* stops at 1 */ if (previousIs0) { unsigned start = symbol; while ((symbol < alphabetSize) && !normalizedCounter[symbol]) symbol++; if (symbol == alphabetSize) break; /* incorrect distribution */ while (symbol >= start+24) { start+=24; bitStream += 0xFFFFU << bitCount; if ((!writeIsSafe) && (out > oend-2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ out[0] = (BYTE) bitStream; out[1] = (BYTE)(bitStream>>8); out+=2; bitStream>>=16; } while (symbol >= start+3) { start+=3; bitStream += 3 << bitCount; bitCount += 2; } bitStream += (symbol-start) << bitCount; bitCount += 2; if (bitCount>16) { if ((!writeIsSafe) && (out > oend - 2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ out[0] = (BYTE)bitStream; out[1] = (BYTE)(bitStream>>8); out += 2; bitStream >>= 16; bitCount -= 16; } } { int count = normalizedCounter[symbol++]; int const max = (2*threshold-1) - remaining; remaining -= count < 0 ? -count : count; count++; /* +1 for extra accuracy */ if (count>=threshold) count += max; /* [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ */ bitStream += count << bitCount; bitCount += nbBits; bitCount -= (count>=1; } } if (bitCount>16) { if ((!writeIsSafe) && (out > oend - 2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ out[0] = (BYTE)bitStream; out[1] = (BYTE)(bitStream>>8); out += 2; bitStream >>= 16; bitCount -= 16; } } if (remaining != 1) return ERROR(GENERIC); /* incorrect normalized distribution */ assert(symbol <= alphabetSize); /* flush remaining bitStream */ if ((!writeIsSafe) && (out > oend - 2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ out[0] = (BYTE)bitStream; out[1] = (BYTE)(bitStream>>8); out+= (bitCount+7) /8; return (out-ostart); } size_t FSE_writeNCount (void* buffer, size_t bufferSize, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) { if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); /* Unsupported */ if (tableLog < FSE_MIN_TABLELOG) return ERROR(GENERIC); /* Unsupported */ if (bufferSize < FSE_NCountWriteBound(maxSymbolValue, tableLog)) return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 0); return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 1 /* write in buffer is safe */); } /*-************************************************************** * FSE Compression Code ****************************************************************/ FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog) { size_t size; if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX; size = FSE_CTABLE_SIZE_U32 (tableLog, maxSymbolValue) * sizeof(U32); return (FSE_CTable*)malloc(size); } void FSE_freeCTable (FSE_CTable* ct) { free(ct); } /* provides the minimum logSize to safely represent a distribution */ static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue) { U32 minBitsSrc = BIT_highbit32((U32)(srcSize)) + 1; U32 minBitsSymbols = BIT_highbit32(maxSymbolValue) + 2; U32 minBits = minBitsSrc < minBitsSymbols ? minBitsSrc : minBitsSymbols; assert(srcSize > 1); /* Not supported, RLE should be used instead */ return minBits; } unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus) { U32 maxBitsSrc = BIT_highbit32((U32)(srcSize - 1)) - minus; U32 tableLog = maxTableLog; U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue); assert(srcSize > 1); /* Not supported, RLE should be used instead */ if (tableLog==0) tableLog = FSE_DEFAULT_TABLELOG; if (maxBitsSrc < tableLog) tableLog = maxBitsSrc; /* Accuracy can be reduced */ if (minBits > tableLog) tableLog = minBits; /* Need a minimum to safely represent all symbol values */ if (tableLog < FSE_MIN_TABLELOG) tableLog = FSE_MIN_TABLELOG; if (tableLog > FSE_MAX_TABLELOG) tableLog = FSE_MAX_TABLELOG; return tableLog; } unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue) { return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 2); } /* Secondary normalization method. To be used when primary method fails. */ static size_t FSE_normalizeM2(short* norm, U32 tableLog, const unsigned* count, size_t total, U32 maxSymbolValue) { short const NOT_YET_ASSIGNED = -2; U32 s; U32 distributed = 0; U32 ToDistribute; /* Init */ U32 const lowThreshold = (U32)(total >> tableLog); U32 lowOne = (U32)((total * 3) >> (tableLog + 1)); for (s=0; s<=maxSymbolValue; s++) { if (count[s] == 0) { norm[s]=0; continue; } if (count[s] <= lowThreshold) { norm[s] = -1; distributed++; total -= count[s]; continue; } if (count[s] <= lowOne) { norm[s] = 1; distributed++; total -= count[s]; continue; } norm[s]=NOT_YET_ASSIGNED; } ToDistribute = (1 << tableLog) - distributed; if (ToDistribute == 0) return 0; if ((total / ToDistribute) > lowOne) { /* risk of rounding to zero */ lowOne = (U32)((total * 3) / (ToDistribute * 2)); for (s=0; s<=maxSymbolValue; s++) { if ((norm[s] == NOT_YET_ASSIGNED) && (count[s] <= lowOne)) { norm[s] = 1; distributed++; total -= count[s]; continue; } } ToDistribute = (1 << tableLog) - distributed; } if (distributed == maxSymbolValue+1) { /* all values are pretty poor; probably incompressible data (should have already been detected); find max, then give all remaining points to max */ U32 maxV = 0, maxC = 0; for (s=0; s<=maxSymbolValue; s++) if (count[s] > maxC) { maxV=s; maxC=count[s]; } norm[maxV] += (short)ToDistribute; return 0; } if (total == 0) { /* all of the symbols were low enough for the lowOne or lowThreshold */ for (s=0; ToDistribute > 0; s = (s+1)%(maxSymbolValue+1)) if (norm[s] > 0) { ToDistribute--; norm[s]++; } return 0; } { U64 const vStepLog = 62 - tableLog; U64 const mid = (1ULL << (vStepLog-1)) - 1; U64 const rStep = ((((U64)1<> vStepLog); U32 const sEnd = (U32)(end >> vStepLog); U32 const weight = sEnd - sStart; if (weight < 1) return ERROR(GENERIC); norm[s] = (short)weight; tmpTotal = end; } } } return 0; } size_t FSE_normalizeCount (short* normalizedCounter, unsigned tableLog, const unsigned* count, size_t total, unsigned maxSymbolValue) { /* Sanity checks */ if (tableLog==0) tableLog = FSE_DEFAULT_TABLELOG; if (tableLog < FSE_MIN_TABLELOG) return ERROR(GENERIC); /* Unsupported size */ if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); /* Unsupported size */ if (tableLog < FSE_minTableLog(total, maxSymbolValue)) return ERROR(GENERIC); /* Too small tableLog, compression potentially impossible */ { static U32 const rtbTable[] = { 0, 473195, 504333, 520860, 550000, 700000, 750000, 830000 }; U64 const scale = 62 - tableLog; U64 const step = ((U64)1<<62) / total; /* <== here, one division ! */ U64 const vStep = 1ULL<<(scale-20); int stillToDistribute = 1<> tableLog); for (s=0; s<=maxSymbolValue; s++) { if (count[s] == total) return 0; /* rle special case */ if (count[s] == 0) { normalizedCounter[s]=0; continue; } if (count[s] <= lowThreshold) { normalizedCounter[s] = -1; stillToDistribute--; } else { short proba = (short)((count[s]*step) >> scale); if (proba<8) { U64 restToBeat = vStep * rtbTable[proba]; proba += (count[s]*step) - ((U64)proba< restToBeat; } if (proba > largestP) { largestP=proba; largest=s; } normalizedCounter[s] = proba; stillToDistribute -= proba; } } if (-stillToDistribute >= (normalizedCounter[largest] >> 1)) { /* corner case, need another normalization method */ size_t const errorCode = FSE_normalizeM2(normalizedCounter, tableLog, count, total, maxSymbolValue); if (FSE_isError(errorCode)) return errorCode; } else normalizedCounter[largest] += (short)stillToDistribute; } #if 0 { /* Print Table (debug) */ U32 s; U32 nTotal = 0; for (s=0; s<=maxSymbolValue; s++) RAWLOG(2, "%3i: %4i \n", s, normalizedCounter[s]); for (s=0; s<=maxSymbolValue; s++) nTotal += abs(normalizedCounter[s]); if (nTotal != (1U<>1); /* assumption : tableLog >= 1 */ FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT); unsigned s; /* Sanity checks */ if (nbBits < 1) return ERROR(GENERIC); /* min size */ /* header */ tableU16[-2] = (U16) nbBits; tableU16[-1] = (U16) maxSymbolValue; /* Build table */ for (s=0; s FSE_MAX_TABLELOG*4+7 ) && (srcSize & 2)) { /* test bit 2 */ FSE_encodeSymbol(&bitC, &CState2, *--ip); FSE_encodeSymbol(&bitC, &CState1, *--ip); FSE_FLUSHBITS(&bitC); } /* 2 or 4 encoding per loop */ while ( ip>istart ) { FSE_encodeSymbol(&bitC, &CState2, *--ip); if (sizeof(bitC.bitContainer)*8 < FSE_MAX_TABLELOG*2+7 ) /* this test must be static */ FSE_FLUSHBITS(&bitC); FSE_encodeSymbol(&bitC, &CState1, *--ip); if (sizeof(bitC.bitContainer)*8 > FSE_MAX_TABLELOG*4+7 ) { /* this test must be static */ FSE_encodeSymbol(&bitC, &CState2, *--ip); FSE_encodeSymbol(&bitC, &CState1, *--ip); } FSE_FLUSHBITS(&bitC); } FSE_flushCState(&bitC, &CState2); FSE_flushCState(&bitC, &CState1); return BIT_closeCStream(&bitC); } size_t FSE_compress_usingCTable (void* dst, size_t dstSize, const void* src, size_t srcSize, const FSE_CTable* ct) { unsigned const fast = (dstSize >= FSE_BLOCKBOUND(srcSize)); if (fast) return FSE_compress_usingCTable_generic(dst, dstSize, src, srcSize, ct, 1); else return FSE_compress_usingCTable_generic(dst, dstSize, src, srcSize, ct, 0); } size_t FSE_compressBound(size_t size) { return FSE_COMPRESSBOUND(size); } /* FSE_compress_wksp() : * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`). * `wkspSize` size must be `(1< not compressible */ if (maxCount < (srcSize >> 7)) return 0; /* Heuristic : not compressible enough */ } tableLog = FSE_optimalTableLog(tableLog, srcSize, maxSymbolValue); CHECK_F( FSE_normalizeCount(norm, tableLog, count, srcSize, maxSymbolValue) ); /* Write table description header */ { CHECK_V_F(nc_err, FSE_writeNCount(op, oend-op, norm, maxSymbolValue, tableLog) ); op += nc_err; } /* Compress */ CHECK_F( FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, scratchBuffer, scratchBufferSize) ); { CHECK_V_F(cSize, FSE_compress_usingCTable(op, oend - op, src, srcSize, CTable) ); if (cSize == 0) return 0; /* not enough space for compressed data */ op += cSize; } /* check compressibility */ if ( (size_t)(op-ostart) >= srcSize-1 ) return 0; return op-ostart; } typedef struct { FSE_CTable CTable_max[FSE_CTABLE_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)]; BYTE scratchBuffer[1 << FSE_MAX_TABLELOG]; } fseWkspMax_t; size_t FSE_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog) { fseWkspMax_t scratchBuffer; DEBUG_STATIC_ASSERT(sizeof(scratchBuffer) >= FSE_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)); /* compilation failures here means scratchBuffer is not large enough */ if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); return FSE_compress_wksp(dst, dstCapacity, src, srcSize, maxSymbolValue, tableLog, &scratchBuffer, sizeof(scratchBuffer)); } size_t FSE_compress (void* dst, size_t dstCapacity, const void* src, size_t srcSize) { return FSE_compress2(dst, dstCapacity, src, srcSize, FSE_MAX_SYMBOL_VALUE, FSE_DEFAULT_TABLELOG); } #endif /* FSE_COMMONDEFS_ONLY */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/hist.c0000644000076500000240000001652314601576577022640 0ustar00twstaff/* ****************************************************************** * hist : Histogram functions * part of Finite State Entropy project * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy * - Public forum : https://groups.google.com/forum/#!forum/lz4c * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. ****************************************************************** */ /* --- dependencies --- */ #include "../common/mem.h" /* U32, BYTE, etc. */ #include "../common/debug.h" /* assert, DEBUGLOG */ #include "../common/error_private.h" /* ERROR */ #include "hist.h" /* --- Error management --- */ unsigned HIST_isError(size_t code) { return ERR_isError(code); } /*-************************************************************** * Histogram functions ****************************************************************/ unsigned HIST_count_simple(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize) { const BYTE* ip = (const BYTE*)src; const BYTE* const end = ip + srcSize; unsigned maxSymbolValue = *maxSymbolValuePtr; unsigned largestCount=0; memset(count, 0, (maxSymbolValue+1) * sizeof(*count)); if (srcSize==0) { *maxSymbolValuePtr = 0; return 0; } while (ip largestCount) largestCount = count[s]; } return largestCount; } typedef enum { trustInput, checkMaxSymbolValue } HIST_checkInput_e; /* HIST_count_parallel_wksp() : * store histogram into 4 intermediate tables, recombined at the end. * this design makes better use of OoO cpus, * and is noticeably faster when some values are heavily repeated. * But it needs some additional workspace for intermediate tables. * `workSpace` size must be a table of size >= HIST_WKSP_SIZE_U32. * @return : largest histogram frequency, * or an error code (notably when histogram would be larger than *maxSymbolValuePtr). */ static size_t HIST_count_parallel_wksp( unsigned* count, unsigned* maxSymbolValuePtr, const void* source, size_t sourceSize, HIST_checkInput_e check, U32* const workSpace) { const BYTE* ip = (const BYTE*)source; const BYTE* const iend = ip+sourceSize; unsigned maxSymbolValue = *maxSymbolValuePtr; unsigned max=0; U32* const Counting1 = workSpace; U32* const Counting2 = Counting1 + 256; U32* const Counting3 = Counting2 + 256; U32* const Counting4 = Counting3 + 256; memset(workSpace, 0, 4*256*sizeof(unsigned)); /* safety checks */ if (!sourceSize) { memset(count, 0, maxSymbolValue + 1); *maxSymbolValuePtr = 0; return 0; } if (!maxSymbolValue) maxSymbolValue = 255; /* 0 == default */ /* by stripes of 16 bytes */ { U32 cached = MEM_read32(ip); ip += 4; while (ip < iend-15) { U32 c = cached; cached = MEM_read32(ip); ip += 4; Counting1[(BYTE) c ]++; Counting2[(BYTE)(c>>8) ]++; Counting3[(BYTE)(c>>16)]++; Counting4[ c>>24 ]++; c = cached; cached = MEM_read32(ip); ip += 4; Counting1[(BYTE) c ]++; Counting2[(BYTE)(c>>8) ]++; Counting3[(BYTE)(c>>16)]++; Counting4[ c>>24 ]++; c = cached; cached = MEM_read32(ip); ip += 4; Counting1[(BYTE) c ]++; Counting2[(BYTE)(c>>8) ]++; Counting3[(BYTE)(c>>16)]++; Counting4[ c>>24 ]++; c = cached; cached = MEM_read32(ip); ip += 4; Counting1[(BYTE) c ]++; Counting2[(BYTE)(c>>8) ]++; Counting3[(BYTE)(c>>16)]++; Counting4[ c>>24 ]++; } ip-=4; } /* finish last symbols */ while (ipmaxSymbolValue; s--) { Counting1[s] += Counting2[s] + Counting3[s] + Counting4[s]; if (Counting1[s]) return ERROR(maxSymbolValue_tooSmall); } } { U32 s; if (maxSymbolValue > 255) maxSymbolValue = 255; for (s=0; s<=maxSymbolValue; s++) { count[s] = Counting1[s] + Counting2[s] + Counting3[s] + Counting4[s]; if (count[s] > max) max = count[s]; } } while (!count[maxSymbolValue]) maxSymbolValue--; *maxSymbolValuePtr = maxSymbolValue; return (size_t)max; } /* HIST_countFast_wksp() : * Same as HIST_countFast(), but using an externally provided scratch buffer. * `workSpace` is a writable buffer which must be 4-bytes aligned, * `workSpaceSize` must be >= HIST_WKSP_SIZE */ size_t HIST_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr, const void* source, size_t sourceSize, void* workSpace, size_t workSpaceSize) { if (sourceSize < 1500) /* heuristic threshold */ return HIST_count_simple(count, maxSymbolValuePtr, source, sourceSize); if ((size_t)workSpace & 3) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */ if (workSpaceSize < HIST_WKSP_SIZE) return ERROR(workSpace_tooSmall); return HIST_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, trustInput, (U32*)workSpace); } /* fast variant (unsafe : won't check if src contains values beyond count[] limit) */ size_t HIST_countFast(unsigned* count, unsigned* maxSymbolValuePtr, const void* source, size_t sourceSize) { unsigned tmpCounters[HIST_WKSP_SIZE_U32]; return HIST_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, tmpCounters, sizeof(tmpCounters)); } /* HIST_count_wksp() : * Same as HIST_count(), but using an externally provided scratch buffer. * `workSpace` size must be table of >= HIST_WKSP_SIZE_U32 unsigned */ size_t HIST_count_wksp(unsigned* count, unsigned* maxSymbolValuePtr, const void* source, size_t sourceSize, void* workSpace, size_t workSpaceSize) { if ((size_t)workSpace & 3) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */ if (workSpaceSize < HIST_WKSP_SIZE) return ERROR(workSpace_tooSmall); if (*maxSymbolValuePtr < 255) return HIST_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, checkMaxSymbolValue, (U32*)workSpace); *maxSymbolValuePtr = 255; return HIST_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, workSpace, workSpaceSize); } size_t HIST_count(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize) { unsigned tmpCounters[HIST_WKSP_SIZE_U32]; return HIST_count_wksp(count, maxSymbolValuePtr, src, srcSize, tmpCounters, sizeof(tmpCounters)); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/hist.h0000644000076500000240000000655514601576577022651 0ustar00twstaff/* ****************************************************************** * hist : Histogram functions * part of Finite State Entropy project * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy * - Public forum : https://groups.google.com/forum/#!forum/lz4c * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. ****************************************************************** */ /* --- dependencies --- */ #include /* size_t */ /* --- simple histogram functions --- */ /*! HIST_count(): * Provides the precise count of each byte within a table 'count'. * 'count' is a table of unsigned int, of minimum size (*maxSymbolValuePtr+1). * Updates *maxSymbolValuePtr with actual largest symbol value detected. * @return : count of the most frequent symbol (which isn't identified). * or an error code, which can be tested using HIST_isError(). * note : if return == srcSize, there is only one symbol. */ size_t HIST_count(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize); unsigned HIST_isError(size_t code); /**< tells if a return value is an error code */ /* --- advanced histogram functions --- */ #define HIST_WKSP_SIZE_U32 1024 #define HIST_WKSP_SIZE (HIST_WKSP_SIZE_U32 * sizeof(unsigned)) /** HIST_count_wksp() : * Same as HIST_count(), but using an externally provided scratch buffer. * Benefit is this function will use very little stack space. * `workSpace` is a writable buffer which must be 4-bytes aligned, * `workSpaceSize` must be >= HIST_WKSP_SIZE */ size_t HIST_count_wksp(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, void* workSpace, size_t workSpaceSize); /** HIST_countFast() : * same as HIST_count(), but blindly trusts that all byte values within src are <= *maxSymbolValuePtr. * This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr` */ size_t HIST_countFast(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize); /** HIST_countFast_wksp() : * Same as HIST_countFast(), but using an externally provided scratch buffer. * `workSpace` is a writable buffer which must be 4-bytes aligned, * `workSpaceSize` must be >= HIST_WKSP_SIZE */ size_t HIST_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, void* workSpace, size_t workSpaceSize); /*! HIST_count_simple() : * Same as HIST_countFast(), this function is unsafe, * and will segfault if any value within `src` is `> *maxSymbolValuePtr`. * It is also a bit slower for large inputs. * However, it does not need any additional memory (not even on stack). * @return : count of the most frequent symbol. * Note this function doesn't produce any error (i.e. it must succeed). */ unsigned HIST_count_simple(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize); ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/huf_compress.c0000644000076500000240000007766114601576577024400 0ustar00twstaff/* ****************************************************************** * Huffman encoder, part of New Generation Entropy library * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy * - Public forum : https://groups.google.com/forum/#!forum/lz4c * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. ****************************************************************** */ /* ************************************************************** * Compiler specifics ****************************************************************/ #ifdef _MSC_VER /* Visual Studio */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #endif /* ************************************************************** * Includes ****************************************************************/ #include /* memcpy, memset */ #include /* printf (debug) */ #include "../common/compiler.h" #include "../common/bitstream.h" #include "hist.h" #define FSE_STATIC_LINKING_ONLY /* FSE_optimalTableLog_internal */ #include "../common/fse.h" /* header compression */ #define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "../common/error_private.h" /* ************************************************************** * Error Management ****************************************************************/ #define HUF_isError ERR_isError #define HUF_STATIC_ASSERT(c) DEBUG_STATIC_ASSERT(c) /* use only *after* variable declarations */ /* ************************************************************** * Utils ****************************************************************/ unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue) { return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1); } /* ******************************************************* * HUF : Huffman block compression *********************************************************/ /* HUF_compressWeights() : * Same as FSE_compress(), but dedicated to huff0's weights compression. * The use case needs much less stack memory. * Note : all elements within weightTable are supposed to be <= HUF_TABLELOG_MAX. */ #define MAX_FSE_TABLELOG_FOR_HUFF_HEADER 6 static size_t HUF_compressWeights (void* dst, size_t dstSize, const void* weightTable, size_t wtSize) { BYTE* const ostart = (BYTE*) dst; BYTE* op = ostart; BYTE* const oend = ostart + dstSize; unsigned maxSymbolValue = HUF_TABLELOG_MAX; U32 tableLog = MAX_FSE_TABLELOG_FOR_HUFF_HEADER; FSE_CTable CTable[FSE_CTABLE_SIZE_U32(MAX_FSE_TABLELOG_FOR_HUFF_HEADER, HUF_TABLELOG_MAX)]; BYTE scratchBuffer[1< not compressible */ } tableLog = FSE_optimalTableLog(tableLog, wtSize, maxSymbolValue); CHECK_F( FSE_normalizeCount(norm, tableLog, count, wtSize, maxSymbolValue) ); /* Write table description header */ { CHECK_V_F(hSize, FSE_writeNCount(op, (size_t)(oend-op), norm, maxSymbolValue, tableLog) ); op += hSize; } /* Compress */ CHECK_F( FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, scratchBuffer, sizeof(scratchBuffer)) ); { CHECK_V_F(cSize, FSE_compress_usingCTable(op, (size_t)(oend - op), weightTable, wtSize, CTable) ); if (cSize == 0) return 0; /* not enough space for compressed data */ op += cSize; } return (size_t)(op-ostart); } struct HUF_CElt_s { U16 val; BYTE nbBits; }; /* typedef'd to HUF_CElt within "huf.h" */ /*! HUF_writeCTable() : `CTable` : Huffman tree to save, using huf representation. @return : size of saved CTable */ size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog) { BYTE bitsToWeight[HUF_TABLELOG_MAX + 1]; /* precomputed conversion table */ BYTE huffWeight[HUF_SYMBOLVALUE_MAX]; BYTE* op = (BYTE*)dst; U32 n; /* check conditions */ if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge); /* convert to weight */ bitsToWeight[0] = 0; for (n=1; n1) & (hSize < maxSymbolValue/2)) { /* FSE compressed */ op[0] = (BYTE)hSize; return hSize+1; } } /* write raw values as 4-bits (max : 15) */ if (maxSymbolValue > (256-128)) return ERROR(GENERIC); /* should not happen : likely means source cannot be compressed */ if (((maxSymbolValue+1)/2) + 1 > maxDstSize) return ERROR(dstSize_tooSmall); /* not enough space within dst buffer */ op[0] = (BYTE)(128 /*special case*/ + (maxSymbolValue-1)); huffWeight[maxSymbolValue] = 0; /* to be sure it doesn't cause msan issue in final combination */ for (n=0; n HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); if (nbSymbols > *maxSymbolValuePtr+1) return ERROR(maxSymbolValue_tooSmall); /* Prepare base value per rank */ { U32 n, nextRankStart = 0; for (n=1; n<=tableLog; n++) { U32 current = nextRankStart; nextRankStart += (rankVal[n] << (n-1)); rankVal[n] = current; } } /* fill nbBits */ *hasZeroWeights = 0; { U32 n; for (n=0; nn=tableLog+1 */ U16 valPerRank[HUF_TABLELOG_MAX+2] = {0}; { U32 n; for (n=0; n0; n--) { /* start at n=tablelog <-> w=1 */ valPerRank[n] = min; /* get starting value within each rank */ min += nbPerRank[n]; min >>= 1; } } /* assign value within rank, symbol order */ { U32 n; for (n=0; n maxNbBits */ /* there are several too large elements (at least >= 2) */ { int totalCost = 0; const U32 baseCost = 1 << (largestBits - maxNbBits); int n = (int)lastNonNull; while (huffNode[n].nbBits > maxNbBits) { totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits)); huffNode[n].nbBits = (BYTE)maxNbBits; n --; } /* n stops at huffNode[n].nbBits <= maxNbBits */ while (huffNode[n].nbBits == maxNbBits) n--; /* n end at index of smallest symbol using < maxNbBits */ /* renorm totalCost */ totalCost >>= (largestBits - maxNbBits); /* note : totalCost is necessarily a multiple of baseCost */ /* repay normalized cost */ { U32 const noSymbol = 0xF0F0F0F0; U32 rankLast[HUF_TABLELOG_MAX+2]; /* Get pos of last (smallest) symbol per rank */ memset(rankLast, 0xF0, sizeof(rankLast)); { U32 currentNbBits = maxNbBits; int pos; for (pos=n ; pos >= 0; pos--) { if (huffNode[pos].nbBits >= currentNbBits) continue; currentNbBits = huffNode[pos].nbBits; /* < maxNbBits */ rankLast[maxNbBits-currentNbBits] = (U32)pos; } } while (totalCost > 0) { U32 nBitsToDecrease = BIT_highbit32((U32)totalCost) + 1; for ( ; nBitsToDecrease > 1; nBitsToDecrease--) { U32 const highPos = rankLast[nBitsToDecrease]; U32 const lowPos = rankLast[nBitsToDecrease-1]; if (highPos == noSymbol) continue; if (lowPos == noSymbol) break; { U32 const highTotal = huffNode[highPos].count; U32 const lowTotal = 2 * huffNode[lowPos].count; if (highTotal <= lowTotal) break; } } /* only triggered when no more rank 1 symbol left => find closest one (note : there is necessarily at least one !) */ /* HUF_MAX_TABLELOG test just to please gcc 5+; but it should not be necessary */ while ((nBitsToDecrease<=HUF_TABLELOG_MAX) && (rankLast[nBitsToDecrease] == noSymbol)) nBitsToDecrease ++; totalCost -= 1 << (nBitsToDecrease-1); if (rankLast[nBitsToDecrease-1] == noSymbol) rankLast[nBitsToDecrease-1] = rankLast[nBitsToDecrease]; /* this rank is no longer empty */ huffNode[rankLast[nBitsToDecrease]].nbBits ++; if (rankLast[nBitsToDecrease] == 0) /* special case, reached largest symbol */ rankLast[nBitsToDecrease] = noSymbol; else { rankLast[nBitsToDecrease]--; if (huffNode[rankLast[nBitsToDecrease]].nbBits != maxNbBits-nBitsToDecrease) rankLast[nBitsToDecrease] = noSymbol; /* this rank is now empty */ } } /* while (totalCost > 0) */ while (totalCost < 0) { /* Sometimes, cost correction overshoot */ if (rankLast[1] == noSymbol) { /* special case : no rank 1 symbol (using maxNbBits-1); let's create one from largest rank 0 (using maxNbBits) */ while (huffNode[n].nbBits == maxNbBits) n--; huffNode[n+1].nbBits--; assert(n >= 0); rankLast[1] = (U32)(n+1); totalCost++; continue; } huffNode[ rankLast[1] + 1 ].nbBits--; rankLast[1]++; totalCost ++; } } } /* there are several too large elements (at least >= 2) */ return maxNbBits; } typedef struct { U32 base; U32 current; } rankPos; typedef nodeElt huffNodeTable[HUF_CTABLE_WORKSPACE_SIZE_U32]; #define RANK_POSITION_TABLE_SIZE 32 typedef struct { huffNodeTable huffNodeTbl; rankPos rankPosition[RANK_POSITION_TABLE_SIZE]; } HUF_buildCTable_wksp_tables; static void HUF_sort(nodeElt* huffNode, const unsigned* count, U32 maxSymbolValue, rankPos* rankPosition) { U32 n; memset(rankPosition, 0, sizeof(*rankPosition) * RANK_POSITION_TABLE_SIZE); for (n=0; n<=maxSymbolValue; n++) { U32 r = BIT_highbit32(count[n] + 1); rankPosition[r].base ++; } for (n=30; n>0; n--) rankPosition[n-1].base += rankPosition[n].base; for (n=0; n<32; n++) rankPosition[n].current = rankPosition[n].base; for (n=0; n<=maxSymbolValue; n++) { U32 const c = count[n]; U32 const r = BIT_highbit32(c+1) + 1; U32 pos = rankPosition[r].current++; while ((pos > rankPosition[r].base) && (c > huffNode[pos-1].count)) { huffNode[pos] = huffNode[pos-1]; pos--; } huffNode[pos].count = c; huffNode[pos].byte = (BYTE)n; } } /** HUF_buildCTable_wksp() : * Same as HUF_buildCTable(), but using externally allocated scratch buffer. * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as sizeof(HUF_buildCTable_wksp_tables). */ #define STARTNODE (HUF_SYMBOLVALUE_MAX+1) size_t HUF_buildCTable_wksp (HUF_CElt* tree, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize) { HUF_buildCTable_wksp_tables* const wksp_tables = (HUF_buildCTable_wksp_tables*)workSpace; nodeElt* const huffNode0 = wksp_tables->huffNodeTbl; nodeElt* const huffNode = huffNode0+1; int nonNullRank; int lowS, lowN; int nodeNb = STARTNODE; int n, nodeRoot; /* safety checks */ if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */ if (wkspSize < sizeof(HUF_buildCTable_wksp_tables)) return ERROR(workSpace_tooSmall); if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT; if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge); memset(huffNode0, 0, sizeof(huffNodeTable)); /* sort, decreasing order */ HUF_sort(huffNode, count, maxSymbolValue, wksp_tables->rankPosition); /* init for parents */ nonNullRank = (int)maxSymbolValue; while(huffNode[nonNullRank].count == 0) nonNullRank--; lowS = nonNullRank; nodeRoot = nodeNb + lowS - 1; lowN = nodeNb; huffNode[nodeNb].count = huffNode[lowS].count + huffNode[lowS-1].count; huffNode[lowS].parent = huffNode[lowS-1].parent = (U16)nodeNb; nodeNb++; lowS-=2; for (n=nodeNb; n<=nodeRoot; n++) huffNode[n].count = (U32)(1U<<30); huffNode0[0].count = (U32)(1U<<31); /* fake entry, strong barrier */ /* create parents */ while (nodeNb <= nodeRoot) { int const n1 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++; int const n2 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++; huffNode[nodeNb].count = huffNode[n1].count + huffNode[n2].count; huffNode[n1].parent = huffNode[n2].parent = (U16)nodeNb; nodeNb++; } /* distribute weights (unlimited tree height) */ huffNode[nodeRoot].nbBits = 0; for (n=nodeRoot-1; n>=STARTNODE; n--) huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1; for (n=0; n<=nonNullRank; n++) huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1; /* enforce maxTableLog */ maxNbBits = HUF_setMaxHeight(huffNode, (U32)nonNullRank, maxNbBits); /* fill result into tree (val, nbBits) */ { U16 nbPerRank[HUF_TABLELOG_MAX+1] = {0}; U16 valPerRank[HUF_TABLELOG_MAX+1] = {0}; int const alphabetSize = (int)(maxSymbolValue + 1); if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC); /* check fit into table */ for (n=0; n<=nonNullRank; n++) nbPerRank[huffNode[n].nbBits]++; /* determine stating value per rank */ { U16 min = 0; for (n=(int)maxNbBits; n>0; n--) { valPerRank[n] = min; /* get starting value within each rank */ min += nbPerRank[n]; min >>= 1; } } for (n=0; n> 3; } int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue) { int bad = 0; int s; for (s = 0; s <= (int)maxSymbolValue; ++s) { bad |= (count[s] != 0) & (CTable[s].nbBits == 0); } return !bad; } size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); } FORCE_INLINE_TEMPLATE void HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable) { BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits); } #define HUF_FLUSHBITS(s) BIT_flushBits(s) #define HUF_FLUSHBITS_1(stream) \ if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*2+7) HUF_FLUSHBITS(stream) #define HUF_FLUSHBITS_2(stream) \ if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*4+7) HUF_FLUSHBITS(stream) FORCE_INLINE_TEMPLATE size_t HUF_compress1X_usingCTable_internal_body(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) { const BYTE* ip = (const BYTE*) src; BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstSize; BYTE* op = ostart; size_t n; BIT_CStream_t bitC; /* init */ if (dstSize < 8) return 0; /* not enough space to compress */ { size_t const initErr = BIT_initCStream(&bitC, op, (size_t)(oend-op)); if (HUF_isError(initErr)) return 0; } n = srcSize & ~3; /* join to mod 4 */ switch (srcSize & 3) { case 3 : HUF_encodeSymbol(&bitC, ip[n+ 2], CTable); HUF_FLUSHBITS_2(&bitC); /* fall-through */ case 2 : HUF_encodeSymbol(&bitC, ip[n+ 1], CTable); HUF_FLUSHBITS_1(&bitC); /* fall-through */ case 1 : HUF_encodeSymbol(&bitC, ip[n+ 0], CTable); HUF_FLUSHBITS(&bitC); /* fall-through */ case 0 : /* fall-through */ default: break; } for (; n>0; n-=4) { /* note : n&3==0 at this stage */ HUF_encodeSymbol(&bitC, ip[n- 1], CTable); HUF_FLUSHBITS_1(&bitC); HUF_encodeSymbol(&bitC, ip[n- 2], CTable); HUF_FLUSHBITS_2(&bitC); HUF_encodeSymbol(&bitC, ip[n- 3], CTable); HUF_FLUSHBITS_1(&bitC); HUF_encodeSymbol(&bitC, ip[n- 4], CTable); HUF_FLUSHBITS(&bitC); } return BIT_closeCStream(&bitC); } #if DYNAMIC_BMI2 static TARGET_ATTRIBUTE("bmi2") size_t HUF_compress1X_usingCTable_internal_bmi2(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) { return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable); } static size_t HUF_compress1X_usingCTable_internal_default(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) { return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable); } static size_t HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, const int bmi2) { if (bmi2) { return HUF_compress1X_usingCTable_internal_bmi2(dst, dstSize, src, srcSize, CTable); } return HUF_compress1X_usingCTable_internal_default(dst, dstSize, src, srcSize, CTable); } #else static size_t HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, const int bmi2) { (void)bmi2; return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable); } #endif size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) { return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0); } static size_t HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int bmi2) { size_t const segmentSize = (srcSize+3)/4; /* first 3 segments */ const BYTE* ip = (const BYTE*) src; const BYTE* const iend = ip + srcSize; BYTE* const ostart = (BYTE*) dst; BYTE* const oend = ostart + dstSize; BYTE* op = ostart; if (dstSize < 6 + 1 + 1 + 1 + 8) return 0; /* minimum space to compress successfully */ if (srcSize < 12) return 0; /* no saving possible : too small input */ op += 6; /* jumpTable */ assert(op <= oend); { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) ); if (cSize==0) return 0; assert(cSize <= 65535); MEM_writeLE16(ostart, (U16)cSize); op += cSize; } ip += segmentSize; assert(op <= oend); { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) ); if (cSize==0) return 0; assert(cSize <= 65535); MEM_writeLE16(ostart+2, (U16)cSize); op += cSize; } ip += segmentSize; assert(op <= oend); { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) ); if (cSize==0) return 0; assert(cSize <= 65535); MEM_writeLE16(ostart+4, (U16)cSize); op += cSize; } ip += segmentSize; assert(op <= oend); assert(ip <= iend); { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, (size_t)(iend-ip), CTable, bmi2) ); if (cSize==0) return 0; op += cSize; } return (size_t)(op-ostart); } size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) { return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0); } typedef enum { HUF_singleStream, HUF_fourStreams } HUF_nbStreams_e; static size_t HUF_compressCTable_internal( BYTE* const ostart, BYTE* op, BYTE* const oend, const void* src, size_t srcSize, HUF_nbStreams_e nbStreams, const HUF_CElt* CTable, const int bmi2) { size_t const cSize = (nbStreams==HUF_singleStream) ? HUF_compress1X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, bmi2) : HUF_compress4X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, bmi2); if (HUF_isError(cSize)) { return cSize; } if (cSize==0) { return 0; } /* uncompressible */ op += cSize; /* check compressibility */ assert(op >= ostart); if ((size_t)(op-ostart) >= srcSize-1) { return 0; } return (size_t)(op-ostart); } typedef struct { unsigned count[HUF_SYMBOLVALUE_MAX + 1]; HUF_CElt CTable[HUF_SYMBOLVALUE_MAX + 1]; HUF_buildCTable_wksp_tables buildCTable_wksp; } HUF_compress_tables_t; /* HUF_compress_internal() : * `workSpace` must a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ static size_t HUF_compress_internal (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, HUF_nbStreams_e nbStreams, void* workSpace, size_t wkspSize, HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat, const int bmi2) { HUF_compress_tables_t* const table = (HUF_compress_tables_t*)workSpace; BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstSize; BYTE* op = ostart; HUF_STATIC_ASSERT(sizeof(*table) <= HUF_WORKSPACE_SIZE); /* checks & inits */ if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */ if (wkspSize < HUF_WORKSPACE_SIZE) return ERROR(workSpace_tooSmall); if (!srcSize) return 0; /* Uncompressed */ if (!dstSize) return 0; /* cannot fit anything within dst budget */ if (srcSize > HUF_BLOCKSIZE_MAX) return ERROR(srcSize_wrong); /* current block size limit */ if (huffLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge); if (!maxSymbolValue) maxSymbolValue = HUF_SYMBOLVALUE_MAX; if (!huffLog) huffLog = HUF_TABLELOG_DEFAULT; /* Heuristic : If old table is valid, use it for small inputs */ if (preferRepeat && repeat && *repeat == HUF_repeat_valid) { return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, nbStreams, oldHufTable, bmi2); } /* Scan input and build symbol stats */ { CHECK_V_F(largest, HIST_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, workSpace, wkspSize) ); if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; } /* single symbol, rle */ if (largest <= (srcSize >> 7)+4) return 0; /* heuristic : probably not compressible enough */ } /* Check validity of previous table */ if ( repeat && *repeat == HUF_repeat_check && !HUF_validateCTable(oldHufTable, table->count, maxSymbolValue)) { *repeat = HUF_repeat_none; } /* Heuristic : use existing table for small inputs */ if (preferRepeat && repeat && *repeat != HUF_repeat_none) { return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, nbStreams, oldHufTable, bmi2); } /* Build Huffman Tree */ huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue); { size_t const maxBits = HUF_buildCTable_wksp(table->CTable, table->count, maxSymbolValue, huffLog, &table->buildCTable_wksp, sizeof(table->buildCTable_wksp)); CHECK_F(maxBits); huffLog = (U32)maxBits; /* Zero unused symbols in CTable, so we can check it for validity */ memset(table->CTable + (maxSymbolValue + 1), 0, sizeof(table->CTable) - ((maxSymbolValue + 1) * sizeof(HUF_CElt))); } /* Write table description header */ { CHECK_V_F(hSize, HUF_writeCTable (op, dstSize, table->CTable, maxSymbolValue, huffLog) ); /* Check if using previous huffman table is beneficial */ if (repeat && *repeat != HUF_repeat_none) { size_t const oldSize = HUF_estimateCompressedSize(oldHufTable, table->count, maxSymbolValue); size_t const newSize = HUF_estimateCompressedSize(table->CTable, table->count, maxSymbolValue); if (oldSize <= hSize + newSize || hSize + 12 >= srcSize) { return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, nbStreams, oldHufTable, bmi2); } } /* Use the new huffman table */ if (hSize + 12ul >= srcSize) { return 0; } op += hSize; if (repeat) { *repeat = HUF_repeat_none; } if (oldHufTable) memcpy(oldHufTable, table->CTable, sizeof(table->CTable)); /* Save new table */ } return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, nbStreams, table->CTable, bmi2); } size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize) { return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_singleStream, workSpace, wkspSize, NULL, NULL, 0, 0 /*bmi2*/); } size_t HUF_compress1X_repeat (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2) { return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_singleStream, workSpace, wkspSize, hufTable, repeat, preferRepeat, bmi2); } size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog) { unsigned workSpace[HUF_WORKSPACE_SIZE_U32]; return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace)); } /* HUF_compress4X_repeat(): * compress input using 4 streams. * provide workspace to generate compression tables */ size_t HUF_compress4X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize) { return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_fourStreams, workSpace, wkspSize, NULL, NULL, 0, 0 /*bmi2*/); } /* HUF_compress4X_repeat(): * compress input using 4 streams. * re-use an existing huffman compression table */ size_t HUF_compress4X_repeat (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2) { return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_fourStreams, workSpace, wkspSize, hufTable, repeat, preferRepeat, bmi2); } size_t HUF_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog) { unsigned workSpace[HUF_WORKSPACE_SIZE_U32]; return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace)); } size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize) { return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_compress.c0000644000076500000240000054504614601576577024577 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /*-************************************* * Dependencies ***************************************/ #include /* INT_MAX */ #include /* memset */ #include "../common/cpu.h" #include "../common/mem.h" #include "hist.h" /* HIST_countFast_wksp */ #define FSE_STATIC_LINKING_ONLY /* FSE_encodeSymbol */ #include "../common/fse.h" #define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "zstd_compress_internal.h" #include "zstd_compress_sequences.h" #include "zstd_compress_literals.h" #include "zstd_fast.h" #include "zstd_double_fast.h" #include "zstd_lazy.h" #include "zstd_opt.h" #include "zstd_ldm.h" #include "zstd_compress_superblock.h" /*-************************************* * Helper functions ***************************************/ /* ZSTD_compressBound() * Note that the result from this function is only compatible with the "normal" * full-block strategy. * When there are a lot of small blocks due to frequent flush in streaming mode * the overhead of headers can make the compressed data to be larger than the * return value of ZSTD_compressBound(). */ size_t ZSTD_compressBound(size_t srcSize) { return ZSTD_COMPRESSBOUND(srcSize); } /*-************************************* * Context memory management ***************************************/ struct ZSTD_CDict_s { const void* dictContent; size_t dictContentSize; U32* entropyWorkspace; /* entropy workspace of HUF_WORKSPACE_SIZE bytes */ ZSTD_cwksp workspace; ZSTD_matchState_t matchState; ZSTD_compressedBlockState_t cBlockState; ZSTD_customMem customMem; U32 dictID; int compressionLevel; /* 0 indicates that advanced API was used to select CDict params */ }; /* typedef'd to ZSTD_CDict within "zstd.h" */ ZSTD_CCtx* ZSTD_createCCtx(void) { return ZSTD_createCCtx_advanced(ZSTD_defaultCMem); } static void ZSTD_initCCtx(ZSTD_CCtx* cctx, ZSTD_customMem memManager) { assert(cctx != NULL); memset(cctx, 0, sizeof(*cctx)); cctx->customMem = memManager; cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid()); { size_t const err = ZSTD_CCtx_reset(cctx, ZSTD_reset_parameters); assert(!ZSTD_isError(err)); (void)err; } } ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem) { ZSTD_STATIC_ASSERT(zcss_init==0); ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN==(0ULL - 1)); if (!customMem.customAlloc ^ !customMem.customFree) return NULL; { ZSTD_CCtx* const cctx = (ZSTD_CCtx*)ZSTD_malloc(sizeof(ZSTD_CCtx), customMem); if (!cctx) return NULL; ZSTD_initCCtx(cctx, customMem); return cctx; } } ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize) { ZSTD_cwksp ws; ZSTD_CCtx* cctx; if (workspaceSize <= sizeof(ZSTD_CCtx)) return NULL; /* minimum size */ if ((size_t)workspace & 7) return NULL; /* must be 8-aligned */ ZSTD_cwksp_init(&ws, workspace, workspaceSize); cctx = (ZSTD_CCtx*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CCtx)); if (cctx == NULL) return NULL; memset(cctx, 0, sizeof(ZSTD_CCtx)); ZSTD_cwksp_move(&cctx->workspace, &ws); cctx->staticSize = workspaceSize; /* statically sized space. entropyWorkspace never moves (but prev/next block swap places) */ if (!ZSTD_cwksp_check_available(&cctx->workspace, HUF_WORKSPACE_SIZE + 2 * sizeof(ZSTD_compressedBlockState_t))) return NULL; cctx->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)ZSTD_cwksp_reserve_object(&cctx->workspace, sizeof(ZSTD_compressedBlockState_t)); cctx->blockState.nextCBlock = (ZSTD_compressedBlockState_t*)ZSTD_cwksp_reserve_object(&cctx->workspace, sizeof(ZSTD_compressedBlockState_t)); cctx->entropyWorkspace = (U32*)ZSTD_cwksp_reserve_object(&cctx->workspace, HUF_WORKSPACE_SIZE); cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid()); return cctx; } /** * Clears and frees all of the dictionaries in the CCtx. */ static void ZSTD_clearAllDicts(ZSTD_CCtx* cctx) { ZSTD_free(cctx->localDict.dictBuffer, cctx->customMem); ZSTD_freeCDict(cctx->localDict.cdict); memset(&cctx->localDict, 0, sizeof(cctx->localDict)); memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict)); cctx->cdict = NULL; } static size_t ZSTD_sizeof_localDict(ZSTD_localDict dict) { size_t const bufferSize = dict.dictBuffer != NULL ? dict.dictSize : 0; size_t const cdictSize = ZSTD_sizeof_CDict(dict.cdict); return bufferSize + cdictSize; } static void ZSTD_freeCCtxContent(ZSTD_CCtx* cctx) { assert(cctx != NULL); assert(cctx->staticSize == 0); ZSTD_clearAllDicts(cctx); #ifdef ZSTD_MULTITHREAD ZSTDMT_freeCCtx(cctx->mtctx); cctx->mtctx = NULL; #endif ZSTD_cwksp_free(&cctx->workspace, cctx->customMem); } size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx) { if (cctx==NULL) return 0; /* support free on NULL */ RETURN_ERROR_IF(cctx->staticSize, memory_allocation, "not compatible with static CCtx"); { int cctxInWorkspace = ZSTD_cwksp_owns_buffer(&cctx->workspace, cctx); ZSTD_freeCCtxContent(cctx); if (!cctxInWorkspace) { ZSTD_free(cctx, cctx->customMem); } } return 0; } static size_t ZSTD_sizeof_mtctx(const ZSTD_CCtx* cctx) { #ifdef ZSTD_MULTITHREAD return ZSTDMT_sizeof_CCtx(cctx->mtctx); #else (void)cctx; return 0; #endif } size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx) { if (cctx==NULL) return 0; /* support sizeof on NULL */ /* cctx may be in the workspace */ return (cctx->workspace.workspace == cctx ? 0 : sizeof(*cctx)) + ZSTD_cwksp_sizeof(&cctx->workspace) + ZSTD_sizeof_localDict(cctx->localDict) + ZSTD_sizeof_mtctx(cctx); } size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs) { return ZSTD_sizeof_CCtx(zcs); /* same object */ } /* private API call, for dictBuilder only */ const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) { return &(ctx->seqStore); } static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams( ZSTD_compressionParameters cParams) { ZSTD_CCtx_params cctxParams; memset(&cctxParams, 0, sizeof(cctxParams)); cctxParams.cParams = cParams; cctxParams.compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */ assert(!ZSTD_checkCParams(cParams)); cctxParams.fParams.contentSizeFlag = 1; return cctxParams; } static ZSTD_CCtx_params* ZSTD_createCCtxParams_advanced( ZSTD_customMem customMem) { ZSTD_CCtx_params* params; if (!customMem.customAlloc ^ !customMem.customFree) return NULL; params = (ZSTD_CCtx_params*)ZSTD_calloc( sizeof(ZSTD_CCtx_params), customMem); if (!params) { return NULL; } params->customMem = customMem; params->compressionLevel = ZSTD_CLEVEL_DEFAULT; params->fParams.contentSizeFlag = 1; return params; } ZSTD_CCtx_params* ZSTD_createCCtxParams(void) { return ZSTD_createCCtxParams_advanced(ZSTD_defaultCMem); } size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params) { if (params == NULL) { return 0; } ZSTD_free(params, params->customMem); return 0; } size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params) { return ZSTD_CCtxParams_init(params, ZSTD_CLEVEL_DEFAULT); } size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel) { RETURN_ERROR_IF(!cctxParams, GENERIC, "NULL pointer!"); memset(cctxParams, 0, sizeof(*cctxParams)); cctxParams->compressionLevel = compressionLevel; cctxParams->fParams.contentSizeFlag = 1; return 0; } size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params) { RETURN_ERROR_IF(!cctxParams, GENERIC, "NULL pointer!"); FORWARD_IF_ERROR( ZSTD_checkCParams(params.cParams) , ""); memset(cctxParams, 0, sizeof(*cctxParams)); assert(!ZSTD_checkCParams(params.cParams)); cctxParams->cParams = params.cParams; cctxParams->fParams = params.fParams; cctxParams->compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */ return 0; } /* ZSTD_assignParamsToCCtxParams() : * params is presumed valid at this stage */ static ZSTD_CCtx_params ZSTD_assignParamsToCCtxParams( const ZSTD_CCtx_params* cctxParams, const ZSTD_parameters* params) { ZSTD_CCtx_params ret = *cctxParams; assert(!ZSTD_checkCParams(params->cParams)); ret.cParams = params->cParams; ret.fParams = params->fParams; ret.compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */ return ret; } ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param) { ZSTD_bounds bounds = { 0, 0, 0 }; switch(param) { case ZSTD_c_compressionLevel: bounds.lowerBound = ZSTD_minCLevel(); bounds.upperBound = ZSTD_maxCLevel(); return bounds; case ZSTD_c_windowLog: bounds.lowerBound = ZSTD_WINDOWLOG_MIN; bounds.upperBound = ZSTD_WINDOWLOG_MAX; return bounds; case ZSTD_c_hashLog: bounds.lowerBound = ZSTD_HASHLOG_MIN; bounds.upperBound = ZSTD_HASHLOG_MAX; return bounds; case ZSTD_c_chainLog: bounds.lowerBound = ZSTD_CHAINLOG_MIN; bounds.upperBound = ZSTD_CHAINLOG_MAX; return bounds; case ZSTD_c_searchLog: bounds.lowerBound = ZSTD_SEARCHLOG_MIN; bounds.upperBound = ZSTD_SEARCHLOG_MAX; return bounds; case ZSTD_c_minMatch: bounds.lowerBound = ZSTD_MINMATCH_MIN; bounds.upperBound = ZSTD_MINMATCH_MAX; return bounds; case ZSTD_c_targetLength: bounds.lowerBound = ZSTD_TARGETLENGTH_MIN; bounds.upperBound = ZSTD_TARGETLENGTH_MAX; return bounds; case ZSTD_c_strategy: bounds.lowerBound = ZSTD_STRATEGY_MIN; bounds.upperBound = ZSTD_STRATEGY_MAX; return bounds; case ZSTD_c_contentSizeFlag: bounds.lowerBound = 0; bounds.upperBound = 1; return bounds; case ZSTD_c_checksumFlag: bounds.lowerBound = 0; bounds.upperBound = 1; return bounds; case ZSTD_c_dictIDFlag: bounds.lowerBound = 0; bounds.upperBound = 1; return bounds; case ZSTD_c_nbWorkers: bounds.lowerBound = 0; #ifdef ZSTD_MULTITHREAD bounds.upperBound = ZSTDMT_NBWORKERS_MAX; #else bounds.upperBound = 0; #endif return bounds; case ZSTD_c_jobSize: bounds.lowerBound = 0; #ifdef ZSTD_MULTITHREAD bounds.upperBound = ZSTDMT_JOBSIZE_MAX; #else bounds.upperBound = 0; #endif return bounds; case ZSTD_c_overlapLog: #ifdef ZSTD_MULTITHREAD bounds.lowerBound = ZSTD_OVERLAPLOG_MIN; bounds.upperBound = ZSTD_OVERLAPLOG_MAX; #else bounds.lowerBound = 0; bounds.upperBound = 0; #endif return bounds; case ZSTD_c_enableLongDistanceMatching: bounds.lowerBound = 0; bounds.upperBound = 1; return bounds; case ZSTD_c_ldmHashLog: bounds.lowerBound = ZSTD_LDM_HASHLOG_MIN; bounds.upperBound = ZSTD_LDM_HASHLOG_MAX; return bounds; case ZSTD_c_ldmMinMatch: bounds.lowerBound = ZSTD_LDM_MINMATCH_MIN; bounds.upperBound = ZSTD_LDM_MINMATCH_MAX; return bounds; case ZSTD_c_ldmBucketSizeLog: bounds.lowerBound = ZSTD_LDM_BUCKETSIZELOG_MIN; bounds.upperBound = ZSTD_LDM_BUCKETSIZELOG_MAX; return bounds; case ZSTD_c_ldmHashRateLog: bounds.lowerBound = ZSTD_LDM_HASHRATELOG_MIN; bounds.upperBound = ZSTD_LDM_HASHRATELOG_MAX; return bounds; /* experimental parameters */ case ZSTD_c_rsyncable: bounds.lowerBound = 0; bounds.upperBound = 1; return bounds; case ZSTD_c_forceMaxWindow : bounds.lowerBound = 0; bounds.upperBound = 1; return bounds; case ZSTD_c_format: ZSTD_STATIC_ASSERT(ZSTD_f_zstd1 < ZSTD_f_zstd1_magicless); bounds.lowerBound = ZSTD_f_zstd1; bounds.upperBound = ZSTD_f_zstd1_magicless; /* note : how to ensure at compile time that this is the highest value enum ? */ return bounds; case ZSTD_c_forceAttachDict: ZSTD_STATIC_ASSERT(ZSTD_dictDefaultAttach < ZSTD_dictForceCopy); bounds.lowerBound = ZSTD_dictDefaultAttach; bounds.upperBound = ZSTD_dictForceLoad; /* note : how to ensure at compile time that this is the highest value enum ? */ return bounds; case ZSTD_c_literalCompressionMode: ZSTD_STATIC_ASSERT(ZSTD_lcm_auto < ZSTD_lcm_huffman && ZSTD_lcm_huffman < ZSTD_lcm_uncompressed); bounds.lowerBound = ZSTD_lcm_auto; bounds.upperBound = ZSTD_lcm_uncompressed; return bounds; case ZSTD_c_targetCBlockSize: bounds.lowerBound = ZSTD_TARGETCBLOCKSIZE_MIN; bounds.upperBound = ZSTD_TARGETCBLOCKSIZE_MAX; return bounds; case ZSTD_c_srcSizeHint: bounds.lowerBound = ZSTD_SRCSIZEHINT_MIN; bounds.upperBound = ZSTD_SRCSIZEHINT_MAX; return bounds; default: bounds.error = ERROR(parameter_unsupported); return bounds; } } /* ZSTD_cParam_clampBounds: * Clamps the value into the bounded range. */ static size_t ZSTD_cParam_clampBounds(ZSTD_cParameter cParam, int* value) { ZSTD_bounds const bounds = ZSTD_cParam_getBounds(cParam); if (ZSTD_isError(bounds.error)) return bounds.error; if (*value < bounds.lowerBound) *value = bounds.lowerBound; if (*value > bounds.upperBound) *value = bounds.upperBound; return 0; } #define BOUNDCHECK(cParam, val) { \ RETURN_ERROR_IF(!ZSTD_cParam_withinBounds(cParam,val), \ parameter_outOfBound, "Param out of bounds"); \ } static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param) { switch(param) { case ZSTD_c_compressionLevel: case ZSTD_c_hashLog: case ZSTD_c_chainLog: case ZSTD_c_searchLog: case ZSTD_c_minMatch: case ZSTD_c_targetLength: case ZSTD_c_strategy: return 1; case ZSTD_c_format: case ZSTD_c_windowLog: case ZSTD_c_contentSizeFlag: case ZSTD_c_checksumFlag: case ZSTD_c_dictIDFlag: case ZSTD_c_forceMaxWindow : case ZSTD_c_nbWorkers: case ZSTD_c_jobSize: case ZSTD_c_overlapLog: case ZSTD_c_rsyncable: case ZSTD_c_enableLongDistanceMatching: case ZSTD_c_ldmHashLog: case ZSTD_c_ldmMinMatch: case ZSTD_c_ldmBucketSizeLog: case ZSTD_c_ldmHashRateLog: case ZSTD_c_forceAttachDict: case ZSTD_c_literalCompressionMode: case ZSTD_c_targetCBlockSize: case ZSTD_c_srcSizeHint: default: return 0; } } size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value) { DEBUGLOG(4, "ZSTD_CCtx_setParameter (%i, %i)", (int)param, value); if (cctx->streamStage != zcss_init) { if (ZSTD_isUpdateAuthorized(param)) { cctx->cParamsChanged = 1; } else { RETURN_ERROR(stage_wrong, "can only set params in ctx init stage"); } } switch(param) { case ZSTD_c_nbWorkers: RETURN_ERROR_IF((value!=0) && cctx->staticSize, parameter_unsupported, "MT not compatible with static alloc"); break; case ZSTD_c_compressionLevel: case ZSTD_c_windowLog: case ZSTD_c_hashLog: case ZSTD_c_chainLog: case ZSTD_c_searchLog: case ZSTD_c_minMatch: case ZSTD_c_targetLength: case ZSTD_c_strategy: case ZSTD_c_ldmHashRateLog: case ZSTD_c_format: case ZSTD_c_contentSizeFlag: case ZSTD_c_checksumFlag: case ZSTD_c_dictIDFlag: case ZSTD_c_forceMaxWindow: case ZSTD_c_forceAttachDict: case ZSTD_c_literalCompressionMode: case ZSTD_c_jobSize: case ZSTD_c_overlapLog: case ZSTD_c_rsyncable: case ZSTD_c_enableLongDistanceMatching: case ZSTD_c_ldmHashLog: case ZSTD_c_ldmMinMatch: case ZSTD_c_ldmBucketSizeLog: case ZSTD_c_targetCBlockSize: case ZSTD_c_srcSizeHint: break; default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); } return ZSTD_CCtxParams_setParameter(&cctx->requestedParams, param, value); } size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams, ZSTD_cParameter param, int value) { DEBUGLOG(4, "ZSTD_CCtxParams_setParameter (%i, %i)", (int)param, value); switch(param) { case ZSTD_c_format : BOUNDCHECK(ZSTD_c_format, value); CCtxParams->format = (ZSTD_format_e)value; return (size_t)CCtxParams->format; case ZSTD_c_compressionLevel : { FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value), ""); if (value) { /* 0 : does not change current level */ CCtxParams->compressionLevel = value; } if (CCtxParams->compressionLevel >= 0) return (size_t)CCtxParams->compressionLevel; return 0; /* return type (size_t) cannot represent negative values */ } case ZSTD_c_windowLog : if (value!=0) /* 0 => use default */ BOUNDCHECK(ZSTD_c_windowLog, value); CCtxParams->cParams.windowLog = (U32)value; return CCtxParams->cParams.windowLog; case ZSTD_c_hashLog : if (value!=0) /* 0 => use default */ BOUNDCHECK(ZSTD_c_hashLog, value); CCtxParams->cParams.hashLog = (U32)value; return CCtxParams->cParams.hashLog; case ZSTD_c_chainLog : if (value!=0) /* 0 => use default */ BOUNDCHECK(ZSTD_c_chainLog, value); CCtxParams->cParams.chainLog = (U32)value; return CCtxParams->cParams.chainLog; case ZSTD_c_searchLog : if (value!=0) /* 0 => use default */ BOUNDCHECK(ZSTD_c_searchLog, value); CCtxParams->cParams.searchLog = (U32)value; return (size_t)value; case ZSTD_c_minMatch : if (value!=0) /* 0 => use default */ BOUNDCHECK(ZSTD_c_minMatch, value); CCtxParams->cParams.minMatch = value; return CCtxParams->cParams.minMatch; case ZSTD_c_targetLength : BOUNDCHECK(ZSTD_c_targetLength, value); CCtxParams->cParams.targetLength = value; return CCtxParams->cParams.targetLength; case ZSTD_c_strategy : if (value!=0) /* 0 => use default */ BOUNDCHECK(ZSTD_c_strategy, value); CCtxParams->cParams.strategy = (ZSTD_strategy)value; return (size_t)CCtxParams->cParams.strategy; case ZSTD_c_contentSizeFlag : /* Content size written in frame header _when known_ (default:1) */ DEBUGLOG(4, "set content size flag = %u", (value!=0)); CCtxParams->fParams.contentSizeFlag = value != 0; return CCtxParams->fParams.contentSizeFlag; case ZSTD_c_checksumFlag : /* A 32-bits content checksum will be calculated and written at end of frame (default:0) */ CCtxParams->fParams.checksumFlag = value != 0; return CCtxParams->fParams.checksumFlag; case ZSTD_c_dictIDFlag : /* When applicable, dictionary's dictID is provided in frame header (default:1) */ DEBUGLOG(4, "set dictIDFlag = %u", (value!=0)); CCtxParams->fParams.noDictIDFlag = !value; return !CCtxParams->fParams.noDictIDFlag; case ZSTD_c_forceMaxWindow : CCtxParams->forceWindow = (value != 0); return CCtxParams->forceWindow; case ZSTD_c_forceAttachDict : { const ZSTD_dictAttachPref_e pref = (ZSTD_dictAttachPref_e)value; BOUNDCHECK(ZSTD_c_forceAttachDict, pref); CCtxParams->attachDictPref = pref; return CCtxParams->attachDictPref; } case ZSTD_c_literalCompressionMode : { const ZSTD_literalCompressionMode_e lcm = (ZSTD_literalCompressionMode_e)value; BOUNDCHECK(ZSTD_c_literalCompressionMode, lcm); CCtxParams->literalCompressionMode = lcm; return CCtxParams->literalCompressionMode; } case ZSTD_c_nbWorkers : #ifndef ZSTD_MULTITHREAD RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading"); return 0; #else FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value), ""); CCtxParams->nbWorkers = value; return CCtxParams->nbWorkers; #endif case ZSTD_c_jobSize : #ifndef ZSTD_MULTITHREAD RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading"); return 0; #else /* Adjust to the minimum non-default value. */ if (value != 0 && value < ZSTDMT_JOBSIZE_MIN) value = ZSTDMT_JOBSIZE_MIN; FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value), ""); assert(value >= 0); CCtxParams->jobSize = value; return CCtxParams->jobSize; #endif case ZSTD_c_overlapLog : #ifndef ZSTD_MULTITHREAD RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading"); return 0; #else FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(ZSTD_c_overlapLog, &value), ""); CCtxParams->overlapLog = value; return CCtxParams->overlapLog; #endif case ZSTD_c_rsyncable : #ifndef ZSTD_MULTITHREAD RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading"); return 0; #else FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(ZSTD_c_overlapLog, &value), ""); CCtxParams->rsyncable = value; return CCtxParams->rsyncable; #endif case ZSTD_c_enableLongDistanceMatching : CCtxParams->ldmParams.enableLdm = (value!=0); return CCtxParams->ldmParams.enableLdm; case ZSTD_c_ldmHashLog : if (value!=0) /* 0 ==> auto */ BOUNDCHECK(ZSTD_c_ldmHashLog, value); CCtxParams->ldmParams.hashLog = value; return CCtxParams->ldmParams.hashLog; case ZSTD_c_ldmMinMatch : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_ldmMinMatch, value); CCtxParams->ldmParams.minMatchLength = value; return CCtxParams->ldmParams.minMatchLength; case ZSTD_c_ldmBucketSizeLog : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_ldmBucketSizeLog, value); CCtxParams->ldmParams.bucketSizeLog = value; return CCtxParams->ldmParams.bucketSizeLog; case ZSTD_c_ldmHashRateLog : RETURN_ERROR_IF(value > ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN, parameter_outOfBound, "Param out of bounds!"); CCtxParams->ldmParams.hashRateLog = value; return CCtxParams->ldmParams.hashRateLog; case ZSTD_c_targetCBlockSize : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_targetCBlockSize, value); CCtxParams->targetCBlockSize = value; return CCtxParams->targetCBlockSize; case ZSTD_c_srcSizeHint : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_srcSizeHint, value); CCtxParams->srcSizeHint = value; return CCtxParams->srcSizeHint; default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); } } size_t ZSTD_CCtx_getParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value) { return ZSTD_CCtxParams_getParameter(&cctx->requestedParams, param, value); } size_t ZSTD_CCtxParams_getParameter( ZSTD_CCtx_params* CCtxParams, ZSTD_cParameter param, int* value) { switch(param) { case ZSTD_c_format : *value = CCtxParams->format; break; case ZSTD_c_compressionLevel : *value = CCtxParams->compressionLevel; break; case ZSTD_c_windowLog : *value = (int)CCtxParams->cParams.windowLog; break; case ZSTD_c_hashLog : *value = (int)CCtxParams->cParams.hashLog; break; case ZSTD_c_chainLog : *value = (int)CCtxParams->cParams.chainLog; break; case ZSTD_c_searchLog : *value = CCtxParams->cParams.searchLog; break; case ZSTD_c_minMatch : *value = CCtxParams->cParams.minMatch; break; case ZSTD_c_targetLength : *value = CCtxParams->cParams.targetLength; break; case ZSTD_c_strategy : *value = (unsigned)CCtxParams->cParams.strategy; break; case ZSTD_c_contentSizeFlag : *value = CCtxParams->fParams.contentSizeFlag; break; case ZSTD_c_checksumFlag : *value = CCtxParams->fParams.checksumFlag; break; case ZSTD_c_dictIDFlag : *value = !CCtxParams->fParams.noDictIDFlag; break; case ZSTD_c_forceMaxWindow : *value = CCtxParams->forceWindow; break; case ZSTD_c_forceAttachDict : *value = CCtxParams->attachDictPref; break; case ZSTD_c_literalCompressionMode : *value = CCtxParams->literalCompressionMode; break; case ZSTD_c_nbWorkers : #ifndef ZSTD_MULTITHREAD assert(CCtxParams->nbWorkers == 0); #endif *value = CCtxParams->nbWorkers; break; case ZSTD_c_jobSize : #ifndef ZSTD_MULTITHREAD RETURN_ERROR(parameter_unsupported, "not compiled with multithreading"); #else assert(CCtxParams->jobSize <= INT_MAX); *value = (int)CCtxParams->jobSize; break; #endif case ZSTD_c_overlapLog : #ifndef ZSTD_MULTITHREAD RETURN_ERROR(parameter_unsupported, "not compiled with multithreading"); #else *value = CCtxParams->overlapLog; break; #endif case ZSTD_c_rsyncable : #ifndef ZSTD_MULTITHREAD RETURN_ERROR(parameter_unsupported, "not compiled with multithreading"); #else *value = CCtxParams->rsyncable; break; #endif case ZSTD_c_enableLongDistanceMatching : *value = CCtxParams->ldmParams.enableLdm; break; case ZSTD_c_ldmHashLog : *value = CCtxParams->ldmParams.hashLog; break; case ZSTD_c_ldmMinMatch : *value = CCtxParams->ldmParams.minMatchLength; break; case ZSTD_c_ldmBucketSizeLog : *value = CCtxParams->ldmParams.bucketSizeLog; break; case ZSTD_c_ldmHashRateLog : *value = CCtxParams->ldmParams.hashRateLog; break; case ZSTD_c_targetCBlockSize : *value = (int)CCtxParams->targetCBlockSize; break; case ZSTD_c_srcSizeHint : *value = (int)CCtxParams->srcSizeHint; break; default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); } return 0; } /** ZSTD_CCtx_setParametersUsingCCtxParams() : * just applies `params` into `cctx` * no action is performed, parameters are merely stored. * If ZSTDMT is enabled, parameters are pushed to cctx->mtctx. * This is possible even if a compression is ongoing. * In which case, new parameters will be applied on the fly, starting with next compression job. */ size_t ZSTD_CCtx_setParametersUsingCCtxParams( ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params) { DEBUGLOG(4, "ZSTD_CCtx_setParametersUsingCCtxParams"); RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, "The context is in the wrong stage!"); RETURN_ERROR_IF(cctx->cdict, stage_wrong, "Can't override parameters with cdict attached (some must " "be inherited from the cdict)."); cctx->requestedParams = *params; return 0; } ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize) { DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %u bytes", (U32)pledgedSrcSize); RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, "Can't set pledgedSrcSize when not in init stage."); cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1; return 0; } /** * Initializes the local dict using the requested parameters. * NOTE: This does not use the pledged src size, because it may be used for more * than one compression. */ static size_t ZSTD_initLocalDict(ZSTD_CCtx* cctx) { ZSTD_localDict* const dl = &cctx->localDict; ZSTD_compressionParameters const cParams = ZSTD_getCParamsFromCCtxParams( &cctx->requestedParams, ZSTD_CONTENTSIZE_UNKNOWN, dl->dictSize); if (dl->dict == NULL) { /* No local dictionary. */ assert(dl->dictBuffer == NULL); assert(dl->cdict == NULL); assert(dl->dictSize == 0); return 0; } if (dl->cdict != NULL) { assert(cctx->cdict == dl->cdict); /* Local dictionary already initialized. */ return 0; } assert(dl->dictSize > 0); assert(cctx->cdict == NULL); assert(cctx->prefixDict.dict == NULL); dl->cdict = ZSTD_createCDict_advanced( dl->dict, dl->dictSize, ZSTD_dlm_byRef, dl->dictContentType, cParams, cctx->customMem); RETURN_ERROR_IF(!dl->cdict, memory_allocation, "ZSTD_createCDict_advanced failed"); cctx->cdict = dl->cdict; return 0; } size_t ZSTD_CCtx_loadDictionary_advanced( ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType) { RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, "Can't load a dictionary when ctx is not in init stage."); RETURN_ERROR_IF(cctx->staticSize, memory_allocation, "no malloc for static CCtx"); DEBUGLOG(4, "ZSTD_CCtx_loadDictionary_advanced (size: %u)", (U32)dictSize); ZSTD_clearAllDicts(cctx); /* in case one already exists */ if (dict == NULL || dictSize == 0) /* no dictionary mode */ return 0; if (dictLoadMethod == ZSTD_dlm_byRef) { cctx->localDict.dict = dict; } else { void* dictBuffer = ZSTD_malloc(dictSize, cctx->customMem); RETURN_ERROR_IF(!dictBuffer, memory_allocation, "NULL pointer!"); memcpy(dictBuffer, dict, dictSize); cctx->localDict.dictBuffer = dictBuffer; cctx->localDict.dict = dictBuffer; } cctx->localDict.dictSize = dictSize; cctx->localDict.dictContentType = dictContentType; return 0; } ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference( ZSTD_CCtx* cctx, const void* dict, size_t dictSize) { return ZSTD_CCtx_loadDictionary_advanced( cctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto); } ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize) { return ZSTD_CCtx_loadDictionary_advanced( cctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto); } size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) { RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, "Can't ref a dict when ctx not in init stage."); /* Free the existing local cdict (if any) to save memory. */ ZSTD_clearAllDicts(cctx); cctx->cdict = cdict; return 0; } size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize) { return ZSTD_CCtx_refPrefix_advanced(cctx, prefix, prefixSize, ZSTD_dct_rawContent); } size_t ZSTD_CCtx_refPrefix_advanced( ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType) { RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, "Can't ref a prefix when ctx not in init stage."); ZSTD_clearAllDicts(cctx); if (prefix != NULL && prefixSize > 0) { cctx->prefixDict.dict = prefix; cctx->prefixDict.dictSize = prefixSize; cctx->prefixDict.dictContentType = dictContentType; } return 0; } /*! ZSTD_CCtx_reset() : * Also dumps dictionary */ size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset) { if ( (reset == ZSTD_reset_session_only) || (reset == ZSTD_reset_session_and_parameters) ) { cctx->streamStage = zcss_init; cctx->pledgedSrcSizePlusOne = 0; } if ( (reset == ZSTD_reset_parameters) || (reset == ZSTD_reset_session_and_parameters) ) { RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, "Can't reset parameters only when not in init stage."); ZSTD_clearAllDicts(cctx); return ZSTD_CCtxParams_reset(&cctx->requestedParams); } return 0; } /** ZSTD_checkCParams() : control CParam values remain within authorized range. @return : 0, or an error code if one value is beyond authorized range */ size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams) { BOUNDCHECK(ZSTD_c_windowLog, (int)cParams.windowLog); BOUNDCHECK(ZSTD_c_chainLog, (int)cParams.chainLog); BOUNDCHECK(ZSTD_c_hashLog, (int)cParams.hashLog); BOUNDCHECK(ZSTD_c_searchLog, (int)cParams.searchLog); BOUNDCHECK(ZSTD_c_minMatch, (int)cParams.minMatch); BOUNDCHECK(ZSTD_c_targetLength,(int)cParams.targetLength); BOUNDCHECK(ZSTD_c_strategy, cParams.strategy); return 0; } /** ZSTD_clampCParams() : * make CParam values within valid range. * @return : valid CParams */ static ZSTD_compressionParameters ZSTD_clampCParams(ZSTD_compressionParameters cParams) { # define CLAMP_TYPE(cParam, val, type) { \ ZSTD_bounds const bounds = ZSTD_cParam_getBounds(cParam); \ if ((int)valbounds.upperBound) val=(type)bounds.upperBound; \ } # define CLAMP(cParam, val) CLAMP_TYPE(cParam, val, unsigned) CLAMP(ZSTD_c_windowLog, cParams.windowLog); CLAMP(ZSTD_c_chainLog, cParams.chainLog); CLAMP(ZSTD_c_hashLog, cParams.hashLog); CLAMP(ZSTD_c_searchLog, cParams.searchLog); CLAMP(ZSTD_c_minMatch, cParams.minMatch); CLAMP(ZSTD_c_targetLength,cParams.targetLength); CLAMP_TYPE(ZSTD_c_strategy,cParams.strategy, ZSTD_strategy); return cParams; } /** ZSTD_cycleLog() : * condition for correct operation : hashLog > 1 */ U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat) { U32 const btScale = ((U32)strat >= (U32)ZSTD_btlazy2); return hashLog - btScale; } /** ZSTD_adjustCParams_internal() : * optimize `cPar` for a specified input (`srcSize` and `dictSize`). * mostly downsize to reduce memory consumption and initialization latency. * `srcSize` can be ZSTD_CONTENTSIZE_UNKNOWN when not known. * note : `srcSize==0` means 0! * condition : cPar is presumed validated (can be checked using ZSTD_checkCParams()). */ static ZSTD_compressionParameters ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize) { static const U64 minSrcSize = 513; /* (1<<9) + 1 */ static const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1); assert(ZSTD_checkCParams(cPar)==0); if (dictSize && srcSize == ZSTD_CONTENTSIZE_UNKNOWN) srcSize = minSrcSize; /* resize windowLog if input is small enough, to use less memory */ if ( (srcSize < maxWindowResize) && (dictSize < maxWindowResize) ) { U32 const tSize = (U32)(srcSize + dictSize); static U32 const hashSizeMin = 1 << ZSTD_HASHLOG_MIN; U32 const srcLog = (tSize < hashSizeMin) ? ZSTD_HASHLOG_MIN : ZSTD_highbit32(tSize-1) + 1; if (cPar.windowLog > srcLog) cPar.windowLog = srcLog; } if (cPar.hashLog > cPar.windowLog+1) cPar.hashLog = cPar.windowLog+1; { U32 const cycleLog = ZSTD_cycleLog(cPar.chainLog, cPar.strategy); if (cycleLog > cPar.windowLog) cPar.chainLog -= (cycleLog - cPar.windowLog); } if (cPar.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN) cPar.windowLog = ZSTD_WINDOWLOG_ABSOLUTEMIN; /* minimum wlog required for valid frame header */ return cPar; } ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize) { cPar = ZSTD_clampCParams(cPar); /* resulting cPar is necessarily valid (all parameters within range) */ if (srcSize == 0) srcSize = ZSTD_CONTENTSIZE_UNKNOWN; return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize); } static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize); static ZSTD_parameters ZSTD_getParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize); ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams( const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize) { ZSTD_compressionParameters cParams; if (srcSizeHint == ZSTD_CONTENTSIZE_UNKNOWN && CCtxParams->srcSizeHint > 0) { srcSizeHint = CCtxParams->srcSizeHint; } cParams = ZSTD_getCParams_internal(CCtxParams->compressionLevel, srcSizeHint, dictSize); if (CCtxParams->ldmParams.enableLdm) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG; if (CCtxParams->cParams.windowLog) cParams.windowLog = CCtxParams->cParams.windowLog; if (CCtxParams->cParams.hashLog) cParams.hashLog = CCtxParams->cParams.hashLog; if (CCtxParams->cParams.chainLog) cParams.chainLog = CCtxParams->cParams.chainLog; if (CCtxParams->cParams.searchLog) cParams.searchLog = CCtxParams->cParams.searchLog; if (CCtxParams->cParams.minMatch) cParams.minMatch = CCtxParams->cParams.minMatch; if (CCtxParams->cParams.targetLength) cParams.targetLength = CCtxParams->cParams.targetLength; if (CCtxParams->cParams.strategy) cParams.strategy = CCtxParams->cParams.strategy; assert(!ZSTD_checkCParams(cParams)); /* srcSizeHint == 0 means 0 */ return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize); } static size_t ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams, const U32 forCCtx) { size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog); size_t const hSize = ((size_t)1) << cParams->hashLog; U32 const hashLog3 = (forCCtx && cParams->minMatch==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0; size_t const h3Size = hashLog3 ? ((size_t)1) << hashLog3 : 0; /* We don't use ZSTD_cwksp_alloc_size() here because the tables aren't * surrounded by redzones in ASAN. */ size_t const tableSpace = chainSize * sizeof(U32) + hSize * sizeof(U32) + h3Size * sizeof(U32); size_t const optPotentialSpace = ZSTD_cwksp_alloc_size((MaxML+1) * sizeof(U32)) + ZSTD_cwksp_alloc_size((MaxLL+1) * sizeof(U32)) + ZSTD_cwksp_alloc_size((MaxOff+1) * sizeof(U32)) + ZSTD_cwksp_alloc_size((1<strategy >= ZSTD_btopt)) ? optPotentialSpace : 0; DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u", (U32)chainSize, (U32)hSize, (U32)h3Size); return tableSpace + optSpace; } size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params) { RETURN_ERROR_IF(params->nbWorkers > 0, GENERIC, "Estimate CCtx size is supported for single-threaded compression only."); { ZSTD_compressionParameters const cParams = ZSTD_getCParamsFromCCtxParams(params, ZSTD_CONTENTSIZE_UNKNOWN, 0); size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog); U32 const divider = (cParams.minMatch==3) ? 3 : 4; size_t const maxNbSeq = blockSize / divider; size_t const tokenSpace = ZSTD_cwksp_alloc_size(WILDCOPY_OVERLENGTH + blockSize) + ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(seqDef)) + 3 * ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(BYTE)); size_t const entropySpace = ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE); size_t const blockStateSpace = 2 * ZSTD_cwksp_alloc_size(sizeof(ZSTD_compressedBlockState_t)); size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 1); size_t const ldmSpace = ZSTD_ldm_getTableSize(params->ldmParams); size_t const ldmSeqSpace = ZSTD_cwksp_alloc_size(ZSTD_ldm_getMaxNbSeq(params->ldmParams, blockSize) * sizeof(rawSeq)); /* estimateCCtxSize is for one-shot compression. So no buffers should * be needed. However, we still allocate two 0-sized buffers, which can * take space under ASAN. */ size_t const bufferSpace = ZSTD_cwksp_alloc_size(0) + ZSTD_cwksp_alloc_size(0); size_t const cctxSpace = ZSTD_cwksp_alloc_size(sizeof(ZSTD_CCtx)); size_t const neededSpace = cctxSpace + entropySpace + blockStateSpace + ldmSpace + ldmSeqSpace + matchStateSize + tokenSpace + bufferSpace; DEBUGLOG(5, "estimate workspace : %u", (U32)neededSpace); return neededSpace; } } size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams) { ZSTD_CCtx_params const params = ZSTD_makeCCtxParamsFromCParams(cParams); return ZSTD_estimateCCtxSize_usingCCtxParams(¶ms); } static size_t ZSTD_estimateCCtxSize_internal(int compressionLevel) { ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0); return ZSTD_estimateCCtxSize_usingCParams(cParams); } size_t ZSTD_estimateCCtxSize(int compressionLevel) { int level; size_t memBudget = 0; for (level=MIN(compressionLevel, 1); level<=compressionLevel; level++) { size_t const newMB = ZSTD_estimateCCtxSize_internal(level); if (newMB > memBudget) memBudget = newMB; } return memBudget; } size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params) { RETURN_ERROR_IF(params->nbWorkers > 0, GENERIC, "Estimate CCtx size is supported for single-threaded compression only."); { ZSTD_compressionParameters const cParams = ZSTD_getCParamsFromCCtxParams(params, ZSTD_CONTENTSIZE_UNKNOWN, 0); size_t const CCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(params); size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog); size_t const inBuffSize = ((size_t)1 << cParams.windowLog) + blockSize; size_t const outBuffSize = ZSTD_compressBound(blockSize) + 1; size_t const streamingSize = ZSTD_cwksp_alloc_size(inBuffSize) + ZSTD_cwksp_alloc_size(outBuffSize); return CCtxSize + streamingSize; } } size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams) { ZSTD_CCtx_params const params = ZSTD_makeCCtxParamsFromCParams(cParams); return ZSTD_estimateCStreamSize_usingCCtxParams(¶ms); } static size_t ZSTD_estimateCStreamSize_internal(int compressionLevel) { ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0); return ZSTD_estimateCStreamSize_usingCParams(cParams); } size_t ZSTD_estimateCStreamSize(int compressionLevel) { int level; size_t memBudget = 0; for (level=MIN(compressionLevel, 1); level<=compressionLevel; level++) { size_t const newMB = ZSTD_estimateCStreamSize_internal(level); if (newMB > memBudget) memBudget = newMB; } return memBudget; } /* ZSTD_getFrameProgression(): * tells how much data has been consumed (input) and produced (output) for current frame. * able to count progression inside worker threads (non-blocking mode). */ ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx) { #ifdef ZSTD_MULTITHREAD if (cctx->appliedParams.nbWorkers > 0) { return ZSTDMT_getFrameProgression(cctx->mtctx); } #endif { ZSTD_frameProgression fp; size_t const buffered = (cctx->inBuff == NULL) ? 0 : cctx->inBuffPos - cctx->inToCompress; if (buffered) assert(cctx->inBuffPos >= cctx->inToCompress); assert(buffered <= ZSTD_BLOCKSIZE_MAX); fp.ingested = cctx->consumedSrcSize + buffered; fp.consumed = cctx->consumedSrcSize; fp.produced = cctx->producedCSize; fp.flushed = cctx->producedCSize; /* simplified; some data might still be left within streaming output buffer */ fp.currentJobID = 0; fp.nbActiveWorkers = 0; return fp; } } /*! ZSTD_toFlushNow() * Only useful for multithreading scenarios currently (nbWorkers >= 1). */ size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx) { #ifdef ZSTD_MULTITHREAD if (cctx->appliedParams.nbWorkers > 0) { return ZSTDMT_toFlushNow(cctx->mtctx); } #endif (void)cctx; return 0; /* over-simplification; could also check if context is currently running in streaming mode, and in which case, report how many bytes are left to be flushed within output buffer */ } static void ZSTD_assertEqualCParams(ZSTD_compressionParameters cParams1, ZSTD_compressionParameters cParams2) { (void)cParams1; (void)cParams2; assert(cParams1.windowLog == cParams2.windowLog); assert(cParams1.chainLog == cParams2.chainLog); assert(cParams1.hashLog == cParams2.hashLog); assert(cParams1.searchLog == cParams2.searchLog); assert(cParams1.minMatch == cParams2.minMatch); assert(cParams1.targetLength == cParams2.targetLength); assert(cParams1.strategy == cParams2.strategy); } void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs) { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) bs->rep[i] = repStartValue[i]; bs->entropy.huf.repeatMode = HUF_repeat_none; bs->entropy.fse.offcode_repeatMode = FSE_repeat_none; bs->entropy.fse.matchlength_repeatMode = FSE_repeat_none; bs->entropy.fse.litlength_repeatMode = FSE_repeat_none; } /*! ZSTD_invalidateMatchState() * Invalidate all the matches in the match finder tables. * Requires nextSrc and base to be set (can be NULL). */ static void ZSTD_invalidateMatchState(ZSTD_matchState_t* ms) { ZSTD_window_clear(&ms->window); ms->nextToUpdate = ms->window.dictLimit; ms->loadedDictEnd = 0; ms->opt.litLengthSum = 0; /* force reset of btopt stats */ ms->dictMatchState = NULL; } /** * Indicates whether this compression proceeds directly from user-provided * source buffer to user-provided destination buffer (ZSTDb_not_buffered), or * whether the context needs to buffer the input/output (ZSTDb_buffered). */ typedef enum { ZSTDb_not_buffered, ZSTDb_buffered } ZSTD_buffered_policy_e; /** * Controls, for this matchState reset, whether the tables need to be cleared / * prepared for the coming compression (ZSTDcrp_makeClean), or whether the * tables can be left unclean (ZSTDcrp_leaveDirty), because we know that a * subsequent operation will overwrite the table space anyways (e.g., copying * the matchState contents in from a CDict). */ typedef enum { ZSTDcrp_makeClean, ZSTDcrp_leaveDirty } ZSTD_compResetPolicy_e; /** * Controls, for this matchState reset, whether indexing can continue where it * left off (ZSTDirp_continue), or whether it needs to be restarted from zero * (ZSTDirp_reset). */ typedef enum { ZSTDirp_continue, ZSTDirp_reset } ZSTD_indexResetPolicy_e; typedef enum { ZSTD_resetTarget_CDict, ZSTD_resetTarget_CCtx } ZSTD_resetTarget_e; static size_t ZSTD_reset_matchState(ZSTD_matchState_t* ms, ZSTD_cwksp* ws, const ZSTD_compressionParameters* cParams, const ZSTD_compResetPolicy_e crp, const ZSTD_indexResetPolicy_e forceResetIndex, const ZSTD_resetTarget_e forWho) { size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog); size_t const hSize = ((size_t)1) << cParams->hashLog; U32 const hashLog3 = ((forWho == ZSTD_resetTarget_CCtx) && cParams->minMatch==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0; size_t const h3Size = hashLog3 ? ((size_t)1) << hashLog3 : 0; DEBUGLOG(4, "reset indices : %u", forceResetIndex == ZSTDirp_reset); if (forceResetIndex == ZSTDirp_reset) { ZSTD_window_init(&ms->window); ZSTD_cwksp_mark_tables_dirty(ws); } ms->hashLog3 = hashLog3; ZSTD_invalidateMatchState(ms); assert(!ZSTD_cwksp_reserve_failed(ws)); /* check that allocation hasn't already failed */ ZSTD_cwksp_clear_tables(ws); DEBUGLOG(5, "reserving table space"); /* table Space */ ms->hashTable = (U32*)ZSTD_cwksp_reserve_table(ws, hSize * sizeof(U32)); ms->chainTable = (U32*)ZSTD_cwksp_reserve_table(ws, chainSize * sizeof(U32)); ms->hashTable3 = (U32*)ZSTD_cwksp_reserve_table(ws, h3Size * sizeof(U32)); RETURN_ERROR_IF(ZSTD_cwksp_reserve_failed(ws), memory_allocation, "failed a workspace allocation in ZSTD_reset_matchState"); DEBUGLOG(4, "reset table : %u", crp!=ZSTDcrp_leaveDirty); if (crp!=ZSTDcrp_leaveDirty) { /* reset tables only */ ZSTD_cwksp_clean_tables(ws); } /* opt parser space */ if ((forWho == ZSTD_resetTarget_CCtx) && (cParams->strategy >= ZSTD_btopt)) { DEBUGLOG(4, "reserving optimal parser space"); ms->opt.litFreq = (unsigned*)ZSTD_cwksp_reserve_aligned(ws, (1<opt.litLengthFreq = (unsigned*)ZSTD_cwksp_reserve_aligned(ws, (MaxLL+1) * sizeof(unsigned)); ms->opt.matchLengthFreq = (unsigned*)ZSTD_cwksp_reserve_aligned(ws, (MaxML+1) * sizeof(unsigned)); ms->opt.offCodeFreq = (unsigned*)ZSTD_cwksp_reserve_aligned(ws, (MaxOff+1) * sizeof(unsigned)); ms->opt.matchTable = (ZSTD_match_t*)ZSTD_cwksp_reserve_aligned(ws, (ZSTD_OPT_NUM+1) * sizeof(ZSTD_match_t)); ms->opt.priceTable = (ZSTD_optimal_t*)ZSTD_cwksp_reserve_aligned(ws, (ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t)); } ms->cParams = *cParams; RETURN_ERROR_IF(ZSTD_cwksp_reserve_failed(ws), memory_allocation, "failed a workspace allocation in ZSTD_reset_matchState"); return 0; } /* ZSTD_indexTooCloseToMax() : * minor optimization : prefer memset() rather than reduceIndex() * which is measurably slow in some circumstances (reported for Visual Studio). * Works when re-using a context for a lot of smallish inputs : * if all inputs are smaller than ZSTD_INDEXOVERFLOW_MARGIN, * memset() will be triggered before reduceIndex(). */ #define ZSTD_INDEXOVERFLOW_MARGIN (16 MB) static int ZSTD_indexTooCloseToMax(ZSTD_window_t w) { return (size_t)(w.nextSrc - w.base) > (ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN); } /*! ZSTD_resetCCtx_internal() : note : `params` are assumed fully validated at this stage */ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, ZSTD_CCtx_params params, U64 const pledgedSrcSize, ZSTD_compResetPolicy_e const crp, ZSTD_buffered_policy_e const zbuff) { ZSTD_cwksp* const ws = &zc->workspace; DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u", (U32)pledgedSrcSize, params.cParams.windowLog); assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); zc->isFirstBlock = 1; if (params.ldmParams.enableLdm) { /* Adjust long distance matching parameters */ ZSTD_ldm_adjustParameters(¶ms.ldmParams, ¶ms.cParams); assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog); assert(params.ldmParams.hashRateLog < 32); zc->ldmState.hashPower = ZSTD_rollingHash_primePower(params.ldmParams.minMatchLength); } { size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params.cParams.windowLog), pledgedSrcSize)); size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); U32 const divider = (params.cParams.minMatch==3) ? 3 : 4; size_t const maxNbSeq = blockSize / divider; size_t const tokenSpace = ZSTD_cwksp_alloc_size(WILDCOPY_OVERLENGTH + blockSize) + ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(seqDef)) + 3 * ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(BYTE)); size_t const buffOutSize = (zbuff==ZSTDb_buffered) ? ZSTD_compressBound(blockSize)+1 : 0; size_t const buffInSize = (zbuff==ZSTDb_buffered) ? windowSize + blockSize : 0; size_t const matchStateSize = ZSTD_sizeof_matchState(¶ms.cParams, /* forCCtx */ 1); size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(params.ldmParams, blockSize); ZSTD_indexResetPolicy_e needsIndexReset = zc->initialized ? ZSTDirp_continue : ZSTDirp_reset; if (ZSTD_indexTooCloseToMax(zc->blockState.matchState.window)) { needsIndexReset = ZSTDirp_reset; } if (!zc->staticSize) ZSTD_cwksp_bump_oversized_duration(ws, 0); /* Check if workspace is large enough, alloc a new one if needed */ { size_t const cctxSpace = zc->staticSize ? ZSTD_cwksp_alloc_size(sizeof(ZSTD_CCtx)) : 0; size_t const entropySpace = ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE); size_t const blockStateSpace = 2 * ZSTD_cwksp_alloc_size(sizeof(ZSTD_compressedBlockState_t)); size_t const bufferSpace = ZSTD_cwksp_alloc_size(buffInSize) + ZSTD_cwksp_alloc_size(buffOutSize); size_t const ldmSpace = ZSTD_ldm_getTableSize(params.ldmParams); size_t const ldmSeqSpace = ZSTD_cwksp_alloc_size(maxNbLdmSeq * sizeof(rawSeq)); size_t const neededSpace = cctxSpace + entropySpace + blockStateSpace + ldmSpace + ldmSeqSpace + matchStateSize + tokenSpace + bufferSpace; int const workspaceTooSmall = ZSTD_cwksp_sizeof(ws) < neededSpace; int const workspaceWasteful = ZSTD_cwksp_check_wasteful(ws, neededSpace); DEBUGLOG(4, "Need %zuKB workspace, including %zuKB for match state, and %zuKB for buffers", neededSpace>>10, matchStateSize>>10, bufferSpace>>10); DEBUGLOG(4, "windowSize: %zu - blockSize: %zu", windowSize, blockSize); if (workspaceTooSmall || workspaceWasteful) { DEBUGLOG(4, "Resize workspaceSize from %zuKB to %zuKB", ZSTD_cwksp_sizeof(ws) >> 10, neededSpace >> 10); RETURN_ERROR_IF(zc->staticSize, memory_allocation, "static cctx : no resize"); needsIndexReset = ZSTDirp_reset; ZSTD_cwksp_free(ws, zc->customMem); FORWARD_IF_ERROR(ZSTD_cwksp_create(ws, neededSpace, zc->customMem), ""); DEBUGLOG(5, "reserving object space"); /* Statically sized space. * entropyWorkspace never moves, * though prev/next block swap places */ assert(ZSTD_cwksp_check_available(ws, 2 * sizeof(ZSTD_compressedBlockState_t))); zc->blockState.prevCBlock = (ZSTD_compressedBlockState_t*) ZSTD_cwksp_reserve_object(ws, sizeof(ZSTD_compressedBlockState_t)); RETURN_ERROR_IF(zc->blockState.prevCBlock == NULL, memory_allocation, "couldn't allocate prevCBlock"); zc->blockState.nextCBlock = (ZSTD_compressedBlockState_t*) ZSTD_cwksp_reserve_object(ws, sizeof(ZSTD_compressedBlockState_t)); RETURN_ERROR_IF(zc->blockState.nextCBlock == NULL, memory_allocation, "couldn't allocate nextCBlock"); zc->entropyWorkspace = (U32*) ZSTD_cwksp_reserve_object(ws, HUF_WORKSPACE_SIZE); RETURN_ERROR_IF(zc->blockState.nextCBlock == NULL, memory_allocation, "couldn't allocate entropyWorkspace"); } } ZSTD_cwksp_clear(ws); /* init params */ zc->appliedParams = params; zc->blockState.matchState.cParams = params.cParams; zc->pledgedSrcSizePlusOne = pledgedSrcSize+1; zc->consumedSrcSize = 0; zc->producedCSize = 0; if (pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN) zc->appliedParams.fParams.contentSizeFlag = 0; DEBUGLOG(4, "pledged content size : %u ; flag : %u", (unsigned)pledgedSrcSize, zc->appliedParams.fParams.contentSizeFlag); zc->blockSize = blockSize; XXH64_reset(&zc->xxhState, 0); zc->stage = ZSTDcs_init; zc->dictID = 0; ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock); /* ZSTD_wildcopy() is used to copy into the literals buffer, * so we have to oversize the buffer by WILDCOPY_OVERLENGTH bytes. */ zc->seqStore.litStart = ZSTD_cwksp_reserve_buffer(ws, blockSize + WILDCOPY_OVERLENGTH); zc->seqStore.maxNbLit = blockSize; /* buffers */ zc->inBuffSize = buffInSize; zc->inBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffInSize); zc->outBuffSize = buffOutSize; zc->outBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffOutSize); /* ldm bucketOffsets table */ if (params.ldmParams.enableLdm) { /* TODO: avoid memset? */ size_t const ldmBucketSize = ((size_t)1) << (params.ldmParams.hashLog - params.ldmParams.bucketSizeLog); zc->ldmState.bucketOffsets = ZSTD_cwksp_reserve_buffer(ws, ldmBucketSize); memset(zc->ldmState.bucketOffsets, 0, ldmBucketSize); } /* sequences storage */ ZSTD_referenceExternalSequences(zc, NULL, 0); zc->seqStore.maxNbSeq = maxNbSeq; zc->seqStore.llCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); zc->seqStore.mlCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); zc->seqStore.ofCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); zc->seqStore.sequencesStart = (seqDef*)ZSTD_cwksp_reserve_aligned(ws, maxNbSeq * sizeof(seqDef)); FORWARD_IF_ERROR(ZSTD_reset_matchState( &zc->blockState.matchState, ws, ¶ms.cParams, crp, needsIndexReset, ZSTD_resetTarget_CCtx), ""); /* ldm hash table */ if (params.ldmParams.enableLdm) { /* TODO: avoid memset? */ size_t const ldmHSize = ((size_t)1) << params.ldmParams.hashLog; zc->ldmState.hashTable = (ldmEntry_t*)ZSTD_cwksp_reserve_aligned(ws, ldmHSize * sizeof(ldmEntry_t)); memset(zc->ldmState.hashTable, 0, ldmHSize * sizeof(ldmEntry_t)); zc->ldmSequences = (rawSeq*)ZSTD_cwksp_reserve_aligned(ws, maxNbLdmSeq * sizeof(rawSeq)); zc->maxNbLdmSequences = maxNbLdmSeq; ZSTD_window_init(&zc->ldmState.window); ZSTD_window_clear(&zc->ldmState.window); zc->ldmState.loadedDictEnd = 0; } DEBUGLOG(3, "wksp: finished allocating, %zd bytes remain available", ZSTD_cwksp_available_space(ws)); zc->initialized = 1; return 0; } } /* ZSTD_invalidateRepCodes() : * ensures next compression will not use repcodes from previous block. * Note : only works with regular variant; * do not use with extDict variant ! */ void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) { int i; for (i=0; iblockState.prevCBlock->rep[i] = 0; assert(!ZSTD_window_hasExtDict(cctx->blockState.matchState.window)); } /* These are the approximate sizes for each strategy past which copying the * dictionary tables into the working context is faster than using them * in-place. */ static const size_t attachDictSizeCutoffs[ZSTD_STRATEGY_MAX+1] = { 8 KB, /* unused */ 8 KB, /* ZSTD_fast */ 16 KB, /* ZSTD_dfast */ 32 KB, /* ZSTD_greedy */ 32 KB, /* ZSTD_lazy */ 32 KB, /* ZSTD_lazy2 */ 32 KB, /* ZSTD_btlazy2 */ 32 KB, /* ZSTD_btopt */ 8 KB, /* ZSTD_btultra */ 8 KB /* ZSTD_btultra2 */ }; static int ZSTD_shouldAttachDict(const ZSTD_CDict* cdict, const ZSTD_CCtx_params* params, U64 pledgedSrcSize) { size_t cutoff = attachDictSizeCutoffs[cdict->matchState.cParams.strategy]; return ( pledgedSrcSize <= cutoff || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN || params->attachDictPref == ZSTD_dictForceAttach ) && params->attachDictPref != ZSTD_dictForceCopy && !params->forceWindow; /* dictMatchState isn't correctly * handled in _enforceMaxDist */ } static size_t ZSTD_resetCCtx_byAttachingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, U64 pledgedSrcSize, ZSTD_buffered_policy_e zbuff) { { const ZSTD_compressionParameters* const cdict_cParams = &cdict->matchState.cParams; unsigned const windowLog = params.cParams.windowLog; assert(windowLog != 0); /* Resize working context table params for input only, since the dict * has its own tables. */ /* pledgeSrcSize == 0 means 0! */ params.cParams = ZSTD_adjustCParams_internal(*cdict_cParams, pledgedSrcSize, 0); params.cParams.windowLog = windowLog; FORWARD_IF_ERROR(ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, ZSTDcrp_makeClean, zbuff), ""); assert(cctx->appliedParams.cParams.strategy == cdict_cParams->strategy); } { const U32 cdictEnd = (U32)( cdict->matchState.window.nextSrc - cdict->matchState.window.base); const U32 cdictLen = cdictEnd - cdict->matchState.window.dictLimit; if (cdictLen == 0) { /* don't even attach dictionaries with no contents */ DEBUGLOG(4, "skipping attaching empty dictionary"); } else { DEBUGLOG(4, "attaching dictionary into context"); cctx->blockState.matchState.dictMatchState = &cdict->matchState; /* prep working match state so dict matches never have negative indices * when they are translated to the working context's index space. */ if (cctx->blockState.matchState.window.dictLimit < cdictEnd) { cctx->blockState.matchState.window.nextSrc = cctx->blockState.matchState.window.base + cdictEnd; ZSTD_window_clear(&cctx->blockState.matchState.window); } /* loadedDictEnd is expressed within the referential of the active context */ cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; } } cctx->dictID = cdict->dictID; /* copy block state */ memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState)); return 0; } static size_t ZSTD_resetCCtx_byCopyingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, U64 pledgedSrcSize, ZSTD_buffered_policy_e zbuff) { const ZSTD_compressionParameters *cdict_cParams = &cdict->matchState.cParams; DEBUGLOG(4, "copying dictionary into context"); { unsigned const windowLog = params.cParams.windowLog; assert(windowLog != 0); /* Copy only compression parameters related to tables. */ params.cParams = *cdict_cParams; params.cParams.windowLog = windowLog; FORWARD_IF_ERROR(ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, ZSTDcrp_leaveDirty, zbuff), ""); assert(cctx->appliedParams.cParams.strategy == cdict_cParams->strategy); assert(cctx->appliedParams.cParams.hashLog == cdict_cParams->hashLog); assert(cctx->appliedParams.cParams.chainLog == cdict_cParams->chainLog); } ZSTD_cwksp_mark_tables_dirty(&cctx->workspace); /* copy tables */ { size_t const chainSize = (cdict_cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cdict_cParams->chainLog); size_t const hSize = (size_t)1 << cdict_cParams->hashLog; memcpy(cctx->blockState.matchState.hashTable, cdict->matchState.hashTable, hSize * sizeof(U32)); memcpy(cctx->blockState.matchState.chainTable, cdict->matchState.chainTable, chainSize * sizeof(U32)); } /* Zero the hashTable3, since the cdict never fills it */ { int const h3log = cctx->blockState.matchState.hashLog3; size_t const h3Size = h3log ? ((size_t)1 << h3log) : 0; assert(cdict->matchState.hashLog3 == 0); memset(cctx->blockState.matchState.hashTable3, 0, h3Size * sizeof(U32)); } ZSTD_cwksp_mark_tables_clean(&cctx->workspace); /* copy dictionary offsets */ { ZSTD_matchState_t const* srcMatchState = &cdict->matchState; ZSTD_matchState_t* dstMatchState = &cctx->blockState.matchState; dstMatchState->window = srcMatchState->window; dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd; } cctx->dictID = cdict->dictID; /* copy block state */ memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState)); return 0; } /* We have a choice between copying the dictionary context into the working * context, or referencing the dictionary context from the working context * in-place. We decide here which strategy to use. */ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, const ZSTD_CCtx_params* params, U64 pledgedSrcSize, ZSTD_buffered_policy_e zbuff) { DEBUGLOG(4, "ZSTD_resetCCtx_usingCDict (pledgedSrcSize=%u)", (unsigned)pledgedSrcSize); if (ZSTD_shouldAttachDict(cdict, params, pledgedSrcSize)) { return ZSTD_resetCCtx_byAttachingCDict( cctx, cdict, *params, pledgedSrcSize, zbuff); } else { return ZSTD_resetCCtx_byCopyingCDict( cctx, cdict, *params, pledgedSrcSize, zbuff); } } /*! ZSTD_copyCCtx_internal() : * Duplicate an existing context `srcCCtx` into another one `dstCCtx`. * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()). * The "context", in this case, refers to the hash and chain tables, * entropy tables, and dictionary references. * `windowLog` value is enforced if != 0, otherwise value is copied from srcCCtx. * @return : 0, or an error code */ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, ZSTD_frameParameters fParams, U64 pledgedSrcSize, ZSTD_buffered_policy_e zbuff) { DEBUGLOG(5, "ZSTD_copyCCtx_internal"); RETURN_ERROR_IF(srcCCtx->stage!=ZSTDcs_init, stage_wrong, "Can't copy a ctx that's not in init stage."); memcpy(&dstCCtx->customMem, &srcCCtx->customMem, sizeof(ZSTD_customMem)); { ZSTD_CCtx_params params = dstCCtx->requestedParams; /* Copy only compression parameters related to tables. */ params.cParams = srcCCtx->appliedParams.cParams; params.fParams = fParams; ZSTD_resetCCtx_internal(dstCCtx, params, pledgedSrcSize, ZSTDcrp_leaveDirty, zbuff); assert(dstCCtx->appliedParams.cParams.windowLog == srcCCtx->appliedParams.cParams.windowLog); assert(dstCCtx->appliedParams.cParams.strategy == srcCCtx->appliedParams.cParams.strategy); assert(dstCCtx->appliedParams.cParams.hashLog == srcCCtx->appliedParams.cParams.hashLog); assert(dstCCtx->appliedParams.cParams.chainLog == srcCCtx->appliedParams.cParams.chainLog); assert(dstCCtx->blockState.matchState.hashLog3 == srcCCtx->blockState.matchState.hashLog3); } ZSTD_cwksp_mark_tables_dirty(&dstCCtx->workspace); /* copy tables */ { size_t const chainSize = (srcCCtx->appliedParams.cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << srcCCtx->appliedParams.cParams.chainLog); size_t const hSize = (size_t)1 << srcCCtx->appliedParams.cParams.hashLog; int const h3log = srcCCtx->blockState.matchState.hashLog3; size_t const h3Size = h3log ? ((size_t)1 << h3log) : 0; memcpy(dstCCtx->blockState.matchState.hashTable, srcCCtx->blockState.matchState.hashTable, hSize * sizeof(U32)); memcpy(dstCCtx->blockState.matchState.chainTable, srcCCtx->blockState.matchState.chainTable, chainSize * sizeof(U32)); memcpy(dstCCtx->blockState.matchState.hashTable3, srcCCtx->blockState.matchState.hashTable3, h3Size * sizeof(U32)); } ZSTD_cwksp_mark_tables_clean(&dstCCtx->workspace); /* copy dictionary offsets */ { const ZSTD_matchState_t* srcMatchState = &srcCCtx->blockState.matchState; ZSTD_matchState_t* dstMatchState = &dstCCtx->blockState.matchState; dstMatchState->window = srcMatchState->window; dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd; } dstCCtx->dictID = srcCCtx->dictID; /* copy block state */ memcpy(dstCCtx->blockState.prevCBlock, srcCCtx->blockState.prevCBlock, sizeof(*srcCCtx->blockState.prevCBlock)); return 0; } /*! ZSTD_copyCCtx() : * Duplicate an existing context `srcCCtx` into another one `dstCCtx`. * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()). * pledgedSrcSize==0 means "unknown". * @return : 0, or an error code */ size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long long pledgedSrcSize) { ZSTD_frameParameters fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ }; ZSTD_buffered_policy_e const zbuff = (ZSTD_buffered_policy_e)(srcCCtx->inBuffSize>0); ZSTD_STATIC_ASSERT((U32)ZSTDb_buffered==1); if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; fParams.contentSizeFlag = (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN); return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx, fParams, pledgedSrcSize, zbuff); } #define ZSTD_ROWSIZE 16 /*! ZSTD_reduceTable() : * reduce table indexes by `reducerValue`, or squash to zero. * PreserveMark preserves "unsorted mark" for btlazy2 strategy. * It must be set to a clear 0/1 value, to remove branch during inlining. * Presume table size is a multiple of ZSTD_ROWSIZE * to help auto-vectorization */ FORCE_INLINE_TEMPLATE void ZSTD_reduceTable_internal (U32* const table, U32 const size, U32 const reducerValue, int const preserveMark) { int const nbRows = (int)size / ZSTD_ROWSIZE; int cellNb = 0; int rowNb; assert((size & (ZSTD_ROWSIZE-1)) == 0); /* multiple of ZSTD_ROWSIZE */ assert(size < (1U<<31)); /* can be casted to int */ #if defined (MEMORY_SANITIZER) && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE) /* To validate that the table re-use logic is sound, and that we don't * access table space that we haven't cleaned, we re-"poison" the table * space every time we mark it dirty. * * This function however is intended to operate on those dirty tables and * re-clean them. So when this function is used correctly, we can unpoison * the memory it operated on. This introduces a blind spot though, since * if we now try to operate on __actually__ poisoned memory, we will not * detect that. */ __msan_unpoison(table, size * sizeof(U32)); #endif for (rowNb=0 ; rowNb < nbRows ; rowNb++) { int column; for (column=0; columncParams.hashLog; ZSTD_reduceTable(ms->hashTable, hSize, reducerValue); } if (params->cParams.strategy != ZSTD_fast) { U32 const chainSize = (U32)1 << params->cParams.chainLog; if (params->cParams.strategy == ZSTD_btlazy2) ZSTD_reduceTable_btlazy2(ms->chainTable, chainSize, reducerValue); else ZSTD_reduceTable(ms->chainTable, chainSize, reducerValue); } if (ms->hashLog3) { U32 const h3Size = (U32)1 << ms->hashLog3; ZSTD_reduceTable(ms->hashTable3, h3Size, reducerValue); } } /*-******************************************************* * Block entropic compression *********************************************************/ /* See doc/zstd_compression_format.md for detailed format description */ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) { const seqDef* const sequences = seqStorePtr->sequencesStart; BYTE* const llCodeTable = seqStorePtr->llCode; BYTE* const ofCodeTable = seqStorePtr->ofCode; BYTE* const mlCodeTable = seqStorePtr->mlCode; U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); U32 u; assert(nbSeq <= seqStorePtr->maxNbSeq); for (u=0; ulongLengthID==1) llCodeTable[seqStorePtr->longLengthPos] = MaxLL; if (seqStorePtr->longLengthID==2) mlCodeTable[seqStorePtr->longLengthPos] = MaxML; } /* ZSTD_useTargetCBlockSize(): * Returns if target compressed block size param is being used. * If used, compression will do best effort to make a compressed block size to be around targetCBlockSize. * Returns 1 if true, 0 otherwise. */ static int ZSTD_useTargetCBlockSize(const ZSTD_CCtx_params* cctxParams) { DEBUGLOG(5, "ZSTD_useTargetCBlockSize (targetCBlockSize=%zu)", cctxParams->targetCBlockSize); return (cctxParams->targetCBlockSize != 0); } /* ZSTD_compressSequences_internal(): * actually compresses both literals and sequences */ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, const ZSTD_entropyCTables_t* prevEntropy, ZSTD_entropyCTables_t* nextEntropy, const ZSTD_CCtx_params* cctxParams, void* dst, size_t dstCapacity, void* entropyWorkspace, size_t entropyWkspSize, const int bmi2) { const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN; ZSTD_strategy const strategy = cctxParams->cParams.strategy; unsigned count[MaxSeq+1]; FSE_CTable* CTable_LitLength = nextEntropy->fse.litlengthCTable; FSE_CTable* CTable_OffsetBits = nextEntropy->fse.offcodeCTable; FSE_CTable* CTable_MatchLength = nextEntropy->fse.matchlengthCTable; U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */ const seqDef* const sequences = seqStorePtr->sequencesStart; const BYTE* const ofCodeTable = seqStorePtr->ofCode; const BYTE* const llCodeTable = seqStorePtr->llCode; const BYTE* const mlCodeTable = seqStorePtr->mlCode; BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstCapacity; BYTE* op = ostart; size_t const nbSeq = (size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart); BYTE* seqHead; BYTE* lastNCount = NULL; DEBUGLOG(5, "ZSTD_compressSequences_internal (nbSeq=%zu)", nbSeq); ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<litStart; size_t const litSize = (size_t)(seqStorePtr->lit - literals); size_t const cSize = ZSTD_compressLiterals( &prevEntropy->huf, &nextEntropy->huf, cctxParams->cParams.strategy, ZSTD_disableLiteralsCompression(cctxParams), op, dstCapacity, literals, litSize, entropyWorkspace, entropyWkspSize, bmi2); FORWARD_IF_ERROR(cSize, "ZSTD_compressLiterals failed"); assert(cSize <= dstCapacity); op += cSize; } /* Sequences Header */ RETURN_ERROR_IF((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead*/, dstSize_tooSmall, "Can't fit seq hdr in output buf!"); if (nbSeq < 128) { *op++ = (BYTE)nbSeq; } else if (nbSeq < LONGNBSEQ) { op[0] = (BYTE)((nbSeq>>8) + 0x80); op[1] = (BYTE)nbSeq; op+=2; } else { op[0]=0xFF; MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)); op+=3; } assert(op <= oend); if (nbSeq==0) { /* Copy the old tables over as if we repeated them */ memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse)); return (size_t)(op - ostart); } /* seqHead : flags for FSE encoding type */ seqHead = op++; assert(op <= oend); /* convert length/distances into codes */ ZSTD_seqToCodes(seqStorePtr); /* build CTable for Literal Lengths */ { unsigned max = MaxLL; size_t const mostFrequent = HIST_countFast_wksp(count, &max, llCodeTable, nbSeq, entropyWorkspace, entropyWkspSize); /* can't fail */ DEBUGLOG(5, "Building LL table"); nextEntropy->fse.litlength_repeatMode = prevEntropy->fse.litlength_repeatMode; LLtype = ZSTD_selectEncodingType(&nextEntropy->fse.litlength_repeatMode, count, max, mostFrequent, nbSeq, LLFSELog, prevEntropy->fse.litlengthCTable, LL_defaultNorm, LL_defaultNormLog, ZSTD_defaultAllowed, strategy); assert(set_basic < set_compressed && set_rle < set_compressed); assert(!(LLtype < set_compressed && nextEntropy->fse.litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable( op, (size_t)(oend - op), CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype, count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, prevEntropy->fse.litlengthCTable, sizeof(prevEntropy->fse.litlengthCTable), entropyWorkspace, entropyWkspSize); FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for LitLens failed"); if (LLtype == set_compressed) lastNCount = op; op += countSize; assert(op <= oend); } } /* build CTable for Offsets */ { unsigned max = MaxOff; size_t const mostFrequent = HIST_countFast_wksp( count, &max, ofCodeTable, nbSeq, entropyWorkspace, entropyWkspSize); /* can't fail */ /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */ ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed; DEBUGLOG(5, "Building OF table"); nextEntropy->fse.offcode_repeatMode = prevEntropy->fse.offcode_repeatMode; Offtype = ZSTD_selectEncodingType(&nextEntropy->fse.offcode_repeatMode, count, max, mostFrequent, nbSeq, OffFSELog, prevEntropy->fse.offcodeCTable, OF_defaultNorm, OF_defaultNormLog, defaultPolicy, strategy); assert(!(Offtype < set_compressed && nextEntropy->fse.offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable( op, (size_t)(oend - op), CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype, count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, prevEntropy->fse.offcodeCTable, sizeof(prevEntropy->fse.offcodeCTable), entropyWorkspace, entropyWkspSize); FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for Offsets failed"); if (Offtype == set_compressed) lastNCount = op; op += countSize; assert(op <= oend); } } /* build CTable for MatchLengths */ { unsigned max = MaxML; size_t const mostFrequent = HIST_countFast_wksp( count, &max, mlCodeTable, nbSeq, entropyWorkspace, entropyWkspSize); /* can't fail */ DEBUGLOG(5, "Building ML table (remaining space : %i)", (int)(oend-op)); nextEntropy->fse.matchlength_repeatMode = prevEntropy->fse.matchlength_repeatMode; MLtype = ZSTD_selectEncodingType(&nextEntropy->fse.matchlength_repeatMode, count, max, mostFrequent, nbSeq, MLFSELog, prevEntropy->fse.matchlengthCTable, ML_defaultNorm, ML_defaultNormLog, ZSTD_defaultAllowed, strategy); assert(!(MLtype < set_compressed && nextEntropy->fse.matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable( op, (size_t)(oend - op), CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype, count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, prevEntropy->fse.matchlengthCTable, sizeof(prevEntropy->fse.matchlengthCTable), entropyWorkspace, entropyWkspSize); FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for MatchLengths failed"); if (MLtype == set_compressed) lastNCount = op; op += countSize; assert(op <= oend); } } *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2)); { size_t const bitstreamSize = ZSTD_encodeSequences( op, (size_t)(oend - op), CTable_MatchLength, mlCodeTable, CTable_OffsetBits, ofCodeTable, CTable_LitLength, llCodeTable, sequences, nbSeq, longOffsets, bmi2); FORWARD_IF_ERROR(bitstreamSize, "ZSTD_encodeSequences failed"); op += bitstreamSize; assert(op <= oend); /* zstd versions <= 1.3.4 mistakenly report corruption when * FSE_readNCount() receives a buffer < 4 bytes. * Fixed by https://github.com/facebook/zstd/pull/1146. * This can happen when the last set_compressed table present is 2 * bytes and the bitstream is only one byte. * In this exceedingly rare case, we will simply emit an uncompressed * block, since it isn't worth optimizing. */ if (lastNCount && (op - lastNCount) < 4) { /* NCountSize >= 2 && bitstreamSize > 0 ==> lastCountSize == 3 */ assert(op - lastNCount == 3); DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.3.4 by " "emitting an uncompressed block."); return 0; } } DEBUGLOG(5, "compressed block size : %u", (unsigned)(op - ostart)); return (size_t)(op - ostart); } MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, const ZSTD_entropyCTables_t* prevEntropy, ZSTD_entropyCTables_t* nextEntropy, const ZSTD_CCtx_params* cctxParams, void* dst, size_t dstCapacity, size_t srcSize, void* entropyWorkspace, size_t entropyWkspSize, int bmi2) { size_t const cSize = ZSTD_compressSequences_internal( seqStorePtr, prevEntropy, nextEntropy, cctxParams, dst, dstCapacity, entropyWorkspace, entropyWkspSize, bmi2); if (cSize == 0) return 0; /* When srcSize <= dstCapacity, there is enough space to write a raw uncompressed block. * Since we ran out of space, block must be not compressible, so fall back to raw uncompressed block. */ if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity)) return 0; /* block not compressed */ FORWARD_IF_ERROR(cSize, "ZSTD_compressSequences_internal failed"); /* Check compressibility */ { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, cctxParams->cParams.strategy); if (cSize >= maxCSize) return 0; /* block not compressed */ } return cSize; } /* ZSTD_selectBlockCompressor() : * Not static, but internal use only (used by long distance matcher) * assumption : strat is a valid strategy */ ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e dictMode) { static const ZSTD_blockCompressor blockCompressor[3][ZSTD_STRATEGY_MAX+1] = { { ZSTD_compressBlock_fast /* default for 0 */, ZSTD_compressBlock_fast, ZSTD_compressBlock_doubleFast, ZSTD_compressBlock_greedy, ZSTD_compressBlock_lazy, ZSTD_compressBlock_lazy2, ZSTD_compressBlock_btlazy2, ZSTD_compressBlock_btopt, ZSTD_compressBlock_btultra, ZSTD_compressBlock_btultra2 }, { ZSTD_compressBlock_fast_extDict /* default for 0 */, ZSTD_compressBlock_fast_extDict, ZSTD_compressBlock_doubleFast_extDict, ZSTD_compressBlock_greedy_extDict, ZSTD_compressBlock_lazy_extDict, ZSTD_compressBlock_lazy2_extDict, ZSTD_compressBlock_btlazy2_extDict, ZSTD_compressBlock_btopt_extDict, ZSTD_compressBlock_btultra_extDict, ZSTD_compressBlock_btultra_extDict }, { ZSTD_compressBlock_fast_dictMatchState /* default for 0 */, ZSTD_compressBlock_fast_dictMatchState, ZSTD_compressBlock_doubleFast_dictMatchState, ZSTD_compressBlock_greedy_dictMatchState, ZSTD_compressBlock_lazy_dictMatchState, ZSTD_compressBlock_lazy2_dictMatchState, ZSTD_compressBlock_btlazy2_dictMatchState, ZSTD_compressBlock_btopt_dictMatchState, ZSTD_compressBlock_btultra_dictMatchState, ZSTD_compressBlock_btultra_dictMatchState } }; ZSTD_blockCompressor selectedCompressor; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, strat)); selectedCompressor = blockCompressor[(int)dictMode][(int)strat]; assert(selectedCompressor != NULL); return selectedCompressor; } static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr, const BYTE* anchor, size_t lastLLSize) { memcpy(seqStorePtr->lit, anchor, lastLLSize); seqStorePtr->lit += lastLLSize; } void ZSTD_resetSeqStore(seqStore_t* ssPtr) { ssPtr->lit = ssPtr->litStart; ssPtr->sequences = ssPtr->sequencesStart; ssPtr->longLengthID = 0; } typedef enum { ZSTDbss_compress, ZSTDbss_noCompress } ZSTD_buildSeqStore_e; static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize) { ZSTD_matchState_t* const ms = &zc->blockState.matchState; DEBUGLOG(5, "ZSTD_buildSeqStore (srcSize=%zu)", srcSize); assert(srcSize <= ZSTD_BLOCKSIZE_MAX); /* Assert that we have correctly flushed the ctx params into the ms's copy */ ZSTD_assertEqualCParams(zc->appliedParams.cParams, ms->cParams); if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) { ZSTD_ldm_skipSequences(&zc->externSeqStore, srcSize, zc->appliedParams.cParams.minMatch); return ZSTDbss_noCompress; /* don't even attempt compression below a certain srcSize */ } ZSTD_resetSeqStore(&(zc->seqStore)); /* required for optimal parser to read stats from dictionary */ ms->opt.symbolCosts = &zc->blockState.prevCBlock->entropy; /* tell the optimal parser how we expect to compress literals */ ms->opt.literalCompressionMode = zc->appliedParams.literalCompressionMode; /* a gap between an attached dict and the current window is not safe, * they must remain adjacent, * and when that stops being the case, the dict must be unset */ assert(ms->dictMatchState == NULL || ms->loadedDictEnd == ms->window.dictLimit); /* limited update after a very long match */ { const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; const U32 current = (U32)(istart-base); if (sizeof(ptrdiff_t)==8) assert(istart - base < (ptrdiff_t)(U32)(-1)); /* ensure no overflow */ if (current > ms->nextToUpdate + 384) ms->nextToUpdate = current - MIN(192, (U32)(current - ms->nextToUpdate - 384)); } /* select and store sequences */ { ZSTD_dictMode_e const dictMode = ZSTD_matchState_dictMode(ms); size_t lastLLSize; { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i]; } if (zc->externSeqStore.pos < zc->externSeqStore.size) { assert(!zc->appliedParams.ldmParams.enableLdm); /* Updates ldmSeqStore.pos */ lastLLSize = ZSTD_ldm_blockCompress(&zc->externSeqStore, ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize); assert(zc->externSeqStore.pos <= zc->externSeqStore.size); } else if (zc->appliedParams.ldmParams.enableLdm) { rawSeqStore_t ldmSeqStore = {NULL, 0, 0, 0}; ldmSeqStore.seq = zc->ldmSequences; ldmSeqStore.capacity = zc->maxNbLdmSequences; /* Updates ldmSeqStore.size */ FORWARD_IF_ERROR(ZSTD_ldm_generateSequences(&zc->ldmState, &ldmSeqStore, &zc->appliedParams.ldmParams, src, srcSize), ""); /* Updates ldmSeqStore.pos */ lastLLSize = ZSTD_ldm_blockCompress(&ldmSeqStore, ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize); assert(ldmSeqStore.pos == ldmSeqStore.size); } else { /* not long range mode */ ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, dictMode); lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize); } { const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize; ZSTD_storeLastLiterals(&zc->seqStore, lastLiterals, lastLLSize); } } return ZSTDbss_compress; } static void ZSTD_copyBlockSequences(ZSTD_CCtx* zc) { const seqStore_t* seqStore = ZSTD_getSeqStore(zc); const seqDef* seqs = seqStore->sequencesStart; size_t seqsSize = seqStore->sequences - seqs; ZSTD_Sequence* outSeqs = &zc->seqCollector.seqStart[zc->seqCollector.seqIndex]; size_t i; size_t position; int repIdx; assert(zc->seqCollector.seqIndex + 1 < zc->seqCollector.maxSequences); for (i = 0, position = 0; i < seqsSize; ++i) { outSeqs[i].offset = seqs[i].offset; outSeqs[i].litLength = seqs[i].litLength; outSeqs[i].matchLength = seqs[i].matchLength + MINMATCH; if (i == seqStore->longLengthPos) { if (seqStore->longLengthID == 1) { outSeqs[i].litLength += 0x10000; } else if (seqStore->longLengthID == 2) { outSeqs[i].matchLength += 0x10000; } } if (outSeqs[i].offset <= ZSTD_REP_NUM) { outSeqs[i].rep = outSeqs[i].offset; repIdx = (unsigned int)i - outSeqs[i].offset; if (outSeqs[i].litLength == 0) { if (outSeqs[i].offset < 3) { --repIdx; } else { repIdx = (unsigned int)i - 1; } ++outSeqs[i].rep; } assert(repIdx >= -3); outSeqs[i].offset = repIdx >= 0 ? outSeqs[repIdx].offset : repStartValue[-repIdx - 1]; if (outSeqs[i].rep == 4) { --outSeqs[i].offset; } } else { outSeqs[i].offset -= ZSTD_REP_NUM; } position += outSeqs[i].litLength; outSeqs[i].matchPos = (unsigned int)position; position += outSeqs[i].matchLength; } zc->seqCollector.seqIndex += seqsSize; } size_t ZSTD_getSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs, size_t outSeqsSize, const void* src, size_t srcSize) { const size_t dstCapacity = ZSTD_compressBound(srcSize); void* dst = ZSTD_malloc(dstCapacity, ZSTD_defaultCMem); SeqCollector seqCollector; RETURN_ERROR_IF(dst == NULL, memory_allocation, "NULL pointer!"); seqCollector.collectSequences = 1; seqCollector.seqStart = outSeqs; seqCollector.seqIndex = 0; seqCollector.maxSequences = outSeqsSize; zc->seqCollector = seqCollector; ZSTD_compress2(zc, dst, dstCapacity, src, srcSize); ZSTD_free(dst, ZSTD_defaultCMem); return zc->seqCollector.seqIndex; } /* Returns true if the given block is a RLE block */ static int ZSTD_isRLE(const BYTE *ip, size_t length) { size_t i; if (length < 2) return 1; for (i = 1; i < length; ++i) { if (ip[0] != ip[i]) return 0; } return 1; } /* Returns true if the given block may be RLE. * This is just a heuristic based on the compressibility. * It may return both false positives and false negatives. */ static int ZSTD_maybeRLE(seqStore_t const* seqStore) { size_t const nbSeqs = (size_t)(seqStore->sequences - seqStore->sequencesStart); size_t const nbLits = (size_t)(seqStore->lit - seqStore->litStart); return nbSeqs < 4 && nbLits < 10; } static void ZSTD_confirmRepcodesAndEntropyTables(ZSTD_CCtx* zc) { ZSTD_compressedBlockState_t* const tmp = zc->blockState.prevCBlock; zc->blockState.prevCBlock = zc->blockState.nextCBlock; zc->blockState.nextCBlock = tmp; } static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 frame) { /* This the upper bound for the length of an rle block. * This isn't the actual upper bound. Finding the real threshold * needs further investigation. */ const U32 rleMaxLength = 25; size_t cSize; const BYTE* ip = (const BYTE*)src; BYTE* op = (BYTE*)dst; DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)", (unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit, (unsigned)zc->blockState.matchState.nextToUpdate); { const size_t bss = ZSTD_buildSeqStore(zc, src, srcSize); FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed"); if (bss == ZSTDbss_noCompress) { cSize = 0; goto out; } } if (zc->seqCollector.collectSequences) { ZSTD_copyBlockSequences(zc); return 0; } /* encode sequences and literals */ cSize = ZSTD_compressSequences(&zc->seqStore, &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy, &zc->appliedParams, dst, dstCapacity, srcSize, zc->entropyWorkspace, HUF_WORKSPACE_SIZE /* statically allocated in resetCCtx */, zc->bmi2); if (frame && /* We don't want to emit our first block as a RLE even if it qualifies because * doing so will cause the decoder (cli only) to throw a "should consume all input error." * This is only an issue for zstd <= v1.4.3 */ !zc->isFirstBlock && cSize < rleMaxLength && ZSTD_isRLE(ip, srcSize)) { cSize = 1; op[0] = ip[0]; } out: if (!ZSTD_isError(cSize) && cSize > 1) { ZSTD_confirmRepcodesAndEntropyTables(zc); } /* We check that dictionaries have offset codes available for the first * block. After the first block, the offcode table might not have large * enough codes to represent the offsets in the data. */ if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid) zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check; return cSize; } static size_t ZSTD_compressBlock_targetCBlockSize_body(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const size_t bss, U32 lastBlock) { DEBUGLOG(6, "Attempting ZSTD_compressSuperBlock()"); if (bss == ZSTDbss_compress) { if (/* We don't want to emit our first block as a RLE even if it qualifies because * doing so will cause the decoder (cli only) to throw a "should consume all input error." * This is only an issue for zstd <= v1.4.3 */ !zc->isFirstBlock && ZSTD_maybeRLE(&zc->seqStore) && ZSTD_isRLE((BYTE const*)src, srcSize)) { return ZSTD_rleCompressBlock(dst, dstCapacity, *(BYTE const*)src, srcSize, lastBlock); } /* Attempt superblock compression. * * Note that compressed size of ZSTD_compressSuperBlock() is not bound by the * standard ZSTD_compressBound(). This is a problem, because even if we have * space now, taking an extra byte now could cause us to run out of space later * and violate ZSTD_compressBound(). * * Define blockBound(blockSize) = blockSize + ZSTD_blockHeaderSize. * * In order to respect ZSTD_compressBound() we must attempt to emit a raw * uncompressed block in these cases: * * cSize == 0: Return code for an uncompressed block. * * cSize == dstSize_tooSmall: We may have expanded beyond blockBound(srcSize). * ZSTD_noCompressBlock() will return dstSize_tooSmall if we are really out of * output space. * * cSize >= blockBound(srcSize): We have expanded the block too much so * emit an uncompressed block. */ { size_t const cSize = ZSTD_compressSuperBlock(zc, dst, dstCapacity, src, srcSize, lastBlock); if (cSize != ERROR(dstSize_tooSmall)) { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, zc->appliedParams.cParams.strategy); FORWARD_IF_ERROR(cSize, "ZSTD_compressSuperBlock failed"); if (cSize != 0 && cSize < maxCSize + ZSTD_blockHeaderSize) { ZSTD_confirmRepcodesAndEntropyTables(zc); return cSize; } } } } DEBUGLOG(6, "Resorting to ZSTD_noCompressBlock()"); /* Superblock compression failed, attempt to emit a single no compress block. * The decoder will be able to stream this block since it is uncompressed. */ return ZSTD_noCompressBlock(dst, dstCapacity, src, srcSize, lastBlock); } static size_t ZSTD_compressBlock_targetCBlockSize(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock) { size_t cSize = 0; const size_t bss = ZSTD_buildSeqStore(zc, src, srcSize); DEBUGLOG(5, "ZSTD_compressBlock_targetCBlockSize (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u, srcSize=%zu)", (unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit, (unsigned)zc->blockState.matchState.nextToUpdate, srcSize); FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed"); cSize = ZSTD_compressBlock_targetCBlockSize_body(zc, dst, dstCapacity, src, srcSize, bss, lastBlock); FORWARD_IF_ERROR(cSize, "ZSTD_compressBlock_targetCBlockSize_body failed"); if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid) zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check; return cSize; } static void ZSTD_overflowCorrectIfNeeded(ZSTD_matchState_t* ms, ZSTD_cwksp* ws, ZSTD_CCtx_params const* params, void const* ip, void const* iend) { if (ZSTD_window_needOverflowCorrection(ms->window, iend)) { U32 const maxDist = (U32)1 << params->cParams.windowLog; U32 const cycleLog = ZSTD_cycleLog(params->cParams.chainLog, params->cParams.strategy); U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, maxDist, ip); ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30); ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_32 <= 30); ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31); ZSTD_cwksp_mark_tables_dirty(ws); ZSTD_reduceIndex(ms, params, correction); ZSTD_cwksp_mark_tables_clean(ws); if (ms->nextToUpdate < correction) ms->nextToUpdate = 0; else ms->nextToUpdate -= correction; /* invalidate dictionaries on overflow correction */ ms->loadedDictEnd = 0; ms->dictMatchState = NULL; } } /*! ZSTD_compress_frameChunk() : * Compress a chunk of data into one or multiple blocks. * All blocks will be terminated, all input will be consumed. * Function will issue an error if there is not enough `dstCapacity` to hold the compressed content. * Frame is supposed already started (header already produced) * @return : compressed size, or an error code */ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastFrameChunk) { size_t blockSize = cctx->blockSize; size_t remaining = srcSize; const BYTE* ip = (const BYTE*)src; BYTE* const ostart = (BYTE*)dst; BYTE* op = ostart; U32 const maxDist = (U32)1 << cctx->appliedParams.cParams.windowLog; assert(cctx->appliedParams.cParams.windowLog <= ZSTD_WINDOWLOG_MAX); DEBUGLOG(5, "ZSTD_compress_frameChunk (blockSize=%u)", (unsigned)blockSize); if (cctx->appliedParams.fParams.checksumFlag && srcSize) XXH64_update(&cctx->xxhState, src, srcSize); while (remaining) { ZSTD_matchState_t* const ms = &cctx->blockState.matchState; U32 const lastBlock = lastFrameChunk & (blockSize >= remaining); RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE, dstSize_tooSmall, "not enough space to store compressed block"); if (remaining < blockSize) blockSize = remaining; ZSTD_overflowCorrectIfNeeded( ms, &cctx->workspace, &cctx->appliedParams, ip, ip + blockSize); ZSTD_checkDictValidity(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd, &ms->dictMatchState); /* Ensure hash/chain table insertion resumes no sooner than lowlimit */ if (ms->nextToUpdate < ms->window.lowLimit) ms->nextToUpdate = ms->window.lowLimit; { size_t cSize; if (ZSTD_useTargetCBlockSize(&cctx->appliedParams)) { cSize = ZSTD_compressBlock_targetCBlockSize(cctx, op, dstCapacity, ip, blockSize, lastBlock); FORWARD_IF_ERROR(cSize, "ZSTD_compressBlock_targetCBlockSize failed"); assert(cSize > 0); assert(cSize <= blockSize + ZSTD_blockHeaderSize); } else { cSize = ZSTD_compressBlock_internal(cctx, op+ZSTD_blockHeaderSize, dstCapacity-ZSTD_blockHeaderSize, ip, blockSize, 1 /* frame */); FORWARD_IF_ERROR(cSize, "ZSTD_compressBlock_internal failed"); if (cSize == 0) { /* block is not compressible */ cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock); FORWARD_IF_ERROR(cSize, "ZSTD_noCompressBlock failed"); } else { U32 const cBlockHeader = cSize == 1 ? lastBlock + (((U32)bt_rle)<<1) + (U32)(blockSize << 3) : lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3); MEM_writeLE24(op, cBlockHeader); cSize += ZSTD_blockHeaderSize; } } ip += blockSize; assert(remaining >= blockSize); remaining -= blockSize; op += cSize; assert(dstCapacity >= cSize); dstCapacity -= cSize; cctx->isFirstBlock = 0; DEBUGLOG(5, "ZSTD_compress_frameChunk: adding a block of size %u", (unsigned)cSize); } } if (lastFrameChunk && (op>ostart)) cctx->stage = ZSTDcs_ending; return (size_t)(op-ostart); } static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity, const ZSTD_CCtx_params* params, U64 pledgedSrcSize, U32 dictID) { BYTE* const op = (BYTE*)dst; U32 const dictIDSizeCodeLength = (dictID>0) + (dictID>=256) + (dictID>=65536); /* 0-3 */ U32 const dictIDSizeCode = params->fParams.noDictIDFlag ? 0 : dictIDSizeCodeLength; /* 0-3 */ U32 const checksumFlag = params->fParams.checksumFlag>0; U32 const windowSize = (U32)1 << params->cParams.windowLog; U32 const singleSegment = params->fParams.contentSizeFlag && (windowSize >= pledgedSrcSize); BYTE const windowLogByte = (BYTE)((params->cParams.windowLog - ZSTD_WINDOWLOG_ABSOLUTEMIN) << 3); U32 const fcsCode = params->fParams.contentSizeFlag ? (pledgedSrcSize>=256) + (pledgedSrcSize>=65536+256) + (pledgedSrcSize>=0xFFFFFFFFU) : 0; /* 0-3 */ BYTE const frameHeaderDescriptionByte = (BYTE)(dictIDSizeCode + (checksumFlag<<2) + (singleSegment<<5) + (fcsCode<<6) ); size_t pos=0; assert(!(params->fParams.contentSizeFlag && pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN)); RETURN_ERROR_IF(dstCapacity < ZSTD_FRAMEHEADERSIZE_MAX, dstSize_tooSmall, "dst buf is too small to fit worst-case frame header size."); DEBUGLOG(4, "ZSTD_writeFrameHeader : dictIDFlag : %u ; dictID : %u ; dictIDSizeCode : %u", !params->fParams.noDictIDFlag, (unsigned)dictID, (unsigned)dictIDSizeCode); if (params->format == ZSTD_f_zstd1) { MEM_writeLE32(dst, ZSTD_MAGICNUMBER); pos = 4; } op[pos++] = frameHeaderDescriptionByte; if (!singleSegment) op[pos++] = windowLogByte; switch(dictIDSizeCode) { default: assert(0); /* impossible */ case 0 : break; case 1 : op[pos] = (BYTE)(dictID); pos++; break; case 2 : MEM_writeLE16(op+pos, (U16)dictID); pos+=2; break; case 3 : MEM_writeLE32(op+pos, dictID); pos+=4; break; } switch(fcsCode) { default: assert(0); /* impossible */ case 0 : if (singleSegment) op[pos++] = (BYTE)(pledgedSrcSize); break; case 1 : MEM_writeLE16(op+pos, (U16)(pledgedSrcSize-256)); pos+=2; break; case 2 : MEM_writeLE32(op+pos, (U32)(pledgedSrcSize)); pos+=4; break; case 3 : MEM_writeLE64(op+pos, (U64)(pledgedSrcSize)); pos+=8; break; } return pos; } /* ZSTD_writeLastEmptyBlock() : * output an empty Block with end-of-frame mark to complete a frame * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h)) * or an error code if `dstCapacity` is too small (stage != ZSTDcs_init, stage_wrong, "wrong cctx stage"); RETURN_ERROR_IF(cctx->appliedParams.ldmParams.enableLdm, parameter_unsupported, "incompatible with ldm"); cctx->externSeqStore.seq = seq; cctx->externSeqStore.size = nbSeq; cctx->externSeqStore.capacity = nbSeq; cctx->externSeqStore.pos = 0; return 0; } static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 frame, U32 lastFrameChunk) { ZSTD_matchState_t* const ms = &cctx->blockState.matchState; size_t fhSize = 0; DEBUGLOG(5, "ZSTD_compressContinue_internal, stage: %u, srcSize: %u", cctx->stage, (unsigned)srcSize); RETURN_ERROR_IF(cctx->stage==ZSTDcs_created, stage_wrong, "missing init (ZSTD_compressBegin)"); if (frame && (cctx->stage==ZSTDcs_init)) { fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, &cctx->appliedParams, cctx->pledgedSrcSizePlusOne-1, cctx->dictID); FORWARD_IF_ERROR(fhSize, "ZSTD_writeFrameHeader failed"); assert(fhSize <= dstCapacity); dstCapacity -= fhSize; dst = (char*)dst + fhSize; cctx->stage = ZSTDcs_ongoing; } if (!srcSize) return fhSize; /* do not generate an empty block if no input */ if (!ZSTD_window_update(&ms->window, src, srcSize)) { ms->nextToUpdate = ms->window.dictLimit; } if (cctx->appliedParams.ldmParams.enableLdm) { ZSTD_window_update(&cctx->ldmState.window, src, srcSize); } if (!frame) { /* overflow check and correction for block mode */ ZSTD_overflowCorrectIfNeeded( ms, &cctx->workspace, &cctx->appliedParams, src, (BYTE const*)src + srcSize); } DEBUGLOG(5, "ZSTD_compressContinue_internal (blockSize=%u)", (unsigned)cctx->blockSize); { size_t const cSize = frame ? ZSTD_compress_frameChunk (cctx, dst, dstCapacity, src, srcSize, lastFrameChunk) : ZSTD_compressBlock_internal (cctx, dst, dstCapacity, src, srcSize, 0 /* frame */); FORWARD_IF_ERROR(cSize, "%s", frame ? "ZSTD_compress_frameChunk failed" : "ZSTD_compressBlock_internal failed"); cctx->consumedSrcSize += srcSize; cctx->producedCSize += (cSize + fhSize); assert(!(cctx->appliedParams.fParams.contentSizeFlag && cctx->pledgedSrcSizePlusOne == 0)); if (cctx->pledgedSrcSizePlusOne != 0) { /* control src size */ ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN == (unsigned long long)-1); RETURN_ERROR_IF( cctx->consumedSrcSize+1 > cctx->pledgedSrcSizePlusOne, srcSize_wrong, "error : pledgedSrcSize = %u, while realSrcSize >= %u", (unsigned)cctx->pledgedSrcSizePlusOne-1, (unsigned)cctx->consumedSrcSize); } return cSize + fhSize; } } size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressContinue (srcSize=%u)", (unsigned)srcSize); return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1 /* frame mode */, 0 /* last chunk */); } size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx) { ZSTD_compressionParameters const cParams = cctx->appliedParams.cParams; assert(!ZSTD_checkCParams(cParams)); return MIN (ZSTD_BLOCKSIZE_MAX, (U32)1 << cParams.windowLog); } size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock: srcSize = %u", (unsigned)srcSize); { size_t const blockSizeMax = ZSTD_getBlockSize(cctx); RETURN_ERROR_IF(srcSize > blockSizeMax, srcSize_wrong, "input is larger than a block"); } return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0 /* frame mode */, 0 /* last chunk */); } /*! ZSTD_loadDictionaryContent() : * @return : 0, or an error code */ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms, ldmState_t* ls, ZSTD_cwksp* ws, ZSTD_CCtx_params const* params, const void* src, size_t srcSize, ZSTD_dictTableLoadMethod_e dtlm) { const BYTE* ip = (const BYTE*) src; const BYTE* const iend = ip + srcSize; ZSTD_window_update(&ms->window, src, srcSize); ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base); if (params->ldmParams.enableLdm && ls != NULL) { ZSTD_window_update(&ls->window, src, srcSize); ls->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ls->window.base); } /* Assert that we the ms params match the params we're being given */ ZSTD_assertEqualCParams(params->cParams, ms->cParams); if (srcSize <= HASH_READ_SIZE) return 0; while (iend - ip > HASH_READ_SIZE) { size_t const remaining = (size_t)(iend - ip); size_t const chunk = MIN(remaining, ZSTD_CHUNKSIZE_MAX); const BYTE* const ichunk = ip + chunk; ZSTD_overflowCorrectIfNeeded(ms, ws, params, ip, ichunk); if (params->ldmParams.enableLdm && ls != NULL) ZSTD_ldm_fillHashTable(ls, (const BYTE*)src, (const BYTE*)src + srcSize, ¶ms->ldmParams); switch(params->cParams.strategy) { case ZSTD_fast: ZSTD_fillHashTable(ms, ichunk, dtlm); break; case ZSTD_dfast: ZSTD_fillDoubleHashTable(ms, ichunk, dtlm); break; case ZSTD_greedy: case ZSTD_lazy: case ZSTD_lazy2: if (chunk >= HASH_READ_SIZE) ZSTD_insertAndFindFirstIndex(ms, ichunk-HASH_READ_SIZE); break; case ZSTD_btlazy2: /* we want the dictionary table fully sorted */ case ZSTD_btopt: case ZSTD_btultra: case ZSTD_btultra2: if (chunk >= HASH_READ_SIZE) ZSTD_updateTree(ms, ichunk-HASH_READ_SIZE, ichunk); break; default: assert(0); /* not possible : not a valid strategy id */ } ip = ichunk; } ms->nextToUpdate = (U32)(iend - ms->window.base); return 0; } /* Dictionaries that assign zero probability to symbols that show up causes problems when FSE encoding. Refuse dictionaries that assign zero probability to symbols that we may encounter during compression. NOTE: This behavior is not standard and could be improved in the future. */ static size_t ZSTD_checkDictNCount(short* normalizedCounter, unsigned dictMaxSymbolValue, unsigned maxSymbolValue) { U32 s; RETURN_ERROR_IF(dictMaxSymbolValue < maxSymbolValue, dictionary_corrupted, "dict fse tables don't have all symbols"); for (s = 0; s <= maxSymbolValue; ++s) { RETURN_ERROR_IF(normalizedCounter[s] == 0, dictionary_corrupted, "dict fse tables don't have all symbols"); } return 0; } size_t ZSTD_loadCEntropy(ZSTD_compressedBlockState_t* bs, void* workspace, short* offcodeNCount, unsigned* offcodeMaxValue, const void* const dict, size_t dictSize) { const BYTE* dictPtr = (const BYTE*)dict; /* skip magic num and dict ID */ const BYTE* const dictEnd = dictPtr + dictSize; dictPtr += 8; bs->entropy.huf.repeatMode = HUF_repeat_check; { unsigned maxSymbolValue = 255; unsigned hasZeroWeights = 1; size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)bs->entropy.huf.CTable, &maxSymbolValue, dictPtr, dictEnd-dictPtr, &hasZeroWeights); /* We only set the loaded table as valid if it contains all non-zero * weights. Otherwise, we set it to check */ if (!hasZeroWeights) bs->entropy.huf.repeatMode = HUF_repeat_valid; RETURN_ERROR_IF(HUF_isError(hufHeaderSize), dictionary_corrupted, ""); RETURN_ERROR_IF(maxSymbolValue < 255, dictionary_corrupted, ""); dictPtr += hufHeaderSize; } { unsigned offcodeLog; size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr); RETURN_ERROR_IF(FSE_isError(offcodeHeaderSize), dictionary_corrupted, ""); RETURN_ERROR_IF(offcodeLog > OffFSELog, dictionary_corrupted, ""); /* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */ /* fill all offset symbols to avoid garbage at end of table */ RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp( bs->entropy.fse.offcodeCTable, offcodeNCount, MaxOff, offcodeLog, workspace, HUF_WORKSPACE_SIZE)), dictionary_corrupted, ""); dictPtr += offcodeHeaderSize; } { short matchlengthNCount[MaxML+1]; unsigned matchlengthMaxValue = MaxML, matchlengthLog; size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr); RETURN_ERROR_IF(FSE_isError(matchlengthHeaderSize), dictionary_corrupted, ""); RETURN_ERROR_IF(matchlengthLog > MLFSELog, dictionary_corrupted, ""); /* Every match length code must have non-zero probability */ FORWARD_IF_ERROR( ZSTD_checkDictNCount(matchlengthNCount, matchlengthMaxValue, MaxML), ""); RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp( bs->entropy.fse.matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, workspace, HUF_WORKSPACE_SIZE)), dictionary_corrupted, ""); dictPtr += matchlengthHeaderSize; } { short litlengthNCount[MaxLL+1]; unsigned litlengthMaxValue = MaxLL, litlengthLog; size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr); RETURN_ERROR_IF(FSE_isError(litlengthHeaderSize), dictionary_corrupted, ""); RETURN_ERROR_IF(litlengthLog > LLFSELog, dictionary_corrupted, ""); /* Every literal length code must have non-zero probability */ FORWARD_IF_ERROR( ZSTD_checkDictNCount(litlengthNCount, litlengthMaxValue, MaxLL), ""); RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp( bs->entropy.fse.litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, workspace, HUF_WORKSPACE_SIZE)), dictionary_corrupted, ""); dictPtr += litlengthHeaderSize; } RETURN_ERROR_IF(dictPtr+12 > dictEnd, dictionary_corrupted, ""); bs->rep[0] = MEM_readLE32(dictPtr+0); bs->rep[1] = MEM_readLE32(dictPtr+4); bs->rep[2] = MEM_readLE32(dictPtr+8); dictPtr += 12; return dictPtr - (const BYTE*)dict; } /* Dictionary format : * See : * https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format */ /*! ZSTD_loadZstdDictionary() : * @return : dictID, or an error code * assumptions : magic number supposed already checked * dictSize supposed >= 8 */ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, ZSTD_matchState_t* ms, ZSTD_cwksp* ws, ZSTD_CCtx_params const* params, const void* dict, size_t dictSize, ZSTD_dictTableLoadMethod_e dtlm, void* workspace) { const BYTE* dictPtr = (const BYTE*)dict; const BYTE* const dictEnd = dictPtr + dictSize; short offcodeNCount[MaxOff+1]; unsigned offcodeMaxValue = MaxOff; size_t dictID; size_t eSize; ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<= 8); assert(MEM_readLE32(dictPtr) == ZSTD_MAGIC_DICTIONARY); dictID = params->fParams.noDictIDFlag ? 0 : MEM_readLE32(dictPtr + 4 /* skip magic number */ ); eSize = ZSTD_loadCEntropy(bs, workspace, offcodeNCount, &offcodeMaxValue, dict, dictSize); FORWARD_IF_ERROR(eSize, "ZSTD_loadCEntropy failed"); dictPtr += eSize; { size_t const dictContentSize = (size_t)(dictEnd - dictPtr); U32 offcodeMax = MaxOff; if (dictContentSize <= ((U32)-1) - 128 KB) { U32 const maxOffset = (U32)dictContentSize + 128 KB; /* The maximum offset that must be supported */ offcodeMax = ZSTD_highbit32(maxOffset); /* Calculate minimum offset code required to represent maxOffset */ } /* All offset values <= dictContentSize + 128 KB must be representable */ FORWARD_IF_ERROR(ZSTD_checkDictNCount(offcodeNCount, offcodeMaxValue, MIN(offcodeMax, MaxOff)), ""); /* All repCodes must be <= dictContentSize and != 0*/ { U32 u; for (u=0; u<3; u++) { RETURN_ERROR_IF(bs->rep[u] == 0, dictionary_corrupted, ""); RETURN_ERROR_IF(bs->rep[u] > dictContentSize, dictionary_corrupted, ""); } } bs->entropy.fse.offcode_repeatMode = FSE_repeat_valid; bs->entropy.fse.matchlength_repeatMode = FSE_repeat_valid; bs->entropy.fse.litlength_repeatMode = FSE_repeat_valid; FORWARD_IF_ERROR(ZSTD_loadDictionaryContent( ms, NULL, ws, params, dictPtr, dictContentSize, dtlm), ""); return dictID; } } /** ZSTD_compress_insertDictionary() : * @return : dictID, or an error code */ static size_t ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, ZSTD_matchState_t* ms, ldmState_t* ls, ZSTD_cwksp* ws, const ZSTD_CCtx_params* params, const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType, ZSTD_dictTableLoadMethod_e dtlm, void* workspace) { DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize); if ((dict==NULL) || (dictSize<8)) { RETURN_ERROR_IF(dictContentType == ZSTD_dct_fullDict, dictionary_wrong, ""); return 0; } ZSTD_reset_compressedBlockState(bs); /* dict restricted modes */ if (dictContentType == ZSTD_dct_rawContent) return ZSTD_loadDictionaryContent(ms, ls, ws, params, dict, dictSize, dtlm); if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) { if (dictContentType == ZSTD_dct_auto) { DEBUGLOG(4, "raw content dictionary detected"); return ZSTD_loadDictionaryContent( ms, ls, ws, params, dict, dictSize, dtlm); } RETURN_ERROR_IF(dictContentType == ZSTD_dct_fullDict, dictionary_wrong, ""); assert(0); /* impossible */ } /* dict as full zstd dictionary */ return ZSTD_loadZstdDictionary( bs, ms, ws, params, dict, dictSize, dtlm, workspace); } #define ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF (128 KB) #define ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER (6) /*! ZSTD_compressBegin_internal() : * @return : 0, or an error code */ static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType, ZSTD_dictTableLoadMethod_e dtlm, const ZSTD_CDict* cdict, const ZSTD_CCtx_params* params, U64 pledgedSrcSize, ZSTD_buffered_policy_e zbuff) { DEBUGLOG(4, "ZSTD_compressBegin_internal: wlog=%u", params->cParams.windowLog); /* params are supposed to be fully validated at this point */ assert(!ZSTD_isError(ZSTD_checkCParams(params->cParams))); assert(!((dict) && (cdict))); /* either dict or cdict, not both */ if ( (cdict) && (cdict->dictContentSize > 0) && ( pledgedSrcSize < ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF || pledgedSrcSize < cdict->dictContentSize * ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN || cdict->compressionLevel == 0) && (params->attachDictPref != ZSTD_dictForceLoad) ) { return ZSTD_resetCCtx_usingCDict(cctx, cdict, params, pledgedSrcSize, zbuff); } FORWARD_IF_ERROR( ZSTD_resetCCtx_internal(cctx, *params, pledgedSrcSize, ZSTDcrp_makeClean, zbuff) , ""); { size_t const dictID = cdict ? ZSTD_compress_insertDictionary( cctx->blockState.prevCBlock, &cctx->blockState.matchState, &cctx->ldmState, &cctx->workspace, &cctx->appliedParams, cdict->dictContent, cdict->dictContentSize, dictContentType, dtlm, cctx->entropyWorkspace) : ZSTD_compress_insertDictionary( cctx->blockState.prevCBlock, &cctx->blockState.matchState, &cctx->ldmState, &cctx->workspace, &cctx->appliedParams, dict, dictSize, dictContentType, dtlm, cctx->entropyWorkspace); FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed"); assert(dictID <= UINT_MAX); cctx->dictID = (U32)dictID; } return 0; } size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType, ZSTD_dictTableLoadMethod_e dtlm, const ZSTD_CDict* cdict, const ZSTD_CCtx_params* params, unsigned long long pledgedSrcSize) { DEBUGLOG(4, "ZSTD_compressBegin_advanced_internal: wlog=%u", params->cParams.windowLog); /* compression parameters verification and optimization */ FORWARD_IF_ERROR( ZSTD_checkCParams(params->cParams) , ""); return ZSTD_compressBegin_internal(cctx, dict, dictSize, dictContentType, dtlm, cdict, params, pledgedSrcSize, ZSTDb_not_buffered); } /*! ZSTD_compressBegin_advanced() : * @return : 0, or an error code */ size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize) { ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(&cctx->requestedParams, ¶ms); return ZSTD_compressBegin_advanced_internal(cctx, dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL /*cdict*/, &cctxParams, pledgedSrcSize); } size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) { ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize); ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(&cctx->requestedParams, ¶ms); DEBUGLOG(4, "ZSTD_compressBegin_usingDict (dictSize=%u)", (unsigned)dictSize); return ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL, &cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered); } size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel) { return ZSTD_compressBegin_usingDict(cctx, NULL, 0, compressionLevel); } /*! ZSTD_writeEpilogue() : * Ends a frame. * @return : nb of bytes written into dst (or an error code) */ static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) { BYTE* const ostart = (BYTE*)dst; BYTE* op = ostart; size_t fhSize = 0; DEBUGLOG(4, "ZSTD_writeEpilogue"); RETURN_ERROR_IF(cctx->stage == ZSTDcs_created, stage_wrong, "init missing"); /* special case : empty frame */ if (cctx->stage == ZSTDcs_init) { fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, &cctx->appliedParams, 0, 0); FORWARD_IF_ERROR(fhSize, "ZSTD_writeFrameHeader failed"); dstCapacity -= fhSize; op += fhSize; cctx->stage = ZSTDcs_ongoing; } if (cctx->stage != ZSTDcs_ending) { /* write one last empty block, make it the "last" block */ U32 const cBlockHeader24 = 1 /* last block */ + (((U32)bt_raw)<<1) + 0; RETURN_ERROR_IF(dstCapacity<4, dstSize_tooSmall, "no room for epilogue"); MEM_writeLE32(op, cBlockHeader24); op += ZSTD_blockHeaderSize; dstCapacity -= ZSTD_blockHeaderSize; } if (cctx->appliedParams.fParams.checksumFlag) { U32 const checksum = (U32) XXH64_digest(&cctx->xxhState); RETURN_ERROR_IF(dstCapacity<4, dstSize_tooSmall, "no room for checksum"); DEBUGLOG(4, "ZSTD_writeEpilogue: write checksum : %08X", (unsigned)checksum); MEM_writeLE32(op, checksum); op += 4; } cctx->stage = ZSTDcs_created; /* return to "created but no init" status */ return op-ostart; } size_t ZSTD_compressEnd (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { size_t endResult; size_t const cSize = ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1 /* frame mode */, 1 /* last chunk */); FORWARD_IF_ERROR(cSize, "ZSTD_compressContinue_internal failed"); endResult = ZSTD_writeEpilogue(cctx, (char*)dst + cSize, dstCapacity-cSize); FORWARD_IF_ERROR(endResult, "ZSTD_writeEpilogue failed"); assert(!(cctx->appliedParams.fParams.contentSizeFlag && cctx->pledgedSrcSizePlusOne == 0)); if (cctx->pledgedSrcSizePlusOne != 0) { /* control src size */ ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN == (unsigned long long)-1); DEBUGLOG(4, "end of frame : controlling src size"); RETURN_ERROR_IF( cctx->pledgedSrcSizePlusOne != cctx->consumedSrcSize+1, srcSize_wrong, "error : pledgedSrcSize = %u, while realSrcSize = %u", (unsigned)cctx->pledgedSrcSizePlusOne-1, (unsigned)cctx->consumedSrcSize); } return cSize + endResult; } static size_t ZSTD_compress_internal (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const void* dict,size_t dictSize, const ZSTD_parameters* params) { ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(&cctx->requestedParams, params); DEBUGLOG(4, "ZSTD_compress_internal"); return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, dict, dictSize, &cctxParams); } size_t ZSTD_compress_advanced (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const void* dict,size_t dictSize, ZSTD_parameters params) { DEBUGLOG(4, "ZSTD_compress_advanced"); FORWARD_IF_ERROR(ZSTD_checkCParams(params.cParams), ""); return ZSTD_compress_internal(cctx, dst, dstCapacity, src, srcSize, dict, dictSize, ¶ms); } /* Internal */ size_t ZSTD_compress_advanced_internal( ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const void* dict,size_t dictSize, const ZSTD_CCtx_params* params) { DEBUGLOG(4, "ZSTD_compress_advanced_internal (srcSize:%u)", (unsigned)srcSize); FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL, params, srcSize, ZSTDb_not_buffered) , ""); return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); } size_t ZSTD_compress_usingDict(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const void* dict, size_t dictSize, int compressionLevel) { ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, srcSize, dict ? dictSize : 0); ZSTD_CCtx_params cctxParams = ZSTD_assignParamsToCCtxParams(&cctx->requestedParams, ¶ms); DEBUGLOG(4, "ZSTD_compress_usingDict (srcSize=%u)", (unsigned)srcSize); assert(params.fParams.contentSizeFlag == 1); return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, dict, dictSize, &cctxParams); } size_t ZSTD_compressCCtx(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) { DEBUGLOG(4, "ZSTD_compressCCtx (srcSize=%u)", (unsigned)srcSize); assert(cctx != NULL); return ZSTD_compress_usingDict(cctx, dst, dstCapacity, src, srcSize, NULL, 0, compressionLevel); } size_t ZSTD_compress(void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) { size_t result; ZSTD_CCtx ctxBody; ZSTD_initCCtx(&ctxBody, ZSTD_defaultCMem); result = ZSTD_compressCCtx(&ctxBody, dst, dstCapacity, src, srcSize, compressionLevel); ZSTD_freeCCtxContent(&ctxBody); /* can't free ctxBody itself, as it's on stack; free only heap content */ return result; } /* ===== Dictionary API ===== */ /*! ZSTD_estimateCDictSize_advanced() : * Estimate amount of memory that will be needed to create a dictionary with following arguments */ size_t ZSTD_estimateCDictSize_advanced( size_t dictSize, ZSTD_compressionParameters cParams, ZSTD_dictLoadMethod_e dictLoadMethod) { DEBUGLOG(5, "sizeof(ZSTD_CDict) : %u", (unsigned)sizeof(ZSTD_CDict)); return ZSTD_cwksp_alloc_size(sizeof(ZSTD_CDict)) + ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE) + ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void *)))); } size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel) { ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize); return ZSTD_estimateCDictSize_advanced(dictSize, cParams, ZSTD_dlm_byCopy); } size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict) { if (cdict==NULL) return 0; /* support sizeof on NULL */ DEBUGLOG(5, "sizeof(*cdict) : %u", (unsigned)sizeof(*cdict)); /* cdict may be in the workspace */ return (cdict->workspace.workspace == cdict ? 0 : sizeof(*cdict)) + ZSTD_cwksp_sizeof(&cdict->workspace); } static size_t ZSTD_initCDict_internal( ZSTD_CDict* cdict, const void* dictBuffer, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType, ZSTD_compressionParameters cParams) { DEBUGLOG(3, "ZSTD_initCDict_internal (dictContentType:%u)", (unsigned)dictContentType); assert(!ZSTD_checkCParams(cParams)); cdict->matchState.cParams = cParams; if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) { cdict->dictContent = dictBuffer; } else { void *internalBuffer = ZSTD_cwksp_reserve_object(&cdict->workspace, ZSTD_cwksp_align(dictSize, sizeof(void*))); RETURN_ERROR_IF(!internalBuffer, memory_allocation, "NULL pointer!"); cdict->dictContent = internalBuffer; memcpy(internalBuffer, dictBuffer, dictSize); } cdict->dictContentSize = dictSize; cdict->entropyWorkspace = (U32*)ZSTD_cwksp_reserve_object(&cdict->workspace, HUF_WORKSPACE_SIZE); /* Reset the state to no dictionary */ ZSTD_reset_compressedBlockState(&cdict->cBlockState); FORWARD_IF_ERROR(ZSTD_reset_matchState( &cdict->matchState, &cdict->workspace, &cParams, ZSTDcrp_makeClean, ZSTDirp_reset, ZSTD_resetTarget_CDict), ""); /* (Maybe) load the dictionary * Skips loading the dictionary if it is < 8 bytes. */ { ZSTD_CCtx_params params; memset(¶ms, 0, sizeof(params)); params.compressionLevel = ZSTD_CLEVEL_DEFAULT; params.fParams.contentSizeFlag = 1; params.cParams = cParams; { size_t const dictID = ZSTD_compress_insertDictionary( &cdict->cBlockState, &cdict->matchState, NULL, &cdict->workspace, ¶ms, cdict->dictContent, cdict->dictContentSize, dictContentType, ZSTD_dtlm_full, cdict->entropyWorkspace); FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed"); assert(dictID <= (size_t)(U32)-1); cdict->dictID = (U32)dictID; } } return 0; } ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType, ZSTD_compressionParameters cParams, ZSTD_customMem customMem) { DEBUGLOG(3, "ZSTD_createCDict_advanced, mode %u", (unsigned)dictContentType); if (!customMem.customAlloc ^ !customMem.customFree) return NULL; { size_t const workspaceSize = ZSTD_cwksp_alloc_size(sizeof(ZSTD_CDict)) + ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE) + ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void*)))); void* const workspace = ZSTD_malloc(workspaceSize, customMem); ZSTD_cwksp ws; ZSTD_CDict* cdict; if (!workspace) { ZSTD_free(workspace, customMem); return NULL; } ZSTD_cwksp_init(&ws, workspace, workspaceSize); cdict = (ZSTD_CDict*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CDict)); assert(cdict != NULL); ZSTD_cwksp_move(&cdict->workspace, &ws); cdict->customMem = customMem; cdict->compressionLevel = 0; /* signals advanced API usage */ if (ZSTD_isError( ZSTD_initCDict_internal(cdict, dictBuffer, dictSize, dictLoadMethod, dictContentType, cParams) )) { ZSTD_freeCDict(cdict); return NULL; } return cdict; } } ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionLevel) { ZSTD_compressionParameters cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize); ZSTD_CDict* cdict = ZSTD_createCDict_advanced(dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto, cParams, ZSTD_defaultCMem); if (cdict) cdict->compressionLevel = compressionLevel == 0 ? ZSTD_CLEVEL_DEFAULT : compressionLevel; return cdict; } ZSTD_CDict* ZSTD_createCDict_byReference(const void* dict, size_t dictSize, int compressionLevel) { ZSTD_compressionParameters cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize); return ZSTD_createCDict_advanced(dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto, cParams, ZSTD_defaultCMem); } size_t ZSTD_freeCDict(ZSTD_CDict* cdict) { if (cdict==NULL) return 0; /* support free on NULL */ { ZSTD_customMem const cMem = cdict->customMem; int cdictInWorkspace = ZSTD_cwksp_owns_buffer(&cdict->workspace, cdict); ZSTD_cwksp_free(&cdict->workspace, cMem); if (!cdictInWorkspace) { ZSTD_free(cdict, cMem); } return 0; } } /*! ZSTD_initStaticCDict_advanced() : * Generate a digested dictionary in provided memory area. * workspace: The memory area to emplace the dictionary into. * Provided pointer must 8-bytes aligned. * It must outlive dictionary usage. * workspaceSize: Use ZSTD_estimateCDictSize() * to determine how large workspace must be. * cParams : use ZSTD_getCParams() to transform a compression level * into its relevants cParams. * @return : pointer to ZSTD_CDict*, or NULL if error (size too small) * Note : there is no corresponding "free" function. * Since workspace was allocated externally, it must be freed externally. */ const ZSTD_CDict* ZSTD_initStaticCDict( void* workspace, size_t workspaceSize, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType, ZSTD_compressionParameters cParams) { size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0); size_t const neededSize = ZSTD_cwksp_alloc_size(sizeof(ZSTD_CDict)) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void*)))) + ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE) + matchStateSize; ZSTD_CDict* cdict; if ((size_t)workspace & 7) return NULL; /* 8-aligned */ { ZSTD_cwksp ws; ZSTD_cwksp_init(&ws, workspace, workspaceSize); cdict = (ZSTD_CDict*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CDict)); if (cdict == NULL) return NULL; ZSTD_cwksp_move(&cdict->workspace, &ws); } DEBUGLOG(4, "(workspaceSize < neededSize) : (%u < %u) => %u", (unsigned)workspaceSize, (unsigned)neededSize, (unsigned)(workspaceSize < neededSize)); if (workspaceSize < neededSize) return NULL; if (ZSTD_isError( ZSTD_initCDict_internal(cdict, dict, dictSize, dictLoadMethod, dictContentType, cParams) )) return NULL; return cdict; } ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict) { assert(cdict != NULL); return cdict->matchState.cParams; } /* ZSTD_compressBegin_usingCDict_advanced() : * cdict must be != NULL */ size_t ZSTD_compressBegin_usingCDict_advanced( ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize) { DEBUGLOG(4, "ZSTD_compressBegin_usingCDict_advanced"); RETURN_ERROR_IF(cdict==NULL, dictionary_wrong, "NULL pointer!"); { ZSTD_CCtx_params params = cctx->requestedParams; params.cParams = ( pledgedSrcSize < ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF || pledgedSrcSize < cdict->dictContentSize * ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN || cdict->compressionLevel == 0 ) && (params.attachDictPref != ZSTD_dictForceLoad) ? ZSTD_getCParamsFromCDict(cdict) : ZSTD_getCParams(cdict->compressionLevel, pledgedSrcSize, cdict->dictContentSize); /* Increase window log to fit the entire dictionary and source if the * source size is known. Limit the increase to 19, which is the * window log for compression level 1 with the largest source size. */ if (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN) { U32 const limitedSrcSize = (U32)MIN(pledgedSrcSize, 1U << 19); U32 const limitedSrcLog = limitedSrcSize > 1 ? ZSTD_highbit32(limitedSrcSize - 1) + 1 : 1; params.cParams.windowLog = MAX(params.cParams.windowLog, limitedSrcLog); } params.fParams = fParams; return ZSTD_compressBegin_internal(cctx, NULL, 0, ZSTD_dct_auto, ZSTD_dtlm_fast, cdict, ¶ms, pledgedSrcSize, ZSTDb_not_buffered); } } /* ZSTD_compressBegin_usingCDict() : * pledgedSrcSize=0 means "unknown" * if pledgedSrcSize>0, it will enable contentSizeFlag */ size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) { ZSTD_frameParameters const fParams = { 0 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ }; DEBUGLOG(4, "ZSTD_compressBegin_usingCDict : dictIDFlag == %u", !fParams.noDictIDFlag); return ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN); } size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams) { FORWARD_IF_ERROR(ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, srcSize), ""); /* will check if cdict != NULL */ return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); } /*! ZSTD_compress_usingCDict() : * Compression using a digested Dictionary. * Faster startup than ZSTD_compress_usingDict(), recommended when same dictionary is used multiple times. * Note that compression parameters are decided at CDict creation time * while frame parameters are hardcoded */ size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const ZSTD_CDict* cdict) { ZSTD_frameParameters const fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ }; return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, fParams); } /* ****************************************************************** * Streaming ********************************************************************/ ZSTD_CStream* ZSTD_createCStream(void) { DEBUGLOG(3, "ZSTD_createCStream"); return ZSTD_createCStream_advanced(ZSTD_defaultCMem); } ZSTD_CStream* ZSTD_initStaticCStream(void *workspace, size_t workspaceSize) { return ZSTD_initStaticCCtx(workspace, workspaceSize); } ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem) { /* CStream and CCtx are now same object */ return ZSTD_createCCtx_advanced(customMem); } size_t ZSTD_freeCStream(ZSTD_CStream* zcs) { return ZSTD_freeCCtx(zcs); /* same object */ } /*====== Initialization ======*/ size_t ZSTD_CStreamInSize(void) { return ZSTD_BLOCKSIZE_MAX; } size_t ZSTD_CStreamOutSize(void) { return ZSTD_compressBound(ZSTD_BLOCKSIZE_MAX) + ZSTD_blockHeaderSize + 4 /* 32-bits hash */ ; } static size_t ZSTD_resetCStream_internal(ZSTD_CStream* cctx, const void* const dict, size_t const dictSize, ZSTD_dictContentType_e const dictContentType, const ZSTD_CDict* const cdict, ZSTD_CCtx_params params, unsigned long long const pledgedSrcSize) { DEBUGLOG(4, "ZSTD_resetCStream_internal"); /* Finalize the compression parameters */ params.cParams = ZSTD_getCParamsFromCCtxParams(¶ms, pledgedSrcSize, dictSize); /* params are supposed to be fully validated at this point */ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); assert(!((dict) && (cdict))); /* either dict or cdict, not both */ FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx, dict, dictSize, dictContentType, ZSTD_dtlm_fast, cdict, ¶ms, pledgedSrcSize, ZSTDb_buffered) , ""); cctx->inToCompress = 0; cctx->inBuffPos = 0; cctx->inBuffTarget = cctx->blockSize + (cctx->blockSize == pledgedSrcSize); /* for small input: avoid automatic flush on reaching end of block, since it would require to add a 3-bytes null block to end frame */ cctx->outBuffContentSize = cctx->outBuffFlushedSize = 0; cctx->streamStage = zcss_load; cctx->frameEnded = 0; return 0; /* ready to go */ } /* ZSTD_resetCStream(): * pledgedSrcSize == 0 means "unknown" */ size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pss) { /* temporary : 0 interpreted as "unknown" during transition period. * Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN. * 0 will be interpreted as "empty" in the future. */ U64 const pledgedSrcSize = (pss==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss; DEBUGLOG(4, "ZSTD_resetCStream: pledgedSrcSize = %u", (unsigned)pledgedSrcSize); FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) , ""); return 0; } /*! ZSTD_initCStream_internal() : * Note : for lib/compress only. Used by zstdmt_compress.c. * Assumption 1 : params are valid * Assumption 2 : either dict, or cdict, is defined, not both */ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs, const void* dict, size_t dictSize, const ZSTD_CDict* cdict, const ZSTD_CCtx_params* params, unsigned long long pledgedSrcSize) { DEBUGLOG(4, "ZSTD_initCStream_internal"); FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) , ""); assert(!ZSTD_isError(ZSTD_checkCParams(params->cParams))); zcs->requestedParams = *params; assert(!((dict) && (cdict))); /* either dict or cdict, not both */ if (dict) { FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) , ""); } else { /* Dictionary is cleared if !cdict */ FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, cdict) , ""); } return 0; } /* ZSTD_initCStream_usingCDict_advanced() : * same as ZSTD_initCStream_usingCDict(), with control over frame parameters */ size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize) { DEBUGLOG(4, "ZSTD_initCStream_usingCDict_advanced"); FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) , ""); zcs->requestedParams.fParams = fParams; FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, cdict) , ""); return 0; } /* note : cdict must outlive compression session */ size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict) { DEBUGLOG(4, "ZSTD_initCStream_usingCDict"); FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, cdict) , ""); return 0; } /* ZSTD_initCStream_advanced() : * pledgedSrcSize must be exact. * if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. * dict is loaded with default parameters ZSTD_dct_auto and ZSTD_dlm_byCopy. */ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pss) { /* for compatibility with older programs relying on this behavior. * Users should now specify ZSTD_CONTENTSIZE_UNKNOWN. * This line will be removed in the future. */ U64 const pledgedSrcSize = (pss==0 && params.fParams.contentSizeFlag==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss; DEBUGLOG(4, "ZSTD_initCStream_advanced"); FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) , ""); FORWARD_IF_ERROR( ZSTD_checkCParams(params.cParams) , ""); zcs->requestedParams = ZSTD_assignParamsToCCtxParams(&zcs->requestedParams, ¶ms); FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) , ""); return 0; } size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel) { DEBUGLOG(4, "ZSTD_initCStream_usingDict"); FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) , ""); FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) , ""); return 0; } size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pss) { /* temporary : 0 interpreted as "unknown" during transition period. * Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN. * 0 will be interpreted as "empty" in the future. */ U64 const pledgedSrcSize = (pss==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss; DEBUGLOG(4, "ZSTD_initCStream_srcSize"); FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, NULL) , ""); FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) , ""); FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) , ""); return 0; } size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel) { DEBUGLOG(4, "ZSTD_initCStream"); FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, NULL) , ""); FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) , ""); return 0; } /*====== Compression ======*/ static size_t ZSTD_nextInputSizeHint(const ZSTD_CCtx* cctx) { size_t hintInSize = cctx->inBuffTarget - cctx->inBuffPos; if (hintInSize==0) hintInSize = cctx->blockSize; return hintInSize; } /** ZSTD_compressStream_generic(): * internal function for all *compressStream*() variants * non-static, because can be called from zstdmt_compress.c * @return : hint size for next input */ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input, ZSTD_EndDirective const flushMode) { const char* const istart = (const char*)input->src; const char* const iend = input->size != 0 ? istart + input->size : istart; const char* ip = input->pos != 0 ? istart + input->pos : istart; char* const ostart = (char*)output->dst; char* const oend = output->size != 0 ? ostart + output->size : ostart; char* op = output->pos != 0 ? ostart + output->pos : ostart; U32 someMoreWork = 1; /* check expectations */ DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%u", (unsigned)flushMode); assert(zcs->inBuff != NULL); assert(zcs->inBuffSize > 0); assert(zcs->outBuff != NULL); assert(zcs->outBuffSize > 0); assert(output->pos <= output->size); assert(input->pos <= input->size); while (someMoreWork) { switch(zcs->streamStage) { case zcss_init: RETURN_ERROR(init_missing, "call ZSTD_initCStream() first!"); case zcss_load: if ( (flushMode == ZSTD_e_end) && ((size_t)(oend-op) >= ZSTD_compressBound(iend-ip)) /* enough dstCapacity */ && (zcs->inBuffPos == 0) ) { /* shortcut to compression pass directly into output buffer */ size_t const cSize = ZSTD_compressEnd(zcs, op, oend-op, ip, iend-ip); DEBUGLOG(4, "ZSTD_compressEnd : cSize=%u", (unsigned)cSize); FORWARD_IF_ERROR(cSize, "ZSTD_compressEnd failed"); ip = iend; op += cSize; zcs->frameEnded = 1; ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); someMoreWork = 0; break; } /* complete loading into inBuffer */ { size_t const toLoad = zcs->inBuffTarget - zcs->inBuffPos; size_t const loaded = ZSTD_limitCopy( zcs->inBuff + zcs->inBuffPos, toLoad, ip, iend-ip); zcs->inBuffPos += loaded; if (loaded != 0) ip += loaded; if ( (flushMode == ZSTD_e_continue) && (zcs->inBuffPos < zcs->inBuffTarget) ) { /* not enough input to fill full block : stop here */ someMoreWork = 0; break; } if ( (flushMode == ZSTD_e_flush) && (zcs->inBuffPos == zcs->inToCompress) ) { /* empty */ someMoreWork = 0; break; } } /* compress current block (note : this stage cannot be stopped in the middle) */ DEBUGLOG(5, "stream compression stage (flushMode==%u)", flushMode); { void* cDst; size_t cSize; size_t const iSize = zcs->inBuffPos - zcs->inToCompress; size_t oSize = oend-op; unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip==iend); if (oSize >= ZSTD_compressBound(iSize)) cDst = op; /* compress into output buffer, to skip flush stage */ else cDst = zcs->outBuff, oSize = zcs->outBuffSize; cSize = lastBlock ? ZSTD_compressEnd(zcs, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize) : ZSTD_compressContinue(zcs, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize); FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed"); zcs->frameEnded = lastBlock; /* prepare next block */ zcs->inBuffTarget = zcs->inBuffPos + zcs->blockSize; if (zcs->inBuffTarget > zcs->inBuffSize) zcs->inBuffPos = 0, zcs->inBuffTarget = zcs->blockSize; DEBUGLOG(5, "inBuffTarget:%u / inBuffSize:%u", (unsigned)zcs->inBuffTarget, (unsigned)zcs->inBuffSize); if (!lastBlock) assert(zcs->inBuffTarget <= zcs->inBuffSize); zcs->inToCompress = zcs->inBuffPos; if (cDst == op) { /* no need to flush */ op += cSize; if (zcs->frameEnded) { DEBUGLOG(5, "Frame completed directly in outBuffer"); someMoreWork = 0; ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); } break; } zcs->outBuffContentSize = cSize; zcs->outBuffFlushedSize = 0; zcs->streamStage = zcss_flush; /* pass-through to flush stage */ } /* fall-through */ case zcss_flush: DEBUGLOG(5, "flush stage"); { size_t const toFlush = zcs->outBuffContentSize - zcs->outBuffFlushedSize; size_t const flushed = ZSTD_limitCopy(op, (size_t)(oend-op), zcs->outBuff + zcs->outBuffFlushedSize, toFlush); DEBUGLOG(5, "toFlush: %u into %u ==> flushed: %u", (unsigned)toFlush, (unsigned)(oend-op), (unsigned)flushed); if (flushed) op += flushed; zcs->outBuffFlushedSize += flushed; if (toFlush!=flushed) { /* flush not fully completed, presumably because dst is too small */ assert(op==oend); someMoreWork = 0; break; } zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0; if (zcs->frameEnded) { DEBUGLOG(5, "Frame completed on flush"); someMoreWork = 0; ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); break; } zcs->streamStage = zcss_load; break; } default: /* impossible */ assert(0); } } input->pos = ip - istart; output->pos = op - ostart; if (zcs->frameEnded) return 0; return ZSTD_nextInputSizeHint(zcs); } static size_t ZSTD_nextInputSizeHint_MTorST(const ZSTD_CCtx* cctx) { #ifdef ZSTD_MULTITHREAD if (cctx->appliedParams.nbWorkers >= 1) { assert(cctx->mtctx != NULL); return ZSTDMT_nextInputSizeHint(cctx->mtctx); } #endif return ZSTD_nextInputSizeHint(cctx); } size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { FORWARD_IF_ERROR( ZSTD_compressStream2(zcs, output, input, ZSTD_e_continue) , ""); return ZSTD_nextInputSizeHint_MTorST(zcs); } size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input, ZSTD_EndDirective endOp) { DEBUGLOG(5, "ZSTD_compressStream2, endOp=%u ", (unsigned)endOp); /* check conditions */ RETURN_ERROR_IF(output->pos > output->size, GENERIC, "invalid buffer"); RETURN_ERROR_IF(input->pos > input->size, GENERIC, "invalid buffer"); assert(cctx!=NULL); /* transparent initialization stage */ if (cctx->streamStage == zcss_init) { ZSTD_CCtx_params params = cctx->requestedParams; ZSTD_prefixDict const prefixDict = cctx->prefixDict; FORWARD_IF_ERROR( ZSTD_initLocalDict(cctx) , ""); /* Init the local dict if present. */ memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict)); /* single usage */ assert(prefixDict.dict==NULL || cctx->cdict==NULL); /* only one can be set */ DEBUGLOG(4, "ZSTD_compressStream2 : transparent init stage"); if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = input->size + 1; /* auto-fix pledgedSrcSize */ params.cParams = ZSTD_getCParamsFromCCtxParams( &cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/); #ifdef ZSTD_MULTITHREAD if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) { params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */ } if (params.nbWorkers > 0) { /* mt context creation */ if (cctx->mtctx == NULL) { DEBUGLOG(4, "ZSTD_compressStream2: creating new mtctx for nbWorkers=%u", params.nbWorkers); cctx->mtctx = ZSTDMT_createCCtx_advanced((U32)params.nbWorkers, cctx->customMem); RETURN_ERROR_IF(cctx->mtctx == NULL, memory_allocation, "NULL pointer!"); } /* mt compression */ DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbWorkers=%u", params.nbWorkers); FORWARD_IF_ERROR( ZSTDMT_initCStream_internal( cctx->mtctx, prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType, cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) , ""); cctx->streamStage = zcss_load; cctx->appliedParams.nbWorkers = params.nbWorkers; } else #endif { FORWARD_IF_ERROR( ZSTD_resetCStream_internal(cctx, prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType, cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) , ""); assert(cctx->streamStage == zcss_load); assert(cctx->appliedParams.nbWorkers == 0); } } /* end of transparent initialization stage */ /* compression stage */ #ifdef ZSTD_MULTITHREAD if (cctx->appliedParams.nbWorkers > 0) { int const forceMaxProgress = (endOp == ZSTD_e_flush || endOp == ZSTD_e_end); size_t flushMin; assert(forceMaxProgress || endOp == ZSTD_e_continue /* Protection for a new flush type */); if (cctx->cParamsChanged) { ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams); cctx->cParamsChanged = 0; } do { flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp); if ( ZSTD_isError(flushMin) || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */ ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only); } FORWARD_IF_ERROR(flushMin, "ZSTDMT_compressStream_generic failed"); } while (forceMaxProgress && flushMin != 0 && output->pos < output->size); DEBUGLOG(5, "completed ZSTD_compressStream2 delegating to ZSTDMT_compressStream_generic"); /* Either we don't require maximum forward progress, we've finished the * flush, or we are out of output space. */ assert(!forceMaxProgress || flushMin == 0 || output->pos == output->size); return flushMin; } #endif FORWARD_IF_ERROR( ZSTD_compressStream_generic(cctx, output, input, endOp) , ""); DEBUGLOG(5, "completed ZSTD_compressStream2"); return cctx->outBuffContentSize - cctx->outBuffFlushedSize; /* remaining to flush */ } size_t ZSTD_compressStream2_simpleArgs ( ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, size_t* dstPos, const void* src, size_t srcSize, size_t* srcPos, ZSTD_EndDirective endOp) { ZSTD_outBuffer output = { dst, dstCapacity, *dstPos }; ZSTD_inBuffer input = { src, srcSize, *srcPos }; /* ZSTD_compressStream2() will check validity of dstPos and srcPos */ size_t const cErr = ZSTD_compressStream2(cctx, &output, &input, endOp); *dstPos = output.pos; *srcPos = input.pos; return cErr; } size_t ZSTD_compress2(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { DEBUGLOG(4, "ZSTD_compress2 (srcSize=%u)", (unsigned)srcSize); ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only); { size_t oPos = 0; size_t iPos = 0; size_t const result = ZSTD_compressStream2_simpleArgs(cctx, dst, dstCapacity, &oPos, src, srcSize, &iPos, ZSTD_e_end); FORWARD_IF_ERROR(result, "ZSTD_compressStream2_simpleArgs failed"); if (result != 0) { /* compression not completed, due to lack of output space */ assert(oPos == dstCapacity); RETURN_ERROR(dstSize_tooSmall, ""); } assert(iPos == srcSize); /* all input is expected consumed */ return oPos; } } /*====== Finalize ======*/ /*! ZSTD_flushStream() : * @return : amount of data remaining to flush */ size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output) { ZSTD_inBuffer input = { NULL, 0, 0 }; return ZSTD_compressStream2(zcs, output, &input, ZSTD_e_flush); } size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output) { ZSTD_inBuffer input = { NULL, 0, 0 }; size_t const remainingToFlush = ZSTD_compressStream2(zcs, output, &input, ZSTD_e_end); FORWARD_IF_ERROR( remainingToFlush , "ZSTD_compressStream2 failed"); if (zcs->appliedParams.nbWorkers > 0) return remainingToFlush; /* minimal estimation */ /* single thread mode : attempt to calculate remaining to flush more precisely */ { size_t const lastBlockSize = zcs->frameEnded ? 0 : ZSTD_BLOCKHEADERSIZE; size_t const checksumSize = (size_t)(zcs->frameEnded ? 0 : zcs->appliedParams.fParams.checksumFlag * 4); size_t const toFlush = remainingToFlush + lastBlockSize + checksumSize; DEBUGLOG(4, "ZSTD_endStream : remaining to flush : %u", (unsigned)toFlush); return toFlush; } } /*-===== Pre-defined compression levels =====-*/ #define ZSTD_MAX_CLEVEL 22 int ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; } int ZSTD_minCLevel(void) { return (int)-ZSTD_TARGETLENGTH_MAX; } static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = { { /* "default" - for any srcSize > 256 KB */ /* W, C, H, S, L, TL, strat */ { 19, 12, 13, 1, 6, 1, ZSTD_fast }, /* base for negative levels */ { 19, 13, 14, 1, 7, 0, ZSTD_fast }, /* level 1 */ { 20, 15, 16, 1, 6, 0, ZSTD_fast }, /* level 2 */ { 21, 16, 17, 1, 5, 0, ZSTD_dfast }, /* level 3 */ { 21, 18, 18, 1, 5, 0, ZSTD_dfast }, /* level 4 */ { 21, 18, 19, 2, 5, 2, ZSTD_greedy }, /* level 5 */ { 21, 19, 19, 3, 5, 4, ZSTD_greedy }, /* level 6 */ { 21, 19, 19, 3, 5, 8, ZSTD_lazy }, /* level 7 */ { 21, 19, 19, 3, 5, 16, ZSTD_lazy2 }, /* level 8 */ { 21, 19, 20, 4, 5, 16, ZSTD_lazy2 }, /* level 9 */ { 22, 20, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */ { 22, 21, 22, 4, 5, 16, ZSTD_lazy2 }, /* level 11 */ { 22, 21, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */ { 22, 21, 22, 5, 5, 32, ZSTD_btlazy2 }, /* level 13 */ { 22, 22, 23, 5, 5, 32, ZSTD_btlazy2 }, /* level 14 */ { 22, 23, 23, 6, 5, 32, ZSTD_btlazy2 }, /* level 15 */ { 22, 22, 22, 5, 5, 48, ZSTD_btopt }, /* level 16 */ { 23, 23, 22, 5, 4, 64, ZSTD_btopt }, /* level 17 */ { 23, 23, 22, 6, 3, 64, ZSTD_btultra }, /* level 18 */ { 23, 24, 22, 7, 3,256, ZSTD_btultra2}, /* level 19 */ { 25, 25, 23, 7, 3,256, ZSTD_btultra2}, /* level 20 */ { 26, 26, 24, 7, 3,512, ZSTD_btultra2}, /* level 21 */ { 27, 27, 25, 9, 3,999, ZSTD_btultra2}, /* level 22 */ }, { /* for srcSize <= 256 KB */ /* W, C, H, S, L, T, strat */ { 18, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ { 18, 13, 14, 1, 6, 0, ZSTD_fast }, /* level 1 */ { 18, 14, 14, 1, 5, 0, ZSTD_dfast }, /* level 2 */ { 18, 16, 16, 1, 4, 0, ZSTD_dfast }, /* level 3 */ { 18, 16, 17, 2, 5, 2, ZSTD_greedy }, /* level 4.*/ { 18, 18, 18, 3, 5, 2, ZSTD_greedy }, /* level 5.*/ { 18, 18, 19, 3, 5, 4, ZSTD_lazy }, /* level 6.*/ { 18, 18, 19, 4, 4, 4, ZSTD_lazy }, /* level 7 */ { 18, 18, 19, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ { 18, 18, 19, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ { 18, 18, 19, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ { 18, 18, 19, 5, 4, 12, ZSTD_btlazy2 }, /* level 11.*/ { 18, 19, 19, 7, 4, 12, ZSTD_btlazy2 }, /* level 12.*/ { 18, 18, 19, 4, 4, 16, ZSTD_btopt }, /* level 13 */ { 18, 18, 19, 4, 3, 32, ZSTD_btopt }, /* level 14.*/ { 18, 18, 19, 6, 3,128, ZSTD_btopt }, /* level 15.*/ { 18, 19, 19, 6, 3,128, ZSTD_btultra }, /* level 16.*/ { 18, 19, 19, 8, 3,256, ZSTD_btultra }, /* level 17.*/ { 18, 19, 19, 6, 3,128, ZSTD_btultra2}, /* level 18.*/ { 18, 19, 19, 8, 3,256, ZSTD_btultra2}, /* level 19.*/ { 18, 19, 19, 10, 3,512, ZSTD_btultra2}, /* level 20.*/ { 18, 19, 19, 12, 3,512, ZSTD_btultra2}, /* level 21.*/ { 18, 19, 19, 13, 3,999, ZSTD_btultra2}, /* level 22.*/ }, { /* for srcSize <= 128 KB */ /* W, C, H, S, L, T, strat */ { 17, 12, 12, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ { 17, 12, 13, 1, 6, 0, ZSTD_fast }, /* level 1 */ { 17, 13, 15, 1, 5, 0, ZSTD_fast }, /* level 2 */ { 17, 15, 16, 2, 5, 0, ZSTD_dfast }, /* level 3 */ { 17, 17, 17, 2, 4, 0, ZSTD_dfast }, /* level 4 */ { 17, 16, 17, 3, 4, 2, ZSTD_greedy }, /* level 5 */ { 17, 17, 17, 3, 4, 4, ZSTD_lazy }, /* level 6 */ { 17, 17, 17, 3, 4, 8, ZSTD_lazy2 }, /* level 7 */ { 17, 17, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ { 17, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ { 17, 17, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ { 17, 17, 17, 5, 4, 8, ZSTD_btlazy2 }, /* level 11 */ { 17, 18, 17, 7, 4, 12, ZSTD_btlazy2 }, /* level 12 */ { 17, 18, 17, 3, 4, 12, ZSTD_btopt }, /* level 13.*/ { 17, 18, 17, 4, 3, 32, ZSTD_btopt }, /* level 14.*/ { 17, 18, 17, 6, 3,256, ZSTD_btopt }, /* level 15.*/ { 17, 18, 17, 6, 3,128, ZSTD_btultra }, /* level 16.*/ { 17, 18, 17, 8, 3,256, ZSTD_btultra }, /* level 17.*/ { 17, 18, 17, 10, 3,512, ZSTD_btultra }, /* level 18.*/ { 17, 18, 17, 5, 3,256, ZSTD_btultra2}, /* level 19.*/ { 17, 18, 17, 7, 3,512, ZSTD_btultra2}, /* level 20.*/ { 17, 18, 17, 9, 3,512, ZSTD_btultra2}, /* level 21.*/ { 17, 18, 17, 11, 3,999, ZSTD_btultra2}, /* level 22.*/ }, { /* for srcSize <= 16 KB */ /* W, C, H, S, L, T, strat */ { 14, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ { 14, 14, 15, 1, 5, 0, ZSTD_fast }, /* level 1 */ { 14, 14, 15, 1, 4, 0, ZSTD_fast }, /* level 2 */ { 14, 14, 15, 2, 4, 0, ZSTD_dfast }, /* level 3 */ { 14, 14, 14, 4, 4, 2, ZSTD_greedy }, /* level 4 */ { 14, 14, 14, 3, 4, 4, ZSTD_lazy }, /* level 5.*/ { 14, 14, 14, 4, 4, 8, ZSTD_lazy2 }, /* level 6 */ { 14, 14, 14, 6, 4, 8, ZSTD_lazy2 }, /* level 7 */ { 14, 14, 14, 8, 4, 8, ZSTD_lazy2 }, /* level 8.*/ { 14, 15, 14, 5, 4, 8, ZSTD_btlazy2 }, /* level 9.*/ { 14, 15, 14, 9, 4, 8, ZSTD_btlazy2 }, /* level 10.*/ { 14, 15, 14, 3, 4, 12, ZSTD_btopt }, /* level 11.*/ { 14, 15, 14, 4, 3, 24, ZSTD_btopt }, /* level 12.*/ { 14, 15, 14, 5, 3, 32, ZSTD_btultra }, /* level 13.*/ { 14, 15, 15, 6, 3, 64, ZSTD_btultra }, /* level 14.*/ { 14, 15, 15, 7, 3,256, ZSTD_btultra }, /* level 15.*/ { 14, 15, 15, 5, 3, 48, ZSTD_btultra2}, /* level 16.*/ { 14, 15, 15, 6, 3,128, ZSTD_btultra2}, /* level 17.*/ { 14, 15, 15, 7, 3,256, ZSTD_btultra2}, /* level 18.*/ { 14, 15, 15, 8, 3,256, ZSTD_btultra2}, /* level 19.*/ { 14, 15, 15, 8, 3,512, ZSTD_btultra2}, /* level 20.*/ { 14, 15, 15, 9, 3,512, ZSTD_btultra2}, /* level 21.*/ { 14, 15, 15, 10, 3,999, ZSTD_btultra2}, /* level 22.*/ }, }; /*! ZSTD_getCParams_internal() : * @return ZSTD_compressionParameters structure for a selected compression level, srcSize and dictSize. * Note: srcSizeHint 0 means 0, use ZSTD_CONTENTSIZE_UNKNOWN for unknown. * Use dictSize == 0 for unknown or unused. */ static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) { int const unknown = srcSizeHint == ZSTD_CONTENTSIZE_UNKNOWN; size_t const addedSize = unknown && dictSize > 0 ? 500 : 0; U64 const rSize = unknown && dictSize == 0 ? ZSTD_CONTENTSIZE_UNKNOWN : srcSizeHint+dictSize+addedSize; U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB); int row = compressionLevel; DEBUGLOG(5, "ZSTD_getCParams_internal (cLevel=%i)", compressionLevel); if (compressionLevel == 0) row = ZSTD_CLEVEL_DEFAULT; /* 0 == default */ if (compressionLevel < 0) row = 0; /* entry 0 is baseline for fast mode */ if (compressionLevel > ZSTD_MAX_CLEVEL) row = ZSTD_MAX_CLEVEL; { ZSTD_compressionParameters cp = ZSTD_defaultCParameters[tableID][row]; if (compressionLevel < 0) cp.targetLength = (unsigned)(-compressionLevel); /* acceleration factor */ /* refine parameters based on srcSize & dictSize */ return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize); } } /*! ZSTD_getCParams() : * @return ZSTD_compressionParameters structure for a selected compression level, srcSize and dictSize. * Size values are optional, provide 0 if not known or unused */ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) { if (srcSizeHint == 0) srcSizeHint = ZSTD_CONTENTSIZE_UNKNOWN; return ZSTD_getCParams_internal(compressionLevel, srcSizeHint, dictSize); } /*! ZSTD_getParams() : * same idea as ZSTD_getCParams() * @return a `ZSTD_parameters` structure (instead of `ZSTD_compressionParameters`). * Fields of `ZSTD_frameParameters` are set to default values */ static ZSTD_parameters ZSTD_getParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) { ZSTD_parameters params; ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, srcSizeHint, dictSize); DEBUGLOG(5, "ZSTD_getParams (cLevel=%i)", compressionLevel); memset(¶ms, 0, sizeof(params)); params.cParams = cParams; params.fParams.contentSizeFlag = 1; return params; } /*! ZSTD_getParams() : * same idea as ZSTD_getCParams() * @return a `ZSTD_parameters` structure (instead of `ZSTD_compressionParameters`). * Fields of `ZSTD_frameParameters` are set to default values */ ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) { if (srcSizeHint == 0) srcSizeHint = ZSTD_CONTENTSIZE_UNKNOWN; return ZSTD_getParams_internal(compressionLevel, srcSizeHint, dictSize); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_compress_internal.h0000644000076500000240000013165214601576577026472 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* This header contains definitions * that shall **only** be used by modules within lib/compress. */ #ifndef ZSTD_COMPRESS_H #define ZSTD_COMPRESS_H /*-************************************* * Dependencies ***************************************/ #include "../common/zstd_internal.h" #include "zstd_cwksp.h" #ifdef ZSTD_MULTITHREAD # include "zstdmt_compress.h" #endif #if defined (__cplusplus) extern "C" { #endif /*-************************************* * Constants ***************************************/ #define kSearchStrength 8 #define HASH_READ_SIZE 8 #define ZSTD_DUBT_UNSORTED_MARK 1 /* For btlazy2 strategy, index ZSTD_DUBT_UNSORTED_MARK==1 means "unsorted". It could be confused for a real successor at index "1", if sorted as larger than its predecessor. It's not a big deal though : candidate will just be sorted again. Additionally, candidate position 1 will be lost. But candidate 1 cannot hide a large tree of candidates, so it's a minimal loss. The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be mishandled after table re-use with a different strategy. This constant is required by ZSTD_compressBlock_btlazy2() and ZSTD_reduceTable_internal() */ /*-************************************* * Context memory management ***************************************/ typedef enum { ZSTDcs_created=0, ZSTDcs_init, ZSTDcs_ongoing, ZSTDcs_ending } ZSTD_compressionStage_e; typedef enum { zcss_init=0, zcss_load, zcss_flush } ZSTD_cStreamStage; typedef struct ZSTD_prefixDict_s { const void* dict; size_t dictSize; ZSTD_dictContentType_e dictContentType; } ZSTD_prefixDict; typedef struct { void* dictBuffer; void const* dict; size_t dictSize; ZSTD_dictContentType_e dictContentType; ZSTD_CDict* cdict; } ZSTD_localDict; typedef struct { U32 CTable[HUF_CTABLE_SIZE_U32(255)]; HUF_repeat repeatMode; } ZSTD_hufCTables_t; typedef struct { FSE_CTable offcodeCTable[FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)]; FSE_CTable matchlengthCTable[FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)]; FSE_CTable litlengthCTable[FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)]; FSE_repeat offcode_repeatMode; FSE_repeat matchlength_repeatMode; FSE_repeat litlength_repeatMode; } ZSTD_fseCTables_t; typedef struct { ZSTD_hufCTables_t huf; ZSTD_fseCTables_t fse; } ZSTD_entropyCTables_t; typedef struct { U32 off; U32 len; } ZSTD_match_t; typedef struct { int price; U32 off; U32 mlen; U32 litlen; U32 rep[ZSTD_REP_NUM]; } ZSTD_optimal_t; typedef enum { zop_dynamic=0, zop_predef } ZSTD_OptPrice_e; typedef struct { /* All tables are allocated inside cctx->workspace by ZSTD_resetCCtx_internal() */ unsigned* litFreq; /* table of literals statistics, of size 256 */ unsigned* litLengthFreq; /* table of litLength statistics, of size (MaxLL+1) */ unsigned* matchLengthFreq; /* table of matchLength statistics, of size (MaxML+1) */ unsigned* offCodeFreq; /* table of offCode statistics, of size (MaxOff+1) */ ZSTD_match_t* matchTable; /* list of found matches, of size ZSTD_OPT_NUM+1 */ ZSTD_optimal_t* priceTable; /* All positions tracked by optimal parser, of size ZSTD_OPT_NUM+1 */ U32 litSum; /* nb of literals */ U32 litLengthSum; /* nb of litLength codes */ U32 matchLengthSum; /* nb of matchLength codes */ U32 offCodeSum; /* nb of offset codes */ U32 litSumBasePrice; /* to compare to log2(litfreq) */ U32 litLengthSumBasePrice; /* to compare to log2(llfreq) */ U32 matchLengthSumBasePrice;/* to compare to log2(mlfreq) */ U32 offCodeSumBasePrice; /* to compare to log2(offreq) */ ZSTD_OptPrice_e priceType; /* prices can be determined dynamically, or follow a pre-defined cost structure */ const ZSTD_entropyCTables_t* symbolCosts; /* pre-calculated dictionary statistics */ ZSTD_literalCompressionMode_e literalCompressionMode; } optState_t; typedef struct { ZSTD_entropyCTables_t entropy; U32 rep[ZSTD_REP_NUM]; } ZSTD_compressedBlockState_t; typedef struct { BYTE const* nextSrc; /* next block here to continue on current prefix */ BYTE const* base; /* All regular indexes relative to this position */ BYTE const* dictBase; /* extDict indexes relative to this position */ U32 dictLimit; /* below that point, need extDict */ U32 lowLimit; /* below that point, no more valid data */ } ZSTD_window_t; typedef struct ZSTD_matchState_t ZSTD_matchState_t; struct ZSTD_matchState_t { ZSTD_window_t window; /* State for window round buffer management */ U32 loadedDictEnd; /* index of end of dictionary, within context's referential. * When loadedDictEnd != 0, a dictionary is in use, and still valid. * This relies on a mechanism to set loadedDictEnd=0 when dictionary is no longer within distance. * Such mechanism is provided within ZSTD_window_enforceMaxDist() and ZSTD_checkDictValidity(). * When dict referential is copied into active context (i.e. not attached), * loadedDictEnd == dictSize, since referential starts from zero. */ U32 nextToUpdate; /* index from which to continue table update */ U32 hashLog3; /* dispatch table for matches of len==3 : larger == faster, more memory */ U32* hashTable; U32* hashTable3; U32* chainTable; optState_t opt; /* optimal parser state */ const ZSTD_matchState_t* dictMatchState; ZSTD_compressionParameters cParams; }; typedef struct { ZSTD_compressedBlockState_t* prevCBlock; ZSTD_compressedBlockState_t* nextCBlock; ZSTD_matchState_t matchState; } ZSTD_blockState_t; typedef struct { U32 offset; U32 checksum; } ldmEntry_t; typedef struct { ZSTD_window_t window; /* State for the window round buffer management */ ldmEntry_t* hashTable; U32 loadedDictEnd; BYTE* bucketOffsets; /* Next position in bucket to insert entry */ U64 hashPower; /* Used to compute the rolling hash. * Depends on ldmParams.minMatchLength */ } ldmState_t; typedef struct { U32 enableLdm; /* 1 if enable long distance matching */ U32 hashLog; /* Log size of hashTable */ U32 bucketSizeLog; /* Log bucket size for collision resolution, at most 8 */ U32 minMatchLength; /* Minimum match length */ U32 hashRateLog; /* Log number of entries to skip */ U32 windowLog; /* Window log for the LDM */ } ldmParams_t; typedef struct { U32 offset; U32 litLength; U32 matchLength; } rawSeq; typedef struct { rawSeq* seq; /* The start of the sequences */ size_t pos; /* The position where reading stopped. <= size. */ size_t size; /* The number of sequences. <= capacity. */ size_t capacity; /* The capacity starting from `seq` pointer */ } rawSeqStore_t; typedef struct { int collectSequences; ZSTD_Sequence* seqStart; size_t seqIndex; size_t maxSequences; } SeqCollector; struct ZSTD_CCtx_params_s { ZSTD_format_e format; ZSTD_compressionParameters cParams; ZSTD_frameParameters fParams; int compressionLevel; int forceWindow; /* force back-references to respect limit of * 1< 63) ? ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; } /* ZSTD_MLcode() : * note : mlBase = matchLength - MINMATCH; * because it's the format it's stored in seqStore->sequences */ MEM_STATIC U32 ZSTD_MLcode(U32 mlBase) { static const BYTE ML_Code[128] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 }; static const U32 ML_deltaCode = 36; return (mlBase > 127) ? ZSTD_highbit32(mlBase) + ML_deltaCode : ML_Code[mlBase]; } typedef struct repcodes_s { U32 rep[3]; } repcodes_t; MEM_STATIC repcodes_t ZSTD_updateRep(U32 const rep[3], U32 const offset, U32 const ll0) { repcodes_t newReps; if (offset >= ZSTD_REP_NUM) { /* full offset */ newReps.rep[2] = rep[1]; newReps.rep[1] = rep[0]; newReps.rep[0] = offset - ZSTD_REP_MOVE; } else { /* repcode */ U32 const repCode = offset + ll0; if (repCode > 0) { /* note : if repCode==0, no change */ U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode]; newReps.rep[2] = (repCode >= 2) ? rep[1] : rep[2]; newReps.rep[1] = rep[0]; newReps.rep[0] = currentOffset; } else { /* repCode == 0 */ memcpy(&newReps, rep, sizeof(newReps)); } } return newReps; } /* ZSTD_cParam_withinBounds: * @return 1 if value is within cParam bounds, * 0 otherwise */ MEM_STATIC int ZSTD_cParam_withinBounds(ZSTD_cParameter cParam, int value) { ZSTD_bounds const bounds = ZSTD_cParam_getBounds(cParam); if (ZSTD_isError(bounds.error)) return 0; if (value < bounds.lowerBound) return 0; if (value > bounds.upperBound) return 0; return 1; } /* ZSTD_noCompressBlock() : * Writes uncompressed block to dst buffer from given src. * Returns the size of the block */ MEM_STATIC size_t ZSTD_noCompressBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock) { U32 const cBlockHeader24 = lastBlock + (((U32)bt_raw)<<1) + (U32)(srcSize << 3); RETURN_ERROR_IF(srcSize + ZSTD_blockHeaderSize > dstCapacity, dstSize_tooSmall, "dst buf too small for uncompressed block"); MEM_writeLE24(dst, cBlockHeader24); memcpy((BYTE*)dst + ZSTD_blockHeaderSize, src, srcSize); return ZSTD_blockHeaderSize + srcSize; } MEM_STATIC size_t ZSTD_rleCompressBlock (void* dst, size_t dstCapacity, BYTE src, size_t srcSize, U32 lastBlock) { BYTE* const op = (BYTE*)dst; U32 const cBlockHeader = lastBlock + (((U32)bt_rle)<<1) + (U32)(srcSize << 3); RETURN_ERROR_IF(dstCapacity < 4, dstSize_tooSmall, ""); MEM_writeLE24(op, cBlockHeader); op[3] = src; return 4; } /* ZSTD_minGain() : * minimum compression required * to generate a compress block or a compressed literals section. * note : use same formula for both situations */ MEM_STATIC size_t ZSTD_minGain(size_t srcSize, ZSTD_strategy strat) { U32 const minlog = (strat>=ZSTD_btultra) ? (U32)(strat) - 1 : 6; ZSTD_STATIC_ASSERT(ZSTD_btultra == 8); assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, strat)); return (srcSize >> minlog) + 2; } MEM_STATIC int ZSTD_disableLiteralsCompression(const ZSTD_CCtx_params* cctxParams) { switch (cctxParams->literalCompressionMode) { case ZSTD_lcm_huffman: return 0; case ZSTD_lcm_uncompressed: return 1; default: assert(0 /* impossible: pre-validated */); /* fall-through */ case ZSTD_lcm_auto: return (cctxParams->cParams.strategy == ZSTD_fast) && (cctxParams->cParams.targetLength > 0); } } /*! ZSTD_safecopyLiterals() : * memcpy() function that won't read beyond more than WILDCOPY_OVERLENGTH bytes past ilimit_w. * Only called when the sequence ends past ilimit_w, so it only needs to be optimized for single * large copies. */ static void ZSTD_safecopyLiterals(BYTE* op, BYTE const* ip, BYTE const* const iend, BYTE const* ilimit_w) { assert(iend > ilimit_w); if (ip <= ilimit_w) { ZSTD_wildcopy(op, ip, ilimit_w - ip, ZSTD_no_overlap); op += ilimit_w - ip; ip = ilimit_w; } while (ip < iend) *op++ = *ip++; } /*! ZSTD_storeSeq() : * Store a sequence (litlen, litPtr, offCode and mlBase) into seqStore_t. * `offCode` : distance to match + ZSTD_REP_MOVE (values <= ZSTD_REP_MOVE are repCodes). * `mlBase` : matchLength - MINMATCH * Allowed to overread literals up to litLimit. */ HINT_INLINE UNUSED_ATTR void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const BYTE* literals, const BYTE* litLimit, U32 offCode, size_t mlBase) { BYTE const* const litLimit_w = litLimit - WILDCOPY_OVERLENGTH; BYTE const* const litEnd = literals + litLength; #if defined(DEBUGLEVEL) && (DEBUGLEVEL >= 6) static const BYTE* g_start = NULL; if (g_start==NULL) g_start = (const BYTE*)literals; /* note : index only works for compression within a single segment */ { U32 const pos = (U32)((const BYTE*)literals - g_start); DEBUGLOG(6, "Cpos%7u :%3u literals, match%4u bytes at offCode%7u", pos, (U32)litLength, (U32)mlBase+MINMATCH, (U32)offCode); } #endif assert((size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart) < seqStorePtr->maxNbSeq); /* copy Literals */ assert(seqStorePtr->maxNbLit <= 128 KB); assert(seqStorePtr->lit + litLength <= seqStorePtr->litStart + seqStorePtr->maxNbLit); assert(literals + litLength <= litLimit); if (litEnd <= litLimit_w) { /* Common case we can use wildcopy. * First copy 16 bytes, because literals are likely short. */ assert(WILDCOPY_OVERLENGTH >= 16); ZSTD_copy16(seqStorePtr->lit, literals); if (litLength > 16) { ZSTD_wildcopy(seqStorePtr->lit+16, literals+16, (ptrdiff_t)litLength-16, ZSTD_no_overlap); } } else { ZSTD_safecopyLiterals(seqStorePtr->lit, literals, litEnd, litLimit_w); } seqStorePtr->lit += litLength; /* literal Length */ if (litLength>0xFFFF) { assert(seqStorePtr->longLengthID == 0); /* there can only be a single long length */ seqStorePtr->longLengthID = 1; seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); } seqStorePtr->sequences[0].litLength = (U16)litLength; /* match offset */ seqStorePtr->sequences[0].offset = offCode + 1; /* match Length */ if (mlBase>0xFFFF) { assert(seqStorePtr->longLengthID == 0); /* there can only be a single long length */ seqStorePtr->longLengthID = 2; seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); } seqStorePtr->sequences[0].matchLength = (U16)mlBase; seqStorePtr->sequences++; } /*-************************************* * Match length counter ***************************************/ static unsigned ZSTD_NbCommonBytes (size_t val) { if (MEM_isLittleEndian()) { if (MEM_64bits()) { # if defined(_MSC_VER) && defined(_WIN64) unsigned long r = 0; return _BitScanForward64( &r, (U64)val ) ? (unsigned)(r >> 3) : 0; # elif defined(__GNUC__) && (__GNUC__ >= 4) return (__builtin_ctzll((U64)val) >> 3); # else static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; # endif } else { /* 32 bits */ # if defined(_MSC_VER) unsigned long r=0; return _BitScanForward( &r, (U32)val ) ? (unsigned)(r >> 3) : 0; # elif defined(__GNUC__) && (__GNUC__ >= 3) return (__builtin_ctz((U32)val) >> 3); # else static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; # endif } } else { /* Big Endian CPU */ if (MEM_64bits()) { # if defined(_MSC_VER) && defined(_WIN64) unsigned long r = 0; return _BitScanReverse64( &r, val ) ? (unsigned)(r >> 3) : 0; # elif defined(__GNUC__) && (__GNUC__ >= 4) return (__builtin_clzll(val) >> 3); # else unsigned r; const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; } if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } r += (!val); return r; # endif } else { /* 32 bits */ # if defined(_MSC_VER) unsigned long r = 0; return _BitScanReverse( &r, (unsigned long)val ) ? (unsigned)(r >> 3) : 0; # elif defined(__GNUC__) && (__GNUC__ >= 3) return (__builtin_clz((U32)val) >> 3); # else unsigned r; if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } r += (!val); return r; # endif } } } MEM_STATIC size_t ZSTD_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* const pInLimit) { const BYTE* const pStart = pIn; const BYTE* const pInLoopLimit = pInLimit - (sizeof(size_t)-1); if (pIn < pInLoopLimit) { { size_t const diff = MEM_readST(pMatch) ^ MEM_readST(pIn); if (diff) return ZSTD_NbCommonBytes(diff); } pIn+=sizeof(size_t); pMatch+=sizeof(size_t); while (pIn < pInLoopLimit) { size_t const diff = MEM_readST(pMatch) ^ MEM_readST(pIn); if (!diff) { pIn+=sizeof(size_t); pMatch+=sizeof(size_t); continue; } pIn += ZSTD_NbCommonBytes(diff); return (size_t)(pIn - pStart); } } if (MEM_64bits() && (pIn<(pInLimit-3)) && (MEM_read32(pMatch) == MEM_read32(pIn))) { pIn+=4; pMatch+=4; } if ((pIn<(pInLimit-1)) && (MEM_read16(pMatch) == MEM_read16(pIn))) { pIn+=2; pMatch+=2; } if ((pIn> (32-h) ; } MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h); } /* only in zstd_opt.h */ static const U32 prime4bytes = 2654435761U; static U32 ZSTD_hash4(U32 u, U32 h) { return (u * prime4bytes) >> (32-h) ; } static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_read32(ptr), h); } static const U64 prime5bytes = 889523592379ULL; static size_t ZSTD_hash5(U64 u, U32 h) { return (size_t)(((u << (64-40)) * prime5bytes) >> (64-h)) ; } static size_t ZSTD_hash5Ptr(const void* p, U32 h) { return ZSTD_hash5(MEM_readLE64(p), h); } static const U64 prime6bytes = 227718039650203ULL; static size_t ZSTD_hash6(U64 u, U32 h) { return (size_t)(((u << (64-48)) * prime6bytes) >> (64-h)) ; } static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h); } static const U64 prime7bytes = 58295818150454627ULL; static size_t ZSTD_hash7(U64 u, U32 h) { return (size_t)(((u << (64-56)) * prime7bytes) >> (64-h)) ; } static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h); } static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL; static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; } static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); } MEM_STATIC size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls) { switch(mls) { default: case 4: return ZSTD_hash4Ptr(p, hBits); case 5: return ZSTD_hash5Ptr(p, hBits); case 6: return ZSTD_hash6Ptr(p, hBits); case 7: return ZSTD_hash7Ptr(p, hBits); case 8: return ZSTD_hash8Ptr(p, hBits); } } /** ZSTD_ipow() : * Return base^exponent. */ static U64 ZSTD_ipow(U64 base, U64 exponent) { U64 power = 1; while (exponent) { if (exponent & 1) power *= base; exponent >>= 1; base *= base; } return power; } #define ZSTD_ROLL_HASH_CHAR_OFFSET 10 /** ZSTD_rollingHash_append() : * Add the buffer to the hash value. */ static U64 ZSTD_rollingHash_append(U64 hash, void const* buf, size_t size) { BYTE const* istart = (BYTE const*)buf; size_t pos; for (pos = 0; pos < size; ++pos) { hash *= prime8bytes; hash += istart[pos] + ZSTD_ROLL_HASH_CHAR_OFFSET; } return hash; } /** ZSTD_rollingHash_compute() : * Compute the rolling hash value of the buffer. */ MEM_STATIC U64 ZSTD_rollingHash_compute(void const* buf, size_t size) { return ZSTD_rollingHash_append(0, buf, size); } /** ZSTD_rollingHash_primePower() : * Compute the primePower to be passed to ZSTD_rollingHash_rotate() for a hash * over a window of length bytes. */ MEM_STATIC U64 ZSTD_rollingHash_primePower(U32 length) { return ZSTD_ipow(prime8bytes, length - 1); } /** ZSTD_rollingHash_rotate() : * Rotate the rolling hash by one byte. */ MEM_STATIC U64 ZSTD_rollingHash_rotate(U64 hash, BYTE toRemove, BYTE toAdd, U64 primePower) { hash -= (toRemove + ZSTD_ROLL_HASH_CHAR_OFFSET) * primePower; hash *= prime8bytes; hash += toAdd + ZSTD_ROLL_HASH_CHAR_OFFSET; return hash; } /*-************************************* * Round buffer management ***************************************/ #if (ZSTD_WINDOWLOG_MAX_64 > 31) # error "ZSTD_WINDOWLOG_MAX is too large : would overflow ZSTD_CURRENT_MAX" #endif /* Max current allowed */ #define ZSTD_CURRENT_MAX ((3U << 29) + (1U << ZSTD_WINDOWLOG_MAX)) /* Maximum chunk size before overflow correction needs to be called again */ #define ZSTD_CHUNKSIZE_MAX \ ( ((U32)-1) /* Maximum ending current index */ \ - ZSTD_CURRENT_MAX) /* Maximum beginning lowLimit */ /** * ZSTD_window_clear(): * Clears the window containing the history by simply setting it to empty. */ MEM_STATIC void ZSTD_window_clear(ZSTD_window_t* window) { size_t const endT = (size_t)(window->nextSrc - window->base); U32 const end = (U32)endT; window->lowLimit = end; window->dictLimit = end; } /** * ZSTD_window_hasExtDict(): * Returns non-zero if the window has a non-empty extDict. */ MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window) { return window.lowLimit < window.dictLimit; } /** * ZSTD_matchState_dictMode(): * Inspects the provided matchState and figures out what dictMode should be * passed to the compressor. */ MEM_STATIC ZSTD_dictMode_e ZSTD_matchState_dictMode(const ZSTD_matchState_t *ms) { return ZSTD_window_hasExtDict(ms->window) ? ZSTD_extDict : ms->dictMatchState != NULL ? ZSTD_dictMatchState : ZSTD_noDict; } /** * ZSTD_window_needOverflowCorrection(): * Returns non-zero if the indices are getting too large and need overflow * protection. */ MEM_STATIC U32 ZSTD_window_needOverflowCorrection(ZSTD_window_t const window, void const* srcEnd) { U32 const current = (U32)((BYTE const*)srcEnd - window.base); return current > ZSTD_CURRENT_MAX; } /** * ZSTD_window_correctOverflow(): * Reduces the indices to protect from index overflow. * Returns the correction made to the indices, which must be applied to every * stored index. * * The least significant cycleLog bits of the indices must remain the same, * which may be 0. Every index up to maxDist in the past must be valid. * NOTE: (maxDist & cycleMask) must be zero. */ MEM_STATIC U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog, U32 maxDist, void const* src) { /* preemptive overflow correction: * 1. correction is large enough: * lowLimit > (3<<29) ==> current > 3<<29 + 1< (3<<29 + 1< (3<<29) - (1< (3<<29) - (1<<30) (NOTE: chainLog <= 30) * > 1<<29 * * 2. (ip+ZSTD_CHUNKSIZE_MAX - cctx->base) doesn't overflow: * After correction, current is less than (1<base < 1<<32. * 3. (cctx->lowLimit + 1< 3<<29 + 1<base); U32 const currentCycle0 = current & cycleMask; /* Exclude zero so that newCurrent - maxDist >= 1. */ U32 const currentCycle1 = currentCycle0 == 0 ? (1U << cycleLog) : currentCycle0; U32 const newCurrent = currentCycle1 + maxDist; U32 const correction = current - newCurrent; assert((maxDist & cycleMask) == 0); assert(current > newCurrent); /* Loose bound, should be around 1<<29 (see above) */ assert(correction > 1<<28); window->base += correction; window->dictBase += correction; if (window->lowLimit <= correction) window->lowLimit = 1; else window->lowLimit -= correction; if (window->dictLimit <= correction) window->dictLimit = 1; else window->dictLimit -= correction; /* Ensure we can still reference the full window. */ assert(newCurrent >= maxDist); assert(newCurrent - maxDist >= 1); /* Ensure that lowLimit and dictLimit didn't underflow. */ assert(window->lowLimit <= newCurrent); assert(window->dictLimit <= newCurrent); DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x", correction, window->lowLimit); return correction; } /** * ZSTD_window_enforceMaxDist(): * Updates lowLimit so that: * (srcEnd - base) - lowLimit == maxDist + loadedDictEnd * * It ensures index is valid as long as index >= lowLimit. * This must be called before a block compression call. * * loadedDictEnd is only defined if a dictionary is in use for current compression. * As the name implies, loadedDictEnd represents the index at end of dictionary. * The value lies within context's referential, it can be directly compared to blockEndIdx. * * If loadedDictEndPtr is NULL, no dictionary is in use, and we use loadedDictEnd == 0. * If loadedDictEndPtr is not NULL, we set it to zero after updating lowLimit. * This is because dictionaries are allowed to be referenced fully * as long as the last byte of the dictionary is in the window. * Once input has progressed beyond window size, dictionary cannot be referenced anymore. * * In normal dict mode, the dictionary lies between lowLimit and dictLimit. * In dictMatchState mode, lowLimit and dictLimit are the same, * and the dictionary is below them. * forceWindow and dictMatchState are therefore incompatible. */ MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window, const void* blockEnd, U32 maxDist, U32* loadedDictEndPtr, const ZSTD_matchState_t** dictMatchStatePtr) { U32 const blockEndIdx = (U32)((BYTE const*)blockEnd - window->base); U32 const loadedDictEnd = (loadedDictEndPtr != NULL) ? *loadedDictEndPtr : 0; DEBUGLOG(5, "ZSTD_window_enforceMaxDist: blockEndIdx=%u, maxDist=%u, loadedDictEnd=%u", (unsigned)blockEndIdx, (unsigned)maxDist, (unsigned)loadedDictEnd); /* - When there is no dictionary : loadedDictEnd == 0. In which case, the test (blockEndIdx > maxDist) is merely to avoid overflowing next operation `newLowLimit = blockEndIdx - maxDist`. - When there is a standard dictionary : Index referential is copied from the dictionary, which means it starts from 0. In which case, loadedDictEnd == dictSize, and it makes sense to compare `blockEndIdx > maxDist + dictSize` since `blockEndIdx` also starts from zero. - When there is an attached dictionary : loadedDictEnd is expressed within the referential of the context, so it can be directly compared against blockEndIdx. */ if (blockEndIdx > maxDist + loadedDictEnd) { U32 const newLowLimit = blockEndIdx - maxDist; if (window->lowLimit < newLowLimit) window->lowLimit = newLowLimit; if (window->dictLimit < window->lowLimit) { DEBUGLOG(5, "Update dictLimit to match lowLimit, from %u to %u", (unsigned)window->dictLimit, (unsigned)window->lowLimit); window->dictLimit = window->lowLimit; } /* On reaching window size, dictionaries are invalidated */ if (loadedDictEndPtr) *loadedDictEndPtr = 0; if (dictMatchStatePtr) *dictMatchStatePtr = NULL; } } /* Similar to ZSTD_window_enforceMaxDist(), * but only invalidates dictionary * when input progresses beyond window size. * assumption : loadedDictEndPtr and dictMatchStatePtr are valid (non NULL) * loadedDictEnd uses same referential as window->base * maxDist is the window size */ MEM_STATIC void ZSTD_checkDictValidity(const ZSTD_window_t* window, const void* blockEnd, U32 maxDist, U32* loadedDictEndPtr, const ZSTD_matchState_t** dictMatchStatePtr) { assert(loadedDictEndPtr != NULL); assert(dictMatchStatePtr != NULL); { U32 const blockEndIdx = (U32)((BYTE const*)blockEnd - window->base); U32 const loadedDictEnd = *loadedDictEndPtr; DEBUGLOG(5, "ZSTD_checkDictValidity: blockEndIdx=%u, maxDist=%u, loadedDictEnd=%u", (unsigned)blockEndIdx, (unsigned)maxDist, (unsigned)loadedDictEnd); assert(blockEndIdx >= loadedDictEnd); if (blockEndIdx > loadedDictEnd + maxDist) { /* On reaching window size, dictionaries are invalidated. * For simplification, if window size is reached anywhere within next block, * the dictionary is invalidated for the full block. */ DEBUGLOG(6, "invalidating dictionary for current block (distance > windowSize)"); *loadedDictEndPtr = 0; *dictMatchStatePtr = NULL; } else { if (*loadedDictEndPtr != 0) { DEBUGLOG(6, "dictionary considered valid for current block"); } } } } MEM_STATIC void ZSTD_window_init(ZSTD_window_t* window) { memset(window, 0, sizeof(*window)); window->base = (BYTE const*)""; window->dictBase = (BYTE const*)""; window->dictLimit = 1; /* start from 1, so that 1st position is valid */ window->lowLimit = 1; /* it ensures first and later CCtx usages compress the same */ window->nextSrc = window->base + 1; /* see issue #1241 */ } /** * ZSTD_window_update(): * Updates the window by appending [src, src + srcSize) to the window. * If it is not contiguous, the current prefix becomes the extDict, and we * forget about the extDict. Handles overlap of the prefix and extDict. * Returns non-zero if the segment is contiguous. */ MEM_STATIC U32 ZSTD_window_update(ZSTD_window_t* window, void const* src, size_t srcSize) { BYTE const* const ip = (BYTE const*)src; U32 contiguous = 1; DEBUGLOG(5, "ZSTD_window_update"); if (srcSize == 0) return contiguous; assert(window->base != NULL); assert(window->dictBase != NULL); /* Check if blocks follow each other */ if (src != window->nextSrc) { /* not contiguous */ size_t const distanceFromBase = (size_t)(window->nextSrc - window->base); DEBUGLOG(5, "Non contiguous blocks, new segment starts at %u", window->dictLimit); window->lowLimit = window->dictLimit; assert(distanceFromBase == (size_t)(U32)distanceFromBase); /* should never overflow */ window->dictLimit = (U32)distanceFromBase; window->dictBase = window->base; window->base = ip - distanceFromBase; /* ms->nextToUpdate = window->dictLimit; */ if (window->dictLimit - window->lowLimit < HASH_READ_SIZE) window->lowLimit = window->dictLimit; /* too small extDict */ contiguous = 0; } window->nextSrc = ip + srcSize; /* if input and dictionary overlap : reduce dictionary (area presumed modified by input) */ if ( (ip+srcSize > window->dictBase + window->lowLimit) & (ip < window->dictBase + window->dictLimit)) { ptrdiff_t const highInputIdx = (ip + srcSize) - window->dictBase; U32 const lowLimitMax = (highInputIdx > (ptrdiff_t)window->dictLimit) ? window->dictLimit : (U32)highInputIdx; window->lowLimit = lowLimitMax; DEBUGLOG(5, "Overlapping extDict and input : new lowLimit = %u", window->lowLimit); } return contiguous; } /** * Returns the lowest allowed match index. It may either be in the ext-dict or the prefix. */ MEM_STATIC U32 ZSTD_getLowestMatchIndex(const ZSTD_matchState_t* ms, U32 current, unsigned windowLog) { U32 const maxDistance = 1U << windowLog; U32 const lowestValid = ms->window.lowLimit; U32 const withinWindow = (current - lowestValid > maxDistance) ? current - maxDistance : lowestValid; U32 const isDictionary = (ms->loadedDictEnd != 0); U32 const matchLowest = isDictionary ? lowestValid : withinWindow; return matchLowest; } /** * Returns the lowest allowed match index in the prefix. */ MEM_STATIC U32 ZSTD_getLowestPrefixIndex(const ZSTD_matchState_t* ms, U32 current, unsigned windowLog) { U32 const maxDistance = 1U << windowLog; U32 const lowestValid = ms->window.dictLimit; U32 const withinWindow = (current - lowestValid > maxDistance) ? current - maxDistance : lowestValid; U32 const isDictionary = (ms->loadedDictEnd != 0); U32 const matchLowest = isDictionary ? lowestValid : withinWindow; return matchLowest; } /* debug functions */ #if (DEBUGLEVEL>=2) MEM_STATIC double ZSTD_fWeight(U32 rawStat) { U32 const fp_accuracy = 8; U32 const fp_multiplier = (1 << fp_accuracy); U32 const newStat = rawStat + 1; U32 const hb = ZSTD_highbit32(newStat); U32 const BWeight = hb * fp_multiplier; U32 const FWeight = (newStat << fp_accuracy) >> hb; U32 const weight = BWeight + FWeight; assert(hb + fp_accuracy < 31); return (double)weight / fp_multiplier; } /* display a table content, * listing each element, its frequency, and its predicted bit cost */ MEM_STATIC void ZSTD_debugTable(const U32* table, U32 max) { unsigned u, sum; for (u=0, sum=0; u<=max; u++) sum += table[u]; DEBUGLOG(2, "total nb elts: %u", sum); for (u=0; u<=max; u++) { DEBUGLOG(2, "%2u: %5u (%.2f)", u, table[u], ZSTD_fWeight(sum) - ZSTD_fWeight(table[u]) ); } } #endif #if defined (__cplusplus) } #endif /* =============================================================== * Shared internal declarations * These prototypes may be called from sources not in lib/compress * =============================================================== */ /* ZSTD_loadCEntropy() : * dict : must point at beginning of a valid zstd dictionary. * return : size of dictionary header (size of magic number + dict ID + entropy tables) * assumptions : magic number supposed already checked * and dictSize >= 8 */ size_t ZSTD_loadCEntropy(ZSTD_compressedBlockState_t* bs, void* workspace, short* offcodeNCount, unsigned* offcodeMaxValue, const void* const dict, size_t dictSize); void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs); /* ============================================================== * Private declarations * These prototypes shall only be called from within lib/compress * ============================================================== */ /* ZSTD_getCParamsFromCCtxParams() : * cParams are built depending on compressionLevel, src size hints, * LDM and manually set compression parameters. * Note: srcSizeHint == 0 means 0! */ ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams( const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize); /*! ZSTD_initCStream_internal() : * Private use only. Init streaming operation. * expects params to be valid. * must receive dict, or cdict, or none, but not both. * @return : 0, or an error code */ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs, const void* dict, size_t dictSize, const ZSTD_CDict* cdict, const ZSTD_CCtx_params* params, unsigned long long pledgedSrcSize); void ZSTD_resetSeqStore(seqStore_t* ssPtr); /*! ZSTD_getCParamsFromCDict() : * as the name implies */ ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict); /* ZSTD_compressBegin_advanced_internal() : * Private use only. To be called from zstdmt_compress.c. */ size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType, ZSTD_dictTableLoadMethod_e dtlm, const ZSTD_CDict* cdict, const ZSTD_CCtx_params* params, unsigned long long pledgedSrcSize); /* ZSTD_compress_advanced_internal() : * Private use only. To be called from zstdmt_compress.c. */ size_t ZSTD_compress_advanced_internal(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const void* dict,size_t dictSize, const ZSTD_CCtx_params* params); /* ZSTD_writeLastEmptyBlock() : * output an empty Block with end-of-frame mark to complete a frame * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h)) * or an error code if `dstCapacity` is too small ( 1 */ U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat); #endif /* ZSTD_COMPRESS_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_compress_literals.c0000644000076500000240000001415414601576577026465 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /*-************************************* * Dependencies ***************************************/ #include "zstd_compress_literals.h" size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize) { BYTE* const ostart = (BYTE* const)dst; U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); RETURN_ERROR_IF(srcSize + flSize > dstCapacity, dstSize_tooSmall, ""); switch(flSize) { case 1: /* 2 - 1 - 5 */ ostart[0] = (BYTE)((U32)set_basic + (srcSize<<3)); break; case 2: /* 2 - 2 - 12 */ MEM_writeLE16(ostart, (U16)((U32)set_basic + (1<<2) + (srcSize<<4))); break; case 3: /* 2 - 2 - 20 */ MEM_writeLE32(ostart, (U32)((U32)set_basic + (3<<2) + (srcSize<<4))); break; default: /* not necessary : flSize is {1,2,3} */ assert(0); } memcpy(ostart + flSize, src, srcSize); DEBUGLOG(5, "Raw literals: %u -> %u", (U32)srcSize, (U32)(srcSize + flSize)); return srcSize + flSize; } size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize) { BYTE* const ostart = (BYTE* const)dst; U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); (void)dstCapacity; /* dstCapacity already guaranteed to be >=4, hence large enough */ switch(flSize) { case 1: /* 2 - 1 - 5 */ ostart[0] = (BYTE)((U32)set_rle + (srcSize<<3)); break; case 2: /* 2 - 2 - 12 */ MEM_writeLE16(ostart, (U16)((U32)set_rle + (1<<2) + (srcSize<<4))); break; case 3: /* 2 - 2 - 20 */ MEM_writeLE32(ostart, (U32)((U32)set_rle + (3<<2) + (srcSize<<4))); break; default: /* not necessary : flSize is {1,2,3} */ assert(0); } ostart[flSize] = *(const BYTE*)src; DEBUGLOG(5, "RLE literals: %u -> %u", (U32)srcSize, (U32)flSize + 1); return flSize+1; } size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, ZSTD_hufCTables_t* nextHuf, ZSTD_strategy strategy, int disableLiteralCompression, void* dst, size_t dstCapacity, const void* src, size_t srcSize, void* entropyWorkspace, size_t entropyWorkspaceSize, const int bmi2) { size_t const minGain = ZSTD_minGain(srcSize, strategy); size_t const lhSize = 3 + (srcSize >= 1 KB) + (srcSize >= 16 KB); BYTE* const ostart = (BYTE*)dst; U32 singleStream = srcSize < 256; symbolEncodingType_e hType = set_compressed; size_t cLitSize; DEBUGLOG(5,"ZSTD_compressLiterals (disableLiteralCompression=%i srcSize=%u)", disableLiteralCompression, (U32)srcSize); /* Prepare nextEntropy assuming reusing the existing table */ memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); if (disableLiteralCompression) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); /* small ? don't even attempt compression (speed opt) */ # define COMPRESS_LITERALS_SIZE_MIN 63 { size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN; if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } RETURN_ERROR_IF(dstCapacity < lhSize+1, dstSize_tooSmall, "not enough space for compression"); { HUF_repeat repeat = prevHuf->repeatMode; int const preferRepeat = strategy < ZSTD_lazy ? srcSize <= 1024 : 0; if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1; cLitSize = singleStream ? HUF_compress1X_repeat( ostart+lhSize, dstCapacity-lhSize, src, srcSize, HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize, (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2) : HUF_compress4X_repeat( ostart+lhSize, dstCapacity-lhSize, src, srcSize, HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize, (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2); if (repeat != HUF_repeat_none) { /* reused the existing table */ DEBUGLOG(5, "Reusing previous huffman table"); hType = set_repeat; } } if ((cLitSize==0) | (cLitSize >= srcSize - minGain) | ERR_isError(cLitSize)) { memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } if (cLitSize==1) { memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); } if (hType == set_compressed) { /* using a newly constructed table */ nextHuf->repeatMode = HUF_repeat_check; } /* Build header */ switch(lhSize) { case 3: /* 2 - 2 - 10 - 10 */ { U32 const lhc = hType + ((!singleStream) << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<14); MEM_writeLE24(ostart, lhc); break; } case 4: /* 2 - 2 - 14 - 14 */ { U32 const lhc = hType + (2 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<18); MEM_writeLE32(ostart, lhc); break; } case 5: /* 2 - 2 - 18 - 18 */ { U32 const lhc = hType + (3 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<22); MEM_writeLE32(ostart, lhc); ostart[4] = (BYTE)(cLitSize >> 10); break; } default: /* not possible : lhSize is {3,4,5} */ assert(0); } DEBUGLOG(5, "Compressed literals: %u -> %u", (U32)srcSize, (U32)(lhSize+cLitSize)); return lhSize+cLitSize; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_compress_literals.h0000644000076500000240000000230514601576577026465 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_COMPRESS_LITERALS_H #define ZSTD_COMPRESS_LITERALS_H #include "zstd_compress_internal.h" /* ZSTD_hufCTables_t, ZSTD_minGain() */ size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize); size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize); size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, ZSTD_hufCTables_t* nextHuf, ZSTD_strategy strategy, int disableLiteralCompression, void* dst, size_t dstCapacity, const void* src, size_t srcSize, void* entropyWorkspace, size_t entropyWorkspaceSize, const int bmi2); #endif /* ZSTD_COMPRESS_LITERALS_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_compress_sequences.c0000644000076500000240000004535014601576577026643 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /*-************************************* * Dependencies ***************************************/ #include "zstd_compress_sequences.h" /** * -log2(x / 256) lookup table for x in [0, 256). * If x == 0: Return 0 * Else: Return floor(-log2(x / 256) * 256) */ static unsigned const kInverseProbabilityLog256[256] = { 0, 2048, 1792, 1642, 1536, 1453, 1386, 1329, 1280, 1236, 1197, 1162, 1130, 1100, 1073, 1047, 1024, 1001, 980, 960, 941, 923, 906, 889, 874, 859, 844, 830, 817, 804, 791, 779, 768, 756, 745, 734, 724, 714, 704, 694, 685, 676, 667, 658, 650, 642, 633, 626, 618, 610, 603, 595, 588, 581, 574, 567, 561, 554, 548, 542, 535, 529, 523, 517, 512, 506, 500, 495, 489, 484, 478, 473, 468, 463, 458, 453, 448, 443, 438, 434, 429, 424, 420, 415, 411, 407, 402, 398, 394, 390, 386, 382, 377, 373, 370, 366, 362, 358, 354, 350, 347, 343, 339, 336, 332, 329, 325, 322, 318, 315, 311, 308, 305, 302, 298, 295, 292, 289, 286, 282, 279, 276, 273, 270, 267, 264, 261, 258, 256, 253, 250, 247, 244, 241, 239, 236, 233, 230, 228, 225, 222, 220, 217, 215, 212, 209, 207, 204, 202, 199, 197, 194, 192, 190, 187, 185, 182, 180, 178, 175, 173, 171, 168, 166, 164, 162, 159, 157, 155, 153, 151, 149, 146, 144, 142, 140, 138, 136, 134, 132, 130, 128, 126, 123, 121, 119, 117, 115, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, 94, 93, 91, 89, 87, 85, 83, 82, 80, 78, 76, 74, 73, 71, 69, 67, 66, 64, 62, 61, 59, 57, 55, 54, 52, 50, 49, 47, 46, 44, 42, 41, 39, 37, 36, 34, 33, 31, 30, 28, 26, 25, 23, 22, 20, 19, 17, 16, 14, 13, 11, 10, 8, 7, 5, 4, 2, 1, }; static unsigned ZSTD_getFSEMaxSymbolValue(FSE_CTable const* ctable) { void const* ptr = ctable; U16 const* u16ptr = (U16 const*)ptr; U32 const maxSymbolValue = MEM_read16(u16ptr + 1); return maxSymbolValue; } /** * Returns the cost in bytes of encoding the normalized count header. * Returns an error if any of the helper functions return an error. */ static size_t ZSTD_NCountCost(unsigned const* count, unsigned const max, size_t const nbSeq, unsigned const FSELog) { BYTE wksp[FSE_NCOUNTBOUND]; S16 norm[MaxSeq + 1]; const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max); FORWARD_IF_ERROR(FSE_normalizeCount(norm, tableLog, count, nbSeq, max), ""); return FSE_writeNCount(wksp, sizeof(wksp), norm, max, tableLog); } /** * Returns the cost in bits of encoding the distribution described by count * using the entropy bound. */ static size_t ZSTD_entropyCost(unsigned const* count, unsigned const max, size_t const total) { unsigned cost = 0; unsigned s; for (s = 0; s <= max; ++s) { unsigned norm = (unsigned)((256 * count[s]) / total); if (count[s] != 0 && norm == 0) norm = 1; assert(count[s] < total); cost += count[s] * kInverseProbabilityLog256[norm]; } return cost >> 8; } /** * Returns the cost in bits of encoding the distribution in count using ctable. * Returns an error if ctable cannot represent all the symbols in count. */ size_t ZSTD_fseBitCost( FSE_CTable const* ctable, unsigned const* count, unsigned const max) { unsigned const kAccuracyLog = 8; size_t cost = 0; unsigned s; FSE_CState_t cstate; FSE_initCState(&cstate, ctable); if (ZSTD_getFSEMaxSymbolValue(ctable) < max) { DEBUGLOG(5, "Repeat FSE_CTable has maxSymbolValue %u < %u", ZSTD_getFSEMaxSymbolValue(ctable), max); return ERROR(GENERIC); } for (s = 0; s <= max; ++s) { unsigned const tableLog = cstate.stateLog; unsigned const badCost = (tableLog + 1) << kAccuracyLog; unsigned const bitCost = FSE_bitCost(cstate.symbolTT, tableLog, s, kAccuracyLog); if (count[s] == 0) continue; if (bitCost >= badCost) { DEBUGLOG(5, "Repeat FSE_CTable has Prob[%u] == 0", s); return ERROR(GENERIC); } cost += (size_t)count[s] * bitCost; } return cost >> kAccuracyLog; } /** * Returns the cost in bits of encoding the distribution in count using the * table described by norm. The max symbol support by norm is assumed >= max. * norm must be valid for every symbol with non-zero probability in count. */ size_t ZSTD_crossEntropyCost(short const* norm, unsigned accuracyLog, unsigned const* count, unsigned const max) { unsigned const shift = 8 - accuracyLog; size_t cost = 0; unsigned s; assert(accuracyLog <= 8); for (s = 0; s <= max; ++s) { unsigned const normAcc = (norm[s] != -1) ? (unsigned)norm[s] : 1; unsigned const norm256 = normAcc << shift; assert(norm256 > 0); assert(norm256 < 256); cost += count[s] * kInverseProbabilityLog256[norm256]; } return cost >> 8; } symbolEncodingType_e ZSTD_selectEncodingType( FSE_repeat* repeatMode, unsigned const* count, unsigned const max, size_t const mostFrequent, size_t nbSeq, unsigned const FSELog, FSE_CTable const* prevCTable, short const* defaultNorm, U32 defaultNormLog, ZSTD_defaultPolicy_e const isDefaultAllowed, ZSTD_strategy const strategy) { ZSTD_STATIC_ASSERT(ZSTD_defaultDisallowed == 0 && ZSTD_defaultAllowed != 0); if (mostFrequent == nbSeq) { *repeatMode = FSE_repeat_none; if (isDefaultAllowed && nbSeq <= 2) { /* Prefer set_basic over set_rle when there are 2 or less symbols, * since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol. * If basic encoding isn't possible, always choose RLE. */ DEBUGLOG(5, "Selected set_basic"); return set_basic; } DEBUGLOG(5, "Selected set_rle"); return set_rle; } if (strategy < ZSTD_lazy) { if (isDefaultAllowed) { size_t const staticFse_nbSeq_max = 1000; size_t const mult = 10 - strategy; size_t const baseLog = 3; size_t const dynamicFse_nbSeq_min = (((size_t)1 << defaultNormLog) * mult) >> baseLog; /* 28-36 for offset, 56-72 for lengths */ assert(defaultNormLog >= 5 && defaultNormLog <= 6); /* xx_DEFAULTNORMLOG */ assert(mult <= 9 && mult >= 7); if ( (*repeatMode == FSE_repeat_valid) && (nbSeq < staticFse_nbSeq_max) ) { DEBUGLOG(5, "Selected set_repeat"); return set_repeat; } if ( (nbSeq < dynamicFse_nbSeq_min) || (mostFrequent < (nbSeq >> (defaultNormLog-1))) ) { DEBUGLOG(5, "Selected set_basic"); /* The format allows default tables to be repeated, but it isn't useful. * When using simple heuristics to select encoding type, we don't want * to confuse these tables with dictionaries. When running more careful * analysis, we don't need to waste time checking both repeating tables * and default tables. */ *repeatMode = FSE_repeat_none; return set_basic; } } } else { size_t const basicCost = isDefaultAllowed ? ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, count, max) : ERROR(GENERIC); size_t const repeatCost = *repeatMode != FSE_repeat_none ? ZSTD_fseBitCost(prevCTable, count, max) : ERROR(GENERIC); size_t const NCountCost = ZSTD_NCountCost(count, max, nbSeq, FSELog); size_t const compressedCost = (NCountCost << 3) + ZSTD_entropyCost(count, max, nbSeq); if (isDefaultAllowed) { assert(!ZSTD_isError(basicCost)); assert(!(*repeatMode == FSE_repeat_valid && ZSTD_isError(repeatCost))); } assert(!ZSTD_isError(NCountCost)); assert(compressedCost < ERROR(maxCode)); DEBUGLOG(5, "Estimated bit costs: basic=%u\trepeat=%u\tcompressed=%u", (unsigned)basicCost, (unsigned)repeatCost, (unsigned)compressedCost); if (basicCost <= repeatCost && basicCost <= compressedCost) { DEBUGLOG(5, "Selected set_basic"); assert(isDefaultAllowed); *repeatMode = FSE_repeat_none; return set_basic; } if (repeatCost <= compressedCost) { DEBUGLOG(5, "Selected set_repeat"); assert(!ZSTD_isError(repeatCost)); return set_repeat; } assert(compressedCost < basicCost && compressedCost < repeatCost); } DEBUGLOG(5, "Selected set_compressed"); *repeatMode = FSE_repeat_check; return set_compressed; } size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, FSE_CTable* nextCTable, U32 FSELog, symbolEncodingType_e type, unsigned* count, U32 max, const BYTE* codeTable, size_t nbSeq, const S16* defaultNorm, U32 defaultNormLog, U32 defaultMax, const FSE_CTable* prevCTable, size_t prevCTableSize, void* entropyWorkspace, size_t entropyWorkspaceSize) { BYTE* op = (BYTE*)dst; const BYTE* const oend = op + dstCapacity; DEBUGLOG(6, "ZSTD_buildCTable (dstCapacity=%u)", (unsigned)dstCapacity); switch (type) { case set_rle: FORWARD_IF_ERROR(FSE_buildCTable_rle(nextCTable, (BYTE)max), ""); RETURN_ERROR_IF(dstCapacity==0, dstSize_tooSmall, "not enough space"); *op = codeTable[0]; return 1; case set_repeat: memcpy(nextCTable, prevCTable, prevCTableSize); return 0; case set_basic: FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, defaultNorm, defaultMax, defaultNormLog, entropyWorkspace, entropyWorkspaceSize), ""); /* note : could be pre-calculated */ return 0; case set_compressed: { S16 norm[MaxSeq + 1]; size_t nbSeq_1 = nbSeq; const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max); if (count[codeTable[nbSeq-1]] > 1) { count[codeTable[nbSeq-1]]--; nbSeq_1--; } assert(nbSeq_1 > 1); FORWARD_IF_ERROR(FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max), ""); { size_t const NCountSize = FSE_writeNCount(op, oend - op, norm, max, tableLog); /* overflow protected */ FORWARD_IF_ERROR(NCountSize, "FSE_writeNCount failed"); FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, norm, max, tableLog, entropyWorkspace, entropyWorkspaceSize), ""); return NCountSize; } } default: assert(0); RETURN_ERROR(GENERIC, "impossible to reach"); } } FORCE_INLINE_TEMPLATE size_t ZSTD_encodeSequences_body( void* dst, size_t dstCapacity, FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, seqDef const* sequences, size_t nbSeq, int longOffsets) { BIT_CStream_t blockStream; FSE_CState_t stateMatchLength; FSE_CState_t stateOffsetBits; FSE_CState_t stateLitLength; RETURN_ERROR_IF( ERR_isError(BIT_initCStream(&blockStream, dst, dstCapacity)), dstSize_tooSmall, "not enough space remaining"); DEBUGLOG(6, "available space for bitstream : %i (dstCapacity=%u)", (int)(blockStream.endPtr - blockStream.startPtr), (unsigned)dstCapacity); /* first symbols */ FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]); FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]); FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]); BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]); if (MEM_32bits()) BIT_flushBits(&blockStream); BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]); if (MEM_32bits()) BIT_flushBits(&blockStream); if (longOffsets) { U32 const ofBits = ofCodeTable[nbSeq-1]; unsigned const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); if (extraBits) { BIT_addBits(&blockStream, sequences[nbSeq-1].offset, extraBits); BIT_flushBits(&blockStream); } BIT_addBits(&blockStream, sequences[nbSeq-1].offset >> extraBits, ofBits - extraBits); } else { BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]); } BIT_flushBits(&blockStream); { size_t n; for (n=nbSeq-2 ; n= 64-7-(LLFSELog+MLFSELog+OffFSELog))) BIT_flushBits(&blockStream); /* (7)*/ BIT_addBits(&blockStream, sequences[n].litLength, llBits); if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream); BIT_addBits(&blockStream, sequences[n].matchLength, mlBits); if (MEM_32bits() || (ofBits+mlBits+llBits > 56)) BIT_flushBits(&blockStream); if (longOffsets) { unsigned const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); if (extraBits) { BIT_addBits(&blockStream, sequences[n].offset, extraBits); BIT_flushBits(&blockStream); /* (7)*/ } BIT_addBits(&blockStream, sequences[n].offset >> extraBits, ofBits - extraBits); /* 31 */ } else { BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */ } BIT_flushBits(&blockStream); /* (7)*/ DEBUGLOG(7, "remaining space : %i", (int)(blockStream.endPtr - blockStream.ptr)); } } DEBUGLOG(6, "ZSTD_encodeSequences: flushing ML state with %u bits", stateMatchLength.stateLog); FSE_flushCState(&blockStream, &stateMatchLength); DEBUGLOG(6, "ZSTD_encodeSequences: flushing Off state with %u bits", stateOffsetBits.stateLog); FSE_flushCState(&blockStream, &stateOffsetBits); DEBUGLOG(6, "ZSTD_encodeSequences: flushing LL state with %u bits", stateLitLength.stateLog); FSE_flushCState(&blockStream, &stateLitLength); { size_t const streamSize = BIT_closeCStream(&blockStream); RETURN_ERROR_IF(streamSize==0, dstSize_tooSmall, "not enough space"); return streamSize; } } static size_t ZSTD_encodeSequences_default( void* dst, size_t dstCapacity, FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, seqDef const* sequences, size_t nbSeq, int longOffsets) { return ZSTD_encodeSequences_body(dst, dstCapacity, CTable_MatchLength, mlCodeTable, CTable_OffsetBits, ofCodeTable, CTable_LitLength, llCodeTable, sequences, nbSeq, longOffsets); } #if DYNAMIC_BMI2 static TARGET_ATTRIBUTE("bmi2") size_t ZSTD_encodeSequences_bmi2( void* dst, size_t dstCapacity, FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, seqDef const* sequences, size_t nbSeq, int longOffsets) { return ZSTD_encodeSequences_body(dst, dstCapacity, CTable_MatchLength, mlCodeTable, CTable_OffsetBits, ofCodeTable, CTable_LitLength, llCodeTable, sequences, nbSeq, longOffsets); } #endif size_t ZSTD_encodeSequences( void* dst, size_t dstCapacity, FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, seqDef const* sequences, size_t nbSeq, int longOffsets, int bmi2) { DEBUGLOG(5, "ZSTD_encodeSequences: dstCapacity = %u", (unsigned)dstCapacity); #if DYNAMIC_BMI2 if (bmi2) { return ZSTD_encodeSequences_bmi2(dst, dstCapacity, CTable_MatchLength, mlCodeTable, CTable_OffsetBits, ofCodeTable, CTable_LitLength, llCodeTable, sequences, nbSeq, longOffsets); } #endif (void)bmi2; return ZSTD_encodeSequences_default(dst, dstCapacity, CTable_MatchLength, mlCodeTable, CTable_OffsetBits, ofCodeTable, CTable_LitLength, llCodeTable, sequences, nbSeq, longOffsets); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_compress_sequences.h0000644000076500000240000000420114601576577026636 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_COMPRESS_SEQUENCES_H #define ZSTD_COMPRESS_SEQUENCES_H #include "../common/fse.h" /* FSE_repeat, FSE_CTable */ #include "../common/zstd_internal.h" /* symbolEncodingType_e, ZSTD_strategy */ typedef enum { ZSTD_defaultDisallowed = 0, ZSTD_defaultAllowed = 1 } ZSTD_defaultPolicy_e; symbolEncodingType_e ZSTD_selectEncodingType( FSE_repeat* repeatMode, unsigned const* count, unsigned const max, size_t const mostFrequent, size_t nbSeq, unsigned const FSELog, FSE_CTable const* prevCTable, short const* defaultNorm, U32 defaultNormLog, ZSTD_defaultPolicy_e const isDefaultAllowed, ZSTD_strategy const strategy); size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, FSE_CTable* nextCTable, U32 FSELog, symbolEncodingType_e type, unsigned* count, U32 max, const BYTE* codeTable, size_t nbSeq, const S16* defaultNorm, U32 defaultNormLog, U32 defaultMax, const FSE_CTable* prevCTable, size_t prevCTableSize, void* entropyWorkspace, size_t entropyWorkspaceSize); size_t ZSTD_encodeSequences( void* dst, size_t dstCapacity, FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, seqDef const* sequences, size_t nbSeq, int longOffsets, int bmi2); size_t ZSTD_fseBitCost( FSE_CTable const* ctable, unsigned const* count, unsigned const max); size_t ZSTD_crossEntropyCost(short const* norm, unsigned accuracyLog, unsigned const* count, unsigned const max); #endif /* ZSTD_COMPRESS_SEQUENCES_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_compress_superblock.c0000644000076500000240000012423714601576577027023 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /*-************************************* * Dependencies ***************************************/ #include "zstd_compress_superblock.h" #include "../common/zstd_internal.h" /* ZSTD_getSequenceLength */ #include "hist.h" /* HIST_countFast_wksp */ #include "zstd_compress_internal.h" #include "zstd_compress_sequences.h" #include "zstd_compress_literals.h" /*-************************************* * Superblock entropy buffer structs ***************************************/ /** ZSTD_hufCTablesMetadata_t : * Stores Literals Block Type for a super-block in hType, and * huffman tree description in hufDesBuffer. * hufDesSize refers to the size of huffman tree description in bytes. * This metadata is populated in ZSTD_buildSuperBlockEntropy_literal() */ typedef struct { symbolEncodingType_e hType; BYTE hufDesBuffer[500]; /* TODO give name to this value */ size_t hufDesSize; } ZSTD_hufCTablesMetadata_t; /** ZSTD_fseCTablesMetadata_t : * Stores symbol compression modes for a super-block in {ll, ol, ml}Type, and * fse tables in fseTablesBuffer. * fseTablesSize refers to the size of fse tables in bytes. * This metadata is populated in ZSTD_buildSuperBlockEntropy_sequences() */ typedef struct { symbolEncodingType_e llType; symbolEncodingType_e ofType; symbolEncodingType_e mlType; BYTE fseTablesBuffer[500]; /* TODO give name to this value */ size_t fseTablesSize; size_t lastCountSize; /* This is to account for bug in 1.3.4. More detail in ZSTD_compressSubBlock_sequences() */ } ZSTD_fseCTablesMetadata_t; typedef struct { ZSTD_hufCTablesMetadata_t hufMetadata; ZSTD_fseCTablesMetadata_t fseMetadata; } ZSTD_entropyCTablesMetadata_t; /** ZSTD_buildSuperBlockEntropy_literal() : * Builds entropy for the super-block literals. * Stores literals block type (raw, rle, compressed, repeat) and * huffman description table to hufMetadata. * @return : size of huffman description table or error code */ static size_t ZSTD_buildSuperBlockEntropy_literal(void* const src, size_t srcSize, const ZSTD_hufCTables_t* prevHuf, ZSTD_hufCTables_t* nextHuf, ZSTD_hufCTablesMetadata_t* hufMetadata, const int disableLiteralsCompression, void* workspace, size_t wkspSize) { BYTE* const wkspStart = (BYTE*)workspace; BYTE* const wkspEnd = wkspStart + wkspSize; BYTE* const countWkspStart = wkspStart; unsigned* const countWksp = (unsigned*)workspace; const size_t countWkspSize = (HUF_SYMBOLVALUE_MAX + 1) * sizeof(unsigned); BYTE* const nodeWksp = countWkspStart + countWkspSize; const size_t nodeWkspSize = wkspEnd-nodeWksp; unsigned maxSymbolValue = 255; unsigned huffLog = HUF_TABLELOG_DEFAULT; HUF_repeat repeat = prevHuf->repeatMode; DEBUGLOG(5, "ZSTD_buildSuperBlockEntropy_literal (srcSize=%zu)", srcSize); /* Prepare nextEntropy assuming reusing the existing table */ memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); if (disableLiteralsCompression) { DEBUGLOG(5, "set_basic - disabled"); hufMetadata->hType = set_basic; return 0; } /* small ? don't even attempt compression (speed opt) */ # define COMPRESS_LITERALS_SIZE_MIN 63 { size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN; if (srcSize <= minLitSize) { DEBUGLOG(5, "set_basic - too small"); hufMetadata->hType = set_basic; return 0; } } /* Scan input and build symbol stats */ { size_t const largest = HIST_count_wksp (countWksp, &maxSymbolValue, (const BYTE*)src, srcSize, workspace, wkspSize); FORWARD_IF_ERROR(largest, "HIST_count_wksp failed"); if (largest == srcSize) { DEBUGLOG(5, "set_rle"); hufMetadata->hType = set_rle; return 0; } if (largest <= (srcSize >> 7)+4) { DEBUGLOG(5, "set_basic - no gain"); hufMetadata->hType = set_basic; return 0; } } /* Validate the previous Huffman table */ if (repeat == HUF_repeat_check && !HUF_validateCTable((HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue)) { repeat = HUF_repeat_none; } /* Build Huffman Tree */ memset(nextHuf->CTable, 0, sizeof(nextHuf->CTable)); huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue); { size_t const maxBits = HUF_buildCTable_wksp((HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue, huffLog, nodeWksp, nodeWkspSize); FORWARD_IF_ERROR(maxBits, "HUF_buildCTable_wksp"); huffLog = (U32)maxBits; { /* Build and write the CTable */ size_t const newCSize = HUF_estimateCompressedSize( (HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue); size_t const hSize = HUF_writeCTable( hufMetadata->hufDesBuffer, sizeof(hufMetadata->hufDesBuffer), (HUF_CElt*)nextHuf->CTable, maxSymbolValue, huffLog); /* Check against repeating the previous CTable */ if (repeat != HUF_repeat_none) { size_t const oldCSize = HUF_estimateCompressedSize( (HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue); if (oldCSize < srcSize && (oldCSize <= hSize + newCSize || hSize + 12 >= srcSize)) { DEBUGLOG(5, "set_repeat - smaller"); memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); hufMetadata->hType = set_repeat; return 0; } } if (newCSize + hSize >= srcSize) { DEBUGLOG(5, "set_basic - no gains"); memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); hufMetadata->hType = set_basic; return 0; } DEBUGLOG(5, "set_compressed (hSize=%u)", (U32)hSize); hufMetadata->hType = set_compressed; nextHuf->repeatMode = HUF_repeat_check; return hSize; } } } /** ZSTD_buildSuperBlockEntropy_sequences() : * Builds entropy for the super-block sequences. * Stores symbol compression modes and fse table to fseMetadata. * @return : size of fse tables or error code */ static size_t ZSTD_buildSuperBlockEntropy_sequences(seqStore_t* seqStorePtr, const ZSTD_fseCTables_t* prevEntropy, ZSTD_fseCTables_t* nextEntropy, const ZSTD_CCtx_params* cctxParams, ZSTD_fseCTablesMetadata_t* fseMetadata, void* workspace, size_t wkspSize) { BYTE* const wkspStart = (BYTE*)workspace; BYTE* const wkspEnd = wkspStart + wkspSize; BYTE* const countWkspStart = wkspStart; unsigned* const countWksp = (unsigned*)workspace; const size_t countWkspSize = (MaxSeq + 1) * sizeof(unsigned); BYTE* const cTableWksp = countWkspStart + countWkspSize; const size_t cTableWkspSize = wkspEnd-cTableWksp; ZSTD_strategy const strategy = cctxParams->cParams.strategy; FSE_CTable* CTable_LitLength = nextEntropy->litlengthCTable; FSE_CTable* CTable_OffsetBits = nextEntropy->offcodeCTable; FSE_CTable* CTable_MatchLength = nextEntropy->matchlengthCTable; const BYTE* const ofCodeTable = seqStorePtr->ofCode; const BYTE* const llCodeTable = seqStorePtr->llCode; const BYTE* const mlCodeTable = seqStorePtr->mlCode; size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; BYTE* const ostart = fseMetadata->fseTablesBuffer; BYTE* const oend = ostart + sizeof(fseMetadata->fseTablesBuffer); BYTE* op = ostart; assert(cTableWkspSize >= (1 << MaxFSELog) * sizeof(FSE_FUNCTION_TYPE)); DEBUGLOG(5, "ZSTD_buildSuperBlockEntropy_sequences (nbSeq=%zu)", nbSeq); memset(workspace, 0, wkspSize); fseMetadata->lastCountSize = 0; /* convert length/distances into codes */ ZSTD_seqToCodes(seqStorePtr); /* build CTable for Literal Lengths */ { U32 LLtype; unsigned max = MaxLL; size_t const mostFrequent = HIST_countFast_wksp(countWksp, &max, llCodeTable, nbSeq, workspace, wkspSize); /* can't fail */ DEBUGLOG(5, "Building LL table"); nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode; LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode, countWksp, max, mostFrequent, nbSeq, LLFSELog, prevEntropy->litlengthCTable, LL_defaultNorm, LL_defaultNormLog, ZSTD_defaultAllowed, strategy); assert(set_basic < set_compressed && set_rle < set_compressed); assert(!(LLtype < set_compressed && nextEntropy->litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype, countWksp, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable), cTableWksp, cTableWkspSize); FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for LitLens failed"); if (LLtype == set_compressed) fseMetadata->lastCountSize = countSize; op += countSize; fseMetadata->llType = (symbolEncodingType_e) LLtype; } } /* build CTable for Offsets */ { U32 Offtype; unsigned max = MaxOff; size_t const mostFrequent = HIST_countFast_wksp(countWksp, &max, ofCodeTable, nbSeq, workspace, wkspSize); /* can't fail */ /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */ ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed; DEBUGLOG(5, "Building OF table"); nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode; Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode, countWksp, max, mostFrequent, nbSeq, OffFSELog, prevEntropy->offcodeCTable, OF_defaultNorm, OF_defaultNormLog, defaultPolicy, strategy); assert(!(Offtype < set_compressed && nextEntropy->offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype, countWksp, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable), cTableWksp, cTableWkspSize); FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for Offsets failed"); if (Offtype == set_compressed) fseMetadata->lastCountSize = countSize; op += countSize; fseMetadata->ofType = (symbolEncodingType_e) Offtype; } } /* build CTable for MatchLengths */ { U32 MLtype; unsigned max = MaxML; size_t const mostFrequent = HIST_countFast_wksp(countWksp, &max, mlCodeTable, nbSeq, workspace, wkspSize); /* can't fail */ DEBUGLOG(5, "Building ML table (remaining space : %i)", (int)(oend-op)); nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode; MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode, countWksp, max, mostFrequent, nbSeq, MLFSELog, prevEntropy->matchlengthCTable, ML_defaultNorm, ML_defaultNormLog, ZSTD_defaultAllowed, strategy); assert(!(MLtype < set_compressed && nextEntropy->matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype, countWksp, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable), cTableWksp, cTableWkspSize); FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for MatchLengths failed"); if (MLtype == set_compressed) fseMetadata->lastCountSize = countSize; op += countSize; fseMetadata->mlType = (symbolEncodingType_e) MLtype; } } assert((size_t) (op-ostart) <= sizeof(fseMetadata->fseTablesBuffer)); return op-ostart; } /** ZSTD_buildSuperBlockEntropy() : * Builds entropy for the super-block. * @return : 0 on success or error code */ static size_t ZSTD_buildSuperBlockEntropy(seqStore_t* seqStorePtr, const ZSTD_entropyCTables_t* prevEntropy, ZSTD_entropyCTables_t* nextEntropy, const ZSTD_CCtx_params* cctxParams, ZSTD_entropyCTablesMetadata_t* entropyMetadata, void* workspace, size_t wkspSize) { size_t const litSize = seqStorePtr->lit - seqStorePtr->litStart; DEBUGLOG(5, "ZSTD_buildSuperBlockEntropy"); entropyMetadata->hufMetadata.hufDesSize = ZSTD_buildSuperBlockEntropy_literal(seqStorePtr->litStart, litSize, &prevEntropy->huf, &nextEntropy->huf, &entropyMetadata->hufMetadata, ZSTD_disableLiteralsCompression(cctxParams), workspace, wkspSize); FORWARD_IF_ERROR(entropyMetadata->hufMetadata.hufDesSize, "ZSTD_buildSuperBlockEntropy_literal failed"); entropyMetadata->fseMetadata.fseTablesSize = ZSTD_buildSuperBlockEntropy_sequences(seqStorePtr, &prevEntropy->fse, &nextEntropy->fse, cctxParams, &entropyMetadata->fseMetadata, workspace, wkspSize); FORWARD_IF_ERROR(entropyMetadata->fseMetadata.fseTablesSize, "ZSTD_buildSuperBlockEntropy_sequences failed"); return 0; } /** ZSTD_compressSubBlock_literal() : * Compresses literals section for a sub-block. * When we have to write the Huffman table we will sometimes choose a header * size larger than necessary. This is because we have to pick the header size * before we know the table size + compressed size, so we have a bound on the * table size. If we guessed incorrectly, we fall back to uncompressed literals. * * We write the header when writeEntropy=1 and set entropyWrriten=1 when we succeeded * in writing the header, otherwise it is set to 0. * * hufMetadata->hType has literals block type info. * If it is set_basic, all sub-blocks literals section will be Raw_Literals_Block. * If it is set_rle, all sub-blocks literals section will be RLE_Literals_Block. * If it is set_compressed, first sub-block's literals section will be Compressed_Literals_Block * If it is set_compressed, first sub-block's literals section will be Treeless_Literals_Block * and the following sub-blocks' literals sections will be Treeless_Literals_Block. * @return : compressed size of literals section of a sub-block * Or 0 if it unable to compress. * Or error code */ static size_t ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable, const ZSTD_hufCTablesMetadata_t* hufMetadata, const BYTE* literals, size_t litSize, void* dst, size_t dstSize, const int bmi2, int writeEntropy, int* entropyWritten) { size_t const header = writeEntropy ? 200 : 0; size_t const lhSize = 3 + (litSize >= (1 KB - header)) + (litSize >= (16 KB - header)); BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstSize; BYTE* op = ostart + lhSize; U32 const singleStream = lhSize == 3; symbolEncodingType_e hType = writeEntropy ? hufMetadata->hType : set_repeat; size_t cLitSize = 0; (void)bmi2; /* TODO bmi2... */ DEBUGLOG(5, "ZSTD_compressSubBlock_literal (litSize=%zu, lhSize=%zu, writeEntropy=%d)", litSize, lhSize, writeEntropy); *entropyWritten = 0; if (litSize == 0 || hufMetadata->hType == set_basic) { DEBUGLOG(5, "ZSTD_compressSubBlock_literal using raw literal"); return ZSTD_noCompressLiterals(dst, dstSize, literals, litSize); } else if (hufMetadata->hType == set_rle) { DEBUGLOG(5, "ZSTD_compressSubBlock_literal using rle literal"); return ZSTD_compressRleLiteralsBlock(dst, dstSize, literals, litSize); } assert(litSize > 0); assert(hufMetadata->hType == set_compressed || hufMetadata->hType == set_repeat); if (writeEntropy && hufMetadata->hType == set_compressed) { memcpy(op, hufMetadata->hufDesBuffer, hufMetadata->hufDesSize); op += hufMetadata->hufDesSize; cLitSize += hufMetadata->hufDesSize; DEBUGLOG(5, "ZSTD_compressSubBlock_literal (hSize=%zu)", hufMetadata->hufDesSize); } /* TODO bmi2 */ { const size_t cSize = singleStream ? HUF_compress1X_usingCTable(op, oend-op, literals, litSize, hufTable) : HUF_compress4X_usingCTable(op, oend-op, literals, litSize, hufTable); op += cSize; cLitSize += cSize; if (cSize == 0 || ERR_isError(cSize)) { DEBUGLOG(5, "Failed to write entropy tables %s", ZSTD_getErrorName(cSize)); return 0; } /* If we expand and we aren't writing a header then emit uncompressed */ if (!writeEntropy && cLitSize >= litSize) { DEBUGLOG(5, "ZSTD_compressSubBlock_literal using raw literal because uncompressible"); return ZSTD_noCompressLiterals(dst, dstSize, literals, litSize); } /* If we are writing headers then allow expansion that doesn't change our header size. */ if (lhSize < (size_t)(3 + (cLitSize >= 1 KB) + (cLitSize >= 16 KB))) { assert(cLitSize > litSize); DEBUGLOG(5, "Literals expanded beyond allowed header size"); return ZSTD_noCompressLiterals(dst, dstSize, literals, litSize); } DEBUGLOG(5, "ZSTD_compressSubBlock_literal (cSize=%zu)", cSize); } /* Build header */ switch(lhSize) { case 3: /* 2 - 2 - 10 - 10 */ { U32 const lhc = hType + ((!singleStream) << 2) + ((U32)litSize<<4) + ((U32)cLitSize<<14); MEM_writeLE24(ostart, lhc); break; } case 4: /* 2 - 2 - 14 - 14 */ { U32 const lhc = hType + (2 << 2) + ((U32)litSize<<4) + ((U32)cLitSize<<18); MEM_writeLE32(ostart, lhc); break; } case 5: /* 2 - 2 - 18 - 18 */ { U32 const lhc = hType + (3 << 2) + ((U32)litSize<<4) + ((U32)cLitSize<<22); MEM_writeLE32(ostart, lhc); ostart[4] = (BYTE)(cLitSize >> 10); break; } default: /* not possible : lhSize is {3,4,5} */ assert(0); } *entropyWritten = 1; DEBUGLOG(5, "Compressed literals: %u -> %u", (U32)litSize, (U32)(op-ostart)); return op-ostart; } static size_t ZSTD_seqDecompressedSize(seqStore_t const* seqStore, const seqDef* sequences, size_t nbSeq, size_t litSize, int lastSequence) { const seqDef* const sstart = sequences; const seqDef* const send = sequences + nbSeq; const seqDef* sp = sstart; size_t matchLengthSum = 0; size_t litLengthSum = 0; while (send-sp > 0) { ZSTD_sequenceLength const seqLen = ZSTD_getSequenceLength(seqStore, sp); litLengthSum += seqLen.litLength; matchLengthSum += seqLen.matchLength; sp++; } assert(litLengthSum <= litSize); if (!lastSequence) { assert(litLengthSum == litSize); } return matchLengthSum + litSize; } /** ZSTD_compressSubBlock_sequences() : * Compresses sequences section for a sub-block. * fseMetadata->llType, fseMetadata->ofType, and fseMetadata->mlType have * symbol compression modes for the super-block. * The first successfully compressed block will have these in its header. * We set entropyWritten=1 when we succeed in compressing the sequences. * The following sub-blocks will always have repeat mode. * @return : compressed size of sequences section of a sub-block * Or 0 if it is unable to compress * Or error code. */ static size_t ZSTD_compressSubBlock_sequences(const ZSTD_fseCTables_t* fseTables, const ZSTD_fseCTablesMetadata_t* fseMetadata, const seqDef* sequences, size_t nbSeq, const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode, const ZSTD_CCtx_params* cctxParams, void* dst, size_t dstCapacity, const int bmi2, int writeEntropy, int* entropyWritten) { const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN; BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstCapacity; BYTE* op = ostart; BYTE* seqHead; DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (nbSeq=%zu, writeEntropy=%d, longOffsets=%d)", nbSeq, writeEntropy, longOffsets); *entropyWritten = 0; /* Sequences Header */ RETURN_ERROR_IF((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead*/, dstSize_tooSmall, ""); if (nbSeq < 0x7F) *op++ = (BYTE)nbSeq; else if (nbSeq < LONGNBSEQ) op[0] = (BYTE)((nbSeq>>8) + 0x80), op[1] = (BYTE)nbSeq, op+=2; else op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3; if (nbSeq==0) { return op - ostart; } /* seqHead : flags for FSE encoding type */ seqHead = op++; DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (seqHeadSize=%u)", (unsigned)(op-ostart)); if (writeEntropy) { const U32 LLtype = fseMetadata->llType; const U32 Offtype = fseMetadata->ofType; const U32 MLtype = fseMetadata->mlType; DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (fseTablesSize=%zu)", fseMetadata->fseTablesSize); *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2)); memcpy(op, fseMetadata->fseTablesBuffer, fseMetadata->fseTablesSize); op += fseMetadata->fseTablesSize; } else { const U32 repeat = set_repeat; *seqHead = (BYTE)((repeat<<6) + (repeat<<4) + (repeat<<2)); } { size_t const bitstreamSize = ZSTD_encodeSequences( op, oend - op, fseTables->matchlengthCTable, mlCode, fseTables->offcodeCTable, ofCode, fseTables->litlengthCTable, llCode, sequences, nbSeq, longOffsets, bmi2); FORWARD_IF_ERROR(bitstreamSize, "ZSTD_encodeSequences failed"); op += bitstreamSize; /* zstd versions <= 1.3.4 mistakenly report corruption when * FSE_readNCount() receives a buffer < 4 bytes. * Fixed by https://github.com/facebook/zstd/pull/1146. * This can happen when the last set_compressed table present is 2 * bytes and the bitstream is only one byte. * In this exceedingly rare case, we will simply emit an uncompressed * block, since it isn't worth optimizing. */ #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if (writeEntropy && fseMetadata->lastCountSize && fseMetadata->lastCountSize + bitstreamSize < 4) { /* NCountSize >= 2 && bitstreamSize > 0 ==> lastCountSize == 3 */ assert(fseMetadata->lastCountSize + bitstreamSize == 3); DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.3.4 by " "emitting an uncompressed block."); return 0; } #endif DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (bitstreamSize=%zu)", bitstreamSize); } /* zstd versions <= 1.4.0 mistakenly report error when * sequences section body size is less than 3 bytes. * Fixed by https://github.com/facebook/zstd/pull/1664. * This can happen when the previous sequences section block is compressed * with rle mode and the current block's sequences section is compressed * with repeat mode where sequences section body size can be 1 byte. */ #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if (op-seqHead < 4) { DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.4.0 by emitting " "an uncompressed block when sequences are < 4 bytes"); return 0; } #endif *entropyWritten = 1; return op - ostart; } /** ZSTD_compressSubBlock() : * Compresses a single sub-block. * @return : compressed size of the sub-block * Or 0 if it failed to compress. */ static size_t ZSTD_compressSubBlock(const ZSTD_entropyCTables_t* entropy, const ZSTD_entropyCTablesMetadata_t* entropyMetadata, const seqDef* sequences, size_t nbSeq, const BYTE* literals, size_t litSize, const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode, const ZSTD_CCtx_params* cctxParams, void* dst, size_t dstCapacity, const int bmi2, int writeLitEntropy, int writeSeqEntropy, int* litEntropyWritten, int* seqEntropyWritten, U32 lastBlock) { BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstCapacity; BYTE* op = ostart + ZSTD_blockHeaderSize; DEBUGLOG(5, "ZSTD_compressSubBlock (litSize=%zu, nbSeq=%zu, writeLitEntropy=%d, writeSeqEntropy=%d, lastBlock=%d)", litSize, nbSeq, writeLitEntropy, writeSeqEntropy, lastBlock); { size_t cLitSize = ZSTD_compressSubBlock_literal((const HUF_CElt*)entropy->huf.CTable, &entropyMetadata->hufMetadata, literals, litSize, op, oend-op, bmi2, writeLitEntropy, litEntropyWritten); FORWARD_IF_ERROR(cLitSize, "ZSTD_compressSubBlock_literal failed"); if (cLitSize == 0) return 0; op += cLitSize; } { size_t cSeqSize = ZSTD_compressSubBlock_sequences(&entropy->fse, &entropyMetadata->fseMetadata, sequences, nbSeq, llCode, mlCode, ofCode, cctxParams, op, oend-op, bmi2, writeSeqEntropy, seqEntropyWritten); FORWARD_IF_ERROR(cSeqSize, "ZSTD_compressSubBlock_sequences failed"); if (cSeqSize == 0) return 0; op += cSeqSize; } /* Write block header */ { size_t cSize = (op-ostart)-ZSTD_blockHeaderSize; U32 const cBlockHeader24 = lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3); MEM_writeLE24(ostart, cBlockHeader24); } return op-ostart; } static size_t ZSTD_estimateSubBlockSize_literal(const BYTE* literals, size_t litSize, const ZSTD_hufCTables_t* huf, const ZSTD_hufCTablesMetadata_t* hufMetadata, void* workspace, size_t wkspSize, int writeEntropy) { unsigned* const countWksp = (unsigned*)workspace; unsigned maxSymbolValue = 255; size_t literalSectionHeaderSize = 3; /* Use hard coded size of 3 bytes */ if (hufMetadata->hType == set_basic) return litSize; else if (hufMetadata->hType == set_rle) return 1; else if (hufMetadata->hType == set_compressed || hufMetadata->hType == set_repeat) { size_t const largest = HIST_count_wksp (countWksp, &maxSymbolValue, (const BYTE*)literals, litSize, workspace, wkspSize); if (ZSTD_isError(largest)) return litSize; { size_t cLitSizeEstimate = HUF_estimateCompressedSize((const HUF_CElt*)huf->CTable, countWksp, maxSymbolValue); if (writeEntropy) cLitSizeEstimate += hufMetadata->hufDesSize; return cLitSizeEstimate + literalSectionHeaderSize; } } assert(0); /* impossible */ return 0; } static size_t ZSTD_estimateSubBlockSize_symbolType(symbolEncodingType_e type, const BYTE* codeTable, unsigned maxCode, size_t nbSeq, const FSE_CTable* fseCTable, const U32* additionalBits, short const* defaultNorm, U32 defaultNormLog, void* workspace, size_t wkspSize) { unsigned* const countWksp = (unsigned*)workspace; const BYTE* ctp = codeTable; const BYTE* const ctStart = ctp; const BYTE* const ctEnd = ctStart + nbSeq; size_t cSymbolTypeSizeEstimateInBits = 0; unsigned max = maxCode; HIST_countFast_wksp(countWksp, &max, codeTable, nbSeq, workspace, wkspSize); /* can't fail */ if (type == set_basic) { cSymbolTypeSizeEstimateInBits = ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, countWksp, max); } else if (type == set_rle) { cSymbolTypeSizeEstimateInBits = 0; } else if (type == set_compressed || type == set_repeat) { cSymbolTypeSizeEstimateInBits = ZSTD_fseBitCost(fseCTable, countWksp, max); } if (ZSTD_isError(cSymbolTypeSizeEstimateInBits)) return nbSeq * 10; while (ctp < ctEnd) { if (additionalBits) cSymbolTypeSizeEstimateInBits += additionalBits[*ctp]; else cSymbolTypeSizeEstimateInBits += *ctp; /* for offset, offset code is also the number of additional bits */ ctp++; } return cSymbolTypeSizeEstimateInBits / 8; } static size_t ZSTD_estimateSubBlockSize_sequences(const BYTE* ofCodeTable, const BYTE* llCodeTable, const BYTE* mlCodeTable, size_t nbSeq, const ZSTD_fseCTables_t* fseTables, const ZSTD_fseCTablesMetadata_t* fseMetadata, void* workspace, size_t wkspSize, int writeEntropy) { size_t sequencesSectionHeaderSize = 3; /* Use hard coded size of 3 bytes */ size_t cSeqSizeEstimate = 0; cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(fseMetadata->ofType, ofCodeTable, MaxOff, nbSeq, fseTables->offcodeCTable, NULL, OF_defaultNorm, OF_defaultNormLog, workspace, wkspSize); cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(fseMetadata->llType, llCodeTable, MaxLL, nbSeq, fseTables->litlengthCTable, LL_bits, LL_defaultNorm, LL_defaultNormLog, workspace, wkspSize); cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(fseMetadata->mlType, mlCodeTable, MaxML, nbSeq, fseTables->matchlengthCTable, ML_bits, ML_defaultNorm, ML_defaultNormLog, workspace, wkspSize); if (writeEntropy) cSeqSizeEstimate += fseMetadata->fseTablesSize; return cSeqSizeEstimate + sequencesSectionHeaderSize; } static size_t ZSTD_estimateSubBlockSize(const BYTE* literals, size_t litSize, const BYTE* ofCodeTable, const BYTE* llCodeTable, const BYTE* mlCodeTable, size_t nbSeq, const ZSTD_entropyCTables_t* entropy, const ZSTD_entropyCTablesMetadata_t* entropyMetadata, void* workspace, size_t wkspSize, int writeLitEntropy, int writeSeqEntropy) { size_t cSizeEstimate = 0; cSizeEstimate += ZSTD_estimateSubBlockSize_literal(literals, litSize, &entropy->huf, &entropyMetadata->hufMetadata, workspace, wkspSize, writeLitEntropy); cSizeEstimate += ZSTD_estimateSubBlockSize_sequences(ofCodeTable, llCodeTable, mlCodeTable, nbSeq, &entropy->fse, &entropyMetadata->fseMetadata, workspace, wkspSize, writeSeqEntropy); return cSizeEstimate + ZSTD_blockHeaderSize; } static int ZSTD_needSequenceEntropyTables(ZSTD_fseCTablesMetadata_t const* fseMetadata) { if (fseMetadata->llType == set_compressed || fseMetadata->llType == set_rle) return 1; if (fseMetadata->mlType == set_compressed || fseMetadata->mlType == set_rle) return 1; if (fseMetadata->ofType == set_compressed || fseMetadata->ofType == set_rle) return 1; return 0; } /** ZSTD_compressSubBlock_multi() : * Breaks super-block into multiple sub-blocks and compresses them. * Entropy will be written to the first block. * The following blocks will use repeat mode to compress. * All sub-blocks are compressed blocks (no raw or rle blocks). * @return : compressed size of the super block (which is multiple ZSTD blocks) * Or 0 if it failed to compress. */ static size_t ZSTD_compressSubBlock_multi(const seqStore_t* seqStorePtr, const ZSTD_compressedBlockState_t* prevCBlock, ZSTD_compressedBlockState_t* nextCBlock, const ZSTD_entropyCTablesMetadata_t* entropyMetadata, const ZSTD_CCtx_params* cctxParams, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const int bmi2, U32 lastBlock, void* workspace, size_t wkspSize) { const seqDef* const sstart = seqStorePtr->sequencesStart; const seqDef* const send = seqStorePtr->sequences; const seqDef* sp = sstart; const BYTE* const lstart = seqStorePtr->litStart; const BYTE* const lend = seqStorePtr->lit; const BYTE* lp = lstart; BYTE const* ip = (BYTE const*)src; BYTE const* const iend = ip + srcSize; BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstCapacity; BYTE* op = ostart; const BYTE* llCodePtr = seqStorePtr->llCode; const BYTE* mlCodePtr = seqStorePtr->mlCode; const BYTE* ofCodePtr = seqStorePtr->ofCode; size_t targetCBlockSize = cctxParams->targetCBlockSize; size_t litSize, seqCount; int writeLitEntropy = entropyMetadata->hufMetadata.hType == set_compressed; int writeSeqEntropy = 1; int lastSequence = 0; DEBUGLOG(5, "ZSTD_compressSubBlock_multi (litSize=%u, nbSeq=%u)", (unsigned)(lend-lp), (unsigned)(send-sstart)); litSize = 0; seqCount = 0; do { size_t cBlockSizeEstimate = 0; if (sstart == send) { lastSequence = 1; } else { const seqDef* const sequence = sp + seqCount; lastSequence = sequence == send - 1; litSize += ZSTD_getSequenceLength(seqStorePtr, sequence).litLength; seqCount++; } if (lastSequence) { assert(lp <= lend); assert(litSize <= (size_t)(lend - lp)); litSize = (size_t)(lend - lp); } /* I think there is an optimization opportunity here. * Calling ZSTD_estimateSubBlockSize for every sequence can be wasteful * since it recalculates estimate from scratch. * For example, it would recount literal distribution and symbol codes everytime. */ cBlockSizeEstimate = ZSTD_estimateSubBlockSize(lp, litSize, ofCodePtr, llCodePtr, mlCodePtr, seqCount, &nextCBlock->entropy, entropyMetadata, workspace, wkspSize, writeLitEntropy, writeSeqEntropy); if (cBlockSizeEstimate > targetCBlockSize || lastSequence) { int litEntropyWritten = 0; int seqEntropyWritten = 0; const size_t decompressedSize = ZSTD_seqDecompressedSize(seqStorePtr, sp, seqCount, litSize, lastSequence); const size_t cSize = ZSTD_compressSubBlock(&nextCBlock->entropy, entropyMetadata, sp, seqCount, lp, litSize, llCodePtr, mlCodePtr, ofCodePtr, cctxParams, op, oend-op, bmi2, writeLitEntropy, writeSeqEntropy, &litEntropyWritten, &seqEntropyWritten, lastBlock && lastSequence); FORWARD_IF_ERROR(cSize, "ZSTD_compressSubBlock failed"); if (cSize > 0 && cSize < decompressedSize) { DEBUGLOG(5, "Committed the sub-block"); assert(ip + decompressedSize <= iend); ip += decompressedSize; sp += seqCount; lp += litSize; op += cSize; llCodePtr += seqCount; mlCodePtr += seqCount; ofCodePtr += seqCount; litSize = 0; seqCount = 0; /* Entropy only needs to be written once */ if (litEntropyWritten) { writeLitEntropy = 0; } if (seqEntropyWritten) { writeSeqEntropy = 0; } } } } while (!lastSequence); if (writeLitEntropy) { DEBUGLOG(5, "ZSTD_compressSubBlock_multi has literal entropy tables unwritten"); memcpy(&nextCBlock->entropy.huf, &prevCBlock->entropy.huf, sizeof(prevCBlock->entropy.huf)); } if (writeSeqEntropy && ZSTD_needSequenceEntropyTables(&entropyMetadata->fseMetadata)) { /* If we haven't written our entropy tables, then we've violated our contract and * must emit an uncompressed block. */ DEBUGLOG(5, "ZSTD_compressSubBlock_multi has sequence entropy tables unwritten"); return 0; } if (ip < iend) { size_t const cSize = ZSTD_noCompressBlock(op, oend - op, ip, iend - ip, lastBlock); DEBUGLOG(5, "ZSTD_compressSubBlock_multi last sub-block uncompressed, %zu bytes", (size_t)(iend - ip)); FORWARD_IF_ERROR(cSize, "ZSTD_noCompressBlock failed"); assert(cSize != 0); op += cSize; /* We have to regenerate the repcodes because we've skipped some sequences */ if (sp < send) { seqDef const* seq; repcodes_t rep; memcpy(&rep, prevCBlock->rep, sizeof(rep)); for (seq = sstart; seq < sp; ++seq) { rep = ZSTD_updateRep(rep.rep, seq->offset - 1, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0); } memcpy(nextCBlock->rep, &rep, sizeof(rep)); } } DEBUGLOG(5, "ZSTD_compressSubBlock_multi compressed"); return op-ostart; } size_t ZSTD_compressSuperBlock(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, void const* src, size_t srcSize, unsigned lastBlock) { ZSTD_entropyCTablesMetadata_t entropyMetadata; FORWARD_IF_ERROR(ZSTD_buildSuperBlockEntropy(&zc->seqStore, &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy, &zc->appliedParams, &entropyMetadata, zc->entropyWorkspace, HUF_WORKSPACE_SIZE /* statically allocated in resetCCtx */), ""); return ZSTD_compressSubBlock_multi(&zc->seqStore, zc->blockState.prevCBlock, zc->blockState.nextCBlock, &entropyMetadata, &zc->appliedParams, dst, dstCapacity, src, srcSize, zc->bmi2, lastBlock, zc->entropyWorkspace, HUF_WORKSPACE_SIZE /* statically allocated in resetCCtx */); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_compress_superblock.h0000644000076500000240000000222214601576577027015 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_COMPRESS_ADVANCED_H #define ZSTD_COMPRESS_ADVANCED_H /*-************************************* * Dependencies ***************************************/ #include "../zstd.h" /* ZSTD_CCtx */ /*-************************************* * Target Compressed Block Size ***************************************/ /* ZSTD_compressSuperBlock() : * Used to compress a super block when targetCBlockSize is being used. * The given block will be compressed into multiple sub blocks that are around targetCBlockSize. */ size_t ZSTD_compressSuperBlock(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, void const* src, size_t srcSize, unsigned lastBlock); #endif /* ZSTD_COMPRESS_ADVANCED_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_cwksp.h0000644000076500000240000004450114601576577024066 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_CWKSP_H #define ZSTD_CWKSP_H /*-************************************* * Dependencies ***************************************/ #include "../common/zstd_internal.h" #if defined (__cplusplus) extern "C" { #endif /*-************************************* * Constants ***************************************/ /* Since the workspace is effectively its own little malloc implementation / * arena, when we run under ASAN, we should similarly insert redzones between * each internal element of the workspace, so ASAN will catch overruns that * reach outside an object but that stay inside the workspace. * * This defines the size of that redzone. */ #ifndef ZSTD_CWKSP_ASAN_REDZONE_SIZE #define ZSTD_CWKSP_ASAN_REDZONE_SIZE 128 #endif /*-************************************* * Structures ***************************************/ typedef enum { ZSTD_cwksp_alloc_objects, ZSTD_cwksp_alloc_buffers, ZSTD_cwksp_alloc_aligned } ZSTD_cwksp_alloc_phase_e; /** * Zstd fits all its internal datastructures into a single continuous buffer, * so that it only needs to perform a single OS allocation (or so that a buffer * can be provided to it and it can perform no allocations at all). This buffer * is called the workspace. * * Several optimizations complicate that process of allocating memory ranges * from this workspace for each internal datastructure: * * - These different internal datastructures have different setup requirements: * * - The static objects need to be cleared once and can then be trivially * reused for each compression. * * - Various buffers don't need to be initialized at all--they are always * written into before they're read. * * - The matchstate tables have a unique requirement that they don't need * their memory to be totally cleared, but they do need the memory to have * some bound, i.e., a guarantee that all values in the memory they've been * allocated is less than some maximum value (which is the starting value * for the indices that they will then use for compression). When this * guarantee is provided to them, they can use the memory without any setup * work. When it can't, they have to clear the area. * * - These buffers also have different alignment requirements. * * - We would like to reuse the objects in the workspace for multiple * compressions without having to perform any expensive reallocation or * reinitialization work. * * - We would like to be able to efficiently reuse the workspace across * multiple compressions **even when the compression parameters change** and * we need to resize some of the objects (where possible). * * To attempt to manage this buffer, given these constraints, the ZSTD_cwksp * abstraction was created. It works as follows: * * Workspace Layout: * * [ ... workspace ... ] * [objects][tables ... ->] free space [<- ... aligned][<- ... buffers] * * The various objects that live in the workspace are divided into the * following categories, and are allocated separately: * * - Static objects: this is optionally the enclosing ZSTD_CCtx or ZSTD_CDict, * so that literally everything fits in a single buffer. Note: if present, * this must be the first object in the workspace, since ZSTD_free{CCtx, * CDict}() rely on a pointer comparison to see whether one or two frees are * required. * * - Fixed size objects: these are fixed-size, fixed-count objects that are * nonetheless "dynamically" allocated in the workspace so that we can * control how they're initialized separately from the broader ZSTD_CCtx. * Examples: * - Entropy Workspace * - 2 x ZSTD_compressedBlockState_t * - CDict dictionary contents * * - Tables: these are any of several different datastructures (hash tables, * chain tables, binary trees) that all respect a common format: they are * uint32_t arrays, all of whose values are between 0 and (nextSrc - base). * Their sizes depend on the cparams. * * - Aligned: these buffers are used for various purposes that require 4 byte * alignment, but don't require any initialization before they're used. * * - Buffers: these buffers are used for various purposes that don't require * any alignment or initialization before they're used. This means they can * be moved around at no cost for a new compression. * * Allocating Memory: * * The various types of objects must be allocated in order, so they can be * correctly packed into the workspace buffer. That order is: * * 1. Objects * 2. Buffers * 3. Aligned * 4. Tables * * Attempts to reserve objects of different types out of order will fail. */ typedef struct { void* workspace; void* workspaceEnd; void* objectEnd; void* tableEnd; void* tableValidEnd; void* allocStart; int allocFailed; int workspaceOversizedDuration; ZSTD_cwksp_alloc_phase_e phase; } ZSTD_cwksp; /*-************************************* * Functions ***************************************/ MEM_STATIC size_t ZSTD_cwksp_available_space(ZSTD_cwksp* ws); MEM_STATIC void ZSTD_cwksp_assert_internal_consistency(ZSTD_cwksp* ws) { (void)ws; assert(ws->workspace <= ws->objectEnd); assert(ws->objectEnd <= ws->tableEnd); assert(ws->objectEnd <= ws->tableValidEnd); assert(ws->tableEnd <= ws->allocStart); assert(ws->tableValidEnd <= ws->allocStart); assert(ws->allocStart <= ws->workspaceEnd); } /** * Align must be a power of 2. */ MEM_STATIC size_t ZSTD_cwksp_align(size_t size, size_t const align) { size_t const mask = align - 1; assert((align & mask) == 0); return (size + mask) & ~mask; } /** * Use this to determine how much space in the workspace we will consume to * allocate this object. (Normally it should be exactly the size of the object, * but under special conditions, like ASAN, where we pad each object, it might * be larger.) * * Since tables aren't currently redzoned, you don't need to call through this * to figure out how much space you need for the matchState tables. Everything * else is though. */ MEM_STATIC size_t ZSTD_cwksp_alloc_size(size_t size) { #if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE) return size + 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE; #else return size; #endif } MEM_STATIC void ZSTD_cwksp_internal_advance_phase( ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase) { assert(phase >= ws->phase); if (phase > ws->phase) { if (ws->phase < ZSTD_cwksp_alloc_buffers && phase >= ZSTD_cwksp_alloc_buffers) { ws->tableValidEnd = ws->objectEnd; } if (ws->phase < ZSTD_cwksp_alloc_aligned && phase >= ZSTD_cwksp_alloc_aligned) { /* If unaligned allocations down from a too-large top have left us * unaligned, we need to realign our alloc ptr. Technically, this * can consume space that is unaccounted for in the neededSpace * calculation. However, I believe this can only happen when the * workspace is too large, and specifically when it is too large * by a larger margin than the space that will be consumed. */ /* TODO: cleaner, compiler warning friendly way to do this??? */ ws->allocStart = (BYTE*)ws->allocStart - ((size_t)ws->allocStart & (sizeof(U32)-1)); if (ws->allocStart < ws->tableValidEnd) { ws->tableValidEnd = ws->allocStart; } } ws->phase = phase; } } /** * Returns whether this object/buffer/etc was allocated in this workspace. */ MEM_STATIC int ZSTD_cwksp_owns_buffer(const ZSTD_cwksp* ws, const void* ptr) { return (ptr != NULL) && (ws->workspace <= ptr) && (ptr <= ws->workspaceEnd); } /** * Internal function. Do not use directly. */ MEM_STATIC void* ZSTD_cwksp_reserve_internal( ZSTD_cwksp* ws, size_t bytes, ZSTD_cwksp_alloc_phase_e phase) { void* alloc; void* bottom = ws->tableEnd; ZSTD_cwksp_internal_advance_phase(ws, phase); alloc = (BYTE *)ws->allocStart - bytes; #if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE) /* over-reserve space */ alloc = (BYTE *)alloc - 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE; #endif DEBUGLOG(5, "cwksp: reserving %p %zd bytes, %zd bytes remaining", alloc, bytes, ZSTD_cwksp_available_space(ws) - bytes); ZSTD_cwksp_assert_internal_consistency(ws); assert(alloc >= bottom); if (alloc < bottom) { DEBUGLOG(4, "cwksp: alloc failed!"); ws->allocFailed = 1; return NULL; } if (alloc < ws->tableValidEnd) { ws->tableValidEnd = alloc; } ws->allocStart = alloc; #if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE) /* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on * either size. */ alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE; __asan_unpoison_memory_region(alloc, bytes); #endif return alloc; } /** * Reserves and returns unaligned memory. */ MEM_STATIC BYTE* ZSTD_cwksp_reserve_buffer(ZSTD_cwksp* ws, size_t bytes) { return (BYTE*)ZSTD_cwksp_reserve_internal(ws, bytes, ZSTD_cwksp_alloc_buffers); } /** * Reserves and returns memory sized on and aligned on sizeof(unsigned). */ MEM_STATIC void* ZSTD_cwksp_reserve_aligned(ZSTD_cwksp* ws, size_t bytes) { assert((bytes & (sizeof(U32)-1)) == 0); return ZSTD_cwksp_reserve_internal(ws, ZSTD_cwksp_align(bytes, sizeof(U32)), ZSTD_cwksp_alloc_aligned); } /** * Aligned on sizeof(unsigned). These buffers have the special property that * their values remain constrained, allowing us to re-use them without * memset()-ing them. */ MEM_STATIC void* ZSTD_cwksp_reserve_table(ZSTD_cwksp* ws, size_t bytes) { const ZSTD_cwksp_alloc_phase_e phase = ZSTD_cwksp_alloc_aligned; void* alloc = ws->tableEnd; void* end = (BYTE *)alloc + bytes; void* top = ws->allocStart; DEBUGLOG(5, "cwksp: reserving %p table %zd bytes, %zd bytes remaining", alloc, bytes, ZSTD_cwksp_available_space(ws) - bytes); assert((bytes & (sizeof(U32)-1)) == 0); ZSTD_cwksp_internal_advance_phase(ws, phase); ZSTD_cwksp_assert_internal_consistency(ws); assert(end <= top); if (end > top) { DEBUGLOG(4, "cwksp: table alloc failed!"); ws->allocFailed = 1; return NULL; } ws->tableEnd = end; #if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE) __asan_unpoison_memory_region(alloc, bytes); #endif return alloc; } /** * Aligned on sizeof(void*). */ MEM_STATIC void* ZSTD_cwksp_reserve_object(ZSTD_cwksp* ws, size_t bytes) { size_t roundedBytes = ZSTD_cwksp_align(bytes, sizeof(void*)); void* alloc = ws->objectEnd; void* end = (BYTE*)alloc + roundedBytes; #if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE) /* over-reserve space */ end = (BYTE *)end + 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE; #endif DEBUGLOG(5, "cwksp: reserving %p object %zd bytes (rounded to %zd), %zd bytes remaining", alloc, bytes, roundedBytes, ZSTD_cwksp_available_space(ws) - roundedBytes); assert(((size_t)alloc & (sizeof(void*)-1)) == 0); assert((bytes & (sizeof(void*)-1)) == 0); ZSTD_cwksp_assert_internal_consistency(ws); /* we must be in the first phase, no advance is possible */ if (ws->phase != ZSTD_cwksp_alloc_objects || end > ws->workspaceEnd) { DEBUGLOG(4, "cwksp: object alloc failed!"); ws->allocFailed = 1; return NULL; } ws->objectEnd = end; ws->tableEnd = end; ws->tableValidEnd = end; #if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE) /* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on * either size. */ alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE; __asan_unpoison_memory_region(alloc, bytes); #endif return alloc; } MEM_STATIC void ZSTD_cwksp_mark_tables_dirty(ZSTD_cwksp* ws) { DEBUGLOG(4, "cwksp: ZSTD_cwksp_mark_tables_dirty"); #if defined (MEMORY_SANITIZER) && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE) /* To validate that the table re-use logic is sound, and that we don't * access table space that we haven't cleaned, we re-"poison" the table * space every time we mark it dirty. */ { size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd; assert(__msan_test_shadow(ws->objectEnd, size) == -1); __msan_poison(ws->objectEnd, size); } #endif assert(ws->tableValidEnd >= ws->objectEnd); assert(ws->tableValidEnd <= ws->allocStart); ws->tableValidEnd = ws->objectEnd; ZSTD_cwksp_assert_internal_consistency(ws); } MEM_STATIC void ZSTD_cwksp_mark_tables_clean(ZSTD_cwksp* ws) { DEBUGLOG(4, "cwksp: ZSTD_cwksp_mark_tables_clean"); assert(ws->tableValidEnd >= ws->objectEnd); assert(ws->tableValidEnd <= ws->allocStart); if (ws->tableValidEnd < ws->tableEnd) { ws->tableValidEnd = ws->tableEnd; } ZSTD_cwksp_assert_internal_consistency(ws); } /** * Zero the part of the allocated tables not already marked clean. */ MEM_STATIC void ZSTD_cwksp_clean_tables(ZSTD_cwksp* ws) { DEBUGLOG(4, "cwksp: ZSTD_cwksp_clean_tables"); assert(ws->tableValidEnd >= ws->objectEnd); assert(ws->tableValidEnd <= ws->allocStart); if (ws->tableValidEnd < ws->tableEnd) { memset(ws->tableValidEnd, 0, (BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd); } ZSTD_cwksp_mark_tables_clean(ws); } /** * Invalidates table allocations. * All other allocations remain valid. */ MEM_STATIC void ZSTD_cwksp_clear_tables(ZSTD_cwksp* ws) { DEBUGLOG(4, "cwksp: clearing tables!"); #if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE) { size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd; __asan_poison_memory_region(ws->objectEnd, size); } #endif ws->tableEnd = ws->objectEnd; ZSTD_cwksp_assert_internal_consistency(ws); } /** * Invalidates all buffer, aligned, and table allocations. * Object allocations remain valid. */ MEM_STATIC void ZSTD_cwksp_clear(ZSTD_cwksp* ws) { DEBUGLOG(4, "cwksp: clearing!"); #if defined (MEMORY_SANITIZER) && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE) /* To validate that the context re-use logic is sound, and that we don't * access stuff that this compression hasn't initialized, we re-"poison" * the workspace (or at least the non-static, non-table parts of it) * every time we start a new compression. */ { size_t size = (BYTE*)ws->workspaceEnd - (BYTE*)ws->tableValidEnd; __msan_poison(ws->tableValidEnd, size); } #endif #if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE) { size_t size = (BYTE*)ws->workspaceEnd - (BYTE*)ws->objectEnd; __asan_poison_memory_region(ws->objectEnd, size); } #endif ws->tableEnd = ws->objectEnd; ws->allocStart = ws->workspaceEnd; ws->allocFailed = 0; if (ws->phase > ZSTD_cwksp_alloc_buffers) { ws->phase = ZSTD_cwksp_alloc_buffers; } ZSTD_cwksp_assert_internal_consistency(ws); } /** * The provided workspace takes ownership of the buffer [start, start+size). * Any existing values in the workspace are ignored (the previously managed * buffer, if present, must be separately freed). */ MEM_STATIC void ZSTD_cwksp_init(ZSTD_cwksp* ws, void* start, size_t size) { DEBUGLOG(4, "cwksp: init'ing workspace with %zd bytes", size); assert(((size_t)start & (sizeof(void*)-1)) == 0); /* ensure correct alignment */ ws->workspace = start; ws->workspaceEnd = (BYTE*)start + size; ws->objectEnd = ws->workspace; ws->tableValidEnd = ws->objectEnd; ws->phase = ZSTD_cwksp_alloc_objects; ZSTD_cwksp_clear(ws); ws->workspaceOversizedDuration = 0; ZSTD_cwksp_assert_internal_consistency(ws); } MEM_STATIC size_t ZSTD_cwksp_create(ZSTD_cwksp* ws, size_t size, ZSTD_customMem customMem) { void* workspace = ZSTD_malloc(size, customMem); DEBUGLOG(4, "cwksp: creating new workspace with %zd bytes", size); RETURN_ERROR_IF(workspace == NULL, memory_allocation, "NULL pointer!"); ZSTD_cwksp_init(ws, workspace, size); return 0; } MEM_STATIC void ZSTD_cwksp_free(ZSTD_cwksp* ws, ZSTD_customMem customMem) { void *ptr = ws->workspace; DEBUGLOG(4, "cwksp: freeing workspace"); memset(ws, 0, sizeof(ZSTD_cwksp)); ZSTD_free(ptr, customMem); } /** * Moves the management of a workspace from one cwksp to another. The src cwksp * is left in an invalid state (src must be re-init()'ed before its used again). */ MEM_STATIC void ZSTD_cwksp_move(ZSTD_cwksp* dst, ZSTD_cwksp* src) { *dst = *src; memset(src, 0, sizeof(ZSTD_cwksp)); } MEM_STATIC size_t ZSTD_cwksp_sizeof(const ZSTD_cwksp* ws) { return (size_t)((BYTE*)ws->workspaceEnd - (BYTE*)ws->workspace); } MEM_STATIC int ZSTD_cwksp_reserve_failed(const ZSTD_cwksp* ws) { return ws->allocFailed; } /*-************************************* * Functions Checking Free Space ***************************************/ MEM_STATIC size_t ZSTD_cwksp_available_space(ZSTD_cwksp* ws) { return (size_t)((BYTE*)ws->allocStart - (BYTE*)ws->tableEnd); } MEM_STATIC int ZSTD_cwksp_check_available(ZSTD_cwksp* ws, size_t additionalNeededSpace) { return ZSTD_cwksp_available_space(ws) >= additionalNeededSpace; } MEM_STATIC int ZSTD_cwksp_check_too_large(ZSTD_cwksp* ws, size_t additionalNeededSpace) { return ZSTD_cwksp_check_available( ws, additionalNeededSpace * ZSTD_WORKSPACETOOLARGE_FACTOR); } MEM_STATIC int ZSTD_cwksp_check_wasteful(ZSTD_cwksp* ws, size_t additionalNeededSpace) { return ZSTD_cwksp_check_too_large(ws, additionalNeededSpace) && ws->workspaceOversizedDuration > ZSTD_WORKSPACETOOLARGE_MAXDURATION; } MEM_STATIC void ZSTD_cwksp_bump_oversized_duration( ZSTD_cwksp* ws, size_t additionalNeededSpace) { if (ZSTD_cwksp_check_too_large(ws, additionalNeededSpace)) { ws->workspaceOversizedDuration++; } else { ws->workspaceOversizedDuration = 0; } } #if defined (__cplusplus) } #endif #endif /* ZSTD_CWKSP_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_double_fast.c0000644000076500000240000006143514601576577025226 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #include "zstd_compress_internal.h" #include "zstd_double_fast.h" void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, void const* end, ZSTD_dictTableLoadMethod_e dtlm) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashLarge = ms->hashTable; U32 const hBitsL = cParams->hashLog; U32 const mls = cParams->minMatch; U32* const hashSmall = ms->chainTable; U32 const hBitsS = cParams->chainLog; const BYTE* const base = ms->window.base; const BYTE* ip = base + ms->nextToUpdate; const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; const U32 fastHashFillStep = 3; /* Always insert every fastHashFillStep position into the hash tables. * Insert the other positions into the large hash table if their entry * is empty. */ for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) { U32 const current = (U32)(ip - base); U32 i; for (i = 0; i < fastHashFillStep; ++i) { size_t const smHash = ZSTD_hashPtr(ip + i, hBitsS, mls); size_t const lgHash = ZSTD_hashPtr(ip + i, hBitsL, 8); if (i == 0) hashSmall[smHash] = current + i; if (i == 0 || hashLarge[lgHash] == 0) hashLarge[lgHash] = current + i; /* Only load extra positions for ZSTD_dtlm_full */ if (dtlm == ZSTD_dtlm_fast) break; } } } FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_doubleFast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, U32 const mls /* template */, ZSTD_dictMode_e const dictMode) { ZSTD_compressionParameters const* cParams = &ms->cParams; U32* const hashLong = ms->hashTable; const U32 hBitsL = cParams->hashLog; U32* const hashSmall = ms->chainTable; const U32 hBitsS = cParams->chainLog; const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; const U32 endIndex = (U32)((size_t)(istart - base) + srcSize); /* presumes that, if there is a dictionary, it must be using Attach mode */ const U32 prefixLowestIndex = ZSTD_getLowestPrefixIndex(ms, endIndex, cParams->windowLog); const BYTE* const prefixLowest = base + prefixLowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; const ZSTD_matchState_t* const dms = ms->dictMatchState; const ZSTD_compressionParameters* const dictCParams = dictMode == ZSTD_dictMatchState ? &dms->cParams : NULL; const U32* const dictHashLong = dictMode == ZSTD_dictMatchState ? dms->hashTable : NULL; const U32* const dictHashSmall = dictMode == ZSTD_dictMatchState ? dms->chainTable : NULL; const U32 dictStartIndex = dictMode == ZSTD_dictMatchState ? dms->window.dictLimit : 0; const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL; const BYTE* const dictStart = dictMode == ZSTD_dictMatchState ? dictBase + dictStartIndex : NULL; const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? prefixLowestIndex - (U32)(dictEnd - dictBase) : 0; const U32 dictHBitsL = dictMode == ZSTD_dictMatchState ? dictCParams->hashLog : hBitsL; const U32 dictHBitsS = dictMode == ZSTD_dictMatchState ? dictCParams->chainLog : hBitsS; const U32 dictAndPrefixLength = (U32)((ip - prefixLowest) + (dictEnd - dictStart)); DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_generic"); assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); /* if a dictionary is attached, it must be within window range */ if (dictMode == ZSTD_dictMatchState) { assert(ms->window.dictLimit + (1U << cParams->windowLog) >= endIndex); } /* init */ ip += (dictAndPrefixLength == 0); if (dictMode == ZSTD_noDict) { U32 const current = (U32)(ip - base); U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, current, cParams->windowLog); U32 const maxRep = current - windowLow; if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } if (dictMode == ZSTD_dictMatchState) { /* dictMatchState repCode checks don't currently handle repCode == 0 * disabling. */ assert(offset_1 <= dictAndPrefixLength); assert(offset_2 <= dictAndPrefixLength); } /* Main Search Loop */ while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ size_t mLength; U32 offset; size_t const h2 = ZSTD_hashPtr(ip, hBitsL, 8); size_t const h = ZSTD_hashPtr(ip, hBitsS, mls); size_t const dictHL = ZSTD_hashPtr(ip, dictHBitsL, 8); size_t const dictHS = ZSTD_hashPtr(ip, dictHBitsS, mls); U32 const current = (U32)(ip-base); U32 const matchIndexL = hashLong[h2]; U32 matchIndexS = hashSmall[h]; const BYTE* matchLong = base + matchIndexL; const BYTE* match = base + matchIndexS; const U32 repIndex = current + 1 - offset_1; const BYTE* repMatch = (dictMode == ZSTD_dictMatchState && repIndex < prefixLowestIndex) ? dictBase + (repIndex - dictIndexDelta) : base + repIndex; hashLong[h2] = hashSmall[h] = current; /* update hash tables */ /* check dictMatchState repcode */ if (dictMode == ZSTD_dictMatchState && ((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; ip++; ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH); goto _match_stored; } /* check noDict repcode */ if ( dictMode == ZSTD_noDict && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH); goto _match_stored; } if (matchIndexL > prefixLowestIndex) { /* check prefix long match */ if (MEM_read64(matchLong) == MEM_read64(ip)) { mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8; offset = (U32)(ip-matchLong); while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ goto _match_found; } } else if (dictMode == ZSTD_dictMatchState) { /* check dictMatchState long match */ U32 const dictMatchIndexL = dictHashLong[dictHL]; const BYTE* dictMatchL = dictBase + dictMatchIndexL; assert(dictMatchL < dictEnd); if (dictMatchL > dictStart && MEM_read64(dictMatchL) == MEM_read64(ip)) { mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; offset = (U32)(current - dictMatchIndexL - dictIndexDelta); while (((ip>anchor) & (dictMatchL>dictStart)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ goto _match_found; } } if (matchIndexS > prefixLowestIndex) { /* check prefix short match */ if (MEM_read32(match) == MEM_read32(ip)) { goto _search_next_long; } } else if (dictMode == ZSTD_dictMatchState) { /* check dictMatchState short match */ U32 const dictMatchIndexS = dictHashSmall[dictHS]; match = dictBase + dictMatchIndexS; matchIndexS = dictMatchIndexS + dictIndexDelta; if (match > dictStart && MEM_read32(match) == MEM_read32(ip)) { goto _search_next_long; } } ip += ((ip-anchor) >> kSearchStrength) + 1; #if defined(__aarch64__) PREFETCH_L1(ip+256); #endif continue; _search_next_long: { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); size_t const dictHLNext = ZSTD_hashPtr(ip+1, dictHBitsL, 8); U32 const matchIndexL3 = hashLong[hl3]; const BYTE* matchL3 = base + matchIndexL3; hashLong[hl3] = current + 1; /* check prefix long +1 match */ if (matchIndexL3 > prefixLowestIndex) { if (MEM_read64(matchL3) == MEM_read64(ip+1)) { mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; ip++; offset = (U32)(ip-matchL3); while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ goto _match_found; } } else if (dictMode == ZSTD_dictMatchState) { /* check dict long +1 match */ U32 const dictMatchIndexL3 = dictHashLong[dictHLNext]; const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; assert(dictMatchL3 < dictEnd); if (dictMatchL3 > dictStart && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) { mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; ip++; offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); while (((ip>anchor) & (dictMatchL3>dictStart)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ goto _match_found; } } } /* if no long +1 match, explore the short match we found */ if (dictMode == ZSTD_dictMatchState && matchIndexS < prefixLowestIndex) { mLength = ZSTD_count_2segments(ip+4, match+4, iend, dictEnd, prefixLowest) + 4; offset = (U32)(current - matchIndexS); while (((ip>anchor) & (match>dictStart)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } else { mLength = ZSTD_count(ip+4, match+4, iend) + 4; offset = (U32)(ip - match); while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } /* fall-through */ _match_found: offset_2 = offset_1; offset_1 = offset; ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); _match_stored: /* match found */ ip += mLength; anchor = ip; if (ip <= ilimit) { /* Complementary insertion */ /* done after iLimit test, as candidates could be > iend-8 */ { U32 const indexToInsert = current+2; hashLong[ZSTD_hashPtr(base+indexToInsert, hBitsL, 8)] = indexToInsert; hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base); hashSmall[ZSTD_hashPtr(base+indexToInsert, hBitsS, mls)] = indexToInsert; hashSmall[ZSTD_hashPtr(ip-1, hBitsS, mls)] = (U32)(ip-1-base); } /* check immediate repcode */ if (dictMode == ZSTD_dictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); U32 const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState && repIndex2 < prefixLowestIndex ? dictBase + repIndex2 - dictIndexDelta : base + repIndex2; if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixLowest) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, repLength2-MINMATCH); hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; ip += repLength2; anchor = ip; continue; } break; } } if (dictMode == ZSTD_noDict) { while ( (ip <= ilimit) && ( (offset_2>0) & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { /* store sequence */ size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */ hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base); hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base); ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, rLength-MINMATCH); ip += rLength; anchor = ip; continue; /* faster when present ... (?) */ } } } } /* while (ip < ilimit) */ /* save reps for next block */ rep[0] = offset_1 ? offset_1 : offsetSaved; rep[1] = offset_2 ? offset_2 : offsetSaved; /* Return the last literals size */ return (size_t)(iend - anchor); } size_t ZSTD_compressBlock_doubleFast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { const U32 mls = ms->cParams.minMatch; switch(mls) { default: /* includes case 3 */ case 4 : return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 4, ZSTD_noDict); case 5 : return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 5, ZSTD_noDict); case 6 : return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 6, ZSTD_noDict); case 7 : return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 7, ZSTD_noDict); } } size_t ZSTD_compressBlock_doubleFast_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { const U32 mls = ms->cParams.minMatch; switch(mls) { default: /* includes case 3 */ case 4 : return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 4, ZSTD_dictMatchState); case 5 : return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 5, ZSTD_dictMatchState); case 6 : return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 6, ZSTD_dictMatchState); case 7 : return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 7, ZSTD_dictMatchState); } } static size_t ZSTD_compressBlock_doubleFast_extDict_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, U32 const mls /* template */) { ZSTD_compressionParameters const* cParams = &ms->cParams; U32* const hashLong = ms->hashTable; U32 const hBitsL = cParams->hashLog; U32* const hashSmall = ms->chainTable; U32 const hBitsS = cParams->chainLog; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; const BYTE* const base = ms->window.base; const U32 endIndex = (U32)((size_t)(istart - base) + srcSize); const U32 lowLimit = ZSTD_getLowestMatchIndex(ms, endIndex, cParams->windowLog); const U32 dictStartIndex = lowLimit; const U32 dictLimit = ms->window.dictLimit; const U32 prefixStartIndex = (dictLimit > lowLimit) ? dictLimit : lowLimit; const BYTE* const prefixStart = base + prefixStartIndex; const BYTE* const dictBase = ms->window.dictBase; const BYTE* const dictStart = dictBase + dictStartIndex; const BYTE* const dictEnd = dictBase + prefixStartIndex; U32 offset_1=rep[0], offset_2=rep[1]; DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_extDict_generic (srcSize=%zu)", srcSize); /* if extDict is invalidated due to maxDistance, switch to "regular" variant */ if (prefixStartIndex == dictStartIndex) return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, mls, ZSTD_noDict); /* Search Loop */ while (ip < ilimit) { /* < instead of <=, because (ip+1) */ const size_t hSmall = ZSTD_hashPtr(ip, hBitsS, mls); const U32 matchIndex = hashSmall[hSmall]; const BYTE* const matchBase = matchIndex < prefixStartIndex ? dictBase : base; const BYTE* match = matchBase + matchIndex; const size_t hLong = ZSTD_hashPtr(ip, hBitsL, 8); const U32 matchLongIndex = hashLong[hLong]; const BYTE* const matchLongBase = matchLongIndex < prefixStartIndex ? dictBase : base; const BYTE* matchLong = matchLongBase + matchLongIndex; const U32 current = (U32)(ip-base); const U32 repIndex = current + 1 - offset_1; /* offset_1 expected <= current +1 */ const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base; const BYTE* const repMatch = repBase + repIndex; size_t mLength; hashSmall[hSmall] = hashLong[hLong] = current; /* update hash table */ if ((((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex doesn't overlap dict + prefix */ & (repIndex > dictStartIndex)) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4; ip++; ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH); } else { if ((matchLongIndex > dictStartIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) { const BYTE* const matchEnd = matchLongIndex < prefixStartIndex ? dictEnd : iend; const BYTE* const lowMatchPtr = matchLongIndex < prefixStartIndex ? dictStart : prefixStart; U32 offset; mLength = ZSTD_count_2segments(ip+8, matchLong+8, iend, matchEnd, prefixStart) + 8; offset = current - matchLongIndex; while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } else if ((matchIndex > dictStartIndex) && (MEM_read32(match) == MEM_read32(ip))) { size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8); U32 const matchIndex3 = hashLong[h3]; const BYTE* const match3Base = matchIndex3 < prefixStartIndex ? dictBase : base; const BYTE* match3 = match3Base + matchIndex3; U32 offset; hashLong[h3] = current + 1; if ( (matchIndex3 > dictStartIndex) && (MEM_read64(match3) == MEM_read64(ip+1)) ) { const BYTE* const matchEnd = matchIndex3 < prefixStartIndex ? dictEnd : iend; const BYTE* const lowMatchPtr = matchIndex3 < prefixStartIndex ? dictStart : prefixStart; mLength = ZSTD_count_2segments(ip+9, match3+8, iend, matchEnd, prefixStart) + 8; ip++; offset = current+1 - matchIndex3; while (((ip>anchor) & (match3>lowMatchPtr)) && (ip[-1] == match3[-1])) { ip--; match3--; mLength++; } /* catch up */ } else { const BYTE* const matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend; const BYTE* const lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart; mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4; offset = current - matchIndex; while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } offset_2 = offset_1; offset_1 = offset; ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } else { ip += ((ip-anchor) >> kSearchStrength) + 1; continue; } } /* move to next sequence start */ ip += mLength; anchor = ip; if (ip <= ilimit) { /* Complementary insertion */ /* done after iLimit test, as candidates could be > iend-8 */ { U32 const indexToInsert = current+2; hashLong[ZSTD_hashPtr(base+indexToInsert, hBitsL, 8)] = indexToInsert; hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base); hashSmall[ZSTD_hashPtr(base+indexToInsert, hBitsS, mls)] = indexToInsert; hashSmall[ZSTD_hashPtr(ip-1, hBitsS, mls)] = (U32)(ip-1-base); } /* check immediate repcode */ while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); U32 const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2; if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) /* intentional overflow : ensure repIndex2 doesn't overlap dict + prefix */ & (repIndex2 > dictStartIndex)) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, repLength2-MINMATCH); hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; ip += repLength2; anchor = ip; continue; } break; } } } /* save reps for next block */ rep[0] = offset_1; rep[1] = offset_2; /* Return the last literals size */ return (size_t)(iend - anchor); } size_t ZSTD_compressBlock_doubleFast_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { U32 const mls = ms->cParams.minMatch; switch(mls) { default: /* includes case 3 */ case 4 : return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 4); case 5 : return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 5); case 6 : return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 6); case 7 : return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 7); } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_double_fast.h0000644000076500000240000000241214601576577025221 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_DOUBLE_FAST_H #define ZSTD_DOUBLE_FAST_H #if defined (__cplusplus) extern "C" { #endif #include "../common/mem.h" /* U32 */ #include "zstd_compress_internal.h" /* ZSTD_CCtx, size_t */ void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, void const* end, ZSTD_dictTableLoadMethod_e dtlm); size_t ZSTD_compressBlock_doubleFast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_doubleFast_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_doubleFast_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); #if defined (__cplusplus) } #endif #endif /* ZSTD_DOUBLE_FAST_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_fast.c0000644000076500000240000005360414601576577023673 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #include "zstd_compress_internal.h" /* ZSTD_hashPtr, ZSTD_count, ZSTD_storeSeq */ #include "zstd_fast.h" void ZSTD_fillHashTable(ZSTD_matchState_t* ms, const void* const end, ZSTD_dictTableLoadMethod_e dtlm) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashTable = ms->hashTable; U32 const hBits = cParams->hashLog; U32 const mls = cParams->minMatch; const BYTE* const base = ms->window.base; const BYTE* ip = base + ms->nextToUpdate; const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; const U32 fastHashFillStep = 3; /* Always insert every fastHashFillStep position into the hash table. * Insert the other positions if their hash entry is empty. */ for ( ; ip + fastHashFillStep < iend + 2; ip += fastHashFillStep) { U32 const current = (U32)(ip - base); size_t const hash0 = ZSTD_hashPtr(ip, hBits, mls); hashTable[hash0] = current; if (dtlm == ZSTD_dtlm_fast) continue; /* Only load extra positions for ZSTD_dtlm_full */ { U32 p; for (p = 1; p < fastHashFillStep; ++p) { size_t const hash = ZSTD_hashPtr(ip + p, hBits, mls); if (hashTable[hash] == 0) { /* not yet filled */ hashTable[hash] = current + p; } } } } } FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_fast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, U32 const mls) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashTable = ms->hashTable; U32 const hlog = cParams->hashLog; /* support stepSize of 0 */ size_t const stepSize = cParams->targetLength + !(cParams->targetLength) + 1; const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; /* We check ip0 (ip + 0) and ip1 (ip + 1) each loop */ const BYTE* ip0 = istart; const BYTE* ip1; const BYTE* anchor = istart; const U32 endIndex = (U32)((size_t)(istart - base) + srcSize); const U32 prefixStartIndex = ZSTD_getLowestPrefixIndex(ms, endIndex, cParams->windowLog); const BYTE* const prefixStart = base + prefixStartIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; /* init */ DEBUGLOG(5, "ZSTD_compressBlock_fast_generic"); ip0 += (ip0 == prefixStart); ip1 = ip0 + 1; { U32 const current = (U32)(ip0 - base); U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, current, cParams->windowLog); U32 const maxRep = current - windowLow; if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } /* Main Search Loop */ #ifdef __INTEL_COMPILER /* From intel 'The vector pragma indicates that the loop should be * vectorized if it is legal to do so'. Can be used together with * #pragma ivdep (but have opted to exclude that because intel * warns against using it).*/ #pragma vector always #endif while (ip1 < ilimit) { /* < instead of <=, because check at ip0+2 */ size_t mLength; BYTE const* ip2 = ip0 + 2; size_t const h0 = ZSTD_hashPtr(ip0, hlog, mls); U32 const val0 = MEM_read32(ip0); size_t const h1 = ZSTD_hashPtr(ip1, hlog, mls); U32 const val1 = MEM_read32(ip1); U32 const current0 = (U32)(ip0-base); U32 const current1 = (U32)(ip1-base); U32 const matchIndex0 = hashTable[h0]; U32 const matchIndex1 = hashTable[h1]; BYTE const* repMatch = ip2 - offset_1; const BYTE* match0 = base + matchIndex0; const BYTE* match1 = base + matchIndex1; U32 offcode; #if defined(__aarch64__) PREFETCH_L1(ip0+256); #endif hashTable[h0] = current0; /* update hash table */ hashTable[h1] = current1; /* update hash table */ assert(ip0 + 1 == ip1); if ((offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip2))) { mLength = (ip2[-1] == repMatch[-1]) ? 1 : 0; ip0 = ip2 - mLength; match0 = repMatch - mLength; mLength += 4; offcode = 0; goto _match; } if ((matchIndex0 > prefixStartIndex) && MEM_read32(match0) == val0) { /* found a regular match */ goto _offset; } if ((matchIndex1 > prefixStartIndex) && MEM_read32(match1) == val1) { /* found a regular match after one literal */ ip0 = ip1; match0 = match1; goto _offset; } { size_t const step = ((size_t)(ip0-anchor) >> (kSearchStrength - 1)) + stepSize; assert(step >= 2); ip0 += step; ip1 += step; continue; } _offset: /* Requires: ip0, match0 */ /* Compute the offset code */ offset_2 = offset_1; offset_1 = (U32)(ip0-match0); offcode = offset_1 + ZSTD_REP_MOVE; mLength = 4; /* Count the backwards match length */ while (((ip0>anchor) & (match0>prefixStart)) && (ip0[-1] == match0[-1])) { ip0--; match0--; mLength++; } /* catch up */ _match: /* Requires: ip0, match0, offcode */ /* Count the forward length */ mLength += ZSTD_count(ip0+mLength, match0+mLength, iend); ZSTD_storeSeq(seqStore, (size_t)(ip0-anchor), anchor, iend, offcode, mLength-MINMATCH); /* match found */ ip0 += mLength; anchor = ip0; if (ip0 <= ilimit) { /* Fill Table */ assert(base+current0+2 > istart); /* check base overflow */ hashTable[ZSTD_hashPtr(base+current0+2, hlog, mls)] = current0+2; /* here because current+2 could be > iend-8 */ hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base); if (offset_2 > 0) { /* offset_2==0 means offset_2 is invalidated */ while ( (ip0 <= ilimit) && (MEM_read32(ip0) == MEM_read32(ip0 - offset_2)) ) { /* store sequence */ size_t const rLength = ZSTD_count(ip0+4, ip0+4-offset_2, iend) + 4; { U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */ hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base); ip0 += rLength; ZSTD_storeSeq(seqStore, 0 /*litLen*/, anchor, iend, 0 /*offCode*/, rLength-MINMATCH); anchor = ip0; continue; /* faster when present (confirmed on gcc-8) ... (?) */ } } } ip1 = ip0 + 1; } /* save reps for next block */ rep[0] = offset_1 ? offset_1 : offsetSaved; rep[1] = offset_2 ? offset_2 : offsetSaved; /* Return the last literals size */ return (size_t)(iend - anchor); } size_t ZSTD_compressBlock_fast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { U32 const mls = ms->cParams.minMatch; assert(ms->dictMatchState == NULL); switch(mls) { default: /* includes case 3 */ case 4 : return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 4); case 5 : return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 5); case 6 : return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 6); case 7 : return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 7); } } FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_fast_dictMatchState_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, U32 const mls) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashTable = ms->hashTable; U32 const hlog = cParams->hashLog; /* support stepSize of 0 */ U32 const stepSize = cParams->targetLength + !(cParams->targetLength); const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; const U32 prefixStartIndex = ms->window.dictLimit; const BYTE* const prefixStart = base + prefixStartIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; const ZSTD_matchState_t* const dms = ms->dictMatchState; const ZSTD_compressionParameters* const dictCParams = &dms->cParams ; const U32* const dictHashTable = dms->hashTable; const U32 dictStartIndex = dms->window.dictLimit; const BYTE* const dictBase = dms->window.base; const BYTE* const dictStart = dictBase + dictStartIndex; const BYTE* const dictEnd = dms->window.nextSrc; const U32 dictIndexDelta = prefixStartIndex - (U32)(dictEnd - dictBase); const U32 dictAndPrefixLength = (U32)(ip - prefixStart + dictEnd - dictStart); const U32 dictHLog = dictCParams->hashLog; /* if a dictionary is still attached, it necessarily means that * it is within window size. So we just check it. */ const U32 maxDistance = 1U << cParams->windowLog; const U32 endIndex = (U32)((size_t)(ip - base) + srcSize); assert(endIndex - prefixStartIndex <= maxDistance); (void)maxDistance; (void)endIndex; /* these variables are not used when assert() is disabled */ /* ensure there will be no no underflow * when translating a dict index into a local index */ assert(prefixStartIndex >= (U32)(dictEnd - dictBase)); /* init */ DEBUGLOG(5, "ZSTD_compressBlock_fast_dictMatchState_generic"); ip += (dictAndPrefixLength == 0); /* dictMatchState repCode checks don't currently handle repCode == 0 * disabling. */ assert(offset_1 <= dictAndPrefixLength); assert(offset_2 <= dictAndPrefixLength); /* Main Search Loop */ while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ size_t mLength; size_t const h = ZSTD_hashPtr(ip, hlog, mls); U32 const current = (U32)(ip-base); U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; const U32 repIndex = current + 1 - offset_1; const BYTE* repMatch = (repIndex < prefixStartIndex) ? dictBase + (repIndex - dictIndexDelta) : base + repIndex; hashTable[h] = current; /* update hash table */ if ( ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */ && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4; ip++; ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH); } else if ( (matchIndex <= prefixStartIndex) ) { size_t const dictHash = ZSTD_hashPtr(ip, dictHLog, mls); U32 const dictMatchIndex = dictHashTable[dictHash]; const BYTE* dictMatch = dictBase + dictMatchIndex; if (dictMatchIndex <= dictStartIndex || MEM_read32(dictMatch) != MEM_read32(ip)) { assert(stepSize >= 1); ip += ((ip-anchor) >> kSearchStrength) + stepSize; continue; } else { /* found a dict match */ U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, prefixStart) + 4; while (((ip>anchor) & (dictMatch>dictStart)) && (ip[-1] == dictMatch[-1])) { ip--; dictMatch--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } } else if (MEM_read32(match) != MEM_read32(ip)) { /* it's not a match, and we're not going to check the dictionary */ assert(stepSize >= 1); ip += ((ip-anchor) >> kSearchStrength) + stepSize; continue; } else { /* found a regular match */ U32 const offset = (U32)(ip-match); mLength = ZSTD_count(ip+4, match+4, iend) + 4; while (((ip>anchor) & (match>prefixStart)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } /* match found */ ip += mLength; anchor = ip; if (ip <= ilimit) { /* Fill Table */ assert(base+current+2 > istart); /* check base overflow */ hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2; /* here because current+2 could be > iend-8 */ hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); U32 const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; if ( ((U32)((prefixStartIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, repLength2-MINMATCH); hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; ip += repLength2; anchor = ip; continue; } break; } } } /* save reps for next block */ rep[0] = offset_1 ? offset_1 : offsetSaved; rep[1] = offset_2 ? offset_2 : offsetSaved; /* Return the last literals size */ return (size_t)(iend - anchor); } size_t ZSTD_compressBlock_fast_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { U32 const mls = ms->cParams.minMatch; assert(ms->dictMatchState != NULL); switch(mls) { default: /* includes case 3 */ case 4 : return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 4); case 5 : return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 5); case 6 : return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 6); case 7 : return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 7); } } static size_t ZSTD_compressBlock_fast_extDict_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, U32 const mls) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashTable = ms->hashTable; U32 const hlog = cParams->hashLog; /* support stepSize of 0 */ U32 const stepSize = cParams->targetLength + !(cParams->targetLength); const BYTE* const base = ms->window.base; const BYTE* const dictBase = ms->window.dictBase; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; const U32 endIndex = (U32)((size_t)(istart - base) + srcSize); const U32 lowLimit = ZSTD_getLowestMatchIndex(ms, endIndex, cParams->windowLog); const U32 dictStartIndex = lowLimit; const BYTE* const dictStart = dictBase + dictStartIndex; const U32 dictLimit = ms->window.dictLimit; const U32 prefixStartIndex = dictLimit < lowLimit ? lowLimit : dictLimit; const BYTE* const prefixStart = base + prefixStartIndex; const BYTE* const dictEnd = dictBase + prefixStartIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; U32 offset_1=rep[0], offset_2=rep[1]; DEBUGLOG(5, "ZSTD_compressBlock_fast_extDict_generic (offset_1=%u)", offset_1); /* switch to "regular" variant if extDict is invalidated due to maxDistance */ if (prefixStartIndex == dictStartIndex) return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, mls); /* Search Loop */ while (ip < ilimit) { /* < instead of <=, because (ip+1) */ const size_t h = ZSTD_hashPtr(ip, hlog, mls); const U32 matchIndex = hashTable[h]; const BYTE* const matchBase = matchIndex < prefixStartIndex ? dictBase : base; const BYTE* match = matchBase + matchIndex; const U32 current = (U32)(ip-base); const U32 repIndex = current + 1 - offset_1; const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base; const BYTE* const repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ DEBUGLOG(7, "offset_1 = %u , current = %u", offset_1, current); assert(offset_1 <= current +1); /* check repIndex */ if ( (((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow */ & (repIndex > dictStartIndex)) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; size_t const rLength = ZSTD_count_2segments(ip+1 +4, repMatch +4, iend, repMatchEnd, prefixStart) + 4; ip++; ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, rLength-MINMATCH); ip += rLength; anchor = ip; } else { if ( (matchIndex < dictStartIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { assert(stepSize >= 1); ip += ((ip-anchor) >> kSearchStrength) + stepSize; continue; } { const BYTE* const matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend; const BYTE* const lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart; U32 const offset = current - matchIndex; size_t mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4; while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; /* update offset history */ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); ip += mLength; anchor = ip; } } if (ip <= ilimit) { /* Fill Table */ hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2; hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); U32 const repIndex2 = current2 - offset_2; const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2; if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (repIndex2 > dictStartIndex)) /* intentional overflow */ && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; { U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; } /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0 /*litlen*/, anchor, iend, 0 /*offcode*/, repLength2-MINMATCH); hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; ip += repLength2; anchor = ip; continue; } break; } } } /* save reps for next block */ rep[0] = offset_1; rep[1] = offset_2; /* Return the last literals size */ return (size_t)(iend - anchor); } size_t ZSTD_compressBlock_fast_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { U32 const mls = ms->cParams.minMatch; switch(mls) { default: /* includes case 3 */ case 4 : return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 4); case 5 : return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 5); case 6 : return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 6); case 7 : return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 7); } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_fast.h0000644000076500000240000000227214601576577023673 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_FAST_H #define ZSTD_FAST_H #if defined (__cplusplus) extern "C" { #endif #include "../common/mem.h" /* U32 */ #include "zstd_compress_internal.h" void ZSTD_fillHashTable(ZSTD_matchState_t* ms, void const* end, ZSTD_dictTableLoadMethod_e dtlm); size_t ZSTD_compressBlock_fast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_fast_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_fast_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); #if defined (__cplusplus) } #endif #endif /* ZSTD_FAST_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_lazy.c0000644000076500000240000014724414601576577023721 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #include "zstd_compress_internal.h" #include "zstd_lazy.h" /*-************************************* * Binary Tree search ***************************************/ static void ZSTD_updateDUBT(ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* iend, U32 mls) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashTable = ms->hashTable; U32 const hashLog = cParams->hashLog; U32* const bt = ms->chainTable; U32 const btLog = cParams->chainLog - 1; U32 const btMask = (1 << btLog) - 1; const BYTE* const base = ms->window.base; U32 const target = (U32)(ip - base); U32 idx = ms->nextToUpdate; if (idx != target) DEBUGLOG(7, "ZSTD_updateDUBT, from %u to %u (dictLimit:%u)", idx, target, ms->window.dictLimit); assert(ip + 8 <= iend); /* condition for ZSTD_hashPtr */ (void)iend; assert(idx >= ms->window.dictLimit); /* condition for valid base+idx */ for ( ; idx < target ; idx++) { size_t const h = ZSTD_hashPtr(base + idx, hashLog, mls); /* assumption : ip + 8 <= iend */ U32 const matchIndex = hashTable[h]; U32* const nextCandidatePtr = bt + 2*(idx&btMask); U32* const sortMarkPtr = nextCandidatePtr + 1; DEBUGLOG(8, "ZSTD_updateDUBT: insert %u", idx); hashTable[h] = idx; /* Update Hash Table */ *nextCandidatePtr = matchIndex; /* update BT like a chain */ *sortMarkPtr = ZSTD_DUBT_UNSORTED_MARK; } ms->nextToUpdate = target; } /** ZSTD_insertDUBT1() : * sort one already inserted but unsorted position * assumption : current >= btlow == (current - btmask) * doesn't fail */ static void ZSTD_insertDUBT1(ZSTD_matchState_t* ms, U32 current, const BYTE* inputEnd, U32 nbCompares, U32 btLow, const ZSTD_dictMode_e dictMode) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const bt = ms->chainTable; U32 const btLog = cParams->chainLog - 1; U32 const btMask = (1 << btLog) - 1; size_t commonLengthSmaller=0, commonLengthLarger=0; const BYTE* const base = ms->window.base; const BYTE* const dictBase = ms->window.dictBase; const U32 dictLimit = ms->window.dictLimit; const BYTE* const ip = (current>=dictLimit) ? base + current : dictBase + current; const BYTE* const iend = (current>=dictLimit) ? inputEnd : dictBase + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; const BYTE* match; U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = smallerPtr + 1; U32 matchIndex = *smallerPtr; /* this candidate is unsorted : next sorted candidate is reached through *smallerPtr, while *largerPtr contains previous unsorted candidate (which is already saved and can be overwritten) */ U32 dummy32; /* to be nullified at the end */ U32 const windowValid = ms->window.lowLimit; U32 const maxDistance = 1U << cParams->windowLog; U32 const windowLow = (current - windowValid > maxDistance) ? current - maxDistance : windowValid; DEBUGLOG(8, "ZSTD_insertDUBT1(%u) (dictLimit=%u, lowLimit=%u)", current, dictLimit, windowLow); assert(current >= btLow); assert(ip < iend); /* condition for ZSTD_count */ while (nbCompares-- && (matchIndex > windowLow)) { U32* const nextPtr = bt + 2*(matchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ assert(matchIndex < current); /* note : all candidates are now supposed sorted, * but it's still possible to have nextPtr[1] == ZSTD_DUBT_UNSORTED_MARK * when a real index has the same value as ZSTD_DUBT_UNSORTED_MARK */ if ( (dictMode != ZSTD_extDict) || (matchIndex+matchLength >= dictLimit) /* both in current segment*/ || (current < dictLimit) /* both in extDict */) { const BYTE* const mBase = ( (dictMode != ZSTD_extDict) || (matchIndex+matchLength >= dictLimit)) ? base : dictBase; assert( (matchIndex+matchLength >= dictLimit) /* might be wrong if extDict is incorrectly set to 0 */ || (current < dictLimit) ); match = mBase + matchIndex; matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend); } else { match = dictBase + matchIndex; matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart); if (matchIndex+matchLength >= dictLimit) match = base + matchIndex; /* preparation for next read of match[matchLength] */ } DEBUGLOG(8, "ZSTD_insertDUBT1: comparing %u with %u : found %u common bytes ", current, matchIndex, (U32)matchLength); if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */ break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt tree */ } if (match[matchLength] < ip[matchLength]) { /* necessarily within buffer */ /* match is smaller than current */ *smallerPtr = matchIndex; /* update smaller idx */ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop searching */ DEBUGLOG(8, "ZSTD_insertDUBT1: %u (>btLow=%u) is smaller : next => %u", matchIndex, btLow, nextPtr[1]); smallerPtr = nextPtr+1; /* new "candidate" => larger than match, which was smaller than target */ matchIndex = nextPtr[1]; /* new matchIndex, larger than previous and closer to current */ } else { /* match is larger than current */ *largerPtr = matchIndex; commonLengthLarger = matchLength; if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop searching */ DEBUGLOG(8, "ZSTD_insertDUBT1: %u (>btLow=%u) is larger => %u", matchIndex, btLow, nextPtr[0]); largerPtr = nextPtr; matchIndex = nextPtr[0]; } } *smallerPtr = *largerPtr = 0; } static size_t ZSTD_DUBT_findBetterDictMatch ( ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iend, size_t* offsetPtr, size_t bestLength, U32 nbCompares, U32 const mls, const ZSTD_dictMode_e dictMode) { const ZSTD_matchState_t * const dms = ms->dictMatchState; const ZSTD_compressionParameters* const dmsCParams = &dms->cParams; const U32 * const dictHashTable = dms->hashTable; U32 const hashLog = dmsCParams->hashLog; size_t const h = ZSTD_hashPtr(ip, hashLog, mls); U32 dictMatchIndex = dictHashTable[h]; const BYTE* const base = ms->window.base; const BYTE* const prefixStart = base + ms->window.dictLimit; U32 const current = (U32)(ip-base); const BYTE* const dictBase = dms->window.base; const BYTE* const dictEnd = dms->window.nextSrc; U32 const dictHighLimit = (U32)(dms->window.nextSrc - dms->window.base); U32 const dictLowLimit = dms->window.lowLimit; U32 const dictIndexDelta = ms->window.lowLimit - dictHighLimit; U32* const dictBt = dms->chainTable; U32 const btLog = dmsCParams->chainLog - 1; U32 const btMask = (1 << btLog) - 1; U32 const btLow = (btMask >= dictHighLimit - dictLowLimit) ? dictLowLimit : dictHighLimit - btMask; size_t commonLengthSmaller=0, commonLengthLarger=0; (void)dictMode; assert(dictMode == ZSTD_dictMatchState); while (nbCompares-- && (dictMatchIndex > dictLowLimit)) { U32* const nextPtr = dictBt + 2*(dictMatchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ const BYTE* match = dictBase + dictMatchIndex; matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart); if (dictMatchIndex+matchLength >= dictHighLimit) match = base + dictMatchIndex + dictIndexDelta; /* to prepare for next usage of match[matchLength] */ if (matchLength > bestLength) { U32 matchIndex = dictMatchIndex + dictIndexDelta; if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(current-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) { DEBUGLOG(9, "ZSTD_DUBT_findBetterDictMatch(%u) : found better match length %u -> %u and offsetCode %u -> %u (dictMatchIndex %u, matchIndex %u)", current, (U32)bestLength, (U32)matchLength, (U32)*offsetPtr, ZSTD_REP_MOVE + current - matchIndex, dictMatchIndex, matchIndex); bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + current - matchIndex; } if (ip+matchLength == iend) { /* reached end of input : ip[matchLength] is not valid, no way to know if it's larger or smaller than match */ break; /* drop, to guarantee consistency (miss a little bit of compression) */ } } if (match[matchLength] < ip[matchLength]) { if (dictMatchIndex <= btLow) { break; } /* beyond tree size, stop the search */ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ dictMatchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ } else { /* match is larger than current */ if (dictMatchIndex <= btLow) { break; } /* beyond tree size, stop the search */ commonLengthLarger = matchLength; dictMatchIndex = nextPtr[0]; } } if (bestLength >= MINMATCH) { U32 const mIndex = current - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex; DEBUGLOG(8, "ZSTD_DUBT_findBetterDictMatch(%u) : found match of length %u and offsetCode %u (pos %u)", current, (U32)bestLength, (U32)*offsetPtr, mIndex); } return bestLength; } static size_t ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iend, size_t* offsetPtr, U32 const mls, const ZSTD_dictMode_e dictMode) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashTable = ms->hashTable; U32 const hashLog = cParams->hashLog; size_t const h = ZSTD_hashPtr(ip, hashLog, mls); U32 matchIndex = hashTable[h]; const BYTE* const base = ms->window.base; U32 const current = (U32)(ip-base); U32 const windowLow = ZSTD_getLowestMatchIndex(ms, current, cParams->windowLog); U32* const bt = ms->chainTable; U32 const btLog = cParams->chainLog - 1; U32 const btMask = (1 << btLog) - 1; U32 const btLow = (btMask >= current) ? 0 : current - btMask; U32 const unsortLimit = MAX(btLow, windowLow); U32* nextCandidate = bt + 2*(matchIndex&btMask); U32* unsortedMark = bt + 2*(matchIndex&btMask) + 1; U32 nbCompares = 1U << cParams->searchLog; U32 nbCandidates = nbCompares; U32 previousCandidate = 0; DEBUGLOG(7, "ZSTD_DUBT_findBestMatch (%u) ", current); assert(ip <= iend-8); /* required for h calculation */ /* reach end of unsorted candidates list */ while ( (matchIndex > unsortLimit) && (*unsortedMark == ZSTD_DUBT_UNSORTED_MARK) && (nbCandidates > 1) ) { DEBUGLOG(8, "ZSTD_DUBT_findBestMatch: candidate %u is unsorted", matchIndex); *unsortedMark = previousCandidate; /* the unsortedMark becomes a reversed chain, to move up back to original position */ previousCandidate = matchIndex; matchIndex = *nextCandidate; nextCandidate = bt + 2*(matchIndex&btMask); unsortedMark = bt + 2*(matchIndex&btMask) + 1; nbCandidates --; } /* nullify last candidate if it's still unsorted * simplification, detrimental to compression ratio, beneficial for speed */ if ( (matchIndex > unsortLimit) && (*unsortedMark==ZSTD_DUBT_UNSORTED_MARK) ) { DEBUGLOG(7, "ZSTD_DUBT_findBestMatch: nullify last unsorted candidate %u", matchIndex); *nextCandidate = *unsortedMark = 0; } /* batch sort stacked candidates */ matchIndex = previousCandidate; while (matchIndex) { /* will end on matchIndex == 0 */ U32* const nextCandidateIdxPtr = bt + 2*(matchIndex&btMask) + 1; U32 const nextCandidateIdx = *nextCandidateIdxPtr; ZSTD_insertDUBT1(ms, matchIndex, iend, nbCandidates, unsortLimit, dictMode); matchIndex = nextCandidateIdx; nbCandidates++; } /* find longest match */ { size_t commonLengthSmaller = 0, commonLengthLarger = 0; const BYTE* const dictBase = ms->window.dictBase; const U32 dictLimit = ms->window.dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = bt + 2*(current&btMask) + 1; U32 matchEndIdx = current + 8 + 1; U32 dummy32; /* to be nullified at the end */ size_t bestLength = 0; matchIndex = hashTable[h]; hashTable[h] = current; /* Update Hash Table */ while (nbCompares-- && (matchIndex > windowLow)) { U32* const nextPtr = bt + 2*(matchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ const BYTE* match; if ((dictMode != ZSTD_extDict) || (matchIndex+matchLength >= dictLimit)) { match = base + matchIndex; matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend); } else { match = dictBase + matchIndex; matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart); if (matchIndex+matchLength >= dictLimit) match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ } if (matchLength > bestLength) { if (matchLength > matchEndIdx - matchIndex) matchEndIdx = matchIndex + (U32)matchLength; if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(current-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + current - matchIndex; if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */ if (dictMode == ZSTD_dictMatchState) { nbCompares = 0; /* in addition to avoiding checking any * further in this loop, make sure we * skip checking in the dictionary. */ } break; /* drop, to guarantee consistency (miss a little bit of compression) */ } } if (match[matchLength] < ip[matchLength]) { /* match is smaller than current */ *smallerPtr = matchIndex; /* update smaller idx */ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */ smallerPtr = nextPtr+1; /* new "smaller" => larger of match */ matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ } else { /* match is larger than current */ *largerPtr = matchIndex; commonLengthLarger = matchLength; if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */ largerPtr = nextPtr; matchIndex = nextPtr[0]; } } *smallerPtr = *largerPtr = 0; if (dictMode == ZSTD_dictMatchState && nbCompares) { bestLength = ZSTD_DUBT_findBetterDictMatch( ms, ip, iend, offsetPtr, bestLength, nbCompares, mls, dictMode); } assert(matchEndIdx > current+8); /* ensure nextToUpdate is increased */ ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ if (bestLength >= MINMATCH) { U32 const mIndex = current - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex; DEBUGLOG(8, "ZSTD_DUBT_findBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)", current, (U32)bestLength, (U32)*offsetPtr, mIndex); } return bestLength; } } /** ZSTD_BtFindBestMatch() : Tree updater, providing best match */ FORCE_INLINE_TEMPLATE size_t ZSTD_BtFindBestMatch( ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iLimit, size_t* offsetPtr, const U32 mls /* template */, const ZSTD_dictMode_e dictMode) { DEBUGLOG(7, "ZSTD_BtFindBestMatch"); if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */ ZSTD_updateDUBT(ms, ip, iLimit, mls); return ZSTD_DUBT_findBestMatch(ms, ip, iLimit, offsetPtr, mls, dictMode); } static size_t ZSTD_BtFindBestMatch_selectMLS ( ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* const iLimit, size_t* offsetPtr) { switch(ms->cParams.minMatch) { default : /* includes case 3 */ case 4 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_noDict); case 5 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_noDict); case 7 : case 6 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_noDict); } } static size_t ZSTD_BtFindBestMatch_dictMatchState_selectMLS ( ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* const iLimit, size_t* offsetPtr) { switch(ms->cParams.minMatch) { default : /* includes case 3 */ case 4 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMatchState); case 5 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMatchState); case 7 : case 6 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMatchState); } } static size_t ZSTD_BtFindBestMatch_extDict_selectMLS ( ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* const iLimit, size_t* offsetPtr) { switch(ms->cParams.minMatch) { default : /* includes case 3 */ case 4 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_extDict); case 5 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_extDict); case 7 : case 6 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_extDict); } } /* ********************************* * Hash Chain ***********************************/ #define NEXT_IN_CHAIN(d, mask) chainTable[(d) & (mask)] /* Update chains up to ip (excluded) Assumption : always within prefix (i.e. not within extDict) */ static U32 ZSTD_insertAndFindFirstIndex_internal( ZSTD_matchState_t* ms, const ZSTD_compressionParameters* const cParams, const BYTE* ip, U32 const mls) { U32* const hashTable = ms->hashTable; const U32 hashLog = cParams->hashLog; U32* const chainTable = ms->chainTable; const U32 chainMask = (1 << cParams->chainLog) - 1; const BYTE* const base = ms->window.base; const U32 target = (U32)(ip - base); U32 idx = ms->nextToUpdate; while(idx < target) { /* catch up */ size_t const h = ZSTD_hashPtr(base+idx, hashLog, mls); NEXT_IN_CHAIN(idx, chainMask) = hashTable[h]; hashTable[h] = idx; idx++; } ms->nextToUpdate = target; return hashTable[ZSTD_hashPtr(ip, hashLog, mls)]; } U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip) { const ZSTD_compressionParameters* const cParams = &ms->cParams; return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, ms->cParams.minMatch); } /* inlining is important to hardwire a hot branch (template emulation) */ FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_generic ( ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iLimit, size_t* offsetPtr, const U32 mls, const ZSTD_dictMode_e dictMode) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const chainTable = ms->chainTable; const U32 chainSize = (1 << cParams->chainLog); const U32 chainMask = chainSize-1; const BYTE* const base = ms->window.base; const BYTE* const dictBase = ms->window.dictBase; const U32 dictLimit = ms->window.dictLimit; const BYTE* const prefixStart = base + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const U32 current = (U32)(ip-base); const U32 maxDistance = 1U << cParams->windowLog; const U32 lowestValid = ms->window.lowLimit; const U32 withinMaxDistance = (current - lowestValid > maxDistance) ? current - maxDistance : lowestValid; const U32 isDictionary = (ms->loadedDictEnd != 0); const U32 lowLimit = isDictionary ? lowestValid : withinMaxDistance; const U32 minChain = current > chainSize ? current - chainSize : 0; U32 nbAttempts = 1U << cParams->searchLog; size_t ml=4-1; /* HC4 match finder */ U32 matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls); for ( ; (matchIndex>lowLimit) & (nbAttempts>0) ; nbAttempts--) { size_t currentMl=0; if ((dictMode != ZSTD_extDict) || matchIndex >= dictLimit) { const BYTE* const match = base + matchIndex; assert(matchIndex >= dictLimit); /* ensures this is true if dictMode != ZSTD_extDict */ if (match[ml] == ip[ml]) /* potentially better */ currentMl = ZSTD_count(ip, match, iLimit); } else { const BYTE* const match = dictBase + matchIndex; assert(match+4 <= dictEnd); if (MEM_read32(match) == MEM_read32(ip)) /* assumption : matchIndex <= dictLimit-4 (by table construction) */ currentMl = ZSTD_count_2segments(ip+4, match+4, iLimit, dictEnd, prefixStart) + 4; } /* save best solution */ if (currentMl > ml) { ml = currentMl; *offsetPtr = current - matchIndex + ZSTD_REP_MOVE; if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */ } if (matchIndex <= minChain) break; matchIndex = NEXT_IN_CHAIN(matchIndex, chainMask); } if (dictMode == ZSTD_dictMatchState) { const ZSTD_matchState_t* const dms = ms->dictMatchState; const U32* const dmsChainTable = dms->chainTable; const U32 dmsChainSize = (1 << dms->cParams.chainLog); const U32 dmsChainMask = dmsChainSize - 1; const U32 dmsLowestIndex = dms->window.dictLimit; const BYTE* const dmsBase = dms->window.base; const BYTE* const dmsEnd = dms->window.nextSrc; const U32 dmsSize = (U32)(dmsEnd - dmsBase); const U32 dmsIndexDelta = dictLimit - dmsSize; const U32 dmsMinChain = dmsSize > dmsChainSize ? dmsSize - dmsChainSize : 0; matchIndex = dms->hashTable[ZSTD_hashPtr(ip, dms->cParams.hashLog, mls)]; for ( ; (matchIndex>dmsLowestIndex) & (nbAttempts>0) ; nbAttempts--) { size_t currentMl=0; const BYTE* const match = dmsBase + matchIndex; assert(match+4 <= dmsEnd); if (MEM_read32(match) == MEM_read32(ip)) /* assumption : matchIndex <= dictLimit-4 (by table construction) */ currentMl = ZSTD_count_2segments(ip+4, match+4, iLimit, dmsEnd, prefixStart) + 4; /* save best solution */ if (currentMl > ml) { ml = currentMl; *offsetPtr = current - (matchIndex + dmsIndexDelta) + ZSTD_REP_MOVE; if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */ } if (matchIndex <= dmsMinChain) break; matchIndex = dmsChainTable[matchIndex & dmsChainMask]; } } return ml; } FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_selectMLS ( ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* const iLimit, size_t* offsetPtr) { switch(ms->cParams.minMatch) { default : /* includes case 3 */ case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_noDict); case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_noDict); case 7 : case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_noDict); } } static size_t ZSTD_HcFindBestMatch_dictMatchState_selectMLS ( ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* const iLimit, size_t* offsetPtr) { switch(ms->cParams.minMatch) { default : /* includes case 3 */ case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMatchState); case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMatchState); case 7 : case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMatchState); } } FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_extDict_selectMLS ( ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* const iLimit, size_t* offsetPtr) { switch(ms->cParams.minMatch) { default : /* includes case 3 */ case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_extDict); case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_extDict); case 7 : case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_extDict); } } /* ******************************* * Common parser - lazy strategy *********************************/ typedef enum { search_hashChain, search_binaryTree } searchMethod_e; FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_lazy_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize, const searchMethod_e searchMethod, const U32 depth, ZSTD_dictMode_e const dictMode) { const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; const BYTE* const base = ms->window.base; const U32 prefixLowestIndex = ms->window.dictLimit; const BYTE* const prefixLowest = base + prefixLowestIndex; typedef size_t (*searchMax_f)( ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr); searchMax_f const searchMax = dictMode == ZSTD_dictMatchState ? (searchMethod==search_binaryTree ? ZSTD_BtFindBestMatch_dictMatchState_selectMLS : ZSTD_HcFindBestMatch_dictMatchState_selectMLS) : (searchMethod==search_binaryTree ? ZSTD_BtFindBestMatch_selectMLS : ZSTD_HcFindBestMatch_selectMLS); U32 offset_1 = rep[0], offset_2 = rep[1], savedOffset=0; const ZSTD_matchState_t* const dms = ms->dictMatchState; const U32 dictLowestIndex = dictMode == ZSTD_dictMatchState ? dms->window.dictLimit : 0; const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL; const BYTE* const dictLowest = dictMode == ZSTD_dictMatchState ? dictBase + dictLowestIndex : NULL; const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? prefixLowestIndex - (U32)(dictEnd - dictBase) : 0; const U32 dictAndPrefixLength = (U32)((ip - prefixLowest) + (dictEnd - dictLowest)); DEBUGLOG(5, "ZSTD_compressBlock_lazy_generic (dictMode=%u)", (U32)dictMode); /* init */ ip += (dictAndPrefixLength == 0); if (dictMode == ZSTD_noDict) { U32 const current = (U32)(ip - base); U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, current, ms->cParams.windowLog); U32 const maxRep = current - windowLow; if (offset_2 > maxRep) savedOffset = offset_2, offset_2 = 0; if (offset_1 > maxRep) savedOffset = offset_1, offset_1 = 0; } if (dictMode == ZSTD_dictMatchState) { /* dictMatchState repCode checks don't currently handle repCode == 0 * disabling. */ assert(offset_1 <= dictAndPrefixLength); assert(offset_2 <= dictAndPrefixLength); } /* Match Loop */ #if defined(__GNUC__) && defined(__x86_64__) /* I've measured random a 5% speed loss on levels 5 & 6 (greedy) when the * code alignment is perturbed. To fix the instability align the loop on 32-bytes. */ __asm__(".p2align 5"); #endif while (ip < ilimit) { size_t matchLength=0; size_t offset=0; const BYTE* start=ip+1; /* check repCode */ if (dictMode == ZSTD_dictMatchState) { const U32 repIndex = (U32)(ip - base) + 1 - offset_1; const BYTE* repMatch = (dictMode == ZSTD_dictMatchState && repIndex < prefixLowestIndex) ? dictBase + (repIndex - dictIndexDelta) : base + repIndex; if (((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; matchLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; if (depth==0) goto _storeSequence; } } if ( dictMode == ZSTD_noDict && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { matchLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; if (depth==0) goto _storeSequence; } /* first search (depth 0) */ { size_t offsetFound = 999999999; size_t const ml2 = searchMax(ms, ip, iend, &offsetFound); if (ml2 > matchLength) matchLength = ml2, start = ip, offset=offsetFound; } if (matchLength < 4) { ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */ continue; } /* let's try to find a better solution */ if (depth>=1) while (ip0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) { size_t const mlRep = ZSTD_count(ip+4, ip+4-offset_1, iend) + 4; int const gain2 = (int)(mlRep * 3); int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1); if ((mlRep >= 4) && (gain2 > gain1)) matchLength = mlRep, offset = 0, start = ip; } if (dictMode == ZSTD_dictMatchState) { const U32 repIndex = (U32)(ip - base) - offset_1; const BYTE* repMatch = repIndex < prefixLowestIndex ? dictBase + (repIndex - dictIndexDelta) : base + repIndex; if (((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip)) ) { const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; size_t const mlRep = ZSTD_count_2segments(ip+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; int const gain2 = (int)(mlRep * 3); int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1); if ((mlRep >= 4) && (gain2 > gain1)) matchLength = mlRep, offset = 0, start = ip; } } { size_t offset2=999999999; size_t const ml2 = searchMax(ms, ip, iend, &offset2); int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4); if ((ml2 >= 4) && (gain2 > gain1)) { matchLength = ml2, offset = offset2, start = ip; continue; /* search a better one */ } } /* let's find an even better one */ if ((depth==2) && (ip0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) { size_t const mlRep = ZSTD_count(ip+4, ip+4-offset_1, iend) + 4; int const gain2 = (int)(mlRep * 4); int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1); if ((mlRep >= 4) && (gain2 > gain1)) matchLength = mlRep, offset = 0, start = ip; } if (dictMode == ZSTD_dictMatchState) { const U32 repIndex = (U32)(ip - base) - offset_1; const BYTE* repMatch = repIndex < prefixLowestIndex ? dictBase + (repIndex - dictIndexDelta) : base + repIndex; if (((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip)) ) { const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; size_t const mlRep = ZSTD_count_2segments(ip+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; int const gain2 = (int)(mlRep * 4); int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1); if ((mlRep >= 4) && (gain2 > gain1)) matchLength = mlRep, offset = 0, start = ip; } } { size_t offset2=999999999; size_t const ml2 = searchMax(ms, ip, iend, &offset2); int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7); if ((ml2 >= 4) && (gain2 > gain1)) { matchLength = ml2, offset = offset2, start = ip; continue; } } } break; /* nothing found : store previous solution */ } /* NOTE: * start[-offset+ZSTD_REP_MOVE-1] is undefined behavior. * (-offset+ZSTD_REP_MOVE-1) is unsigned, and is added to start, which * overflows the pointer, which is undefined behavior. */ /* catch up */ if (offset) { if (dictMode == ZSTD_noDict) { while ( ((start > anchor) & (start - (offset-ZSTD_REP_MOVE) > prefixLowest)) && (start[-1] == (start-(offset-ZSTD_REP_MOVE))[-1]) ) /* only search for offset within prefix */ { start--; matchLength++; } } if (dictMode == ZSTD_dictMatchState) { U32 const matchIndex = (U32)((start-base) - (offset - ZSTD_REP_MOVE)); const BYTE* match = (matchIndex < prefixLowestIndex) ? dictBase + matchIndex - dictIndexDelta : base + matchIndex; const BYTE* const mStart = (matchIndex < prefixLowestIndex) ? dictLowest : prefixLowest; while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; } /* catch up */ } offset_2 = offset_1; offset_1 = (U32)(offset - ZSTD_REP_MOVE); } /* store sequence */ _storeSequence: { size_t const litLength = start - anchor; ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offset, matchLength-MINMATCH); anchor = ip = start + matchLength; } /* check immediate repcode */ if (dictMode == ZSTD_dictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); U32 const repIndex = current2 - offset_2; const BYTE* repMatch = dictMode == ZSTD_dictMatchState && repIndex < prefixLowestIndex ? dictBase - dictIndexDelta + repIndex : base + repIndex; if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex) >= 3 /* intentional overflow */) && (MEM_read32(repMatch) == MEM_read32(ip)) ) { const BYTE* const repEnd2 = repIndex < prefixLowestIndex ? dictEnd : iend; matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd2, prefixLowest) + 4; offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, matchLength-MINMATCH); ip += matchLength; anchor = ip; continue; } break; } } if (dictMode == ZSTD_noDict) { while ( ((ip <= ilimit) & (offset_2>0)) && (MEM_read32(ip) == MEM_read32(ip - offset_2)) ) { /* store sequence */ matchLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap repcodes */ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, matchLength-MINMATCH); ip += matchLength; anchor = ip; continue; /* faster when present ... (?) */ } } } /* Save reps for next block */ rep[0] = offset_1 ? offset_1 : savedOffset; rep[1] = offset_2 ? offset_2 : savedOffset; /* Return the last literals size */ return (size_t)(iend - anchor); } size_t ZSTD_compressBlock_btlazy2( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_binaryTree, 2, ZSTD_noDict); } size_t ZSTD_compressBlock_lazy2( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 2, ZSTD_noDict); } size_t ZSTD_compressBlock_lazy( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 1, ZSTD_noDict); } size_t ZSTD_compressBlock_greedy( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 0, ZSTD_noDict); } size_t ZSTD_compressBlock_btlazy2_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_binaryTree, 2, ZSTD_dictMatchState); } size_t ZSTD_compressBlock_lazy2_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 2, ZSTD_dictMatchState); } size_t ZSTD_compressBlock_lazy_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 1, ZSTD_dictMatchState); } size_t ZSTD_compressBlock_greedy_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 0, ZSTD_dictMatchState); } FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_lazy_extDict_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize, const searchMethod_e searchMethod, const U32 depth) { const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; const BYTE* const base = ms->window.base; const U32 dictLimit = ms->window.dictLimit; const BYTE* const prefixStart = base + dictLimit; const BYTE* const dictBase = ms->window.dictBase; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const dictStart = dictBase + ms->window.lowLimit; const U32 windowLog = ms->cParams.windowLog; typedef size_t (*searchMax_f)( ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr); searchMax_f searchMax = searchMethod==search_binaryTree ? ZSTD_BtFindBestMatch_extDict_selectMLS : ZSTD_HcFindBestMatch_extDict_selectMLS; U32 offset_1 = rep[0], offset_2 = rep[1]; DEBUGLOG(5, "ZSTD_compressBlock_lazy_extDict_generic"); /* init */ ip += (ip == prefixStart); /* Match Loop */ #if defined(__GNUC__) && defined(__x86_64__) /* I've measured random a 5% speed loss on levels 5 & 6 (greedy) when the * code alignment is perturbed. To fix the instability align the loop on 32-bytes. */ __asm__(".p2align 5"); #endif while (ip < ilimit) { size_t matchLength=0; size_t offset=0; const BYTE* start=ip+1; U32 current = (U32)(ip-base); /* check repCode */ { const U32 windowLow = ZSTD_getLowestMatchIndex(ms, current+1, windowLog); const U32 repIndex = (U32)(current+1 - offset_1); const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; const BYTE* const repMatch = repBase + repIndex; if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > windowLow)) /* intentional overflow */ if (MEM_read32(ip+1) == MEM_read32(repMatch)) { /* repcode detected we should take it */ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; matchLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repEnd, prefixStart) + 4; if (depth==0) goto _storeSequence; } } /* first search (depth 0) */ { size_t offsetFound = 999999999; size_t const ml2 = searchMax(ms, ip, iend, &offsetFound); if (ml2 > matchLength) matchLength = ml2, start = ip, offset=offsetFound; } if (matchLength < 4) { ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */ continue; } /* let's try to find a better solution */ if (depth>=1) while (ip= 3) & (repIndex > windowLow)) /* intentional overflow */ if (MEM_read32(ip) == MEM_read32(repMatch)) { /* repcode detected */ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; size_t const repLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4; int const gain2 = (int)(repLength * 3); int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1); if ((repLength >= 4) && (gain2 > gain1)) matchLength = repLength, offset = 0, start = ip; } } /* search match, depth 1 */ { size_t offset2=999999999; size_t const ml2 = searchMax(ms, ip, iend, &offset2); int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4); if ((ml2 >= 4) && (gain2 > gain1)) { matchLength = ml2, offset = offset2, start = ip; continue; /* search a better one */ } } /* let's find an even better one */ if ((depth==2) && (ip= 3) & (repIndex > windowLow)) /* intentional overflow */ if (MEM_read32(ip) == MEM_read32(repMatch)) { /* repcode detected */ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; size_t const repLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4; int const gain2 = (int)(repLength * 4); int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1); if ((repLength >= 4) && (gain2 > gain1)) matchLength = repLength, offset = 0, start = ip; } } /* search match, depth 2 */ { size_t offset2=999999999; size_t const ml2 = searchMax(ms, ip, iend, &offset2); int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7); if ((ml2 >= 4) && (gain2 > gain1)) { matchLength = ml2, offset = offset2, start = ip; continue; } } } break; /* nothing found : store previous solution */ } /* catch up */ if (offset) { U32 const matchIndex = (U32)((start-base) - (offset - ZSTD_REP_MOVE)); const BYTE* match = (matchIndex < dictLimit) ? dictBase + matchIndex : base + matchIndex; const BYTE* const mStart = (matchIndex < dictLimit) ? dictStart : prefixStart; while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; } /* catch up */ offset_2 = offset_1; offset_1 = (U32)(offset - ZSTD_REP_MOVE); } /* store sequence */ _storeSequence: { size_t const litLength = start - anchor; ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offset, matchLength-MINMATCH); anchor = ip = start + matchLength; } /* check immediate repcode */ while (ip <= ilimit) { const U32 repCurrent = (U32)(ip-base); const U32 windowLow = ZSTD_getLowestMatchIndex(ms, repCurrent, windowLog); const U32 repIndex = repCurrent - offset_2; const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; const BYTE* const repMatch = repBase + repIndex; if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > windowLow)) /* intentional overflow */ if (MEM_read32(ip) == MEM_read32(repMatch)) { /* repcode detected we should take it */ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4; offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap offset history */ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, matchLength-MINMATCH); ip += matchLength; anchor = ip; continue; /* faster when present ... (?) */ } break; } } /* Save reps for next block */ rep[0] = offset_1; rep[1] = offset_2; /* Return the last literals size */ return (size_t)(iend - anchor); } size_t ZSTD_compressBlock_greedy_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 0); } size_t ZSTD_compressBlock_lazy_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 1); } size_t ZSTD_compressBlock_lazy2_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 2); } size_t ZSTD_compressBlock_btlazy2_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_binaryTree, 2); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_lazy.h0000644000076500000240000000526614601576577023723 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_LAZY_H #define ZSTD_LAZY_H #if defined (__cplusplus) extern "C" { #endif #include "zstd_compress_internal.h" U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip); void ZSTD_preserveUnsortedMark (U32* const table, U32 const size, U32 const reducerValue); /*! used in ZSTD_reduceIndex(). preemptively increase value of ZSTD_DUBT_UNSORTED_MARK */ size_t ZSTD_compressBlock_btlazy2( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_lazy2( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_lazy( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_greedy( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_btlazy2_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_lazy2_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_lazy_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_greedy_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_greedy_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_lazy_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_lazy2_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_btlazy2_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); #if defined (__cplusplus) } #endif #endif /* ZSTD_LAZY_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_ldm.c0000644000076500000240000006057014601576577023512 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #include "zstd_ldm.h" #include "../common/debug.h" #include "zstd_fast.h" /* ZSTD_fillHashTable() */ #include "zstd_double_fast.h" /* ZSTD_fillDoubleHashTable() */ #define LDM_BUCKET_SIZE_LOG 3 #define LDM_MIN_MATCH_LENGTH 64 #define LDM_HASH_RLOG 7 #define LDM_HASH_CHAR_OFFSET 10 void ZSTD_ldm_adjustParameters(ldmParams_t* params, ZSTD_compressionParameters const* cParams) { params->windowLog = cParams->windowLog; ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX); DEBUGLOG(4, "ZSTD_ldm_adjustParameters"); if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG; if (!params->minMatchLength) params->minMatchLength = LDM_MIN_MATCH_LENGTH; if (cParams->strategy >= ZSTD_btopt) { /* Get out of the way of the optimal parser */ U32 const minMatch = MAX(cParams->targetLength, params->minMatchLength); assert(minMatch >= ZSTD_LDM_MINMATCH_MIN); assert(minMatch <= ZSTD_LDM_MINMATCH_MAX); params->minMatchLength = minMatch; } if (params->hashLog == 0) { params->hashLog = MAX(ZSTD_HASHLOG_MIN, params->windowLog - LDM_HASH_RLOG); assert(params->hashLog <= ZSTD_HASHLOG_MAX); } if (params->hashRateLog == 0) { params->hashRateLog = params->windowLog < params->hashLog ? 0 : params->windowLog - params->hashLog; } params->bucketSizeLog = MIN(params->bucketSizeLog, params->hashLog); } size_t ZSTD_ldm_getTableSize(ldmParams_t params) { size_t const ldmHSize = ((size_t)1) << params.hashLog; size_t const ldmBucketSizeLog = MIN(params.bucketSizeLog, params.hashLog); size_t const ldmBucketSize = ((size_t)1) << (params.hashLog - ldmBucketSizeLog); size_t const totalSize = ZSTD_cwksp_alloc_size(ldmBucketSize) + ZSTD_cwksp_alloc_size(ldmHSize * sizeof(ldmEntry_t)); return params.enableLdm ? totalSize : 0; } size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize) { return params.enableLdm ? (maxChunkSize / params.minMatchLength) : 0; } /** ZSTD_ldm_getSmallHash() : * numBits should be <= 32 * If numBits==0, returns 0. * @return : the most significant numBits of value. */ static U32 ZSTD_ldm_getSmallHash(U64 value, U32 numBits) { assert(numBits <= 32); return numBits == 0 ? 0 : (U32)(value >> (64 - numBits)); } /** ZSTD_ldm_getChecksum() : * numBitsToDiscard should be <= 32 * @return : the next most significant 32 bits after numBitsToDiscard */ static U32 ZSTD_ldm_getChecksum(U64 hash, U32 numBitsToDiscard) { assert(numBitsToDiscard <= 32); return (hash >> (64 - 32 - numBitsToDiscard)) & 0xFFFFFFFF; } /** ZSTD_ldm_getTag() ; * Given the hash, returns the most significant numTagBits bits * after (32 + hbits) bits. * * If there are not enough bits remaining, return the last * numTagBits bits. */ static U32 ZSTD_ldm_getTag(U64 hash, U32 hbits, U32 numTagBits) { assert(numTagBits < 32 && hbits <= 32); if (32 - hbits < numTagBits) { return hash & (((U32)1 << numTagBits) - 1); } else { return (hash >> (32 - hbits - numTagBits)) & (((U32)1 << numTagBits) - 1); } } /** ZSTD_ldm_getBucket() : * Returns a pointer to the start of the bucket associated with hash. */ static ldmEntry_t* ZSTD_ldm_getBucket( ldmState_t* ldmState, size_t hash, ldmParams_t const ldmParams) { return ldmState->hashTable + (hash << ldmParams.bucketSizeLog); } /** ZSTD_ldm_insertEntry() : * Insert the entry with corresponding hash into the hash table */ static void ZSTD_ldm_insertEntry(ldmState_t* ldmState, size_t const hash, const ldmEntry_t entry, ldmParams_t const ldmParams) { BYTE* const bucketOffsets = ldmState->bucketOffsets; *(ZSTD_ldm_getBucket(ldmState, hash, ldmParams) + bucketOffsets[hash]) = entry; bucketOffsets[hash]++; bucketOffsets[hash] &= ((U32)1 << ldmParams.bucketSizeLog) - 1; } /** ZSTD_ldm_makeEntryAndInsertByTag() : * * Gets the small hash, checksum, and tag from the rollingHash. * * If the tag matches (1 << ldmParams.hashRateLog)-1, then * creates an ldmEntry from the offset, and inserts it into the hash table. * * hBits is the length of the small hash, which is the most significant hBits * of rollingHash. The checksum is the next 32 most significant bits, followed * by ldmParams.hashRateLog bits that make up the tag. */ static void ZSTD_ldm_makeEntryAndInsertByTag(ldmState_t* ldmState, U64 const rollingHash, U32 const hBits, U32 const offset, ldmParams_t const ldmParams) { U32 const tag = ZSTD_ldm_getTag(rollingHash, hBits, ldmParams.hashRateLog); U32 const tagMask = ((U32)1 << ldmParams.hashRateLog) - 1; if (tag == tagMask) { U32 const hash = ZSTD_ldm_getSmallHash(rollingHash, hBits); U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits); ldmEntry_t entry; entry.offset = offset; entry.checksum = checksum; ZSTD_ldm_insertEntry(ldmState, hash, entry, ldmParams); } } /** ZSTD_ldm_countBackwardsMatch() : * Returns the number of bytes that match backwards before pIn and pMatch. * * We count only bytes where pMatch >= pBase and pIn >= pAnchor. */ static size_t ZSTD_ldm_countBackwardsMatch( const BYTE* pIn, const BYTE* pAnchor, const BYTE* pMatch, const BYTE* pBase) { size_t matchLength = 0; while (pIn > pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) { pIn--; pMatch--; matchLength++; } return matchLength; } /** ZSTD_ldm_fillFastTables() : * * Fills the relevant tables for the ZSTD_fast and ZSTD_dfast strategies. * This is similar to ZSTD_loadDictionaryContent. * * The tables for the other strategies are filled within their * block compressors. */ static size_t ZSTD_ldm_fillFastTables(ZSTD_matchState_t* ms, void const* end) { const BYTE* const iend = (const BYTE*)end; switch(ms->cParams.strategy) { case ZSTD_fast: ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast); break; case ZSTD_dfast: ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast); break; case ZSTD_greedy: case ZSTD_lazy: case ZSTD_lazy2: case ZSTD_btlazy2: case ZSTD_btopt: case ZSTD_btultra: case ZSTD_btultra2: break; default: assert(0); /* not possible : not a valid strategy id */ } return 0; } /** ZSTD_ldm_fillLdmHashTable() : * * Fills hashTable from (lastHashed + 1) to iend (non-inclusive). * lastHash is the rolling hash that corresponds to lastHashed. * * Returns the rolling hash corresponding to position iend-1. */ static U64 ZSTD_ldm_fillLdmHashTable(ldmState_t* state, U64 lastHash, const BYTE* lastHashed, const BYTE* iend, const BYTE* base, U32 hBits, ldmParams_t const ldmParams) { U64 rollingHash = lastHash; const BYTE* cur = lastHashed + 1; while (cur < iend) { rollingHash = ZSTD_rollingHash_rotate(rollingHash, cur[-1], cur[ldmParams.minMatchLength-1], state->hashPower); ZSTD_ldm_makeEntryAndInsertByTag(state, rollingHash, hBits, (U32)(cur - base), ldmParams); ++cur; } return rollingHash; } void ZSTD_ldm_fillHashTable( ldmState_t* state, const BYTE* ip, const BYTE* iend, ldmParams_t const* params) { DEBUGLOG(5, "ZSTD_ldm_fillHashTable"); if ((size_t)(iend - ip) >= params->minMatchLength) { U64 startingHash = ZSTD_rollingHash_compute(ip, params->minMatchLength); ZSTD_ldm_fillLdmHashTable( state, startingHash, ip, iend - params->minMatchLength, state->window.base, params->hashLog - params->bucketSizeLog, *params); } } /** ZSTD_ldm_limitTableUpdate() : * * Sets cctx->nextToUpdate to a position corresponding closer to anchor * if it is far way * (after a long match, only update tables a limited amount). */ static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor) { U32 const current = (U32)(anchor - ms->window.base); if (current > ms->nextToUpdate + 1024) { ms->nextToUpdate = current - MIN(512, current - ms->nextToUpdate - 1024); } } static size_t ZSTD_ldm_generateSequences_internal( ldmState_t* ldmState, rawSeqStore_t* rawSeqStore, ldmParams_t const* params, void const* src, size_t srcSize) { /* LDM parameters */ int const extDict = ZSTD_window_hasExtDict(ldmState->window); U32 const minMatchLength = params->minMatchLength; U64 const hashPower = ldmState->hashPower; U32 const hBits = params->hashLog - params->bucketSizeLog; U32 const ldmBucketSize = 1U << params->bucketSizeLog; U32 const hashRateLog = params->hashRateLog; U32 const ldmTagMask = (1U << params->hashRateLog) - 1; /* Prefix and extDict parameters */ U32 const dictLimit = ldmState->window.dictLimit; U32 const lowestIndex = extDict ? ldmState->window.lowLimit : dictLimit; BYTE const* const base = ldmState->window.base; BYTE const* const dictBase = extDict ? ldmState->window.dictBase : NULL; BYTE const* const dictStart = extDict ? dictBase + lowestIndex : NULL; BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL; BYTE const* const lowPrefixPtr = base + dictLimit; /* Input bounds */ BYTE const* const istart = (BYTE const*)src; BYTE const* const iend = istart + srcSize; BYTE const* const ilimit = iend - MAX(minMatchLength, HASH_READ_SIZE); /* Input positions */ BYTE const* anchor = istart; BYTE const* ip = istart; /* Rolling hash */ BYTE const* lastHashed = NULL; U64 rollingHash = 0; while (ip <= ilimit) { size_t mLength; U32 const current = (U32)(ip - base); size_t forwardMatchLength = 0, backwardMatchLength = 0; ldmEntry_t* bestEntry = NULL; if (ip != istart) { rollingHash = ZSTD_rollingHash_rotate(rollingHash, lastHashed[0], lastHashed[minMatchLength], hashPower); } else { rollingHash = ZSTD_rollingHash_compute(ip, minMatchLength); } lastHashed = ip; /* Do not insert and do not look for a match */ if (ZSTD_ldm_getTag(rollingHash, hBits, hashRateLog) != ldmTagMask) { ip++; continue; } /* Get the best entry and compute the match lengths */ { ldmEntry_t* const bucket = ZSTD_ldm_getBucket(ldmState, ZSTD_ldm_getSmallHash(rollingHash, hBits), *params); ldmEntry_t* cur; size_t bestMatchLength = 0; U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits); for (cur = bucket; cur < bucket + ldmBucketSize; ++cur) { size_t curForwardMatchLength, curBackwardMatchLength, curTotalMatchLength; if (cur->checksum != checksum || cur->offset <= lowestIndex) { continue; } if (extDict) { BYTE const* const curMatchBase = cur->offset < dictLimit ? dictBase : base; BYTE const* const pMatch = curMatchBase + cur->offset; BYTE const* const matchEnd = cur->offset < dictLimit ? dictEnd : iend; BYTE const* const lowMatchPtr = cur->offset < dictLimit ? dictStart : lowPrefixPtr; curForwardMatchLength = ZSTD_count_2segments( ip, pMatch, iend, matchEnd, lowPrefixPtr); if (curForwardMatchLength < minMatchLength) { continue; } curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch, lowMatchPtr); curTotalMatchLength = curForwardMatchLength + curBackwardMatchLength; } else { /* !extDict */ BYTE const* const pMatch = base + cur->offset; curForwardMatchLength = ZSTD_count(ip, pMatch, iend); if (curForwardMatchLength < minMatchLength) { continue; } curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch, lowPrefixPtr); curTotalMatchLength = curForwardMatchLength + curBackwardMatchLength; } if (curTotalMatchLength > bestMatchLength) { bestMatchLength = curTotalMatchLength; forwardMatchLength = curForwardMatchLength; backwardMatchLength = curBackwardMatchLength; bestEntry = cur; } } } /* No match found -- continue searching */ if (bestEntry == NULL) { ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits, current, *params); ip++; continue; } /* Match found */ mLength = forwardMatchLength + backwardMatchLength; ip -= backwardMatchLength; { /* Store the sequence: * ip = current - backwardMatchLength * The match is at (bestEntry->offset - backwardMatchLength) */ U32 const matchIndex = bestEntry->offset; U32 const offset = current - matchIndex; rawSeq* const seq = rawSeqStore->seq + rawSeqStore->size; /* Out of sequence storage */ if (rawSeqStore->size == rawSeqStore->capacity) return ERROR(dstSize_tooSmall); seq->litLength = (U32)(ip - anchor); seq->matchLength = (U32)mLength; seq->offset = offset; rawSeqStore->size++; } /* Insert the current entry into the hash table */ ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits, (U32)(lastHashed - base), *params); assert(ip + backwardMatchLength == lastHashed); /* Fill the hash table from lastHashed+1 to ip+mLength*/ /* Heuristic: don't need to fill the entire table at end of block */ if (ip + mLength <= ilimit) { rollingHash = ZSTD_ldm_fillLdmHashTable( ldmState, rollingHash, lastHashed, ip + mLength, base, hBits, *params); lastHashed = ip + mLength - 1; } ip += mLength; anchor = ip; } return iend - anchor; } /*! ZSTD_ldm_reduceTable() : * reduce table indexes by `reducerValue` */ static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size, U32 const reducerValue) { U32 u; for (u = 0; u < size; u++) { if (table[u].offset < reducerValue) table[u].offset = 0; else table[u].offset -= reducerValue; } } size_t ZSTD_ldm_generateSequences( ldmState_t* ldmState, rawSeqStore_t* sequences, ldmParams_t const* params, void const* src, size_t srcSize) { U32 const maxDist = 1U << params->windowLog; BYTE const* const istart = (BYTE const*)src; BYTE const* const iend = istart + srcSize; size_t const kMaxChunkSize = 1 << 20; size_t const nbChunks = (srcSize / kMaxChunkSize) + ((srcSize % kMaxChunkSize) != 0); size_t chunk; size_t leftoverSize = 0; assert(ZSTD_CHUNKSIZE_MAX >= kMaxChunkSize); /* Check that ZSTD_window_update() has been called for this chunk prior * to passing it to this function. */ assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize); /* The input could be very large (in zstdmt), so it must be broken up into * chunks to enforce the maximum distance and handle overflow correction. */ assert(sequences->pos <= sequences->size); assert(sequences->size <= sequences->capacity); for (chunk = 0; chunk < nbChunks && sequences->size < sequences->capacity; ++chunk) { BYTE const* const chunkStart = istart + chunk * kMaxChunkSize; size_t const remaining = (size_t)(iend - chunkStart); BYTE const *const chunkEnd = (remaining < kMaxChunkSize) ? iend : chunkStart + kMaxChunkSize; size_t const chunkSize = chunkEnd - chunkStart; size_t newLeftoverSize; size_t const prevSize = sequences->size; assert(chunkStart < iend); /* 1. Perform overflow correction if necessary. */ if (ZSTD_window_needOverflowCorrection(ldmState->window, chunkEnd)) { U32 const ldmHSize = 1U << params->hashLog; U32 const correction = ZSTD_window_correctOverflow( &ldmState->window, /* cycleLog */ 0, maxDist, chunkStart); ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction); /* invalidate dictionaries on overflow correction */ ldmState->loadedDictEnd = 0; } /* 2. We enforce the maximum offset allowed. * * kMaxChunkSize should be small enough that we don't lose too much of * the window through early invalidation. * TODO: * Test the chunk size. * * Try invalidation after the sequence generation and test the * the offset against maxDist directly. * * NOTE: Because of dictionaries + sequence splitting we MUST make sure * that any offset used is valid at the END of the sequence, since it may * be split into two sequences. This condition holds when using * ZSTD_window_enforceMaxDist(), but if we move to checking offsets * against maxDist directly, we'll have to carefully handle that case. */ ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, &ldmState->loadedDictEnd, NULL); /* 3. Generate the sequences for the chunk, and get newLeftoverSize. */ newLeftoverSize = ZSTD_ldm_generateSequences_internal( ldmState, sequences, params, chunkStart, chunkSize); if (ZSTD_isError(newLeftoverSize)) return newLeftoverSize; /* 4. We add the leftover literals from previous iterations to the first * newly generated sequence, or add the `newLeftoverSize` if none are * generated. */ /* Prepend the leftover literals from the last call */ if (prevSize < sequences->size) { sequences->seq[prevSize].litLength += (U32)leftoverSize; leftoverSize = newLeftoverSize; } else { assert(newLeftoverSize == chunkSize); leftoverSize += chunkSize; } } return 0; } void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch) { while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) { rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos; if (srcSize <= seq->litLength) { /* Skip past srcSize literals */ seq->litLength -= (U32)srcSize; return; } srcSize -= seq->litLength; seq->litLength = 0; if (srcSize < seq->matchLength) { /* Skip past the first srcSize of the match */ seq->matchLength -= (U32)srcSize; if (seq->matchLength < minMatch) { /* The match is too short, omit it */ if (rawSeqStore->pos + 1 < rawSeqStore->size) { seq[1].litLength += seq[0].matchLength; } rawSeqStore->pos++; } return; } srcSize -= seq->matchLength; seq->matchLength = 0; rawSeqStore->pos++; } } /** * If the sequence length is longer than remaining then the sequence is split * between this block and the next. * * Returns the current sequence to handle, or if the rest of the block should * be literals, it returns a sequence with offset == 0. */ static rawSeq maybeSplitSequence(rawSeqStore_t* rawSeqStore, U32 const remaining, U32 const minMatch) { rawSeq sequence = rawSeqStore->seq[rawSeqStore->pos]; assert(sequence.offset > 0); /* Likely: No partial sequence */ if (remaining >= sequence.litLength + sequence.matchLength) { rawSeqStore->pos++; return sequence; } /* Cut the sequence short (offset == 0 ==> rest is literals). */ if (remaining <= sequence.litLength) { sequence.offset = 0; } else if (remaining < sequence.litLength + sequence.matchLength) { sequence.matchLength = remaining - sequence.litLength; if (sequence.matchLength < minMatch) { sequence.offset = 0; } } /* Skip past `remaining` bytes for the future sequences. */ ZSTD_ldm_skipSequences(rawSeqStore, remaining, minMatch); return sequence; } size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { const ZSTD_compressionParameters* const cParams = &ms->cParams; unsigned const minMatch = cParams->minMatch; ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(cParams->strategy, ZSTD_matchState_dictMode(ms)); /* Input bounds */ BYTE const* const istart = (BYTE const*)src; BYTE const* const iend = istart + srcSize; /* Input positions */ BYTE const* ip = istart; DEBUGLOG(5, "ZSTD_ldm_blockCompress: srcSize=%zu", srcSize); assert(rawSeqStore->pos <= rawSeqStore->size); assert(rawSeqStore->size <= rawSeqStore->capacity); /* Loop through each sequence and apply the block compressor to the lits */ while (rawSeqStore->pos < rawSeqStore->size && ip < iend) { /* maybeSplitSequence updates rawSeqStore->pos */ rawSeq const sequence = maybeSplitSequence(rawSeqStore, (U32)(iend - ip), minMatch); int i; /* End signal */ if (sequence.offset == 0) break; assert(ip + sequence.litLength + sequence.matchLength <= iend); /* Fill tables for block compressor */ ZSTD_ldm_limitTableUpdate(ms, ip); ZSTD_ldm_fillFastTables(ms, ip); /* Run the block compressor */ DEBUGLOG(5, "pos %u : calling block compressor on segment of size %u", (unsigned)(ip-istart), sequence.litLength); { size_t const newLitLength = blockCompressor(ms, seqStore, rep, ip, sequence.litLength); ip += sequence.litLength; /* Update the repcodes */ for (i = ZSTD_REP_NUM - 1; i > 0; i--) rep[i] = rep[i-1]; rep[0] = sequence.offset; /* Store the sequence */ ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength, iend, sequence.offset + ZSTD_REP_MOVE, sequence.matchLength - MINMATCH); ip += sequence.matchLength; } } /* Fill the tables for the block compressor */ ZSTD_ldm_limitTableUpdate(ms, ip); ZSTD_ldm_fillFastTables(ms, ip); /* Compress the last literals */ return blockCompressor(ms, seqStore, rep, ip, iend - ip); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_ldm.h0000644000076500000240000000772614601576577023523 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_LDM_H #define ZSTD_LDM_H #if defined (__cplusplus) extern "C" { #endif #include "zstd_compress_internal.h" /* ldmParams_t, U32 */ #include "../zstd.h" /* ZSTD_CCtx, size_t */ /*-************************************* * Long distance matching ***************************************/ #define ZSTD_LDM_DEFAULT_WINDOW_LOG ZSTD_WINDOWLOG_LIMIT_DEFAULT void ZSTD_ldm_fillHashTable( ldmState_t* state, const BYTE* ip, const BYTE* iend, ldmParams_t const* params); /** * ZSTD_ldm_generateSequences(): * * Generates the sequences using the long distance match finder. * Generates long range matching sequences in `sequences`, which parse a prefix * of the source. `sequences` must be large enough to store every sequence, * which can be checked with `ZSTD_ldm_getMaxNbSeq()`. * @returns 0 or an error code. * * NOTE: The user must have called ZSTD_window_update() for all of the input * they have, even if they pass it to ZSTD_ldm_generateSequences() in chunks. * NOTE: This function returns an error if it runs out of space to store * sequences. */ size_t ZSTD_ldm_generateSequences( ldmState_t* ldms, rawSeqStore_t* sequences, ldmParams_t const* params, void const* src, size_t srcSize); /** * ZSTD_ldm_blockCompress(): * * Compresses a block using the predefined sequences, along with a secondary * block compressor. The literals section of every sequence is passed to the * secondary block compressor, and those sequences are interspersed with the * predefined sequences. Returns the length of the last literals. * Updates `rawSeqStore.pos` to indicate how many sequences have been consumed. * `rawSeqStore.seq` may also be updated to split the last sequence between two * blocks. * @return The length of the last literals. * * NOTE: The source must be at most the maximum block size, but the predefined * sequences can be any size, and may be longer than the block. In the case that * they are longer than the block, the last sequences may need to be split into * two. We handle that case correctly, and update `rawSeqStore` appropriately. * NOTE: This function does not return any errors. */ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); /** * ZSTD_ldm_skipSequences(): * * Skip past `srcSize` bytes worth of sequences in `rawSeqStore`. * Avoids emitting matches less than `minMatch` bytes. * Must be called for data with is not passed to ZSTD_ldm_blockCompress(). */ void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch); /** ZSTD_ldm_getTableSize() : * Estimate the space needed for long distance matching tables or 0 if LDM is * disabled. */ size_t ZSTD_ldm_getTableSize(ldmParams_t params); /** ZSTD_ldm_getSeqSpace() : * Return an upper bound on the number of sequences that can be produced by * the long distance matcher, or 0 if LDM is disabled. */ size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize); /** ZSTD_ldm_adjustParameters() : * If the params->hashRateLog is not set, set it to its default value based on * windowLog and params->hashLog. * * Ensures that params->bucketSizeLog is <= params->hashLog (setting it to * params->hashLog if it is not). * * Ensures that the minMatchLength >= targetLength during optimal parsing. */ void ZSTD_ldm_adjustParameters(ldmParams_t* params, ZSTD_compressionParameters const* cParams); #if defined (__cplusplus) } #endif #endif /* ZSTD_FAST_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_opt.c0000644000076500000240000015454214601576577023543 0ustar00twstaff/* * Copyright (c) 2016-2020, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #include "zstd_compress_internal.h" #include "hist.h" #include "zstd_opt.h" #define ZSTD_LITFREQ_ADD 2 /* scaling factor for litFreq, so that frequencies adapt faster to new stats */ #define ZSTD_FREQ_DIV 4 /* log factor when using previous stats to init next stats */ #define ZSTD_MAX_PRICE (1<<30) #define ZSTD_PREDEF_THRESHOLD 1024 /* if srcSize < ZSTD_PREDEF_THRESHOLD, symbols' cost is assumed static, directly determined by pre-defined distributions */ /*-************************************* * Price functions for optimal parser ***************************************/ #if 0 /* approximation at bit level */ # define BITCOST_ACCURACY 0 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) # define WEIGHT(stat) ((void)opt, ZSTD_bitWeight(stat)) #elif 0 /* fractional bit accuracy */ # define BITCOST_ACCURACY 8 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) # define WEIGHT(stat,opt) ((void)opt, ZSTD_fracWeight(stat)) #else /* opt==approx, ultra==accurate */ # define BITCOST_ACCURACY 8 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) # define WEIGHT(stat,opt) (opt ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat)) #endif MEM_STATIC U32 ZSTD_bitWeight(U32 stat) { return (ZSTD_highbit32(stat+1) * BITCOST_MULTIPLIER); } MEM_STATIC U32 ZSTD_fracWeight(U32 rawStat) { U32 const stat = rawStat + 1; U32 const hb = ZSTD_highbit32(stat); U32 const BWeight = hb * BITCOST_MULTIPLIER; U32 const FWeight = (stat << BITCOST_ACCURACY) >> hb; U32 const weight = BWeight + FWeight; assert(hb + BITCOST_ACCURACY < 31); return weight; } #if (DEBUGLEVEL>=2) /* debugging function, * @return price in bytes as fractional value * for debug messages only */ MEM_STATIC double ZSTD_fCost(U32 price) { return (double)price / (BITCOST_MULTIPLIER*8); } #endif static int ZSTD_compressedLiterals(optState_t const* const optPtr) { return optPtr->literalCompressionMode != ZSTD_lcm_uncompressed; } static void ZSTD_setBasePrices(optState_t* optPtr, int optLevel) { if (ZSTD_compressedLiterals(optPtr)) optPtr->litSumBasePrice = WEIGHT(optPtr->litSum, optLevel); optPtr->litLengthSumBasePrice = WEIGHT(optPtr->litLengthSum, optLevel); optPtr->matchLengthSumBasePrice = WEIGHT(optPtr->matchLengthSum, optLevel); optPtr->offCodeSumBasePrice = WEIGHT(optPtr->offCodeSum, optLevel); } /* ZSTD_downscaleStat() : * reduce all elements in table by a factor 2^(ZSTD_FREQ_DIV+malus) * return the resulting sum of elements */ static U32 ZSTD_downscaleStat(unsigned* table, U32 lastEltIndex, int malus) { U32 s, sum=0; DEBUGLOG(5, "ZSTD_downscaleStat (nbElts=%u)", (unsigned)lastEltIndex+1); assert(ZSTD_FREQ_DIV+malus > 0 && ZSTD_FREQ_DIV+malus < 31); for (s=0; s> (ZSTD_FREQ_DIV+malus)); sum += table[s]; } return sum; } /* ZSTD_rescaleFreqs() : * if first block (detected by optPtr->litLengthSum == 0) : init statistics * take hints from dictionary if there is one * or init from zero, using src for literals stats, or flat 1 for match symbols * otherwise downscale existing stats, to be used as seed for next block. */ static void ZSTD_rescaleFreqs(optState_t* const optPtr, const BYTE* const src, size_t const srcSize, int const optLevel) { int const compressedLiterals = ZSTD_compressedLiterals(optPtr); DEBUGLOG(5, "ZSTD_rescaleFreqs (srcSize=%u)", (unsigned)srcSize); optPtr->priceType = zop_dynamic; if (optPtr->litLengthSum == 0) { /* first block : init */ if (srcSize <= ZSTD_PREDEF_THRESHOLD) { /* heuristic */ DEBUGLOG(5, "(srcSize <= ZSTD_PREDEF_THRESHOLD) => zop_predef"); optPtr->priceType = zop_predef; } assert(optPtr->symbolCosts != NULL); if (optPtr->symbolCosts->huf.repeatMode == HUF_repeat_valid) { /* huffman table presumed generated by dictionary */ optPtr->priceType = zop_dynamic; if (compressedLiterals) { unsigned lit; assert(optPtr->litFreq != NULL); optPtr->litSum = 0; for (lit=0; lit<=MaxLit; lit++) { U32 const scaleLog = 11; /* scale to 2K */ U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->huf.CTable, lit); assert(bitCost <= scaleLog); optPtr->litFreq[lit] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; optPtr->litSum += optPtr->litFreq[lit]; } } { unsigned ll; FSE_CState_t llstate; FSE_initCState(&llstate, optPtr->symbolCosts->fse.litlengthCTable); optPtr->litLengthSum = 0; for (ll=0; ll<=MaxLL; ll++) { U32 const scaleLog = 10; /* scale to 1K */ U32 const bitCost = FSE_getMaxNbBits(llstate.symbolTT, ll); assert(bitCost < scaleLog); optPtr->litLengthFreq[ll] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; optPtr->litLengthSum += optPtr->litLengthFreq[ll]; } } { unsigned ml; FSE_CState_t mlstate; FSE_initCState(&mlstate, optPtr->symbolCosts->fse.matchlengthCTable); optPtr->matchLengthSum = 0; for (ml=0; ml<=MaxML; ml++) { U32 const scaleLog = 10; U32 const bitCost = FSE_getMaxNbBits(mlstate.symbolTT, ml); assert(bitCost < scaleLog); optPtr->matchLengthFreq[ml] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; optPtr->matchLengthSum += optPtr->matchLengthFreq[ml]; } } { unsigned of; FSE_CState_t ofstate; FSE_initCState(&ofstate, optPtr->symbolCosts->fse.offcodeCTable); optPtr->offCodeSum = 0; for (of=0; of<=MaxOff; of++) { U32 const scaleLog = 10; U32 const bitCost = FSE_getMaxNbBits(ofstate.symbolTT, of); assert(bitCost < scaleLog); optPtr->offCodeFreq[of] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; optPtr->offCodeSum += optPtr->offCodeFreq[of]; } } } else { /* not a dictionary */ assert(optPtr->litFreq != NULL); if (compressedLiterals) { unsigned lit = MaxLit; HIST_count_simple(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */ optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1); } { unsigned ll; for (ll=0; ll<=MaxLL; ll++) optPtr->litLengthFreq[ll] = 1; } optPtr->litLengthSum = MaxLL+1; { unsigned ml; for (ml=0; ml<=MaxML; ml++) optPtr->matchLengthFreq[ml] = 1; } optPtr->matchLengthSum = MaxML+1; { unsigned of; for (of=0; of<=MaxOff; of++) optPtr->offCodeFreq[of] = 1; } optPtr->offCodeSum = MaxOff+1; } } else { /* new block : re-use previous statistics, scaled down */ if (compressedLiterals) optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1); optPtr->litLengthSum = ZSTD_downscaleStat(optPtr->litLengthFreq, MaxLL, 0); optPtr->matchLengthSum = ZSTD_downscaleStat(optPtr->matchLengthFreq, MaxML, 0); optPtr->offCodeSum = ZSTD_downscaleStat(optPtr->offCodeFreq, MaxOff, 0); } ZSTD_setBasePrices(optPtr, optLevel); } /* ZSTD_rawLiteralsCost() : * price of literals (only) in specified segment (which length can be 0). * does not include price of literalLength symbol */ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, const optState_t* const optPtr, int optLevel) { if (litLength == 0) return 0; if (!ZSTD_compressedLiterals(optPtr)) return (litLength << 3) * BITCOST_MULTIPLIER; /* Uncompressed - 8 bytes per literal. */ if (optPtr->priceType == zop_predef) return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */ /* dynamic statistics */ { U32 price = litLength * optPtr->litSumBasePrice; U32 u; for (u=0; u < litLength; u++) { assert(WEIGHT(optPtr->litFreq[literals[u]], optLevel) <= optPtr->litSumBasePrice); /* literal cost should never be negative */ price -= WEIGHT(optPtr->litFreq[literals[u]], optLevel); } return price; } } /* ZSTD_litLengthPrice() : * cost of literalLength symbol */ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr, int optLevel) { if (optPtr->priceType == zop_predef) return WEIGHT(litLength, optLevel); /* dynamic statistics */ { U32 const llCode = ZSTD_LLcode(litLength); return (LL_bits[llCode] * BITCOST_MULTIPLIER) + optPtr->litLengthSumBasePrice - WEIGHT(optPtr->litLengthFreq[llCode], optLevel); } } /* ZSTD_getMatchPrice() : * Provides the cost of the match part (offset + matchLength) of a sequence * Must be combined with ZSTD_fullLiteralsCost() to get the full cost of a sequence. * optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */ FORCE_INLINE_TEMPLATE U32 ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, const optState_t* const optPtr, int const optLevel) { U32 price; U32 const offCode = ZSTD_highbit32(offset+1); U32 const mlBase = matchLength - MINMATCH; assert(matchLength >= MINMATCH); if (optPtr->priceType == zop_predef) /* fixed scheme, do not use statistics */ return WEIGHT(mlBase, optLevel) + ((16 + offCode) * BITCOST_MULTIPLIER); /* dynamic statistics */ price = (offCode * BITCOST_MULTIPLIER) + (optPtr->offCodeSumBasePrice - WEIGHT(optPtr->offCodeFreq[offCode], optLevel)); if ((optLevel<2) /*static*/ && offCode >= 20) price += (offCode-19)*2 * BITCOST_MULTIPLIER; /* handicap for long distance offsets, favor decompression speed */ /* match Length */ { U32 const mlCode = ZSTD_MLcode(mlBase); price += (ML_bits[mlCode] * BITCOST_MULTIPLIER) + (optPtr->matchLengthSumBasePrice - WEIGHT(optPtr->matchLengthFreq[mlCode], optLevel)); } price += BITCOST_MULTIPLIER / 5; /* heuristic : make matches a bit more costly to favor less sequences -> faster decompression speed */ DEBUGLOG(8, "ZSTD_getMatchPrice(ml:%u) = %u", matchLength, price); return price; } /* ZSTD_updateStats() : * assumption : literals + litLengtn <= iend */ static void ZSTD_updateStats(optState_t* const optPtr, U32 litLength, const BYTE* literals, U32 offsetCode, U32 matchLength) { /* literals */ if (ZSTD_compressedLiterals(optPtr)) { U32 u; for (u=0; u < litLength; u++) optPtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD; optPtr->litSum += litLength*ZSTD_LITFREQ_ADD; } /* literal Length */ { U32 const llCode = ZSTD_LLcode(litLength); optPtr->litLengthFreq[llCode]++; optPtr->litLengthSum++; } /* match offset code (0-2=>repCode; 3+=>offset+2) */ { U32 const offCode = ZSTD_highbit32(offsetCode+1); assert(offCode <= MaxOff); optPtr->offCodeFreq[offCode]++; optPtr->offCodeSum++; } /* match Length */ { U32 const mlBase = matchLength - MINMATCH; U32 const mlCode = ZSTD_MLcode(mlBase); optPtr->matchLengthFreq[mlCode]++; optPtr->matchLengthSum++; } } /* ZSTD_readMINMATCH() : * function safe only for comparisons * assumption : memPtr must be at least 4 bytes before end of buffer */ MEM_STATIC U32 ZSTD_readMINMATCH(const void* memPtr, U32 length) { switch (length) { default : case 4 : return MEM_read32(memPtr); case 3 : if (MEM_isLittleEndian()) return MEM_read32(memPtr)<<8; else return MEM_read32(memPtr)>>8; } } /* Update hashTable3 up to ip (excluded) Assumption : always within prefix (i.e. not within extDict) */ static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, U32* nextToUpdate3, const BYTE* const ip) { U32* const hashTable3 = ms->hashTable3; U32 const hashLog3 = ms->hashLog3; const BYTE* const base = ms->window.base; U32 idx = *nextToUpdate3; U32 const target = (U32)(ip - base); size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3); assert(hashLog3 > 0); while(idx < target) { hashTable3[ZSTD_hash3Ptr(base+idx, hashLog3)] = idx; idx++; } *nextToUpdate3 = target; return hashTable3[hash3]; } /*-************************************* * Binary Tree search ***************************************/ /** ZSTD_insertBt1() : add one or multiple positions to tree. * ip : assumed <= iend-8 . * @return : nb of positions added */ static U32 ZSTD_insertBt1( ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iend, U32 const mls, const int extDict) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashTable = ms->hashTable; U32 const hashLog = cParams->hashLog; size_t const h = ZSTD_hashPtr(ip, hashLog, mls); U32* const bt = ms->chainTable; U32 const btLog = cParams->chainLog - 1; U32 const btMask = (1 << btLog) - 1; U32 matchIndex = hashTable[h]; size_t commonLengthSmaller=0, commonLengthLarger=0; const BYTE* const base = ms->window.base; const BYTE* const dictBase = ms->window.dictBase; const U32 dictLimit = ms->window.dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; const BYTE* match; const U32 current = (U32)(ip-base); const U32 btLow = btMask >= current ? 0 : current - btMask; U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = smallerPtr + 1; U32 dummy32; /* to be nullified at the end */ U32 const windowLow = ms->window.lowLimit; U32 matchEndIdx = current+8+1; size_t bestLength = 8; U32 nbCompares = 1U << cParams->searchLog; #ifdef ZSTD_C_PREDICT U32 predictedSmall = *(bt + 2*((current-1)&btMask) + 0); U32 predictedLarge = *(bt + 2*((current-1)&btMask) + 1); predictedSmall += (predictedSmall>0); predictedLarge += (predictedLarge>0); #endif /* ZSTD_C_PREDICT */ DEBUGLOG(8, "ZSTD_insertBt1 (%u)", current); assert(ip <= iend-8); /* required for h calculation */ hashTable[h] = current; /* Update Hash Table */ assert(windowLow > 0); while (nbCompares-- && (matchIndex >= windowLow)) { U32* const nextPtr = bt + 2*(matchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ assert(matchIndex < current); #ifdef ZSTD_C_PREDICT /* note : can create issues when hlog small <= 11 */ const U32* predictPtr = bt + 2*((matchIndex-1) & btMask); /* written this way, as bt is a roll buffer */ if (matchIndex == predictedSmall) { /* no need to check length, result known */ *smallerPtr = matchIndex; if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */ smallerPtr = nextPtr+1; /* new "smaller" => larger of match */ matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ predictedSmall = predictPtr[1] + (predictPtr[1]>0); continue; } if (matchIndex == predictedLarge) { *largerPtr = matchIndex; if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */ largerPtr = nextPtr; matchIndex = nextPtr[0]; predictedLarge = predictPtr[0] + (predictPtr[0]>0); continue; } #endif if (!extDict || (matchIndex+matchLength >= dictLimit)) { assert(matchIndex+matchLength >= dictLimit); /* might be wrong if actually extDict */ match = base + matchIndex; matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend); } else { match = dictBase + matchIndex; matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart); if (matchIndex+matchLength >= dictLimit) match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ } if (matchLength > bestLength) { bestLength = matchLength; if (matchLength > matchEndIdx - matchIndex) matchEndIdx = matchIndex + (U32)matchLength; } if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */ break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt tree */ } if (match[matchLength] < ip[matchLength]) { /* necessarily within buffer */ /* match is smaller than current */ *smallerPtr = matchIndex; /* update smaller idx */ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop searching */ smallerPtr = nextPtr+1; /* new "candidate" => larger than match, which was smaller than target */ matchIndex = nextPtr[1]; /* new matchIndex, larger than previous and closer to current */ } else { /* match is larger than current */ *largerPtr = matchIndex; commonLengthLarger = matchLength; if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop searching */ largerPtr = nextPtr; matchIndex = nextPtr[0]; } } *smallerPtr = *largerPtr = 0; { U32 positions = 0; if (bestLength > 384) positions = MIN(192, (U32)(bestLength - 384)); /* speed optimization */ assert(matchEndIdx > current + 8); return MAX(positions, matchEndIdx - (current + 8)); } } FORCE_INLINE_TEMPLATE void ZSTD_updateTree_internal( ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iend, const U32 mls, const ZSTD_dictMode_e dictMode) { const BYTE* const base = ms->window.base; U32 const target = (U32)(ip - base); U32 idx = ms->nextToUpdate; DEBUGLOG(6, "ZSTD_updateTree_internal, from %u to %u (dictMode:%u)", idx, target, dictMode); while(idx < target) { U32 const forward = ZSTD_insertBt1(ms, base+idx, iend, mls, dictMode == ZSTD_extDict); assert(idx < (U32)(idx + forward)); idx += forward; } assert((size_t)(ip - base) <= (size_t)(U32)(-1)); assert((size_t)(iend - base) <= (size_t)(U32)(-1)); ms->nextToUpdate = target; } void ZSTD_updateTree(ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* iend) { ZSTD_updateTree_internal(ms, ip, iend, ms->cParams.minMatch, ZSTD_noDict); } FORCE_INLINE_TEMPLATE U32 ZSTD_insertBtAndGetAllMatches ( ZSTD_match_t* matches, /* store result (found matches) in this table (presumed large enough) */ ZSTD_matchState_t* ms, U32* nextToUpdate3, const BYTE* const ip, const BYTE* const iLimit, const ZSTD_dictMode_e dictMode, const U32 rep[ZSTD_REP_NUM], U32 const ll0, /* tells if associated literal length is 0 or not. This value must be 0 or 1 */ const U32 lengthToBeat, U32 const mls /* template */) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1); const BYTE* const base = ms->window.base; U32 const current = (U32)(ip-base); U32 const hashLog = cParams->hashLog; U32 const minMatch = (mls==3) ? 3 : 4; U32* const hashTable = ms->hashTable; size_t const h = ZSTD_hashPtr(ip, hashLog, mls); U32 matchIndex = hashTable[h]; U32* const bt = ms->chainTable; U32 const btLog = cParams->chainLog - 1; U32 const btMask= (1U << btLog) - 1; size_t commonLengthSmaller=0, commonLengthLarger=0; const BYTE* const dictBase = ms->window.dictBase; U32 const dictLimit = ms->window.dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; U32 const btLow = (btMask >= current) ? 0 : current - btMask; U32 const windowLow = ZSTD_getLowestMatchIndex(ms, current, cParams->windowLog); U32 const matchLow = windowLow ? windowLow : 1; U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = bt + 2*(current&btMask) + 1; U32 matchEndIdx = current+8+1; /* farthest referenced position of any match => detects repetitive patterns */ U32 dummy32; /* to be nullified at the end */ U32 mnum = 0; U32 nbCompares = 1U << cParams->searchLog; const ZSTD_matchState_t* dms = dictMode == ZSTD_dictMatchState ? ms->dictMatchState : NULL; const ZSTD_compressionParameters* const dmsCParams = dictMode == ZSTD_dictMatchState ? &dms->cParams : NULL; const BYTE* const dmsBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL; const BYTE* const dmsEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; U32 const dmsHighLimit = dictMode == ZSTD_dictMatchState ? (U32)(dmsEnd - dmsBase) : 0; U32 const dmsLowLimit = dictMode == ZSTD_dictMatchState ? dms->window.lowLimit : 0; U32 const dmsIndexDelta = dictMode == ZSTD_dictMatchState ? windowLow - dmsHighLimit : 0; U32 const dmsHashLog = dictMode == ZSTD_dictMatchState ? dmsCParams->hashLog : hashLog; U32 const dmsBtLog = dictMode == ZSTD_dictMatchState ? dmsCParams->chainLog - 1 : btLog; U32 const dmsBtMask = dictMode == ZSTD_dictMatchState ? (1U << dmsBtLog) - 1 : 0; U32 const dmsBtLow = dictMode == ZSTD_dictMatchState && dmsBtMask < dmsHighLimit - dmsLowLimit ? dmsHighLimit - dmsBtMask : dmsLowLimit; size_t bestLength = lengthToBeat-1; DEBUGLOG(8, "ZSTD_insertBtAndGetAllMatches: current=%u", current); /* check repCode */ assert(ll0 <= 1); /* necessarily 1 or 0 */ { U32 const lastR = ZSTD_REP_NUM + ll0; U32 repCode; for (repCode = ll0; repCode < lastR; repCode++) { U32 const repOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode]; U32 const repIndex = current - repOffset; U32 repLen = 0; assert(current >= dictLimit); if (repOffset-1 /* intentional overflow, discards 0 and -1 */ < current-dictLimit) { /* equivalent to `current > repIndex >= dictLimit` */ /* We must validate the repcode offset because when we're using a dictionary the * valid offset range shrinks when the dictionary goes out of bounds. */ if ((repIndex >= windowLow) & (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(ip - repOffset, minMatch))) { repLen = (U32)ZSTD_count(ip+minMatch, ip+minMatch-repOffset, iLimit) + minMatch; } } else { /* repIndex < dictLimit || repIndex >= current */ const BYTE* const repMatch = dictMode == ZSTD_dictMatchState ? dmsBase + repIndex - dmsIndexDelta : dictBase + repIndex; assert(current >= windowLow); if ( dictMode == ZSTD_extDict && ( ((repOffset-1) /*intentional overflow*/ < current - windowLow) /* equivalent to `current > repIndex >= windowLow` */ & (((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */) && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) { repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dictEnd, prefixStart) + minMatch; } if (dictMode == ZSTD_dictMatchState && ( ((repOffset-1) /*intentional overflow*/ < current - (dmsLowLimit + dmsIndexDelta)) /* equivalent to `current > repIndex >= dmsLowLimit` */ & ((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */ && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) { repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dmsEnd, prefixStart) + minMatch; } } /* save longer solution */ if (repLen > bestLength) { DEBUGLOG(8, "found repCode %u (ll0:%u, offset:%u) of length %u", repCode, ll0, repOffset, repLen); bestLength = repLen; matches[mnum].off = repCode - ll0; matches[mnum].len = (U32)repLen; mnum++; if ( (repLen > sufficient_len) | (ip+repLen == iLimit) ) { /* best possible */ return mnum; } } } } /* HC3 match finder */ if ((mls == 3) /*static*/ && (bestLength < mls)) { U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, nextToUpdate3, ip); if ((matchIndex3 >= matchLow) & (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) { size_t mlen; if ((dictMode == ZSTD_noDict) /*static*/ || (dictMode == ZSTD_dictMatchState) /*static*/ || (matchIndex3 >= dictLimit)) { const BYTE* const match = base + matchIndex3; mlen = ZSTD_count(ip, match, iLimit); } else { const BYTE* const match = dictBase + matchIndex3; mlen = ZSTD_count_2segments(ip, match, iLimit, dictEnd, prefixStart); } /* save best solution */ if (mlen >= mls /* == 3 > bestLength */) { DEBUGLOG(8, "found small match with hlog3, of length %u", (U32)mlen); bestLength = mlen; assert(current > matchIndex3); assert(mnum==0); /* no prior solution */ matches[0].off = (current - matchIndex3) + ZSTD_REP_MOVE; matches[0].len = (U32)mlen; mnum = 1; if ( (mlen > sufficient_len) | (ip+mlen == iLimit) ) { /* best possible length */ ms->nextToUpdate = current+1; /* skip insertion */ return 1; } } } /* no dictMatchState lookup: dicts don't have a populated HC3 table */ } hashTable[h] = current; /* Update Hash Table */ while (nbCompares-- && (matchIndex >= matchLow)) { U32* const nextPtr = bt + 2*(matchIndex & btMask); const BYTE* match; size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ assert(current > matchIndex); if ((dictMode == ZSTD_noDict) || (dictMode == ZSTD_dictMatchState) || (matchIndex+matchLength >= dictLimit)) { assert(matchIndex+matchLength >= dictLimit); /* ensure the condition is correct when !extDict */ match = base + matchIndex; if (matchIndex >= dictLimit) assert(memcmp(match, ip, matchLength) == 0); /* ensure early section of match is equal as expected */ matchLength += ZSTD_count(ip+matchLength, match+matchLength, iLimit); } else { match = dictBase + matchIndex; assert(memcmp(match, ip, matchLength) == 0); /* ensure early section of match is equal as expected */ matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iLimit, dictEnd, prefixStart); if (matchIndex+matchLength >= dictLimit) match = base + matchIndex; /* prepare for match[matchLength] read */ } if (matchLength > bestLength) { DEBUGLOG(8, "found match of length %u at distance %u (offCode=%u)", (U32)matchLength, current - matchIndex, current - matchIndex + ZSTD_REP_MOVE); assert(matchEndIdx > matchIndex); if (matchLength > matchEndIdx - matchIndex) matchEndIdx = matchIndex + (U32)matchLength; bestLength = matchLength; matches[mnum].off = (current - matchIndex) + ZSTD_REP_MOVE; matches[mnum].len = (U32)matchLength; mnum++; if ( (matchLength > ZSTD_OPT_NUM) | (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) { if (dictMode == ZSTD_dictMatchState) nbCompares = 0; /* break should also skip searching dms */ break; /* drop, to preserve bt consistency (miss a little bit of compression) */ } } if (match[matchLength] < ip[matchLength]) { /* match smaller than current */ *smallerPtr = matchIndex; /* update smaller idx */ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */ smallerPtr = nextPtr+1; /* new candidate => larger than match, which was smaller than current */ matchIndex = nextPtr[1]; /* new matchIndex, larger than previous, closer to current */ } else { *largerPtr = matchIndex; commonLengthLarger = matchLength; if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */ largerPtr = nextPtr; matchIndex = nextPtr[0]; } } *smallerPtr = *largerPtr = 0; if (dictMode == ZSTD_dictMatchState && nbCompares) { size_t const dmsH = ZSTD_hashPtr(ip, dmsHashLog, mls); U32 dictMatchIndex = dms->hashTable[dmsH]; const U32* const dmsBt = dms->chainTable; commonLengthSmaller = commonLengthLarger = 0; while (nbCompares-- && (dictMatchIndex > dmsLowLimit)) { const U32* const nextPtr = dmsBt + 2*(dictMatchIndex & dmsBtMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ const BYTE* match = dmsBase + dictMatchIndex; matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iLimit, dmsEnd, prefixStart); if (dictMatchIndex+matchLength >= dmsHighLimit) match = base + dictMatchIndex + dmsIndexDelta; /* to prepare for next usage of match[matchLength] */ if (matchLength > bestLength) { matchIndex = dictMatchIndex + dmsIndexDelta; DEBUGLOG(8, "found dms match of length %u at distance %u (offCode=%u)", (U32)matchLength, current - matchIndex, current - matchIndex + ZSTD_REP_MOVE); if (matchLength > matchEndIdx - matchIndex) matchEndIdx = matchIndex + (U32)matchLength; bestLength = matchLength; matches[mnum].off = (current - matchIndex) + ZSTD_REP_MOVE; matches[mnum].len = (U32)matchLength; mnum++; if ( (matchLength > ZSTD_OPT_NUM) | (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) { break; /* drop, to guarantee consistency (miss a little bit of compression) */ } } if (dictMatchIndex <= dmsBtLow) { break; } /* beyond tree size, stop the search */ if (match[matchLength] < ip[matchLength]) { commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ dictMatchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ } else { /* match is larger than current */ commonLengthLarger = matchLength; dictMatchIndex = nextPtr[0]; } } } assert(matchEndIdx > current+8); ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ return mnum; } FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches ( ZSTD_match_t* matches, /* store result (match found, increasing size) in this table */ ZSTD_matchState_t* ms, U32* nextToUpdate3, const BYTE* ip, const BYTE* const iHighLimit, const ZSTD_dictMode_e dictMode, const U32 rep[ZSTD_REP_NUM], U32 const ll0, U32 const lengthToBeat) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32 const matchLengthSearch = cParams->minMatch; DEBUGLOG(8, "ZSTD_BtGetAllMatches"); if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */ ZSTD_updateTree_internal(ms, ip, iHighLimit, matchLengthSearch, dictMode); switch(matchLengthSearch) { case 3 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 3); default : case 4 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 4); case 5 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 5); case 7 : case 6 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 6); } } /*-******************************* * Optimal parser *********************************/ static U32 ZSTD_totalLen(ZSTD_optimal_t sol) { return sol.litlen + sol.mlen; } #if 0 /* debug */ static void listStats(const U32* table, int lastEltID) { int const nbElts = lastEltID + 1; int enb; for (enb=0; enb < nbElts; enb++) { (void)table; /* RAWLOG(2, "%3i:%3i, ", enb, table[enb]); */ RAWLOG(2, "%4i,", table[enb]); } RAWLOG(2, " \n"); } #endif FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize, const int optLevel, const ZSTD_dictMode_e dictMode) { optState_t* const optStatePtr = &ms->opt; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; const BYTE* const base = ms->window.base; const BYTE* const prefixStart = base + ms->window.dictLimit; const ZSTD_compressionParameters* const cParams = &ms->cParams; U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1); U32 const minMatch = (cParams->minMatch == 3) ? 3 : 4; U32 nextToUpdate3 = ms->nextToUpdate; ZSTD_optimal_t* const opt = optStatePtr->priceTable; ZSTD_match_t* const matches = optStatePtr->matchTable; ZSTD_optimal_t lastSequence; /* init */ DEBUGLOG(5, "ZSTD_compressBlock_opt_generic: current=%u, prefix=%u, nextToUpdate=%u", (U32)(ip - base), ms->window.dictLimit, ms->nextToUpdate); assert(optLevel <= 2); ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize, optLevel); ip += (ip==prefixStart); /* Match Loop */ while (ip < ilimit) { U32 cur, last_pos = 0; /* find first match */ { U32 const litlen = (U32)(ip - anchor); U32 const ll0 = !litlen; U32 const nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, ip, iend, dictMode, rep, ll0, minMatch); if (!nbMatches) { ip++; continue; } /* initialize opt[0] */ { U32 i ; for (i=0; i immediate encoding */ { U32 const maxML = matches[nbMatches-1].len; U32 const maxOffset = matches[nbMatches-1].off; DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffCode=%u at cPos=%u => start new series", nbMatches, maxML, maxOffset, (U32)(ip-prefixStart)); if (maxML > sufficient_len) { lastSequence.litlen = litlen; lastSequence.mlen = maxML; lastSequence.off = maxOffset; DEBUGLOG(6, "large match (%u>%u), immediate encoding", maxML, sufficient_len); cur = 0; last_pos = ZSTD_totalLen(lastSequence); goto _shortestPath; } } /* set prices for first matches starting position == 0 */ { U32 const literalsPrice = opt[0].price + ZSTD_litLengthPrice(0, optStatePtr, optLevel); U32 pos; U32 matchNb; for (pos = 1; pos < minMatch; pos++) { opt[pos].price = ZSTD_MAX_PRICE; /* mlen, litlen and price will be fixed during forward scanning */ } for (matchNb = 0; matchNb < nbMatches; matchNb++) { U32 const offset = matches[matchNb].off; U32 const end = matches[matchNb].len; for ( ; pos <= end ; pos++ ) { U32 const matchPrice = ZSTD_getMatchPrice(offset, pos, optStatePtr, optLevel); U32 const sequencePrice = literalsPrice + matchPrice; DEBUGLOG(7, "rPos:%u => set initial price : %.2f", pos, ZSTD_fCost(sequencePrice)); opt[pos].mlen = pos; opt[pos].off = offset; opt[pos].litlen = litlen; opt[pos].price = sequencePrice; } } last_pos = pos-1; } } /* check further positions */ for (cur = 1; cur <= last_pos; cur++) { const BYTE* const inr = ip + cur; assert(cur < ZSTD_OPT_NUM); DEBUGLOG(7, "cPos:%zi==rPos:%u", inr-istart, cur) /* Fix current position with one literal if cheaper */ { U32 const litlen = (opt[cur-1].mlen == 0) ? opt[cur-1].litlen + 1 : 1; int const price = opt[cur-1].price + ZSTD_rawLiteralsCost(ip+cur-1, 1, optStatePtr, optLevel) + ZSTD_litLengthPrice(litlen, optStatePtr, optLevel) - ZSTD_litLengthPrice(litlen-1, optStatePtr, optLevel); assert(price < 1000000000); /* overflow check */ if (price <= opt[cur].price) { DEBUGLOG(7, "cPos:%zi==rPos:%u : better price (%.2f<=%.2f) using literal (ll==%u) (hist:%u,%u,%u)", inr-istart, cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price), litlen, opt[cur-1].rep[0], opt[cur-1].rep[1], opt[cur-1].rep[2]); opt[cur].mlen = 0; opt[cur].off = 0; opt[cur].litlen = litlen; opt[cur].price = price; } else { DEBUGLOG(7, "cPos:%zi==rPos:%u : literal would cost more (%.2f>%.2f) (hist:%u,%u,%u)", inr-istart, cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price), opt[cur].rep[0], opt[cur].rep[1], opt[cur].rep[2]); } } /* Set the repcodes of the current position. We must do it here * because we rely on the repcodes of the 2nd to last sequence being * correct to set the next chunks repcodes during the backward * traversal. */ ZSTD_STATIC_ASSERT(sizeof(opt[cur].rep) == sizeof(repcodes_t)); assert(cur >= opt[cur].mlen); if (opt[cur].mlen != 0) { U32 const prev = cur - opt[cur].mlen; repcodes_t newReps = ZSTD_updateRep(opt[prev].rep, opt[cur].off, opt[cur].litlen==0); memcpy(opt[cur].rep, &newReps, sizeof(repcodes_t)); } else { memcpy(opt[cur].rep, opt[cur - 1].rep, sizeof(repcodes_t)); } /* last match must start at a minimum distance of 8 from oend */ if (inr > ilimit) continue; if (cur == last_pos) break; if ( (optLevel==0) /*static_test*/ && (opt[cur+1].price <= opt[cur].price + (BITCOST_MULTIPLIER/2)) ) { DEBUGLOG(7, "move to next rPos:%u : price is <=", cur+1); continue; /* skip unpromising positions; about ~+6% speed, -0.01 ratio */ } { U32 const ll0 = (opt[cur].mlen != 0); U32 const litlen = (opt[cur].mlen == 0) ? opt[cur].litlen : 0; U32 const previousPrice = opt[cur].price; U32 const basePrice = previousPrice + ZSTD_litLengthPrice(0, optStatePtr, optLevel); U32 const nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, inr, iend, dictMode, opt[cur].rep, ll0, minMatch); U32 matchNb; if (!nbMatches) { DEBUGLOG(7, "rPos:%u : no match found", cur); continue; } { U32 const maxML = matches[nbMatches-1].len; DEBUGLOG(7, "cPos:%zi==rPos:%u, found %u matches, of maxLength=%u", inr-istart, cur, nbMatches, maxML); if ( (maxML > sufficient_len) || (cur + maxML >= ZSTD_OPT_NUM) ) { lastSequence.mlen = maxML; lastSequence.off = matches[nbMatches-1].off; lastSequence.litlen = litlen; cur -= (opt[cur].mlen==0) ? opt[cur].litlen : 0; /* last sequence is actually only literals, fix cur to last match - note : may underflow, in which case, it's first sequence, and it's okay */ last_pos = cur + ZSTD_totalLen(lastSequence); if (cur > ZSTD_OPT_NUM) cur = 0; /* underflow => first match */ goto _shortestPath; } } /* set prices using matches found at position == cur */ for (matchNb = 0; matchNb < nbMatches; matchNb++) { U32 const offset = matches[matchNb].off; U32 const lastML = matches[matchNb].len; U32 const startML = (matchNb>0) ? matches[matchNb-1].len+1 : minMatch; U32 mlen; DEBUGLOG(7, "testing match %u => offCode=%4u, mlen=%2u, llen=%2u", matchNb, matches[matchNb].off, lastML, litlen); for (mlen = lastML; mlen >= startML; mlen--) { /* scan downward */ U32 const pos = cur + mlen; int const price = basePrice + ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel); if ((pos > last_pos) || (price < opt[pos].price)) { DEBUGLOG(7, "rPos:%u (ml=%2u) => new better price (%.2f<%.2f)", pos, mlen, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price)); while (last_pos < pos) { opt[last_pos+1].price = ZSTD_MAX_PRICE; last_pos++; } /* fill empty positions */ opt[pos].mlen = mlen; opt[pos].off = offset; opt[pos].litlen = litlen; opt[pos].price = price; } else { DEBUGLOG(7, "rPos:%u (ml=%2u) => new price is worse (%.2f>=%.2f)", pos, mlen, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price)); if (optLevel==0) break; /* early update abort; gets ~+10% speed for about -0.01 ratio loss */ } } } } } /* for (cur = 1; cur <= last_pos; cur++) */ lastSequence = opt[last_pos]; cur = last_pos > ZSTD_totalLen(lastSequence) ? last_pos - ZSTD_totalLen(lastSequence) : 0; /* single sequence, and it starts before `ip` */ assert(cur < ZSTD_OPT_NUM); /* control overflow*/ _shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */ assert(opt[0].mlen == 0); /* Set the next chunk's repcodes based on the repcodes of the beginning * of the last match, and the last sequence. This avoids us having to * update them while traversing the sequences. */ if (lastSequence.mlen != 0) { repcodes_t reps = ZSTD_updateRep(opt[cur].rep, lastSequence.off, lastSequence.litlen==0); memcpy(rep, &reps, sizeof(reps)); } else { memcpy(rep, opt[cur].rep, sizeof(repcodes_t)); } { U32 const storeEnd = cur + 1; U32 storeStart = storeEnd; U32 seqPos = cur; DEBUGLOG(6, "start reverse traversal (last_pos:%u, cur:%u)", last_pos, cur); (void)last_pos; assert(storeEnd < ZSTD_OPT_NUM); DEBUGLOG(6, "last sequence copied into pos=%u (llen=%u,mlen=%u,ofc=%u)", storeEnd, lastSequence.litlen, lastSequence.mlen, lastSequence.off); opt[storeEnd] = lastSequence; while (seqPos > 0) { U32 const backDist = ZSTD_totalLen(opt[seqPos]); storeStart--; DEBUGLOG(6, "sequence from rPos=%u copied into pos=%u (llen=%u,mlen=%u,ofc=%u)", seqPos, storeStart, opt[seqPos].litlen, opt[seqPos].mlen, opt[seqPos].off); opt[storeStart] = opt[seqPos]; seqPos = (seqPos > backDist) ? seqPos - backDist : 0; } /* save sequences */ DEBUGLOG(6, "sending selected sequences into seqStore") { U32 storePos; for (storePos=storeStart; storePos <= storeEnd; storePos++) { U32 const llen = opt[storePos].litlen; U32 const mlen = opt[storePos].mlen; U32 const offCode = opt[storePos].off; U32 const advance = llen + mlen; DEBUGLOG(6, "considering seq starting at %zi, llen=%u, mlen=%u", anchor - istart, (unsigned)llen, (unsigned)mlen); if (mlen==0) { /* only literals => must be last "sequence", actually starting a new stream of sequences */ assert(storePos == storeEnd); /* must be last sequence */ ip = anchor + llen; /* last "sequence" is a bunch of literals => don't progress anchor */ continue; /* will finish */ } assert(anchor + llen <= iend); ZSTD_updateStats(optStatePtr, llen, anchor, offCode, mlen); ZSTD_storeSeq(seqStore, llen, anchor, iend, offCode, mlen-MINMATCH); anchor += advance; ip = anchor; } } ZSTD_setBasePrices(optStatePtr, optLevel); } } /* while (ip < ilimit) */ /* Return the last literals size */ return (size_t)(iend - anchor); } size_t ZSTD_compressBlock_btopt( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock_btopt"); return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /*optLevel*/, ZSTD_noDict); } /* used in 2-pass strategy */ static U32 ZSTD_upscaleStat(unsigned* table, U32 lastEltIndex, int bonus) { U32 s, sum=0; assert(ZSTD_FREQ_DIV+bonus >= 0); for (s=0; slitSum = ZSTD_upscaleStat(optPtr->litFreq, MaxLit, 0); optPtr->litLengthSum = ZSTD_upscaleStat(optPtr->litLengthFreq, MaxLL, 0); optPtr->matchLengthSum = ZSTD_upscaleStat(optPtr->matchLengthFreq, MaxML, 0); optPtr->offCodeSum = ZSTD_upscaleStat(optPtr->offCodeFreq, MaxOff, 0); } /* ZSTD_initStats_ultra(): * make a first compression pass, just to seed stats with more accurate starting values. * only works on first block, with no dictionary and no ldm. * this function cannot error, hence its contract must be respected. */ static void ZSTD_initStats_ultra(ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { U32 tmpRep[ZSTD_REP_NUM]; /* updated rep codes will sink here */ memcpy(tmpRep, rep, sizeof(tmpRep)); DEBUGLOG(4, "ZSTD_initStats_ultra (srcSize=%zu)", srcSize); assert(ms->opt.litLengthSum == 0); /* first block */ assert(seqStore->sequences == seqStore->sequencesStart); /* no ldm */ assert(ms->window.dictLimit == ms->window.lowLimit); /* no dictionary */ assert(ms->window.dictLimit - ms->nextToUpdate <= 1); /* no prefix (note: intentional overflow, defined as 2-complement) */ ZSTD_compressBlock_opt_generic(ms, seqStore, tmpRep, src, srcSize, 2 /*optLevel*/, ZSTD_noDict); /* generate stats into ms->opt*/ /* invalidate first scan from history */ ZSTD_resetSeqStore(seqStore); ms->window.base -= srcSize; ms->window.dictLimit += (U32)srcSize; ms->window.lowLimit = ms->window.dictLimit; ms->nextToUpdate = ms->window.dictLimit; /* re-inforce weight of collected statistics */ ZSTD_upscaleStats(&ms->opt); } size_t ZSTD_compressBlock_btultra( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock_btultra (srcSize=%zu)", srcSize); return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_noDict); } size_t ZSTD_compressBlock_btultra2( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { U32 const current = (U32)((const BYTE*)src - ms->window.base); DEBUGLOG(5, "ZSTD_compressBlock_btultra2 (srcSize=%zu)", srcSize); /* 2-pass strategy: * this strategy makes a first pass over first block to collect statistics * and seed next round's statistics with it. * After 1st pass, function forgets everything, and starts a new block. * Consequently, this can only work if no data has been previously loaded in tables, * aka, no dictionary, no prefix, no ldm preprocessing. * The compression ratio gain is generally small (~0.5% on first block), * the cost is 2x cpu time on first block. */ assert(srcSize <= ZSTD_BLOCKSIZE_MAX); if ( (ms->opt.litLengthSum==0) /* first block */ && (seqStore->sequences == seqStore->sequencesStart) /* no ldm */ && (ms->window.dictLimit == ms->window.lowLimit) /* no dictionary */ && (current == ms->window.dictLimit) /* start of frame, nothing already loaded nor skipped */ && (srcSize > ZSTD_PREDEF_THRESHOLD) ) { ZSTD_initStats_ultra(ms, seqStore, rep, src, srcSize); } return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_noDict); } size_t ZSTD_compressBlock_btopt_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /*optLevel*/, ZSTD_dictMatchState); } size_t ZSTD_compressBlock_btultra_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_dictMatchState); } size_t ZSTD_compressBlock_btopt_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /*optLevel*/, ZSTD_extDict); } size_t ZSTD_compressBlock_btultra_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_extDict); } /* note : no btultra2 variant for extDict nor dictMatchState, * because btultra2 is not meant to work with dictionaries * and is only specific for the first block (no prefix) */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstd_opt.h0000644000076500000240000000373514601576577023545 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_OPT_H #define ZSTD_OPT_H #if defined (__cplusplus) extern "C" { #endif #include "zstd_compress_internal.h" /* used in ZSTD_loadDictionaryContent() */ void ZSTD_updateTree(ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* iend); size_t ZSTD_compressBlock_btopt( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_btultra( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_btultra2( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_btopt_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_btultra_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_btopt_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); size_t ZSTD_compressBlock_btultra_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); /* note : no btultra2 variant for extDict nor dictMatchState, * because btultra2 is not meant to work with dictionaries * and is only specific for the first block (no prefix) */ #if defined (__cplusplus) } #endif #endif /* ZSTD_OPT_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstdmt_compress.c0000644000076500000240000026732214601576577025136 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* ====== Compiler specifics ====== */ #if defined(_MSC_VER) # pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */ #endif /* ====== Constants ====== */ #define ZSTDMT_OVERLAPLOG_DEFAULT 0 /* ====== Dependencies ====== */ #include /* memcpy, memset */ #include /* INT_MAX, UINT_MAX */ #include "../common/mem.h" /* MEM_STATIC */ #include "../common/pool.h" /* threadpool */ #include "../common/threading.h" /* mutex */ #include "zstd_compress_internal.h" /* MIN, ERROR, ZSTD_*, ZSTD_highbit32 */ #include "zstd_ldm.h" #include "zstdmt_compress.h" /* Guards code to support resizing the SeqPool. * We will want to resize the SeqPool to save memory in the future. * Until then, comment the code out since it is unused. */ #define ZSTD_RESIZE_SEQPOOL 0 /* ====== Debug ====== */ #if defined(DEBUGLEVEL) && (DEBUGLEVEL>=2) \ && !defined(_MSC_VER) \ && !defined(__MINGW32__) # include # include # include # define DEBUG_PRINTHEX(l,p,n) { \ unsigned debug_u; \ for (debug_u=0; debug_u<(n); debug_u++) \ RAWLOG(l, "%02X ", ((const unsigned char*)(p))[debug_u]); \ RAWLOG(l, " \n"); \ } static unsigned long long GetCurrentClockTimeMicroseconds(void) { static clock_t _ticksPerSecond = 0; if (_ticksPerSecond <= 0) _ticksPerSecond = sysconf(_SC_CLK_TCK); { struct tms junk; clock_t newTicks = (clock_t) times(&junk); return ((((unsigned long long)newTicks)*(1000000))/_ticksPerSecond); } } #define MUTEX_WAIT_TIME_DLEVEL 6 #define ZSTD_PTHREAD_MUTEX_LOCK(mutex) { \ if (DEBUGLEVEL >= MUTEX_WAIT_TIME_DLEVEL) { \ unsigned long long const beforeTime = GetCurrentClockTimeMicroseconds(); \ ZSTD_pthread_mutex_lock(mutex); \ { unsigned long long const afterTime = GetCurrentClockTimeMicroseconds(); \ unsigned long long const elapsedTime = (afterTime-beforeTime); \ if (elapsedTime > 1000) { /* or whatever threshold you like; I'm using 1 millisecond here */ \ DEBUGLOG(MUTEX_WAIT_TIME_DLEVEL, "Thread took %llu microseconds to acquire mutex %s \n", \ elapsedTime, #mutex); \ } } \ } else { \ ZSTD_pthread_mutex_lock(mutex); \ } \ } #else # define ZSTD_PTHREAD_MUTEX_LOCK(m) ZSTD_pthread_mutex_lock(m) # define DEBUG_PRINTHEX(l,p,n) {} #endif /* ===== Buffer Pool ===== */ /* a single Buffer Pool can be invoked from multiple threads in parallel */ typedef struct buffer_s { void* start; size_t capacity; } buffer_t; static const buffer_t g_nullBuffer = { NULL, 0 }; typedef struct ZSTDMT_bufferPool_s { ZSTD_pthread_mutex_t poolMutex; size_t bufferSize; unsigned totalBuffers; unsigned nbBuffers; ZSTD_customMem cMem; buffer_t bTable[1]; /* variable size */ } ZSTDMT_bufferPool; static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbWorkers, ZSTD_customMem cMem) { unsigned const maxNbBuffers = 2*nbWorkers + 3; ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)ZSTD_calloc( sizeof(ZSTDMT_bufferPool) + (maxNbBuffers-1) * sizeof(buffer_t), cMem); if (bufPool==NULL) return NULL; if (ZSTD_pthread_mutex_init(&bufPool->poolMutex, NULL)) { ZSTD_free(bufPool, cMem); return NULL; } bufPool->bufferSize = 64 KB; bufPool->totalBuffers = maxNbBuffers; bufPool->nbBuffers = 0; bufPool->cMem = cMem; return bufPool; } static void ZSTDMT_freeBufferPool(ZSTDMT_bufferPool* bufPool) { unsigned u; DEBUGLOG(3, "ZSTDMT_freeBufferPool (address:%08X)", (U32)(size_t)bufPool); if (!bufPool) return; /* compatibility with free on NULL */ for (u=0; utotalBuffers; u++) { DEBUGLOG(4, "free buffer %2u (address:%08X)", u, (U32)(size_t)bufPool->bTable[u].start); ZSTD_free(bufPool->bTable[u].start, bufPool->cMem); } ZSTD_pthread_mutex_destroy(&bufPool->poolMutex); ZSTD_free(bufPool, bufPool->cMem); } /* only works at initialization, not during compression */ static size_t ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool* bufPool) { size_t const poolSize = sizeof(*bufPool) + (bufPool->totalBuffers - 1) * sizeof(buffer_t); unsigned u; size_t totalBufferSize = 0; ZSTD_pthread_mutex_lock(&bufPool->poolMutex); for (u=0; utotalBuffers; u++) totalBufferSize += bufPool->bTable[u].capacity; ZSTD_pthread_mutex_unlock(&bufPool->poolMutex); return poolSize + totalBufferSize; } /* ZSTDMT_setBufferSize() : * all future buffers provided by this buffer pool will have _at least_ this size * note : it's better for all buffers to have same size, * as they become freely interchangeable, reducing malloc/free usages and memory fragmentation */ static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* const bufPool, size_t const bSize) { ZSTD_pthread_mutex_lock(&bufPool->poolMutex); DEBUGLOG(4, "ZSTDMT_setBufferSize: bSize = %u", (U32)bSize); bufPool->bufferSize = bSize; ZSTD_pthread_mutex_unlock(&bufPool->poolMutex); } static ZSTDMT_bufferPool* ZSTDMT_expandBufferPool(ZSTDMT_bufferPool* srcBufPool, U32 nbWorkers) { unsigned const maxNbBuffers = 2*nbWorkers + 3; if (srcBufPool==NULL) return NULL; if (srcBufPool->totalBuffers >= maxNbBuffers) /* good enough */ return srcBufPool; /* need a larger buffer pool */ { ZSTD_customMem const cMem = srcBufPool->cMem; size_t const bSize = srcBufPool->bufferSize; /* forward parameters */ ZSTDMT_bufferPool* newBufPool; ZSTDMT_freeBufferPool(srcBufPool); newBufPool = ZSTDMT_createBufferPool(nbWorkers, cMem); if (newBufPool==NULL) return newBufPool; ZSTDMT_setBufferSize(newBufPool, bSize); return newBufPool; } } /** ZSTDMT_getBuffer() : * assumption : bufPool must be valid * @return : a buffer, with start pointer and size * note: allocation may fail, in this case, start==NULL and size==0 */ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool) { size_t const bSize = bufPool->bufferSize; DEBUGLOG(5, "ZSTDMT_getBuffer: bSize = %u", (U32)bufPool->bufferSize); ZSTD_pthread_mutex_lock(&bufPool->poolMutex); if (bufPool->nbBuffers) { /* try to use an existing buffer */ buffer_t const buf = bufPool->bTable[--(bufPool->nbBuffers)]; size_t const availBufferSize = buf.capacity; bufPool->bTable[bufPool->nbBuffers] = g_nullBuffer; if ((availBufferSize >= bSize) & ((availBufferSize>>3) <= bSize)) { /* large enough, but not too much */ DEBUGLOG(5, "ZSTDMT_getBuffer: provide buffer %u of size %u", bufPool->nbBuffers, (U32)buf.capacity); ZSTD_pthread_mutex_unlock(&bufPool->poolMutex); return buf; } /* size conditions not respected : scratch this buffer, create new one */ DEBUGLOG(5, "ZSTDMT_getBuffer: existing buffer does not meet size conditions => freeing"); ZSTD_free(buf.start, bufPool->cMem); } ZSTD_pthread_mutex_unlock(&bufPool->poolMutex); /* create new buffer */ DEBUGLOG(5, "ZSTDMT_getBuffer: create a new buffer"); { buffer_t buffer; void* const start = ZSTD_malloc(bSize, bufPool->cMem); buffer.start = start; /* note : start can be NULL if malloc fails ! */ buffer.capacity = (start==NULL) ? 0 : bSize; if (start==NULL) { DEBUGLOG(5, "ZSTDMT_getBuffer: buffer allocation failure !!"); } else { DEBUGLOG(5, "ZSTDMT_getBuffer: created buffer of size %u", (U32)bSize); } return buffer; } } #if ZSTD_RESIZE_SEQPOOL /** ZSTDMT_resizeBuffer() : * assumption : bufPool must be valid * @return : a buffer that is at least the buffer pool buffer size. * If a reallocation happens, the data in the input buffer is copied. */ static buffer_t ZSTDMT_resizeBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buffer) { size_t const bSize = bufPool->bufferSize; if (buffer.capacity < bSize) { void* const start = ZSTD_malloc(bSize, bufPool->cMem); buffer_t newBuffer; newBuffer.start = start; newBuffer.capacity = start == NULL ? 0 : bSize; if (start != NULL) { assert(newBuffer.capacity >= buffer.capacity); memcpy(newBuffer.start, buffer.start, buffer.capacity); DEBUGLOG(5, "ZSTDMT_resizeBuffer: created buffer of size %u", (U32)bSize); return newBuffer; } DEBUGLOG(5, "ZSTDMT_resizeBuffer: buffer allocation failure !!"); } return buffer; } #endif /* store buffer for later re-use, up to pool capacity */ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf) { DEBUGLOG(5, "ZSTDMT_releaseBuffer"); if (buf.start == NULL) return; /* compatible with release on NULL */ ZSTD_pthread_mutex_lock(&bufPool->poolMutex); if (bufPool->nbBuffers < bufPool->totalBuffers) { bufPool->bTable[bufPool->nbBuffers++] = buf; /* stored for later use */ DEBUGLOG(5, "ZSTDMT_releaseBuffer: stored buffer of size %u in slot %u", (U32)buf.capacity, (U32)(bufPool->nbBuffers-1)); ZSTD_pthread_mutex_unlock(&bufPool->poolMutex); return; } ZSTD_pthread_mutex_unlock(&bufPool->poolMutex); /* Reached bufferPool capacity (should not happen) */ DEBUGLOG(5, "ZSTDMT_releaseBuffer: pool capacity reached => freeing "); ZSTD_free(buf.start, bufPool->cMem); } /* ===== Seq Pool Wrapper ====== */ static rawSeqStore_t kNullRawSeqStore = {NULL, 0, 0, 0}; typedef ZSTDMT_bufferPool ZSTDMT_seqPool; static size_t ZSTDMT_sizeof_seqPool(ZSTDMT_seqPool* seqPool) { return ZSTDMT_sizeof_bufferPool(seqPool); } static rawSeqStore_t bufferToSeq(buffer_t buffer) { rawSeqStore_t seq = {NULL, 0, 0, 0}; seq.seq = (rawSeq*)buffer.start; seq.capacity = buffer.capacity / sizeof(rawSeq); return seq; } static buffer_t seqToBuffer(rawSeqStore_t seq) { buffer_t buffer; buffer.start = seq.seq; buffer.capacity = seq.capacity * sizeof(rawSeq); return buffer; } static rawSeqStore_t ZSTDMT_getSeq(ZSTDMT_seqPool* seqPool) { if (seqPool->bufferSize == 0) { return kNullRawSeqStore; } return bufferToSeq(ZSTDMT_getBuffer(seqPool)); } #if ZSTD_RESIZE_SEQPOOL static rawSeqStore_t ZSTDMT_resizeSeq(ZSTDMT_seqPool* seqPool, rawSeqStore_t seq) { return bufferToSeq(ZSTDMT_resizeBuffer(seqPool, seqToBuffer(seq))); } #endif static void ZSTDMT_releaseSeq(ZSTDMT_seqPool* seqPool, rawSeqStore_t seq) { ZSTDMT_releaseBuffer(seqPool, seqToBuffer(seq)); } static void ZSTDMT_setNbSeq(ZSTDMT_seqPool* const seqPool, size_t const nbSeq) { ZSTDMT_setBufferSize(seqPool, nbSeq * sizeof(rawSeq)); } static ZSTDMT_seqPool* ZSTDMT_createSeqPool(unsigned nbWorkers, ZSTD_customMem cMem) { ZSTDMT_seqPool* const seqPool = ZSTDMT_createBufferPool(nbWorkers, cMem); if (seqPool == NULL) return NULL; ZSTDMT_setNbSeq(seqPool, 0); return seqPool; } static void ZSTDMT_freeSeqPool(ZSTDMT_seqPool* seqPool) { ZSTDMT_freeBufferPool(seqPool); } static ZSTDMT_seqPool* ZSTDMT_expandSeqPool(ZSTDMT_seqPool* pool, U32 nbWorkers) { return ZSTDMT_expandBufferPool(pool, nbWorkers); } /* ===== CCtx Pool ===== */ /* a single CCtx Pool can be invoked from multiple threads in parallel */ typedef struct { ZSTD_pthread_mutex_t poolMutex; int totalCCtx; int availCCtx; ZSTD_customMem cMem; ZSTD_CCtx* cctx[1]; /* variable size */ } ZSTDMT_CCtxPool; /* note : all CCtx borrowed from the pool should be released back to the pool _before_ freeing the pool */ static void ZSTDMT_freeCCtxPool(ZSTDMT_CCtxPool* pool) { int cid; for (cid=0; cidtotalCCtx; cid++) ZSTD_freeCCtx(pool->cctx[cid]); /* note : compatible with free on NULL */ ZSTD_pthread_mutex_destroy(&pool->poolMutex); ZSTD_free(pool, pool->cMem); } /* ZSTDMT_createCCtxPool() : * implies nbWorkers >= 1 , checked by caller ZSTDMT_createCCtx() */ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(int nbWorkers, ZSTD_customMem cMem) { ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) ZSTD_calloc( sizeof(ZSTDMT_CCtxPool) + (nbWorkers-1)*sizeof(ZSTD_CCtx*), cMem); assert(nbWorkers > 0); if (!cctxPool) return NULL; if (ZSTD_pthread_mutex_init(&cctxPool->poolMutex, NULL)) { ZSTD_free(cctxPool, cMem); return NULL; } cctxPool->cMem = cMem; cctxPool->totalCCtx = nbWorkers; cctxPool->availCCtx = 1; /* at least one cctx for single-thread mode */ cctxPool->cctx[0] = ZSTD_createCCtx_advanced(cMem); if (!cctxPool->cctx[0]) { ZSTDMT_freeCCtxPool(cctxPool); return NULL; } DEBUGLOG(3, "cctxPool created, with %u workers", nbWorkers); return cctxPool; } static ZSTDMT_CCtxPool* ZSTDMT_expandCCtxPool(ZSTDMT_CCtxPool* srcPool, int nbWorkers) { if (srcPool==NULL) return NULL; if (nbWorkers <= srcPool->totalCCtx) return srcPool; /* good enough */ /* need a larger cctx pool */ { ZSTD_customMem const cMem = srcPool->cMem; ZSTDMT_freeCCtxPool(srcPool); return ZSTDMT_createCCtxPool(nbWorkers, cMem); } } /* only works during initialization phase, not during compression */ static size_t ZSTDMT_sizeof_CCtxPool(ZSTDMT_CCtxPool* cctxPool) { ZSTD_pthread_mutex_lock(&cctxPool->poolMutex); { unsigned const nbWorkers = cctxPool->totalCCtx; size_t const poolSize = sizeof(*cctxPool) + (nbWorkers-1) * sizeof(ZSTD_CCtx*); unsigned u; size_t totalCCtxSize = 0; for (u=0; ucctx[u]); } ZSTD_pthread_mutex_unlock(&cctxPool->poolMutex); assert(nbWorkers > 0); return poolSize + totalCCtxSize; } } static ZSTD_CCtx* ZSTDMT_getCCtx(ZSTDMT_CCtxPool* cctxPool) { DEBUGLOG(5, "ZSTDMT_getCCtx"); ZSTD_pthread_mutex_lock(&cctxPool->poolMutex); if (cctxPool->availCCtx) { cctxPool->availCCtx--; { ZSTD_CCtx* const cctx = cctxPool->cctx[cctxPool->availCCtx]; ZSTD_pthread_mutex_unlock(&cctxPool->poolMutex); return cctx; } } ZSTD_pthread_mutex_unlock(&cctxPool->poolMutex); DEBUGLOG(5, "create one more CCtx"); return ZSTD_createCCtx_advanced(cctxPool->cMem); /* note : can be NULL, when creation fails ! */ } static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) { if (cctx==NULL) return; /* compatibility with release on NULL */ ZSTD_pthread_mutex_lock(&pool->poolMutex); if (pool->availCCtx < pool->totalCCtx) pool->cctx[pool->availCCtx++] = cctx; else { /* pool overflow : should not happen, since totalCCtx==nbWorkers */ DEBUGLOG(4, "CCtx pool overflow : free cctx"); ZSTD_freeCCtx(cctx); } ZSTD_pthread_mutex_unlock(&pool->poolMutex); } /* ==== Serial State ==== */ typedef struct { void const* start; size_t size; } range_t; typedef struct { /* All variables in the struct are protected by mutex. */ ZSTD_pthread_mutex_t mutex; ZSTD_pthread_cond_t cond; ZSTD_CCtx_params params; ldmState_t ldmState; XXH64_state_t xxhState; unsigned nextJobID; /* Protects ldmWindow. * Must be acquired after the main mutex when acquiring both. */ ZSTD_pthread_mutex_t ldmWindowMutex; ZSTD_pthread_cond_t ldmWindowCond; /* Signaled when ldmWindow is updated */ ZSTD_window_t ldmWindow; /* A thread-safe copy of ldmState.window */ } serialState_t; static int ZSTDMT_serialState_reset(serialState_t* serialState, ZSTDMT_seqPool* seqPool, ZSTD_CCtx_params params, size_t jobSize, const void* dict, size_t const dictSize, ZSTD_dictContentType_e dictContentType) { /* Adjust parameters */ if (params.ldmParams.enableLdm) { DEBUGLOG(4, "LDM window size = %u KB", (1U << params.cParams.windowLog) >> 10); ZSTD_ldm_adjustParameters(¶ms.ldmParams, ¶ms.cParams); assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog); assert(params.ldmParams.hashRateLog < 32); serialState->ldmState.hashPower = ZSTD_rollingHash_primePower(params.ldmParams.minMatchLength); } else { memset(¶ms.ldmParams, 0, sizeof(params.ldmParams)); } serialState->nextJobID = 0; if (params.fParams.checksumFlag) XXH64_reset(&serialState->xxhState, 0); if (params.ldmParams.enableLdm) { ZSTD_customMem cMem = params.customMem; unsigned const hashLog = params.ldmParams.hashLog; size_t const hashSize = ((size_t)1 << hashLog) * sizeof(ldmEntry_t); unsigned const bucketLog = params.ldmParams.hashLog - params.ldmParams.bucketSizeLog; size_t const bucketSize = (size_t)1 << bucketLog; unsigned const prevBucketLog = serialState->params.ldmParams.hashLog - serialState->params.ldmParams.bucketSizeLog; /* Size the seq pool tables */ ZSTDMT_setNbSeq(seqPool, ZSTD_ldm_getMaxNbSeq(params.ldmParams, jobSize)); /* Reset the window */ ZSTD_window_init(&serialState->ldmState.window); /* Resize tables and output space if necessary. */ if (serialState->ldmState.hashTable == NULL || serialState->params.ldmParams.hashLog < hashLog) { ZSTD_free(serialState->ldmState.hashTable, cMem); serialState->ldmState.hashTable = (ldmEntry_t*)ZSTD_malloc(hashSize, cMem); } if (serialState->ldmState.bucketOffsets == NULL || prevBucketLog < bucketLog) { ZSTD_free(serialState->ldmState.bucketOffsets, cMem); serialState->ldmState.bucketOffsets = (BYTE*)ZSTD_malloc(bucketSize, cMem); } if (!serialState->ldmState.hashTable || !serialState->ldmState.bucketOffsets) return 1; /* Zero the tables */ memset(serialState->ldmState.hashTable, 0, hashSize); memset(serialState->ldmState.bucketOffsets, 0, bucketSize); /* Update window state and fill hash table with dict */ serialState->ldmState.loadedDictEnd = 0; if (dictSize > 0) { if (dictContentType == ZSTD_dct_rawContent) { BYTE const* const dictEnd = (const BYTE*)dict + dictSize; ZSTD_window_update(&serialState->ldmState.window, dict, dictSize); ZSTD_ldm_fillHashTable(&serialState->ldmState, (const BYTE*)dict, dictEnd, ¶ms.ldmParams); serialState->ldmState.loadedDictEnd = params.forceWindow ? 0 : (U32)(dictEnd - serialState->ldmState.window.base); } else { /* don't even load anything */ } } /* Initialize serialState's copy of ldmWindow. */ serialState->ldmWindow = serialState->ldmState.window; } serialState->params = params; serialState->params.jobSize = (U32)jobSize; return 0; } static int ZSTDMT_serialState_init(serialState_t* serialState) { int initError = 0; memset(serialState, 0, sizeof(*serialState)); initError |= ZSTD_pthread_mutex_init(&serialState->mutex, NULL); initError |= ZSTD_pthread_cond_init(&serialState->cond, NULL); initError |= ZSTD_pthread_mutex_init(&serialState->ldmWindowMutex, NULL); initError |= ZSTD_pthread_cond_init(&serialState->ldmWindowCond, NULL); return initError; } static void ZSTDMT_serialState_free(serialState_t* serialState) { ZSTD_customMem cMem = serialState->params.customMem; ZSTD_pthread_mutex_destroy(&serialState->mutex); ZSTD_pthread_cond_destroy(&serialState->cond); ZSTD_pthread_mutex_destroy(&serialState->ldmWindowMutex); ZSTD_pthread_cond_destroy(&serialState->ldmWindowCond); ZSTD_free(serialState->ldmState.hashTable, cMem); ZSTD_free(serialState->ldmState.bucketOffsets, cMem); } static void ZSTDMT_serialState_update(serialState_t* serialState, ZSTD_CCtx* jobCCtx, rawSeqStore_t seqStore, range_t src, unsigned jobID) { /* Wait for our turn */ ZSTD_PTHREAD_MUTEX_LOCK(&serialState->mutex); while (serialState->nextJobID < jobID) { DEBUGLOG(5, "wait for serialState->cond"); ZSTD_pthread_cond_wait(&serialState->cond, &serialState->mutex); } /* A future job may error and skip our job */ if (serialState->nextJobID == jobID) { /* It is now our turn, do any processing necessary */ if (serialState->params.ldmParams.enableLdm) { size_t error; assert(seqStore.seq != NULL && seqStore.pos == 0 && seqStore.size == 0 && seqStore.capacity > 0); assert(src.size <= serialState->params.jobSize); ZSTD_window_update(&serialState->ldmState.window, src.start, src.size); error = ZSTD_ldm_generateSequences( &serialState->ldmState, &seqStore, &serialState->params.ldmParams, src.start, src.size); /* We provide a large enough buffer to never fail. */ assert(!ZSTD_isError(error)); (void)error; /* Update ldmWindow to match the ldmState.window and signal the main * thread if it is waiting for a buffer. */ ZSTD_PTHREAD_MUTEX_LOCK(&serialState->ldmWindowMutex); serialState->ldmWindow = serialState->ldmState.window; ZSTD_pthread_cond_signal(&serialState->ldmWindowCond); ZSTD_pthread_mutex_unlock(&serialState->ldmWindowMutex); } if (serialState->params.fParams.checksumFlag && src.size > 0) XXH64_update(&serialState->xxhState, src.start, src.size); } /* Now it is the next jobs turn */ serialState->nextJobID++; ZSTD_pthread_cond_broadcast(&serialState->cond); ZSTD_pthread_mutex_unlock(&serialState->mutex); if (seqStore.size > 0) { size_t const err = ZSTD_referenceExternalSequences( jobCCtx, seqStore.seq, seqStore.size); assert(serialState->params.ldmParams.enableLdm); assert(!ZSTD_isError(err)); (void)err; } } static void ZSTDMT_serialState_ensureFinished(serialState_t* serialState, unsigned jobID, size_t cSize) { ZSTD_PTHREAD_MUTEX_LOCK(&serialState->mutex); if (serialState->nextJobID <= jobID) { assert(ZSTD_isError(cSize)); (void)cSize; DEBUGLOG(5, "Skipping past job %u because of error", jobID); serialState->nextJobID = jobID + 1; ZSTD_pthread_cond_broadcast(&serialState->cond); ZSTD_PTHREAD_MUTEX_LOCK(&serialState->ldmWindowMutex); ZSTD_window_clear(&serialState->ldmWindow); ZSTD_pthread_cond_signal(&serialState->ldmWindowCond); ZSTD_pthread_mutex_unlock(&serialState->ldmWindowMutex); } ZSTD_pthread_mutex_unlock(&serialState->mutex); } /* ------------------------------------------ */ /* ===== Worker thread ===== */ /* ------------------------------------------ */ static const range_t kNullRange = { NULL, 0 }; typedef struct { size_t consumed; /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx */ size_t cSize; /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx, then set0 by mtctx */ ZSTD_pthread_mutex_t job_mutex; /* Thread-safe - used by mtctx and worker */ ZSTD_pthread_cond_t job_cond; /* Thread-safe - used by mtctx and worker */ ZSTDMT_CCtxPool* cctxPool; /* Thread-safe - used by mtctx and (all) workers */ ZSTDMT_bufferPool* bufPool; /* Thread-safe - used by mtctx and (all) workers */ ZSTDMT_seqPool* seqPool; /* Thread-safe - used by mtctx and (all) workers */ serialState_t* serial; /* Thread-safe - used by mtctx and (all) workers */ buffer_t dstBuff; /* set by worker (or mtctx), then read by worker & mtctx, then modified by mtctx => no barrier */ range_t prefix; /* set by mtctx, then read by worker & mtctx => no barrier */ range_t src; /* set by mtctx, then read by worker & mtctx => no barrier */ unsigned jobID; /* set by mtctx, then read by worker => no barrier */ unsigned firstJob; /* set by mtctx, then read by worker => no barrier */ unsigned lastJob; /* set by mtctx, then read by worker => no barrier */ ZSTD_CCtx_params params; /* set by mtctx, then read by worker => no barrier */ const ZSTD_CDict* cdict; /* set by mtctx, then read by worker => no barrier */ unsigned long long fullFrameSize; /* set by mtctx, then read by worker => no barrier */ size_t dstFlushed; /* used only by mtctx */ unsigned frameChecksumNeeded; /* used only by mtctx */ } ZSTDMT_jobDescription; #define JOB_ERROR(e) { \ ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex); \ job->cSize = e; \ ZSTD_pthread_mutex_unlock(&job->job_mutex); \ goto _endJob; \ } /* ZSTDMT_compressionJob() is a POOL_function type */ static void ZSTDMT_compressionJob(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; ZSTD_CCtx_params jobParams = job->params; /* do not modify job->params ! copy it, modify the copy */ ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool); rawSeqStore_t rawSeqStore = ZSTDMT_getSeq(job->seqPool); buffer_t dstBuff = job->dstBuff; size_t lastCBlockSize = 0; /* resources */ if (cctx==NULL) JOB_ERROR(ERROR(memory_allocation)); if (dstBuff.start == NULL) { /* streaming job : doesn't provide a dstBuffer */ dstBuff = ZSTDMT_getBuffer(job->bufPool); if (dstBuff.start==NULL) JOB_ERROR(ERROR(memory_allocation)); job->dstBuff = dstBuff; /* this value can be read in ZSTDMT_flush, when it copies the whole job */ } if (jobParams.ldmParams.enableLdm && rawSeqStore.seq == NULL) JOB_ERROR(ERROR(memory_allocation)); /* Don't compute the checksum for chunks, since we compute it externally, * but write it in the header. */ if (job->jobID != 0) jobParams.fParams.checksumFlag = 0; /* Don't run LDM for the chunks, since we handle it externally */ jobParams.ldmParams.enableLdm = 0; /* init */ if (job->cdict) { size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dct_auto, ZSTD_dtlm_fast, job->cdict, &jobParams, job->fullFrameSize); assert(job->firstJob); /* only allowed for first job */ if (ZSTD_isError(initError)) JOB_ERROR(initError); } else { /* srcStart points at reloaded section */ U64 const pledgedSrcSize = job->firstJob ? job->fullFrameSize : job->src.size; { size_t const forceWindowError = ZSTD_CCtxParams_setParameter(&jobParams, ZSTD_c_forceMaxWindow, !job->firstJob); if (ZSTD_isError(forceWindowError)) JOB_ERROR(forceWindowError); } { size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->prefix.start, job->prefix.size, ZSTD_dct_rawContent, /* load dictionary in "content-only" mode (no header analysis) */ ZSTD_dtlm_fast, NULL, /*cdict*/ &jobParams, pledgedSrcSize); if (ZSTD_isError(initError)) JOB_ERROR(initError); } } /* Perform serial step as early as possible, but after CCtx initialization */ ZSTDMT_serialState_update(job->serial, cctx, rawSeqStore, job->src, job->jobID); if (!job->firstJob) { /* flush and overwrite frame header when it's not first job */ size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0); if (ZSTD_isError(hSize)) JOB_ERROR(hSize); DEBUGLOG(5, "ZSTDMT_compressionJob: flush and overwrite %u bytes of frame header (not first job)", (U32)hSize); ZSTD_invalidateRepCodes(cctx); } /* compress */ { size_t const chunkSize = 4*ZSTD_BLOCKSIZE_MAX; int const nbChunks = (int)((job->src.size + (chunkSize-1)) / chunkSize); const BYTE* ip = (const BYTE*) job->src.start; BYTE* const ostart = (BYTE*)dstBuff.start; BYTE* op = ostart; BYTE* oend = op + dstBuff.capacity; int chunkNb; if (sizeof(size_t) > sizeof(int)) assert(job->src.size < ((size_t)INT_MAX) * chunkSize); /* check overflow */ DEBUGLOG(5, "ZSTDMT_compressionJob: compress %u bytes in %i blocks", (U32)job->src.size, nbChunks); assert(job->cSize == 0); for (chunkNb = 1; chunkNb < nbChunks; chunkNb++) { size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, chunkSize); if (ZSTD_isError(cSize)) JOB_ERROR(cSize); ip += chunkSize; op += cSize; assert(op < oend); /* stats */ ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex); job->cSize += cSize; job->consumed = chunkSize * chunkNb; DEBUGLOG(5, "ZSTDMT_compressionJob: compress new block : cSize==%u bytes (total: %u)", (U32)cSize, (U32)job->cSize); ZSTD_pthread_cond_signal(&job->job_cond); /* warns some more data is ready to be flushed */ ZSTD_pthread_mutex_unlock(&job->job_mutex); } /* last block */ assert(chunkSize > 0); assert((chunkSize & (chunkSize - 1)) == 0); /* chunkSize must be power of 2 for mask==(chunkSize-1) to work */ if ((nbChunks > 0) | job->lastJob /*must output a "last block" flag*/ ) { size_t const lastBlockSize1 = job->src.size & (chunkSize-1); size_t const lastBlockSize = ((lastBlockSize1==0) & (job->src.size>=chunkSize)) ? chunkSize : lastBlockSize1; size_t const cSize = (job->lastJob) ? ZSTD_compressEnd (cctx, op, oend-op, ip, lastBlockSize) : ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize); if (ZSTD_isError(cSize)) JOB_ERROR(cSize); lastCBlockSize = cSize; } } _endJob: ZSTDMT_serialState_ensureFinished(job->serial, job->jobID, job->cSize); if (job->prefix.size > 0) DEBUGLOG(5, "Finished with prefix: %zx", (size_t)job->prefix.start); DEBUGLOG(5, "Finished with source: %zx", (size_t)job->src.start); /* release resources */ ZSTDMT_releaseSeq(job->seqPool, rawSeqStore); ZSTDMT_releaseCCtx(job->cctxPool, cctx); /* report */ ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex); if (ZSTD_isError(job->cSize)) assert(lastCBlockSize == 0); job->cSize += lastCBlockSize; job->consumed = job->src.size; /* when job->consumed == job->src.size , compression job is presumed completed */ ZSTD_pthread_cond_signal(&job->job_cond); ZSTD_pthread_mutex_unlock(&job->job_mutex); } /* ------------------------------------------ */ /* ===== Multi-threaded compression ===== */ /* ------------------------------------------ */ typedef struct { range_t prefix; /* read-only non-owned prefix buffer */ buffer_t buffer; size_t filled; } inBuff_t; typedef struct { BYTE* buffer; /* The round input buffer. All jobs get references * to pieces of the buffer. ZSTDMT_tryGetInputRange() * handles handing out job input buffers, and makes * sure it doesn't overlap with any pieces still in use. */ size_t capacity; /* The capacity of buffer. */ size_t pos; /* The position of the current inBuff in the round * buffer. Updated past the end if the inBuff once * the inBuff is sent to the worker thread. * pos <= capacity. */ } roundBuff_t; static const roundBuff_t kNullRoundBuff = {NULL, 0, 0}; #define RSYNC_LENGTH 32 typedef struct { U64 hash; U64 hitMask; U64 primePower; } rsyncState_t; struct ZSTDMT_CCtx_s { POOL_ctx* factory; ZSTDMT_jobDescription* jobs; ZSTDMT_bufferPool* bufPool; ZSTDMT_CCtxPool* cctxPool; ZSTDMT_seqPool* seqPool; ZSTD_CCtx_params params; size_t targetSectionSize; size_t targetPrefixSize; int jobReady; /* 1 => one job is already prepared, but pool has shortage of workers. Don't create a new job. */ inBuff_t inBuff; roundBuff_t roundBuff; serialState_t serial; rsyncState_t rsync; unsigned singleBlockingThread; unsigned jobIDMask; unsigned doneJobID; unsigned nextJobID; unsigned frameEnded; unsigned allJobsCompleted; unsigned long long frameContentSize; unsigned long long consumed; unsigned long long produced; ZSTD_customMem cMem; ZSTD_CDict* cdictLocal; const ZSTD_CDict* cdict; }; static void ZSTDMT_freeJobsTable(ZSTDMT_jobDescription* jobTable, U32 nbJobs, ZSTD_customMem cMem) { U32 jobNb; if (jobTable == NULL) return; for (jobNb=0; jobNb mtctx->jobIDMask+1) { /* need more job capacity */ ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem); mtctx->jobIDMask = 0; mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, mtctx->cMem); if (mtctx->jobs==NULL) return ERROR(memory_allocation); assert((nbJobs != 0) && ((nbJobs & (nbJobs - 1)) == 0)); /* ensure nbJobs is a power of 2 */ mtctx->jobIDMask = nbJobs - 1; } return 0; } /* ZSTDMT_CCtxParam_setNbWorkers(): * Internal use only */ size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers) { return ZSTD_CCtxParams_setParameter(params, ZSTD_c_nbWorkers, (int)nbWorkers); } MEM_STATIC ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced_internal(unsigned nbWorkers, ZSTD_customMem cMem) { ZSTDMT_CCtx* mtctx; U32 nbJobs = nbWorkers + 2; int initError; DEBUGLOG(3, "ZSTDMT_createCCtx_advanced (nbWorkers = %u)", nbWorkers); if (nbWorkers < 1) return NULL; nbWorkers = MIN(nbWorkers , ZSTDMT_NBWORKERS_MAX); if ((cMem.customAlloc!=NULL) ^ (cMem.customFree!=NULL)) /* invalid custom allocator */ return NULL; mtctx = (ZSTDMT_CCtx*) ZSTD_calloc(sizeof(ZSTDMT_CCtx), cMem); if (!mtctx) return NULL; ZSTDMT_CCtxParam_setNbWorkers(&mtctx->params, nbWorkers); mtctx->cMem = cMem; mtctx->allJobsCompleted = 1; mtctx->factory = POOL_create_advanced(nbWorkers, 0, cMem); mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, cMem); assert(nbJobs > 0); assert((nbJobs & (nbJobs - 1)) == 0); /* ensure nbJobs is a power of 2 */ mtctx->jobIDMask = nbJobs - 1; mtctx->bufPool = ZSTDMT_createBufferPool(nbWorkers, cMem); mtctx->cctxPool = ZSTDMT_createCCtxPool(nbWorkers, cMem); mtctx->seqPool = ZSTDMT_createSeqPool(nbWorkers, cMem); initError = ZSTDMT_serialState_init(&mtctx->serial); mtctx->roundBuff = kNullRoundBuff; if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool | !mtctx->seqPool | initError) { ZSTDMT_freeCCtx(mtctx); return NULL; } DEBUGLOG(3, "mt_cctx created, for %u threads", nbWorkers); return mtctx; } ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem) { #ifdef ZSTD_MULTITHREAD return ZSTDMT_createCCtx_advanced_internal(nbWorkers, cMem); #else (void)nbWorkers; (void)cMem; return NULL; #endif } ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers) { return ZSTDMT_createCCtx_advanced(nbWorkers, ZSTD_defaultCMem); } /* ZSTDMT_releaseAllJobResources() : * note : ensure all workers are killed first ! */ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx) { unsigned jobID; DEBUGLOG(3, "ZSTDMT_releaseAllJobResources"); for (jobID=0; jobID <= mtctx->jobIDMask; jobID++) { /* Copy the mutex/cond out */ ZSTD_pthread_mutex_t const mutex = mtctx->jobs[jobID].job_mutex; ZSTD_pthread_cond_t const cond = mtctx->jobs[jobID].job_cond; DEBUGLOG(4, "job%02u: release dst address %08X", jobID, (U32)(size_t)mtctx->jobs[jobID].dstBuff.start); ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff); /* Clear the job description, but keep the mutex/cond */ memset(&mtctx->jobs[jobID], 0, sizeof(mtctx->jobs[jobID])); mtctx->jobs[jobID].job_mutex = mutex; mtctx->jobs[jobID].job_cond = cond; } mtctx->inBuff.buffer = g_nullBuffer; mtctx->inBuff.filled = 0; mtctx->allJobsCompleted = 1; } static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* mtctx) { DEBUGLOG(4, "ZSTDMT_waitForAllJobsCompleted"); while (mtctx->doneJobID < mtctx->nextJobID) { unsigned const jobID = mtctx->doneJobID & mtctx->jobIDMask; ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[jobID].job_mutex); while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].src.size) { DEBUGLOG(4, "waiting for jobCompleted signal from job %u", mtctx->doneJobID); /* we want to block when waiting for data to flush */ ZSTD_pthread_cond_wait(&mtctx->jobs[jobID].job_cond, &mtctx->jobs[jobID].job_mutex); } ZSTD_pthread_mutex_unlock(&mtctx->jobs[jobID].job_mutex); mtctx->doneJobID++; } } size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) { if (mtctx==NULL) return 0; /* compatible with free on NULL */ POOL_free(mtctx->factory); /* stop and free worker threads */ ZSTDMT_releaseAllJobResources(mtctx); /* release job resources into pools first */ ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem); ZSTDMT_freeBufferPool(mtctx->bufPool); ZSTDMT_freeCCtxPool(mtctx->cctxPool); ZSTDMT_freeSeqPool(mtctx->seqPool); ZSTDMT_serialState_free(&mtctx->serial); ZSTD_freeCDict(mtctx->cdictLocal); if (mtctx->roundBuff.buffer) ZSTD_free(mtctx->roundBuff.buffer, mtctx->cMem); ZSTD_free(mtctx, mtctx->cMem); return 0; } size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx) { if (mtctx == NULL) return 0; /* supports sizeof NULL */ return sizeof(*mtctx) + POOL_sizeof(mtctx->factory) + ZSTDMT_sizeof_bufferPool(mtctx->bufPool) + (mtctx->jobIDMask+1) * sizeof(ZSTDMT_jobDescription) + ZSTDMT_sizeof_CCtxPool(mtctx->cctxPool) + ZSTDMT_sizeof_seqPool(mtctx->seqPool) + ZSTD_sizeof_CDict(mtctx->cdictLocal) + mtctx->roundBuff.capacity; } /* Internal only */ size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, ZSTDMT_parameter parameter, int value) { DEBUGLOG(4, "ZSTDMT_CCtxParam_setMTCtxParameter"); switch(parameter) { case ZSTDMT_p_jobSize : DEBUGLOG(4, "ZSTDMT_CCtxParam_setMTCtxParameter : set jobSize to %i", value); return ZSTD_CCtxParams_setParameter(params, ZSTD_c_jobSize, value); case ZSTDMT_p_overlapLog : DEBUGLOG(4, "ZSTDMT_p_overlapLog : %i", value); return ZSTD_CCtxParams_setParameter(params, ZSTD_c_overlapLog, value); case ZSTDMT_p_rsyncable : DEBUGLOG(4, "ZSTD_p_rsyncable : %i", value); return ZSTD_CCtxParams_setParameter(params, ZSTD_c_rsyncable, value); default : return ERROR(parameter_unsupported); } } size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int value) { DEBUGLOG(4, "ZSTDMT_setMTCtxParameter"); return ZSTDMT_CCtxParam_setMTCtxParameter(&mtctx->params, parameter, value); } size_t ZSTDMT_getMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int* value) { switch (parameter) { case ZSTDMT_p_jobSize: return ZSTD_CCtxParams_getParameter(&mtctx->params, ZSTD_c_jobSize, value); case ZSTDMT_p_overlapLog: return ZSTD_CCtxParams_getParameter(&mtctx->params, ZSTD_c_overlapLog, value); case ZSTDMT_p_rsyncable: return ZSTD_CCtxParams_getParameter(&mtctx->params, ZSTD_c_rsyncable, value); default: return ERROR(parameter_unsupported); } } /* Sets parameters relevant to the compression job, * initializing others to default values. */ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(const ZSTD_CCtx_params* params) { ZSTD_CCtx_params jobParams = *params; /* Clear parameters related to multithreading */ jobParams.forceWindow = 0; jobParams.nbWorkers = 0; jobParams.jobSize = 0; jobParams.overlapLog = 0; jobParams.rsyncable = 0; memset(&jobParams.ldmParams, 0, sizeof(ldmParams_t)); memset(&jobParams.customMem, 0, sizeof(ZSTD_customMem)); return jobParams; } /* ZSTDMT_resize() : * @return : error code if fails, 0 on success */ static size_t ZSTDMT_resize(ZSTDMT_CCtx* mtctx, unsigned nbWorkers) { if (POOL_resize(mtctx->factory, nbWorkers)) return ERROR(memory_allocation); FORWARD_IF_ERROR( ZSTDMT_expandJobsTable(mtctx, nbWorkers) , ""); mtctx->bufPool = ZSTDMT_expandBufferPool(mtctx->bufPool, nbWorkers); if (mtctx->bufPool == NULL) return ERROR(memory_allocation); mtctx->cctxPool = ZSTDMT_expandCCtxPool(mtctx->cctxPool, nbWorkers); if (mtctx->cctxPool == NULL) return ERROR(memory_allocation); mtctx->seqPool = ZSTDMT_expandSeqPool(mtctx->seqPool, nbWorkers); if (mtctx->seqPool == NULL) return ERROR(memory_allocation); ZSTDMT_CCtxParam_setNbWorkers(&mtctx->params, nbWorkers); return 0; } /*! ZSTDMT_updateCParams_whileCompressing() : * Updates a selected set of compression parameters, remaining compatible with currently active frame. * New parameters will be applied to next compression job. */ void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* cctxParams) { U32 const saved_wlog = mtctx->params.cParams.windowLog; /* Do not modify windowLog while compressing */ int const compressionLevel = cctxParams->compressionLevel; DEBUGLOG(5, "ZSTDMT_updateCParams_whileCompressing (level:%i)", compressionLevel); mtctx->params.compressionLevel = compressionLevel; { ZSTD_compressionParameters cParams = ZSTD_getCParamsFromCCtxParams(cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, 0); cParams.windowLog = saved_wlog; mtctx->params.cParams = cParams; } } /* ZSTDMT_getFrameProgression(): * tells how much data has been consumed (input) and produced (output) for current frame. * able to count progression inside worker threads. * Note : mutex will be acquired during statistics collection inside workers. */ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx) { ZSTD_frameProgression fps; DEBUGLOG(5, "ZSTDMT_getFrameProgression"); fps.ingested = mtctx->consumed + mtctx->inBuff.filled; fps.consumed = mtctx->consumed; fps.produced = fps.flushed = mtctx->produced; fps.currentJobID = mtctx->nextJobID; fps.nbActiveWorkers = 0; { unsigned jobNb; unsigned lastJobNb = mtctx->nextJobID + mtctx->jobReady; assert(mtctx->jobReady <= 1); DEBUGLOG(6, "ZSTDMT_getFrameProgression: jobs: from %u to <%u (jobReady:%u)", mtctx->doneJobID, lastJobNb, mtctx->jobReady) for (jobNb = mtctx->doneJobID ; jobNb < lastJobNb ; jobNb++) { unsigned const wJobID = jobNb & mtctx->jobIDMask; ZSTDMT_jobDescription* jobPtr = &mtctx->jobs[wJobID]; ZSTD_pthread_mutex_lock(&jobPtr->job_mutex); { size_t const cResult = jobPtr->cSize; size_t const produced = ZSTD_isError(cResult) ? 0 : cResult; size_t const flushed = ZSTD_isError(cResult) ? 0 : jobPtr->dstFlushed; assert(flushed <= produced); fps.ingested += jobPtr->src.size; fps.consumed += jobPtr->consumed; fps.produced += produced; fps.flushed += flushed; fps.nbActiveWorkers += (jobPtr->consumed < jobPtr->src.size); } ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex); } } return fps; } size_t ZSTDMT_toFlushNow(ZSTDMT_CCtx* mtctx) { size_t toFlush; unsigned const jobID = mtctx->doneJobID; assert(jobID <= mtctx->nextJobID); if (jobID == mtctx->nextJobID) return 0; /* no active job => nothing to flush */ /* look into oldest non-fully-flushed job */ { unsigned const wJobID = jobID & mtctx->jobIDMask; ZSTDMT_jobDescription* const jobPtr = &mtctx->jobs[wJobID]; ZSTD_pthread_mutex_lock(&jobPtr->job_mutex); { size_t const cResult = jobPtr->cSize; size_t const produced = ZSTD_isError(cResult) ? 0 : cResult; size_t const flushed = ZSTD_isError(cResult) ? 0 : jobPtr->dstFlushed; assert(flushed <= produced); assert(jobPtr->consumed <= jobPtr->src.size); toFlush = produced - flushed; /* if toFlush==0, nothing is available to flush. * However, jobID is expected to still be active: * if jobID was already completed and fully flushed, * ZSTDMT_flushProduced() should have already moved onto next job. * Therefore, some input has not yet been consumed. */ if (toFlush==0) { assert(jobPtr->consumed < jobPtr->src.size); } } ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex); } return toFlush; } /* ------------------------------------------ */ /* ===== Multi-threaded compression ===== */ /* ------------------------------------------ */ static unsigned ZSTDMT_computeTargetJobLog(const ZSTD_CCtx_params* params) { unsigned jobLog; if (params->ldmParams.enableLdm) { /* In Long Range Mode, the windowLog is typically oversized. * In which case, it's preferable to determine the jobSize * based on chainLog instead. */ jobLog = MAX(21, params->cParams.chainLog + 4); } else { jobLog = MAX(20, params->cParams.windowLog + 2); } return MIN(jobLog, (unsigned)ZSTDMT_JOBLOG_MAX); } static int ZSTDMT_overlapLog_default(ZSTD_strategy strat) { switch(strat) { case ZSTD_btultra2: return 9; case ZSTD_btultra: case ZSTD_btopt: return 8; case ZSTD_btlazy2: case ZSTD_lazy2: return 7; case ZSTD_lazy: case ZSTD_greedy: case ZSTD_dfast: case ZSTD_fast: default:; } return 6; } static int ZSTDMT_overlapLog(int ovlog, ZSTD_strategy strat) { assert(0 <= ovlog && ovlog <= 9); if (ovlog == 0) return ZSTDMT_overlapLog_default(strat); return ovlog; } static size_t ZSTDMT_computeOverlapSize(const ZSTD_CCtx_params* params) { int const overlapRLog = 9 - ZSTDMT_overlapLog(params->overlapLog, params->cParams.strategy); int ovLog = (overlapRLog >= 8) ? 0 : (params->cParams.windowLog - overlapRLog); assert(0 <= overlapRLog && overlapRLog <= 8); if (params->ldmParams.enableLdm) { /* In Long Range Mode, the windowLog is typically oversized. * In which case, it's preferable to determine the jobSize * based on chainLog instead. * Then, ovLog becomes a fraction of the jobSize, rather than windowSize */ ovLog = MIN(params->cParams.windowLog, ZSTDMT_computeTargetJobLog(params) - 2) - overlapRLog; } assert(0 <= ovLog && ovLog <= ZSTD_WINDOWLOG_MAX); DEBUGLOG(4, "overlapLog : %i", params->overlapLog); DEBUGLOG(4, "overlap size : %i", 1 << ovLog); return (ovLog==0) ? 0 : (size_t)1 << ovLog; } static unsigned ZSTDMT_computeNbJobs(const ZSTD_CCtx_params* params, size_t srcSize, unsigned nbWorkers) { assert(nbWorkers>0); { size_t const jobSizeTarget = (size_t)1 << ZSTDMT_computeTargetJobLog(params); size_t const jobMaxSize = jobSizeTarget << 2; size_t const passSizeMax = jobMaxSize * nbWorkers; unsigned const multiplier = (unsigned)(srcSize / passSizeMax) + 1; unsigned const nbJobsLarge = multiplier * nbWorkers; unsigned const nbJobsMax = (unsigned)(srcSize / jobSizeTarget) + 1; unsigned const nbJobsSmall = MIN(nbJobsMax, nbWorkers); return (multiplier>1) ? nbJobsLarge : nbJobsSmall; } } /* ZSTDMT_compress_advanced_internal() : * This is a blocking function : it will only give back control to caller after finishing its compression job. */ static size_t ZSTDMT_compress_advanced_internal( ZSTDMT_CCtx* mtctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const ZSTD_CDict* cdict, ZSTD_CCtx_params params) { ZSTD_CCtx_params const jobParams = ZSTDMT_initJobCCtxParams(¶ms); size_t const overlapSize = ZSTDMT_computeOverlapSize(¶ms); unsigned const nbJobs = ZSTDMT_computeNbJobs(¶ms, srcSize, params.nbWorkers); size_t const proposedJobSize = (srcSize + (nbJobs-1)) / nbJobs; size_t const avgJobSize = (((proposedJobSize-1) & 0x1FFFF) < 0x7FFF) ? proposedJobSize + 0xFFFF : proposedJobSize; /* avoid too small last block */ const char* const srcStart = (const char*)src; size_t remainingSrcSize = srcSize; unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbJobs : (unsigned)(dstCapacity / ZSTD_compressBound(avgJobSize)); /* presumes avgJobSize >= 256 KB, which should be the case */ size_t frameStartPos = 0, dstBufferPos = 0; assert(jobParams.nbWorkers == 0); assert(mtctx->cctxPool->totalCCtx == params.nbWorkers); params.jobSize = (U32)avgJobSize; DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbJobs=%2u (rawSize=%u bytes; fixedSize=%u) ", nbJobs, (U32)proposedJobSize, (U32)avgJobSize); if ((nbJobs==1) | (params.nbWorkers<=1)) { /* fallback to single-thread mode : this is a blocking invocation anyway */ ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0]; DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: fallback to single-thread mode"); if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams); return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, NULL, 0, &jobParams); } assert(avgJobSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), required to compress directly into Dst (no additional buffer) */ ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgJobSize) ); /* LDM doesn't even try to load the dictionary in single-ingestion mode */ if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params, avgJobSize, NULL, 0, ZSTD_dct_auto)) return ERROR(memory_allocation); FORWARD_IF_ERROR( ZSTDMT_expandJobsTable(mtctx, nbJobs) , ""); /* only expands if necessary */ { unsigned u; for (u=0; ujobs[u].prefix.start = srcStart + frameStartPos - dictSize; mtctx->jobs[u].prefix.size = dictSize; mtctx->jobs[u].src.start = srcStart + frameStartPos; mtctx->jobs[u].src.size = jobSize; assert(jobSize > 0); /* avoid job.src.size == 0 */ mtctx->jobs[u].consumed = 0; mtctx->jobs[u].cSize = 0; mtctx->jobs[u].cdict = (u==0) ? cdict : NULL; mtctx->jobs[u].fullFrameSize = srcSize; mtctx->jobs[u].params = jobParams; /* do not calculate checksum within sections, but write it in header for first section */ mtctx->jobs[u].dstBuff = dstBuffer; mtctx->jobs[u].cctxPool = mtctx->cctxPool; mtctx->jobs[u].bufPool = mtctx->bufPool; mtctx->jobs[u].seqPool = mtctx->seqPool; mtctx->jobs[u].serial = &mtctx->serial; mtctx->jobs[u].jobID = u; mtctx->jobs[u].firstJob = (u==0); mtctx->jobs[u].lastJob = (u==nbJobs-1); DEBUGLOG(5, "ZSTDMT_compress_advanced_internal: posting job %u (%u bytes)", u, (U32)jobSize); DEBUG_PRINTHEX(6, mtctx->jobs[u].prefix.start, 12); POOL_add(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[u]); frameStartPos += jobSize; dstBufferPos += dstBufferCapacity; remainingSrcSize -= jobSize; } } /* collect result */ { size_t error = 0, dstPos = 0; unsigned jobID; for (jobID=0; jobIDjobs[jobID].job_mutex); while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].src.size) { DEBUGLOG(5, "waiting for jobCompleted signal from job %u", jobID); ZSTD_pthread_cond_wait(&mtctx->jobs[jobID].job_cond, &mtctx->jobs[jobID].job_mutex); } ZSTD_pthread_mutex_unlock(&mtctx->jobs[jobID].job_mutex); DEBUGLOG(5, "ready to write job %u ", jobID); { size_t const cSize = mtctx->jobs[jobID].cSize; if (ZSTD_isError(cSize)) error = cSize; if ((!error) && (dstPos + cSize > dstCapacity)) error = ERROR(dstSize_tooSmall); if (jobID) { /* note : job 0 is written directly at dst, which is correct position */ if (!error) memmove((char*)dst + dstPos, mtctx->jobs[jobID].dstBuff.start, cSize); /* may overlap when job compressed within dst */ if (jobID >= compressWithinDst) { /* job compressed into its own buffer, which must be released */ DEBUGLOG(5, "releasing buffer %u>=%u", jobID, compressWithinDst); ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff); } } mtctx->jobs[jobID].dstBuff = g_nullBuffer; mtctx->jobs[jobID].cSize = 0; dstPos += cSize ; } } /* for (jobID=0; jobIDserial.xxhState); if (dstPos + 4 > dstCapacity) { error = ERROR(dstSize_tooSmall); } else { DEBUGLOG(4, "writing checksum : %08X \n", checksum); MEM_writeLE32((char*)dst + dstPos, checksum); dstPos += 4; } } if (!error) DEBUGLOG(4, "compressed size : %u ", (U32)dstPos); return error ? error : dstPos; } } size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const ZSTD_CDict* cdict, ZSTD_parameters params, int overlapLog) { ZSTD_CCtx_params cctxParams = mtctx->params; cctxParams.cParams = params.cParams; cctxParams.fParams = params.fParams; assert(ZSTD_OVERLAPLOG_MIN <= overlapLog && overlapLog <= ZSTD_OVERLAPLOG_MAX); cctxParams.overlapLog = overlapLog; return ZSTDMT_compress_advanced_internal(mtctx, dst, dstCapacity, src, srcSize, cdict, cctxParams); } size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) { ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize, 0); int const overlapLog = ZSTDMT_overlapLog_default(params.cParams.strategy); params.fParams.contentSizeFlag = 1; return ZSTDMT_compress_advanced(mtctx, dst, dstCapacity, src, srcSize, NULL, params, overlapLog); } /* ====================================== */ /* ======= Streaming API ======= */ /* ====================================== */ size_t ZSTDMT_initCStream_internal( ZSTDMT_CCtx* mtctx, const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize) { DEBUGLOG(4, "ZSTDMT_initCStream_internal (pledgedSrcSize=%u, nbWorkers=%u, cctxPool=%u)", (U32)pledgedSrcSize, params.nbWorkers, mtctx->cctxPool->totalCCtx); /* params supposed partially fully validated at this point */ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); assert(!((dict) && (cdict))); /* either dict or cdict, not both */ /* init */ if (params.nbWorkers != mtctx->params.nbWorkers) FORWARD_IF_ERROR( ZSTDMT_resize(mtctx, params.nbWorkers) , ""); if (params.jobSize != 0 && params.jobSize < ZSTDMT_JOBSIZE_MIN) params.jobSize = ZSTDMT_JOBSIZE_MIN; if (params.jobSize > (size_t)ZSTDMT_JOBSIZE_MAX) params.jobSize = (size_t)ZSTDMT_JOBSIZE_MAX; mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */ if (mtctx->singleBlockingThread) { ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(¶ms); DEBUGLOG(5, "ZSTDMT_initCStream_internal: switch to single blocking thread mode"); assert(singleThreadParams.nbWorkers == 0); return ZSTD_initCStream_internal(mtctx->cctxPool->cctx[0], dict, dictSize, cdict, &singleThreadParams, pledgedSrcSize); } DEBUGLOG(4, "ZSTDMT_initCStream_internal: %u workers", params.nbWorkers); if (mtctx->allJobsCompleted == 0) { /* previous compression not correctly finished */ ZSTDMT_waitForAllJobsCompleted(mtctx); ZSTDMT_releaseAllJobResources(mtctx); mtctx->allJobsCompleted = 1; } mtctx->params = params; mtctx->frameContentSize = pledgedSrcSize; if (dict) { ZSTD_freeCDict(mtctx->cdictLocal); mtctx->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize, ZSTD_dlm_byCopy, dictContentType, /* note : a loadPrefix becomes an internal CDict */ params.cParams, mtctx->cMem); mtctx->cdict = mtctx->cdictLocal; if (mtctx->cdictLocal == NULL) return ERROR(memory_allocation); } else { ZSTD_freeCDict(mtctx->cdictLocal); mtctx->cdictLocal = NULL; mtctx->cdict = cdict; } mtctx->targetPrefixSize = ZSTDMT_computeOverlapSize(¶ms); DEBUGLOG(4, "overlapLog=%i => %u KB", params.overlapLog, (U32)(mtctx->targetPrefixSize>>10)); mtctx->targetSectionSize = params.jobSize; if (mtctx->targetSectionSize == 0) { mtctx->targetSectionSize = 1ULL << ZSTDMT_computeTargetJobLog(¶ms); } assert(mtctx->targetSectionSize <= (size_t)ZSTDMT_JOBSIZE_MAX); if (params.rsyncable) { /* Aim for the targetsectionSize as the average job size. */ U32 const jobSizeMB = (U32)(mtctx->targetSectionSize >> 20); U32 const rsyncBits = ZSTD_highbit32(jobSizeMB) + 20; assert(jobSizeMB >= 1); DEBUGLOG(4, "rsyncLog = %u", rsyncBits); mtctx->rsync.hash = 0; mtctx->rsync.hitMask = (1ULL << rsyncBits) - 1; mtctx->rsync.primePower = ZSTD_rollingHash_primePower(RSYNC_LENGTH); } if (mtctx->targetSectionSize < mtctx->targetPrefixSize) mtctx->targetSectionSize = mtctx->targetPrefixSize; /* job size must be >= overlap size */ DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(mtctx->targetSectionSize>>10), (U32)params.jobSize); DEBUGLOG(4, "inBuff Size : %u KB", (U32)(mtctx->targetSectionSize>>10)); ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(mtctx->targetSectionSize)); { /* If ldm is enabled we need windowSize space. */ size_t const windowSize = mtctx->params.ldmParams.enableLdm ? (1U << mtctx->params.cParams.windowLog) : 0; /* Two buffers of slack, plus extra space for the overlap * This is the minimum slack that LDM works with. One extra because * flush might waste up to targetSectionSize-1 bytes. Another extra * for the overlap (if > 0), then one to fill which doesn't overlap * with the LDM window. */ size_t const nbSlackBuffers = 2 + (mtctx->targetPrefixSize > 0); size_t const slackSize = mtctx->targetSectionSize * nbSlackBuffers; /* Compute the total size, and always have enough slack */ size_t const nbWorkers = MAX(mtctx->params.nbWorkers, 1); size_t const sectionsSize = mtctx->targetSectionSize * nbWorkers; size_t const capacity = MAX(windowSize, sectionsSize) + slackSize; if (mtctx->roundBuff.capacity < capacity) { if (mtctx->roundBuff.buffer) ZSTD_free(mtctx->roundBuff.buffer, mtctx->cMem); mtctx->roundBuff.buffer = (BYTE*)ZSTD_malloc(capacity, mtctx->cMem); if (mtctx->roundBuff.buffer == NULL) { mtctx->roundBuff.capacity = 0; return ERROR(memory_allocation); } mtctx->roundBuff.capacity = capacity; } } DEBUGLOG(4, "roundBuff capacity : %u KB", (U32)(mtctx->roundBuff.capacity>>10)); mtctx->roundBuff.pos = 0; mtctx->inBuff.buffer = g_nullBuffer; mtctx->inBuff.filled = 0; mtctx->inBuff.prefix = kNullRange; mtctx->doneJobID = 0; mtctx->nextJobID = 0; mtctx->frameEnded = 0; mtctx->allJobsCompleted = 0; mtctx->consumed = 0; mtctx->produced = 0; if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params, mtctx->targetSectionSize, dict, dictSize, dictContentType)) return ERROR(memory_allocation); return 0; } size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize) { ZSTD_CCtx_params cctxParams = mtctx->params; /* retrieve sticky params */ DEBUGLOG(4, "ZSTDMT_initCStream_advanced (pledgedSrcSize=%u)", (U32)pledgedSrcSize); cctxParams.cParams = params.cParams; cctxParams.fParams = params.fParams; return ZSTDMT_initCStream_internal(mtctx, dict, dictSize, ZSTD_dct_auto, NULL, cctxParams, pledgedSrcSize); } size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize) { ZSTD_CCtx_params cctxParams = mtctx->params; if (cdict==NULL) return ERROR(dictionary_wrong); /* method incompatible with NULL cdict */ cctxParams.cParams = ZSTD_getCParamsFromCDict(cdict); cctxParams.fParams = fParams; return ZSTDMT_initCStream_internal(mtctx, NULL, 0 /*dictSize*/, ZSTD_dct_auto, cdict, cctxParams, pledgedSrcSize); } /* ZSTDMT_resetCStream() : * pledgedSrcSize can be zero == unknown (for the time being) * prefer using ZSTD_CONTENTSIZE_UNKNOWN, * as `0` might mean "empty" in the future */ size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize) { if (!pledgedSrcSize) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dct_auto, 0, mtctx->params, pledgedSrcSize); } size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel) { ZSTD_parameters const params = ZSTD_getParams(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0); ZSTD_CCtx_params cctxParams = mtctx->params; /* retrieve sticky params */ DEBUGLOG(4, "ZSTDMT_initCStream (cLevel=%i)", compressionLevel); cctxParams.cParams = params.cParams; cctxParams.fParams = params.fParams; return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dct_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN); } /* ZSTDMT_writeLastEmptyBlock() * Write a single empty block with an end-of-frame to finish a frame. * Job must be created from streaming variant. * This function is always successful if expected conditions are fulfilled. */ static void ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job) { assert(job->lastJob == 1); assert(job->src.size == 0); /* last job is empty -> will be simplified into a last empty block */ assert(job->firstJob == 0); /* cannot be first job, as it also needs to create frame header */ assert(job->dstBuff.start == NULL); /* invoked from streaming variant only (otherwise, dstBuff might be user's output) */ job->dstBuff = ZSTDMT_getBuffer(job->bufPool); if (job->dstBuff.start == NULL) { job->cSize = ERROR(memory_allocation); return; } assert(job->dstBuff.capacity >= ZSTD_blockHeaderSize); /* no buffer should ever be that small */ job->src = kNullRange; job->cSize = ZSTD_writeLastEmptyBlock(job->dstBuff.start, job->dstBuff.capacity); assert(!ZSTD_isError(job->cSize)); assert(job->consumed == 0); } static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZSTD_EndDirective endOp) { unsigned const jobID = mtctx->nextJobID & mtctx->jobIDMask; int const endFrame = (endOp == ZSTD_e_end); if (mtctx->nextJobID > mtctx->doneJobID + mtctx->jobIDMask) { DEBUGLOG(5, "ZSTDMT_createCompressionJob: will not create new job : table is full"); assert((mtctx->nextJobID & mtctx->jobIDMask) == (mtctx->doneJobID & mtctx->jobIDMask)); return 0; } if (!mtctx->jobReady) { BYTE const* src = (BYTE const*)mtctx->inBuff.buffer.start; DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ", mtctx->nextJobID, (U32)srcSize, (U32)mtctx->inBuff.prefix.size); mtctx->jobs[jobID].src.start = src; mtctx->jobs[jobID].src.size = srcSize; assert(mtctx->inBuff.filled >= srcSize); mtctx->jobs[jobID].prefix = mtctx->inBuff.prefix; mtctx->jobs[jobID].consumed = 0; mtctx->jobs[jobID].cSize = 0; mtctx->jobs[jobID].params = mtctx->params; mtctx->jobs[jobID].cdict = mtctx->nextJobID==0 ? mtctx->cdict : NULL; mtctx->jobs[jobID].fullFrameSize = mtctx->frameContentSize; mtctx->jobs[jobID].dstBuff = g_nullBuffer; mtctx->jobs[jobID].cctxPool = mtctx->cctxPool; mtctx->jobs[jobID].bufPool = mtctx->bufPool; mtctx->jobs[jobID].seqPool = mtctx->seqPool; mtctx->jobs[jobID].serial = &mtctx->serial; mtctx->jobs[jobID].jobID = mtctx->nextJobID; mtctx->jobs[jobID].firstJob = (mtctx->nextJobID==0); mtctx->jobs[jobID].lastJob = endFrame; mtctx->jobs[jobID].frameChecksumNeeded = mtctx->params.fParams.checksumFlag && endFrame && (mtctx->nextJobID>0); mtctx->jobs[jobID].dstFlushed = 0; /* Update the round buffer pos and clear the input buffer to be reset */ mtctx->roundBuff.pos += srcSize; mtctx->inBuff.buffer = g_nullBuffer; mtctx->inBuff.filled = 0; /* Set the prefix */ if (!endFrame) { size_t const newPrefixSize = MIN(srcSize, mtctx->targetPrefixSize); mtctx->inBuff.prefix.start = src + srcSize - newPrefixSize; mtctx->inBuff.prefix.size = newPrefixSize; } else { /* endFrame==1 => no need for another input buffer */ mtctx->inBuff.prefix = kNullRange; mtctx->frameEnded = endFrame; if (mtctx->nextJobID == 0) { /* single job exception : checksum is already calculated directly within worker thread */ mtctx->params.fParams.checksumFlag = 0; } } if ( (srcSize == 0) && (mtctx->nextJobID>0)/*single job must also write frame header*/ ) { DEBUGLOG(5, "ZSTDMT_createCompressionJob: creating a last empty block to end frame"); assert(endOp == ZSTD_e_end); /* only possible case : need to end the frame with an empty last block */ ZSTDMT_writeLastEmptyBlock(mtctx->jobs + jobID); mtctx->nextJobID++; return 0; } } DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes (end:%u, jobNb == %u (mod:%u))", mtctx->nextJobID, (U32)mtctx->jobs[jobID].src.size, mtctx->jobs[jobID].lastJob, mtctx->nextJobID, jobID); if (POOL_tryAdd(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[jobID])) { mtctx->nextJobID++; mtctx->jobReady = 0; } else { DEBUGLOG(5, "ZSTDMT_createCompressionJob: no worker available for job %u", mtctx->nextJobID); mtctx->jobReady = 1; } return 0; } /*! ZSTDMT_flushProduced() : * flush whatever data has been produced but not yet flushed in current job. * move to next job if current one is fully flushed. * `output` : `pos` will be updated with amount of data flushed . * `blockToFlush` : if >0, the function will block and wait if there is no data available to flush . * @return : amount of data remaining within internal buffer, 0 if no more, 1 if unknown but > 0, or an error code */ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, unsigned blockToFlush, ZSTD_EndDirective end) { unsigned const wJobID = mtctx->doneJobID & mtctx->jobIDMask; DEBUGLOG(5, "ZSTDMT_flushProduced (blocking:%u , job %u <= %u)", blockToFlush, mtctx->doneJobID, mtctx->nextJobID); assert(output->size >= output->pos); ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[wJobID].job_mutex); if ( blockToFlush && (mtctx->doneJobID < mtctx->nextJobID) ) { assert(mtctx->jobs[wJobID].dstFlushed <= mtctx->jobs[wJobID].cSize); while (mtctx->jobs[wJobID].dstFlushed == mtctx->jobs[wJobID].cSize) { /* nothing to flush */ if (mtctx->jobs[wJobID].consumed == mtctx->jobs[wJobID].src.size) { DEBUGLOG(5, "job %u is completely consumed (%u == %u) => don't wait for cond, there will be none", mtctx->doneJobID, (U32)mtctx->jobs[wJobID].consumed, (U32)mtctx->jobs[wJobID].src.size); break; } DEBUGLOG(5, "waiting for something to flush from job %u (currently flushed: %u bytes)", mtctx->doneJobID, (U32)mtctx->jobs[wJobID].dstFlushed); ZSTD_pthread_cond_wait(&mtctx->jobs[wJobID].job_cond, &mtctx->jobs[wJobID].job_mutex); /* block when nothing to flush but some to come */ } } /* try to flush something */ { size_t cSize = mtctx->jobs[wJobID].cSize; /* shared */ size_t const srcConsumed = mtctx->jobs[wJobID].consumed; /* shared */ size_t const srcSize = mtctx->jobs[wJobID].src.size; /* read-only, could be done after mutex lock, but no-declaration-after-statement */ ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex); if (ZSTD_isError(cSize)) { DEBUGLOG(5, "ZSTDMT_flushProduced: job %u : compression error detected : %s", mtctx->doneJobID, ZSTD_getErrorName(cSize)); ZSTDMT_waitForAllJobsCompleted(mtctx); ZSTDMT_releaseAllJobResources(mtctx); return cSize; } /* add frame checksum if necessary (can only happen once) */ assert(srcConsumed <= srcSize); if ( (srcConsumed == srcSize) /* job completed -> worker no longer active */ && mtctx->jobs[wJobID].frameChecksumNeeded ) { U32 const checksum = (U32)XXH64_digest(&mtctx->serial.xxhState); DEBUGLOG(4, "ZSTDMT_flushProduced: writing checksum : %08X \n", checksum); MEM_writeLE32((char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].cSize, checksum); cSize += 4; mtctx->jobs[wJobID].cSize += 4; /* can write this shared value, as worker is no longer active */ mtctx->jobs[wJobID].frameChecksumNeeded = 0; } if (cSize > 0) { /* compression is ongoing or completed */ size_t const toFlush = MIN(cSize - mtctx->jobs[wJobID].dstFlushed, output->size - output->pos); DEBUGLOG(5, "ZSTDMT_flushProduced: Flushing %u bytes from job %u (completion:%u/%u, generated:%u)", (U32)toFlush, mtctx->doneJobID, (U32)srcConsumed, (U32)srcSize, (U32)cSize); assert(mtctx->doneJobID < mtctx->nextJobID); assert(cSize >= mtctx->jobs[wJobID].dstFlushed); assert(mtctx->jobs[wJobID].dstBuff.start != NULL); if (toFlush > 0) { memcpy((char*)output->dst + output->pos, (const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed, toFlush); } output->pos += toFlush; mtctx->jobs[wJobID].dstFlushed += toFlush; /* can write : this value is only used by mtctx */ if ( (srcConsumed == srcSize) /* job is completed */ && (mtctx->jobs[wJobID].dstFlushed == cSize) ) { /* output buffer fully flushed => free this job position */ DEBUGLOG(5, "Job %u completed (%u bytes), moving to next one", mtctx->doneJobID, (U32)mtctx->jobs[wJobID].dstFlushed); ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[wJobID].dstBuff); DEBUGLOG(5, "dstBuffer released"); mtctx->jobs[wJobID].dstBuff = g_nullBuffer; mtctx->jobs[wJobID].cSize = 0; /* ensure this job slot is considered "not started" in future check */ mtctx->consumed += srcSize; mtctx->produced += cSize; mtctx->doneJobID++; } } /* return value : how many bytes left in buffer ; fake it to 1 when unknown but >0 */ if (cSize > mtctx->jobs[wJobID].dstFlushed) return (cSize - mtctx->jobs[wJobID].dstFlushed); if (srcSize > srcConsumed) return 1; /* current job not completely compressed */ } if (mtctx->doneJobID < mtctx->nextJobID) return 1; /* some more jobs ongoing */ if (mtctx->jobReady) return 1; /* one job is ready to push, just not yet in the list */ if (mtctx->inBuff.filled > 0) return 1; /* input is not empty, and still needs to be converted into a job */ mtctx->allJobsCompleted = mtctx->frameEnded; /* all jobs are entirely flushed => if this one is last one, frame is completed */ if (end == ZSTD_e_end) return !mtctx->frameEnded; /* for ZSTD_e_end, question becomes : is frame completed ? instead of : are internal buffers fully flushed ? */ return 0; /* internal buffers fully flushed */ } /** * Returns the range of data used by the earliest job that is not yet complete. * If the data of the first job is broken up into two segments, we cover both * sections. */ static range_t ZSTDMT_getInputDataInUse(ZSTDMT_CCtx* mtctx) { unsigned const firstJobID = mtctx->doneJobID; unsigned const lastJobID = mtctx->nextJobID; unsigned jobID; for (jobID = firstJobID; jobID < lastJobID; ++jobID) { unsigned const wJobID = jobID & mtctx->jobIDMask; size_t consumed; ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[wJobID].job_mutex); consumed = mtctx->jobs[wJobID].consumed; ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex); if (consumed < mtctx->jobs[wJobID].src.size) { range_t range = mtctx->jobs[wJobID].prefix; if (range.size == 0) { /* Empty prefix */ range = mtctx->jobs[wJobID].src; } /* Job source in multiple segments not supported yet */ assert(range.start <= mtctx->jobs[wJobID].src.start); return range; } } return kNullRange; } /** * Returns non-zero iff buffer and range overlap. */ static int ZSTDMT_isOverlapped(buffer_t buffer, range_t range) { BYTE const* const bufferStart = (BYTE const*)buffer.start; BYTE const* const bufferEnd = bufferStart + buffer.capacity; BYTE const* const rangeStart = (BYTE const*)range.start; BYTE const* const rangeEnd = range.size != 0 ? rangeStart + range.size : rangeStart; if (rangeStart == NULL || bufferStart == NULL) return 0; /* Empty ranges cannot overlap */ if (bufferStart == bufferEnd || rangeStart == rangeEnd) return 0; return bufferStart < rangeEnd && rangeStart < bufferEnd; } static int ZSTDMT_doesOverlapWindow(buffer_t buffer, ZSTD_window_t window) { range_t extDict; range_t prefix; DEBUGLOG(5, "ZSTDMT_doesOverlapWindow"); extDict.start = window.dictBase + window.lowLimit; extDict.size = window.dictLimit - window.lowLimit; prefix.start = window.base + window.dictLimit; prefix.size = window.nextSrc - (window.base + window.dictLimit); DEBUGLOG(5, "extDict [0x%zx, 0x%zx)", (size_t)extDict.start, (size_t)extDict.start + extDict.size); DEBUGLOG(5, "prefix [0x%zx, 0x%zx)", (size_t)prefix.start, (size_t)prefix.start + prefix.size); return ZSTDMT_isOverlapped(buffer, extDict) || ZSTDMT_isOverlapped(buffer, prefix); } static void ZSTDMT_waitForLdmComplete(ZSTDMT_CCtx* mtctx, buffer_t buffer) { if (mtctx->params.ldmParams.enableLdm) { ZSTD_pthread_mutex_t* mutex = &mtctx->serial.ldmWindowMutex; DEBUGLOG(5, "ZSTDMT_waitForLdmComplete"); DEBUGLOG(5, "source [0x%zx, 0x%zx)", (size_t)buffer.start, (size_t)buffer.start + buffer.capacity); ZSTD_PTHREAD_MUTEX_LOCK(mutex); while (ZSTDMT_doesOverlapWindow(buffer, mtctx->serial.ldmWindow)) { DEBUGLOG(5, "Waiting for LDM to finish..."); ZSTD_pthread_cond_wait(&mtctx->serial.ldmWindowCond, mutex); } DEBUGLOG(6, "Done waiting for LDM to finish"); ZSTD_pthread_mutex_unlock(mutex); } } /** * Attempts to set the inBuff to the next section to fill. * If any part of the new section is still in use we give up. * Returns non-zero if the buffer is filled. */ static int ZSTDMT_tryGetInputRange(ZSTDMT_CCtx* mtctx) { range_t const inUse = ZSTDMT_getInputDataInUse(mtctx); size_t const spaceLeft = mtctx->roundBuff.capacity - mtctx->roundBuff.pos; size_t const target = mtctx->targetSectionSize; buffer_t buffer; DEBUGLOG(5, "ZSTDMT_tryGetInputRange"); assert(mtctx->inBuff.buffer.start == NULL); assert(mtctx->roundBuff.capacity >= target); if (spaceLeft < target) { /* ZSTD_invalidateRepCodes() doesn't work for extDict variants. * Simply copy the prefix to the beginning in that case. */ BYTE* const start = (BYTE*)mtctx->roundBuff.buffer; size_t const prefixSize = mtctx->inBuff.prefix.size; buffer.start = start; buffer.capacity = prefixSize; if (ZSTDMT_isOverlapped(buffer, inUse)) { DEBUGLOG(5, "Waiting for buffer..."); return 0; } ZSTDMT_waitForLdmComplete(mtctx, buffer); memmove(start, mtctx->inBuff.prefix.start, prefixSize); mtctx->inBuff.prefix.start = start; mtctx->roundBuff.pos = prefixSize; } buffer.start = mtctx->roundBuff.buffer + mtctx->roundBuff.pos; buffer.capacity = target; if (ZSTDMT_isOverlapped(buffer, inUse)) { DEBUGLOG(5, "Waiting for buffer..."); return 0; } assert(!ZSTDMT_isOverlapped(buffer, mtctx->inBuff.prefix)); ZSTDMT_waitForLdmComplete(mtctx, buffer); DEBUGLOG(5, "Using prefix range [%zx, %zx)", (size_t)mtctx->inBuff.prefix.start, (size_t)mtctx->inBuff.prefix.start + mtctx->inBuff.prefix.size); DEBUGLOG(5, "Using source range [%zx, %zx)", (size_t)buffer.start, (size_t)buffer.start + buffer.capacity); mtctx->inBuff.buffer = buffer; mtctx->inBuff.filled = 0; assert(mtctx->roundBuff.pos + buffer.capacity <= mtctx->roundBuff.capacity); return 1; } typedef struct { size_t toLoad; /* The number of bytes to load from the input. */ int flush; /* Boolean declaring if we must flush because we found a synchronization point. */ } syncPoint_t; /** * Searches through the input for a synchronization point. If one is found, we * will instruct the caller to flush, and return the number of bytes to load. * Otherwise, we will load as many bytes as possible and instruct the caller * to continue as normal. */ static syncPoint_t findSynchronizationPoint(ZSTDMT_CCtx const* mtctx, ZSTD_inBuffer const input) { BYTE const* const istart = (BYTE const*)input.src + input.pos; U64 const primePower = mtctx->rsync.primePower; U64 const hitMask = mtctx->rsync.hitMask; syncPoint_t syncPoint; U64 hash; BYTE const* prev; size_t pos; syncPoint.toLoad = MIN(input.size - input.pos, mtctx->targetSectionSize - mtctx->inBuff.filled); syncPoint.flush = 0; if (!mtctx->params.rsyncable) /* Rsync is disabled. */ return syncPoint; if (mtctx->inBuff.filled + syncPoint.toLoad < RSYNC_LENGTH) /* Not enough to compute the hash. * We will miss any synchronization points in this RSYNC_LENGTH byte * window. However, since it depends only in the internal buffers, if the * state is already synchronized, we will remain synchronized. * Additionally, the probability that we miss a synchronization point is * low: RSYNC_LENGTH / targetSectionSize. */ return syncPoint; /* Initialize the loop variables. */ if (mtctx->inBuff.filled >= RSYNC_LENGTH) { /* We have enough bytes buffered to initialize the hash. * Start scanning at the beginning of the input. */ pos = 0; prev = (BYTE const*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled - RSYNC_LENGTH; hash = ZSTD_rollingHash_compute(prev, RSYNC_LENGTH); } else { /* We don't have enough bytes buffered to initialize the hash, but * we know we have at least RSYNC_LENGTH bytes total. * Start scanning after the first RSYNC_LENGTH bytes less the bytes * already buffered. */ pos = RSYNC_LENGTH - mtctx->inBuff.filled; prev = (BYTE const*)mtctx->inBuff.buffer.start - pos; hash = ZSTD_rollingHash_compute(mtctx->inBuff.buffer.start, mtctx->inBuff.filled); hash = ZSTD_rollingHash_append(hash, istart, pos); } /* Starting with the hash of the previous RSYNC_LENGTH bytes, roll * through the input. If we hit a synchronization point, then cut the * job off, and tell the compressor to flush the job. Otherwise, load * all the bytes and continue as normal. * If we go too long without a synchronization point (targetSectionSize) * then a block will be emitted anyways, but this is okay, since if we * are already synchronized we will remain synchronized. */ for (; pos < syncPoint.toLoad; ++pos) { BYTE const toRemove = pos < RSYNC_LENGTH ? prev[pos] : istart[pos - RSYNC_LENGTH]; /* if (pos >= RSYNC_LENGTH) assert(ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash); */ hash = ZSTD_rollingHash_rotate(hash, toRemove, istart[pos], primePower); if ((hash & hitMask) == hitMask) { syncPoint.toLoad = pos + 1; syncPoint.flush = 1; break; } } return syncPoint; } size_t ZSTDMT_nextInputSizeHint(const ZSTDMT_CCtx* mtctx) { size_t hintInSize = mtctx->targetSectionSize - mtctx->inBuff.filled; if (hintInSize==0) hintInSize = mtctx->targetSectionSize; return hintInSize; } /** ZSTDMT_compressStream_generic() : * internal use only - exposed to be invoked from zstd_compress.c * assumption : output and input are valid (pos <= size) * @return : minimum amount of data remaining to flush, 0 if none */ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input, ZSTD_EndDirective endOp) { unsigned forwardInputProgress = 0; DEBUGLOG(5, "ZSTDMT_compressStream_generic (endOp=%u, srcSize=%u)", (U32)endOp, (U32)(input->size - input->pos)); assert(output->pos <= output->size); assert(input->pos <= input->size); if (mtctx->singleBlockingThread) { /* delegate to single-thread (synchronous) */ return ZSTD_compressStream2(mtctx->cctxPool->cctx[0], output, input, endOp); } if ((mtctx->frameEnded) && (endOp==ZSTD_e_continue)) { /* current frame being ended. Only flush/end are allowed */ return ERROR(stage_wrong); } /* single-pass shortcut (note : synchronous-mode) */ if ( (!mtctx->params.rsyncable) /* rsyncable mode is disabled */ && (mtctx->nextJobID == 0) /* just started */ && (mtctx->inBuff.filled == 0) /* nothing buffered */ && (!mtctx->jobReady) /* no job already created */ && (endOp == ZSTD_e_end) /* end order */ && (output->size - output->pos >= ZSTD_compressBound(input->size - input->pos)) ) { /* enough space in dst */ size_t const cSize = ZSTDMT_compress_advanced_internal(mtctx, (char*)output->dst + output->pos, output->size - output->pos, (const char*)input->src + input->pos, input->size - input->pos, mtctx->cdict, mtctx->params); if (ZSTD_isError(cSize)) return cSize; input->pos = input->size; output->pos += cSize; mtctx->allJobsCompleted = 1; mtctx->frameEnded = 1; return 0; } /* fill input buffer */ if ( (!mtctx->jobReady) && (input->size > input->pos) ) { /* support NULL input */ if (mtctx->inBuff.buffer.start == NULL) { assert(mtctx->inBuff.filled == 0); /* Can't fill an empty buffer */ if (!ZSTDMT_tryGetInputRange(mtctx)) { /* It is only possible for this operation to fail if there are * still compression jobs ongoing. */ DEBUGLOG(5, "ZSTDMT_tryGetInputRange failed"); assert(mtctx->doneJobID != mtctx->nextJobID); } else DEBUGLOG(5, "ZSTDMT_tryGetInputRange completed successfully : mtctx->inBuff.buffer.start = %p", mtctx->inBuff.buffer.start); } if (mtctx->inBuff.buffer.start != NULL) { syncPoint_t const syncPoint = findSynchronizationPoint(mtctx, *input); if (syncPoint.flush && endOp == ZSTD_e_continue) { endOp = ZSTD_e_flush; } assert(mtctx->inBuff.buffer.capacity >= mtctx->targetSectionSize); DEBUGLOG(5, "ZSTDMT_compressStream_generic: adding %u bytes on top of %u to buffer of size %u", (U32)syncPoint.toLoad, (U32)mtctx->inBuff.filled, (U32)mtctx->targetSectionSize); memcpy((char*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled, (const char*)input->src + input->pos, syncPoint.toLoad); input->pos += syncPoint.toLoad; mtctx->inBuff.filled += syncPoint.toLoad; forwardInputProgress = syncPoint.toLoad>0; } if ((input->pos < input->size) && (endOp == ZSTD_e_end)) endOp = ZSTD_e_flush; /* can't end now : not all input consumed */ } if ( (mtctx->jobReady) || (mtctx->inBuff.filled >= mtctx->targetSectionSize) /* filled enough : let's compress */ || ((endOp != ZSTD_e_continue) && (mtctx->inBuff.filled > 0)) /* something to flush : let's go */ || ((endOp == ZSTD_e_end) && (!mtctx->frameEnded)) ) { /* must finish the frame with a zero-size block */ size_t const jobSize = mtctx->inBuff.filled; assert(mtctx->inBuff.filled <= mtctx->targetSectionSize); FORWARD_IF_ERROR( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp) , ""); } /* check for potential compressed data ready to be flushed */ { size_t const remainingToFlush = ZSTDMT_flushProduced(mtctx, output, !forwardInputProgress, endOp); /* block if there was no forward input progress */ if (input->pos < input->size) return MAX(remainingToFlush, 1); /* input not consumed : do not end flush yet */ DEBUGLOG(5, "end of ZSTDMT_compressStream_generic: remainingToFlush = %u", (U32)remainingToFlush); return remainingToFlush; } } size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { FORWARD_IF_ERROR( ZSTDMT_compressStream_generic(mtctx, output, input, ZSTD_e_continue) , ""); /* recommended next input size : fill current input buffer */ return mtctx->targetSectionSize - mtctx->inBuff.filled; /* note : could be zero when input buffer is fully filled and no more availability to create new job */ } static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_EndDirective endFrame) { size_t const srcSize = mtctx->inBuff.filled; DEBUGLOG(5, "ZSTDMT_flushStream_internal"); if ( mtctx->jobReady /* one job ready for a worker to pick up */ || (srcSize > 0) /* still some data within input buffer */ || ((endFrame==ZSTD_e_end) && !mtctx->frameEnded)) { /* need a last 0-size block to end frame */ DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job (%u bytes, end:%u)", (U32)srcSize, (U32)endFrame); FORWARD_IF_ERROR( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) , ""); } /* check if there is any data available to flush */ return ZSTDMT_flushProduced(mtctx, output, 1 /* blockToFlush */, endFrame); } size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output) { DEBUGLOG(5, "ZSTDMT_flushStream"); if (mtctx->singleBlockingThread) return ZSTD_flushStream(mtctx->cctxPool->cctx[0], output); return ZSTDMT_flushStream_internal(mtctx, output, ZSTD_e_flush); } size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output) { DEBUGLOG(4, "ZSTDMT_endStream"); if (mtctx->singleBlockingThread) return ZSTD_endStream(mtctx->cctxPool->cctx[0], output); return ZSTDMT_flushStream_internal(mtctx, output, ZSTD_e_end); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/compress/zstdmt_compress.h0000644000076500000240000002163014601576577025131 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTDMT_COMPRESS_H #define ZSTDMT_COMPRESS_H #if defined (__cplusplus) extern "C" { #endif /* Note : This is an internal API. * These APIs used to be exposed with ZSTDLIB_API, * because it used to be the only way to invoke MT compression. * Now, it's recommended to use ZSTD_compress2 and ZSTD_compressStream2() * instead. * * If you depend on these APIs and can't switch, then define * ZSTD_LEGACY_MULTITHREADED_API when making the dynamic library. * However, we may completely remove these functions in a future * release, so please switch soon. * * This API requires ZSTD_MULTITHREAD to be defined during compilation, * otherwise ZSTDMT_createCCtx*() will fail. */ #ifdef ZSTD_LEGACY_MULTITHREADED_API # define ZSTDMT_API ZSTDLIB_API #else # define ZSTDMT_API #endif /* === Dependencies === */ #include /* size_t */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters */ #include "../zstd.h" /* ZSTD_inBuffer, ZSTD_outBuffer, ZSTDLIB_API */ /* === Constants === */ #ifndef ZSTDMT_NBWORKERS_MAX # define ZSTDMT_NBWORKERS_MAX 200 #endif #ifndef ZSTDMT_JOBSIZE_MIN # define ZSTDMT_JOBSIZE_MIN (1 MB) #endif #define ZSTDMT_JOBLOG_MAX (MEM_32bits() ? 29 : 30) #define ZSTDMT_JOBSIZE_MAX (MEM_32bits() ? (512 MB) : (1024 MB)) /* === Memory management === */ typedef struct ZSTDMT_CCtx_s ZSTDMT_CCtx; /* Requires ZSTD_MULTITHREAD to be defined during compilation, otherwise it will return NULL. */ ZSTDMT_API ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers); /* Requires ZSTD_MULTITHREAD to be defined during compilation, otherwise it will return NULL. */ ZSTDMT_API ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem); ZSTDMT_API size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx); ZSTDMT_API size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx); /* === Simple one-pass compression function === */ ZSTDMT_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel); /* === Streaming functions === */ ZSTDMT_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel); ZSTDMT_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize); /**< if srcSize is not known at reset time, use ZSTD_CONTENTSIZE_UNKNOWN. Note: for compatibility with older programs, 0 means the same as ZSTD_CONTENTSIZE_UNKNOWN, but it will change in the future to mean "empty" */ ZSTDMT_API size_t ZSTDMT_nextInputSizeHint(const ZSTDMT_CCtx* mtctx); ZSTDMT_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input); ZSTDMT_API size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ ZSTDMT_API size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ /* === Advanced functions and parameters === */ ZSTDMT_API size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const ZSTD_CDict* cdict, ZSTD_parameters params, int overlapLog); ZSTDMT_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, const void* dict, size_t dictSize, /* dict can be released after init, a local copy is preserved within zcs */ ZSTD_parameters params, unsigned long long pledgedSrcSize); /* pledgedSrcSize is optional and can be zero == unknown */ ZSTDMT_API size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx, const ZSTD_CDict* cdict, ZSTD_frameParameters fparams, unsigned long long pledgedSrcSize); /* note : zero means empty */ /* ZSTDMT_parameter : * List of parameters that can be set using ZSTDMT_setMTCtxParameter() */ typedef enum { ZSTDMT_p_jobSize, /* Each job is compressed in parallel. By default, this value is dynamically determined depending on compression parameters. Can be set explicitly here. */ ZSTDMT_p_overlapLog, /* Each job may reload a part of previous job to enhance compression ratio; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window. This is a "sticky" parameter : its value will be re-used on next compression job */ ZSTDMT_p_rsyncable /* Enables rsyncable mode. */ } ZSTDMT_parameter; /* ZSTDMT_setMTCtxParameter() : * allow setting individual parameters, one at a time, among a list of enums defined in ZSTDMT_parameter. * The function must be called typically after ZSTD_createCCtx() but __before ZSTDMT_init*() !__ * Parameters not explicitly reset by ZSTDMT_init*() remain the same in consecutive compression sessions. * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ ZSTDMT_API size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int value); /* ZSTDMT_getMTCtxParameter() : * Query the ZSTDMT_CCtx for a parameter value. * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ ZSTDMT_API size_t ZSTDMT_getMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int* value); /*! ZSTDMT_compressStream_generic() : * Combines ZSTDMT_compressStream() with optional ZSTDMT_flushStream() or ZSTDMT_endStream() * depending on flush directive. * @return : minimum amount of data still to be flushed * 0 if fully flushed * or an error code * note : needs to be init using any ZSTD_initCStream*() variant */ ZSTDMT_API size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input, ZSTD_EndDirective endOp); /* ======================================================== * === Private interface, for use by ZSTD_compress.c === * === Not exposed in libzstd. Never invoke directly === * ======================================================== */ /*! ZSTDMT_toFlushNow() * Tell how many bytes are ready to be flushed immediately. * Probe the oldest active job (not yet entirely flushed) and check its output buffer. * If return 0, it means there is no active job, * or, it means oldest job is still active, but everything produced has been flushed so far, * therefore flushing is limited by speed of oldest job. */ size_t ZSTDMT_toFlushNow(ZSTDMT_CCtx* mtctx); /*! ZSTDMT_CCtxParam_setMTCtxParameter() * like ZSTDMT_setMTCtxParameter(), but into a ZSTD_CCtx_Params */ size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, ZSTDMT_parameter parameter, int value); /*! ZSTDMT_CCtxParam_setNbWorkers() * Set nbWorkers, and clamp it. * Also reset jobSize and overlapLog */ size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers); /*! ZSTDMT_updateCParams_whileCompressing() : * Updates only a selected set of compression parameters, to remain compatible with current frame. * New parameters will be applied to next compression job. */ void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* cctxParams); /*! ZSTDMT_getFrameProgression(): * tells how much data has been consumed (input) and produced (output) for current frame. * able to count progression inside worker threads. */ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx); /*! ZSTDMT_initCStream_internal() : * Private use only. Init streaming operation. * expects params to be valid. * must receive dict, or cdict, or none, but not both. * @return : 0, or an error code */ size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize); #if defined (__cplusplus) } #endif #endif /* ZSTDMT_COMPRESS_H */ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1841013 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/decompress/0000755000076500000240000000000014601577064022017 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/decompress/huf_decompress.c0000644000076500000240000014405014601576577025205 0ustar00twstaff/* ****************************************************************** * huff0 huffman decoder, * part of Finite State Entropy library * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc. * * You can contact the author at : * - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. ****************************************************************** */ /* ************************************************************** * Dependencies ****************************************************************/ #include /* memcpy, memset */ #include "../common/compiler.h" #include "../common/bitstream.h" /* BIT_* */ #include "../common/fse.h" /* to compress headers */ #define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "../common/error_private.h" /* ************************************************************** * Macros ****************************************************************/ /* These two optional macros force the use one way or another of the two * Huffman decompression implementations. You can't force in both directions * at the same time. */ #if defined(HUF_FORCE_DECOMPRESS_X1) && \ defined(HUF_FORCE_DECOMPRESS_X2) #error "Cannot force the use of the X1 and X2 decoders at the same time!" #endif /* ************************************************************** * Error Management ****************************************************************/ #define HUF_isError ERR_isError /* ************************************************************** * Byte alignment for workSpace management ****************************************************************/ #define HUF_ALIGN(x, a) HUF_ALIGN_MASK((x), (a) - 1) #define HUF_ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) /* ************************************************************** * BMI2 Variant Wrappers ****************************************************************/ #if DYNAMIC_BMI2 #define HUF_DGEN(fn) \ \ static size_t fn##_default( \ void* dst, size_t dstSize, \ const void* cSrc, size_t cSrcSize, \ const HUF_DTable* DTable) \ { \ return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ } \ \ static TARGET_ATTRIBUTE("bmi2") size_t fn##_bmi2( \ void* dst, size_t dstSize, \ const void* cSrc, size_t cSrcSize, \ const HUF_DTable* DTable) \ { \ return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ } \ \ static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ { \ if (bmi2) { \ return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); \ } \ return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \ } #else #define HUF_DGEN(fn) \ static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ { \ (void)bmi2; \ return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ } #endif /*-***************************/ /* generic DTableDesc */ /*-***************************/ typedef struct { BYTE maxTableLog; BYTE tableType; BYTE tableLog; BYTE reserved; } DTableDesc; static DTableDesc HUF_getDTableDesc(const HUF_DTable* table) { DTableDesc dtd; memcpy(&dtd, table, sizeof(dtd)); return dtd; } #ifndef HUF_FORCE_DECOMPRESS_X2 /*-***************************/ /* single-symbol decoding */ /*-***************************/ typedef struct { BYTE byte; BYTE nbBits; } HUF_DEltX1; /* single-symbol decoding */ size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize) { U32 tableLog = 0; U32 nbSymbols = 0; size_t iSize; void* const dtPtr = DTable + 1; HUF_DEltX1* const dt = (HUF_DEltX1*)dtPtr; U32* rankVal; BYTE* huffWeight; size_t spaceUsed32 = 0; rankVal = (U32 *)workSpace + spaceUsed32; spaceUsed32 += HUF_TABLELOG_ABSOLUTEMAX + 1; huffWeight = (BYTE *)((U32 *)workSpace + spaceUsed32); spaceUsed32 += HUF_ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2; if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge); DEBUG_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable)); /* memset(huffWeight, 0, sizeof(huffWeight)); */ /* is not necessary, even though some analyzer complain ... */ iSize = HUF_readStats(huffWeight, HUF_SYMBOLVALUE_MAX + 1, rankVal, &nbSymbols, &tableLog, src, srcSize); if (HUF_isError(iSize)) return iSize; /* Table header */ { DTableDesc dtd = HUF_getDTableDesc(DTable); if (tableLog > (U32)(dtd.maxTableLog+1)) return ERROR(tableLog_tooLarge); /* DTable too small, Huffman tree cannot fit in */ dtd.tableType = 0; dtd.tableLog = (BYTE)tableLog; memcpy(DTable, &dtd, sizeof(dtd)); } /* Calculate starting value for each rank */ { U32 n, nextRankStart = 0; for (n=1; n> 1; size_t const uStart = rankVal[w]; size_t const uEnd = uStart + length; size_t u; HUF_DEltX1 D; D.byte = (BYTE)n; D.nbBits = (BYTE)(tableLog + 1 - w); rankVal[w] = (U32)uEnd; if (length < 4) { /* Use length in the loop bound so the compiler knows it is short. */ for (u = 0; u < length; ++u) dt[uStart + u] = D; } else { /* Unroll the loop 4 times, we know it is a power of 2. */ for (u = uStart; u < uEnd; u += 4) { dt[u + 0] = D; dt[u + 1] = D; dt[u + 2] = D; dt[u + 3] = D; } } } } return iSize; } size_t HUF_readDTableX1(HUF_DTable* DTable, const void* src, size_t srcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; return HUF_readDTableX1_wksp(DTable, src, srcSize, workSpace, sizeof(workSpace)); } FORCE_INLINE_TEMPLATE BYTE HUF_decodeSymbolX1(BIT_DStream_t* Dstream, const HUF_DEltX1* dt, const U32 dtLog) { size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */ BYTE const c = dt[val].byte; BIT_skipBits(Dstream, dt[val].nbBits); return c; } #define HUF_DECODE_SYMBOLX1_0(ptr, DStreamPtr) \ *ptr++ = HUF_decodeSymbolX1(DStreamPtr, dt, dtLog) #define HUF_DECODE_SYMBOLX1_1(ptr, DStreamPtr) \ if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \ HUF_DECODE_SYMBOLX1_0(ptr, DStreamPtr) #define HUF_DECODE_SYMBOLX1_2(ptr, DStreamPtr) \ if (MEM_64bits()) \ HUF_DECODE_SYMBOLX1_0(ptr, DStreamPtr) HINT_INLINE size_t HUF_decodeStreamX1(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX1* const dt, const U32 dtLog) { BYTE* const pStart = p; /* up to 4 symbols at a time */ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-3)) { HUF_DECODE_SYMBOLX1_2(p, bitDPtr); HUF_DECODE_SYMBOLX1_1(p, bitDPtr); HUF_DECODE_SYMBOLX1_2(p, bitDPtr); HUF_DECODE_SYMBOLX1_0(p, bitDPtr); } /* [0-3] symbols remaining */ if (MEM_32bits()) while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd)) HUF_DECODE_SYMBOLX1_0(p, bitDPtr); /* no more data to retrieve from bitstream, no need to reload */ while (p < pEnd) HUF_DECODE_SYMBOLX1_0(p, bitDPtr); return pEnd-pStart; } FORCE_INLINE_TEMPLATE size_t HUF_decompress1X1_usingDTable_internal_body( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { BYTE* op = (BYTE*)dst; BYTE* const oend = op + dstSize; const void* dtPtr = DTable + 1; const HUF_DEltX1* const dt = (const HUF_DEltX1*)dtPtr; BIT_DStream_t bitD; DTableDesc const dtd = HUF_getDTableDesc(DTable); U32 const dtLog = dtd.tableLog; CHECK_F( BIT_initDStream(&bitD, cSrc, cSrcSize) ); HUF_decodeStreamX1(op, &bitD, oend, dt, dtLog); if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected); return dstSize; } FORCE_INLINE_TEMPLATE size_t HUF_decompress4X1_usingDTable_internal_body( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { /* Check */ if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ { const BYTE* const istart = (const BYTE*) cSrc; BYTE* const ostart = (BYTE*) dst; BYTE* const oend = ostart + dstSize; BYTE* const olimit = oend - 3; const void* const dtPtr = DTable + 1; const HUF_DEltX1* const dt = (const HUF_DEltX1*)dtPtr; /* Init */ BIT_DStream_t bitD1; BIT_DStream_t bitD2; BIT_DStream_t bitD3; BIT_DStream_t bitD4; size_t const length1 = MEM_readLE16(istart); size_t const length2 = MEM_readLE16(istart+2); size_t const length3 = MEM_readLE16(istart+4); size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); const BYTE* const istart1 = istart + 6; /* jumpTable */ const BYTE* const istart2 = istart1 + length1; const BYTE* const istart3 = istart2 + length2; const BYTE* const istart4 = istart3 + length3; const size_t segmentSize = (dstSize+3) / 4; BYTE* const opStart2 = ostart + segmentSize; BYTE* const opStart3 = opStart2 + segmentSize; BYTE* const opStart4 = opStart3 + segmentSize; BYTE* op1 = ostart; BYTE* op2 = opStart2; BYTE* op3 = opStart3; BYTE* op4 = opStart4; DTableDesc const dtd = HUF_getDTableDesc(DTable); U32 const dtLog = dtd.tableLog; U32 endSignal = 1; if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ CHECK_F( BIT_initDStream(&bitD1, istart1, length1) ); CHECK_F( BIT_initDStream(&bitD2, istart2, length2) ); CHECK_F( BIT_initDStream(&bitD3, istart3, length3) ); CHECK_F( BIT_initDStream(&bitD4, istart4, length4) ); /* up to 16 symbols per loop (4 symbols per stream) in 64-bit mode */ for ( ; (endSignal) & (op4 < olimit) ; ) { HUF_DECODE_SYMBOLX1_2(op1, &bitD1); HUF_DECODE_SYMBOLX1_2(op2, &bitD2); HUF_DECODE_SYMBOLX1_2(op3, &bitD3); HUF_DECODE_SYMBOLX1_2(op4, &bitD4); HUF_DECODE_SYMBOLX1_1(op1, &bitD1); HUF_DECODE_SYMBOLX1_1(op2, &bitD2); HUF_DECODE_SYMBOLX1_1(op3, &bitD3); HUF_DECODE_SYMBOLX1_1(op4, &bitD4); HUF_DECODE_SYMBOLX1_2(op1, &bitD1); HUF_DECODE_SYMBOLX1_2(op2, &bitD2); HUF_DECODE_SYMBOLX1_2(op3, &bitD3); HUF_DECODE_SYMBOLX1_2(op4, &bitD4); HUF_DECODE_SYMBOLX1_0(op1, &bitD1); HUF_DECODE_SYMBOLX1_0(op2, &bitD2); HUF_DECODE_SYMBOLX1_0(op3, &bitD3); HUF_DECODE_SYMBOLX1_0(op4, &bitD4); endSignal &= BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished; endSignal &= BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished; endSignal &= BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished; endSignal &= BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished; } /* check corruption */ /* note : should not be necessary : op# advance in lock step, and we control op4. * but curiously, binary generated by gcc 7.2 & 7.3 with -mbmi2 runs faster when >=1 test is present */ if (op1 > opStart2) return ERROR(corruption_detected); if (op2 > opStart3) return ERROR(corruption_detected); if (op3 > opStart4) return ERROR(corruption_detected); /* note : op4 supposed already verified within main loop */ /* finish bitStreams one by one */ HUF_decodeStreamX1(op1, &bitD1, opStart2, dt, dtLog); HUF_decodeStreamX1(op2, &bitD2, opStart3, dt, dtLog); HUF_decodeStreamX1(op3, &bitD3, opStart4, dt, dtLog); HUF_decodeStreamX1(op4, &bitD4, oend, dt, dtLog); /* check */ { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); if (!endCheck) return ERROR(corruption_detected); } /* decoded size */ return dstSize; } } typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); HUF_DGEN(HUF_decompress1X1_usingDTable_internal) HUF_DGEN(HUF_decompress4X1_usingDTable_internal) size_t HUF_decompress1X1_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { DTableDesc dtd = HUF_getDTableDesc(DTable); if (dtd.tableType != 0) return ERROR(GENERIC); return HUF_decompress1X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize) { const BYTE* ip = (const BYTE*) cSrc; size_t const hSize = HUF_readDTableX1_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); } size_t HUF_decompress1X1_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; return HUF_decompress1X1_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize, workSpace, sizeof(workSpace)); } size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX); return HUF_decompress1X1_DCtx (DTable, dst, dstSize, cSrc, cSrcSize); } size_t HUF_decompress4X1_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { DTableDesc dtd = HUF_getDTableDesc(DTable); if (dtd.tableType != 0) return ERROR(GENERIC); return HUF_decompress4X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } static size_t HUF_decompress4X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) { const BYTE* ip = (const BYTE*) cSrc; size_t const hSize = HUF_readDTableX1_wksp (dctx, cSrc, cSrcSize, workSpace, wkspSize); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; return HUF_decompress4X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); } size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize) { return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, 0); } size_t HUF_decompress4X1_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, sizeof(workSpace)); } size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX); return HUF_decompress4X1_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); } #endif /* HUF_FORCE_DECOMPRESS_X2 */ #ifndef HUF_FORCE_DECOMPRESS_X1 /* *************************/ /* double-symbols decoding */ /* *************************/ typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX2; /* double-symbols decoding */ typedef struct { BYTE symbol; BYTE weight; } sortedSymbol_t; typedef U32 rankValCol_t[HUF_TABLELOG_MAX + 1]; typedef rankValCol_t rankVal_t[HUF_TABLELOG_MAX]; /* HUF_fillDTableX2Level2() : * `rankValOrigin` must be a table of at least (HUF_TABLELOG_MAX + 1) U32 */ static void HUF_fillDTableX2Level2(HUF_DEltX2* DTable, U32 sizeLog, const U32 consumed, const U32* rankValOrigin, const int minWeight, const sortedSymbol_t* sortedSymbols, const U32 sortedListSize, U32 nbBitsBaseline, U16 baseSeq) { HUF_DEltX2 DElt; U32 rankVal[HUF_TABLELOG_MAX + 1]; /* get pre-calculated rankVal */ memcpy(rankVal, rankValOrigin, sizeof(rankVal)); /* fill skipped values */ if (minWeight>1) { U32 i, skipSize = rankVal[minWeight]; MEM_writeLE16(&(DElt.sequence), baseSeq); DElt.nbBits = (BYTE)(consumed); DElt.length = 1; for (i = 0; i < skipSize; i++) DTable[i] = DElt; } /* fill DTable */ { U32 s; for (s=0; s= 1 */ rankVal[weight] += length; } } } static void HUF_fillDTableX2(HUF_DEltX2* DTable, const U32 targetLog, const sortedSymbol_t* sortedList, const U32 sortedListSize, const U32* rankStart, rankVal_t rankValOrigin, const U32 maxWeight, const U32 nbBitsBaseline) { U32 rankVal[HUF_TABLELOG_MAX + 1]; const int scaleLog = nbBitsBaseline - targetLog; /* note : targetLog >= srcLog, hence scaleLog <= 1 */ const U32 minBits = nbBitsBaseline - maxWeight; U32 s; memcpy(rankVal, rankValOrigin, sizeof(rankVal)); /* fill DTable */ for (s=0; s= minBits) { /* enough room for a second symbol */ U32 sortedRank; int minWeight = nbBits + scaleLog; if (minWeight < 1) minWeight = 1; sortedRank = rankStart[minWeight]; HUF_fillDTableX2Level2(DTable+start, targetLog-nbBits, nbBits, rankValOrigin[nbBits], minWeight, sortedList+sortedRank, sortedListSize-sortedRank, nbBitsBaseline, symbol); } else { HUF_DEltX2 DElt; MEM_writeLE16(&(DElt.sequence), symbol); DElt.nbBits = (BYTE)(nbBits); DElt.length = 1; { U32 const end = start + length; U32 u; for (u = start; u < end; u++) DTable[u] = DElt; } } rankVal[weight] += length; } } size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize) { U32 tableLog, maxW, sizeOfSort, nbSymbols; DTableDesc dtd = HUF_getDTableDesc(DTable); U32 const maxTableLog = dtd.maxTableLog; size_t iSize; void* dtPtr = DTable+1; /* force compiler to avoid strict-aliasing */ HUF_DEltX2* const dt = (HUF_DEltX2*)dtPtr; U32 *rankStart; rankValCol_t* rankVal; U32* rankStats; U32* rankStart0; sortedSymbol_t* sortedSymbol; BYTE* weightList; size_t spaceUsed32 = 0; rankVal = (rankValCol_t *)((U32 *)workSpace + spaceUsed32); spaceUsed32 += (sizeof(rankValCol_t) * HUF_TABLELOG_MAX) >> 2; rankStats = (U32 *)workSpace + spaceUsed32; spaceUsed32 += HUF_TABLELOG_MAX + 1; rankStart0 = (U32 *)workSpace + spaceUsed32; spaceUsed32 += HUF_TABLELOG_MAX + 2; sortedSymbol = (sortedSymbol_t *)workSpace + (spaceUsed32 * sizeof(U32)) / sizeof(sortedSymbol_t); spaceUsed32 += HUF_ALIGN(sizeof(sortedSymbol_t) * (HUF_SYMBOLVALUE_MAX + 1), sizeof(U32)) >> 2; weightList = (BYTE *)((U32 *)workSpace + spaceUsed32); spaceUsed32 += HUF_ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2; if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge); rankStart = rankStart0 + 1; memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1)); DEBUG_STATIC_ASSERT(sizeof(HUF_DEltX2) == sizeof(HUF_DTable)); /* if compiler fails here, assertion is wrong */ if (maxTableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); /* memset(weightList, 0, sizeof(weightList)); */ /* is not necessary, even though some analyzer complain ... */ iSize = HUF_readStats(weightList, HUF_SYMBOLVALUE_MAX + 1, rankStats, &nbSymbols, &tableLog, src, srcSize); if (HUF_isError(iSize)) return iSize; /* check result */ if (tableLog > maxTableLog) return ERROR(tableLog_tooLarge); /* DTable can't fit code depth */ /* find maxWeight */ for (maxW = tableLog; rankStats[maxW]==0; maxW--) {} /* necessarily finds a solution before 0 */ /* Get start index of each weight */ { U32 w, nextRankStart = 0; for (w=1; w> consumed; } } } } HUF_fillDTableX2(dt, maxTableLog, sortedSymbol, sizeOfSort, rankStart0, rankVal, maxW, tableLog+1); dtd.tableLog = (BYTE)maxTableLog; dtd.tableType = 1; memcpy(DTable, &dtd, sizeof(dtd)); return iSize; } size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; return HUF_readDTableX2_wksp(DTable, src, srcSize, workSpace, sizeof(workSpace)); } FORCE_INLINE_TEMPLATE U32 HUF_decodeSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog) { size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ memcpy(op, dt+val, 2); BIT_skipBits(DStream, dt[val].nbBits); return dt[val].length; } FORCE_INLINE_TEMPLATE U32 HUF_decodeLastSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog) { size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ memcpy(op, dt+val, 1); if (dt[val].length==1) BIT_skipBits(DStream, dt[val].nbBits); else { if (DStream->bitsConsumed < (sizeof(DStream->bitContainer)*8)) { BIT_skipBits(DStream, dt[val].nbBits); if (DStream->bitsConsumed > (sizeof(DStream->bitContainer)*8)) /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */ DStream->bitsConsumed = (sizeof(DStream->bitContainer)*8); } } return 1; } #define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) \ ptr += HUF_decodeSymbolX2(ptr, DStreamPtr, dt, dtLog) #define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \ if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \ ptr += HUF_decodeSymbolX2(ptr, DStreamPtr, dt, dtLog) #define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \ if (MEM_64bits()) \ ptr += HUF_decodeSymbolX2(ptr, DStreamPtr, dt, dtLog) HINT_INLINE size_t HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog) { BYTE* const pStart = p; /* up to 8 symbols at a time */ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) { HUF_DECODE_SYMBOLX2_2(p, bitDPtr); HUF_DECODE_SYMBOLX2_1(p, bitDPtr); HUF_DECODE_SYMBOLX2_2(p, bitDPtr); HUF_DECODE_SYMBOLX2_0(p, bitDPtr); } /* closer to end : up to 2 symbols at a time */ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2)) HUF_DECODE_SYMBOLX2_0(p, bitDPtr); while (p <= pEnd-2) HUF_DECODE_SYMBOLX2_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ if (p < pEnd) p += HUF_decodeLastSymbolX2(p, bitDPtr, dt, dtLog); return p-pStart; } FORCE_INLINE_TEMPLATE size_t HUF_decompress1X2_usingDTable_internal_body( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { BIT_DStream_t bitD; /* Init */ CHECK_F( BIT_initDStream(&bitD, cSrc, cSrcSize) ); /* decode */ { BYTE* const ostart = (BYTE*) dst; BYTE* const oend = ostart + dstSize; const void* const dtPtr = DTable+1; /* force compiler to not use strict-aliasing */ const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; DTableDesc const dtd = HUF_getDTableDesc(DTable); HUF_decodeStreamX2(ostart, &bitD, oend, dt, dtd.tableLog); } /* check */ if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected); /* decoded size */ return dstSize; } FORCE_INLINE_TEMPLATE size_t HUF_decompress4X2_usingDTable_internal_body( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ { const BYTE* const istart = (const BYTE*) cSrc; BYTE* const ostart = (BYTE*) dst; BYTE* const oend = ostart + dstSize; BYTE* const olimit = oend - (sizeof(size_t)-1); const void* const dtPtr = DTable+1; const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; /* Init */ BIT_DStream_t bitD1; BIT_DStream_t bitD2; BIT_DStream_t bitD3; BIT_DStream_t bitD4; size_t const length1 = MEM_readLE16(istart); size_t const length2 = MEM_readLE16(istart+2); size_t const length3 = MEM_readLE16(istart+4); size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); const BYTE* const istart1 = istart + 6; /* jumpTable */ const BYTE* const istart2 = istart1 + length1; const BYTE* const istart3 = istart2 + length2; const BYTE* const istart4 = istart3 + length3; size_t const segmentSize = (dstSize+3) / 4; BYTE* const opStart2 = ostart + segmentSize; BYTE* const opStart3 = opStart2 + segmentSize; BYTE* const opStart4 = opStart3 + segmentSize; BYTE* op1 = ostart; BYTE* op2 = opStart2; BYTE* op3 = opStart3; BYTE* op4 = opStart4; U32 endSignal = 1; DTableDesc const dtd = HUF_getDTableDesc(DTable); U32 const dtLog = dtd.tableLog; if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ CHECK_F( BIT_initDStream(&bitD1, istart1, length1) ); CHECK_F( BIT_initDStream(&bitD2, istart2, length2) ); CHECK_F( BIT_initDStream(&bitD3, istart3, length3) ); CHECK_F( BIT_initDStream(&bitD4, istart4, length4) ); /* 16-32 symbols per loop (4-8 symbols per stream) */ for ( ; (endSignal) & (op4 < olimit); ) { #if defined(__clang__) && (defined(__x86_64__) || defined(__i386__)) HUF_DECODE_SYMBOLX2_2(op1, &bitD1); HUF_DECODE_SYMBOLX2_1(op1, &bitD1); HUF_DECODE_SYMBOLX2_2(op1, &bitD1); HUF_DECODE_SYMBOLX2_0(op1, &bitD1); HUF_DECODE_SYMBOLX2_2(op2, &bitD2); HUF_DECODE_SYMBOLX2_1(op2, &bitD2); HUF_DECODE_SYMBOLX2_2(op2, &bitD2); HUF_DECODE_SYMBOLX2_0(op2, &bitD2); endSignal &= BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished; endSignal &= BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished; HUF_DECODE_SYMBOLX2_2(op3, &bitD3); HUF_DECODE_SYMBOLX2_1(op3, &bitD3); HUF_DECODE_SYMBOLX2_2(op3, &bitD3); HUF_DECODE_SYMBOLX2_0(op3, &bitD3); HUF_DECODE_SYMBOLX2_2(op4, &bitD4); HUF_DECODE_SYMBOLX2_1(op4, &bitD4); HUF_DECODE_SYMBOLX2_2(op4, &bitD4); HUF_DECODE_SYMBOLX2_0(op4, &bitD4); endSignal &= BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished; endSignal &= BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished; #else HUF_DECODE_SYMBOLX2_2(op1, &bitD1); HUF_DECODE_SYMBOLX2_2(op2, &bitD2); HUF_DECODE_SYMBOLX2_2(op3, &bitD3); HUF_DECODE_SYMBOLX2_2(op4, &bitD4); HUF_DECODE_SYMBOLX2_1(op1, &bitD1); HUF_DECODE_SYMBOLX2_1(op2, &bitD2); HUF_DECODE_SYMBOLX2_1(op3, &bitD3); HUF_DECODE_SYMBOLX2_1(op4, &bitD4); HUF_DECODE_SYMBOLX2_2(op1, &bitD1); HUF_DECODE_SYMBOLX2_2(op2, &bitD2); HUF_DECODE_SYMBOLX2_2(op3, &bitD3); HUF_DECODE_SYMBOLX2_2(op4, &bitD4); HUF_DECODE_SYMBOLX2_0(op1, &bitD1); HUF_DECODE_SYMBOLX2_0(op2, &bitD2); HUF_DECODE_SYMBOLX2_0(op3, &bitD3); HUF_DECODE_SYMBOLX2_0(op4, &bitD4); endSignal = (U32)LIKELY( (BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished) & (BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished) & (BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished) & (BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished)); #endif } /* check corruption */ if (op1 > opStart2) return ERROR(corruption_detected); if (op2 > opStart3) return ERROR(corruption_detected); if (op3 > opStart4) return ERROR(corruption_detected); /* note : op4 already verified within main loop */ /* finish bitStreams one by one */ HUF_decodeStreamX2(op1, &bitD1, opStart2, dt, dtLog); HUF_decodeStreamX2(op2, &bitD2, opStart3, dt, dtLog); HUF_decodeStreamX2(op3, &bitD3, opStart4, dt, dtLog); HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog); /* check */ { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); if (!endCheck) return ERROR(corruption_detected); } /* decoded size */ return dstSize; } } HUF_DGEN(HUF_decompress1X2_usingDTable_internal) HUF_DGEN(HUF_decompress4X2_usingDTable_internal) size_t HUF_decompress1X2_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { DTableDesc dtd = HUF_getDTableDesc(DTable); if (dtd.tableType != 1) return ERROR(GENERIC); return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize) { const BYTE* ip = (const BYTE*) cSrc; size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); } size_t HUF_decompress1X2_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; return HUF_decompress1X2_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize, workSpace, sizeof(workSpace)); } size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX); return HUF_decompress1X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); } size_t HUF_decompress4X2_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { DTableDesc dtd = HUF_getDTableDesc(DTable); if (dtd.tableType != 1) return ERROR(GENERIC); return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } static size_t HUF_decompress4X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) { const BYTE* ip = (const BYTE*) cSrc; size_t hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); } size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize) { return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, /* bmi2 */ 0); } size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, sizeof(workSpace)); } size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX); return HUF_decompress4X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); } #endif /* HUF_FORCE_DECOMPRESS_X1 */ /* ***********************************/ /* Universal decompression selectors */ /* ***********************************/ size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { DTableDesc const dtd = HUF_getDTableDesc(DTable); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)dtd; assert(dtd.tableType == 0); return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)dtd; assert(dtd.tableType == 1); return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); #else return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); #endif } size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { DTableDesc const dtd = HUF_getDTableDesc(DTable); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)dtd; assert(dtd.tableType == 0); return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)dtd; assert(dtd.tableType == 1); return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); #else return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); #endif } #if !defined(HUF_FORCE_DECOMPRESS_X1) && !defined(HUF_FORCE_DECOMPRESS_X2) typedef struct { U32 tableTime; U32 decode256Time; } algo_time_t; static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, quad */] = { /* single, double, quad */ {{0,0}, {1,1}, {2,2}}, /* Q==0 : impossible */ {{0,0}, {1,1}, {2,2}}, /* Q==1 : impossible */ {{ 38,130}, {1313, 74}, {2151, 38}}, /* Q == 2 : 12-18% */ {{ 448,128}, {1353, 74}, {2238, 41}}, /* Q == 3 : 18-25% */ {{ 556,128}, {1353, 74}, {2238, 47}}, /* Q == 4 : 25-32% */ {{ 714,128}, {1418, 74}, {2436, 53}}, /* Q == 5 : 32-38% */ {{ 883,128}, {1437, 74}, {2464, 61}}, /* Q == 6 : 38-44% */ {{ 897,128}, {1515, 75}, {2622, 68}}, /* Q == 7 : 44-50% */ {{ 926,128}, {1613, 75}, {2730, 75}}, /* Q == 8 : 50-56% */ {{ 947,128}, {1729, 77}, {3359, 77}}, /* Q == 9 : 56-62% */ {{1107,128}, {2083, 81}, {4006, 84}}, /* Q ==10 : 62-69% */ {{1177,128}, {2379, 87}, {4785, 88}}, /* Q ==11 : 69-75% */ {{1242,128}, {2415, 93}, {5155, 84}}, /* Q ==12 : 75-81% */ {{1349,128}, {2644,106}, {5260,106}}, /* Q ==13 : 81-87% */ {{1455,128}, {2422,124}, {4174,124}}, /* Q ==14 : 87-93% */ {{ 722,128}, {1891,145}, {1936,146}}, /* Q ==15 : 93-99% */ }; #endif /** HUF_selectDecoder() : * Tells which decoder is likely to decode faster, * based on a set of pre-computed metrics. * @return : 0==HUF_decompress4X1, 1==HUF_decompress4X2 . * Assumption : 0 < dstSize <= 128 KB */ U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize) { assert(dstSize > 0); assert(dstSize <= 128*1024); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)dstSize; (void)cSrcSize; return 0; #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)dstSize; (void)cSrcSize; return 1; #else /* decoder timing evaluation */ { U32 const Q = (cSrcSize >= dstSize) ? 15 : (U32)(cSrcSize * 16 / dstSize); /* Q < 16 */ U32 const D256 = (U32)(dstSize >> 8); U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256); U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256); DTime1 += DTime1 >> 3; /* advantage to algorithm using less memory, to reduce cache eviction */ return DTime1 < DTime0; } #endif } typedef size_t (*decompressionAlgo)(void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { #if !defined(HUF_FORCE_DECOMPRESS_X1) && !defined(HUF_FORCE_DECOMPRESS_X2) static const decompressionAlgo decompress[2] = { HUF_decompress4X1, HUF_decompress4X2 }; #endif /* validation checks */ if (dstSize == 0) return ERROR(dstSize_tooSmall); if (cSrcSize > dstSize) return ERROR(corruption_detected); /* invalid */ if (cSrcSize == dstSize) { memcpy(dst, cSrc, dstSize); return dstSize; } /* not compressed */ if (cSrcSize == 1) { memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; } /* RLE */ { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)algoNb; assert(algoNb == 0); return HUF_decompress4X1(dst, dstSize, cSrc, cSrcSize); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)algoNb; assert(algoNb == 1); return HUF_decompress4X2(dst, dstSize, cSrc, cSrcSize); #else return decompress[algoNb](dst, dstSize, cSrc, cSrcSize); #endif } } size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { /* validation checks */ if (dstSize == 0) return ERROR(dstSize_tooSmall); if (cSrcSize > dstSize) return ERROR(corruption_detected); /* invalid */ if (cSrcSize == dstSize) { memcpy(dst, cSrc, dstSize); return dstSize; } /* not compressed */ if (cSrcSize == 1) { memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; } /* RLE */ { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)algoNb; assert(algoNb == 0); return HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)algoNb; assert(algoNb == 1); return HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize); #else return algoNb ? HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) : HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) ; #endif } } size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; return HUF_decompress4X_hufOnly_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, sizeof(workSpace)); } size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize) { /* validation checks */ if (dstSize == 0) return ERROR(dstSize_tooSmall); if (cSrcSize == 0) return ERROR(corruption_detected); { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)algoNb; assert(algoNb == 0); return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)algoNb; assert(algoNb == 1); return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); #else return algoNb ? HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize): HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); #endif } } size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize) { /* validation checks */ if (dstSize == 0) return ERROR(dstSize_tooSmall); if (cSrcSize > dstSize) return ERROR(corruption_detected); /* invalid */ if (cSrcSize == dstSize) { memcpy(dst, cSrc, dstSize); return dstSize; } /* not compressed */ if (cSrcSize == 1) { memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; } /* RLE */ { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)algoNb; assert(algoNb == 0); return HUF_decompress1X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)algoNb; assert(algoNb == 1); return HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); #else return algoNb ? HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize): HUF_decompress1X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); #endif } } size_t HUF_decompress1X_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; return HUF_decompress1X_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, sizeof(workSpace)); } size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2) { DTableDesc const dtd = HUF_getDTableDesc(DTable); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)dtd; assert(dtd.tableType == 0); return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)dtd; assert(dtd.tableType == 1); return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); #else return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); #endif } #ifndef HUF_FORCE_DECOMPRESS_X2 size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) { const BYTE* ip = (const BYTE*) cSrc; size_t const hSize = HUF_readDTableX1_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); } #endif size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2) { DTableDesc const dtd = HUF_getDTableDesc(DTable); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)dtd; assert(dtd.tableType == 0); return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)dtd; assert(dtd.tableType == 1); return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); #else return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); #endif } size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) { /* validation checks */ if (dstSize == 0) return ERROR(dstSize_tooSmall); if (cSrcSize == 0) return ERROR(corruption_detected); { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)algoNb; assert(algoNb == 0); return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)algoNb; assert(algoNb == 1); return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); #else return algoNb ? HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2) : HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); #endif } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/decompress/zstd_ddict.c0000644000076500000240000002161514601576577024333 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* zstd_ddict.c : * concentrates all logic that needs to know the internals of ZSTD_DDict object */ /*-******************************************************* * Dependencies *********************************************************/ #include /* memcpy, memmove, memset */ #include "../common/cpu.h" /* bmi2 */ #include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY #include "../common/fse.h" #define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "zstd_decompress_internal.h" #include "zstd_ddict.h" #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) # include "../legacy/zstd_legacy.h" #endif /*-******************************************************* * Types *********************************************************/ struct ZSTD_DDict_s { void* dictBuffer; const void* dictContent; size_t dictSize; ZSTD_entropyDTables_t entropy; U32 dictID; U32 entropyPresent; ZSTD_customMem cMem; }; /* typedef'd to ZSTD_DDict within "zstd.h" */ const void* ZSTD_DDict_dictContent(const ZSTD_DDict* ddict) { assert(ddict != NULL); return ddict->dictContent; } size_t ZSTD_DDict_dictSize(const ZSTD_DDict* ddict) { assert(ddict != NULL); return ddict->dictSize; } void ZSTD_copyDDictParameters(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict) { DEBUGLOG(4, "ZSTD_copyDDictParameters"); assert(dctx != NULL); assert(ddict != NULL); dctx->dictID = ddict->dictID; dctx->prefixStart = ddict->dictContent; dctx->virtualStart = ddict->dictContent; dctx->dictEnd = (const BYTE*)ddict->dictContent + ddict->dictSize; dctx->previousDstEnd = dctx->dictEnd; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION dctx->dictContentBeginForFuzzing = dctx->prefixStart; dctx->dictContentEndForFuzzing = dctx->previousDstEnd; #endif if (ddict->entropyPresent) { dctx->litEntropy = 1; dctx->fseEntropy = 1; dctx->LLTptr = ddict->entropy.LLTable; dctx->MLTptr = ddict->entropy.MLTable; dctx->OFTptr = ddict->entropy.OFTable; dctx->HUFptr = ddict->entropy.hufTable; dctx->entropy.rep[0] = ddict->entropy.rep[0]; dctx->entropy.rep[1] = ddict->entropy.rep[1]; dctx->entropy.rep[2] = ddict->entropy.rep[2]; } else { dctx->litEntropy = 0; dctx->fseEntropy = 0; } } static size_t ZSTD_loadEntropy_intoDDict(ZSTD_DDict* ddict, ZSTD_dictContentType_e dictContentType) { ddict->dictID = 0; ddict->entropyPresent = 0; if (dictContentType == ZSTD_dct_rawContent) return 0; if (ddict->dictSize < 8) { if (dictContentType == ZSTD_dct_fullDict) return ERROR(dictionary_corrupted); /* only accept specified dictionaries */ return 0; /* pure content mode */ } { U32 const magic = MEM_readLE32(ddict->dictContent); if (magic != ZSTD_MAGIC_DICTIONARY) { if (dictContentType == ZSTD_dct_fullDict) return ERROR(dictionary_corrupted); /* only accept specified dictionaries */ return 0; /* pure content mode */ } } ddict->dictID = MEM_readLE32((const char*)ddict->dictContent + ZSTD_FRAMEIDSIZE); /* load entropy tables */ RETURN_ERROR_IF(ZSTD_isError(ZSTD_loadDEntropy( &ddict->entropy, ddict->dictContent, ddict->dictSize)), dictionary_corrupted, ""); ddict->entropyPresent = 1; return 0; } static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType) { if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dict) || (!dictSize)) { ddict->dictBuffer = NULL; ddict->dictContent = dict; if (!dict) dictSize = 0; } else { void* const internalBuffer = ZSTD_malloc(dictSize, ddict->cMem); ddict->dictBuffer = internalBuffer; ddict->dictContent = internalBuffer; if (!internalBuffer) return ERROR(memory_allocation); memcpy(internalBuffer, dict, dictSize); } ddict->dictSize = dictSize; ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ /* parse dictionary content */ FORWARD_IF_ERROR( ZSTD_loadEntropy_intoDDict(ddict, dictContentType) , ""); return 0; } ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType, ZSTD_customMem customMem) { if (!customMem.customAlloc ^ !customMem.customFree) return NULL; { ZSTD_DDict* const ddict = (ZSTD_DDict*) ZSTD_malloc(sizeof(ZSTD_DDict), customMem); if (ddict == NULL) return NULL; ddict->cMem = customMem; { size_t const initResult = ZSTD_initDDict_internal(ddict, dict, dictSize, dictLoadMethod, dictContentType); if (ZSTD_isError(initResult)) { ZSTD_freeDDict(ddict); return NULL; } } return ddict; } } /*! ZSTD_createDDict() : * Create a digested dictionary, to start decompression without startup delay. * `dict` content is copied inside DDict. * Consequently, `dict` can be released after `ZSTD_DDict` creation */ ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize) { ZSTD_customMem const allocator = { NULL, NULL, NULL }; return ZSTD_createDDict_advanced(dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto, allocator); } /*! ZSTD_createDDict_byReference() : * Create a digested dictionary, to start decompression without startup delay. * Dictionary content is simply referenced, it will be accessed during decompression. * Warning : dictBuffer must outlive DDict (DDict must be freed before dictBuffer) */ ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize) { ZSTD_customMem const allocator = { NULL, NULL, NULL }; return ZSTD_createDDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto, allocator); } const ZSTD_DDict* ZSTD_initStaticDDict( void* sBuffer, size_t sBufferSize, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType) { size_t const neededSpace = sizeof(ZSTD_DDict) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize); ZSTD_DDict* const ddict = (ZSTD_DDict*)sBuffer; assert(sBuffer != NULL); assert(dict != NULL); if ((size_t)sBuffer & 7) return NULL; /* 8-aligned */ if (sBufferSize < neededSpace) return NULL; if (dictLoadMethod == ZSTD_dlm_byCopy) { memcpy(ddict+1, dict, dictSize); /* local copy */ dict = ddict+1; } if (ZSTD_isError( ZSTD_initDDict_internal(ddict, dict, dictSize, ZSTD_dlm_byRef, dictContentType) )) return NULL; return ddict; } size_t ZSTD_freeDDict(ZSTD_DDict* ddict) { if (ddict==NULL) return 0; /* support free on NULL */ { ZSTD_customMem const cMem = ddict->cMem; ZSTD_free(ddict->dictBuffer, cMem); ZSTD_free(ddict, cMem); return 0; } } /*! ZSTD_estimateDDictSize() : * Estimate amount of memory that will be needed to create a dictionary for decompression. * Note : dictionary created by reference using ZSTD_dlm_byRef are smaller */ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod) { return sizeof(ZSTD_DDict) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize); } size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict) { if (ddict==NULL) return 0; /* support sizeof on NULL */ return sizeof(*ddict) + (ddict->dictBuffer ? ddict->dictSize : 0) ; } /*! ZSTD_getDictID_fromDDict() : * Provides the dictID of the dictionary loaded into `ddict`. * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty. * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */ unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict) { if (ddict==NULL) return 0; return ZSTD_getDictID_fromDict(ddict->dictContent, ddict->dictSize); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/decompress/zstd_ddict.h0000644000076500000240000000243414601576577024336 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_DDICT_H #define ZSTD_DDICT_H /*-******************************************************* * Dependencies *********************************************************/ #include /* size_t */ #include "../zstd.h" /* ZSTD_DDict, and several public functions */ /*-******************************************************* * Interface *********************************************************/ /* note: several prototypes are already published in `zstd.h` : * ZSTD_createDDict() * ZSTD_createDDict_byReference() * ZSTD_createDDict_advanced() * ZSTD_freeDDict() * ZSTD_initStaticDDict() * ZSTD_sizeof_DDict() * ZSTD_estimateDDictSize() * ZSTD_getDictID_fromDict() */ const void* ZSTD_DDict_dictContent(const ZSTD_DDict* ddict); size_t ZSTD_DDict_dictSize(const ZSTD_DDict* ddict); void ZSTD_copyDDictParameters(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict); #endif /* ZSTD_DDICT_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/decompress/zstd_decompress.c0000644000076500000240000023124414601576577025411 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* *************************************************************** * Tuning parameters *****************************************************************/ /*! * HEAPMODE : * Select how default decompression function ZSTD_decompress() allocates its context, * on stack (0), or into heap (1, default; requires malloc()). * Note that functions with explicit context such as ZSTD_decompressDCtx() are unaffected. */ #ifndef ZSTD_HEAPMODE # define ZSTD_HEAPMODE 1 #endif /*! * LEGACY_SUPPORT : * if set to 1+, ZSTD_decompress() can decode older formats (v0.1+) */ #ifndef ZSTD_LEGACY_SUPPORT # define ZSTD_LEGACY_SUPPORT 0 #endif /*! * MAXWINDOWSIZE_DEFAULT : * maximum window size accepted by DStream __by default__. * Frames requiring more memory will be rejected. * It's possible to set a different limit using ZSTD_DCtx_setMaxWindowSize(). */ #ifndef ZSTD_MAXWINDOWSIZE_DEFAULT # define ZSTD_MAXWINDOWSIZE_DEFAULT (((U32)1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT) + 1) #endif /*! * NO_FORWARD_PROGRESS_MAX : * maximum allowed nb of calls to ZSTD_decompressStream() * without any forward progress * (defined as: no byte read from input, and no byte flushed to output) * before triggering an error. */ #ifndef ZSTD_NO_FORWARD_PROGRESS_MAX # define ZSTD_NO_FORWARD_PROGRESS_MAX 16 #endif /*-******************************************************* * Dependencies *********************************************************/ #include /* memcpy, memmove, memset */ #include "../common/cpu.h" /* bmi2 */ #include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY #include "../common/fse.h" #define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "../common/zstd_internal.h" /* blockProperties_t */ #include "zstd_decompress_internal.h" /* ZSTD_DCtx */ #include "zstd_ddict.h" /* ZSTD_DDictDictContent */ #include "zstd_decompress_block.h" /* ZSTD_decompressBlock_internal */ #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) # include "../legacy/zstd_legacy.h" #endif /*-************************************************************* * Context management ***************************************************************/ size_t ZSTD_sizeof_DCtx (const ZSTD_DCtx* dctx) { if (dctx==NULL) return 0; /* support sizeof NULL */ return sizeof(*dctx) + ZSTD_sizeof_DDict(dctx->ddictLocal) + dctx->inBuffSize + dctx->outBuffSize; } size_t ZSTD_estimateDCtxSize(void) { return sizeof(ZSTD_DCtx); } static size_t ZSTD_startingInputLength(ZSTD_format_e format) { size_t const startingInputLength = ZSTD_FRAMEHEADERSIZE_PREFIX(format); /* only supports formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless */ assert( (format == ZSTD_f_zstd1) || (format == ZSTD_f_zstd1_magicless) ); return startingInputLength; } static void ZSTD_initDCtx_internal(ZSTD_DCtx* dctx) { dctx->format = ZSTD_f_zstd1; /* ZSTD_decompressBegin() invokes ZSTD_startingInputLength() with argument dctx->format */ dctx->staticSize = 0; dctx->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT; dctx->ddict = NULL; dctx->ddictLocal = NULL; dctx->dictEnd = NULL; dctx->ddictIsCold = 0; dctx->dictUses = ZSTD_dont_use; dctx->inBuff = NULL; dctx->inBuffSize = 0; dctx->outBuffSize = 0; dctx->streamStage = zdss_init; dctx->legacyContext = NULL; dctx->previousLegacyVersion = 0; dctx->noForwardProgress = 0; dctx->oversizedDuration = 0; dctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid()); dctx->outBufferMode = ZSTD_obm_buffered; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION dctx->dictContentEndForFuzzing = NULL; #endif } ZSTD_DCtx* ZSTD_initStaticDCtx(void *workspace, size_t workspaceSize) { ZSTD_DCtx* const dctx = (ZSTD_DCtx*) workspace; if ((size_t)workspace & 7) return NULL; /* 8-aligned */ if (workspaceSize < sizeof(ZSTD_DCtx)) return NULL; /* minimum size */ ZSTD_initDCtx_internal(dctx); dctx->staticSize = workspaceSize; dctx->inBuff = (char*)(dctx+1); return dctx; } ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem) { if (!customMem.customAlloc ^ !customMem.customFree) return NULL; { ZSTD_DCtx* const dctx = (ZSTD_DCtx*)ZSTD_malloc(sizeof(*dctx), customMem); if (!dctx) return NULL; dctx->customMem = customMem; ZSTD_initDCtx_internal(dctx); return dctx; } } ZSTD_DCtx* ZSTD_createDCtx(void) { DEBUGLOG(3, "ZSTD_createDCtx"); return ZSTD_createDCtx_advanced(ZSTD_defaultCMem); } static void ZSTD_clearDict(ZSTD_DCtx* dctx) { ZSTD_freeDDict(dctx->ddictLocal); dctx->ddictLocal = NULL; dctx->ddict = NULL; dctx->dictUses = ZSTD_dont_use; } size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx) { if (dctx==NULL) return 0; /* support free on NULL */ RETURN_ERROR_IF(dctx->staticSize, memory_allocation, "not compatible with static DCtx"); { ZSTD_customMem const cMem = dctx->customMem; ZSTD_clearDict(dctx); ZSTD_free(dctx->inBuff, cMem); dctx->inBuff = NULL; #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) if (dctx->legacyContext) ZSTD_freeLegacyStreamContext(dctx->legacyContext, dctx->previousLegacyVersion); #endif ZSTD_free(dctx, cMem); return 0; } } /* no longer useful */ void ZSTD_copyDCtx(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx) { size_t const toCopy = (size_t)((char*)(&dstDCtx->inBuff) - (char*)dstDCtx); memcpy(dstDCtx, srcDCtx, toCopy); /* no need to copy workspace */ } /*-************************************************************* * Frame header decoding ***************************************************************/ /*! ZSTD_isFrame() : * Tells if the content of `buffer` starts with a valid Frame Identifier. * Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0. * Note 2 : Legacy Frame Identifiers are considered valid only if Legacy Support is enabled. * Note 3 : Skippable Frame Identifiers are considered valid. */ unsigned ZSTD_isFrame(const void* buffer, size_t size) { if (size < ZSTD_FRAMEIDSIZE) return 0; { U32 const magic = MEM_readLE32(buffer); if (magic == ZSTD_MAGICNUMBER) return 1; if ((magic & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) return 1; } #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) if (ZSTD_isLegacy(buffer, size)) return 1; #endif return 0; } /** ZSTD_frameHeaderSize_internal() : * srcSize must be large enough to reach header size fields. * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless. * @return : size of the Frame Header * or an error code, which can be tested with ZSTD_isError() */ static size_t ZSTD_frameHeaderSize_internal(const void* src, size_t srcSize, ZSTD_format_e format) { size_t const minInputSize = ZSTD_startingInputLength(format); RETURN_ERROR_IF(srcSize < minInputSize, srcSize_wrong, ""); { BYTE const fhd = ((const BYTE*)src)[minInputSize-1]; U32 const dictID= fhd & 3; U32 const singleSegment = (fhd >> 5) & 1; U32 const fcsId = fhd >> 6; return minInputSize + !singleSegment + ZSTD_did_fieldSize[dictID] + ZSTD_fcs_fieldSize[fcsId] + (singleSegment && !fcsId); } } /** ZSTD_frameHeaderSize() : * srcSize must be >= ZSTD_frameHeaderSize_prefix. * @return : size of the Frame Header, * or an error code (if srcSize is too small) */ size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize) { return ZSTD_frameHeaderSize_internal(src, srcSize, ZSTD_f_zstd1); } /** ZSTD_getFrameHeader_advanced() : * decode Frame Header, or require larger `srcSize`. * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless * @return : 0, `zfhPtr` is correctly filled, * >0, `srcSize` is too small, value is wanted `srcSize` amount, * or an error code, which can be tested using ZSTD_isError() */ size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format) { const BYTE* ip = (const BYTE*)src; size_t const minInputSize = ZSTD_startingInputLength(format); memset(zfhPtr, 0, sizeof(*zfhPtr)); /* not strictly necessary, but static analyzer do not understand that zfhPtr is only going to be read only if return value is zero, since they are 2 different signals */ if (srcSize < minInputSize) return minInputSize; RETURN_ERROR_IF(src==NULL, GENERIC, "invalid parameter"); if ( (format != ZSTD_f_zstd1_magicless) && (MEM_readLE32(src) != ZSTD_MAGICNUMBER) ) { if ((MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */ if (srcSize < ZSTD_SKIPPABLEHEADERSIZE) return ZSTD_SKIPPABLEHEADERSIZE; /* magic number + frame length */ memset(zfhPtr, 0, sizeof(*zfhPtr)); zfhPtr->frameContentSize = MEM_readLE32((const char *)src + ZSTD_FRAMEIDSIZE); zfhPtr->frameType = ZSTD_skippableFrame; return 0; } RETURN_ERROR(prefix_unknown, ""); } /* ensure there is enough `srcSize` to fully read/decode frame header */ { size_t const fhsize = ZSTD_frameHeaderSize_internal(src, srcSize, format); if (srcSize < fhsize) return fhsize; zfhPtr->headerSize = (U32)fhsize; } { BYTE const fhdByte = ip[minInputSize-1]; size_t pos = minInputSize; U32 const dictIDSizeCode = fhdByte&3; U32 const checksumFlag = (fhdByte>>2)&1; U32 const singleSegment = (fhdByte>>5)&1; U32 const fcsID = fhdByte>>6; U64 windowSize = 0; U32 dictID = 0; U64 frameContentSize = ZSTD_CONTENTSIZE_UNKNOWN; RETURN_ERROR_IF((fhdByte & 0x08) != 0, frameParameter_unsupported, "reserved bits, must be zero"); if (!singleSegment) { BYTE const wlByte = ip[pos++]; U32 const windowLog = (wlByte >> 3) + ZSTD_WINDOWLOG_ABSOLUTEMIN; RETURN_ERROR_IF(windowLog > ZSTD_WINDOWLOG_MAX, frameParameter_windowTooLarge, ""); windowSize = (1ULL << windowLog); windowSize += (windowSize >> 3) * (wlByte&7); } switch(dictIDSizeCode) { default: assert(0); /* impossible */ case 0 : break; case 1 : dictID = ip[pos]; pos++; break; case 2 : dictID = MEM_readLE16(ip+pos); pos+=2; break; case 3 : dictID = MEM_readLE32(ip+pos); pos+=4; break; } switch(fcsID) { default: assert(0); /* impossible */ case 0 : if (singleSegment) frameContentSize = ip[pos]; break; case 1 : frameContentSize = MEM_readLE16(ip+pos)+256; break; case 2 : frameContentSize = MEM_readLE32(ip+pos); break; case 3 : frameContentSize = MEM_readLE64(ip+pos); break; } if (singleSegment) windowSize = frameContentSize; zfhPtr->frameType = ZSTD_frame; zfhPtr->frameContentSize = frameContentSize; zfhPtr->windowSize = windowSize; zfhPtr->blockSizeMax = (unsigned) MIN(windowSize, ZSTD_BLOCKSIZE_MAX); zfhPtr->dictID = dictID; zfhPtr->checksumFlag = checksumFlag; } return 0; } /** ZSTD_getFrameHeader() : * decode Frame Header, or require larger `srcSize`. * note : this function does not consume input, it only reads it. * @return : 0, `zfhPtr` is correctly filled, * >0, `srcSize` is too small, value is wanted `srcSize` amount, * or an error code, which can be tested using ZSTD_isError() */ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize) { return ZSTD_getFrameHeader_advanced(zfhPtr, src, srcSize, ZSTD_f_zstd1); } /** ZSTD_getFrameContentSize() : * compatible with legacy mode * @return : decompressed size of the single frame pointed to be `src` if known, otherwise * - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined * - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) */ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize) { #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) if (ZSTD_isLegacy(src, srcSize)) { unsigned long long const ret = ZSTD_getDecompressedSize_legacy(src, srcSize); return ret == 0 ? ZSTD_CONTENTSIZE_UNKNOWN : ret; } #endif { ZSTD_frameHeader zfh; if (ZSTD_getFrameHeader(&zfh, src, srcSize) != 0) return ZSTD_CONTENTSIZE_ERROR; if (zfh.frameType == ZSTD_skippableFrame) { return 0; } else { return zfh.frameContentSize; } } } static size_t readSkippableFrameSize(void const* src, size_t srcSize) { size_t const skippableHeaderSize = ZSTD_SKIPPABLEHEADERSIZE; U32 sizeU32; RETURN_ERROR_IF(srcSize < ZSTD_SKIPPABLEHEADERSIZE, srcSize_wrong, ""); sizeU32 = MEM_readLE32((BYTE const*)src + ZSTD_FRAMEIDSIZE); RETURN_ERROR_IF((U32)(sizeU32 + ZSTD_SKIPPABLEHEADERSIZE) < sizeU32, frameParameter_unsupported, ""); { size_t const skippableSize = skippableHeaderSize + sizeU32; RETURN_ERROR_IF(skippableSize > srcSize, srcSize_wrong, ""); return skippableSize; } } /** ZSTD_findDecompressedSize() : * compatible with legacy mode * `srcSize` must be the exact length of some number of ZSTD compressed and/or * skippable frames * @return : decompressed size of the frames contained */ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize) { unsigned long long totalDstSize = 0; while (srcSize >= ZSTD_startingInputLength(ZSTD_f_zstd1)) { U32 const magicNumber = MEM_readLE32(src); if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { size_t const skippableSize = readSkippableFrameSize(src, srcSize); if (ZSTD_isError(skippableSize)) { return ZSTD_CONTENTSIZE_ERROR; } assert(skippableSize <= srcSize); src = (const BYTE *)src + skippableSize; srcSize -= skippableSize; continue; } { unsigned long long const ret = ZSTD_getFrameContentSize(src, srcSize); if (ret >= ZSTD_CONTENTSIZE_ERROR) return ret; /* check for overflow */ if (totalDstSize + ret < totalDstSize) return ZSTD_CONTENTSIZE_ERROR; totalDstSize += ret; } { size_t const frameSrcSize = ZSTD_findFrameCompressedSize(src, srcSize); if (ZSTD_isError(frameSrcSize)) { return ZSTD_CONTENTSIZE_ERROR; } src = (const BYTE *)src + frameSrcSize; srcSize -= frameSrcSize; } } /* while (srcSize >= ZSTD_frameHeaderSize_prefix) */ if (srcSize) return ZSTD_CONTENTSIZE_ERROR; return totalDstSize; } /** ZSTD_getDecompressedSize() : * compatible with legacy mode * @return : decompressed size if known, 0 otherwise note : 0 can mean any of the following : - frame content is empty - decompressed size field is not present in frame header - frame header unknown / not supported - frame header not complete (`srcSize` too small) */ unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize) { unsigned long long const ret = ZSTD_getFrameContentSize(src, srcSize); ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_ERROR < ZSTD_CONTENTSIZE_UNKNOWN); return (ret >= ZSTD_CONTENTSIZE_ERROR) ? 0 : ret; } /** ZSTD_decodeFrameHeader() : * `headerSize` must be the size provided by ZSTD_frameHeaderSize(). * @return : 0 if success, or an error code, which can be tested using ZSTD_isError() */ static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t headerSize) { size_t const result = ZSTD_getFrameHeader_advanced(&(dctx->fParams), src, headerSize, dctx->format); if (ZSTD_isError(result)) return result; /* invalid header */ RETURN_ERROR_IF(result>0, srcSize_wrong, "headerSize too small"); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* Skip the dictID check in fuzzing mode, because it makes the search * harder. */ RETURN_ERROR_IF(dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID), dictionary_wrong, ""); #endif if (dctx->fParams.checksumFlag) XXH64_reset(&dctx->xxhState, 0); return 0; } static ZSTD_frameSizeInfo ZSTD_errorFrameSizeInfo(size_t ret) { ZSTD_frameSizeInfo frameSizeInfo; frameSizeInfo.compressedSize = ret; frameSizeInfo.decompressedBound = ZSTD_CONTENTSIZE_ERROR; return frameSizeInfo; } static ZSTD_frameSizeInfo ZSTD_findFrameSizeInfo(const void* src, size_t srcSize) { ZSTD_frameSizeInfo frameSizeInfo; memset(&frameSizeInfo, 0, sizeof(ZSTD_frameSizeInfo)); #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) if (ZSTD_isLegacy(src, srcSize)) return ZSTD_findFrameSizeInfoLegacy(src, srcSize); #endif if ((srcSize >= ZSTD_SKIPPABLEHEADERSIZE) && (MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { frameSizeInfo.compressedSize = readSkippableFrameSize(src, srcSize); assert(ZSTD_isError(frameSizeInfo.compressedSize) || frameSizeInfo.compressedSize <= srcSize); return frameSizeInfo; } else { const BYTE* ip = (const BYTE*)src; const BYTE* const ipstart = ip; size_t remainingSize = srcSize; size_t nbBlocks = 0; ZSTD_frameHeader zfh; /* Extract Frame Header */ { size_t const ret = ZSTD_getFrameHeader(&zfh, src, srcSize); if (ZSTD_isError(ret)) return ZSTD_errorFrameSizeInfo(ret); if (ret > 0) return ZSTD_errorFrameSizeInfo(ERROR(srcSize_wrong)); } ip += zfh.headerSize; remainingSize -= zfh.headerSize; /* Iterate over each block */ while (1) { blockProperties_t blockProperties; size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties); if (ZSTD_isError(cBlockSize)) return ZSTD_errorFrameSizeInfo(cBlockSize); if (ZSTD_blockHeaderSize + cBlockSize > remainingSize) return ZSTD_errorFrameSizeInfo(ERROR(srcSize_wrong)); ip += ZSTD_blockHeaderSize + cBlockSize; remainingSize -= ZSTD_blockHeaderSize + cBlockSize; nbBlocks++; if (blockProperties.lastBlock) break; } /* Final frame content checksum */ if (zfh.checksumFlag) { if (remainingSize < 4) return ZSTD_errorFrameSizeInfo(ERROR(srcSize_wrong)); ip += 4; } frameSizeInfo.compressedSize = ip - ipstart; frameSizeInfo.decompressedBound = (zfh.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) ? zfh.frameContentSize : nbBlocks * zfh.blockSizeMax; return frameSizeInfo; } } /** ZSTD_findFrameCompressedSize() : * compatible with legacy mode * `src` must point to the start of a ZSTD frame, ZSTD legacy frame, or skippable frame * `srcSize` must be at least as large as the frame contained * @return : the compressed size of the frame starting at `src` */ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) { ZSTD_frameSizeInfo const frameSizeInfo = ZSTD_findFrameSizeInfo(src, srcSize); return frameSizeInfo.compressedSize; } /** ZSTD_decompressBound() : * compatible with legacy mode * `src` must point to the start of a ZSTD frame or a skippeable frame * `srcSize` must be at least as large as the frame contained * @return : the maximum decompressed size of the compressed source */ unsigned long long ZSTD_decompressBound(const void* src, size_t srcSize) { unsigned long long bound = 0; /* Iterate over each frame */ while (srcSize > 0) { ZSTD_frameSizeInfo const frameSizeInfo = ZSTD_findFrameSizeInfo(src, srcSize); size_t const compressedSize = frameSizeInfo.compressedSize; unsigned long long const decompressedBound = frameSizeInfo.decompressedBound; if (ZSTD_isError(compressedSize) || decompressedBound == ZSTD_CONTENTSIZE_ERROR) return ZSTD_CONTENTSIZE_ERROR; assert(srcSize >= compressedSize); src = (const BYTE*)src + compressedSize; srcSize -= compressedSize; bound += decompressedBound; } return bound; } /*-************************************************************* * Frame decoding ***************************************************************/ /** ZSTD_insertBlock() : * insert `src` block into `dctx` history. Useful to track uncompressed blocks. */ size_t ZSTD_insertBlock(ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize) { DEBUGLOG(5, "ZSTD_insertBlock: %u bytes", (unsigned)blockSize); ZSTD_checkContinuity(dctx, blockStart); dctx->previousDstEnd = (const char*)blockStart + blockSize; return blockSize; } static size_t ZSTD_copyRawBlock(void* dst, size_t dstCapacity, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_copyRawBlock"); if (dst == NULL) { if (srcSize == 0) return 0; RETURN_ERROR(dstBuffer_null, ""); } RETURN_ERROR_IF(srcSize > dstCapacity, dstSize_tooSmall, ""); memcpy(dst, src, srcSize); return srcSize; } static size_t ZSTD_setRleBlock(void* dst, size_t dstCapacity, BYTE b, size_t regenSize) { if (dst == NULL) { if (regenSize == 0) return 0; RETURN_ERROR(dstBuffer_null, ""); } RETURN_ERROR_IF(regenSize > dstCapacity, dstSize_tooSmall, ""); memset(dst, b, regenSize); return regenSize; } /*! ZSTD_decompressFrame() : * @dctx must be properly initialized * will update *srcPtr and *srcSizePtr, * to make *srcPtr progress by one frame. */ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void** srcPtr, size_t *srcSizePtr) { const BYTE* ip = (const BYTE*)(*srcPtr); BYTE* const ostart = (BYTE* const)dst; BYTE* const oend = dstCapacity != 0 ? ostart + dstCapacity : ostart; BYTE* op = ostart; size_t remainingSrcSize = *srcSizePtr; DEBUGLOG(4, "ZSTD_decompressFrame (srcSize:%i)", (int)*srcSizePtr); /* check */ RETURN_ERROR_IF( remainingSrcSize < ZSTD_FRAMEHEADERSIZE_MIN(dctx->format)+ZSTD_blockHeaderSize, srcSize_wrong, ""); /* Frame Header */ { size_t const frameHeaderSize = ZSTD_frameHeaderSize_internal( ip, ZSTD_FRAMEHEADERSIZE_PREFIX(dctx->format), dctx->format); if (ZSTD_isError(frameHeaderSize)) return frameHeaderSize; RETURN_ERROR_IF(remainingSrcSize < frameHeaderSize+ZSTD_blockHeaderSize, srcSize_wrong, ""); FORWARD_IF_ERROR( ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize) , ""); ip += frameHeaderSize; remainingSrcSize -= frameHeaderSize; } /* Loop on each block */ while (1) { size_t decodedSize; blockProperties_t blockProperties; size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSrcSize, &blockProperties); if (ZSTD_isError(cBlockSize)) return cBlockSize; ip += ZSTD_blockHeaderSize; remainingSrcSize -= ZSTD_blockHeaderSize; RETURN_ERROR_IF(cBlockSize > remainingSrcSize, srcSize_wrong, ""); switch(blockProperties.blockType) { case bt_compressed: decodedSize = ZSTD_decompressBlock_internal(dctx, op, oend-op, ip, cBlockSize, /* frame */ 1); break; case bt_raw : decodedSize = ZSTD_copyRawBlock(op, oend-op, ip, cBlockSize); break; case bt_rle : decodedSize = ZSTD_setRleBlock(op, oend-op, *ip, blockProperties.origSize); break; case bt_reserved : default: RETURN_ERROR(corruption_detected, "invalid block type"); } if (ZSTD_isError(decodedSize)) return decodedSize; if (dctx->fParams.checksumFlag) XXH64_update(&dctx->xxhState, op, decodedSize); if (decodedSize != 0) op += decodedSize; assert(ip != NULL); ip += cBlockSize; remainingSrcSize -= cBlockSize; if (blockProperties.lastBlock) break; } if (dctx->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) { RETURN_ERROR_IF((U64)(op-ostart) != dctx->fParams.frameContentSize, corruption_detected, ""); } if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */ U32 const checkCalc = (U32)XXH64_digest(&dctx->xxhState); U32 checkRead; RETURN_ERROR_IF(remainingSrcSize<4, checksum_wrong, ""); checkRead = MEM_readLE32(ip); RETURN_ERROR_IF(checkRead != checkCalc, checksum_wrong, ""); ip += 4; remainingSrcSize -= 4; } /* Allow caller to get size read */ *srcPtr = ip; *srcSizePtr = remainingSrcSize; return op-ostart; } static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const void* dict, size_t dictSize, const ZSTD_DDict* ddict) { void* const dststart = dst; int moreThan1Frame = 0; DEBUGLOG(5, "ZSTD_decompressMultiFrame"); assert(dict==NULL || ddict==NULL); /* either dict or ddict set, not both */ if (ddict) { dict = ZSTD_DDict_dictContent(ddict); dictSize = ZSTD_DDict_dictSize(ddict); } while (srcSize >= ZSTD_startingInputLength(dctx->format)) { #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) if (ZSTD_isLegacy(src, srcSize)) { size_t decodedSize; size_t const frameSize = ZSTD_findFrameCompressedSizeLegacy(src, srcSize); if (ZSTD_isError(frameSize)) return frameSize; RETURN_ERROR_IF(dctx->staticSize, memory_allocation, "legacy support is not compatible with static dctx"); decodedSize = ZSTD_decompressLegacy(dst, dstCapacity, src, frameSize, dict, dictSize); if (ZSTD_isError(decodedSize)) return decodedSize; assert(decodedSize <=- dstCapacity); dst = (BYTE*)dst + decodedSize; dstCapacity -= decodedSize; src = (const BYTE*)src + frameSize; srcSize -= frameSize; continue; } #endif { U32 const magicNumber = MEM_readLE32(src); DEBUGLOG(4, "reading magic number %08X (expecting %08X)", (unsigned)magicNumber, ZSTD_MAGICNUMBER); if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { size_t const skippableSize = readSkippableFrameSize(src, srcSize); FORWARD_IF_ERROR(skippableSize, "readSkippableFrameSize failed"); assert(skippableSize <= srcSize); src = (const BYTE *)src + skippableSize; srcSize -= skippableSize; continue; } } if (ddict) { /* we were called from ZSTD_decompress_usingDDict */ FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDDict(dctx, ddict), ""); } else { /* this will initialize correctly with no dict if dict == NULL, so * use this in all cases but ddict */ FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDict(dctx, dict, dictSize), ""); } ZSTD_checkContinuity(dctx, dst); { const size_t res = ZSTD_decompressFrame(dctx, dst, dstCapacity, &src, &srcSize); RETURN_ERROR_IF( (ZSTD_getErrorCode(res) == ZSTD_error_prefix_unknown) && (moreThan1Frame==1), srcSize_wrong, "at least one frame successfully completed, but following " "bytes are garbage: it's more likely to be a srcSize error, " "specifying more bytes than compressed size of frame(s). This " "error message replaces ERROR(prefix_unknown), which would be " "confusing, as the first header is actually correct. Note that " "one could be unlucky, it might be a corruption error instead, " "happening right at the place where we expect zstd magic " "bytes. But this is _much_ less likely than a srcSize field " "error."); if (ZSTD_isError(res)) return res; assert(res <= dstCapacity); if (res != 0) dst = (BYTE*)dst + res; dstCapacity -= res; } moreThan1Frame = 1; } /* while (srcSize >= ZSTD_frameHeaderSize_prefix) */ RETURN_ERROR_IF(srcSize, srcSize_wrong, "input not entirely consumed"); return (BYTE*)dst - (BYTE*)dststart; } size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const void* dict, size_t dictSize) { return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, dict, dictSize, NULL); } static ZSTD_DDict const* ZSTD_getDDict(ZSTD_DCtx* dctx) { switch (dctx->dictUses) { default: assert(0 /* Impossible */); /* fall-through */ case ZSTD_dont_use: ZSTD_clearDict(dctx); return NULL; case ZSTD_use_indefinitely: return dctx->ddict; case ZSTD_use_once: dctx->dictUses = ZSTD_dont_use; return dctx->ddict; } } size_t ZSTD_decompressDCtx(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { return ZSTD_decompress_usingDDict(dctx, dst, dstCapacity, src, srcSize, ZSTD_getDDict(dctx)); } size_t ZSTD_decompress(void* dst, size_t dstCapacity, const void* src, size_t srcSize) { #if defined(ZSTD_HEAPMODE) && (ZSTD_HEAPMODE>=1) size_t regenSize; ZSTD_DCtx* const dctx = ZSTD_createDCtx(); RETURN_ERROR_IF(dctx==NULL, memory_allocation, "NULL pointer!"); regenSize = ZSTD_decompressDCtx(dctx, dst, dstCapacity, src, srcSize); ZSTD_freeDCtx(dctx); return regenSize; #else /* stack mode */ ZSTD_DCtx dctx; ZSTD_initDCtx_internal(&dctx); return ZSTD_decompressDCtx(&dctx, dst, dstCapacity, src, srcSize); #endif } /*-************************************** * Advanced Streaming Decompression API * Bufferless and synchronous ****************************************/ size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx) { return dctx->expected; } /** * Similar to ZSTD_nextSrcSizeToDecompress(), but when when a block input can be streamed, * we allow taking a partial block as the input. Currently only raw uncompressed blocks can * be streamed. * * For blocks that can be streamed, this allows us to reduce the latency until we produce * output, and avoid copying the input. * * @param inputSize - The total amount of input that the caller currently has. */ static size_t ZSTD_nextSrcSizeToDecompressWithInputSize(ZSTD_DCtx* dctx, size_t inputSize) { if (!(dctx->stage == ZSTDds_decompressBlock || dctx->stage == ZSTDds_decompressLastBlock)) return dctx->expected; if (dctx->bType != bt_raw) return dctx->expected; return MIN(MAX(inputSize, 1), dctx->expected); } ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx) { switch(dctx->stage) { default: /* should not happen */ assert(0); case ZSTDds_getFrameHeaderSize: case ZSTDds_decodeFrameHeader: return ZSTDnit_frameHeader; case ZSTDds_decodeBlockHeader: return ZSTDnit_blockHeader; case ZSTDds_decompressBlock: return ZSTDnit_block; case ZSTDds_decompressLastBlock: return ZSTDnit_lastBlock; case ZSTDds_checkChecksum: return ZSTDnit_checksum; case ZSTDds_decodeSkippableHeader: case ZSTDds_skipFrame: return ZSTDnit_skippableFrame; } } static int ZSTD_isSkipFrame(ZSTD_DCtx* dctx) { return dctx->stage == ZSTDds_skipFrame; } /** ZSTD_decompressContinue() : * srcSize : must be the exact nb of bytes expected (see ZSTD_nextSrcSizeToDecompress()) * @return : nb of bytes generated into `dst` (necessarily <= `dstCapacity) * or an error code, which can be tested using ZSTD_isError() */ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_decompressContinue (srcSize:%u)", (unsigned)srcSize); /* Sanity check */ RETURN_ERROR_IF(srcSize != ZSTD_nextSrcSizeToDecompressWithInputSize(dctx, srcSize), srcSize_wrong, "not allowed"); if (dstCapacity) ZSTD_checkContinuity(dctx, dst); switch (dctx->stage) { case ZSTDds_getFrameHeaderSize : assert(src != NULL); if (dctx->format == ZSTD_f_zstd1) { /* allows header */ assert(srcSize >= ZSTD_FRAMEIDSIZE); /* to read skippable magic number */ if ((MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */ memcpy(dctx->headerBuffer, src, srcSize); dctx->expected = ZSTD_SKIPPABLEHEADERSIZE - srcSize; /* remaining to load to get full skippable frame header */ dctx->stage = ZSTDds_decodeSkippableHeader; return 0; } } dctx->headerSize = ZSTD_frameHeaderSize_internal(src, srcSize, dctx->format); if (ZSTD_isError(dctx->headerSize)) return dctx->headerSize; memcpy(dctx->headerBuffer, src, srcSize); dctx->expected = dctx->headerSize - srcSize; dctx->stage = ZSTDds_decodeFrameHeader; return 0; case ZSTDds_decodeFrameHeader: assert(src != NULL); memcpy(dctx->headerBuffer + (dctx->headerSize - srcSize), src, srcSize); FORWARD_IF_ERROR(ZSTD_decodeFrameHeader(dctx, dctx->headerBuffer, dctx->headerSize), ""); dctx->expected = ZSTD_blockHeaderSize; dctx->stage = ZSTDds_decodeBlockHeader; return 0; case ZSTDds_decodeBlockHeader: { blockProperties_t bp; size_t const cBlockSize = ZSTD_getcBlockSize(src, ZSTD_blockHeaderSize, &bp); if (ZSTD_isError(cBlockSize)) return cBlockSize; RETURN_ERROR_IF(cBlockSize > dctx->fParams.blockSizeMax, corruption_detected, "Block Size Exceeds Maximum"); dctx->expected = cBlockSize; dctx->bType = bp.blockType; dctx->rleSize = bp.origSize; if (cBlockSize) { dctx->stage = bp.lastBlock ? ZSTDds_decompressLastBlock : ZSTDds_decompressBlock; return 0; } /* empty block */ if (bp.lastBlock) { if (dctx->fParams.checksumFlag) { dctx->expected = 4; dctx->stage = ZSTDds_checkChecksum; } else { dctx->expected = 0; /* end of frame */ dctx->stage = ZSTDds_getFrameHeaderSize; } } else { dctx->expected = ZSTD_blockHeaderSize; /* jump to next header */ dctx->stage = ZSTDds_decodeBlockHeader; } return 0; } case ZSTDds_decompressLastBlock: case ZSTDds_decompressBlock: DEBUGLOG(5, "ZSTD_decompressContinue: case ZSTDds_decompressBlock"); { size_t rSize; switch(dctx->bType) { case bt_compressed: DEBUGLOG(5, "ZSTD_decompressContinue: case bt_compressed"); rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 1); dctx->expected = 0; /* Streaming not supported */ break; case bt_raw : assert(srcSize <= dctx->expected); rSize = ZSTD_copyRawBlock(dst, dstCapacity, src, srcSize); FORWARD_IF_ERROR(rSize, "ZSTD_copyRawBlock failed"); assert(rSize == srcSize); dctx->expected -= rSize; break; case bt_rle : rSize = ZSTD_setRleBlock(dst, dstCapacity, *(const BYTE*)src, dctx->rleSize); dctx->expected = 0; /* Streaming not supported */ break; case bt_reserved : /* should never happen */ default: RETURN_ERROR(corruption_detected, "invalid block type"); } FORWARD_IF_ERROR(rSize, ""); RETURN_ERROR_IF(rSize > dctx->fParams.blockSizeMax, corruption_detected, "Decompressed Block Size Exceeds Maximum"); DEBUGLOG(5, "ZSTD_decompressContinue: decoded size from block : %u", (unsigned)rSize); dctx->decodedSize += rSize; if (dctx->fParams.checksumFlag) XXH64_update(&dctx->xxhState, dst, rSize); dctx->previousDstEnd = (char*)dst + rSize; /* Stay on the same stage until we are finished streaming the block. */ if (dctx->expected > 0) { return rSize; } if (dctx->stage == ZSTDds_decompressLastBlock) { /* end of frame */ DEBUGLOG(4, "ZSTD_decompressContinue: decoded size from frame : %u", (unsigned)dctx->decodedSize); RETURN_ERROR_IF( dctx->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN && dctx->decodedSize != dctx->fParams.frameContentSize, corruption_detected, ""); if (dctx->fParams.checksumFlag) { /* another round for frame checksum */ dctx->expected = 4; dctx->stage = ZSTDds_checkChecksum; } else { dctx->expected = 0; /* ends here */ dctx->stage = ZSTDds_getFrameHeaderSize; } } else { dctx->stage = ZSTDds_decodeBlockHeader; dctx->expected = ZSTD_blockHeaderSize; } return rSize; } case ZSTDds_checkChecksum: assert(srcSize == 4); /* guaranteed by dctx->expected */ { U32 const h32 = (U32)XXH64_digest(&dctx->xxhState); U32 const check32 = MEM_readLE32(src); DEBUGLOG(4, "ZSTD_decompressContinue: checksum : calculated %08X :: %08X read", (unsigned)h32, (unsigned)check32); RETURN_ERROR_IF(check32 != h32, checksum_wrong, ""); dctx->expected = 0; dctx->stage = ZSTDds_getFrameHeaderSize; return 0; } case ZSTDds_decodeSkippableHeader: assert(src != NULL); assert(srcSize <= ZSTD_SKIPPABLEHEADERSIZE); memcpy(dctx->headerBuffer + (ZSTD_SKIPPABLEHEADERSIZE - srcSize), src, srcSize); /* complete skippable header */ dctx->expected = MEM_readLE32(dctx->headerBuffer + ZSTD_FRAMEIDSIZE); /* note : dctx->expected can grow seriously large, beyond local buffer size */ dctx->stage = ZSTDds_skipFrame; return 0; case ZSTDds_skipFrame: dctx->expected = 0; dctx->stage = ZSTDds_getFrameHeaderSize; return 0; default: assert(0); /* impossible */ RETURN_ERROR(GENERIC, "impossible to reach"); /* some compiler require default to do something */ } } static size_t ZSTD_refDictContent(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) { dctx->dictEnd = dctx->previousDstEnd; dctx->virtualStart = (const char*)dict - ((const char*)(dctx->previousDstEnd) - (const char*)(dctx->prefixStart)); dctx->prefixStart = dict; dctx->previousDstEnd = (const char*)dict + dictSize; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION dctx->dictContentBeginForFuzzing = dctx->prefixStart; dctx->dictContentEndForFuzzing = dctx->previousDstEnd; #endif return 0; } /*! ZSTD_loadDEntropy() : * dict : must point at beginning of a valid zstd dictionary. * @return : size of entropy tables read */ size_t ZSTD_loadDEntropy(ZSTD_entropyDTables_t* entropy, const void* const dict, size_t const dictSize) { const BYTE* dictPtr = (const BYTE*)dict; const BYTE* const dictEnd = dictPtr + dictSize; RETURN_ERROR_IF(dictSize <= 8, dictionary_corrupted, "dict is too small"); assert(MEM_readLE32(dict) == ZSTD_MAGIC_DICTIONARY); /* dict must be valid */ dictPtr += 8; /* skip header = magic + dictID */ ZSTD_STATIC_ASSERT(offsetof(ZSTD_entropyDTables_t, OFTable) == offsetof(ZSTD_entropyDTables_t, LLTable) + sizeof(entropy->LLTable)); ZSTD_STATIC_ASSERT(offsetof(ZSTD_entropyDTables_t, MLTable) == offsetof(ZSTD_entropyDTables_t, OFTable) + sizeof(entropy->OFTable)); ZSTD_STATIC_ASSERT(sizeof(entropy->LLTable) + sizeof(entropy->OFTable) + sizeof(entropy->MLTable) >= HUF_DECOMPRESS_WORKSPACE_SIZE); { void* const workspace = &entropy->LLTable; /* use fse tables as temporary workspace; implies fse tables are grouped together */ size_t const workspaceSize = sizeof(entropy->LLTable) + sizeof(entropy->OFTable) + sizeof(entropy->MLTable); #ifdef HUF_FORCE_DECOMPRESS_X1 /* in minimal huffman, we always use X1 variants */ size_t const hSize = HUF_readDTableX1_wksp(entropy->hufTable, dictPtr, dictEnd - dictPtr, workspace, workspaceSize); #else size_t const hSize = HUF_readDTableX2_wksp(entropy->hufTable, dictPtr, dictEnd - dictPtr, workspace, workspaceSize); #endif RETURN_ERROR_IF(HUF_isError(hSize), dictionary_corrupted, ""); dictPtr += hSize; } { short offcodeNCount[MaxOff+1]; unsigned offcodeMaxValue = MaxOff, offcodeLog; size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr); RETURN_ERROR_IF(FSE_isError(offcodeHeaderSize), dictionary_corrupted, ""); RETURN_ERROR_IF(offcodeMaxValue > MaxOff, dictionary_corrupted, ""); RETURN_ERROR_IF(offcodeLog > OffFSELog, dictionary_corrupted, ""); ZSTD_buildFSETable( entropy->OFTable, offcodeNCount, offcodeMaxValue, OF_base, OF_bits, offcodeLog); dictPtr += offcodeHeaderSize; } { short matchlengthNCount[MaxML+1]; unsigned matchlengthMaxValue = MaxML, matchlengthLog; size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr); RETURN_ERROR_IF(FSE_isError(matchlengthHeaderSize), dictionary_corrupted, ""); RETURN_ERROR_IF(matchlengthMaxValue > MaxML, dictionary_corrupted, ""); RETURN_ERROR_IF(matchlengthLog > MLFSELog, dictionary_corrupted, ""); ZSTD_buildFSETable( entropy->MLTable, matchlengthNCount, matchlengthMaxValue, ML_base, ML_bits, matchlengthLog); dictPtr += matchlengthHeaderSize; } { short litlengthNCount[MaxLL+1]; unsigned litlengthMaxValue = MaxLL, litlengthLog; size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr); RETURN_ERROR_IF(FSE_isError(litlengthHeaderSize), dictionary_corrupted, ""); RETURN_ERROR_IF(litlengthMaxValue > MaxLL, dictionary_corrupted, ""); RETURN_ERROR_IF(litlengthLog > LLFSELog, dictionary_corrupted, ""); ZSTD_buildFSETable( entropy->LLTable, litlengthNCount, litlengthMaxValue, LL_base, LL_bits, litlengthLog); dictPtr += litlengthHeaderSize; } RETURN_ERROR_IF(dictPtr+12 > dictEnd, dictionary_corrupted, ""); { int i; size_t const dictContentSize = (size_t)(dictEnd - (dictPtr+12)); for (i=0; i<3; i++) { U32 const rep = MEM_readLE32(dictPtr); dictPtr += 4; RETURN_ERROR_IF(rep==0 || rep > dictContentSize, dictionary_corrupted, ""); entropy->rep[i] = rep; } } return dictPtr - (const BYTE*)dict; } static size_t ZSTD_decompress_insertDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) { if (dictSize < 8) return ZSTD_refDictContent(dctx, dict, dictSize); { U32 const magic = MEM_readLE32(dict); if (magic != ZSTD_MAGIC_DICTIONARY) { return ZSTD_refDictContent(dctx, dict, dictSize); /* pure content mode */ } } dctx->dictID = MEM_readLE32((const char*)dict + ZSTD_FRAMEIDSIZE); /* load entropy tables */ { size_t const eSize = ZSTD_loadDEntropy(&dctx->entropy, dict, dictSize); RETURN_ERROR_IF(ZSTD_isError(eSize), dictionary_corrupted, ""); dict = (const char*)dict + eSize; dictSize -= eSize; } dctx->litEntropy = dctx->fseEntropy = 1; /* reference dictionary content */ return ZSTD_refDictContent(dctx, dict, dictSize); } size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx) { assert(dctx != NULL); dctx->expected = ZSTD_startingInputLength(dctx->format); /* dctx->format must be properly set */ dctx->stage = ZSTDds_getFrameHeaderSize; dctx->decodedSize = 0; dctx->previousDstEnd = NULL; dctx->prefixStart = NULL; dctx->virtualStart = NULL; dctx->dictEnd = NULL; dctx->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ dctx->litEntropy = dctx->fseEntropy = 0; dctx->dictID = 0; dctx->bType = bt_reserved; ZSTD_STATIC_ASSERT(sizeof(dctx->entropy.rep) == sizeof(repStartValue)); memcpy(dctx->entropy.rep, repStartValue, sizeof(repStartValue)); /* initial repcodes */ dctx->LLTptr = dctx->entropy.LLTable; dctx->MLTptr = dctx->entropy.MLTable; dctx->OFTptr = dctx->entropy.OFTable; dctx->HUFptr = dctx->entropy.hufTable; return 0; } size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) { FORWARD_IF_ERROR( ZSTD_decompressBegin(dctx) , ""); if (dict && dictSize) RETURN_ERROR_IF( ZSTD_isError(ZSTD_decompress_insertDictionary(dctx, dict, dictSize)), dictionary_corrupted, ""); return 0; } /* ====== ZSTD_DDict ====== */ size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict) { DEBUGLOG(4, "ZSTD_decompressBegin_usingDDict"); assert(dctx != NULL); if (ddict) { const char* const dictStart = (const char*)ZSTD_DDict_dictContent(ddict); size_t const dictSize = ZSTD_DDict_dictSize(ddict); const void* const dictEnd = dictStart + dictSize; dctx->ddictIsCold = (dctx->dictEnd != dictEnd); DEBUGLOG(4, "DDict is %s", dctx->ddictIsCold ? "~cold~" : "hot!"); } FORWARD_IF_ERROR( ZSTD_decompressBegin(dctx) , ""); if (ddict) { /* NULL ddict is equivalent to no dictionary */ ZSTD_copyDDictParameters(dctx, ddict); } return 0; } /*! ZSTD_getDictID_fromDict() : * Provides the dictID stored within dictionary. * if @return == 0, the dictionary is not conformant with Zstandard specification. * It can still be loaded, but as a content-only dictionary. */ unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize) { if (dictSize < 8) return 0; if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) return 0; return MEM_readLE32((const char*)dict + ZSTD_FRAMEIDSIZE); } /*! ZSTD_getDictID_fromFrame() : * Provides the dictID required to decompress frame stored within `src`. * If @return == 0, the dictID could not be decoded. * This could for one of the following reasons : * - The frame does not require a dictionary (most common case). * - The frame was built with dictID intentionally removed. * Needed dictionary is a hidden information. * Note : this use case also happens when using a non-conformant dictionary. * - `srcSize` is too small, and as a result, frame header could not be decoded. * Note : possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`. * - This is not a Zstandard frame. * When identifying the exact failure cause, it's possible to use * ZSTD_getFrameHeader(), which will provide a more precise error code. */ unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize) { ZSTD_frameHeader zfp = { 0, 0, 0, ZSTD_frame, 0, 0, 0 }; size_t const hError = ZSTD_getFrameHeader(&zfp, src, srcSize); if (ZSTD_isError(hError)) return 0; return zfp.dictID; } /*! ZSTD_decompress_usingDDict() : * Decompression using a pre-digested Dictionary * Use dictionary without significant overhead. */ size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const ZSTD_DDict* ddict) { /* pass content and size in case legacy frames are encountered */ return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, NULL, 0, ddict); } /*===================================== * Streaming decompression *====================================*/ ZSTD_DStream* ZSTD_createDStream(void) { DEBUGLOG(3, "ZSTD_createDStream"); return ZSTD_createDStream_advanced(ZSTD_defaultCMem); } ZSTD_DStream* ZSTD_initStaticDStream(void *workspace, size_t workspaceSize) { return ZSTD_initStaticDCtx(workspace, workspaceSize); } ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem) { return ZSTD_createDCtx_advanced(customMem); } size_t ZSTD_freeDStream(ZSTD_DStream* zds) { return ZSTD_freeDCtx(zds); } /* *** Initialization *** */ size_t ZSTD_DStreamInSize(void) { return ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize; } size_t ZSTD_DStreamOutSize(void) { return ZSTD_BLOCKSIZE_MAX; } size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType) { RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong, ""); ZSTD_clearDict(dctx); if (dict && dictSize != 0) { dctx->ddictLocal = ZSTD_createDDict_advanced(dict, dictSize, dictLoadMethod, dictContentType, dctx->customMem); RETURN_ERROR_IF(dctx->ddictLocal == NULL, memory_allocation, "NULL pointer!"); dctx->ddict = dctx->ddictLocal; dctx->dictUses = ZSTD_use_indefinitely; } return 0; } size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) { return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto); } size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) { return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto); } size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType) { FORWARD_IF_ERROR(ZSTD_DCtx_loadDictionary_advanced(dctx, prefix, prefixSize, ZSTD_dlm_byRef, dictContentType), ""); dctx->dictUses = ZSTD_use_once; return 0; } size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize) { return ZSTD_DCtx_refPrefix_advanced(dctx, prefix, prefixSize, ZSTD_dct_rawContent); } /* ZSTD_initDStream_usingDict() : * return : expected size, aka ZSTD_startingInputLength(). * this function cannot fail */ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize) { DEBUGLOG(4, "ZSTD_initDStream_usingDict"); FORWARD_IF_ERROR( ZSTD_DCtx_reset(zds, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_DCtx_loadDictionary(zds, dict, dictSize) , ""); return ZSTD_startingInputLength(zds->format); } /* note : this variant can't fail */ size_t ZSTD_initDStream(ZSTD_DStream* zds) { DEBUGLOG(4, "ZSTD_initDStream"); return ZSTD_initDStream_usingDDict(zds, NULL); } /* ZSTD_initDStream_usingDDict() : * ddict will just be referenced, and must outlive decompression session * this function cannot fail */ size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict) { FORWARD_IF_ERROR( ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_DCtx_refDDict(dctx, ddict) , ""); return ZSTD_startingInputLength(dctx->format); } /* ZSTD_resetDStream() : * return : expected size, aka ZSTD_startingInputLength(). * this function cannot fail */ size_t ZSTD_resetDStream(ZSTD_DStream* dctx) { FORWARD_IF_ERROR(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only), ""); return ZSTD_startingInputLength(dctx->format); } size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict) { RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong, ""); ZSTD_clearDict(dctx); if (ddict) { dctx->ddict = ddict; dctx->dictUses = ZSTD_use_indefinitely; } return 0; } /* ZSTD_DCtx_setMaxWindowSize() : * note : no direct equivalence in ZSTD_DCtx_setParameter, * since this version sets windowSize, and the other sets windowLog */ size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize) { ZSTD_bounds const bounds = ZSTD_dParam_getBounds(ZSTD_d_windowLogMax); size_t const min = (size_t)1 << bounds.lowerBound; size_t const max = (size_t)1 << bounds.upperBound; RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong, ""); RETURN_ERROR_IF(maxWindowSize < min, parameter_outOfBound, ""); RETURN_ERROR_IF(maxWindowSize > max, parameter_outOfBound, ""); dctx->maxWindowSize = maxWindowSize; return 0; } size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format) { return ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, format); } ZSTD_bounds ZSTD_dParam_getBounds(ZSTD_dParameter dParam) { ZSTD_bounds bounds = { 0, 0, 0 }; switch(dParam) { case ZSTD_d_windowLogMax: bounds.lowerBound = ZSTD_WINDOWLOG_ABSOLUTEMIN; bounds.upperBound = ZSTD_WINDOWLOG_MAX; return bounds; case ZSTD_d_format: bounds.lowerBound = (int)ZSTD_f_zstd1; bounds.upperBound = (int)ZSTD_f_zstd1_magicless; ZSTD_STATIC_ASSERT(ZSTD_f_zstd1 < ZSTD_f_zstd1_magicless); return bounds; case ZSTD_d_stableOutBuffer: bounds.lowerBound = (int)ZSTD_obm_buffered; bounds.upperBound = (int)ZSTD_obm_stable; return bounds; default:; } bounds.error = ERROR(parameter_unsupported); return bounds; } /* ZSTD_dParam_withinBounds: * @return 1 if value is within dParam bounds, * 0 otherwise */ static int ZSTD_dParam_withinBounds(ZSTD_dParameter dParam, int value) { ZSTD_bounds const bounds = ZSTD_dParam_getBounds(dParam); if (ZSTD_isError(bounds.error)) return 0; if (value < bounds.lowerBound) return 0; if (value > bounds.upperBound) return 0; return 1; } #define CHECK_DBOUNDS(p,v) { \ RETURN_ERROR_IF(!ZSTD_dParam_withinBounds(p, v), parameter_outOfBound, ""); \ } size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter dParam, int value) { RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong, ""); switch(dParam) { case ZSTD_d_windowLogMax: if (value == 0) value = ZSTD_WINDOWLOG_LIMIT_DEFAULT; CHECK_DBOUNDS(ZSTD_d_windowLogMax, value); dctx->maxWindowSize = ((size_t)1) << value; return 0; case ZSTD_d_format: CHECK_DBOUNDS(ZSTD_d_format, value); dctx->format = (ZSTD_format_e)value; return 0; case ZSTD_d_stableOutBuffer: CHECK_DBOUNDS(ZSTD_d_stableOutBuffer, value); dctx->outBufferMode = (ZSTD_outBufferMode_e)value; return 0; default:; } RETURN_ERROR(parameter_unsupported, ""); } size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset) { if ( (reset == ZSTD_reset_session_only) || (reset == ZSTD_reset_session_and_parameters) ) { dctx->streamStage = zdss_init; dctx->noForwardProgress = 0; } if ( (reset == ZSTD_reset_parameters) || (reset == ZSTD_reset_session_and_parameters) ) { RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong, ""); ZSTD_clearDict(dctx); dctx->format = ZSTD_f_zstd1; dctx->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT; } return 0; } size_t ZSTD_sizeof_DStream(const ZSTD_DStream* dctx) { return ZSTD_sizeof_DCtx(dctx); } size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize) { size_t const blockSize = (size_t) MIN(windowSize, ZSTD_BLOCKSIZE_MAX); unsigned long long const neededRBSize = windowSize + blockSize + (WILDCOPY_OVERLENGTH * 2); unsigned long long const neededSize = MIN(frameContentSize, neededRBSize); size_t const minRBSize = (size_t) neededSize; RETURN_ERROR_IF((unsigned long long)minRBSize != neededSize, frameParameter_windowTooLarge, ""); return minRBSize; } size_t ZSTD_estimateDStreamSize(size_t windowSize) { size_t const blockSize = MIN(windowSize, ZSTD_BLOCKSIZE_MAX); size_t const inBuffSize = blockSize; /* no block can be larger */ size_t const outBuffSize = ZSTD_decodingBufferSize_min(windowSize, ZSTD_CONTENTSIZE_UNKNOWN); return ZSTD_estimateDCtxSize() + inBuffSize + outBuffSize; } size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize) { U32 const windowSizeMax = 1U << ZSTD_WINDOWLOG_MAX; /* note : should be user-selectable, but requires an additional parameter (or a dctx) */ ZSTD_frameHeader zfh; size_t const err = ZSTD_getFrameHeader(&zfh, src, srcSize); if (ZSTD_isError(err)) return err; RETURN_ERROR_IF(err>0, srcSize_wrong, ""); RETURN_ERROR_IF(zfh.windowSize > windowSizeMax, frameParameter_windowTooLarge, ""); return ZSTD_estimateDStreamSize((size_t)zfh.windowSize); } /* ***** Decompression ***** */ static int ZSTD_DCtx_isOverflow(ZSTD_DStream* zds, size_t const neededInBuffSize, size_t const neededOutBuffSize) { return (zds->inBuffSize + zds->outBuffSize) >= (neededInBuffSize + neededOutBuffSize) * ZSTD_WORKSPACETOOLARGE_FACTOR; } static void ZSTD_DCtx_updateOversizedDuration(ZSTD_DStream* zds, size_t const neededInBuffSize, size_t const neededOutBuffSize) { if (ZSTD_DCtx_isOverflow(zds, neededInBuffSize, neededOutBuffSize)) zds->oversizedDuration++; else zds->oversizedDuration = 0; } static int ZSTD_DCtx_isOversizedTooLong(ZSTD_DStream* zds) { return zds->oversizedDuration >= ZSTD_WORKSPACETOOLARGE_MAXDURATION; } /* Checks that the output buffer hasn't changed if ZSTD_obm_stable is used. */ static size_t ZSTD_checkOutBuffer(ZSTD_DStream const* zds, ZSTD_outBuffer const* output) { ZSTD_outBuffer const expect = zds->expectedOutBuffer; /* No requirement when ZSTD_obm_stable is not enabled. */ if (zds->outBufferMode != ZSTD_obm_stable) return 0; /* Any buffer is allowed in zdss_init, this must be the same for every other call until * the context is reset. */ if (zds->streamStage == zdss_init) return 0; /* The buffer must match our expectation exactly. */ if (expect.dst == output->dst && expect.pos == output->pos && expect.size == output->size) return 0; RETURN_ERROR(dstBuffer_wrong, "ZSTD_obm_stable enabled but output differs!"); } /* Calls ZSTD_decompressContinue() with the right parameters for ZSTD_decompressStream() * and updates the stage and the output buffer state. This call is extracted so it can be * used both when reading directly from the ZSTD_inBuffer, and in buffered input mode. * NOTE: You must break after calling this function since the streamStage is modified. */ static size_t ZSTD_decompressContinueStream( ZSTD_DStream* zds, char** op, char* oend, void const* src, size_t srcSize) { int const isSkipFrame = ZSTD_isSkipFrame(zds); if (zds->outBufferMode == ZSTD_obm_buffered) { size_t const dstSize = isSkipFrame ? 0 : zds->outBuffSize - zds->outStart; size_t const decodedSize = ZSTD_decompressContinue(zds, zds->outBuff + zds->outStart, dstSize, src, srcSize); FORWARD_IF_ERROR(decodedSize, ""); if (!decodedSize && !isSkipFrame) { zds->streamStage = zdss_read; } else { zds->outEnd = zds->outStart + decodedSize; zds->streamStage = zdss_flush; } } else { /* Write directly into the output buffer */ size_t const dstSize = isSkipFrame ? 0 : oend - *op; size_t const decodedSize = ZSTD_decompressContinue(zds, *op, dstSize, src, srcSize); FORWARD_IF_ERROR(decodedSize, ""); *op += decodedSize; /* Flushing is not needed. */ zds->streamStage = zdss_read; assert(*op <= oend); assert(zds->outBufferMode == ZSTD_obm_stable); } return 0; } size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { const char* const src = (const char*)input->src; const char* const istart = input->pos != 0 ? src + input->pos : src; const char* const iend = input->size != 0 ? src + input->size : src; const char* ip = istart; char* const dst = (char*)output->dst; char* const ostart = output->pos != 0 ? dst + output->pos : dst; char* const oend = output->size != 0 ? dst + output->size : dst; char* op = ostart; U32 someMoreWork = 1; DEBUGLOG(5, "ZSTD_decompressStream"); RETURN_ERROR_IF( input->pos > input->size, srcSize_wrong, "forbidden. in: pos: %u vs size: %u", (U32)input->pos, (U32)input->size); RETURN_ERROR_IF( output->pos > output->size, dstSize_tooSmall, "forbidden. out: pos: %u vs size: %u", (U32)output->pos, (U32)output->size); DEBUGLOG(5, "input size : %u", (U32)(input->size - input->pos)); FORWARD_IF_ERROR(ZSTD_checkOutBuffer(zds, output), ""); while (someMoreWork) { switch(zds->streamStage) { case zdss_init : DEBUGLOG(5, "stage zdss_init => transparent reset "); zds->streamStage = zdss_loadHeader; zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; zds->legacyVersion = 0; zds->hostageByte = 0; zds->expectedOutBuffer = *output; /* fall-through */ case zdss_loadHeader : DEBUGLOG(5, "stage zdss_loadHeader (srcSize : %u)", (U32)(iend - ip)); #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) if (zds->legacyVersion) { RETURN_ERROR_IF(zds->staticSize, memory_allocation, "legacy support is incompatible with static dctx"); { size_t const hint = ZSTD_decompressLegacyStream(zds->legacyContext, zds->legacyVersion, output, input); if (hint==0) zds->streamStage = zdss_init; return hint; } } #endif { size_t const hSize = ZSTD_getFrameHeader_advanced(&zds->fParams, zds->headerBuffer, zds->lhSize, zds->format); DEBUGLOG(5, "header size : %u", (U32)hSize); if (ZSTD_isError(hSize)) { #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) U32 const legacyVersion = ZSTD_isLegacy(istart, iend-istart); if (legacyVersion) { ZSTD_DDict const* const ddict = ZSTD_getDDict(zds); const void* const dict = ddict ? ZSTD_DDict_dictContent(ddict) : NULL; size_t const dictSize = ddict ? ZSTD_DDict_dictSize(ddict) : 0; DEBUGLOG(5, "ZSTD_decompressStream: detected legacy version v0.%u", legacyVersion); RETURN_ERROR_IF(zds->staticSize, memory_allocation, "legacy support is incompatible with static dctx"); FORWARD_IF_ERROR(ZSTD_initLegacyStream(&zds->legacyContext, zds->previousLegacyVersion, legacyVersion, dict, dictSize), ""); zds->legacyVersion = zds->previousLegacyVersion = legacyVersion; { size_t const hint = ZSTD_decompressLegacyStream(zds->legacyContext, legacyVersion, output, input); if (hint==0) zds->streamStage = zdss_init; /* or stay in stage zdss_loadHeader */ return hint; } } #endif return hSize; /* error */ } if (hSize != 0) { /* need more input */ size_t const toLoad = hSize - zds->lhSize; /* if hSize!=0, hSize > zds->lhSize */ size_t const remainingInput = (size_t)(iend-ip); assert(iend >= ip); if (toLoad > remainingInput) { /* not enough input to load full header */ if (remainingInput > 0) { memcpy(zds->headerBuffer + zds->lhSize, ip, remainingInput); zds->lhSize += remainingInput; } input->pos = input->size; return (MAX((size_t)ZSTD_FRAMEHEADERSIZE_MIN(zds->format), hSize) - zds->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */ } assert(ip != NULL); memcpy(zds->headerBuffer + zds->lhSize, ip, toLoad); zds->lhSize = hSize; ip += toLoad; break; } } /* check for single-pass mode opportunity */ if (zds->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN && zds->fParams.frameType != ZSTD_skippableFrame && (U64)(size_t)(oend-op) >= zds->fParams.frameContentSize) { size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend-istart); if (cSize <= (size_t)(iend-istart)) { /* shortcut : using single-pass mode */ size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, oend-op, istart, cSize, ZSTD_getDDict(zds)); if (ZSTD_isError(decompressedSize)) return decompressedSize; DEBUGLOG(4, "shortcut to single-pass ZSTD_decompress_usingDDict()") ip = istart + cSize; op += decompressedSize; zds->expected = 0; zds->streamStage = zdss_init; someMoreWork = 0; break; } } /* Check output buffer is large enough for ZSTD_odm_stable. */ if (zds->outBufferMode == ZSTD_obm_stable && zds->fParams.frameType != ZSTD_skippableFrame && zds->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN && (U64)(size_t)(oend-op) < zds->fParams.frameContentSize) { RETURN_ERROR(dstSize_tooSmall, "ZSTD_obm_stable passed but ZSTD_outBuffer is too small"); } /* Consume header (see ZSTDds_decodeFrameHeader) */ DEBUGLOG(4, "Consume header"); FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDDict(zds, ZSTD_getDDict(zds)), ""); if ((MEM_readLE32(zds->headerBuffer) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */ zds->expected = MEM_readLE32(zds->headerBuffer + ZSTD_FRAMEIDSIZE); zds->stage = ZSTDds_skipFrame; } else { FORWARD_IF_ERROR(ZSTD_decodeFrameHeader(zds, zds->headerBuffer, zds->lhSize), ""); zds->expected = ZSTD_blockHeaderSize; zds->stage = ZSTDds_decodeBlockHeader; } /* control buffer memory usage */ DEBUGLOG(4, "Control max memory usage (%u KB <= max %u KB)", (U32)(zds->fParams.windowSize >>10), (U32)(zds->maxWindowSize >> 10) ); zds->fParams.windowSize = MAX(zds->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN); RETURN_ERROR_IF(zds->fParams.windowSize > zds->maxWindowSize, frameParameter_windowTooLarge, ""); /* Adapt buffer sizes to frame header instructions */ { size_t const neededInBuffSize = MAX(zds->fParams.blockSizeMax, 4 /* frame checksum */); size_t const neededOutBuffSize = zds->outBufferMode == ZSTD_obm_buffered ? ZSTD_decodingBufferSize_min(zds->fParams.windowSize, zds->fParams.frameContentSize) : 0; ZSTD_DCtx_updateOversizedDuration(zds, neededInBuffSize, neededOutBuffSize); { int const tooSmall = (zds->inBuffSize < neededInBuffSize) || (zds->outBuffSize < neededOutBuffSize); int const tooLarge = ZSTD_DCtx_isOversizedTooLong(zds); if (tooSmall || tooLarge) { size_t const bufferSize = neededInBuffSize + neededOutBuffSize; DEBUGLOG(4, "inBuff : from %u to %u", (U32)zds->inBuffSize, (U32)neededInBuffSize); DEBUGLOG(4, "outBuff : from %u to %u", (U32)zds->outBuffSize, (U32)neededOutBuffSize); if (zds->staticSize) { /* static DCtx */ DEBUGLOG(4, "staticSize : %u", (U32)zds->staticSize); assert(zds->staticSize >= sizeof(ZSTD_DCtx)); /* controlled at init */ RETURN_ERROR_IF( bufferSize > zds->staticSize - sizeof(ZSTD_DCtx), memory_allocation, ""); } else { ZSTD_free(zds->inBuff, zds->customMem); zds->inBuffSize = 0; zds->outBuffSize = 0; zds->inBuff = (char*)ZSTD_malloc(bufferSize, zds->customMem); RETURN_ERROR_IF(zds->inBuff == NULL, memory_allocation, ""); } zds->inBuffSize = neededInBuffSize; zds->outBuff = zds->inBuff + zds->inBuffSize; zds->outBuffSize = neededOutBuffSize; } } } zds->streamStage = zdss_read; /* fall-through */ case zdss_read: DEBUGLOG(5, "stage zdss_read"); { size_t const neededInSize = ZSTD_nextSrcSizeToDecompressWithInputSize(zds, iend - ip); DEBUGLOG(5, "neededInSize = %u", (U32)neededInSize); if (neededInSize==0) { /* end of frame */ zds->streamStage = zdss_init; someMoreWork = 0; break; } if ((size_t)(iend-ip) >= neededInSize) { /* decode directly from src */ FORWARD_IF_ERROR(ZSTD_decompressContinueStream(zds, &op, oend, ip, neededInSize), ""); ip += neededInSize; /* Function modifies the stage so we must break */ break; } } if (ip==iend) { someMoreWork = 0; break; } /* no more input */ zds->streamStage = zdss_load; /* fall-through */ case zdss_load: { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds); size_t const toLoad = neededInSize - zds->inPos; int const isSkipFrame = ZSTD_isSkipFrame(zds); size_t loadedSize; /* At this point we shouldn't be decompressing a block that we can stream. */ assert(neededInSize == ZSTD_nextSrcSizeToDecompressWithInputSize(zds, iend - ip)); if (isSkipFrame) { loadedSize = MIN(toLoad, (size_t)(iend-ip)); } else { RETURN_ERROR_IF(toLoad > zds->inBuffSize - zds->inPos, corruption_detected, "should never happen"); loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, iend-ip); } ip += loadedSize; zds->inPos += loadedSize; if (loadedSize < toLoad) { someMoreWork = 0; break; } /* not enough input, wait for more */ /* decode loaded input */ zds->inPos = 0; /* input is consumed */ FORWARD_IF_ERROR(ZSTD_decompressContinueStream(zds, &op, oend, zds->inBuff, neededInSize), ""); /* Function modifies the stage so we must break */ break; } case zdss_flush: { size_t const toFlushSize = zds->outEnd - zds->outStart; size_t const flushedSize = ZSTD_limitCopy(op, oend-op, zds->outBuff + zds->outStart, toFlushSize); op += flushedSize; zds->outStart += flushedSize; if (flushedSize == toFlushSize) { /* flush completed */ zds->streamStage = zdss_read; if ( (zds->outBuffSize < zds->fParams.frameContentSize) && (zds->outStart + zds->fParams.blockSizeMax > zds->outBuffSize) ) { DEBUGLOG(5, "restart filling outBuff from beginning (left:%i, needed:%u)", (int)(zds->outBuffSize - zds->outStart), (U32)zds->fParams.blockSizeMax); zds->outStart = zds->outEnd = 0; } break; } } /* cannot complete flush */ someMoreWork = 0; break; default: assert(0); /* impossible */ RETURN_ERROR(GENERIC, "impossible to reach"); /* some compiler require default to do something */ } } /* result */ input->pos = (size_t)(ip - (const char*)(input->src)); output->pos = (size_t)(op - (char*)(output->dst)); /* Update the expected output buffer for ZSTD_obm_stable. */ zds->expectedOutBuffer = *output; if ((ip==istart) && (op==ostart)) { /* no forward progress */ zds->noForwardProgress ++; if (zds->noForwardProgress >= ZSTD_NO_FORWARD_PROGRESS_MAX) { RETURN_ERROR_IF(op==oend, dstSize_tooSmall, ""); RETURN_ERROR_IF(ip==iend, srcSize_wrong, ""); assert(0); } } else { zds->noForwardProgress = 0; } { size_t nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zds); if (!nextSrcSizeHint) { /* frame fully decoded */ if (zds->outEnd == zds->outStart) { /* output fully flushed */ if (zds->hostageByte) { if (input->pos >= input->size) { /* can't release hostage (not present) */ zds->streamStage = zdss_read; return 1; } input->pos++; /* release hostage */ } /* zds->hostageByte */ return 0; } /* zds->outEnd == zds->outStart */ if (!zds->hostageByte) { /* output not fully flushed; keep last byte as hostage; will be released when all output is flushed */ input->pos--; /* note : pos > 0, otherwise, impossible to finish reading last block */ zds->hostageByte=1; } return 1; } /* nextSrcSizeHint==0 */ nextSrcSizeHint += ZSTD_blockHeaderSize * (ZSTD_nextInputType(zds) == ZSTDnit_block); /* preload header of next block */ assert(zds->inPos <= nextSrcSizeHint); nextSrcSizeHint -= zds->inPos; /* part already loaded*/ return nextSrcSizeHint; } } size_t ZSTD_decompressStream_simpleArgs ( ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, size_t* dstPos, const void* src, size_t srcSize, size_t* srcPos) { ZSTD_outBuffer output = { dst, dstCapacity, *dstPos }; ZSTD_inBuffer input = { src, srcSize, *srcPos }; /* ZSTD_compress_generic() will check validity of dstPos and srcPos */ size_t const cErr = ZSTD_decompressStream(dctx, &output, &input); *dstPos = output.pos; *srcPos = input.pos; return cErr; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/decompress/zstd_decompress_block.c0000644000076500000240000017117514601576577026571 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* zstd_decompress_block : * this module takes care of decompressing _compressed_ block */ /*-******************************************************* * Dependencies *********************************************************/ #include /* memcpy, memmove, memset */ #include "../common/compiler.h" /* prefetch */ #include "../common/cpu.h" /* bmi2 */ #include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY #include "../common/fse.h" #define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "../common/zstd_internal.h" #include "zstd_decompress_internal.h" /* ZSTD_DCtx */ #include "zstd_ddict.h" /* ZSTD_DDictDictContent */ #include "zstd_decompress_block.h" /*_******************************************************* * Macros **********************************************************/ /* These two optional macros force the use one way or another of the two * ZSTD_decompressSequences implementations. You can't force in both directions * at the same time. */ #if defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) #error "Cannot force the use of the short and the long ZSTD_decompressSequences variants!" #endif /*_******************************************************* * Memory operations **********************************************************/ static void ZSTD_copy4(void* dst, const void* src) { memcpy(dst, src, 4); } /*-************************************************************* * Block decoding ***************************************************************/ /*! ZSTD_getcBlockSize() : * Provides the size of compressed block from block header `src` */ size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* bpPtr) { RETURN_ERROR_IF(srcSize < ZSTD_blockHeaderSize, srcSize_wrong, ""); { U32 const cBlockHeader = MEM_readLE24(src); U32 const cSize = cBlockHeader >> 3; bpPtr->lastBlock = cBlockHeader & 1; bpPtr->blockType = (blockType_e)((cBlockHeader >> 1) & 3); bpPtr->origSize = cSize; /* only useful for RLE */ if (bpPtr->blockType == bt_rle) return 1; RETURN_ERROR_IF(bpPtr->blockType == bt_reserved, corruption_detected, ""); return cSize; } } /* Hidden declaration for fullbench */ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, const void* src, size_t srcSize); /*! ZSTD_decodeLiteralsBlock() : * @return : nb of bytes read from src (< srcSize ) * note : symbol not declared but exposed for fullbench */ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, const void* src, size_t srcSize) /* note : srcSize < BLOCKSIZE */ { DEBUGLOG(5, "ZSTD_decodeLiteralsBlock"); RETURN_ERROR_IF(srcSize < MIN_CBLOCK_SIZE, corruption_detected, ""); { const BYTE* const istart = (const BYTE*) src; symbolEncodingType_e const litEncType = (symbolEncodingType_e)(istart[0] & 3); switch(litEncType) { case set_repeat: DEBUGLOG(5, "set_repeat flag : re-using stats from previous compressed literals block"); RETURN_ERROR_IF(dctx->litEntropy==0, dictionary_corrupted, ""); /* fall-through */ case set_compressed: RETURN_ERROR_IF(srcSize < 5, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need up to 5 for case 3"); { size_t lhSize, litSize, litCSize; U32 singleStream=0; U32 const lhlCode = (istart[0] >> 2) & 3; U32 const lhc = MEM_readLE32(istart); size_t hufSuccess; switch(lhlCode) { case 0: case 1: default: /* note : default is impossible, since lhlCode into [0..3] */ /* 2 - 2 - 10 - 10 */ singleStream = !lhlCode; lhSize = 3; litSize = (lhc >> 4) & 0x3FF; litCSize = (lhc >> 14) & 0x3FF; break; case 2: /* 2 - 2 - 14 - 14 */ lhSize = 4; litSize = (lhc >> 4) & 0x3FFF; litCSize = lhc >> 18; break; case 3: /* 2 - 2 - 18 - 18 */ lhSize = 5; litSize = (lhc >> 4) & 0x3FFFF; litCSize = (lhc >> 22) + ((size_t)istart[4] << 10); break; } RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected, ""); RETURN_ERROR_IF(litCSize + lhSize > srcSize, corruption_detected, ""); /* prefetch huffman table if cold */ if (dctx->ddictIsCold && (litSize > 768 /* heuristic */)) { PREFETCH_AREA(dctx->HUFptr, sizeof(dctx->entropy.hufTable)); } if (litEncType==set_repeat) { if (singleStream) { hufSuccess = HUF_decompress1X_usingDTable_bmi2( dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr, dctx->bmi2); } else { hufSuccess = HUF_decompress4X_usingDTable_bmi2( dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr, dctx->bmi2); } } else { if (singleStream) { #if defined(HUF_FORCE_DECOMPRESS_X2) hufSuccess = HUF_decompress1X_DCtx_wksp( dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->workspace, sizeof(dctx->workspace)); #else hufSuccess = HUF_decompress1X1_DCtx_wksp_bmi2( dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->workspace, sizeof(dctx->workspace), dctx->bmi2); #endif } else { hufSuccess = HUF_decompress4X_hufOnly_wksp_bmi2( dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->workspace, sizeof(dctx->workspace), dctx->bmi2); } } RETURN_ERROR_IF(HUF_isError(hufSuccess), corruption_detected, ""); dctx->litPtr = dctx->litBuffer; dctx->litSize = litSize; dctx->litEntropy = 1; if (litEncType==set_compressed) dctx->HUFptr = dctx->entropy.hufTable; memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH); return litCSize + lhSize; } case set_basic: { size_t litSize, lhSize; U32 const lhlCode = ((istart[0]) >> 2) & 3; switch(lhlCode) { case 0: case 2: default: /* note : default is impossible, since lhlCode into [0..3] */ lhSize = 1; litSize = istart[0] >> 3; break; case 1: lhSize = 2; litSize = MEM_readLE16(istart) >> 4; break; case 3: lhSize = 3; litSize = MEM_readLE24(istart) >> 4; break; } if (lhSize+litSize+WILDCOPY_OVERLENGTH > srcSize) { /* risk reading beyond src buffer with wildcopy */ RETURN_ERROR_IF(litSize+lhSize > srcSize, corruption_detected, ""); memcpy(dctx->litBuffer, istart+lhSize, litSize); dctx->litPtr = dctx->litBuffer; dctx->litSize = litSize; memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH); return lhSize+litSize; } /* direct reference into compressed stream */ dctx->litPtr = istart+lhSize; dctx->litSize = litSize; return lhSize+litSize; } case set_rle: { U32 const lhlCode = ((istart[0]) >> 2) & 3; size_t litSize, lhSize; switch(lhlCode) { case 0: case 2: default: /* note : default is impossible, since lhlCode into [0..3] */ lhSize = 1; litSize = istart[0] >> 3; break; case 1: lhSize = 2; litSize = MEM_readLE16(istart) >> 4; break; case 3: lhSize = 3; litSize = MEM_readLE24(istart) >> 4; RETURN_ERROR_IF(srcSize<4, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need lhSize+1 = 4"); break; } RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected, ""); memset(dctx->litBuffer, istart[lhSize], litSize + WILDCOPY_OVERLENGTH); dctx->litPtr = dctx->litBuffer; dctx->litSize = litSize; return lhSize+1; } default: RETURN_ERROR(corruption_detected, "impossible"); } } } /* Default FSE distribution tables. * These are pre-calculated FSE decoding tables using default distributions as defined in specification : * https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#default-distributions * They were generated programmatically with following method : * - start from default distributions, present in /lib/common/zstd_internal.h * - generate tables normally, using ZSTD_buildFSETable() * - printout the content of tables * - pretify output, report below, test with fuzzer to ensure it's correct */ /* Default FSE distribution table for Literal Lengths */ static const ZSTD_seqSymbol LL_defaultDTable[(1<tableLog = 0; DTableH->fastMode = 0; cell->nbBits = 0; cell->nextState = 0; assert(nbAddBits < 255); cell->nbAdditionalBits = (BYTE)nbAddBits; cell->baseValue = baseValue; } /* ZSTD_buildFSETable() : * generate FSE decoding table for one symbol (ll, ml or off) * cannot fail if input is valid => * all inputs are presumed validated at this stage */ void ZSTD_buildFSETable(ZSTD_seqSymbol* dt, const short* normalizedCounter, unsigned maxSymbolValue, const U32* baseValue, const U32* nbAdditionalBits, unsigned tableLog) { ZSTD_seqSymbol* const tableDecode = dt+1; U16 symbolNext[MaxSeq+1]; U32 const maxSV1 = maxSymbolValue + 1; U32 const tableSize = 1 << tableLog; U32 highThreshold = tableSize-1; /* Sanity Checks */ assert(maxSymbolValue <= MaxSeq); assert(tableLog <= MaxFSELog); /* Init, lay down lowprob symbols */ { ZSTD_seqSymbol_header DTableH; DTableH.tableLog = tableLog; DTableH.fastMode = 1; { S16 const largeLimit= (S16)(1 << (tableLog-1)); U32 s; for (s=0; s= largeLimit) DTableH.fastMode=0; assert(normalizedCounter[s]>=0); symbolNext[s] = (U16)normalizedCounter[s]; } } } memcpy(dt, &DTableH, sizeof(DTableH)); } /* Spread symbols */ { U32 const tableMask = tableSize-1; U32 const step = FSE_TABLESTEP(tableSize); U32 s, position = 0; for (s=0; s highThreshold) position = (position + step) & tableMask; /* lowprob area */ } } assert(position == 0); /* position must reach all cells once, otherwise normalizedCounter is incorrect */ } /* Build Decoding table */ { U32 u; for (u=0; u max, corruption_detected, ""); { U32 const symbol = *(const BYTE*)src; U32 const baseline = baseValue[symbol]; U32 const nbBits = nbAdditionalBits[symbol]; ZSTD_buildSeqTable_rle(DTableSpace, baseline, nbBits); } *DTablePtr = DTableSpace; return 1; case set_basic : *DTablePtr = defaultTable; return 0; case set_repeat: RETURN_ERROR_IF(!flagRepeatTable, corruption_detected, ""); /* prefetch FSE table if used */ if (ddictIsCold && (nbSeq > 24 /* heuristic */)) { const void* const pStart = *DTablePtr; size_t const pSize = sizeof(ZSTD_seqSymbol) * (SEQSYMBOL_TABLE_SIZE(maxLog)); PREFETCH_AREA(pStart, pSize); } return 0; case set_compressed : { unsigned tableLog; S16 norm[MaxSeq+1]; size_t const headerSize = FSE_readNCount(norm, &max, &tableLog, src, srcSize); RETURN_ERROR_IF(FSE_isError(headerSize), corruption_detected, ""); RETURN_ERROR_IF(tableLog > maxLog, corruption_detected, ""); ZSTD_buildFSETable(DTableSpace, norm, max, baseValue, nbAdditionalBits, tableLog); *DTablePtr = DTableSpace; return headerSize; } default : assert(0); RETURN_ERROR(GENERIC, "impossible"); } } size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, const void* src, size_t srcSize) { const BYTE* const istart = (const BYTE* const)src; const BYTE* const iend = istart + srcSize; const BYTE* ip = istart; int nbSeq; DEBUGLOG(5, "ZSTD_decodeSeqHeaders"); /* check */ RETURN_ERROR_IF(srcSize < MIN_SEQUENCES_SIZE, srcSize_wrong, ""); /* SeqHead */ nbSeq = *ip++; if (!nbSeq) { *nbSeqPtr=0; RETURN_ERROR_IF(srcSize != 1, srcSize_wrong, ""); return 1; } if (nbSeq > 0x7F) { if (nbSeq == 0xFF) { RETURN_ERROR_IF(ip+2 > iend, srcSize_wrong, ""); nbSeq = MEM_readLE16(ip) + LONGNBSEQ, ip+=2; } else { RETURN_ERROR_IF(ip >= iend, srcSize_wrong, ""); nbSeq = ((nbSeq-0x80)<<8) + *ip++; } } *nbSeqPtr = nbSeq; /* FSE table descriptors */ RETURN_ERROR_IF(ip+1 > iend, srcSize_wrong, ""); /* minimum possible size: 1 byte for symbol encoding types */ { symbolEncodingType_e const LLtype = (symbolEncodingType_e)(*ip >> 6); symbolEncodingType_e const OFtype = (symbolEncodingType_e)((*ip >> 4) & 3); symbolEncodingType_e const MLtype = (symbolEncodingType_e)((*ip >> 2) & 3); ip++; /* Build DTables */ { size_t const llhSize = ZSTD_buildSeqTable(dctx->entropy.LLTable, &dctx->LLTptr, LLtype, MaxLL, LLFSELog, ip, iend-ip, LL_base, LL_bits, LL_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq); RETURN_ERROR_IF(ZSTD_isError(llhSize), corruption_detected, "ZSTD_buildSeqTable failed"); ip += llhSize; } { size_t const ofhSize = ZSTD_buildSeqTable(dctx->entropy.OFTable, &dctx->OFTptr, OFtype, MaxOff, OffFSELog, ip, iend-ip, OF_base, OF_bits, OF_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq); RETURN_ERROR_IF(ZSTD_isError(ofhSize), corruption_detected, "ZSTD_buildSeqTable failed"); ip += ofhSize; } { size_t const mlhSize = ZSTD_buildSeqTable(dctx->entropy.MLTable, &dctx->MLTptr, MLtype, MaxML, MLFSELog, ip, iend-ip, ML_base, ML_bits, ML_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq); RETURN_ERROR_IF(ZSTD_isError(mlhSize), corruption_detected, "ZSTD_buildSeqTable failed"); ip += mlhSize; } } return ip-istart; } typedef struct { size_t litLength; size_t matchLength; size_t offset; const BYTE* match; } seq_t; typedef struct { size_t state; const ZSTD_seqSymbol* table; } ZSTD_fseState; typedef struct { BIT_DStream_t DStream; ZSTD_fseState stateLL; ZSTD_fseState stateOffb; ZSTD_fseState stateML; size_t prevOffset[ZSTD_REP_NUM]; const BYTE* prefixStart; const BYTE* dictEnd; size_t pos; } seqState_t; /*! ZSTD_overlapCopy8() : * Copies 8 bytes from ip to op and updates op and ip where ip <= op. * If the offset is < 8 then the offset is spread to at least 8 bytes. * * Precondition: *ip <= *op * Postcondition: *op - *op >= 8 */ HINT_INLINE void ZSTD_overlapCopy8(BYTE** op, BYTE const** ip, size_t offset) { assert(*ip <= *op); if (offset < 8) { /* close range match, overlap */ static const U32 dec32table[] = { 0, 1, 2, 1, 4, 4, 4, 4 }; /* added */ static const int dec64table[] = { 8, 8, 8, 7, 8, 9,10,11 }; /* subtracted */ int const sub2 = dec64table[offset]; (*op)[0] = (*ip)[0]; (*op)[1] = (*ip)[1]; (*op)[2] = (*ip)[2]; (*op)[3] = (*ip)[3]; *ip += dec32table[offset]; ZSTD_copy4(*op+4, *ip); *ip -= sub2; } else { ZSTD_copy8(*op, *ip); } *ip += 8; *op += 8; assert(*op - *ip >= 8); } /*! ZSTD_safecopy() : * Specialized version of memcpy() that is allowed to READ up to WILDCOPY_OVERLENGTH past the input buffer * and write up to 16 bytes past oend_w (op >= oend_w is allowed). * This function is only called in the uncommon case where the sequence is near the end of the block. It * should be fast for a single long sequence, but can be slow for several short sequences. * * @param ovtype controls the overlap detection * - ZSTD_no_overlap: The source and destination are guaranteed to be at least WILDCOPY_VECLEN bytes apart. * - ZSTD_overlap_src_before_dst: The src and dst may overlap and may be any distance apart. * The src buffer must be before the dst buffer. */ static void ZSTD_safecopy(BYTE* op, BYTE* const oend_w, BYTE const* ip, ptrdiff_t length, ZSTD_overlap_e ovtype) { ptrdiff_t const diff = op - ip; BYTE* const oend = op + length; assert((ovtype == ZSTD_no_overlap && (diff <= -8 || diff >= 8 || op >= oend_w)) || (ovtype == ZSTD_overlap_src_before_dst && diff >= 0)); if (length < 8) { /* Handle short lengths. */ while (op < oend) *op++ = *ip++; return; } if (ovtype == ZSTD_overlap_src_before_dst) { /* Copy 8 bytes and ensure the offset >= 8 when there can be overlap. */ assert(length >= 8); ZSTD_overlapCopy8(&op, &ip, diff); assert(op - ip >= 8); assert(op <= oend); } if (oend <= oend_w) { /* No risk of overwrite. */ ZSTD_wildcopy(op, ip, length, ovtype); return; } if (op <= oend_w) { /* Wildcopy until we get close to the end. */ assert(oend > oend_w); ZSTD_wildcopy(op, ip, oend_w - op, ovtype); ip += oend_w - op; op = oend_w; } /* Handle the leftovers. */ while (op < oend) *op++ = *ip++; } /* ZSTD_execSequenceEnd(): * This version handles cases that are near the end of the output buffer. It requires * more careful checks to make sure there is no overflow. By separating out these hard * and unlikely cases, we can speed up the common cases. * * NOTE: This function needs to be fast for a single long sequence, but doesn't need * to be optimized for many small sequences, since those fall into ZSTD_execSequence(). */ FORCE_NOINLINE size_t ZSTD_execSequenceEnd(BYTE* op, BYTE* const oend, seq_t sequence, const BYTE** litPtr, const BYTE* const litLimit, const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd) { BYTE* const oLitEnd = op + sequence.litLength; size_t const sequenceLength = sequence.litLength + sequence.matchLength; const BYTE* const iLitEnd = *litPtr + sequence.litLength; const BYTE* match = oLitEnd - sequence.offset; BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH; /* bounds checks : careful of address space overflow in 32-bit mode */ RETURN_ERROR_IF(sequenceLength > (size_t)(oend - op), dstSize_tooSmall, "last match must fit within dstBuffer"); RETURN_ERROR_IF(sequence.litLength > (size_t)(litLimit - *litPtr), corruption_detected, "try to read beyond literal buffer"); assert(op < op + sequenceLength); assert(oLitEnd < op + sequenceLength); /* copy literals */ ZSTD_safecopy(op, oend_w, *litPtr, sequence.litLength, ZSTD_no_overlap); op = oLitEnd; *litPtr = iLitEnd; /* copy Match */ if (sequence.offset > (size_t)(oLitEnd - prefixStart)) { /* offset beyond prefix */ RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - virtualStart), corruption_detected, ""); match = dictEnd - (prefixStart-match); if (match + sequence.matchLength <= dictEnd) { memmove(oLitEnd, match, sequence.matchLength); return sequenceLength; } /* span extDict & currentPrefixSegment */ { size_t const length1 = dictEnd - match; memmove(oLitEnd, match, length1); op = oLitEnd + length1; sequence.matchLength -= length1; match = prefixStart; } } ZSTD_safecopy(op, oend_w, match, sequence.matchLength, ZSTD_overlap_src_before_dst); return sequenceLength; } HINT_INLINE size_t ZSTD_execSequence(BYTE* op, BYTE* const oend, seq_t sequence, const BYTE** litPtr, const BYTE* const litLimit, const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd) { BYTE* const oLitEnd = op + sequence.litLength; size_t const sequenceLength = sequence.litLength + sequence.matchLength; BYTE* const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH; /* risk : address space underflow on oend=NULL */ const BYTE* const iLitEnd = *litPtr + sequence.litLength; const BYTE* match = oLitEnd - sequence.offset; assert(op != NULL /* Precondition */); assert(oend_w < oend /* No underflow */); /* Handle edge cases in a slow path: * - Read beyond end of literals * - Match end is within WILDCOPY_OVERLIMIT of oend * - 32-bit mode and the match length overflows */ if (UNLIKELY( iLitEnd > litLimit || oMatchEnd > oend_w || (MEM_32bits() && (size_t)(oend - op) < sequenceLength + WILDCOPY_OVERLENGTH))) return ZSTD_execSequenceEnd(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd); /* Assumptions (everything else goes into ZSTD_execSequenceEnd()) */ assert(op <= oLitEnd /* No overflow */); assert(oLitEnd < oMatchEnd /* Non-zero match & no overflow */); assert(oMatchEnd <= oend /* No underflow */); assert(iLitEnd <= litLimit /* Literal length is in bounds */); assert(oLitEnd <= oend_w /* Can wildcopy literals */); assert(oMatchEnd <= oend_w /* Can wildcopy matches */); /* Copy Literals: * Split out litLength <= 16 since it is nearly always true. +1.6% on gcc-9. * We likely don't need the full 32-byte wildcopy. */ assert(WILDCOPY_OVERLENGTH >= 16); ZSTD_copy16(op, (*litPtr)); if (UNLIKELY(sequence.litLength > 16)) { ZSTD_wildcopy(op+16, (*litPtr)+16, sequence.litLength-16, ZSTD_no_overlap); } op = oLitEnd; *litPtr = iLitEnd; /* update for next sequence */ /* Copy Match */ if (sequence.offset > (size_t)(oLitEnd - prefixStart)) { /* offset beyond prefix -> go into extDict */ RETURN_ERROR_IF(UNLIKELY(sequence.offset > (size_t)(oLitEnd - virtualStart)), corruption_detected, ""); match = dictEnd + (match - prefixStart); if (match + sequence.matchLength <= dictEnd) { memmove(oLitEnd, match, sequence.matchLength); return sequenceLength; } /* span extDict & currentPrefixSegment */ { size_t const length1 = dictEnd - match; memmove(oLitEnd, match, length1); op = oLitEnd + length1; sequence.matchLength -= length1; match = prefixStart; } } /* Match within prefix of 1 or more bytes */ assert(op <= oMatchEnd); assert(oMatchEnd <= oend_w); assert(match >= prefixStart); assert(sequence.matchLength >= 1); /* Nearly all offsets are >= WILDCOPY_VECLEN bytes, which means we can use wildcopy * without overlap checking. */ if (LIKELY(sequence.offset >= WILDCOPY_VECLEN)) { /* We bet on a full wildcopy for matches, since we expect matches to be * longer than literals (in general). In silesia, ~10% of matches are longer * than 16 bytes. */ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength, ZSTD_no_overlap); return sequenceLength; } assert(sequence.offset < WILDCOPY_VECLEN); /* Copy 8 bytes and spread the offset to be >= 8. */ ZSTD_overlapCopy8(&op, &match, sequence.offset); /* If the match length is > 8 bytes, then continue with the wildcopy. */ if (sequence.matchLength > 8) { assert(op < oMatchEnd); ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8, ZSTD_overlap_src_before_dst); } return sequenceLength; } static void ZSTD_initFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, const ZSTD_seqSymbol* dt) { const void* ptr = dt; const ZSTD_seqSymbol_header* const DTableH = (const ZSTD_seqSymbol_header*)ptr; DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog); DEBUGLOG(6, "ZSTD_initFseState : val=%u using %u bits", (U32)DStatePtr->state, DTableH->tableLog); BIT_reloadDStream(bitD); DStatePtr->table = dt + 1; } FORCE_INLINE_TEMPLATE void ZSTD_updateFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD) { ZSTD_seqSymbol const DInfo = DStatePtr->table[DStatePtr->state]; U32 const nbBits = DInfo.nbBits; size_t const lowBits = BIT_readBits(bitD, nbBits); DStatePtr->state = DInfo.nextState + lowBits; } FORCE_INLINE_TEMPLATE void ZSTD_updateFseStateWithDInfo(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, ZSTD_seqSymbol const DInfo) { U32 const nbBits = DInfo.nbBits; size_t const lowBits = BIT_readBits(bitD, nbBits); DStatePtr->state = DInfo.nextState + lowBits; } /* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum * offset bits. But we can only read at most (STREAM_ACCUMULATOR_MIN_32 - 1) * bits before reloading. This value is the maximum number of bytes we read * after reloading when we are decoding long offsets. */ #define LONG_OFFSETS_MAX_EXTRA_BITS_32 \ (ZSTD_WINDOWLOG_MAX_32 > STREAM_ACCUMULATOR_MIN_32 \ ? ZSTD_WINDOWLOG_MAX_32 - STREAM_ACCUMULATOR_MIN_32 \ : 0) typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset_e; typedef enum { ZSTD_p_noPrefetch=0, ZSTD_p_prefetch=1 } ZSTD_prefetch_e; FORCE_INLINE_TEMPLATE seq_t ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets, const ZSTD_prefetch_e prefetch) { seq_t seq; ZSTD_seqSymbol const llDInfo = seqState->stateLL.table[seqState->stateLL.state]; ZSTD_seqSymbol const mlDInfo = seqState->stateML.table[seqState->stateML.state]; ZSTD_seqSymbol const ofDInfo = seqState->stateOffb.table[seqState->stateOffb.state]; U32 const llBase = llDInfo.baseValue; U32 const mlBase = mlDInfo.baseValue; U32 const ofBase = ofDInfo.baseValue; BYTE const llBits = llDInfo.nbAdditionalBits; BYTE const mlBits = mlDInfo.nbAdditionalBits; BYTE const ofBits = ofDInfo.nbAdditionalBits; BYTE const totalBits = llBits+mlBits+ofBits; /* sequence */ { size_t offset; if (ofBits > 1) { ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); assert(ofBits <= MaxOff); if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) { U32 const extraBits = ofBits - MIN(ofBits, 32 - seqState->DStream.bitsConsumed); offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); BIT_reloadDStream(&seqState->DStream); if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); assert(extraBits <= LONG_OFFSETS_MAX_EXTRA_BITS_32); /* to avoid another reload */ } else { offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); } seqState->prevOffset[2] = seqState->prevOffset[1]; seqState->prevOffset[1] = seqState->prevOffset[0]; seqState->prevOffset[0] = offset; } else { U32 const ll0 = (llBase == 0); if (LIKELY((ofBits == 0))) { if (LIKELY(!ll0)) offset = seqState->prevOffset[0]; else { offset = seqState->prevOffset[1]; seqState->prevOffset[1] = seqState->prevOffset[0]; seqState->prevOffset[0] = offset; } } else { offset = ofBase + ll0 + BIT_readBitsFast(&seqState->DStream, 1); { size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; seqState->prevOffset[1] = seqState->prevOffset[0]; seqState->prevOffset[0] = offset = temp; } } } seq.offset = offset; } seq.matchLength = mlBase; if (mlBits > 0) seq.matchLength += BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/); if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) BIT_reloadDStream(&seqState->DStream); if (MEM_64bits() && UNLIKELY(totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) BIT_reloadDStream(&seqState->DStream); /* Ensure there are enough bits to read the rest of data in 64-bit mode. */ ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); seq.litLength = llBase; if (llBits > 0) seq.litLength += BIT_readBitsFast(&seqState->DStream, llBits/*>0*/); if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u", (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset); if (prefetch == ZSTD_p_prefetch) { size_t const pos = seqState->pos + seq.litLength; const BYTE* const matchBase = (seq.offset > pos) ? seqState->dictEnd : seqState->prefixStart; seq.match = matchBase + pos - seq.offset; /* note : this operation can overflow when seq.offset is really too large, which can only happen when input is corrupted. * No consequence though : no memory access will occur, offset is only used for prefetching */ seqState->pos = pos + seq.matchLength; } /* ANS state update * gcc-9.0.0 does 2.5% worse with ZSTD_updateFseStateWithDInfo(). * clang-9.2.0 does 7% worse with ZSTD_updateFseState(). * Naturally it seems like ZSTD_updateFseStateWithDInfo() should be the * better option, so it is the default for other compilers. But, if you * measure that it is worse, please put up a pull request. */ { #if defined(__GNUC__) && !defined(__clang__) const int kUseUpdateFseState = 1; #else const int kUseUpdateFseState = 0; #endif if (kUseUpdateFseState) { ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ } else { ZSTD_updateFseStateWithDInfo(&seqState->stateLL, &seqState->DStream, llDInfo); /* <= 9 bits */ ZSTD_updateFseStateWithDInfo(&seqState->stateML, &seqState->DStream, mlDInfo); /* <= 9 bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ ZSTD_updateFseStateWithDInfo(&seqState->stateOffb, &seqState->DStream, ofDInfo); /* <= 8 bits */ } } return seq; } #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION static int ZSTD_dictionaryIsActive(ZSTD_DCtx const* dctx, BYTE const* prefixStart, BYTE const* oLitEnd) { size_t const windowSize = dctx->fParams.windowSize; /* No dictionary used. */ if (dctx->dictContentEndForFuzzing == NULL) return 0; /* Dictionary is our prefix. */ if (prefixStart == dctx->dictContentBeginForFuzzing) return 1; /* Dictionary is not our ext-dict. */ if (dctx->dictEnd != dctx->dictContentEndForFuzzing) return 0; /* Dictionary is not within our window size. */ if ((size_t)(oLitEnd - prefixStart) >= windowSize) return 0; /* Dictionary is active. */ return 1; } MEM_STATIC void ZSTD_assertValidSequence( ZSTD_DCtx const* dctx, BYTE const* op, BYTE const* oend, seq_t const seq, BYTE const* prefixStart, BYTE const* virtualStart) { size_t const windowSize = dctx->fParams.windowSize; size_t const sequenceSize = seq.litLength + seq.matchLength; BYTE const* const oLitEnd = op + seq.litLength; DEBUGLOG(6, "Checking sequence: litL=%u matchL=%u offset=%u", (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset); assert(op <= oend); assert((size_t)(oend - op) >= sequenceSize); assert(sequenceSize <= ZSTD_BLOCKSIZE_MAX); if (ZSTD_dictionaryIsActive(dctx, prefixStart, oLitEnd)) { size_t const dictSize = (size_t)((char const*)dctx->dictContentEndForFuzzing - (char const*)dctx->dictContentBeginForFuzzing); /* Offset must be within the dictionary. */ assert(seq.offset <= (size_t)(oLitEnd - virtualStart)); assert(seq.offset <= windowSize + dictSize); } else { /* Offset must be within our window. */ assert(seq.offset <= windowSize); } } #endif #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG FORCE_INLINE_TEMPLATE size_t DONT_VECTORIZE ZSTD_decompressSequences_body( ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset, const int frame) { const BYTE* ip = (const BYTE*)seqStart; const BYTE* const iend = ip + seqSize; BYTE* const ostart = (BYTE* const)dst; BYTE* const oend = ostart + maxDstSize; BYTE* op = ostart; const BYTE* litPtr = dctx->litPtr; const BYTE* const litEnd = litPtr + dctx->litSize; const BYTE* const prefixStart = (const BYTE*) (dctx->prefixStart); const BYTE* const vBase = (const BYTE*) (dctx->virtualStart); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); DEBUGLOG(5, "ZSTD_decompressSequences_body"); (void)frame; /* Regen sequences */ if (nbSeq) { seqState_t seqState; size_t error = 0; dctx->fseEntropy = 1; { U32 i; for (i=0; ientropy.rep[i]; } RETURN_ERROR_IF( ERR_isError(BIT_initDStream(&seqState.DStream, ip, iend-ip)), corruption_detected, ""); ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); assert(dst != NULL); ZSTD_STATIC_ASSERT( BIT_DStream_unfinished < BIT_DStream_completed && BIT_DStream_endOfBuffer < BIT_DStream_completed && BIT_DStream_completed < BIT_DStream_overflow); #if defined(__GNUC__) && defined(__x86_64__) /* Align the decompression loop to 32 + 16 bytes. * * zstd compiled with gcc-9 on an Intel i9-9900k shows 10% decompression * speed swings based on the alignment of the decompression loop. This * performance swing is caused by parts of the decompression loop falling * out of the DSB. The entire decompression loop should fit in the DSB, * when it can't we get much worse performance. You can measure if you've * hit the good case or the bad case with this perf command for some * compressed file test.zst: * * perf stat -e cycles -e instructions -e idq.all_dsb_cycles_any_uops \ * -e idq.all_mite_cycles_any_uops -- ./zstd -tq test.zst * * If you see most cycles served out of the MITE you've hit the bad case. * If you see most cycles served out of the DSB you've hit the good case. * If it is pretty even then you may be in an okay case. * * I've been able to reproduce this issue on the following CPUs: * - Kabylake: Macbook Pro (15-inch, 2019) 2.4 GHz Intel Core i9 * Use Instruments->Counters to get DSB/MITE cycles. * I never got performance swings, but I was able to * go from the good case of mostly DSB to half of the * cycles served from MITE. * - Coffeelake: Intel i9-9900k * * I haven't been able to reproduce the instability or DSB misses on any * of the following CPUS: * - Haswell * - Broadwell: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GH * - Skylake * * If you are seeing performance stability this script can help test. * It tests on 4 commits in zstd where I saw performance change. * * https://gist.github.com/terrelln/9889fc06a423fd5ca6e99351564473f4 */ __asm__(".p2align 5"); __asm__("nop"); __asm__(".p2align 4"); #endif for ( ; ; ) { seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset, ZSTD_p_noPrefetch); size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, prefixStart, vBase, dictEnd); #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE) assert(!ZSTD_isError(oneSeqSize)); if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequence, prefixStart, vBase); #endif DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize); BIT_reloadDStream(&(seqState.DStream)); /* gcc and clang both don't like early returns in this loop. * gcc doesn't like early breaks either. * Instead save an error and report it at the end. * When there is an error, don't increment op, so we don't * overwrite. */ if (UNLIKELY(ZSTD_isError(oneSeqSize))) error = oneSeqSize; else op += oneSeqSize; if (UNLIKELY(!--nbSeq)) break; } /* check if reached exact end */ DEBUGLOG(5, "ZSTD_decompressSequences_body: after decode loop, remaining nbSeq : %i", nbSeq); if (ZSTD_isError(error)) return error; RETURN_ERROR_IF(nbSeq, corruption_detected, ""); RETURN_ERROR_IF(BIT_reloadDStream(&seqState.DStream) < BIT_DStream_completed, corruption_detected, ""); /* save reps for next block */ { U32 i; for (i=0; ientropy.rep[i] = (U32)(seqState.prevOffset[i]); } } /* last literal segment */ { size_t const lastLLSize = litEnd - litPtr; RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, ""); if (op != NULL) { memcpy(op, litPtr, lastLLSize); op += lastLLSize; } } return op-ostart; } static size_t ZSTD_decompressSequences_default(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset, const int frame) { return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */ #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT FORCE_INLINE_TEMPLATE size_t ZSTD_decompressSequencesLong_body( ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset, const int frame) { const BYTE* ip = (const BYTE*)seqStart; const BYTE* const iend = ip + seqSize; BYTE* const ostart = (BYTE* const)dst; BYTE* const oend = ostart + maxDstSize; BYTE* op = ostart; const BYTE* litPtr = dctx->litPtr; const BYTE* const litEnd = litPtr + dctx->litSize; const BYTE* const prefixStart = (const BYTE*) (dctx->prefixStart); const BYTE* const dictStart = (const BYTE*) (dctx->virtualStart); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); (void)frame; /* Regen sequences */ if (nbSeq) { #define STORED_SEQS 4 #define STORED_SEQS_MASK (STORED_SEQS-1) #define ADVANCED_SEQS 4 seq_t sequences[STORED_SEQS]; int const seqAdvance = MIN(nbSeq, ADVANCED_SEQS); seqState_t seqState; int seqNb; dctx->fseEntropy = 1; { int i; for (i=0; ientropy.rep[i]; } seqState.prefixStart = prefixStart; seqState.pos = (size_t)(op-prefixStart); seqState.dictEnd = dictEnd; assert(dst != NULL); assert(iend >= ip); RETURN_ERROR_IF( ERR_isError(BIT_initDStream(&seqState.DStream, ip, iend-ip)), corruption_detected, ""); ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); /* prepare in advance */ for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && (seqNbentropy.rep[i] = (U32)(seqState.prevOffset[i]); } } /* last literal segment */ { size_t const lastLLSize = litEnd - litPtr; RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, ""); if (op != NULL) { memcpy(op, litPtr, lastLLSize); op += lastLLSize; } } return op-ostart; } static size_t ZSTD_decompressSequencesLong_default(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset, const int frame) { return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT */ #if DYNAMIC_BMI2 #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG static TARGET_ATTRIBUTE("bmi2") size_t DONT_VECTORIZE ZSTD_decompressSequences_bmi2(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset, const int frame) { return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */ #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT static TARGET_ATTRIBUTE("bmi2") size_t ZSTD_decompressSequencesLong_bmi2(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset, const int frame) { return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT */ #endif /* DYNAMIC_BMI2 */ typedef size_t (*ZSTD_decompressSequences_t)( ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset, const int frame); #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG static size_t ZSTD_decompressSequences(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset, const int frame) { DEBUGLOG(5, "ZSTD_decompressSequences"); #if DYNAMIC_BMI2 if (dctx->bmi2) { return ZSTD_decompressSequences_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */ #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT /* ZSTD_decompressSequencesLong() : * decompression function triggered when a minimum share of offsets is considered "long", * aka out of cache. * note : "long" definition seems overloaded here, sometimes meaning "wider than bitstream register", and sometimes meaning "farther than memory cache distance". * This function will try to mitigate main memory latency through the use of prefetching */ static size_t ZSTD_decompressSequencesLong(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset, const int frame) { DEBUGLOG(5, "ZSTD_decompressSequencesLong"); #if DYNAMIC_BMI2 if (dctx->bmi2) { return ZSTD_decompressSequencesLong_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif return ZSTD_decompressSequencesLong_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT */ #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) /* ZSTD_getLongOffsetsShare() : * condition : offTable must be valid * @return : "share" of long offsets (arbitrarily defined as > (1<<23)) * compared to maximum possible of (1< 22) total += 1; } assert(tableLog <= OffFSELog); total <<= (OffFSELog - tableLog); /* scale to OffFSELog */ return total; } #endif size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const int frame) { /* blockType == blockCompressed */ const BYTE* ip = (const BYTE*)src; /* isLongOffset must be true if there are long offsets. * Offsets are long if they are larger than 2^STREAM_ACCUMULATOR_MIN. * We don't expect that to be the case in 64-bit mode. * In block mode, window size is not known, so we have to be conservative. * (note: but it could be evaluated from current-lowLimit) */ ZSTD_longOffset_e const isLongOffset = (ZSTD_longOffset_e)(MEM_32bits() && (!frame || (dctx->fParams.windowSize > (1ULL << STREAM_ACCUMULATOR_MIN)))); DEBUGLOG(5, "ZSTD_decompressBlock_internal (size : %u)", (U32)srcSize); RETURN_ERROR_IF(srcSize >= ZSTD_BLOCKSIZE_MAX, srcSize_wrong, ""); /* Decode literals section */ { size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize); DEBUGLOG(5, "ZSTD_decodeLiteralsBlock : %u", (U32)litCSize); if (ZSTD_isError(litCSize)) return litCSize; ip += litCSize; srcSize -= litCSize; } /* Build Decoding Tables */ { /* These macros control at build-time which decompressor implementation * we use. If neither is defined, we do some inspection and dispatch at * runtime. */ #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) int usePrefetchDecoder = dctx->ddictIsCold; #endif int nbSeq; size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, srcSize); if (ZSTD_isError(seqHSize)) return seqHSize; ip += seqHSize; srcSize -= seqHSize; RETURN_ERROR_IF(dst == NULL && nbSeq > 0, dstSize_tooSmall, "NULL not handled"); #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) if ( !usePrefetchDecoder && (!frame || (dctx->fParams.windowSize > (1<<24))) && (nbSeq>ADVANCED_SEQS) ) { /* could probably use a larger nbSeq limit */ U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->OFTptr); U32 const minShare = MEM_64bits() ? 7 : 20; /* heuristic values, correspond to 2.73% and 7.81% */ usePrefetchDecoder = (shareLongOffsets >= minShare); } #endif dctx->ddictIsCold = 0; #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) if (usePrefetchDecoder) #endif #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame); #endif #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG /* else */ return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame); #endif } } void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst) { if (dst != dctx->previousDstEnd) { /* not contiguous */ dctx->dictEnd = dctx->previousDstEnd; dctx->virtualStart = (const char*)dst - ((const char*)(dctx->previousDstEnd) - (const char*)(dctx->prefixStart)); dctx->prefixStart = dst; dctx->previousDstEnd = dst; } } size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { size_t dSize; ZSTD_checkContinuity(dctx, dst); dSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 0); dctx->previousDstEnd = (char*)dst + dSize; return dSize; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/decompress/zstd_decompress_block.h0000644000076500000240000000377014601576577026571 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_DEC_BLOCK_H #define ZSTD_DEC_BLOCK_H /*-******************************************************* * Dependencies *********************************************************/ #include /* size_t */ #include "../zstd.h" /* DCtx, and some public functions */ #include "../common/zstd_internal.h" /* blockProperties_t, and some public functions */ #include "zstd_decompress_internal.h" /* ZSTD_seqSymbol */ /* === Prototypes === */ /* note: prototypes already published within `zstd.h` : * ZSTD_decompressBlock() */ /* note: prototypes already published within `zstd_internal.h` : * ZSTD_getcBlockSize() * ZSTD_decodeSeqHeaders() */ /* ZSTD_decompressBlock_internal() : * decompress block, starting at `src`, * into destination buffer `dst`. * @return : decompressed block size, * or an error code (which can be tested using ZSTD_isError()) */ size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const int frame); /* ZSTD_buildFSETable() : * generate FSE decoding table for one symbol (ll, ml or off) * this function must be called with valid parameters only * (dt is large enough, normalizedCounter distribution total is a power of 2, max is within range, etc.) * in which case it cannot fail. * Internal use only. */ void ZSTD_buildFSETable(ZSTD_seqSymbol* dt, const short* normalizedCounter, unsigned maxSymbolValue, const U32* baseValue, const U32* nbAdditionalBits, unsigned tableLog); #endif /* ZSTD_DEC_BLOCK_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/decompress/zstd_decompress_internal.h0000644000076500000240000001644314601576577027314 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* zstd_decompress_internal: * objects and definitions shared within lib/decompress modules */ #ifndef ZSTD_DECOMPRESS_INTERNAL_H #define ZSTD_DECOMPRESS_INTERNAL_H /*-******************************************************* * Dependencies *********************************************************/ #include "../common/mem.h" /* BYTE, U16, U32 */ #include "../common/zstd_internal.h" /* ZSTD_seqSymbol */ /*-******************************************************* * Constants *********************************************************/ static const U32 LL_base[MaxLL+1] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24, 28, 32, 40, 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000 }; static const U32 OF_base[MaxOff+1] = { 0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D, 0xFD, 0x1FD, 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD, 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD, 0x1FFFFFFD, 0x3FFFFFFD, 0x7FFFFFFD }; static const U32 OF_bits[MaxOff+1] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }; static const U32 ML_base[MaxML+1] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, 43, 47, 51, 59, 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803, 0x1003, 0x2003, 0x4003, 0x8003, 0x10003 }; /*-******************************************************* * Decompression types *********************************************************/ typedef struct { U32 fastMode; U32 tableLog; } ZSTD_seqSymbol_header; typedef struct { U16 nextState; BYTE nbAdditionalBits; BYTE nbBits; U32 baseValue; } ZSTD_seqSymbol; #define SEQSYMBOL_TABLE_SIZE(log) (1 + (1 << (log))) typedef struct { ZSTD_seqSymbol LLTable[SEQSYMBOL_TABLE_SIZE(LLFSELog)]; /* Note : Space reserved for FSE Tables */ ZSTD_seqSymbol OFTable[SEQSYMBOL_TABLE_SIZE(OffFSELog)]; /* is also used as temporary workspace while building hufTable during DDict creation */ ZSTD_seqSymbol MLTable[SEQSYMBOL_TABLE_SIZE(MLFSELog)]; /* and therefore must be at least HUF_DECOMPRESS_WORKSPACE_SIZE large */ HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)]; /* can accommodate HUF_decompress4X */ U32 rep[ZSTD_REP_NUM]; } ZSTD_entropyDTables_t; typedef enum { ZSTDds_getFrameHeaderSize, ZSTDds_decodeFrameHeader, ZSTDds_decodeBlockHeader, ZSTDds_decompressBlock, ZSTDds_decompressLastBlock, ZSTDds_checkChecksum, ZSTDds_decodeSkippableHeader, ZSTDds_skipFrame } ZSTD_dStage; typedef enum { zdss_init=0, zdss_loadHeader, zdss_read, zdss_load, zdss_flush } ZSTD_dStreamStage; typedef enum { ZSTD_use_indefinitely = -1, /* Use the dictionary indefinitely */ ZSTD_dont_use = 0, /* Do not use the dictionary (if one exists free it) */ ZSTD_use_once = 1 /* Use the dictionary once and set to ZSTD_dont_use */ } ZSTD_dictUses_e; typedef enum { ZSTD_obm_buffered = 0, /* Buffer the output */ ZSTD_obm_stable = 1 /* ZSTD_outBuffer is stable */ } ZSTD_outBufferMode_e; struct ZSTD_DCtx_s { const ZSTD_seqSymbol* LLTptr; const ZSTD_seqSymbol* MLTptr; const ZSTD_seqSymbol* OFTptr; const HUF_DTable* HUFptr; ZSTD_entropyDTables_t entropy; U32 workspace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; /* space needed when building huffman tables */ const void* previousDstEnd; /* detect continuity */ const void* prefixStart; /* start of current segment */ const void* virtualStart; /* virtual start of previous segment if it was just before current one */ const void* dictEnd; /* end of previous segment */ size_t expected; ZSTD_frameHeader fParams; U64 decodedSize; blockType_e bType; /* used in ZSTD_decompressContinue(), store blockType between block header decoding and block decompression stages */ ZSTD_dStage stage; U32 litEntropy; U32 fseEntropy; XXH64_state_t xxhState; size_t headerSize; ZSTD_format_e format; const BYTE* litPtr; ZSTD_customMem customMem; size_t litSize; size_t rleSize; size_t staticSize; int bmi2; /* == 1 if the CPU supports BMI2 and 0 otherwise. CPU support is determined dynamically once per context lifetime. */ /* dictionary */ ZSTD_DDict* ddictLocal; const ZSTD_DDict* ddict; /* set by ZSTD_initDStream_usingDDict(), or ZSTD_DCtx_refDDict() */ U32 dictID; int ddictIsCold; /* if == 1 : dictionary is "new" for working context, and presumed "cold" (not in cpu cache) */ ZSTD_dictUses_e dictUses; /* streaming */ ZSTD_dStreamStage streamStage; char* inBuff; size_t inBuffSize; size_t inPos; size_t maxWindowSize; char* outBuff; size_t outBuffSize; size_t outStart; size_t outEnd; size_t lhSize; void* legacyContext; U32 previousLegacyVersion; U32 legacyVersion; U32 hostageByte; int noForwardProgress; ZSTD_outBufferMode_e outBufferMode; ZSTD_outBuffer expectedOutBuffer; /* workspace */ BYTE litBuffer[ZSTD_BLOCKSIZE_MAX + WILDCOPY_OVERLENGTH]; BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; size_t oversizedDuration; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION void const* dictContentBeginForFuzzing; void const* dictContentEndForFuzzing; #endif }; /* typedef'd to ZSTD_DCtx within "zstd.h" */ /*-******************************************************* * Shared internal functions *********************************************************/ /*! ZSTD_loadDEntropy() : * dict : must point at beginning of a valid zstd dictionary. * @return : size of dictionary header (size of magic number + dict ID + entropy tables) */ size_t ZSTD_loadDEntropy(ZSTD_entropyDTables_t* entropy, const void* const dict, size_t const dictSize); /*! ZSTD_checkContinuity() : * check if next `dst` follows previous position, where decompression ended. * If yes, do nothing (continue on current segment). * If not, classify previous segment as "external dictionary", and start a new segment. * This function cannot fail. */ void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst); #endif /* ZSTD_DECOMPRESS_INTERNAL_H */ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1849446 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/deprecated/0000755000076500000240000000000014601577064021753 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/deprecated/zbuff.h0000644000076500000240000002635714601576577023265 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* *************************************************************** * NOTES/WARNINGS ******************************************************************/ /* The streaming API defined here is deprecated. * Consider migrating towards ZSTD_compressStream() API in `zstd.h` * See 'lib/README.md'. *****************************************************************/ #if defined (__cplusplus) extern "C" { #endif #ifndef ZSTD_BUFFERED_H_23987 #define ZSTD_BUFFERED_H_23987 /* ************************************* * Dependencies ***************************************/ #include /* size_t */ #include "../zstd.h" /* ZSTD_CStream, ZSTD_DStream, ZSTDLIB_API */ /* *************************************************************** * Compiler specifics *****************************************************************/ /* Deprecation warnings */ /* Should these warnings be a problem, * it is generally possible to disable them, * typically with -Wno-deprecated-declarations for gcc * or _CRT_SECURE_NO_WARNINGS in Visual. * Otherwise, it's also possible to define ZBUFF_DISABLE_DEPRECATE_WARNINGS */ #ifdef ZBUFF_DISABLE_DEPRECATE_WARNINGS # define ZBUFF_DEPRECATED(message) ZSTDLIB_API /* disable deprecation warnings */ #else # if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ # define ZBUFF_DEPRECATED(message) [[deprecated(message)]] ZSTDLIB_API # elif (defined(GNUC) && (GNUC > 4 || (GNUC == 4 && GNUC_MINOR >= 5))) || defined(__clang__) # define ZBUFF_DEPRECATED(message) ZSTDLIB_API __attribute__((deprecated(message))) # elif defined(__GNUC__) && (__GNUC__ >= 3) # define ZBUFF_DEPRECATED(message) ZSTDLIB_API __attribute__((deprecated)) # elif defined(_MSC_VER) # define ZBUFF_DEPRECATED(message) ZSTDLIB_API __declspec(deprecated(message)) # else # pragma message("WARNING: You need to implement ZBUFF_DEPRECATED for this compiler") # define ZBUFF_DEPRECATED(message) ZSTDLIB_API # endif #endif /* ZBUFF_DISABLE_DEPRECATE_WARNINGS */ /* ************************************* * Streaming functions ***************************************/ /* This is the easier "buffered" streaming API, * using an internal buffer to lift all restrictions on user-provided buffers * which can be any size, any place, for both input and output. * ZBUFF and ZSTD are 100% interoperable, * frames created by one can be decoded by the other one */ typedef ZSTD_CStream ZBUFF_CCtx; ZBUFF_DEPRECATED("use ZSTD_createCStream") ZBUFF_CCtx* ZBUFF_createCCtx(void); ZBUFF_DEPRECATED("use ZSTD_freeCStream") size_t ZBUFF_freeCCtx(ZBUFF_CCtx* cctx); ZBUFF_DEPRECATED("use ZSTD_initCStream") size_t ZBUFF_compressInit(ZBUFF_CCtx* cctx, int compressionLevel); ZBUFF_DEPRECATED("use ZSTD_initCStream_usingDict") size_t ZBUFF_compressInitDictionary(ZBUFF_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel); ZBUFF_DEPRECATED("use ZSTD_compressStream") size_t ZBUFF_compressContinue(ZBUFF_CCtx* cctx, void* dst, size_t* dstCapacityPtr, const void* src, size_t* srcSizePtr); ZBUFF_DEPRECATED("use ZSTD_flushStream") size_t ZBUFF_compressFlush(ZBUFF_CCtx* cctx, void* dst, size_t* dstCapacityPtr); ZBUFF_DEPRECATED("use ZSTD_endStream") size_t ZBUFF_compressEnd(ZBUFF_CCtx* cctx, void* dst, size_t* dstCapacityPtr); /*-************************************************* * Streaming compression - howto * * A ZBUFF_CCtx object is required to track streaming operation. * Use ZBUFF_createCCtx() and ZBUFF_freeCCtx() to create/release resources. * ZBUFF_CCtx objects can be reused multiple times. * * Start by initializing ZBUF_CCtx. * Use ZBUFF_compressInit() to start a new compression operation. * Use ZBUFF_compressInitDictionary() for a compression which requires a dictionary. * * Use ZBUFF_compressContinue() repetitively to consume input stream. * *srcSizePtr and *dstCapacityPtr can be any size. * The function will report how many bytes were read or written within *srcSizePtr and *dstCapacityPtr. * Note that it may not consume the entire input, in which case it's up to the caller to present again remaining data. * The content of `dst` will be overwritten (up to *dstCapacityPtr) at each call, so save its content if it matters or change @dst . * @return : a hint to preferred nb of bytes to use as input for next function call (it's just a hint, to improve latency) * or an error code, which can be tested using ZBUFF_isError(). * * At any moment, it's possible to flush whatever data remains within buffer, using ZBUFF_compressFlush(). * The nb of bytes written into `dst` will be reported into *dstCapacityPtr. * Note that the function cannot output more than *dstCapacityPtr, * therefore, some content might still be left into internal buffer if *dstCapacityPtr is too small. * @return : nb of bytes still present into internal buffer (0 if it's empty) * or an error code, which can be tested using ZBUFF_isError(). * * ZBUFF_compressEnd() instructs to finish a frame. * It will perform a flush and write frame epilogue. * The epilogue is required for decoders to consider a frame completed. * Similar to ZBUFF_compressFlush(), it may not be able to output the entire internal buffer content if *dstCapacityPtr is too small. * In which case, call again ZBUFF_compressFlush() to complete the flush. * @return : nb of bytes still present into internal buffer (0 if it's empty) * or an error code, which can be tested using ZBUFF_isError(). * * Hint : _recommended buffer_ sizes (not compulsory) : ZBUFF_recommendedCInSize() / ZBUFF_recommendedCOutSize() * input : ZBUFF_recommendedCInSize==128 KB block size is the internal unit, use this value to reduce intermediate stages (better latency) * output : ZBUFF_recommendedCOutSize==ZSTD_compressBound(128 KB) + 3 + 3 : ensures it's always possible to write/flush/end a full block. Skip some buffering. * By using both, it ensures that input will be entirely consumed, and output will always contain the result, reducing intermediate buffering. * **************************************************/ typedef ZSTD_DStream ZBUFF_DCtx; ZBUFF_DEPRECATED("use ZSTD_createDStream") ZBUFF_DCtx* ZBUFF_createDCtx(void); ZBUFF_DEPRECATED("use ZSTD_freeDStream") size_t ZBUFF_freeDCtx(ZBUFF_DCtx* dctx); ZBUFF_DEPRECATED("use ZSTD_initDStream") size_t ZBUFF_decompressInit(ZBUFF_DCtx* dctx); ZBUFF_DEPRECATED("use ZSTD_initDStream_usingDict") size_t ZBUFF_decompressInitDictionary(ZBUFF_DCtx* dctx, const void* dict, size_t dictSize); ZBUFF_DEPRECATED("use ZSTD_decompressStream") size_t ZBUFF_decompressContinue(ZBUFF_DCtx* dctx, void* dst, size_t* dstCapacityPtr, const void* src, size_t* srcSizePtr); /*-*************************************************************************** * Streaming decompression howto * * A ZBUFF_DCtx object is required to track streaming operations. * Use ZBUFF_createDCtx() and ZBUFF_freeDCtx() to create/release resources. * Use ZBUFF_decompressInit() to start a new decompression operation, * or ZBUFF_decompressInitDictionary() if decompression requires a dictionary. * Note that ZBUFF_DCtx objects can be re-init multiple times. * * Use ZBUFF_decompressContinue() repetitively to consume your input. * *srcSizePtr and *dstCapacityPtr can be any size. * The function will report how many bytes were read or written by modifying *srcSizePtr and *dstCapacityPtr. * Note that it may not consume the entire input, in which case it's up to the caller to present remaining input again. * The content of `dst` will be overwritten (up to *dstCapacityPtr) at each function call, so save its content if it matters, or change `dst`. * @return : 0 when a frame is completely decoded and fully flushed, * 1 when there is still some data left within internal buffer to flush, * >1 when more data is expected, with value being a suggested next input size (it's just a hint, which helps latency), * or an error code, which can be tested using ZBUFF_isError(). * * Hint : recommended buffer sizes (not compulsory) : ZBUFF_recommendedDInSize() and ZBUFF_recommendedDOutSize() * output : ZBUFF_recommendedDOutSize== 128 KB block size is the internal unit, it ensures it's always possible to write a full block when decoded. * input : ZBUFF_recommendedDInSize == 128KB + 3; * just follow indications from ZBUFF_decompressContinue() to minimize latency. It should always be <= 128 KB + 3 . * *******************************************************************************/ /* ************************************* * Tool functions ***************************************/ ZBUFF_DEPRECATED("use ZSTD_isError") unsigned ZBUFF_isError(size_t errorCode); ZBUFF_DEPRECATED("use ZSTD_getErrorName") const char* ZBUFF_getErrorName(size_t errorCode); /** Functions below provide recommended buffer sizes for Compression or Decompression operations. * These sizes are just hints, they tend to offer better latency */ ZBUFF_DEPRECATED("use ZSTD_CStreamInSize") size_t ZBUFF_recommendedCInSize(void); ZBUFF_DEPRECATED("use ZSTD_CStreamOutSize") size_t ZBUFF_recommendedCOutSize(void); ZBUFF_DEPRECATED("use ZSTD_DStreamInSize") size_t ZBUFF_recommendedDInSize(void); ZBUFF_DEPRECATED("use ZSTD_DStreamOutSize") size_t ZBUFF_recommendedDOutSize(void); #endif /* ZSTD_BUFFERED_H_23987 */ #ifdef ZBUFF_STATIC_LINKING_ONLY #ifndef ZBUFF_STATIC_H_30298098432 #define ZBUFF_STATIC_H_30298098432 /* ==================================================================================== * The definitions in this section are considered experimental. * They should never be used in association with a dynamic library, as they may change in the future. * They are provided for advanced usages. * Use them only in association with static linking. * ==================================================================================== */ /*--- Dependency ---*/ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters, ZSTD_customMem */ #include "../zstd.h" /*--- Custom memory allocator ---*/ /*! ZBUFF_createCCtx_advanced() : * Create a ZBUFF compression context using external alloc and free functions */ ZBUFF_DEPRECATED("use ZSTD_createCStream_advanced") ZBUFF_CCtx* ZBUFF_createCCtx_advanced(ZSTD_customMem customMem); /*! ZBUFF_createDCtx_advanced() : * Create a ZBUFF decompression context using external alloc and free functions */ ZBUFF_DEPRECATED("use ZSTD_createDStream_advanced") ZBUFF_DCtx* ZBUFF_createDCtx_advanced(ZSTD_customMem customMem); /*--- Advanced Streaming Initialization ---*/ ZBUFF_DEPRECATED("use ZSTD_initDStream_usingDict") size_t ZBUFF_compressInit_advanced(ZBUFF_CCtx* zbc, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); #endif /* ZBUFF_STATIC_H_30298098432 */ #endif /* ZBUFF_STATIC_LINKING_ONLY */ #if defined (__cplusplus) } #endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/deprecated/zbuff_common.c0000644000076500000240000000176214601576577024621 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /*-************************************* * Dependencies ***************************************/ #include "../common/error_private.h" #include "zbuff.h" /*-**************************************** * ZBUFF Error Management (deprecated) ******************************************/ /*! ZBUFF_isError() : * tells if a return value is an error code */ unsigned ZBUFF_isError(size_t errorCode) { return ERR_isError(errorCode); } /*! ZBUFF_getErrorName() : * provides error code string from function result (useful for debugging) */ const char* ZBUFF_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/deprecated/zbuff_compress.c0000644000076500000240000001232314601576577025157 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* ************************************* * Dependencies ***************************************/ #define ZBUFF_STATIC_LINKING_ONLY #include "zbuff.h" /*-*********************************************************** * Streaming compression * * A ZBUFF_CCtx object is required to track streaming operation. * Use ZBUFF_createCCtx() and ZBUFF_freeCCtx() to create/release resources. * Use ZBUFF_compressInit() to start a new compression operation. * ZBUFF_CCtx objects can be reused multiple times. * * Use ZBUFF_compressContinue() repetitively to consume your input. * *srcSizePtr and *dstCapacityPtr can be any size. * The function will report how many bytes were read or written by modifying *srcSizePtr and *dstCapacityPtr. * Note that it may not consume the entire input, in which case it's up to the caller to call again the function with remaining input. * The content of dst will be overwritten (up to *dstCapacityPtr) at each function call, so save its content if it matters or change dst . * @return : a hint to preferred nb of bytes to use as input for next function call (it's only a hint, to improve latency) * or an error code, which can be tested using ZBUFF_isError(). * * ZBUFF_compressFlush() can be used to instruct ZBUFF to compress and output whatever remains within its buffer. * Note that it will not output more than *dstCapacityPtr. * Therefore, some content might still be left into its internal buffer if dst buffer is too small. * @return : nb of bytes still present into internal buffer (0 if it's empty) * or an error code, which can be tested using ZBUFF_isError(). * * ZBUFF_compressEnd() instructs to finish a frame. * It will perform a flush and write frame epilogue. * Similar to ZBUFF_compressFlush(), it may not be able to output the entire internal buffer content if *dstCapacityPtr is too small. * @return : nb of bytes still present into internal buffer (0 if it's empty) * or an error code, which can be tested using ZBUFF_isError(). * * Hint : recommended buffer sizes (not compulsory) * input : ZSTD_BLOCKSIZE_MAX (128 KB), internal unit size, it improves latency to use this value. * output : ZSTD_compressBound(ZSTD_BLOCKSIZE_MAX) + ZSTD_blockHeaderSize + ZBUFF_endFrameSize : ensures it's always possible to write/flush/end a full block at best speed. * ***********************************************************/ ZBUFF_CCtx* ZBUFF_createCCtx(void) { return ZSTD_createCStream(); } ZBUFF_CCtx* ZBUFF_createCCtx_advanced(ZSTD_customMem customMem) { return ZSTD_createCStream_advanced(customMem); } size_t ZBUFF_freeCCtx(ZBUFF_CCtx* zbc) { return ZSTD_freeCStream(zbc); } /* ====== Initialization ====== */ size_t ZBUFF_compressInit_advanced(ZBUFF_CCtx* zbc, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize) { if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; /* preserve "0 == unknown" behavior */ return ZSTD_initCStream_advanced(zbc, dict, dictSize, params, pledgedSrcSize); } size_t ZBUFF_compressInitDictionary(ZBUFF_CCtx* zbc, const void* dict, size_t dictSize, int compressionLevel) { return ZSTD_initCStream_usingDict(zbc, dict, dictSize, compressionLevel); } size_t ZBUFF_compressInit(ZBUFF_CCtx* zbc, int compressionLevel) { return ZSTD_initCStream(zbc, compressionLevel); } /* ====== Compression ====== */ size_t ZBUFF_compressContinue(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr, const void* src, size_t* srcSizePtr) { size_t result; ZSTD_outBuffer outBuff; ZSTD_inBuffer inBuff; outBuff.dst = dst; outBuff.pos = 0; outBuff.size = *dstCapacityPtr; inBuff.src = src; inBuff.pos = 0; inBuff.size = *srcSizePtr; result = ZSTD_compressStream(zbc, &outBuff, &inBuff); *dstCapacityPtr = outBuff.pos; *srcSizePtr = inBuff.pos; return result; } /* ====== Finalize ====== */ size_t ZBUFF_compressFlush(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr) { size_t result; ZSTD_outBuffer outBuff; outBuff.dst = dst; outBuff.pos = 0; outBuff.size = *dstCapacityPtr; result = ZSTD_flushStream(zbc, &outBuff); *dstCapacityPtr = outBuff.pos; return result; } size_t ZBUFF_compressEnd(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr) { size_t result; ZSTD_outBuffer outBuff; outBuff.dst = dst; outBuff.pos = 0; outBuff.size = *dstCapacityPtr; result = ZSTD_endStream(zbc, &outBuff); *dstCapacityPtr = outBuff.pos; return result; } /* ************************************* * Tool functions ***************************************/ size_t ZBUFF_recommendedCInSize(void) { return ZSTD_CStreamInSize(); } size_t ZBUFF_recommendedCOutSize(void) { return ZSTD_CStreamOutSize(); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/deprecated/zbuff_decompress.c0000644000076500000240000000357414601576577025500 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* ************************************* * Dependencies ***************************************/ #define ZBUFF_STATIC_LINKING_ONLY #include "zbuff.h" ZBUFF_DCtx* ZBUFF_createDCtx(void) { return ZSTD_createDStream(); } ZBUFF_DCtx* ZBUFF_createDCtx_advanced(ZSTD_customMem customMem) { return ZSTD_createDStream_advanced(customMem); } size_t ZBUFF_freeDCtx(ZBUFF_DCtx* zbd) { return ZSTD_freeDStream(zbd); } /* *** Initialization *** */ size_t ZBUFF_decompressInitDictionary(ZBUFF_DCtx* zbd, const void* dict, size_t dictSize) { return ZSTD_initDStream_usingDict(zbd, dict, dictSize); } size_t ZBUFF_decompressInit(ZBUFF_DCtx* zbd) { return ZSTD_initDStream(zbd); } /* *** Decompression *** */ size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, void* dst, size_t* dstCapacityPtr, const void* src, size_t* srcSizePtr) { ZSTD_outBuffer outBuff; ZSTD_inBuffer inBuff; size_t result; outBuff.dst = dst; outBuff.pos = 0; outBuff.size = *dstCapacityPtr; inBuff.src = src; inBuff.pos = 0; inBuff.size = *srcSizePtr; result = ZSTD_decompressStream(zbd, &outBuff, &inBuff); *dstCapacityPtr = outBuff.pos; *srcSizePtr = inBuff.pos; return result; } /* ************************************* * Tool functions ***************************************/ size_t ZBUFF_recommendedDInSize(void) { return ZSTD_DStreamInSize(); } size_t ZBUFF_recommendedDOutSize(void) { return ZSTD_DStreamOutSize(); } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1861656 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/dictBuilder/0000755000076500000240000000000014601577064022105 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/dictBuilder/cover.c0000644000076500000240000012232014601576577023377 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /* ***************************************************************************** * Constructs a dictionary using a heuristic based on the following paper: * * Liao, Petri, Moffat, Wirth * Effective Construction of Relative Lempel-Ziv Dictionaries * Published in WWW 2016. * * Adapted from code originally written by @ot (Giuseppe Ottaviano). ******************************************************************************/ /*-************************************* * Dependencies ***************************************/ #include /* fprintf */ #include /* malloc, free, qsort */ #include /* memset */ #include /* clock */ #include "../common/mem.h" /* read */ #include "../common/pool.h" #include "../common/threading.h" #include "cover.h" #include "../common/zstd_internal.h" /* includes zstd.h */ #ifndef ZDICT_STATIC_LINKING_ONLY #define ZDICT_STATIC_LINKING_ONLY #endif #include "zdict.h" /*-************************************* * Constants ***************************************/ #define COVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB)) #define DEFAULT_SPLITPOINT 1.0 /*-************************************* * Console display ***************************************/ static int g_displayLevel = 2; #define DISPLAY(...) \ { \ fprintf(stderr, __VA_ARGS__); \ fflush(stderr); \ } #define LOCALDISPLAYLEVEL(displayLevel, l, ...) \ if (displayLevel >= l) { \ DISPLAY(__VA_ARGS__); \ } /* 0 : no display; 1: errors; 2: default; 3: details; 4: debug */ #define DISPLAYLEVEL(l, ...) LOCALDISPLAYLEVEL(g_displayLevel, l, __VA_ARGS__) #define LOCALDISPLAYUPDATE(displayLevel, l, ...) \ if (displayLevel >= l) { \ if ((clock() - g_time > refreshRate) || (displayLevel >= 4)) { \ g_time = clock(); \ DISPLAY(__VA_ARGS__); \ } \ } #define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__) static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; static clock_t g_time = 0; /*-************************************* * Hash table *************************************** * A small specialized hash map for storing activeDmers. * The map does not resize, so if it becomes full it will loop forever. * Thus, the map must be large enough to store every value. * The map implements linear probing and keeps its load less than 0.5. */ #define MAP_EMPTY_VALUE ((U32)-1) typedef struct COVER_map_pair_t_s { U32 key; U32 value; } COVER_map_pair_t; typedef struct COVER_map_s { COVER_map_pair_t *data; U32 sizeLog; U32 size; U32 sizeMask; } COVER_map_t; /** * Clear the map. */ static void COVER_map_clear(COVER_map_t *map) { memset(map->data, MAP_EMPTY_VALUE, map->size * sizeof(COVER_map_pair_t)); } /** * Initializes a map of the given size. * Returns 1 on success and 0 on failure. * The map must be destroyed with COVER_map_destroy(). * The map is only guaranteed to be large enough to hold size elements. */ static int COVER_map_init(COVER_map_t *map, U32 size) { map->sizeLog = ZSTD_highbit32(size) + 2; map->size = (U32)1 << map->sizeLog; map->sizeMask = map->size - 1; map->data = (COVER_map_pair_t *)malloc(map->size * sizeof(COVER_map_pair_t)); if (!map->data) { map->sizeLog = 0; map->size = 0; return 0; } COVER_map_clear(map); return 1; } /** * Internal hash function */ static const U32 prime4bytes = 2654435761U; static U32 COVER_map_hash(COVER_map_t *map, U32 key) { return (key * prime4bytes) >> (32 - map->sizeLog); } /** * Helper function that returns the index that a key should be placed into. */ static U32 COVER_map_index(COVER_map_t *map, U32 key) { const U32 hash = COVER_map_hash(map, key); U32 i; for (i = hash;; i = (i + 1) & map->sizeMask) { COVER_map_pair_t *pos = &map->data[i]; if (pos->value == MAP_EMPTY_VALUE) { return i; } if (pos->key == key) { return i; } } } /** * Returns the pointer to the value for key. * If key is not in the map, it is inserted and the value is set to 0. * The map must not be full. */ static U32 *COVER_map_at(COVER_map_t *map, U32 key) { COVER_map_pair_t *pos = &map->data[COVER_map_index(map, key)]; if (pos->value == MAP_EMPTY_VALUE) { pos->key = key; pos->value = 0; } return &pos->value; } /** * Deletes key from the map if present. */ static void COVER_map_remove(COVER_map_t *map, U32 key) { U32 i = COVER_map_index(map, key); COVER_map_pair_t *del = &map->data[i]; U32 shift = 1; if (del->value == MAP_EMPTY_VALUE) { return; } for (i = (i + 1) & map->sizeMask;; i = (i + 1) & map->sizeMask) { COVER_map_pair_t *const pos = &map->data[i]; /* If the position is empty we are done */ if (pos->value == MAP_EMPTY_VALUE) { del->value = MAP_EMPTY_VALUE; return; } /* If pos can be moved to del do so */ if (((i - COVER_map_hash(map, pos->key)) & map->sizeMask) >= shift) { del->key = pos->key; del->value = pos->value; del = pos; shift = 1; } else { ++shift; } } } /** * Destroys a map that is inited with COVER_map_init(). */ static void COVER_map_destroy(COVER_map_t *map) { if (map->data) { free(map->data); } map->data = NULL; map->size = 0; } /*-************************************* * Context ***************************************/ typedef struct { const BYTE *samples; size_t *offsets; const size_t *samplesSizes; size_t nbSamples; size_t nbTrainSamples; size_t nbTestSamples; U32 *suffix; size_t suffixSize; U32 *freqs; U32 *dmerAt; unsigned d; } COVER_ctx_t; /* We need a global context for qsort... */ static COVER_ctx_t *g_ctx = NULL; /*-************************************* * Helper functions ***************************************/ /** * Returns the sum of the sample sizes. */ size_t COVER_sum(const size_t *samplesSizes, unsigned nbSamples) { size_t sum = 0; unsigned i; for (i = 0; i < nbSamples; ++i) { sum += samplesSizes[i]; } return sum; } /** * Returns -1 if the dmer at lp is less than the dmer at rp. * Return 0 if the dmers at lp and rp are equal. * Returns 1 if the dmer at lp is greater than the dmer at rp. */ static int COVER_cmp(COVER_ctx_t *ctx, const void *lp, const void *rp) { U32 const lhs = *(U32 const *)lp; U32 const rhs = *(U32 const *)rp; return memcmp(ctx->samples + lhs, ctx->samples + rhs, ctx->d); } /** * Faster version for d <= 8. */ static int COVER_cmp8(COVER_ctx_t *ctx, const void *lp, const void *rp) { U64 const mask = (ctx->d == 8) ? (U64)-1 : (((U64)1 << (8 * ctx->d)) - 1); U64 const lhs = MEM_readLE64(ctx->samples + *(U32 const *)lp) & mask; U64 const rhs = MEM_readLE64(ctx->samples + *(U32 const *)rp) & mask; if (lhs < rhs) { return -1; } return (lhs > rhs); } /** * Same as COVER_cmp() except ties are broken by pointer value * NOTE: g_ctx must be set to call this function. A global is required because * qsort doesn't take an opaque pointer. */ static int COVER_strict_cmp(const void *lp, const void *rp) { int result = COVER_cmp(g_ctx, lp, rp); if (result == 0) { result = lp < rp ? -1 : 1; } return result; } /** * Faster version for d <= 8. */ static int COVER_strict_cmp8(const void *lp, const void *rp) { int result = COVER_cmp8(g_ctx, lp, rp); if (result == 0) { result = lp < rp ? -1 : 1; } return result; } /** * Returns the first pointer in [first, last) whose element does not compare * less than value. If no such element exists it returns last. */ static const size_t *COVER_lower_bound(const size_t *first, const size_t *last, size_t value) { size_t count = last - first; while (count != 0) { size_t step = count / 2; const size_t *ptr = first; ptr += step; if (*ptr < value) { first = ++ptr; count -= step + 1; } else { count = step; } } return first; } /** * Generic groupBy function. * Groups an array sorted by cmp into groups with equivalent values. * Calls grp for each group. */ static void COVER_groupBy(const void *data, size_t count, size_t size, COVER_ctx_t *ctx, int (*cmp)(COVER_ctx_t *, const void *, const void *), void (*grp)(COVER_ctx_t *, const void *, const void *)) { const BYTE *ptr = (const BYTE *)data; size_t num = 0; while (num < count) { const BYTE *grpEnd = ptr + size; ++num; while (num < count && cmp(ctx, ptr, grpEnd) == 0) { grpEnd += size; ++num; } grp(ctx, ptr, grpEnd); ptr = grpEnd; } } /*-************************************* * Cover functions ***************************************/ /** * Called on each group of positions with the same dmer. * Counts the frequency of each dmer and saves it in the suffix array. * Fills `ctx->dmerAt`. */ static void COVER_group(COVER_ctx_t *ctx, const void *group, const void *groupEnd) { /* The group consists of all the positions with the same first d bytes. */ const U32 *grpPtr = (const U32 *)group; const U32 *grpEnd = (const U32 *)groupEnd; /* The dmerId is how we will reference this dmer. * This allows us to map the whole dmer space to a much smaller space, the * size of the suffix array. */ const U32 dmerId = (U32)(grpPtr - ctx->suffix); /* Count the number of samples this dmer shows up in */ U32 freq = 0; /* Details */ const size_t *curOffsetPtr = ctx->offsets; const size_t *offsetsEnd = ctx->offsets + ctx->nbSamples; /* Once *grpPtr >= curSampleEnd this occurrence of the dmer is in a * different sample than the last. */ size_t curSampleEnd = ctx->offsets[0]; for (; grpPtr != grpEnd; ++grpPtr) { /* Save the dmerId for this position so we can get back to it. */ ctx->dmerAt[*grpPtr] = dmerId; /* Dictionaries only help for the first reference to the dmer. * After that zstd can reference the match from the previous reference. * So only count each dmer once for each sample it is in. */ if (*grpPtr < curSampleEnd) { continue; } freq += 1; /* Binary search to find the end of the sample *grpPtr is in. * In the common case that grpPtr + 1 == grpEnd we can skip the binary * search because the loop is over. */ if (grpPtr + 1 != grpEnd) { const size_t *sampleEndPtr = COVER_lower_bound(curOffsetPtr, offsetsEnd, *grpPtr); curSampleEnd = *sampleEndPtr; curOffsetPtr = sampleEndPtr + 1; } } /* At this point we are never going to look at this segment of the suffix * array again. We take advantage of this fact to save memory. * We store the frequency of the dmer in the first position of the group, * which is dmerId. */ ctx->suffix[dmerId] = freq; } /** * Selects the best segment in an epoch. * Segments of are scored according to the function: * * Let F(d) be the frequency of dmer d. * Let S_i be the dmer at position i of segment S which has length k. * * Score(S) = F(S_1) + F(S_2) + ... + F(S_{k-d+1}) * * Once the dmer d is in the dictionary we set F(d) = 0. */ static COVER_segment_t COVER_selectSegment(const COVER_ctx_t *ctx, U32 *freqs, COVER_map_t *activeDmers, U32 begin, U32 end, ZDICT_cover_params_t parameters) { /* Constants */ const U32 k = parameters.k; const U32 d = parameters.d; const U32 dmersInK = k - d + 1; /* Try each segment (activeSegment) and save the best (bestSegment) */ COVER_segment_t bestSegment = {0, 0, 0}; COVER_segment_t activeSegment; /* Reset the activeDmers in the segment */ COVER_map_clear(activeDmers); /* The activeSegment starts at the beginning of the epoch. */ activeSegment.begin = begin; activeSegment.end = begin; activeSegment.score = 0; /* Slide the activeSegment through the whole epoch. * Save the best segment in bestSegment. */ while (activeSegment.end < end) { /* The dmerId for the dmer at the next position */ U32 newDmer = ctx->dmerAt[activeSegment.end]; /* The entry in activeDmers for this dmerId */ U32 *newDmerOcc = COVER_map_at(activeDmers, newDmer); /* If the dmer isn't already present in the segment add its score. */ if (*newDmerOcc == 0) { /* The paper suggest using the L-0.5 norm, but experiments show that it * doesn't help. */ activeSegment.score += freqs[newDmer]; } /* Add the dmer to the segment */ activeSegment.end += 1; *newDmerOcc += 1; /* If the window is now too large, drop the first position */ if (activeSegment.end - activeSegment.begin == dmersInK + 1) { U32 delDmer = ctx->dmerAt[activeSegment.begin]; U32 *delDmerOcc = COVER_map_at(activeDmers, delDmer); activeSegment.begin += 1; *delDmerOcc -= 1; /* If this is the last occurrence of the dmer, subtract its score */ if (*delDmerOcc == 0) { COVER_map_remove(activeDmers, delDmer); activeSegment.score -= freqs[delDmer]; } } /* If this segment is the best so far save it */ if (activeSegment.score > bestSegment.score) { bestSegment = activeSegment; } } { /* Trim off the zero frequency head and tail from the segment. */ U32 newBegin = bestSegment.end; U32 newEnd = bestSegment.begin; U32 pos; for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) { U32 freq = freqs[ctx->dmerAt[pos]]; if (freq != 0) { newBegin = MIN(newBegin, pos); newEnd = pos + 1; } } bestSegment.begin = newBegin; bestSegment.end = newEnd; } { /* Zero out the frequency of each dmer covered by the chosen segment. */ U32 pos; for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) { freqs[ctx->dmerAt[pos]] = 0; } } return bestSegment; } /** * Check the validity of the parameters. * Returns non-zero if the parameters are valid and 0 otherwise. */ static int COVER_checkParameters(ZDICT_cover_params_t parameters, size_t maxDictSize) { /* k and d are required parameters */ if (parameters.d == 0 || parameters.k == 0) { return 0; } /* k <= maxDictSize */ if (parameters.k > maxDictSize) { return 0; } /* d <= k */ if (parameters.d > parameters.k) { return 0; } /* 0 < splitPoint <= 1 */ if (parameters.splitPoint <= 0 || parameters.splitPoint > 1){ return 0; } return 1; } /** * Clean up a context initialized with `COVER_ctx_init()`. */ static void COVER_ctx_destroy(COVER_ctx_t *ctx) { if (!ctx) { return; } if (ctx->suffix) { free(ctx->suffix); ctx->suffix = NULL; } if (ctx->freqs) { free(ctx->freqs); ctx->freqs = NULL; } if (ctx->dmerAt) { free(ctx->dmerAt); ctx->dmerAt = NULL; } if (ctx->offsets) { free(ctx->offsets); ctx->offsets = NULL; } } /** * Prepare a context for dictionary building. * The context is only dependent on the parameter `d` and can used multiple * times. * Returns 0 on success or error code on error. * The context must be destroyed with `COVER_ctx_destroy()`. */ static size_t COVER_ctx_init(COVER_ctx_t *ctx, const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, unsigned d, double splitPoint) { const BYTE *const samples = (const BYTE *)samplesBuffer; const size_t totalSamplesSize = COVER_sum(samplesSizes, nbSamples); /* Split samples into testing and training sets */ const unsigned nbTrainSamples = splitPoint < 1.0 ? (unsigned)((double)nbSamples * splitPoint) : nbSamples; const unsigned nbTestSamples = splitPoint < 1.0 ? nbSamples - nbTrainSamples : nbSamples; const size_t trainingSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes, nbTrainSamples) : totalSamplesSize; const size_t testSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes + nbTrainSamples, nbTestSamples) : totalSamplesSize; /* Checks */ if (totalSamplesSize < MAX(d, sizeof(U64)) || totalSamplesSize >= (size_t)COVER_MAX_SAMPLES_SIZE) { DISPLAYLEVEL(1, "Total samples size is too large (%u MB), maximum size is %u MB\n", (unsigned)(totalSamplesSize>>20), (COVER_MAX_SAMPLES_SIZE >> 20)); return ERROR(srcSize_wrong); } /* Check if there are at least 5 training samples */ if (nbTrainSamples < 5) { DISPLAYLEVEL(1, "Total number of training samples is %u and is invalid.", nbTrainSamples); return ERROR(srcSize_wrong); } /* Check if there's testing sample */ if (nbTestSamples < 1) { DISPLAYLEVEL(1, "Total number of testing samples is %u and is invalid.", nbTestSamples); return ERROR(srcSize_wrong); } /* Zero the context */ memset(ctx, 0, sizeof(*ctx)); DISPLAYLEVEL(2, "Training on %u samples of total size %u\n", nbTrainSamples, (unsigned)trainingSamplesSize); DISPLAYLEVEL(2, "Testing on %u samples of total size %u\n", nbTestSamples, (unsigned)testSamplesSize); ctx->samples = samples; ctx->samplesSizes = samplesSizes; ctx->nbSamples = nbSamples; ctx->nbTrainSamples = nbTrainSamples; ctx->nbTestSamples = nbTestSamples; /* Partial suffix array */ ctx->suffixSize = trainingSamplesSize - MAX(d, sizeof(U64)) + 1; ctx->suffix = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); /* Maps index to the dmerID */ ctx->dmerAt = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); /* The offsets of each file */ ctx->offsets = (size_t *)malloc((nbSamples + 1) * sizeof(size_t)); if (!ctx->suffix || !ctx->dmerAt || !ctx->offsets) { DISPLAYLEVEL(1, "Failed to allocate scratch buffers\n"); COVER_ctx_destroy(ctx); return ERROR(memory_allocation); } ctx->freqs = NULL; ctx->d = d; /* Fill offsets from the samplesSizes */ { U32 i; ctx->offsets[0] = 0; for (i = 1; i <= nbSamples; ++i) { ctx->offsets[i] = ctx->offsets[i - 1] + samplesSizes[i - 1]; } } DISPLAYLEVEL(2, "Constructing partial suffix array\n"); { /* suffix is a partial suffix array. * It only sorts suffixes by their first parameters.d bytes. * The sort is stable, so each dmer group is sorted by position in input. */ U32 i; for (i = 0; i < ctx->suffixSize; ++i) { ctx->suffix[i] = i; } /* qsort doesn't take an opaque pointer, so pass as a global. * On OpenBSD qsort() is not guaranteed to be stable, their mergesort() is. */ g_ctx = ctx; #if defined(__OpenBSD__) mergesort(ctx->suffix, ctx->suffixSize, sizeof(U32), (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp)); #else qsort(ctx->suffix, ctx->suffixSize, sizeof(U32), (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp)); #endif } DISPLAYLEVEL(2, "Computing frequencies\n"); /* For each dmer group (group of positions with the same first d bytes): * 1. For each position we set dmerAt[position] = dmerID. The dmerID is * (groupBeginPtr - suffix). This allows us to go from position to * dmerID so we can look up values in freq. * 2. We calculate how many samples the dmer occurs in and save it in * freqs[dmerId]. */ COVER_groupBy(ctx->suffix, ctx->suffixSize, sizeof(U32), ctx, (ctx->d <= 8 ? &COVER_cmp8 : &COVER_cmp), &COVER_group); ctx->freqs = ctx->suffix; ctx->suffix = NULL; return 0; } void COVER_warnOnSmallCorpus(size_t maxDictSize, size_t nbDmers, int displayLevel) { const double ratio = (double)nbDmers / maxDictSize; if (ratio >= 10) { return; } LOCALDISPLAYLEVEL(displayLevel, 1, "WARNING: The maximum dictionary size %u is too large " "compared to the source size %u! " "size(source)/size(dictionary) = %f, but it should be >= " "10! This may lead to a subpar dictionary! We recommend " "training on sources at least 10x, and preferably 100x " "the size of the dictionary! \n", (U32)maxDictSize, (U32)nbDmers, ratio); } COVER_epoch_info_t COVER_computeEpochs(U32 maxDictSize, U32 nbDmers, U32 k, U32 passes) { const U32 minEpochSize = k * 10; COVER_epoch_info_t epochs; epochs.num = MAX(1, maxDictSize / k / passes); epochs.size = nbDmers / epochs.num; if (epochs.size >= minEpochSize) { assert(epochs.size * epochs.num <= nbDmers); return epochs; } epochs.size = MIN(minEpochSize, nbDmers); epochs.num = nbDmers / epochs.size; assert(epochs.size * epochs.num <= nbDmers); return epochs; } /** * Given the prepared context build the dictionary. */ static size_t COVER_buildDictionary(const COVER_ctx_t *ctx, U32 *freqs, COVER_map_t *activeDmers, void *dictBuffer, size_t dictBufferCapacity, ZDICT_cover_params_t parameters) { BYTE *const dict = (BYTE *)dictBuffer; size_t tail = dictBufferCapacity; /* Divide the data into epochs. We will select one segment from each epoch. */ const COVER_epoch_info_t epochs = COVER_computeEpochs( (U32)dictBufferCapacity, (U32)ctx->suffixSize, parameters.k, 4); const size_t maxZeroScoreRun = MAX(10, MIN(100, epochs.num >> 3)); size_t zeroScoreRun = 0; size_t epoch; DISPLAYLEVEL(2, "Breaking content into %u epochs of size %u\n", (U32)epochs.num, (U32)epochs.size); /* Loop through the epochs until there are no more segments or the dictionary * is full. */ for (epoch = 0; tail > 0; epoch = (epoch + 1) % epochs.num) { const U32 epochBegin = (U32)(epoch * epochs.size); const U32 epochEnd = epochBegin + epochs.size; size_t segmentSize; /* Select a segment */ COVER_segment_t segment = COVER_selectSegment( ctx, freqs, activeDmers, epochBegin, epochEnd, parameters); /* If the segment covers no dmers, then we are out of content. * There may be new content in other epochs, for continue for some time. */ if (segment.score == 0) { if (++zeroScoreRun >= maxZeroScoreRun) { break; } continue; } zeroScoreRun = 0; /* Trim the segment if necessary and if it is too small then we are done */ segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail); if (segmentSize < parameters.d) { break; } /* We fill the dictionary from the back to allow the best segments to be * referenced with the smallest offsets. */ tail -= segmentSize; memcpy(dict + tail, ctx->samples + segment.begin, segmentSize); DISPLAYUPDATE( 2, "\r%u%% ", (unsigned)(((dictBufferCapacity - tail) * 100) / dictBufferCapacity)); } DISPLAYLEVEL(2, "\r%79s\r", ""); return tail; } ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, ZDICT_cover_params_t parameters) { BYTE* const dict = (BYTE*)dictBuffer; COVER_ctx_t ctx; COVER_map_t activeDmers; parameters.splitPoint = 1.0; /* Initialize global data */ g_displayLevel = parameters.zParams.notificationLevel; /* Checks */ if (!COVER_checkParameters(parameters, dictBufferCapacity)) { DISPLAYLEVEL(1, "Cover parameters incorrect\n"); return ERROR(parameter_outOfBound); } if (nbSamples == 0) { DISPLAYLEVEL(1, "Cover must have at least one input file\n"); return ERROR(srcSize_wrong); } if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n", ZDICT_DICTSIZE_MIN); return ERROR(dstSize_tooSmall); } /* Initialize context and activeDmers */ { size_t const initVal = COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, parameters.d, parameters.splitPoint); if (ZSTD_isError(initVal)) { return initVal; } } COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.suffixSize, g_displayLevel); if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) { DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n"); COVER_ctx_destroy(&ctx); return ERROR(memory_allocation); } DISPLAYLEVEL(2, "Building dictionary\n"); { const size_t tail = COVER_buildDictionary(&ctx, ctx.freqs, &activeDmers, dictBuffer, dictBufferCapacity, parameters); const size_t dictionarySize = ZDICT_finalizeDictionary( dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail, samplesBuffer, samplesSizes, nbSamples, parameters.zParams); if (!ZSTD_isError(dictionarySize)) { DISPLAYLEVEL(2, "Constructed dictionary of size %u\n", (unsigned)dictionarySize); } COVER_ctx_destroy(&ctx); COVER_map_destroy(&activeDmers); return dictionarySize; } } size_t COVER_checkTotalCompressedSize(const ZDICT_cover_params_t parameters, const size_t *samplesSizes, const BYTE *samples, size_t *offsets, size_t nbTrainSamples, size_t nbSamples, BYTE *const dict, size_t dictBufferCapacity) { size_t totalCompressedSize = ERROR(GENERIC); /* Pointers */ ZSTD_CCtx *cctx; ZSTD_CDict *cdict; void *dst; /* Local variables */ size_t dstCapacity; size_t i; /* Allocate dst with enough space to compress the maximum sized sample */ { size_t maxSampleSize = 0; i = parameters.splitPoint < 1.0 ? nbTrainSamples : 0; for (; i < nbSamples; ++i) { maxSampleSize = MAX(samplesSizes[i], maxSampleSize); } dstCapacity = ZSTD_compressBound(maxSampleSize); dst = malloc(dstCapacity); } /* Create the cctx and cdict */ cctx = ZSTD_createCCtx(); cdict = ZSTD_createCDict(dict, dictBufferCapacity, parameters.zParams.compressionLevel); if (!dst || !cctx || !cdict) { goto _compressCleanup; } /* Compress each sample and sum their sizes (or error) */ totalCompressedSize = dictBufferCapacity; i = parameters.splitPoint < 1.0 ? nbTrainSamples : 0; for (; i < nbSamples; ++i) { const size_t size = ZSTD_compress_usingCDict( cctx, dst, dstCapacity, samples + offsets[i], samplesSizes[i], cdict); if (ZSTD_isError(size)) { totalCompressedSize = size; goto _compressCleanup; } totalCompressedSize += size; } _compressCleanup: ZSTD_freeCCtx(cctx); ZSTD_freeCDict(cdict); if (dst) { free(dst); } return totalCompressedSize; } /** * Initialize the `COVER_best_t`. */ void COVER_best_init(COVER_best_t *best) { if (best==NULL) return; /* compatible with init on NULL */ (void)ZSTD_pthread_mutex_init(&best->mutex, NULL); (void)ZSTD_pthread_cond_init(&best->cond, NULL); best->liveJobs = 0; best->dict = NULL; best->dictSize = 0; best->compressedSize = (size_t)-1; memset(&best->parameters, 0, sizeof(best->parameters)); } /** * Wait until liveJobs == 0. */ void COVER_best_wait(COVER_best_t *best) { if (!best) { return; } ZSTD_pthread_mutex_lock(&best->mutex); while (best->liveJobs != 0) { ZSTD_pthread_cond_wait(&best->cond, &best->mutex); } ZSTD_pthread_mutex_unlock(&best->mutex); } /** * Call COVER_best_wait() and then destroy the COVER_best_t. */ void COVER_best_destroy(COVER_best_t *best) { if (!best) { return; } COVER_best_wait(best); if (best->dict) { free(best->dict); } ZSTD_pthread_mutex_destroy(&best->mutex); ZSTD_pthread_cond_destroy(&best->cond); } /** * Called when a thread is about to be launched. * Increments liveJobs. */ void COVER_best_start(COVER_best_t *best) { if (!best) { return; } ZSTD_pthread_mutex_lock(&best->mutex); ++best->liveJobs; ZSTD_pthread_mutex_unlock(&best->mutex); } /** * Called when a thread finishes executing, both on error or success. * Decrements liveJobs and signals any waiting threads if liveJobs == 0. * If this dictionary is the best so far save it and its parameters. */ void COVER_best_finish(COVER_best_t *best, ZDICT_cover_params_t parameters, COVER_dictSelection_t selection) { void* dict = selection.dictContent; size_t compressedSize = selection.totalCompressedSize; size_t dictSize = selection.dictSize; if (!best) { return; } { size_t liveJobs; ZSTD_pthread_mutex_lock(&best->mutex); --best->liveJobs; liveJobs = best->liveJobs; /* If the new dictionary is better */ if (compressedSize < best->compressedSize) { /* Allocate space if necessary */ if (!best->dict || best->dictSize < dictSize) { if (best->dict) { free(best->dict); } best->dict = malloc(dictSize); if (!best->dict) { best->compressedSize = ERROR(GENERIC); best->dictSize = 0; ZSTD_pthread_cond_signal(&best->cond); ZSTD_pthread_mutex_unlock(&best->mutex); return; } } /* Save the dictionary, parameters, and size */ if (dict) { memcpy(best->dict, dict, dictSize); best->dictSize = dictSize; best->parameters = parameters; best->compressedSize = compressedSize; } } if (liveJobs == 0) { ZSTD_pthread_cond_broadcast(&best->cond); } ZSTD_pthread_mutex_unlock(&best->mutex); } } COVER_dictSelection_t COVER_dictSelectionError(size_t error) { COVER_dictSelection_t selection = { NULL, 0, error }; return selection; } unsigned COVER_dictSelectionIsError(COVER_dictSelection_t selection) { return (ZSTD_isError(selection.totalCompressedSize) || !selection.dictContent); } void COVER_dictSelectionFree(COVER_dictSelection_t selection){ free(selection.dictContent); } COVER_dictSelection_t COVER_selectDict(BYTE* customDictContent, size_t dictContentSize, const BYTE* samplesBuffer, const size_t* samplesSizes, unsigned nbFinalizeSamples, size_t nbCheckSamples, size_t nbSamples, ZDICT_cover_params_t params, size_t* offsets, size_t totalCompressedSize) { size_t largestDict = 0; size_t largestCompressed = 0; BYTE* customDictContentEnd = customDictContent + dictContentSize; BYTE * largestDictbuffer = (BYTE *)malloc(dictContentSize); BYTE * candidateDictBuffer = (BYTE *)malloc(dictContentSize); double regressionTolerance = ((double)params.shrinkDictMaxRegression / 100.0) + 1.00; if (!largestDictbuffer || !candidateDictBuffer) { free(largestDictbuffer); free(candidateDictBuffer); return COVER_dictSelectionError(dictContentSize); } /* Initial dictionary size and compressed size */ memcpy(largestDictbuffer, customDictContent, dictContentSize); dictContentSize = ZDICT_finalizeDictionary( largestDictbuffer, dictContentSize, customDictContent, dictContentSize, samplesBuffer, samplesSizes, nbFinalizeSamples, params.zParams); if (ZDICT_isError(dictContentSize)) { free(largestDictbuffer); free(candidateDictBuffer); return COVER_dictSelectionError(dictContentSize); } totalCompressedSize = COVER_checkTotalCompressedSize(params, samplesSizes, samplesBuffer, offsets, nbCheckSamples, nbSamples, largestDictbuffer, dictContentSize); if (ZSTD_isError(totalCompressedSize)) { free(largestDictbuffer); free(candidateDictBuffer); return COVER_dictSelectionError(totalCompressedSize); } if (params.shrinkDict == 0) { COVER_dictSelection_t selection = { largestDictbuffer, dictContentSize, totalCompressedSize }; free(candidateDictBuffer); return selection; } largestDict = dictContentSize; largestCompressed = totalCompressedSize; dictContentSize = ZDICT_DICTSIZE_MIN; /* Largest dict is initially at least ZDICT_DICTSIZE_MIN */ while (dictContentSize < largestDict) { memcpy(candidateDictBuffer, largestDictbuffer, largestDict); dictContentSize = ZDICT_finalizeDictionary( candidateDictBuffer, dictContentSize, customDictContentEnd - dictContentSize, dictContentSize, samplesBuffer, samplesSizes, nbFinalizeSamples, params.zParams); if (ZDICT_isError(dictContentSize)) { free(largestDictbuffer); free(candidateDictBuffer); return COVER_dictSelectionError(dictContentSize); } totalCompressedSize = COVER_checkTotalCompressedSize(params, samplesSizes, samplesBuffer, offsets, nbCheckSamples, nbSamples, candidateDictBuffer, dictContentSize); if (ZSTD_isError(totalCompressedSize)) { free(largestDictbuffer); free(candidateDictBuffer); return COVER_dictSelectionError(totalCompressedSize); } if (totalCompressedSize <= largestCompressed * regressionTolerance) { COVER_dictSelection_t selection = { candidateDictBuffer, dictContentSize, totalCompressedSize }; free(largestDictbuffer); return selection; } dictContentSize *= 2; } dictContentSize = largestDict; totalCompressedSize = largestCompressed; { COVER_dictSelection_t selection = { largestDictbuffer, dictContentSize, totalCompressedSize }; free(candidateDictBuffer); return selection; } } /** * Parameters for COVER_tryParameters(). */ typedef struct COVER_tryParameters_data_s { const COVER_ctx_t *ctx; COVER_best_t *best; size_t dictBufferCapacity; ZDICT_cover_params_t parameters; } COVER_tryParameters_data_t; /** * Tries a set of parameters and updates the COVER_best_t with the results. * This function is thread safe if zstd is compiled with multithreaded support. * It takes its parameters as an *OWNING* opaque pointer to support threading. */ static void COVER_tryParameters(void *opaque) { /* Save parameters as local variables */ COVER_tryParameters_data_t *const data = (COVER_tryParameters_data_t *)opaque; const COVER_ctx_t *const ctx = data->ctx; const ZDICT_cover_params_t parameters = data->parameters; size_t dictBufferCapacity = data->dictBufferCapacity; size_t totalCompressedSize = ERROR(GENERIC); /* Allocate space for hash table, dict, and freqs */ COVER_map_t activeDmers; BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity); COVER_dictSelection_t selection = COVER_dictSelectionError(ERROR(GENERIC)); U32 *freqs = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) { DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n"); goto _cleanup; } if (!dict || !freqs) { DISPLAYLEVEL(1, "Failed to allocate buffers: out of memory\n"); goto _cleanup; } /* Copy the frequencies because we need to modify them */ memcpy(freqs, ctx->freqs, ctx->suffixSize * sizeof(U32)); /* Build the dictionary */ { const size_t tail = COVER_buildDictionary(ctx, freqs, &activeDmers, dict, dictBufferCapacity, parameters); selection = COVER_selectDict(dict + tail, dictBufferCapacity - tail, ctx->samples, ctx->samplesSizes, (unsigned)ctx->nbTrainSamples, ctx->nbTrainSamples, ctx->nbSamples, parameters, ctx->offsets, totalCompressedSize); if (COVER_dictSelectionIsError(selection)) { DISPLAYLEVEL(1, "Failed to select dictionary\n"); goto _cleanup; } } _cleanup: free(dict); COVER_best_finish(data->best, parameters, selection); free(data); COVER_map_destroy(&activeDmers); COVER_dictSelectionFree(selection); if (freqs) { free(freqs); } } ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover( void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, ZDICT_cover_params_t *parameters) { /* constants */ const unsigned nbThreads = parameters->nbThreads; const double splitPoint = parameters->splitPoint <= 0.0 ? DEFAULT_SPLITPOINT : parameters->splitPoint; const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d; const unsigned kMaxD = parameters->d == 0 ? 8 : parameters->d; const unsigned kMinK = parameters->k == 0 ? 50 : parameters->k; const unsigned kMaxK = parameters->k == 0 ? 2000 : parameters->k; const unsigned kSteps = parameters->steps == 0 ? 40 : parameters->steps; const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1); const unsigned kIterations = (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize); const unsigned shrinkDict = 0; /* Local variables */ const int displayLevel = parameters->zParams.notificationLevel; unsigned iteration = 1; unsigned d; unsigned k; COVER_best_t best; POOL_ctx *pool = NULL; int warned = 0; /* Checks */ if (splitPoint <= 0 || splitPoint > 1) { LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n"); return ERROR(parameter_outOfBound); } if (kMinK < kMaxD || kMaxK < kMinK) { LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n"); return ERROR(parameter_outOfBound); } if (nbSamples == 0) { DISPLAYLEVEL(1, "Cover must have at least one input file\n"); return ERROR(srcSize_wrong); } if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n", ZDICT_DICTSIZE_MIN); return ERROR(dstSize_tooSmall); } if (nbThreads > 1) { pool = POOL_create(nbThreads, 1); if (!pool) { return ERROR(memory_allocation); } } /* Initialization */ COVER_best_init(&best); /* Turn down global display level to clean up display at level 2 and below */ g_displayLevel = displayLevel == 0 ? 0 : displayLevel - 1; /* Loop through d first because each new value needs a new context */ LOCALDISPLAYLEVEL(displayLevel, 2, "Trying %u different sets of parameters\n", kIterations); for (d = kMinD; d <= kMaxD; d += 2) { /* Initialize the context for this value of d */ COVER_ctx_t ctx; LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d); { const size_t initVal = COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d, splitPoint); if (ZSTD_isError(initVal)) { LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n"); COVER_best_destroy(&best); POOL_free(pool); return initVal; } } if (!warned) { COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.suffixSize, displayLevel); warned = 1; } /* Loop through k reusing the same context */ for (k = kMinK; k <= kMaxK; k += kStepSize) { /* Prepare the arguments */ COVER_tryParameters_data_t *data = (COVER_tryParameters_data_t *)malloc( sizeof(COVER_tryParameters_data_t)); LOCALDISPLAYLEVEL(displayLevel, 3, "k=%u\n", k); if (!data) { LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to allocate parameters\n"); COVER_best_destroy(&best); COVER_ctx_destroy(&ctx); POOL_free(pool); return ERROR(memory_allocation); } data->ctx = &ctx; data->best = &best; data->dictBufferCapacity = dictBufferCapacity; data->parameters = *parameters; data->parameters.k = k; data->parameters.d = d; data->parameters.splitPoint = splitPoint; data->parameters.steps = kSteps; data->parameters.shrinkDict = shrinkDict; data->parameters.zParams.notificationLevel = g_displayLevel; /* Check the parameters */ if (!COVER_checkParameters(data->parameters, dictBufferCapacity)) { DISPLAYLEVEL(1, "Cover parameters incorrect\n"); free(data); continue; } /* Call the function and pass ownership of data to it */ COVER_best_start(&best); if (pool) { POOL_add(pool, &COVER_tryParameters, data); } else { COVER_tryParameters(data); } /* Print status */ LOCALDISPLAYUPDATE(displayLevel, 2, "\r%u%% ", (unsigned)((iteration * 100) / kIterations)); ++iteration; } COVER_best_wait(&best); COVER_ctx_destroy(&ctx); } LOCALDISPLAYLEVEL(displayLevel, 2, "\r%79s\r", ""); /* Fill the output buffer and parameters with output of the best parameters */ { const size_t dictSize = best.dictSize; if (ZSTD_isError(best.compressedSize)) { const size_t compressedSize = best.compressedSize; COVER_best_destroy(&best); POOL_free(pool); return compressedSize; } *parameters = best.parameters; memcpy(dictBuffer, best.dict, dictSize); COVER_best_destroy(&best); POOL_free(pool); return dictSize; } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/dictBuilder/cover.h0000644000076500000240000001156614601576577023415 0ustar00twstaff/* * Copyright (c) 2017-2020, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #include /* fprintf */ #include /* malloc, free, qsort */ #include /* memset */ #include /* clock */ #include "../common/mem.h" /* read */ #include "../common/pool.h" #include "../common/threading.h" #include "../common/zstd_internal.h" /* includes zstd.h */ #ifndef ZDICT_STATIC_LINKING_ONLY #define ZDICT_STATIC_LINKING_ONLY #endif #include "zdict.h" /** * COVER_best_t is used for two purposes: * 1. Synchronizing threads. * 2. Saving the best parameters and dictionary. * * All of the methods except COVER_best_init() are thread safe if zstd is * compiled with multithreaded support. */ typedef struct COVER_best_s { ZSTD_pthread_mutex_t mutex; ZSTD_pthread_cond_t cond; size_t liveJobs; void *dict; size_t dictSize; ZDICT_cover_params_t parameters; size_t compressedSize; } COVER_best_t; /** * A segment is a range in the source as well as the score of the segment. */ typedef struct { U32 begin; U32 end; U32 score; } COVER_segment_t; /** *Number of epochs and size of each epoch. */ typedef struct { U32 num; U32 size; } COVER_epoch_info_t; /** * Struct used for the dictionary selection function. */ typedef struct COVER_dictSelection { BYTE* dictContent; size_t dictSize; size_t totalCompressedSize; } COVER_dictSelection_t; /** * Computes the number of epochs and the size of each epoch. * We will make sure that each epoch gets at least 10 * k bytes. * * The COVER algorithms divide the data up into epochs of equal size and * select one segment from each epoch. * * @param maxDictSize The maximum allowed dictionary size. * @param nbDmers The number of dmers we are training on. * @param k The parameter k (segment size). * @param passes The target number of passes over the dmer corpus. * More passes means a better dictionary. */ COVER_epoch_info_t COVER_computeEpochs(U32 maxDictSize, U32 nbDmers, U32 k, U32 passes); /** * Warns the user when their corpus is too small. */ void COVER_warnOnSmallCorpus(size_t maxDictSize, size_t nbDmers, int displayLevel); /** * Checks total compressed size of a dictionary */ size_t COVER_checkTotalCompressedSize(const ZDICT_cover_params_t parameters, const size_t *samplesSizes, const BYTE *samples, size_t *offsets, size_t nbTrainSamples, size_t nbSamples, BYTE *const dict, size_t dictBufferCapacity); /** * Returns the sum of the sample sizes. */ size_t COVER_sum(const size_t *samplesSizes, unsigned nbSamples) ; /** * Initialize the `COVER_best_t`. */ void COVER_best_init(COVER_best_t *best); /** * Wait until liveJobs == 0. */ void COVER_best_wait(COVER_best_t *best); /** * Call COVER_best_wait() and then destroy the COVER_best_t. */ void COVER_best_destroy(COVER_best_t *best); /** * Called when a thread is about to be launched. * Increments liveJobs. */ void COVER_best_start(COVER_best_t *best); /** * Called when a thread finishes executing, both on error or success. * Decrements liveJobs and signals any waiting threads if liveJobs == 0. * If this dictionary is the best so far save it and its parameters. */ void COVER_best_finish(COVER_best_t *best, ZDICT_cover_params_t parameters, COVER_dictSelection_t selection); /** * Error function for COVER_selectDict function. Checks if the return * value is an error. */ unsigned COVER_dictSelectionIsError(COVER_dictSelection_t selection); /** * Error function for COVER_selectDict function. Returns a struct where * return.totalCompressedSize is a ZSTD error. */ COVER_dictSelection_t COVER_dictSelectionError(size_t error); /** * Always call after selectDict is called to free up used memory from * newly created dictionary. */ void COVER_dictSelectionFree(COVER_dictSelection_t selection); /** * Called to finalize the dictionary and select one based on whether or not * the shrink-dict flag was enabled. If enabled the dictionary used is the * smallest dictionary within a specified regression of the compressed size * from the largest dictionary. */ COVER_dictSelection_t COVER_selectDict(BYTE* customDictContent, size_t dictContentSize, const BYTE* samplesBuffer, const size_t* samplesSizes, unsigned nbFinalizeSamples, size_t nbCheckSamples, size_t nbSamples, ZDICT_cover_params_t params, size_t* offsets, size_t totalCompressedSize); ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/dictBuilder/divsufsort.c0000644000076500000240000015257114601576577024504 0ustar00twstaff/* * divsufsort.c for libdivsufsort-lite * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /*- Compiler specifics -*/ #ifdef __clang__ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #endif #if defined(_MSC_VER) # pragma warning(disable : 4244) # pragma warning(disable : 4127) /* C4127 : Condition expression is constant */ #endif /*- Dependencies -*/ #include #include #include #include "divsufsort.h" /*- Constants -*/ #if defined(INLINE) # undef INLINE #endif #if !defined(INLINE) # define INLINE __inline #endif #if defined(ALPHABET_SIZE) && (ALPHABET_SIZE < 1) # undef ALPHABET_SIZE #endif #if !defined(ALPHABET_SIZE) # define ALPHABET_SIZE (256) #endif #define BUCKET_A_SIZE (ALPHABET_SIZE) #define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE) #if defined(SS_INSERTIONSORT_THRESHOLD) # if SS_INSERTIONSORT_THRESHOLD < 1 # undef SS_INSERTIONSORT_THRESHOLD # define SS_INSERTIONSORT_THRESHOLD (1) # endif #else # define SS_INSERTIONSORT_THRESHOLD (8) #endif #if defined(SS_BLOCKSIZE) # if SS_BLOCKSIZE < 0 # undef SS_BLOCKSIZE # define SS_BLOCKSIZE (0) # elif 32768 <= SS_BLOCKSIZE # undef SS_BLOCKSIZE # define SS_BLOCKSIZE (32767) # endif #else # define SS_BLOCKSIZE (1024) #endif /* minstacksize = log(SS_BLOCKSIZE) / log(3) * 2 */ #if SS_BLOCKSIZE == 0 # define SS_MISORT_STACKSIZE (96) #elif SS_BLOCKSIZE <= 4096 # define SS_MISORT_STACKSIZE (16) #else # define SS_MISORT_STACKSIZE (24) #endif #define SS_SMERGE_STACKSIZE (32) #define TR_INSERTIONSORT_THRESHOLD (8) #define TR_STACKSIZE (64) /*- Macros -*/ #ifndef SWAP # define SWAP(_a, _b) do { t = (_a); (_a) = (_b); (_b) = t; } while(0) #endif /* SWAP */ #ifndef MIN # define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) #endif /* MIN */ #ifndef MAX # define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) #endif /* MAX */ #define STACK_PUSH(_a, _b, _c, _d)\ do {\ assert(ssize < STACK_SIZE);\ stack[ssize].a = (_a), stack[ssize].b = (_b),\ stack[ssize].c = (_c), stack[ssize++].d = (_d);\ } while(0) #define STACK_PUSH5(_a, _b, _c, _d, _e)\ do {\ assert(ssize < STACK_SIZE);\ stack[ssize].a = (_a), stack[ssize].b = (_b),\ stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e);\ } while(0) #define STACK_POP(_a, _b, _c, _d)\ do {\ assert(0 <= ssize);\ if(ssize == 0) { return; }\ (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ (_c) = stack[ssize].c, (_d) = stack[ssize].d;\ } while(0) #define STACK_POP5(_a, _b, _c, _d, _e)\ do {\ assert(0 <= ssize);\ if(ssize == 0) { return; }\ (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e;\ } while(0) #define BUCKET_A(_c0) bucket_A[(_c0)] #if ALPHABET_SIZE == 256 #define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)]) #define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)]) #else #define BUCKET_B(_c0, _c1) (bucket_B[(_c1) * ALPHABET_SIZE + (_c0)]) #define BUCKET_BSTAR(_c0, _c1) (bucket_B[(_c0) * ALPHABET_SIZE + (_c1)]) #endif /*- Private Functions -*/ static const int lg_table[256]= { -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 }; #if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) static INLINE int ss_ilg(int n) { #if SS_BLOCKSIZE == 0 return (n & 0xffff0000) ? ((n & 0xff000000) ? 24 + lg_table[(n >> 24) & 0xff] : 16 + lg_table[(n >> 16) & 0xff]) : ((n & 0x0000ff00) ? 8 + lg_table[(n >> 8) & 0xff] : 0 + lg_table[(n >> 0) & 0xff]); #elif SS_BLOCKSIZE < 256 return lg_table[n]; #else return (n & 0xff00) ? 8 + lg_table[(n >> 8) & 0xff] : 0 + lg_table[(n >> 0) & 0xff]; #endif } #endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ #if SS_BLOCKSIZE != 0 static const int sqq_table[256] = { 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89, 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255 }; static INLINE int ss_isqrt(int x) { int y, e; if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; } e = (x & 0xffff0000) ? ((x & 0xff000000) ? 24 + lg_table[(x >> 24) & 0xff] : 16 + lg_table[(x >> 16) & 0xff]) : ((x & 0x0000ff00) ? 8 + lg_table[(x >> 8) & 0xff] : 0 + lg_table[(x >> 0) & 0xff]); if(e >= 16) { y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7); if(e >= 24) { y = (y + 1 + x / y) >> 1; } y = (y + 1 + x / y) >> 1; } else if(e >= 8) { y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1; } else { return sqq_table[x] >> 4; } return (x < (y * y)) ? y - 1 : y; } #endif /* SS_BLOCKSIZE != 0 */ /*---------------------------------------------------------------------------*/ /* Compares two suffixes. */ static INLINE int ss_compare(const unsigned char *T, const int *p1, const int *p2, int depth) { const unsigned char *U1, *U2, *U1n, *U2n; for(U1 = T + depth + *p1, U2 = T + depth + *p2, U1n = T + *(p1 + 1) + 2, U2n = T + *(p2 + 1) + 2; (U1 < U1n) && (U2 < U2n) && (*U1 == *U2); ++U1, ++U2) { } return U1 < U1n ? (U2 < U2n ? *U1 - *U2 : 1) : (U2 < U2n ? -1 : 0); } /*---------------------------------------------------------------------------*/ #if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) /* Insertionsort for small size groups */ static void ss_insertionsort(const unsigned char *T, const int *PA, int *first, int *last, int depth) { int *i, *j; int t; int r; for(i = last - 2; first <= i; --i) { for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) { do { *(j - 1) = *j; } while((++j < last) && (*j < 0)); if(last <= j) { break; } } if(r == 0) { *j = ~*j; } *(j - 1) = t; } } #endif /* (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) */ /*---------------------------------------------------------------------------*/ #if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) static INLINE void ss_fixdown(const unsigned char *Td, const int *PA, int *SA, int i, int size) { int j, k; int v; int c, d, e; for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { d = Td[PA[SA[k = j++]]]; if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; } if(d <= c) { break; } } SA[i] = v; } /* Simple top-down heapsort. */ static void ss_heapsort(const unsigned char *Td, const int *PA, int *SA, int size) { int i, m; int t; m = size; if((size % 2) == 0) { m--; if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) { SWAP(SA[m], SA[m / 2]); } } for(i = m / 2 - 1; 0 <= i; --i) { ss_fixdown(Td, PA, SA, i, m); } if((size % 2) == 0) { SWAP(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); } for(i = m - 1; 0 < i; --i) { t = SA[0], SA[0] = SA[i]; ss_fixdown(Td, PA, SA, 0, i); SA[i] = t; } } /*---------------------------------------------------------------------------*/ /* Returns the median of three elements. */ static INLINE int * ss_median3(const unsigned char *Td, const int *PA, int *v1, int *v2, int *v3) { int *t; if(Td[PA[*v1]] > Td[PA[*v2]]) { SWAP(v1, v2); } if(Td[PA[*v2]] > Td[PA[*v3]]) { if(Td[PA[*v1]] > Td[PA[*v3]]) { return v1; } else { return v3; } } return v2; } /* Returns the median of five elements. */ static INLINE int * ss_median5(const unsigned char *Td, const int *PA, int *v1, int *v2, int *v3, int *v4, int *v5) { int *t; if(Td[PA[*v2]] > Td[PA[*v3]]) { SWAP(v2, v3); } if(Td[PA[*v4]] > Td[PA[*v5]]) { SWAP(v4, v5); } if(Td[PA[*v2]] > Td[PA[*v4]]) { SWAP(v2, v4); SWAP(v3, v5); } if(Td[PA[*v1]] > Td[PA[*v3]]) { SWAP(v1, v3); } if(Td[PA[*v1]] > Td[PA[*v4]]) { SWAP(v1, v4); SWAP(v3, v5); } if(Td[PA[*v3]] > Td[PA[*v4]]) { return v4; } return v3; } /* Returns the pivot element. */ static INLINE int * ss_pivot(const unsigned char *Td, const int *PA, int *first, int *last) { int *middle; int t; t = last - first; middle = first + t / 2; if(t <= 512) { if(t <= 32) { return ss_median3(Td, PA, first, middle, last - 1); } else { t >>= 2; return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1); } } t >>= 3; first = ss_median3(Td, PA, first, first + t, first + (t << 1)); middle = ss_median3(Td, PA, middle - t, middle, middle + t); last = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1); return ss_median3(Td, PA, first, middle, last); } /*---------------------------------------------------------------------------*/ /* Binary partition for substrings. */ static INLINE int * ss_partition(const int *PA, int *first, int *last, int depth) { int *a, *b; int t; for(a = first - 1, b = last;;) { for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) { *a = ~*a; } for(; (a < --b) && ((PA[*b] + depth) < (PA[*b + 1] + 1));) { } if(b <= a) { break; } t = ~*b; *b = *a; *a = t; } if(first < a) { *first = ~*first; } return a; } /* Multikey introsort for medium size groups. */ static void ss_mintrosort(const unsigned char *T, const int *PA, int *first, int *last, int depth) { #define STACK_SIZE SS_MISORT_STACKSIZE struct { int *a, *b, c; int d; } stack[STACK_SIZE]; const unsigned char *Td; int *a, *b, *c, *d, *e, *f; int s, t; int ssize; int limit; int v, x = 0; for(ssize = 0, limit = ss_ilg(last - first);;) { if((last - first) <= SS_INSERTIONSORT_THRESHOLD) { #if 1 < SS_INSERTIONSORT_THRESHOLD if(1 < (last - first)) { ss_insertionsort(T, PA, first, last, depth); } #endif STACK_POP(first, last, depth, limit); continue; } Td = T + depth; if(limit-- == 0) { ss_heapsort(Td, PA, first, last - first); } if(limit < 0) { for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) { if((x = Td[PA[*a]]) != v) { if(1 < (a - first)) { break; } v = x; first = a; } } if(Td[PA[*first] - 1] < v) { first = ss_partition(PA, first, a, depth); } if((a - first) <= (last - a)) { if(1 < (a - first)) { STACK_PUSH(a, last, depth, -1); last = a, depth += 1, limit = ss_ilg(a - first); } else { first = a, limit = -1; } } else { if(1 < (last - a)) { STACK_PUSH(first, a, depth + 1, ss_ilg(a - first)); first = a, limit = -1; } else { last = a, depth += 1, limit = ss_ilg(a - first); } } continue; } /* choose pivot */ a = ss_pivot(Td, PA, first, last); v = Td[PA[*a]]; SWAP(*first, *a); /* partition */ for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);) { } if(((a = b) < last) && (x < v)) { for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) { if(x == v) { SWAP(*b, *a); ++a; } } } for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);) { } if((b < (d = c)) && (x > v)) { for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { if(x == v) { SWAP(*c, *d); --d; } } } for(; b < c;) { SWAP(*b, *c); for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) { if(x == v) { SWAP(*b, *a); ++a; } } for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { if(x == v) { SWAP(*c, *d); --d; } } } if(a <= d) { c = b - 1; if((s = a - first) > (t = b - a)) { s = t; } for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } if((s = d - c) > (t = last - d - 1)) { s = t; } for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } a = first + (b - a), c = last - (d - c); b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth); if((a - first) <= (last - c)) { if((last - c) <= (c - b)) { STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); STACK_PUSH(c, last, depth, limit); last = a; } else if((a - first) <= (c - b)) { STACK_PUSH(c, last, depth, limit); STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); last = a; } else { STACK_PUSH(c, last, depth, limit); STACK_PUSH(first, a, depth, limit); first = b, last = c, depth += 1, limit = ss_ilg(c - b); } } else { if((a - first) <= (c - b)) { STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); STACK_PUSH(first, a, depth, limit); first = c; } else if((last - c) <= (c - b)) { STACK_PUSH(first, a, depth, limit); STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); first = c; } else { STACK_PUSH(first, a, depth, limit); STACK_PUSH(c, last, depth, limit); first = b, last = c, depth += 1, limit = ss_ilg(c - b); } } } else { limit += 1; if(Td[PA[*first] - 1] < v) { first = ss_partition(PA, first, last, depth); limit = ss_ilg(last - first); } depth += 1; } } #undef STACK_SIZE } #endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ /*---------------------------------------------------------------------------*/ #if SS_BLOCKSIZE != 0 static INLINE void ss_blockswap(int *a, int *b, int n) { int t; for(; 0 < n; --n, ++a, ++b) { t = *a, *a = *b, *b = t; } } static INLINE void ss_rotate(int *first, int *middle, int *last) { int *a, *b, t; int l, r; l = middle - first, r = last - middle; for(; (0 < l) && (0 < r);) { if(l == r) { ss_blockswap(first, middle, l); break; } if(l < r) { a = last - 1, b = middle - 1; t = *a; do { *a-- = *b, *b-- = *a; if(b < first) { *a = t; last = a; if((r -= l + 1) <= l) { break; } a -= 1, b = middle - 1; t = *a; } } while(1); } else { a = first, b = middle; t = *a; do { *a++ = *b, *b++ = *a; if(last <= b) { *a = t; first = a + 1; if((l -= r + 1) <= r) { break; } a += 1, b = middle; t = *a; } } while(1); } } } /*---------------------------------------------------------------------------*/ static void ss_inplacemerge(const unsigned char *T, const int *PA, int *first, int *middle, int *last, int depth) { const int *p; int *a, *b; int len, half; int q, r; int x; for(;;) { if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); } else { x = 0; p = PA + *(last - 1); } for(a = first, len = middle - first, half = len >> 1, r = -1; 0 < len; len = half, half >>= 1) { b = a + half; q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth); if(q < 0) { a = b + 1; half -= (len & 1) ^ 1; } else { r = q; } } if(a < middle) { if(r == 0) { *a = ~*a; } ss_rotate(a, middle, last); last -= middle - a; middle = a; if(first == middle) { break; } } --last; if(x != 0) { while(*--last < 0) { } } if(middle == last) { break; } } } /*---------------------------------------------------------------------------*/ /* Merge-forward with internal buffer. */ static void ss_mergeforward(const unsigned char *T, const int *PA, int *first, int *middle, int *last, int *buf, int depth) { int *a, *b, *c, *bufend; int t; int r; bufend = buf + (middle - first) - 1; ss_blockswap(buf, first, middle - first); for(t = *(a = first), b = buf, c = middle;;) { r = ss_compare(T, PA + *b, PA + *c, depth); if(r < 0) { do { *a++ = *b; if(bufend <= b) { *bufend = t; return; } *b++ = *a; } while(*b < 0); } else if(r > 0) { do { *a++ = *c, *c++ = *a; if(last <= c) { while(b < bufend) { *a++ = *b, *b++ = *a; } *a = *b, *b = t; return; } } while(*c < 0); } else { *c = ~*c; do { *a++ = *b; if(bufend <= b) { *bufend = t; return; } *b++ = *a; } while(*b < 0); do { *a++ = *c, *c++ = *a; if(last <= c) { while(b < bufend) { *a++ = *b, *b++ = *a; } *a = *b, *b = t; return; } } while(*c < 0); } } } /* Merge-backward with internal buffer. */ static void ss_mergebackward(const unsigned char *T, const int *PA, int *first, int *middle, int *last, int *buf, int depth) { const int *p1, *p2; int *a, *b, *c, *bufend; int t; int r; int x; bufend = buf + (last - middle) - 1; ss_blockswap(buf, middle, last - middle); x = 0; if(*bufend < 0) { p1 = PA + ~*bufend; x |= 1; } else { p1 = PA + *bufend; } if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; } else { p2 = PA + *(middle - 1); } for(t = *(a = last - 1), b = bufend, c = middle - 1;;) { r = ss_compare(T, p1, p2, depth); if(0 < r) { if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } *a-- = *b; if(b <= buf) { *buf = t; break; } *b-- = *a; if(*b < 0) { p1 = PA + ~*b; x |= 1; } else { p1 = PA + *b; } } else if(r < 0) { if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } *a-- = *c, *c-- = *a; if(c < first) { while(buf < b) { *a-- = *b, *b-- = *a; } *a = *b, *b = t; break; } if(*c < 0) { p2 = PA + ~*c; x |= 2; } else { p2 = PA + *c; } } else { if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } *a-- = ~*b; if(b <= buf) { *buf = t; break; } *b-- = *a; if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } *a-- = *c, *c-- = *a; if(c < first) { while(buf < b) { *a-- = *b, *b-- = *a; } *a = *b, *b = t; break; } if(*b < 0) { p1 = PA + ~*b; x |= 1; } else { p1 = PA + *b; } if(*c < 0) { p2 = PA + ~*c; x |= 2; } else { p2 = PA + *c; } } } } /* D&C based merge. */ static void ss_swapmerge(const unsigned char *T, const int *PA, int *first, int *middle, int *last, int *buf, int bufsize, int depth) { #define STACK_SIZE SS_SMERGE_STACKSIZE #define GETIDX(a) ((0 <= (a)) ? (a) : (~(a))) #define MERGE_CHECK(a, b, c)\ do {\ if(((c) & 1) ||\ (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) {\ *(a) = ~*(a);\ }\ if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) {\ *(b) = ~*(b);\ }\ } while(0) struct { int *a, *b, *c; int d; } stack[STACK_SIZE]; int *l, *r, *lm, *rm; int m, len, half; int ssize; int check, next; for(check = 0, ssize = 0;;) { if((last - middle) <= bufsize) { if((first < middle) && (middle < last)) { ss_mergebackward(T, PA, first, middle, last, buf, depth); } MERGE_CHECK(first, last, check); STACK_POP(first, middle, last, check); continue; } if((middle - first) <= bufsize) { if(first < middle) { ss_mergeforward(T, PA, first, middle, last, buf, depth); } MERGE_CHECK(first, last, check); STACK_POP(first, middle, last, check); continue; } for(m = 0, len = MIN(middle - first, last - middle), half = len >> 1; 0 < len; len = half, half >>= 1) { if(ss_compare(T, PA + GETIDX(*(middle + m + half)), PA + GETIDX(*(middle - m - half - 1)), depth) < 0) { m += half + 1; half -= (len & 1) ^ 1; } } if(0 < m) { lm = middle - m, rm = middle + m; ss_blockswap(lm, middle, m); l = r = middle, next = 0; if(rm < last) { if(*rm < 0) { *rm = ~*rm; if(first < lm) { for(; *--l < 0;) { } next |= 4; } next |= 1; } else if(first < lm) { for(; *r < 0; ++r) { } next |= 2; } } if((l - first) <= (last - r)) { STACK_PUSH(r, rm, last, (next & 3) | (check & 4)); middle = lm, last = l, check = (check & 3) | (next & 4); } else { if((next & 2) && (r == middle)) { next ^= 6; } STACK_PUSH(first, lm, l, (check & 3) | (next & 4)); first = r, middle = rm, check = (next & 3) | (check & 4); } } else { if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) { *middle = ~*middle; } MERGE_CHECK(first, last, check); STACK_POP(first, middle, last, check); } } #undef STACK_SIZE } #endif /* SS_BLOCKSIZE != 0 */ /*---------------------------------------------------------------------------*/ /* Substring sort */ static void sssort(const unsigned char *T, const int *PA, int *first, int *last, int *buf, int bufsize, int depth, int n, int lastsuffix) { int *a; #if SS_BLOCKSIZE != 0 int *b, *middle, *curbuf; int j, k, curbufsize, limit; #endif int i; if(lastsuffix != 0) { ++first; } #if SS_BLOCKSIZE == 0 ss_mintrosort(T, PA, first, last, depth); #else if((bufsize < SS_BLOCKSIZE) && (bufsize < (last - first)) && (bufsize < (limit = ss_isqrt(last - first)))) { if(SS_BLOCKSIZE < limit) { limit = SS_BLOCKSIZE; } buf = middle = last - limit, bufsize = limit; } else { middle = last, limit = 0; } for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) { #if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth); #elif 1 < SS_BLOCKSIZE ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth); #endif curbufsize = last - (a + SS_BLOCKSIZE); curbuf = a + SS_BLOCKSIZE; if(curbufsize <= bufsize) { curbufsize = bufsize, curbuf = buf; } for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) { ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth); } } #if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE ss_mintrosort(T, PA, a, middle, depth); #elif 1 < SS_BLOCKSIZE ss_insertionsort(T, PA, a, middle, depth); #endif for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) { if(i & 1) { ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth); a -= k; } } if(limit != 0) { #if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE ss_mintrosort(T, PA, middle, last, depth); #elif 1 < SS_BLOCKSIZE ss_insertionsort(T, PA, middle, last, depth); #endif ss_inplacemerge(T, PA, first, middle, last, depth); } #endif if(lastsuffix != 0) { /* Insert last type B* suffix. */ int PAi[2]; PAi[0] = PA[*(first - 1)], PAi[1] = n - 2; for(a = first, i = *(first - 1); (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth))); ++a) { *(a - 1) = *a; } *(a - 1) = i; } } /*---------------------------------------------------------------------------*/ static INLINE int tr_ilg(int n) { return (n & 0xffff0000) ? ((n & 0xff000000) ? 24 + lg_table[(n >> 24) & 0xff] : 16 + lg_table[(n >> 16) & 0xff]) : ((n & 0x0000ff00) ? 8 + lg_table[(n >> 8) & 0xff] : 0 + lg_table[(n >> 0) & 0xff]); } /*---------------------------------------------------------------------------*/ /* Simple insertionsort for small size groups. */ static void tr_insertionsort(const int *ISAd, int *first, int *last) { int *a, *b; int t, r; for(a = first + 1; a < last; ++a) { for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) { do { *(b + 1) = *b; } while((first <= --b) && (*b < 0)); if(b < first) { break; } } if(r == 0) { *b = ~*b; } *(b + 1) = t; } } /*---------------------------------------------------------------------------*/ static INLINE void tr_fixdown(const int *ISAd, int *SA, int i, int size) { int j, k; int v; int c, d, e; for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { d = ISAd[SA[k = j++]]; if(d < (e = ISAd[SA[j]])) { k = j; d = e; } if(d <= c) { break; } } SA[i] = v; } /* Simple top-down heapsort. */ static void tr_heapsort(const int *ISAd, int *SA, int size) { int i, m; int t; m = size; if((size % 2) == 0) { m--; if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { SWAP(SA[m], SA[m / 2]); } } for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); } if((size % 2) == 0) { SWAP(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); } for(i = m - 1; 0 < i; --i) { t = SA[0], SA[0] = SA[i]; tr_fixdown(ISAd, SA, 0, i); SA[i] = t; } } /*---------------------------------------------------------------------------*/ /* Returns the median of three elements. */ static INLINE int * tr_median3(const int *ISAd, int *v1, int *v2, int *v3) { int *t; if(ISAd[*v1] > ISAd[*v2]) { SWAP(v1, v2); } if(ISAd[*v2] > ISAd[*v3]) { if(ISAd[*v1] > ISAd[*v3]) { return v1; } else { return v3; } } return v2; } /* Returns the median of five elements. */ static INLINE int * tr_median5(const int *ISAd, int *v1, int *v2, int *v3, int *v4, int *v5) { int *t; if(ISAd[*v2] > ISAd[*v3]) { SWAP(v2, v3); } if(ISAd[*v4] > ISAd[*v5]) { SWAP(v4, v5); } if(ISAd[*v2] > ISAd[*v4]) { SWAP(v2, v4); SWAP(v3, v5); } if(ISAd[*v1] > ISAd[*v3]) { SWAP(v1, v3); } if(ISAd[*v1] > ISAd[*v4]) { SWAP(v1, v4); SWAP(v3, v5); } if(ISAd[*v3] > ISAd[*v4]) { return v4; } return v3; } /* Returns the pivot element. */ static INLINE int * tr_pivot(const int *ISAd, int *first, int *last) { int *middle; int t; t = last - first; middle = first + t / 2; if(t <= 512) { if(t <= 32) { return tr_median3(ISAd, first, middle, last - 1); } else { t >>= 2; return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1); } } t >>= 3; first = tr_median3(ISAd, first, first + t, first + (t << 1)); middle = tr_median3(ISAd, middle - t, middle, middle + t); last = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1); return tr_median3(ISAd, first, middle, last); } /*---------------------------------------------------------------------------*/ typedef struct _trbudget_t trbudget_t; struct _trbudget_t { int chance; int remain; int incval; int count; }; static INLINE void trbudget_init(trbudget_t *budget, int chance, int incval) { budget->chance = chance; budget->remain = budget->incval = incval; } static INLINE int trbudget_check(trbudget_t *budget, int size) { if(size <= budget->remain) { budget->remain -= size; return 1; } if(budget->chance == 0) { budget->count += size; return 0; } budget->remain += budget->incval - size; budget->chance -= 1; return 1; } /*---------------------------------------------------------------------------*/ static INLINE void tr_partition(const int *ISAd, int *first, int *middle, int *last, int **pa, int **pb, int v) { int *a, *b, *c, *d, *e, *f; int t, s; int x = 0; for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);) { } if(((a = b) < last) && (x < v)) { for(; (++b < last) && ((x = ISAd[*b]) <= v);) { if(x == v) { SWAP(*b, *a); ++a; } } } for(c = last; (b < --c) && ((x = ISAd[*c]) == v);) { } if((b < (d = c)) && (x > v)) { for(; (b < --c) && ((x = ISAd[*c]) >= v);) { if(x == v) { SWAP(*c, *d); --d; } } } for(; b < c;) { SWAP(*b, *c); for(; (++b < c) && ((x = ISAd[*b]) <= v);) { if(x == v) { SWAP(*b, *a); ++a; } } for(; (b < --c) && ((x = ISAd[*c]) >= v);) { if(x == v) { SWAP(*c, *d); --d; } } } if(a <= d) { c = b - 1; if((s = a - first) > (t = b - a)) { s = t; } for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } if((s = d - c) > (t = last - d - 1)) { s = t; } for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } first += (b - a), last -= (d - c); } *pa = first, *pb = last; } static void tr_copy(int *ISA, const int *SA, int *first, int *a, int *b, int *last, int depth) { /* sort suffixes of middle partition by using sorted order of suffixes of left and right partition. */ int *c, *d, *e; int s, v; v = b - SA - 1; for(c = first, d = a - 1; c <= d; ++c) { if((0 <= (s = *c - depth)) && (ISA[s] == v)) { *++d = s; ISA[s] = d - SA; } } for(c = last - 1, e = d + 1, d = b; e < d; --c) { if((0 <= (s = *c - depth)) && (ISA[s] == v)) { *--d = s; ISA[s] = d - SA; } } } static void tr_partialcopy(int *ISA, const int *SA, int *first, int *a, int *b, int *last, int depth) { int *c, *d, *e; int s, v; int rank, lastrank, newrank = -1; v = b - SA - 1; lastrank = -1; for(c = first, d = a - 1; c <= d; ++c) { if((0 <= (s = *c - depth)) && (ISA[s] == v)) { *++d = s; rank = ISA[s + depth]; if(lastrank != rank) { lastrank = rank; newrank = d - SA; } ISA[s] = newrank; } } lastrank = -1; for(e = d; first <= e; --e) { rank = ISA[*e]; if(lastrank != rank) { lastrank = rank; newrank = e - SA; } if(newrank != rank) { ISA[*e] = newrank; } } lastrank = -1; for(c = last - 1, e = d + 1, d = b; e < d; --c) { if((0 <= (s = *c - depth)) && (ISA[s] == v)) { *--d = s; rank = ISA[s + depth]; if(lastrank != rank) { lastrank = rank; newrank = d - SA; } ISA[s] = newrank; } } } static void tr_introsort(int *ISA, const int *ISAd, int *SA, int *first, int *last, trbudget_t *budget) { #define STACK_SIZE TR_STACKSIZE struct { const int *a; int *b, *c; int d, e; }stack[STACK_SIZE]; int *a, *b, *c; int t; int v, x = 0; int incr = ISAd - ISA; int limit, next; int ssize, trlink = -1; for(ssize = 0, limit = tr_ilg(last - first);;) { if(limit < 0) { if(limit == -1) { /* tandem repeat partition */ tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1); /* update ranks */ if(a < last) { for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } } if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } /* push */ if(1 < (b - a)) { STACK_PUSH5(NULL, a, b, 0, 0); STACK_PUSH5(ISAd - incr, first, last, -2, trlink); trlink = ssize - 2; } if((a - first) <= (last - b)) { if(1 < (a - first)) { STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink); last = a, limit = tr_ilg(a - first); } else if(1 < (last - b)) { first = b, limit = tr_ilg(last - b); } else { STACK_POP5(ISAd, first, last, limit, trlink); } } else { if(1 < (last - b)) { STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink); first = b, limit = tr_ilg(last - b); } else if(1 < (a - first)) { last = a, limit = tr_ilg(a - first); } else { STACK_POP5(ISAd, first, last, limit, trlink); } } } else if(limit == -2) { /* tandem repeat copy */ a = stack[--ssize].b, b = stack[ssize].c; if(stack[ssize].d == 0) { tr_copy(ISA, SA, first, a, b, last, ISAd - ISA); } else { if(0 <= trlink) { stack[trlink].d = -1; } tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA); } STACK_POP5(ISAd, first, last, limit, trlink); } else { /* sorted partition */ if(0 <= *first) { a = first; do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a)); first = a; } if(first < last) { a = first; do { *a = ~*a; } while(*++a < 0); next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1; if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } } /* push */ if(trbudget_check(budget, a - first)) { if((a - first) <= (last - a)) { STACK_PUSH5(ISAd, a, last, -3, trlink); ISAd += incr, last = a, limit = next; } else { if(1 < (last - a)) { STACK_PUSH5(ISAd + incr, first, a, next, trlink); first = a, limit = -3; } else { ISAd += incr, last = a, limit = next; } } } else { if(0 <= trlink) { stack[trlink].d = -1; } if(1 < (last - a)) { first = a, limit = -3; } else { STACK_POP5(ISAd, first, last, limit, trlink); } } } else { STACK_POP5(ISAd, first, last, limit, trlink); } } continue; } if((last - first) <= TR_INSERTIONSORT_THRESHOLD) { tr_insertionsort(ISAd, first, last); limit = -3; continue; } if(limit-- == 0) { tr_heapsort(ISAd, first, last - first); for(a = last - 1; first < a; a = b) { for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; } } limit = -3; continue; } /* choose pivot */ a = tr_pivot(ISAd, first, last); SWAP(*first, *a); v = ISAd[*first]; /* partition */ tr_partition(ISAd, first, first + 1, last, &a, &b, v); if((last - first) != (b - a)) { next = (ISA[*a] != v) ? tr_ilg(b - a) : -1; /* update ranks */ for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } /* push */ if((1 < (b - a)) && (trbudget_check(budget, b - a))) { if((a - first) <= (last - b)) { if((last - b) <= (b - a)) { if(1 < (a - first)) { STACK_PUSH5(ISAd + incr, a, b, next, trlink); STACK_PUSH5(ISAd, b, last, limit, trlink); last = a; } else if(1 < (last - b)) { STACK_PUSH5(ISAd + incr, a, b, next, trlink); first = b; } else { ISAd += incr, first = a, last = b, limit = next; } } else if((a - first) <= (b - a)) { if(1 < (a - first)) { STACK_PUSH5(ISAd, b, last, limit, trlink); STACK_PUSH5(ISAd + incr, a, b, next, trlink); last = a; } else { STACK_PUSH5(ISAd, b, last, limit, trlink); ISAd += incr, first = a, last = b, limit = next; } } else { STACK_PUSH5(ISAd, b, last, limit, trlink); STACK_PUSH5(ISAd, first, a, limit, trlink); ISAd += incr, first = a, last = b, limit = next; } } else { if((a - first) <= (b - a)) { if(1 < (last - b)) { STACK_PUSH5(ISAd + incr, a, b, next, trlink); STACK_PUSH5(ISAd, first, a, limit, trlink); first = b; } else if(1 < (a - first)) { STACK_PUSH5(ISAd + incr, a, b, next, trlink); last = a; } else { ISAd += incr, first = a, last = b, limit = next; } } else if((last - b) <= (b - a)) { if(1 < (last - b)) { STACK_PUSH5(ISAd, first, a, limit, trlink); STACK_PUSH5(ISAd + incr, a, b, next, trlink); first = b; } else { STACK_PUSH5(ISAd, first, a, limit, trlink); ISAd += incr, first = a, last = b, limit = next; } } else { STACK_PUSH5(ISAd, first, a, limit, trlink); STACK_PUSH5(ISAd, b, last, limit, trlink); ISAd += incr, first = a, last = b, limit = next; } } } else { if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; } if((a - first) <= (last - b)) { if(1 < (a - first)) { STACK_PUSH5(ISAd, b, last, limit, trlink); last = a; } else if(1 < (last - b)) { first = b; } else { STACK_POP5(ISAd, first, last, limit, trlink); } } else { if(1 < (last - b)) { STACK_PUSH5(ISAd, first, a, limit, trlink); first = b; } else if(1 < (a - first)) { last = a; } else { STACK_POP5(ISAd, first, last, limit, trlink); } } } } else { if(trbudget_check(budget, last - first)) { limit = tr_ilg(last - first), ISAd += incr; } else { if(0 <= trlink) { stack[trlink].d = -1; } STACK_POP5(ISAd, first, last, limit, trlink); } } } #undef STACK_SIZE } /*---------------------------------------------------------------------------*/ /* Tandem repeat sort */ static void trsort(int *ISA, int *SA, int n, int depth) { int *ISAd; int *first, *last; trbudget_t budget; int t, skip, unsorted; trbudget_init(&budget, tr_ilg(n) * 2 / 3, n); /* trbudget_init(&budget, tr_ilg(n) * 3 / 4, n); */ for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) { first = SA; skip = 0; unsorted = 0; do { if((t = *first) < 0) { first -= t; skip += t; } else { if(skip != 0) { *(first + skip) = skip; skip = 0; } last = SA + ISA[t] + 1; if(1 < (last - first)) { budget.count = 0; tr_introsort(ISA, ISAd, SA, first, last, &budget); if(budget.count != 0) { unsorted += budget.count; } else { skip = first - last; } } else if((last - first) == 1) { skip = -1; } first = last; } } while(first < (SA + n)); if(skip != 0) { *(first + skip) = skip; } if(unsorted == 0) { break; } } } /*---------------------------------------------------------------------------*/ /* Sorts suffixes of type B*. */ static int sort_typeBstar(const unsigned char *T, int *SA, int *bucket_A, int *bucket_B, int n, int openMP) { int *PAb, *ISAb, *buf; #ifdef LIBBSC_OPENMP int *curbuf; int l; #endif int i, j, k, t, m, bufsize; int c0, c1; #ifdef LIBBSC_OPENMP int d0, d1; #endif (void)openMP; /* Initialize bucket arrays. */ for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; } for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; } /* Count the number of occurrences of the first one or two characters of each type A, B and B* suffix. Moreover, store the beginning position of all type B* suffixes into the array SA. */ for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) { /* type A suffix. */ do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1)); if(0 <= i) { /* type B* suffix. */ ++BUCKET_BSTAR(c0, c1); SA[--m] = i; /* type B suffix. */ for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { ++BUCKET_B(c0, c1); } } } m = n - m; /* note: A type B* suffix is lexicographically smaller than a type B suffix that begins with the same first two characters. */ /* Calculate the index of start/end point of each bucket. */ for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) { t = i + BUCKET_A(c0); BUCKET_A(c0) = i + j; /* start point */ i = t + BUCKET_B(c0, c0); for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) { j += BUCKET_BSTAR(c0, c1); BUCKET_BSTAR(c0, c1) = j; /* end point */ i += BUCKET_B(c0, c1); } } if(0 < m) { /* Sort the type B* suffixes by their first two characters. */ PAb = SA + n - m; ISAb = SA + m; for(i = m - 2; 0 <= i; --i) { t = PAb[i], c0 = T[t], c1 = T[t + 1]; SA[--BUCKET_BSTAR(c0, c1)] = i; } t = PAb[m - 1], c0 = T[t], c1 = T[t + 1]; SA[--BUCKET_BSTAR(c0, c1)] = m - 1; /* Sort the type B* substrings using sssort. */ #ifdef LIBBSC_OPENMP if (openMP) { buf = SA + m; c0 = ALPHABET_SIZE - 2, c1 = ALPHABET_SIZE - 1, j = m; #pragma omp parallel default(shared) private(bufsize, curbuf, k, l, d0, d1) { bufsize = (n - (2 * m)) / omp_get_num_threads(); curbuf = buf + omp_get_thread_num() * bufsize; k = 0; for(;;) { #pragma omp critical(sssort_lock) { if(0 < (l = j)) { d0 = c0, d1 = c1; do { k = BUCKET_BSTAR(d0, d1); if(--d1 <= d0) { d1 = ALPHABET_SIZE - 1; if(--d0 < 0) { break; } } } while(((l - k) <= 1) && (0 < (l = k))); c0 = d0, c1 = d1, j = k; } } if(l == 0) { break; } sssort(T, PAb, SA + k, SA + l, curbuf, bufsize, 2, n, *(SA + k) == (m - 1)); } } } else { buf = SA + m, bufsize = n - (2 * m); for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { i = BUCKET_BSTAR(c0, c1); if(1 < (j - i)) { sssort(T, PAb, SA + i, SA + j, buf, bufsize, 2, n, *(SA + i) == (m - 1)); } } } } #else buf = SA + m, bufsize = n - (2 * m); for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { i = BUCKET_BSTAR(c0, c1); if(1 < (j - i)) { sssort(T, PAb, SA + i, SA + j, buf, bufsize, 2, n, *(SA + i) == (m - 1)); } } } #endif /* Compute ranks of type B* substrings. */ for(i = m - 1; 0 <= i; --i) { if(0 <= SA[i]) { j = i; do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i])); SA[i + 1] = i - j; if(i <= 0) { break; } } j = i; do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0); ISAb[SA[i]] = j; } /* Construct the inverse suffix array of type B* suffixes using trsort. */ trsort(ISAb, SA, m, 1); /* Set the sorted order of tyoe B* suffixes. */ for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) { for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { } if(0 <= i) { t = i; for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { } SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t; } } /* Calculate the index of start/end point of each bucket. */ BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; /* end point */ for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) { i = BUCKET_A(c0 + 1) - 1; for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) { t = i - BUCKET_B(c0, c1); BUCKET_B(c0, c1) = i; /* end point */ /* Move all type B* suffixes to the correct position. */ for(i = t, j = BUCKET_BSTAR(c0, c1); j <= k; --i, --k) { SA[i] = SA[k]; } } BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; /* start point */ BUCKET_B(c0, c0) = i; /* end point */ } } return m; } /* Constructs the suffix array by using the sorted order of type B* suffixes. */ static void construct_SA(const unsigned char *T, int *SA, int *bucket_A, int *bucket_B, int n, int m) { int *i, *j, *k; int s; int c0, c1, c2; if(0 < m) { /* Construct the sorted order of type B suffixes by using the sorted order of type B* suffixes. */ for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { /* Scan the suffix array from right to left. */ for(i = SA + BUCKET_BSTAR(c1, c1 + 1), j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; i <= j; --j) { if(0 < (s = *j)) { assert(T[s] == c1); assert(((s + 1) < n) && (T[s] <= T[s + 1])); assert(T[s - 1] <= T[s]); *j = ~s; c0 = T[--s]; if((0 < s) && (T[s - 1] > c0)) { s = ~s; } if(c0 != c2) { if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } k = SA + BUCKET_B(c2 = c0, c1); } assert(k < j); assert(k != NULL); *k-- = s; } else { assert(((s == 0) && (T[s] == c1)) || (s < 0)); *j = ~s; } } } } /* Construct the suffix array by using the sorted order of type B suffixes. */ k = SA + BUCKET_A(c2 = T[n - 1]); *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1); /* Scan the suffix array from left to right. */ for(i = SA, j = SA + n; i < j; ++i) { if(0 < (s = *i)) { assert(T[s - 1] >= T[s]); c0 = T[--s]; if((s == 0) || (T[s - 1] < c0)) { s = ~s; } if(c0 != c2) { BUCKET_A(c2) = k - SA; k = SA + BUCKET_A(c2 = c0); } assert(i < k); *k++ = s; } else { assert(s < 0); *i = ~s; } } } /* Constructs the burrows-wheeler transformed string directly by using the sorted order of type B* suffixes. */ static int construct_BWT(const unsigned char *T, int *SA, int *bucket_A, int *bucket_B, int n, int m) { int *i, *j, *k, *orig; int s; int c0, c1, c2; if(0 < m) { /* Construct the sorted order of type B suffixes by using the sorted order of type B* suffixes. */ for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { /* Scan the suffix array from right to left. */ for(i = SA + BUCKET_BSTAR(c1, c1 + 1), j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; i <= j; --j) { if(0 < (s = *j)) { assert(T[s] == c1); assert(((s + 1) < n) && (T[s] <= T[s + 1])); assert(T[s - 1] <= T[s]); c0 = T[--s]; *j = ~((int)c0); if((0 < s) && (T[s - 1] > c0)) { s = ~s; } if(c0 != c2) { if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } k = SA + BUCKET_B(c2 = c0, c1); } assert(k < j); assert(k != NULL); *k-- = s; } else if(s != 0) { *j = ~s; #ifndef NDEBUG } else { assert(T[s] == c1); #endif } } } } /* Construct the BWTed string by using the sorted order of type B suffixes. */ k = SA + BUCKET_A(c2 = T[n - 1]); *k++ = (T[n - 2] < c2) ? ~((int)T[n - 2]) : (n - 1); /* Scan the suffix array from left to right. */ for(i = SA, j = SA + n, orig = SA; i < j; ++i) { if(0 < (s = *i)) { assert(T[s - 1] >= T[s]); c0 = T[--s]; *i = c0; if((0 < s) && (T[s - 1] < c0)) { s = ~((int)T[s - 1]); } if(c0 != c2) { BUCKET_A(c2) = k - SA; k = SA + BUCKET_A(c2 = c0); } assert(i < k); *k++ = s; } else if(s != 0) { *i = ~s; } else { orig = i; } } return orig - SA; } /* Constructs the burrows-wheeler transformed string directly by using the sorted order of type B* suffixes. */ static int construct_BWT_indexes(const unsigned char *T, int *SA, int *bucket_A, int *bucket_B, int n, int m, unsigned char * num_indexes, int * indexes) { int *i, *j, *k, *orig; int s; int c0, c1, c2; int mod = n / 8; { mod |= mod >> 1; mod |= mod >> 2; mod |= mod >> 4; mod |= mod >> 8; mod |= mod >> 16; mod >>= 1; *num_indexes = (unsigned char)((n - 1) / (mod + 1)); } if(0 < m) { /* Construct the sorted order of type B suffixes by using the sorted order of type B* suffixes. */ for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { /* Scan the suffix array from right to left. */ for(i = SA + BUCKET_BSTAR(c1, c1 + 1), j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; i <= j; --j) { if(0 < (s = *j)) { assert(T[s] == c1); assert(((s + 1) < n) && (T[s] <= T[s + 1])); assert(T[s - 1] <= T[s]); if ((s & mod) == 0) indexes[s / (mod + 1) - 1] = j - SA; c0 = T[--s]; *j = ~((int)c0); if((0 < s) && (T[s - 1] > c0)) { s = ~s; } if(c0 != c2) { if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } k = SA + BUCKET_B(c2 = c0, c1); } assert(k < j); assert(k != NULL); *k-- = s; } else if(s != 0) { *j = ~s; #ifndef NDEBUG } else { assert(T[s] == c1); #endif } } } } /* Construct the BWTed string by using the sorted order of type B suffixes. */ k = SA + BUCKET_A(c2 = T[n - 1]); if (T[n - 2] < c2) { if (((n - 1) & mod) == 0) indexes[(n - 1) / (mod + 1) - 1] = k - SA; *k++ = ~((int)T[n - 2]); } else { *k++ = n - 1; } /* Scan the suffix array from left to right. */ for(i = SA, j = SA + n, orig = SA; i < j; ++i) { if(0 < (s = *i)) { assert(T[s - 1] >= T[s]); if ((s & mod) == 0) indexes[s / (mod + 1) - 1] = i - SA; c0 = T[--s]; *i = c0; if(c0 != c2) { BUCKET_A(c2) = k - SA; k = SA + BUCKET_A(c2 = c0); } assert(i < k); if((0 < s) && (T[s - 1] < c0)) { if ((s & mod) == 0) indexes[s / (mod + 1) - 1] = k - SA; *k++ = ~((int)T[s - 1]); } else *k++ = s; } else if(s != 0) { *i = ~s; } else { orig = i; } } return orig - SA; } /*---------------------------------------------------------------------------*/ /*- Function -*/ int divsufsort(const unsigned char *T, int *SA, int n, int openMP) { int *bucket_A, *bucket_B; int m; int err = 0; /* Check arguments. */ if((T == NULL) || (SA == NULL) || (n < 0)) { return -1; } else if(n == 0) { return 0; } else if(n == 1) { SA[0] = 0; return 0; } else if(n == 2) { m = (T[0] < T[1]); SA[m ^ 1] = 0, SA[m] = 1; return 0; } bucket_A = (int *)malloc(BUCKET_A_SIZE * sizeof(int)); bucket_B = (int *)malloc(BUCKET_B_SIZE * sizeof(int)); /* Suffixsort. */ if((bucket_A != NULL) && (bucket_B != NULL)) { m = sort_typeBstar(T, SA, bucket_A, bucket_B, n, openMP); construct_SA(T, SA, bucket_A, bucket_B, n, m); } else { err = -2; } free(bucket_B); free(bucket_A); return err; } int divbwt(const unsigned char *T, unsigned char *U, int *A, int n, unsigned char * num_indexes, int * indexes, int openMP) { int *B; int *bucket_A, *bucket_B; int m, pidx, i; /* Check arguments. */ if((T == NULL) || (U == NULL) || (n < 0)) { return -1; } else if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; } if((B = A) == NULL) { B = (int *)malloc((size_t)(n + 1) * sizeof(int)); } bucket_A = (int *)malloc(BUCKET_A_SIZE * sizeof(int)); bucket_B = (int *)malloc(BUCKET_B_SIZE * sizeof(int)); /* Burrows-Wheeler Transform. */ if((B != NULL) && (bucket_A != NULL) && (bucket_B != NULL)) { m = sort_typeBstar(T, B, bucket_A, bucket_B, n, openMP); if (num_indexes == NULL || indexes == NULL) { pidx = construct_BWT(T, B, bucket_A, bucket_B, n, m); } else { pidx = construct_BWT_indexes(T, B, bucket_A, bucket_B, n, m, num_indexes, indexes); } /* Copy to output string. */ U[0] = T[n - 1]; for(i = 0; i < pidx; ++i) { U[i + 1] = (unsigned char)B[i]; } for(i += 1; i < n; ++i) { U[i] = (unsigned char)B[i]; } pidx += 1; } else { pidx = -2; } free(bucket_B); free(bucket_A); if(A == NULL) { free(B); } return pidx; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/dictBuilder/divsufsort.h0000644000076500000240000000456314601576577024506 0ustar00twstaff/* * divsufsort.h for libdivsufsort-lite * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _DIVSUFSORT_H #define _DIVSUFSORT_H 1 #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /*- Prototypes -*/ /** * Constructs the suffix array of a given string. * @param T [0..n-1] The input string. * @param SA [0..n-1] The output array of suffixes. * @param n The length of the given string. * @param openMP enables OpenMP optimization. * @return 0 if no error occurred, -1 or -2 otherwise. */ int divsufsort(const unsigned char *T, int *SA, int n, int openMP); /** * Constructs the burrows-wheeler transformed string of a given string. * @param T [0..n-1] The input string. * @param U [0..n-1] The output string. (can be T) * @param A [0..n-1] The temporary array. (can be NULL) * @param n The length of the given string. * @param num_indexes The length of secondary indexes array. (can be NULL) * @param indexes The secondary indexes array. (can be NULL) * @param openMP enables OpenMP optimization. * @return The primary index if no error occurred, -1 or -2 otherwise. */ int divbwt(const unsigned char *T, unsigned char *U, int *A, int n, unsigned char * num_indexes, int * indexes, int openMP); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* _DIVSUFSORT_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/dictBuilder/fastcover.c0000644000076500000240000006746014601576577024272 0ustar00twstaff/* * Copyright (c) 2018-2020, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /*-************************************* * Dependencies ***************************************/ #include /* fprintf */ #include /* malloc, free, qsort */ #include /* memset */ #include /* clock */ #include "../common/mem.h" /* read */ #include "../common/pool.h" #include "../common/threading.h" #include "cover.h" #include "../common/zstd_internal.h" /* includes zstd.h */ #ifndef ZDICT_STATIC_LINKING_ONLY #define ZDICT_STATIC_LINKING_ONLY #endif #include "zdict.h" /*-************************************* * Constants ***************************************/ #define FASTCOVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB)) #define FASTCOVER_MAX_F 31 #define FASTCOVER_MAX_ACCEL 10 #define DEFAULT_SPLITPOINT 0.75 #define DEFAULT_F 20 #define DEFAULT_ACCEL 1 /*-************************************* * Console display ***************************************/ static int g_displayLevel = 2; #define DISPLAY(...) \ { \ fprintf(stderr, __VA_ARGS__); \ fflush(stderr); \ } #define LOCALDISPLAYLEVEL(displayLevel, l, ...) \ if (displayLevel >= l) { \ DISPLAY(__VA_ARGS__); \ } /* 0 : no display; 1: errors; 2: default; 3: details; 4: debug */ #define DISPLAYLEVEL(l, ...) LOCALDISPLAYLEVEL(g_displayLevel, l, __VA_ARGS__) #define LOCALDISPLAYUPDATE(displayLevel, l, ...) \ if (displayLevel >= l) { \ if ((clock() - g_time > refreshRate) || (displayLevel >= 4)) { \ g_time = clock(); \ DISPLAY(__VA_ARGS__); \ } \ } #define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__) static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; static clock_t g_time = 0; /*-************************************* * Hash Functions ***************************************/ static const U64 prime6bytes = 227718039650203ULL; static size_t ZSTD_hash6(U64 u, U32 h) { return (size_t)(((u << (64-48)) * prime6bytes) >> (64-h)) ; } static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h); } static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL; static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; } static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); } /** * Hash the d-byte value pointed to by p and mod 2^f */ static size_t FASTCOVER_hashPtrToIndex(const void* p, U32 h, unsigned d) { if (d == 6) { return ZSTD_hash6Ptr(p, h) & ((1 << h) - 1); } return ZSTD_hash8Ptr(p, h) & ((1 << h) - 1); } /*-************************************* * Acceleration ***************************************/ typedef struct { unsigned finalize; /* Percentage of training samples used for ZDICT_finalizeDictionary */ unsigned skip; /* Number of dmer skipped between each dmer counted in computeFrequency */ } FASTCOVER_accel_t; static const FASTCOVER_accel_t FASTCOVER_defaultAccelParameters[FASTCOVER_MAX_ACCEL+1] = { { 100, 0 }, /* accel = 0, should not happen because accel = 0 defaults to accel = 1 */ { 100, 0 }, /* accel = 1 */ { 50, 1 }, /* accel = 2 */ { 34, 2 }, /* accel = 3 */ { 25, 3 }, /* accel = 4 */ { 20, 4 }, /* accel = 5 */ { 17, 5 }, /* accel = 6 */ { 14, 6 }, /* accel = 7 */ { 13, 7 }, /* accel = 8 */ { 11, 8 }, /* accel = 9 */ { 10, 9 }, /* accel = 10 */ }; /*-************************************* * Context ***************************************/ typedef struct { const BYTE *samples; size_t *offsets; const size_t *samplesSizes; size_t nbSamples; size_t nbTrainSamples; size_t nbTestSamples; size_t nbDmers; U32 *freqs; unsigned d; unsigned f; FASTCOVER_accel_t accelParams; } FASTCOVER_ctx_t; /*-************************************* * Helper functions ***************************************/ /** * Selects the best segment in an epoch. * Segments of are scored according to the function: * * Let F(d) be the frequency of all dmers with hash value d. * Let S_i be hash value of the dmer at position i of segment S which has length k. * * Score(S) = F(S_1) + F(S_2) + ... + F(S_{k-d+1}) * * Once the dmer with hash value d is in the dictionary we set F(d) = 0. */ static COVER_segment_t FASTCOVER_selectSegment(const FASTCOVER_ctx_t *ctx, U32 *freqs, U32 begin, U32 end, ZDICT_cover_params_t parameters, U16* segmentFreqs) { /* Constants */ const U32 k = parameters.k; const U32 d = parameters.d; const U32 f = ctx->f; const U32 dmersInK = k - d + 1; /* Try each segment (activeSegment) and save the best (bestSegment) */ COVER_segment_t bestSegment = {0, 0, 0}; COVER_segment_t activeSegment; /* Reset the activeDmers in the segment */ /* The activeSegment starts at the beginning of the epoch. */ activeSegment.begin = begin; activeSegment.end = begin; activeSegment.score = 0; /* Slide the activeSegment through the whole epoch. * Save the best segment in bestSegment. */ while (activeSegment.end < end) { /* Get hash value of current dmer */ const size_t idx = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.end, f, d); /* Add frequency of this index to score if this is the first occurrence of index in active segment */ if (segmentFreqs[idx] == 0) { activeSegment.score += freqs[idx]; } /* Increment end of segment and segmentFreqs*/ activeSegment.end += 1; segmentFreqs[idx] += 1; /* If the window is now too large, drop the first position */ if (activeSegment.end - activeSegment.begin == dmersInK + 1) { /* Get hash value of the dmer to be eliminated from active segment */ const size_t delIndex = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.begin, f, d); segmentFreqs[delIndex] -= 1; /* Subtract frequency of this index from score if this is the last occurrence of this index in active segment */ if (segmentFreqs[delIndex] == 0) { activeSegment.score -= freqs[delIndex]; } /* Increment start of segment */ activeSegment.begin += 1; } /* If this segment is the best so far save it */ if (activeSegment.score > bestSegment.score) { bestSegment = activeSegment; } } /* Zero out rest of segmentFreqs array */ while (activeSegment.begin < end) { const size_t delIndex = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.begin, f, d); segmentFreqs[delIndex] -= 1; activeSegment.begin += 1; } { /* Zero the frequency of hash value of each dmer covered by the chosen segment. */ U32 pos; for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) { const size_t i = FASTCOVER_hashPtrToIndex(ctx->samples + pos, f, d); freqs[i] = 0; } } return bestSegment; } static int FASTCOVER_checkParameters(ZDICT_cover_params_t parameters, size_t maxDictSize, unsigned f, unsigned accel) { /* k, d, and f are required parameters */ if (parameters.d == 0 || parameters.k == 0) { return 0; } /* d has to be 6 or 8 */ if (parameters.d != 6 && parameters.d != 8) { return 0; } /* k <= maxDictSize */ if (parameters.k > maxDictSize) { return 0; } /* d <= k */ if (parameters.d > parameters.k) { return 0; } /* 0 < f <= FASTCOVER_MAX_F*/ if (f > FASTCOVER_MAX_F || f == 0) { return 0; } /* 0 < splitPoint <= 1 */ if (parameters.splitPoint <= 0 || parameters.splitPoint > 1) { return 0; } /* 0 < accel <= 10 */ if (accel > 10 || accel == 0) { return 0; } return 1; } /** * Clean up a context initialized with `FASTCOVER_ctx_init()`. */ static void FASTCOVER_ctx_destroy(FASTCOVER_ctx_t* ctx) { if (!ctx) return; free(ctx->freqs); ctx->freqs = NULL; free(ctx->offsets); ctx->offsets = NULL; } /** * Calculate for frequency of hash value of each dmer in ctx->samples */ static void FASTCOVER_computeFrequency(U32* freqs, const FASTCOVER_ctx_t* ctx) { const unsigned f = ctx->f; const unsigned d = ctx->d; const unsigned skip = ctx->accelParams.skip; const unsigned readLength = MAX(d, 8); size_t i; assert(ctx->nbTrainSamples >= 5); assert(ctx->nbTrainSamples <= ctx->nbSamples); for (i = 0; i < ctx->nbTrainSamples; i++) { size_t start = ctx->offsets[i]; /* start of current dmer */ size_t const currSampleEnd = ctx->offsets[i+1]; while (start + readLength <= currSampleEnd) { const size_t dmerIndex = FASTCOVER_hashPtrToIndex(ctx->samples + start, f, d); freqs[dmerIndex]++; start = start + skip + 1; } } } /** * Prepare a context for dictionary building. * The context is only dependent on the parameter `d` and can used multiple * times. * Returns 0 on success or error code on error. * The context must be destroyed with `FASTCOVER_ctx_destroy()`. */ static size_t FASTCOVER_ctx_init(FASTCOVER_ctx_t* ctx, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, unsigned d, double splitPoint, unsigned f, FASTCOVER_accel_t accelParams) { const BYTE* const samples = (const BYTE*)samplesBuffer; const size_t totalSamplesSize = COVER_sum(samplesSizes, nbSamples); /* Split samples into testing and training sets */ const unsigned nbTrainSamples = splitPoint < 1.0 ? (unsigned)((double)nbSamples * splitPoint) : nbSamples; const unsigned nbTestSamples = splitPoint < 1.0 ? nbSamples - nbTrainSamples : nbSamples; const size_t trainingSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes, nbTrainSamples) : totalSamplesSize; const size_t testSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes + nbTrainSamples, nbTestSamples) : totalSamplesSize; /* Checks */ if (totalSamplesSize < MAX(d, sizeof(U64)) || totalSamplesSize >= (size_t)FASTCOVER_MAX_SAMPLES_SIZE) { DISPLAYLEVEL(1, "Total samples size is too large (%u MB), maximum size is %u MB\n", (unsigned)(totalSamplesSize >> 20), (FASTCOVER_MAX_SAMPLES_SIZE >> 20)); return ERROR(srcSize_wrong); } /* Check if there are at least 5 training samples */ if (nbTrainSamples < 5) { DISPLAYLEVEL(1, "Total number of training samples is %u and is invalid\n", nbTrainSamples); return ERROR(srcSize_wrong); } /* Check if there's testing sample */ if (nbTestSamples < 1) { DISPLAYLEVEL(1, "Total number of testing samples is %u and is invalid.\n", nbTestSamples); return ERROR(srcSize_wrong); } /* Zero the context */ memset(ctx, 0, sizeof(*ctx)); DISPLAYLEVEL(2, "Training on %u samples of total size %u\n", nbTrainSamples, (unsigned)trainingSamplesSize); DISPLAYLEVEL(2, "Testing on %u samples of total size %u\n", nbTestSamples, (unsigned)testSamplesSize); ctx->samples = samples; ctx->samplesSizes = samplesSizes; ctx->nbSamples = nbSamples; ctx->nbTrainSamples = nbTrainSamples; ctx->nbTestSamples = nbTestSamples; ctx->nbDmers = trainingSamplesSize - MAX(d, sizeof(U64)) + 1; ctx->d = d; ctx->f = f; ctx->accelParams = accelParams; /* The offsets of each file */ ctx->offsets = (size_t*)calloc((nbSamples + 1), sizeof(size_t)); if (ctx->offsets == NULL) { DISPLAYLEVEL(1, "Failed to allocate scratch buffers \n"); FASTCOVER_ctx_destroy(ctx); return ERROR(memory_allocation); } /* Fill offsets from the samplesSizes */ { U32 i; ctx->offsets[0] = 0; assert(nbSamples >= 5); for (i = 1; i <= nbSamples; ++i) { ctx->offsets[i] = ctx->offsets[i - 1] + samplesSizes[i - 1]; } } /* Initialize frequency array of size 2^f */ ctx->freqs = (U32*)calloc(((U64)1 << f), sizeof(U32)); if (ctx->freqs == NULL) { DISPLAYLEVEL(1, "Failed to allocate frequency table \n"); FASTCOVER_ctx_destroy(ctx); return ERROR(memory_allocation); } DISPLAYLEVEL(2, "Computing frequencies\n"); FASTCOVER_computeFrequency(ctx->freqs, ctx); return 0; } /** * Given the prepared context build the dictionary. */ static size_t FASTCOVER_buildDictionary(const FASTCOVER_ctx_t* ctx, U32* freqs, void* dictBuffer, size_t dictBufferCapacity, ZDICT_cover_params_t parameters, U16* segmentFreqs) { BYTE *const dict = (BYTE *)dictBuffer; size_t tail = dictBufferCapacity; /* Divide the data into epochs. We will select one segment from each epoch. */ const COVER_epoch_info_t epochs = COVER_computeEpochs( (U32)dictBufferCapacity, (U32)ctx->nbDmers, parameters.k, 1); const size_t maxZeroScoreRun = 10; size_t zeroScoreRun = 0; size_t epoch; DISPLAYLEVEL(2, "Breaking content into %u epochs of size %u\n", (U32)epochs.num, (U32)epochs.size); /* Loop through the epochs until there are no more segments or the dictionary * is full. */ for (epoch = 0; tail > 0; epoch = (epoch + 1) % epochs.num) { const U32 epochBegin = (U32)(epoch * epochs.size); const U32 epochEnd = epochBegin + epochs.size; size_t segmentSize; /* Select a segment */ COVER_segment_t segment = FASTCOVER_selectSegment( ctx, freqs, epochBegin, epochEnd, parameters, segmentFreqs); /* If the segment covers no dmers, then we are out of content. * There may be new content in other epochs, for continue for some time. */ if (segment.score == 0) { if (++zeroScoreRun >= maxZeroScoreRun) { break; } continue; } zeroScoreRun = 0; /* Trim the segment if necessary and if it is too small then we are done */ segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail); if (segmentSize < parameters.d) { break; } /* We fill the dictionary from the back to allow the best segments to be * referenced with the smallest offsets. */ tail -= segmentSize; memcpy(dict + tail, ctx->samples + segment.begin, segmentSize); DISPLAYUPDATE( 2, "\r%u%% ", (unsigned)(((dictBufferCapacity - tail) * 100) / dictBufferCapacity)); } DISPLAYLEVEL(2, "\r%79s\r", ""); return tail; } /** * Parameters for FASTCOVER_tryParameters(). */ typedef struct FASTCOVER_tryParameters_data_s { const FASTCOVER_ctx_t* ctx; COVER_best_t* best; size_t dictBufferCapacity; ZDICT_cover_params_t parameters; } FASTCOVER_tryParameters_data_t; /** * Tries a set of parameters and updates the COVER_best_t with the results. * This function is thread safe if zstd is compiled with multithreaded support. * It takes its parameters as an *OWNING* opaque pointer to support threading. */ static void FASTCOVER_tryParameters(void *opaque) { /* Save parameters as local variables */ FASTCOVER_tryParameters_data_t *const data = (FASTCOVER_tryParameters_data_t *)opaque; const FASTCOVER_ctx_t *const ctx = data->ctx; const ZDICT_cover_params_t parameters = data->parameters; size_t dictBufferCapacity = data->dictBufferCapacity; size_t totalCompressedSize = ERROR(GENERIC); /* Initialize array to keep track of frequency of dmer within activeSegment */ U16* segmentFreqs = (U16 *)calloc(((U64)1 << ctx->f), sizeof(U16)); /* Allocate space for hash table, dict, and freqs */ BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity); COVER_dictSelection_t selection = COVER_dictSelectionError(ERROR(GENERIC)); U32 *freqs = (U32*) malloc(((U64)1 << ctx->f) * sizeof(U32)); if (!segmentFreqs || !dict || !freqs) { DISPLAYLEVEL(1, "Failed to allocate buffers: out of memory\n"); goto _cleanup; } /* Copy the frequencies because we need to modify them */ memcpy(freqs, ctx->freqs, ((U64)1 << ctx->f) * sizeof(U32)); /* Build the dictionary */ { const size_t tail = FASTCOVER_buildDictionary(ctx, freqs, dict, dictBufferCapacity, parameters, segmentFreqs); const unsigned nbFinalizeSamples = (unsigned)(ctx->nbTrainSamples * ctx->accelParams.finalize / 100); selection = COVER_selectDict(dict + tail, dictBufferCapacity - tail, ctx->samples, ctx->samplesSizes, nbFinalizeSamples, ctx->nbTrainSamples, ctx->nbSamples, parameters, ctx->offsets, totalCompressedSize); if (COVER_dictSelectionIsError(selection)) { DISPLAYLEVEL(1, "Failed to select dictionary\n"); goto _cleanup; } } _cleanup: free(dict); COVER_best_finish(data->best, parameters, selection); free(data); free(segmentFreqs); COVER_dictSelectionFree(selection); free(freqs); } static void FASTCOVER_convertToCoverParams(ZDICT_fastCover_params_t fastCoverParams, ZDICT_cover_params_t* coverParams) { coverParams->k = fastCoverParams.k; coverParams->d = fastCoverParams.d; coverParams->steps = fastCoverParams.steps; coverParams->nbThreads = fastCoverParams.nbThreads; coverParams->splitPoint = fastCoverParams.splitPoint; coverParams->zParams = fastCoverParams.zParams; coverParams->shrinkDict = fastCoverParams.shrinkDict; } static void FASTCOVER_convertToFastCoverParams(ZDICT_cover_params_t coverParams, ZDICT_fastCover_params_t* fastCoverParams, unsigned f, unsigned accel) { fastCoverParams->k = coverParams.k; fastCoverParams->d = coverParams.d; fastCoverParams->steps = coverParams.steps; fastCoverParams->nbThreads = coverParams.nbThreads; fastCoverParams->splitPoint = coverParams.splitPoint; fastCoverParams->f = f; fastCoverParams->accel = accel; fastCoverParams->zParams = coverParams.zParams; fastCoverParams->shrinkDict = coverParams.shrinkDict; } ZDICTLIB_API size_t ZDICT_trainFromBuffer_fastCover(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_fastCover_params_t parameters) { BYTE* const dict = (BYTE*)dictBuffer; FASTCOVER_ctx_t ctx; ZDICT_cover_params_t coverParams; FASTCOVER_accel_t accelParams; /* Initialize global data */ g_displayLevel = parameters.zParams.notificationLevel; /* Assign splitPoint and f if not provided */ parameters.splitPoint = 1.0; parameters.f = parameters.f == 0 ? DEFAULT_F : parameters.f; parameters.accel = parameters.accel == 0 ? DEFAULT_ACCEL : parameters.accel; /* Convert to cover parameter */ memset(&coverParams, 0 , sizeof(coverParams)); FASTCOVER_convertToCoverParams(parameters, &coverParams); /* Checks */ if (!FASTCOVER_checkParameters(coverParams, dictBufferCapacity, parameters.f, parameters.accel)) { DISPLAYLEVEL(1, "FASTCOVER parameters incorrect\n"); return ERROR(parameter_outOfBound); } if (nbSamples == 0) { DISPLAYLEVEL(1, "FASTCOVER must have at least one input file\n"); return ERROR(srcSize_wrong); } if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n", ZDICT_DICTSIZE_MIN); return ERROR(dstSize_tooSmall); } /* Assign corresponding FASTCOVER_accel_t to accelParams*/ accelParams = FASTCOVER_defaultAccelParameters[parameters.accel]; /* Initialize context */ { size_t const initVal = FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, coverParams.d, parameters.splitPoint, parameters.f, accelParams); if (ZSTD_isError(initVal)) { DISPLAYLEVEL(1, "Failed to initialize context\n"); return initVal; } } COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.nbDmers, g_displayLevel); /* Build the dictionary */ DISPLAYLEVEL(2, "Building dictionary\n"); { /* Initialize array to keep track of frequency of dmer within activeSegment */ U16* segmentFreqs = (U16 *)calloc(((U64)1 << parameters.f), sizeof(U16)); const size_t tail = FASTCOVER_buildDictionary(&ctx, ctx.freqs, dictBuffer, dictBufferCapacity, coverParams, segmentFreqs); const unsigned nbFinalizeSamples = (unsigned)(ctx.nbTrainSamples * ctx.accelParams.finalize / 100); const size_t dictionarySize = ZDICT_finalizeDictionary( dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail, samplesBuffer, samplesSizes, nbFinalizeSamples, coverParams.zParams); if (!ZSTD_isError(dictionarySize)) { DISPLAYLEVEL(2, "Constructed dictionary of size %u\n", (unsigned)dictionarySize); } FASTCOVER_ctx_destroy(&ctx); free(segmentFreqs); return dictionarySize; } } ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_fastCover( void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_fastCover_params_t* parameters) { ZDICT_cover_params_t coverParams; FASTCOVER_accel_t accelParams; /* constants */ const unsigned nbThreads = parameters->nbThreads; const double splitPoint = parameters->splitPoint <= 0.0 ? DEFAULT_SPLITPOINT : parameters->splitPoint; const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d; const unsigned kMaxD = parameters->d == 0 ? 8 : parameters->d; const unsigned kMinK = parameters->k == 0 ? 50 : parameters->k; const unsigned kMaxK = parameters->k == 0 ? 2000 : parameters->k; const unsigned kSteps = parameters->steps == 0 ? 40 : parameters->steps; const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1); const unsigned kIterations = (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize); const unsigned f = parameters->f == 0 ? DEFAULT_F : parameters->f; const unsigned accel = parameters->accel == 0 ? DEFAULT_ACCEL : parameters->accel; const unsigned shrinkDict = 0; /* Local variables */ const int displayLevel = parameters->zParams.notificationLevel; unsigned iteration = 1; unsigned d; unsigned k; COVER_best_t best; POOL_ctx *pool = NULL; int warned = 0; /* Checks */ if (splitPoint <= 0 || splitPoint > 1) { LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect splitPoint\n"); return ERROR(parameter_outOfBound); } if (accel == 0 || accel > FASTCOVER_MAX_ACCEL) { LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect accel\n"); return ERROR(parameter_outOfBound); } if (kMinK < kMaxD || kMaxK < kMinK) { LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect k\n"); return ERROR(parameter_outOfBound); } if (nbSamples == 0) { LOCALDISPLAYLEVEL(displayLevel, 1, "FASTCOVER must have at least one input file\n"); return ERROR(srcSize_wrong); } if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { LOCALDISPLAYLEVEL(displayLevel, 1, "dictBufferCapacity must be at least %u\n", ZDICT_DICTSIZE_MIN); return ERROR(dstSize_tooSmall); } if (nbThreads > 1) { pool = POOL_create(nbThreads, 1); if (!pool) { return ERROR(memory_allocation); } } /* Initialization */ COVER_best_init(&best); memset(&coverParams, 0 , sizeof(coverParams)); FASTCOVER_convertToCoverParams(*parameters, &coverParams); accelParams = FASTCOVER_defaultAccelParameters[accel]; /* Turn down global display level to clean up display at level 2 and below */ g_displayLevel = displayLevel == 0 ? 0 : displayLevel - 1; /* Loop through d first because each new value needs a new context */ LOCALDISPLAYLEVEL(displayLevel, 2, "Trying %u different sets of parameters\n", kIterations); for (d = kMinD; d <= kMaxD; d += 2) { /* Initialize the context for this value of d */ FASTCOVER_ctx_t ctx; LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d); { size_t const initVal = FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d, splitPoint, f, accelParams); if (ZSTD_isError(initVal)) { LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n"); COVER_best_destroy(&best); POOL_free(pool); return initVal; } } if (!warned) { COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.nbDmers, displayLevel); warned = 1; } /* Loop through k reusing the same context */ for (k = kMinK; k <= kMaxK; k += kStepSize) { /* Prepare the arguments */ FASTCOVER_tryParameters_data_t *data = (FASTCOVER_tryParameters_data_t *)malloc( sizeof(FASTCOVER_tryParameters_data_t)); LOCALDISPLAYLEVEL(displayLevel, 3, "k=%u\n", k); if (!data) { LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to allocate parameters\n"); COVER_best_destroy(&best); FASTCOVER_ctx_destroy(&ctx); POOL_free(pool); return ERROR(memory_allocation); } data->ctx = &ctx; data->best = &best; data->dictBufferCapacity = dictBufferCapacity; data->parameters = coverParams; data->parameters.k = k; data->parameters.d = d; data->parameters.splitPoint = splitPoint; data->parameters.steps = kSteps; data->parameters.shrinkDict = shrinkDict; data->parameters.zParams.notificationLevel = g_displayLevel; /* Check the parameters */ if (!FASTCOVER_checkParameters(data->parameters, dictBufferCapacity, data->ctx->f, accel)) { DISPLAYLEVEL(1, "FASTCOVER parameters incorrect\n"); free(data); continue; } /* Call the function and pass ownership of data to it */ COVER_best_start(&best); if (pool) { POOL_add(pool, &FASTCOVER_tryParameters, data); } else { FASTCOVER_tryParameters(data); } /* Print status */ LOCALDISPLAYUPDATE(displayLevel, 2, "\r%u%% ", (unsigned)((iteration * 100) / kIterations)); ++iteration; } COVER_best_wait(&best); FASTCOVER_ctx_destroy(&ctx); } LOCALDISPLAYLEVEL(displayLevel, 2, "\r%79s\r", ""); /* Fill the output buffer and parameters with output of the best parameters */ { const size_t dictSize = best.dictSize; if (ZSTD_isError(best.compressedSize)) { const size_t compressedSize = best.compressedSize; COVER_best_destroy(&best); POOL_free(pool); return compressedSize; } FASTCOVER_convertToFastCoverParams(best.parameters, parameters, f, accel); memcpy(dictBuffer, best.dict, dictSize); COVER_best_destroy(&best); POOL_free(pool); return dictSize; } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/dictBuilder/zdict.c0000644000076500000240000012734114601576577023406 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /*-************************************** * Tuning parameters ****************************************/ #define MINRATIO 4 /* minimum nb of apparition to be selected in dictionary */ #define ZDICT_MAX_SAMPLES_SIZE (2000U << 20) #define ZDICT_MIN_SAMPLES_SIZE (ZDICT_CONTENTSIZE_MIN * MINRATIO) /*-************************************** * Compiler Options ****************************************/ /* Unix Large Files support (>4GB) */ #define _FILE_OFFSET_BITS 64 #if (defined(__sun__) && (!defined(__LP64__))) /* Sun Solaris 32-bits requires specific definitions */ # define _LARGEFILE_SOURCE #elif ! defined(__LP64__) /* No point defining Large file for 64 bit */ # define _LARGEFILE64_SOURCE #endif /*-************************************* * Dependencies ***************************************/ #include /* malloc, free */ #include /* memset */ #include /* fprintf, fopen, ftello64 */ #include /* clock */ #include "../common/mem.h" /* read */ #include "../common/fse.h" /* FSE_normalizeCount, FSE_writeNCount */ #define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" /* HUF_buildCTable, HUF_writeCTable */ #include "../common/zstd_internal.h" /* includes zstd.h */ #include "../common/xxhash.h" /* XXH64 */ #include "divsufsort.h" #ifndef ZDICT_STATIC_LINKING_ONLY # define ZDICT_STATIC_LINKING_ONLY #endif #include "zdict.h" #include "../compress/zstd_compress_internal.h" /* ZSTD_loadCEntropy() */ /*-************************************* * Constants ***************************************/ #define KB *(1 <<10) #define MB *(1 <<20) #define GB *(1U<<30) #define DICTLISTSIZE_DEFAULT 10000 #define NOISELENGTH 32 static const int g_compressionLevel_default = 3; static const U32 g_selectivity_default = 9; /*-************************************* * Console display ***************************************/ #define DISPLAY(...) { fprintf(stderr, __VA_ARGS__); fflush( stderr ); } #define DISPLAYLEVEL(l, ...) if (notificationLevel>=l) { DISPLAY(__VA_ARGS__); } /* 0 : no display; 1: errors; 2: default; 3: details; 4: debug */ static clock_t ZDICT_clockSpan(clock_t nPrevious) { return clock() - nPrevious; } static void ZDICT_printHex(const void* ptr, size_t length) { const BYTE* const b = (const BYTE*)ptr; size_t u; for (u=0; u126) c = '.'; /* non-printable char */ DISPLAY("%c", c); } } /*-******************************************************** * Helper functions **********************************************************/ unsigned ZDICT_isError(size_t errorCode) { return ERR_isError(errorCode); } const char* ZDICT_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); } unsigned ZDICT_getDictID(const void* dictBuffer, size_t dictSize) { if (dictSize < 8) return 0; if (MEM_readLE32(dictBuffer) != ZSTD_MAGIC_DICTIONARY) return 0; return MEM_readLE32((const char*)dictBuffer + 4); } size_t ZDICT_getDictHeaderSize(const void* dictBuffer, size_t dictSize) { size_t headerSize; if (dictSize <= 8 || MEM_readLE32(dictBuffer) != ZSTD_MAGIC_DICTIONARY) return ERROR(dictionary_corrupted); { unsigned offcodeMaxValue = MaxOff; ZSTD_compressedBlockState_t* bs = (ZSTD_compressedBlockState_t*)malloc(sizeof(ZSTD_compressedBlockState_t)); U32* wksp = (U32*)malloc(HUF_WORKSPACE_SIZE); short* offcodeNCount = (short*)malloc((MaxOff+1)*sizeof(short)); if (!bs || !wksp || !offcodeNCount) { headerSize = ERROR(memory_allocation); } else { ZSTD_reset_compressedBlockState(bs); headerSize = ZSTD_loadCEntropy(bs, wksp, offcodeNCount, &offcodeMaxValue, dictBuffer, dictSize); } free(bs); free(wksp); free(offcodeNCount); } return headerSize; } /*-******************************************************** * Dictionary training functions **********************************************************/ static unsigned ZDICT_NbCommonBytes (size_t val) { if (MEM_isLittleEndian()) { if (MEM_64bits()) { # if defined(_MSC_VER) && defined(_WIN64) unsigned long r = 0; _BitScanForward64( &r, (U64)val ); return (unsigned)(r>>3); # elif defined(__GNUC__) && (__GNUC__ >= 3) return (__builtin_ctzll((U64)val) >> 3); # else static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; # endif } else { /* 32 bits */ # if defined(_MSC_VER) unsigned long r=0; _BitScanForward( &r, (U32)val ); return (unsigned)(r>>3); # elif defined(__GNUC__) && (__GNUC__ >= 3) return (__builtin_ctz((U32)val) >> 3); # else static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; # endif } } else { /* Big Endian CPU */ if (MEM_64bits()) { # if defined(_MSC_VER) && defined(_WIN64) unsigned long r = 0; _BitScanReverse64( &r, val ); return (unsigned)(r>>3); # elif defined(__GNUC__) && (__GNUC__ >= 3) return (__builtin_clzll(val) >> 3); # else unsigned r; const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; } if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } r += (!val); return r; # endif } else { /* 32 bits */ # if defined(_MSC_VER) unsigned long r = 0; _BitScanReverse( &r, (unsigned long)val ); return (unsigned)(r>>3); # elif defined(__GNUC__) && (__GNUC__ >= 3) return (__builtin_clz((U32)val) >> 3); # else unsigned r; if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } r += (!val); return r; # endif } } } /*! ZDICT_count() : Count the nb of common bytes between 2 pointers. Note : this function presumes end of buffer followed by noisy guard band. */ static size_t ZDICT_count(const void* pIn, const void* pMatch) { const char* const pStart = (const char*)pIn; for (;;) { size_t const diff = MEM_readST(pMatch) ^ MEM_readST(pIn); if (!diff) { pIn = (const char*)pIn+sizeof(size_t); pMatch = (const char*)pMatch+sizeof(size_t); continue; } pIn = (const char*)pIn+ZDICT_NbCommonBytes(diff); return (size_t)((const char*)pIn - pStart); } } typedef struct { U32 pos; U32 length; U32 savings; } dictItem; static void ZDICT_initDictItem(dictItem* d) { d->pos = 1; d->length = 0; d->savings = (U32)(-1); } #define LLIMIT 64 /* heuristic determined experimentally */ #define MINMATCHLENGTH 7 /* heuristic determined experimentally */ static dictItem ZDICT_analyzePos( BYTE* doneMarks, const int* suffix, U32 start, const void* buffer, U32 minRatio, U32 notificationLevel) { U32 lengthList[LLIMIT] = {0}; U32 cumulLength[LLIMIT] = {0}; U32 savings[LLIMIT] = {0}; const BYTE* b = (const BYTE*)buffer; size_t maxLength = LLIMIT; size_t pos = suffix[start]; U32 end = start; dictItem solution; /* init */ memset(&solution, 0, sizeof(solution)); doneMarks[pos] = 1; /* trivial repetition cases */ if ( (MEM_read16(b+pos+0) == MEM_read16(b+pos+2)) ||(MEM_read16(b+pos+1) == MEM_read16(b+pos+3)) ||(MEM_read16(b+pos+2) == MEM_read16(b+pos+4)) ) { /* skip and mark segment */ U16 const pattern16 = MEM_read16(b+pos+4); U32 u, patternEnd = 6; while (MEM_read16(b+pos+patternEnd) == pattern16) patternEnd+=2 ; if (b[pos+patternEnd] == b[pos+patternEnd-1]) patternEnd++; for (u=1; u= MINMATCHLENGTH); } /* look backward */ { size_t length; do { length = ZDICT_count(b + pos, b + *(suffix+start-1)); if (length >=MINMATCHLENGTH) start--; } while(length >= MINMATCHLENGTH); } /* exit if not found a minimum nb of repetitions */ if (end-start < minRatio) { U32 idx; for(idx=start; idx= %i at pos %7u ", (unsigned)(end-start), MINMATCHLENGTH, (unsigned)pos); DISPLAYLEVEL(4, "\n"); for (mml = MINMATCHLENGTH ; ; mml++) { BYTE currentChar = 0; U32 currentCount = 0; U32 currentID = refinedStart; U32 id; U32 selectedCount = 0; U32 selectedID = currentID; for (id =refinedStart; id < refinedEnd; id++) { if (b[suffix[id] + mml] != currentChar) { if (currentCount > selectedCount) { selectedCount = currentCount; selectedID = currentID; } currentID = id; currentChar = b[ suffix[id] + mml]; currentCount = 0; } currentCount ++; } if (currentCount > selectedCount) { /* for last */ selectedCount = currentCount; selectedID = currentID; } if (selectedCount < minRatio) break; refinedStart = selectedID; refinedEnd = refinedStart + selectedCount; } /* evaluate gain based on new dict */ start = refinedStart; pos = suffix[refinedStart]; end = start; memset(lengthList, 0, sizeof(lengthList)); /* look forward */ { size_t length; do { end++; length = ZDICT_count(b + pos, b + suffix[end]); if (length >= LLIMIT) length = LLIMIT-1; lengthList[length]++; } while (length >=MINMATCHLENGTH); } /* look backward */ { size_t length = MINMATCHLENGTH; while ((length >= MINMATCHLENGTH) & (start > 0)) { length = ZDICT_count(b + pos, b + suffix[start - 1]); if (length >= LLIMIT) length = LLIMIT - 1; lengthList[length]++; if (length >= MINMATCHLENGTH) start--; } } /* largest useful length */ memset(cumulLength, 0, sizeof(cumulLength)); cumulLength[maxLength-1] = lengthList[maxLength-1]; for (i=(int)(maxLength-2); i>=0; i--) cumulLength[i] = cumulLength[i+1] + lengthList[i]; for (i=LLIMIT-1; i>=MINMATCHLENGTH; i--) if (cumulLength[i]>=minRatio) break; maxLength = i; /* reduce maxLength in case of final into repetitive data */ { U32 l = (U32)maxLength; BYTE const c = b[pos + maxLength-1]; while (b[pos+l-2]==c) l--; maxLength = l; } if (maxLength < MINMATCHLENGTH) return solution; /* skip : no long-enough solution */ /* calculate savings */ savings[5] = 0; for (i=MINMATCHLENGTH; i<=(int)maxLength; i++) savings[i] = savings[i-1] + (lengthList[i] * (i-3)); DISPLAYLEVEL(4, "Selected dict at position %u, of length %u : saves %u (ratio: %.2f) \n", (unsigned)pos, (unsigned)maxLength, (unsigned)savings[maxLength], (double)savings[maxLength] / maxLength); solution.pos = (U32)pos; solution.length = (U32)maxLength; solution.savings = savings[maxLength]; /* mark positions done */ { U32 id; for (id=start; id solution.length) length = solution.length; } pEnd = (U32)(testedPos + length); for (p=testedPos; ppos; const U32 eltEnd = elt.pos + elt.length; const char* const buf = (const char*) buffer; /* tail overlap */ U32 u; for (u=1; u elt.pos) && (table[u].pos <= eltEnd)) { /* overlap, existing > new */ /* append */ U32 const addedLength = table[u].pos - elt.pos; table[u].length += addedLength; table[u].pos = elt.pos; table[u].savings += elt.savings * addedLength / elt.length; /* rough approx */ table[u].savings += elt.length / 8; /* rough approx bonus */ elt = table[u]; /* sort : improve rank */ while ((u>1) && (table[u-1].savings < elt.savings)) table[u] = table[u-1], u--; table[u] = elt; return u; } } /* front overlap */ for (u=1; u= elt.pos) && (table[u].pos < elt.pos)) { /* overlap, existing < new */ /* append */ int const addedLength = (int)eltEnd - (table[u].pos + table[u].length); table[u].savings += elt.length / 8; /* rough approx bonus */ if (addedLength > 0) { /* otherwise, elt fully included into existing */ table[u].length += addedLength; table[u].savings += elt.savings * addedLength / elt.length; /* rough approx */ } /* sort : improve rank */ elt = table[u]; while ((u>1) && (table[u-1].savings < elt.savings)) table[u] = table[u-1], u--; table[u] = elt; return u; } if (MEM_read64(buf + table[u].pos) == MEM_read64(buf + elt.pos + 1)) { if (isIncluded(buf + table[u].pos, buf + elt.pos + 1, table[u].length)) { size_t const addedLength = MAX( (int)elt.length - (int)table[u].length , 1 ); table[u].pos = elt.pos; table[u].savings += (U32)(elt.savings * addedLength / elt.length); table[u].length = MIN(elt.length, table[u].length + 1); return u; } } } return 0; } static void ZDICT_removeDictItem(dictItem* table, U32 id) { /* convention : table[0].pos stores nb of elts */ U32 const max = table[0].pos; U32 u; if (!id) return; /* protection, should never happen */ for (u=id; upos--; } static void ZDICT_insertDictItem(dictItem* table, U32 maxSize, dictItem elt, const void* buffer) { /* merge if possible */ U32 mergeId = ZDICT_tryMerge(table, elt, 0, buffer); if (mergeId) { U32 newMerge = 1; while (newMerge) { newMerge = ZDICT_tryMerge(table, table[mergeId], mergeId, buffer); if (newMerge) ZDICT_removeDictItem(table, mergeId); mergeId = newMerge; } return; } /* insert */ { U32 current; U32 nextElt = table->pos; if (nextElt >= maxSize) nextElt = maxSize-1; current = nextElt-1; while (table[current].savings < elt.savings) { table[current+1] = table[current]; current--; } table[current+1] = elt; table->pos = nextElt+1; } } static U32 ZDICT_dictSize(const dictItem* dictList) { U32 u, dictSize = 0; for (u=1; u=l) { \ if (ZDICT_clockSpan(displayClock) > refreshRate) \ { displayClock = clock(); DISPLAY(__VA_ARGS__); \ if (notificationLevel>=4) fflush(stderr); } } /* init */ DISPLAYLEVEL(2, "\r%70s\r", ""); /* clean display line */ if (!suffix0 || !reverseSuffix || !doneMarks || !filePos) { result = ERROR(memory_allocation); goto _cleanup; } if (minRatio < MINRATIO) minRatio = MINRATIO; memset(doneMarks, 0, bufferSize+16); /* limit sample set size (divsufsort limitation)*/ if (bufferSize > ZDICT_MAX_SAMPLES_SIZE) DISPLAYLEVEL(3, "sample set too large : reduced to %u MB ...\n", (unsigned)(ZDICT_MAX_SAMPLES_SIZE>>20)); while (bufferSize > ZDICT_MAX_SAMPLES_SIZE) bufferSize -= fileSizes[--nbFiles]; /* sort */ DISPLAYLEVEL(2, "sorting %u files of total size %u MB ...\n", nbFiles, (unsigned)(bufferSize>>20)); { int const divSuftSortResult = divsufsort((const unsigned char*)buffer, suffix, (int)bufferSize, 0); if (divSuftSortResult != 0) { result = ERROR(GENERIC); goto _cleanup; } } suffix[bufferSize] = (int)bufferSize; /* leads into noise */ suffix0[0] = (int)bufferSize; /* leads into noise */ /* build reverse suffix sort */ { size_t pos; for (pos=0; pos < bufferSize; pos++) reverseSuffix[suffix[pos]] = (U32)pos; /* note filePos tracks borders between samples. It's not used at this stage, but planned to become useful in a later update */ filePos[0] = 0; for (pos=1; pos> 21); } } typedef struct { ZSTD_CDict* dict; /* dictionary */ ZSTD_CCtx* zc; /* working context */ void* workPlace; /* must be ZSTD_BLOCKSIZE_MAX allocated */ } EStats_ress_t; #define MAXREPOFFSET 1024 static void ZDICT_countEStats(EStats_ress_t esr, const ZSTD_parameters* params, unsigned* countLit, unsigned* offsetcodeCount, unsigned* matchlengthCount, unsigned* litlengthCount, U32* repOffsets, const void* src, size_t srcSize, U32 notificationLevel) { size_t const blockSizeMax = MIN (ZSTD_BLOCKSIZE_MAX, 1 << params->cParams.windowLog); size_t cSize; if (srcSize > blockSizeMax) srcSize = blockSizeMax; /* protection vs large samples */ { size_t const errorCode = ZSTD_compressBegin_usingCDict(esr.zc, esr.dict); if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_compressBegin_usingCDict failed \n"); return; } } cSize = ZSTD_compressBlock(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_MAX, src, srcSize); if (ZSTD_isError(cSize)) { DISPLAYLEVEL(3, "warning : could not compress sample size %u \n", (unsigned)srcSize); return; } if (cSize) { /* if == 0; block is not compressible */ const seqStore_t* const seqStorePtr = ZSTD_getSeqStore(esr.zc); /* literals stats */ { const BYTE* bytePtr; for(bytePtr = seqStorePtr->litStart; bytePtr < seqStorePtr->lit; bytePtr++) countLit[*bytePtr]++; } /* seqStats */ { U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); ZSTD_seqToCodes(seqStorePtr); { const BYTE* codePtr = seqStorePtr->ofCode; U32 u; for (u=0; umlCode; U32 u; for (u=0; ullCode; U32 u; for (u=0; u= 2) { /* rep offsets */ const seqDef* const seq = seqStorePtr->sequencesStart; U32 offset1 = seq[0].offset - 3; U32 offset2 = seq[1].offset - 3; if (offset1 >= MAXREPOFFSET) offset1 = 0; if (offset2 >= MAXREPOFFSET) offset2 = 0; repOffsets[offset1] += 3; repOffsets[offset2] += 1; } } } } static size_t ZDICT_totalSampleSize(const size_t* fileSizes, unsigned nbFiles) { size_t total=0; unsigned u; for (u=0; u0; u--) { offsetCount_t tmp; if (table[u-1].count >= table[u].count) break; tmp = table[u-1]; table[u-1] = table[u]; table[u] = tmp; } } /* ZDICT_flatLit() : * rewrite `countLit` to contain a mostly flat but still compressible distribution of literals. * necessary to avoid generating a non-compressible distribution that HUF_writeCTable() cannot encode. */ static void ZDICT_flatLit(unsigned* countLit) { int u; for (u=1; u<256; u++) countLit[u] = 2; countLit[0] = 4; countLit[253] = 1; countLit[254] = 1; } #define OFFCODE_MAX 30 /* only applicable to first block */ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, unsigned compressionLevel, const void* srcBuffer, const size_t* fileSizes, unsigned nbFiles, const void* dictBuffer, size_t dictBufferSize, unsigned notificationLevel) { unsigned countLit[256]; HUF_CREATE_STATIC_CTABLE(hufTable, 255); unsigned offcodeCount[OFFCODE_MAX+1]; short offcodeNCount[OFFCODE_MAX+1]; U32 offcodeMax = ZSTD_highbit32((U32)(dictBufferSize + 128 KB)); unsigned matchLengthCount[MaxML+1]; short matchLengthNCount[MaxML+1]; unsigned litLengthCount[MaxLL+1]; short litLengthNCount[MaxLL+1]; U32 repOffset[MAXREPOFFSET]; offsetCount_t bestRepOffset[ZSTD_REP_NUM+1]; EStats_ress_t esr = { NULL, NULL, NULL }; ZSTD_parameters params; U32 u, huffLog = 11, Offlog = OffFSELog, mlLog = MLFSELog, llLog = LLFSELog, total; size_t pos = 0, errorCode; size_t eSize = 0; size_t const totalSrcSize = ZDICT_totalSampleSize(fileSizes, nbFiles); size_t const averageSampleSize = totalSrcSize / (nbFiles + !nbFiles); BYTE* dstPtr = (BYTE*)dstBuffer; /* init */ DEBUGLOG(4, "ZDICT_analyzeEntropy"); if (offcodeMax>OFFCODE_MAX) { eSize = ERROR(dictionaryCreation_failed); goto _cleanup; } /* too large dictionary */ for (u=0; u<256; u++) countLit[u] = 1; /* any character must be described */ for (u=0; u<=offcodeMax; u++) offcodeCount[u] = 1; for (u=0; u<=MaxML; u++) matchLengthCount[u] = 1; for (u=0; u<=MaxLL; u++) litLengthCount[u] = 1; memset(repOffset, 0, sizeof(repOffset)); repOffset[1] = repOffset[4] = repOffset[8] = 1; memset(bestRepOffset, 0, sizeof(bestRepOffset)); if (compressionLevel==0) compressionLevel = g_compressionLevel_default; params = ZSTD_getParams(compressionLevel, averageSampleSize, dictBufferSize); esr.dict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, ZSTD_dlm_byRef, ZSTD_dct_rawContent, params.cParams, ZSTD_defaultCMem); esr.zc = ZSTD_createCCtx(); esr.workPlace = malloc(ZSTD_BLOCKSIZE_MAX); if (!esr.dict || !esr.zc || !esr.workPlace) { eSize = ERROR(memory_allocation); DISPLAYLEVEL(1, "Not enough memory \n"); goto _cleanup; } /* collect stats on all samples */ for (u=0; u dictBufferCapacity) dictContentSize = dictBufferCapacity - hSize; { size_t const dictSize = hSize + dictContentSize; char* dictEnd = (char*)dictBuffer + dictSize; memmove(dictEnd - dictContentSize, customDictContent, dictContentSize); memcpy(dictBuffer, header, hSize); return dictSize; } } static size_t ZDICT_addEntropyTablesFromBuffer_advanced( void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_params_t params) { int const compressionLevel = (params.compressionLevel == 0) ? g_compressionLevel_default : params.compressionLevel; U32 const notificationLevel = params.notificationLevel; size_t hSize = 8; /* calculate entropy tables */ DISPLAYLEVEL(2, "\r%70s\r", ""); /* clean display line */ DISPLAYLEVEL(2, "statistics ... \n"); { size_t const eSize = ZDICT_analyzeEntropy((char*)dictBuffer+hSize, dictBufferCapacity-hSize, compressionLevel, samplesBuffer, samplesSizes, nbSamples, (char*)dictBuffer + dictBufferCapacity - dictContentSize, dictContentSize, notificationLevel); if (ZDICT_isError(eSize)) return eSize; hSize += eSize; } /* add dictionary header (after entropy tables) */ MEM_writeLE32(dictBuffer, ZSTD_MAGIC_DICTIONARY); { U64 const randomID = XXH64((char*)dictBuffer + dictBufferCapacity - dictContentSize, dictContentSize, 0); U32 const compliantID = (randomID % ((1U<<31)-32768)) + 32768; U32 const dictID = params.dictID ? params.dictID : compliantID; MEM_writeLE32((char*)dictBuffer+4, dictID); } if (hSize + dictContentSize < dictBufferCapacity) memmove((char*)dictBuffer + hSize, (char*)dictBuffer + dictBufferCapacity - dictContentSize, dictContentSize); return MIN(dictBufferCapacity, hSize+dictContentSize); } /* Hidden declaration for dbio.c */ size_t ZDICT_trainFromBuffer_unsafe_legacy( void* dictBuffer, size_t maxDictSize, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_legacy_params_t params); /*! ZDICT_trainFromBuffer_unsafe_legacy() : * Warning : `samplesBuffer` must be followed by noisy guard band. * @return : size of dictionary, or an error code which can be tested with ZDICT_isError() */ size_t ZDICT_trainFromBuffer_unsafe_legacy( void* dictBuffer, size_t maxDictSize, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_legacy_params_t params) { U32 const dictListSize = MAX(MAX(DICTLISTSIZE_DEFAULT, nbSamples), (U32)(maxDictSize/16)); dictItem* const dictList = (dictItem*)malloc(dictListSize * sizeof(*dictList)); unsigned const selectivity = params.selectivityLevel == 0 ? g_selectivity_default : params.selectivityLevel; unsigned const minRep = (selectivity > 30) ? MINRATIO : nbSamples >> selectivity; size_t const targetDictSize = maxDictSize; size_t const samplesBuffSize = ZDICT_totalSampleSize(samplesSizes, nbSamples); size_t dictSize = 0; U32 const notificationLevel = params.zParams.notificationLevel; /* checks */ if (!dictList) return ERROR(memory_allocation); if (maxDictSize < ZDICT_DICTSIZE_MIN) { free(dictList); return ERROR(dstSize_tooSmall); } /* requested dictionary size is too small */ if (samplesBuffSize < ZDICT_MIN_SAMPLES_SIZE) { free(dictList); return ERROR(dictionaryCreation_failed); } /* not enough source to create dictionary */ /* init */ ZDICT_initDictItem(dictList); /* build dictionary */ ZDICT_trainBuffer_legacy(dictList, dictListSize, samplesBuffer, samplesBuffSize, samplesSizes, nbSamples, minRep, notificationLevel); /* display best matches */ if (params.zParams.notificationLevel>= 3) { unsigned const nb = MIN(25, dictList[0].pos); unsigned const dictContentSize = ZDICT_dictSize(dictList); unsigned u; DISPLAYLEVEL(3, "\n %u segments found, of total size %u \n", (unsigned)dictList[0].pos-1, dictContentSize); DISPLAYLEVEL(3, "list %u best segments \n", nb-1); for (u=1; u samplesBuffSize) || ((pos + length) > samplesBuffSize)) { free(dictList); return ERROR(GENERIC); /* should never happen */ } DISPLAYLEVEL(3, "%3u:%3u bytes at pos %8u, savings %7u bytes |", u, length, pos, (unsigned)dictList[u].savings); ZDICT_printHex((const char*)samplesBuffer+pos, printedLength); DISPLAYLEVEL(3, "| \n"); } } /* create dictionary */ { unsigned dictContentSize = ZDICT_dictSize(dictList); if (dictContentSize < ZDICT_CONTENTSIZE_MIN) { free(dictList); return ERROR(dictionaryCreation_failed); } /* dictionary content too small */ if (dictContentSize < targetDictSize/4) { DISPLAYLEVEL(2, "! warning : selected content significantly smaller than requested (%u < %u) \n", dictContentSize, (unsigned)maxDictSize); if (samplesBuffSize < 10 * targetDictSize) DISPLAYLEVEL(2, "! consider increasing the number of samples (total size : %u MB)\n", (unsigned)(samplesBuffSize>>20)); if (minRep > MINRATIO) { DISPLAYLEVEL(2, "! consider increasing selectivity to produce larger dictionary (-s%u) \n", selectivity+1); DISPLAYLEVEL(2, "! note : larger dictionaries are not necessarily better, test its efficiency on samples \n"); } } if ((dictContentSize > targetDictSize*3) && (nbSamples > 2*MINRATIO) && (selectivity>1)) { unsigned proposedSelectivity = selectivity-1; while ((nbSamples >> proposedSelectivity) <= MINRATIO) { proposedSelectivity--; } DISPLAYLEVEL(2, "! note : calculated dictionary significantly larger than requested (%u > %u) \n", dictContentSize, (unsigned)maxDictSize); DISPLAYLEVEL(2, "! consider increasing dictionary size, or produce denser dictionary (-s%u) \n", proposedSelectivity); DISPLAYLEVEL(2, "! always test dictionary efficiency on real samples \n"); } /* limit dictionary size */ { U32 const max = dictList->pos; /* convention : nb of useful elts within dictList */ U32 currentSize = 0; U32 n; for (n=1; n targetDictSize) { currentSize -= dictList[n].length; break; } } dictList->pos = n; dictContentSize = currentSize; } /* build dict content */ { U32 u; BYTE* ptr = (BYTE*)dictBuffer + maxDictSize; for (u=1; upos; u++) { U32 l = dictList[u].length; ptr -= l; if (ptr<(BYTE*)dictBuffer) { free(dictList); return ERROR(GENERIC); } /* should not happen */ memcpy(ptr, (const char*)samplesBuffer+dictList[u].pos, l); } } dictSize = ZDICT_addEntropyTablesFromBuffer_advanced(dictBuffer, dictContentSize, maxDictSize, samplesBuffer, samplesSizes, nbSamples, params.zParams); } /* clean up */ free(dictList); return dictSize; } /* ZDICT_trainFromBuffer_legacy() : * issue : samplesBuffer need to be followed by a noisy guard band. * work around : duplicate the buffer, and add the noise */ size_t ZDICT_trainFromBuffer_legacy(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_legacy_params_t params) { size_t result; void* newBuff; size_t const sBuffSize = ZDICT_totalSampleSize(samplesSizes, nbSamples); if (sBuffSize < ZDICT_MIN_SAMPLES_SIZE) return 0; /* not enough content => no dictionary */ newBuff = malloc(sBuffSize + NOISELENGTH); if (!newBuff) return ERROR(memory_allocation); memcpy(newBuff, samplesBuffer, sBuffSize); ZDICT_fillNoise((char*)newBuff + sBuffSize, NOISELENGTH); /* guard band, for end of buffer condition */ result = ZDICT_trainFromBuffer_unsafe_legacy(dictBuffer, dictBufferCapacity, newBuff, samplesSizes, nbSamples, params); free(newBuff); return result; } size_t ZDICT_trainFromBuffer(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples) { ZDICT_fastCover_params_t params; DEBUGLOG(3, "ZDICT_trainFromBuffer"); memset(¶ms, 0, sizeof(params)); params.d = 8; params.steps = 4; /* Default to level 6 since no compression level information is available */ params.zParams.compressionLevel = 3; #if defined(DEBUGLEVEL) && (DEBUGLEVEL>=1) params.zParams.notificationLevel = DEBUGLEVEL; #endif return ZDICT_optimizeTrainFromBuffer_fastCover(dictBuffer, dictBufferCapacity, samplesBuffer, samplesSizes, nbSamples, ¶ms); } size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples) { ZDICT_params_t params; memset(¶ms, 0, sizeof(params)); return ZDICT_addEntropyTablesFromBuffer_advanced(dictBuffer, dictContentSize, dictBufferCapacity, samplesBuffer, samplesSizes, nbSamples, params); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/dictBuilder/zdict.h0000644000076500000240000004402314601576577023406 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef DICTBUILDER_H_001 #define DICTBUILDER_H_001 #if defined (__cplusplus) extern "C" { #endif /*====== Dependencies ======*/ #include /* size_t */ /* ===== ZDICTLIB_API : control library symbols visibility ===== */ #ifndef ZDICTLIB_VISIBILITY # if defined(__GNUC__) && (__GNUC__ >= 4) # define ZDICTLIB_VISIBILITY __attribute__ ((visibility ("default"))) # else # define ZDICTLIB_VISIBILITY # endif #endif #if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) # define ZDICTLIB_API __declspec(dllexport) ZDICTLIB_VISIBILITY #elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) # define ZDICTLIB_API __declspec(dllimport) ZDICTLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ #else # define ZDICTLIB_API ZDICTLIB_VISIBILITY #endif /*! ZDICT_trainFromBuffer(): * Train a dictionary from an array of samples. * Redirect towards ZDICT_optimizeTrainFromBuffer_fastCover() single-threaded, with d=8, steps=4, * f=20, and accel=1. * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. * The resulting dictionary will be saved into `dictBuffer`. * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) * or an error code, which can be tested with ZDICT_isError(). * Note: Dictionary training will fail if there are not enough samples to construct a * dictionary, or if most of the samples are too small (< 8 bytes being the lower limit). * If dictionary training fails, you should use zstd without a dictionary, as the dictionary * would've been ineffective anyways. If you believe your samples would benefit from a dictionary * please open an issue with details, and we can look into it. * Note: ZDICT_trainFromBuffer()'s memory usage is about 6 MB. * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`. * In general, it's recommended to provide a few thousands samples, though this can vary a lot. * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. */ ZDICTLIB_API size_t ZDICT_trainFromBuffer(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); typedef struct { int compressionLevel; /*< optimize for a specific zstd compression level; 0 means default */ unsigned notificationLevel; /*< Write log to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */ unsigned dictID; /*< force dictID value; 0 means auto mode (32-bits random value) */ } ZDICT_params_t; /*! ZDICT_finalizeDictionary(): * Given a custom content as a basis for dictionary, and a set of samples, * finalize dictionary by adding headers and statistics according to the zstd * dictionary format. * * Samples must be stored concatenated in a flat buffer `samplesBuffer`, * supplied with an array of sizes `samplesSizes`, providing the size of each * sample in order. The samples are used to construct the statistics, so they * should be representative of what you will compress with this dictionary. * * The compression level can be set in `parameters`. You should pass the * compression level you expect to use in production. The statistics for each * compression level differ, so tuning the dictionary for the compression level * can help quite a bit. * * You can set an explicit dictionary ID in `parameters`, or allow us to pick * a random dictionary ID for you, but we can't guarantee no collisions. * * The dstDictBuffer and the dictContent may overlap, and the content will be * appended to the end of the header. If the header + the content doesn't fit in * maxDictSize the beginning of the content is truncated to make room, since it * is presumed that the most profitable content is at the end of the dictionary, * since that is the cheapest to reference. * * `dictContentSize` must be >= ZDICT_CONTENTSIZE_MIN bytes. * `maxDictSize` must be >= max(dictContentSize, ZSTD_DICTSIZE_MIN). * * @return: size of dictionary stored into `dstDictBuffer` (<= `maxDictSize`), * or an error code, which can be tested by ZDICT_isError(). * Note: ZDICT_finalizeDictionary() will push notifications into stderr if * instructed to, using notificationLevel>0. * NOTE: This function currently may fail in several edge cases including: * * Not enough samples * * Samples are uncompressible * * Samples are all exactly the same */ ZDICTLIB_API size_t ZDICT_finalizeDictionary(void* dstDictBuffer, size_t maxDictSize, const void* dictContent, size_t dictContentSize, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_params_t parameters); /*====== Helper functions ======*/ ZDICTLIB_API unsigned ZDICT_getDictID(const void* dictBuffer, size_t dictSize); /**< extracts dictID; @return zero if error (not a valid dictionary) */ ZDICTLIB_API size_t ZDICT_getDictHeaderSize(const void* dictBuffer, size_t dictSize); /* returns dict header size; returns a ZSTD error code on failure */ ZDICTLIB_API unsigned ZDICT_isError(size_t errorCode); ZDICTLIB_API const char* ZDICT_getErrorName(size_t errorCode); #ifdef ZDICT_STATIC_LINKING_ONLY /* ==================================================================================== * The definitions in this section are considered experimental. * They should never be used with a dynamic library, as they may change in the future. * They are provided for advanced usages. * Use them only in association with static linking. * ==================================================================================== */ #define ZDICT_CONTENTSIZE_MIN 128 #define ZDICT_DICTSIZE_MIN 256 /*! ZDICT_cover_params_t: * k and d are the only required parameters. * For others, value 0 means default. */ typedef struct { unsigned k; /* Segment size : constraint: 0 < k : Reasonable range [16, 2048+] */ unsigned d; /* dmer size : constraint: 0 < d <= k : Reasonable range [6, 16] */ unsigned steps; /* Number of steps : Only used for optimization : 0 means default (40) : Higher means more parameters checked */ unsigned nbThreads; /* Number of threads : constraint: 0 < nbThreads : 1 means single-threaded : Only used for optimization : Ignored if ZSTD_MULTITHREAD is not defined */ double splitPoint; /* Percentage of samples used for training: Only used for optimization : the first nbSamples * splitPoint samples will be used to training, the last nbSamples * (1 - splitPoint) samples will be used for testing, 0 means default (1.0), 1.0 when all samples are used for both training and testing */ unsigned shrinkDict; /* Train dictionaries to shrink in size starting from the minimum size and selects the smallest dictionary that is shrinkDictMaxRegression% worse than the largest dictionary. 0 means no shrinking and 1 means shrinking */ unsigned shrinkDictMaxRegression; /* Sets shrinkDictMaxRegression so that a smaller dictionary can be at worse shrinkDictMaxRegression% worse than the max dict size dictionary. */ ZDICT_params_t zParams; } ZDICT_cover_params_t; typedef struct { unsigned k; /* Segment size : constraint: 0 < k : Reasonable range [16, 2048+] */ unsigned d; /* dmer size : constraint: 0 < d <= k : Reasonable range [6, 16] */ unsigned f; /* log of size of frequency array : constraint: 0 < f <= 31 : 1 means default(20)*/ unsigned steps; /* Number of steps : Only used for optimization : 0 means default (40) : Higher means more parameters checked */ unsigned nbThreads; /* Number of threads : constraint: 0 < nbThreads : 1 means single-threaded : Only used for optimization : Ignored if ZSTD_MULTITHREAD is not defined */ double splitPoint; /* Percentage of samples used for training: Only used for optimization : the first nbSamples * splitPoint samples will be used to training, the last nbSamples * (1 - splitPoint) samples will be used for testing, 0 means default (0.75), 1.0 when all samples are used for both training and testing */ unsigned accel; /* Acceleration level: constraint: 0 < accel <= 10, higher means faster and less accurate, 0 means default(1) */ unsigned shrinkDict; /* Train dictionaries to shrink in size starting from the minimum size and selects the smallest dictionary that is shrinkDictMaxRegression% worse than the largest dictionary. 0 means no shrinking and 1 means shrinking */ unsigned shrinkDictMaxRegression; /* Sets shrinkDictMaxRegression so that a smaller dictionary can be at worse shrinkDictMaxRegression% worse than the max dict size dictionary. */ ZDICT_params_t zParams; } ZDICT_fastCover_params_t; /*! ZDICT_trainFromBuffer_cover(): * Train a dictionary from an array of samples using the COVER algorithm. * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. * The resulting dictionary will be saved into `dictBuffer`. * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) * or an error code, which can be tested with ZDICT_isError(). * See ZDICT_trainFromBuffer() for details on failure modes. * Note: ZDICT_trainFromBuffer_cover() requires about 9 bytes of memory for each input byte. * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`. * In general, it's recommended to provide a few thousands samples, though this can vary a lot. * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. */ ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, ZDICT_cover_params_t parameters); /*! ZDICT_optimizeTrainFromBuffer_cover(): * The same requirements as above hold for all the parameters except `parameters`. * This function tries many parameter combinations and picks the best parameters. * `*parameters` is filled with the best parameters found, * dictionary constructed with those parameters is stored in `dictBuffer`. * * All of the parameters d, k, steps are optional. * If d is non-zero then we don't check multiple values of d, otherwise we check d = {6, 8}. * if steps is zero it defaults to its default value. * If k is non-zero then we don't check multiple values of k, otherwise we check steps values in [50, 2000]. * * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) * or an error code, which can be tested with ZDICT_isError(). * On success `*parameters` contains the parameters selected. * See ZDICT_trainFromBuffer() for details on failure modes. * Note: ZDICT_optimizeTrainFromBuffer_cover() requires about 8 bytes of memory for each input byte and additionally another 5 bytes of memory for each byte of memory for each thread. */ ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover( void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_cover_params_t* parameters); /*! ZDICT_trainFromBuffer_fastCover(): * Train a dictionary from an array of samples using a modified version of COVER algorithm. * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. * d and k are required. * All other parameters are optional, will use default values if not provided * The resulting dictionary will be saved into `dictBuffer`. * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) * or an error code, which can be tested with ZDICT_isError(). * See ZDICT_trainFromBuffer() for details on failure modes. * Note: ZDICT_trainFromBuffer_fastCover() requires 6 * 2^f bytes of memory. * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`. * In general, it's recommended to provide a few thousands samples, though this can vary a lot. * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. */ ZDICTLIB_API size_t ZDICT_trainFromBuffer_fastCover(void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, ZDICT_fastCover_params_t parameters); /*! ZDICT_optimizeTrainFromBuffer_fastCover(): * The same requirements as above hold for all the parameters except `parameters`. * This function tries many parameter combinations (specifically, k and d combinations) * and picks the best parameters. `*parameters` is filled with the best parameters found, * dictionary constructed with those parameters is stored in `dictBuffer`. * All of the parameters d, k, steps, f, and accel are optional. * If d is non-zero then we don't check multiple values of d, otherwise we check d = {6, 8}. * if steps is zero it defaults to its default value. * If k is non-zero then we don't check multiple values of k, otherwise we check steps values in [50, 2000]. * If f is zero, default value of 20 is used. * If accel is zero, default value of 1 is used. * * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) * or an error code, which can be tested with ZDICT_isError(). * On success `*parameters` contains the parameters selected. * See ZDICT_trainFromBuffer() for details on failure modes. * Note: ZDICT_optimizeTrainFromBuffer_fastCover() requires about 6 * 2^f bytes of memory for each thread. */ ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_fastCover(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_fastCover_params_t* parameters); typedef struct { unsigned selectivityLevel; /* 0 means default; larger => select more => larger dictionary */ ZDICT_params_t zParams; } ZDICT_legacy_params_t; /*! ZDICT_trainFromBuffer_legacy(): * Train a dictionary from an array of samples. * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. * The resulting dictionary will be saved into `dictBuffer`. * `parameters` is optional and can be provided with values set to 0 to mean "default". * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) * or an error code, which can be tested with ZDICT_isError(). * See ZDICT_trainFromBuffer() for details on failure modes. * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`. * In general, it's recommended to provide a few thousands samples, though this can vary a lot. * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. * Note: ZDICT_trainFromBuffer_legacy() will send notifications into stderr if instructed to, using notificationLevel>0. */ ZDICTLIB_API size_t ZDICT_trainFromBuffer_legacy( void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, ZDICT_legacy_params_t parameters); /* Deprecation warnings */ /* It is generally possible to disable deprecation warnings from compiler, for example with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual. Otherwise, it's also possible to manually define ZDICT_DISABLE_DEPRECATE_WARNINGS */ #ifdef ZDICT_DISABLE_DEPRECATE_WARNINGS # define ZDICT_DEPRECATED(message) ZDICTLIB_API /* disable deprecation warnings */ #else # define ZDICT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ # define ZDICT_DEPRECATED(message) [[deprecated(message)]] ZDICTLIB_API # elif (ZDICT_GCC_VERSION >= 405) || defined(__clang__) # define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated(message))) # elif (ZDICT_GCC_VERSION >= 301) # define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated)) # elif defined(_MSC_VER) # define ZDICT_DEPRECATED(message) ZDICTLIB_API __declspec(deprecated(message)) # else # pragma message("WARNING: You need to implement ZDICT_DEPRECATED for this compiler") # define ZDICT_DEPRECATED(message) ZDICTLIB_API # endif #endif /* ZDICT_DISABLE_DEPRECATE_WARNINGS */ ZDICT_DEPRECATED("use ZDICT_finalizeDictionary() instead") size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); #endif /* ZDICT_STATIC_LINKING_ONLY */ #if defined (__cplusplus) } #endif #endif /* DICTBUILDER_H_001 */ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711734324.1947002 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/legacy/0000755000076500000240000000000014601577064021117 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/legacy/zstd_legacy.h0000644000076500000240000003312514601576577023614 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_LEGACY_H #define ZSTD_LEGACY_H #if defined (__cplusplus) extern "C" { #endif /* ************************************* * Includes ***************************************/ #include "../common/mem.h" /* MEM_STATIC */ #include "../common/error_private.h" /* ERROR */ #include "../common/zstd_internal.h" /* ZSTD_inBuffer, ZSTD_outBuffer, ZSTD_frameSizeInfo */ #if !defined (ZSTD_LEGACY_SUPPORT) || (ZSTD_LEGACY_SUPPORT == 0) # undef ZSTD_LEGACY_SUPPORT # define ZSTD_LEGACY_SUPPORT 8 #endif #if (ZSTD_LEGACY_SUPPORT <= 1) # include "zstd_v01.h" #endif #if (ZSTD_LEGACY_SUPPORT <= 2) # include "zstd_v02.h" #endif #if (ZSTD_LEGACY_SUPPORT <= 3) # include "zstd_v03.h" #endif #if (ZSTD_LEGACY_SUPPORT <= 4) # include "zstd_v04.h" #endif #if (ZSTD_LEGACY_SUPPORT <= 5) # include "zstd_v05.h" #endif #if (ZSTD_LEGACY_SUPPORT <= 6) # include "zstd_v06.h" #endif #if (ZSTD_LEGACY_SUPPORT <= 7) # include "zstd_v07.h" #endif /** ZSTD_isLegacy() : @return : > 0 if supported by legacy decoder. 0 otherwise. return value is the version. */ MEM_STATIC unsigned ZSTD_isLegacy(const void* src, size_t srcSize) { U32 magicNumberLE; if (srcSize<4) return 0; magicNumberLE = MEM_readLE32(src); switch(magicNumberLE) { #if (ZSTD_LEGACY_SUPPORT <= 1) case ZSTDv01_magicNumberLE:return 1; #endif #if (ZSTD_LEGACY_SUPPORT <= 2) case ZSTDv02_magicNumber : return 2; #endif #if (ZSTD_LEGACY_SUPPORT <= 3) case ZSTDv03_magicNumber : return 3; #endif #if (ZSTD_LEGACY_SUPPORT <= 4) case ZSTDv04_magicNumber : return 4; #endif #if (ZSTD_LEGACY_SUPPORT <= 5) case ZSTDv05_MAGICNUMBER : return 5; #endif #if (ZSTD_LEGACY_SUPPORT <= 6) case ZSTDv06_MAGICNUMBER : return 6; #endif #if (ZSTD_LEGACY_SUPPORT <= 7) case ZSTDv07_MAGICNUMBER : return 7; #endif default : return 0; } } MEM_STATIC unsigned long long ZSTD_getDecompressedSize_legacy(const void* src, size_t srcSize) { U32 const version = ZSTD_isLegacy(src, srcSize); if (version < 5) return 0; /* no decompressed size in frame header, or not a legacy format */ #if (ZSTD_LEGACY_SUPPORT <= 5) if (version==5) { ZSTDv05_parameters fParams; size_t const frResult = ZSTDv05_getFrameParams(&fParams, src, srcSize); if (frResult != 0) return 0; return fParams.srcSize; } #endif #if (ZSTD_LEGACY_SUPPORT <= 6) if (version==6) { ZSTDv06_frameParams fParams; size_t const frResult = ZSTDv06_getFrameParams(&fParams, src, srcSize); if (frResult != 0) return 0; return fParams.frameContentSize; } #endif #if (ZSTD_LEGACY_SUPPORT <= 7) if (version==7) { ZSTDv07_frameParams fParams; size_t const frResult = ZSTDv07_getFrameParams(&fParams, src, srcSize); if (frResult != 0) return 0; return fParams.frameContentSize; } #endif return 0; /* should not be possible */ } MEM_STATIC size_t ZSTD_decompressLegacy( void* dst, size_t dstCapacity, const void* src, size_t compressedSize, const void* dict,size_t dictSize) { U32 const version = ZSTD_isLegacy(src, compressedSize); (void)dst; (void)dstCapacity; (void)dict; (void)dictSize; /* unused when ZSTD_LEGACY_SUPPORT >= 8 */ switch(version) { #if (ZSTD_LEGACY_SUPPORT <= 1) case 1 : return ZSTDv01_decompress(dst, dstCapacity, src, compressedSize); #endif #if (ZSTD_LEGACY_SUPPORT <= 2) case 2 : return ZSTDv02_decompress(dst, dstCapacity, src, compressedSize); #endif #if (ZSTD_LEGACY_SUPPORT <= 3) case 3 : return ZSTDv03_decompress(dst, dstCapacity, src, compressedSize); #endif #if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : return ZSTDv04_decompress(dst, dstCapacity, src, compressedSize); #endif #if (ZSTD_LEGACY_SUPPORT <= 5) case 5 : { size_t result; ZSTDv05_DCtx* const zd = ZSTDv05_createDCtx(); if (zd==NULL) return ERROR(memory_allocation); result = ZSTDv05_decompress_usingDict(zd, dst, dstCapacity, src, compressedSize, dict, dictSize); ZSTDv05_freeDCtx(zd); return result; } #endif #if (ZSTD_LEGACY_SUPPORT <= 6) case 6 : { size_t result; ZSTDv06_DCtx* const zd = ZSTDv06_createDCtx(); if (zd==NULL) return ERROR(memory_allocation); result = ZSTDv06_decompress_usingDict(zd, dst, dstCapacity, src, compressedSize, dict, dictSize); ZSTDv06_freeDCtx(zd); return result; } #endif #if (ZSTD_LEGACY_SUPPORT <= 7) case 7 : { size_t result; ZSTDv07_DCtx* const zd = ZSTDv07_createDCtx(); if (zd==NULL) return ERROR(memory_allocation); result = ZSTDv07_decompress_usingDict(zd, dst, dstCapacity, src, compressedSize, dict, dictSize); ZSTDv07_freeDCtx(zd); return result; } #endif default : return ERROR(prefix_unknown); } } MEM_STATIC ZSTD_frameSizeInfo ZSTD_findFrameSizeInfoLegacy(const void *src, size_t srcSize) { ZSTD_frameSizeInfo frameSizeInfo; U32 const version = ZSTD_isLegacy(src, srcSize); switch(version) { #if (ZSTD_LEGACY_SUPPORT <= 1) case 1 : ZSTDv01_findFrameSizeInfoLegacy(src, srcSize, &frameSizeInfo.compressedSize, &frameSizeInfo.decompressedBound); break; #endif #if (ZSTD_LEGACY_SUPPORT <= 2) case 2 : ZSTDv02_findFrameSizeInfoLegacy(src, srcSize, &frameSizeInfo.compressedSize, &frameSizeInfo.decompressedBound); break; #endif #if (ZSTD_LEGACY_SUPPORT <= 3) case 3 : ZSTDv03_findFrameSizeInfoLegacy(src, srcSize, &frameSizeInfo.compressedSize, &frameSizeInfo.decompressedBound); break; #endif #if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : ZSTDv04_findFrameSizeInfoLegacy(src, srcSize, &frameSizeInfo.compressedSize, &frameSizeInfo.decompressedBound); break; #endif #if (ZSTD_LEGACY_SUPPORT <= 5) case 5 : ZSTDv05_findFrameSizeInfoLegacy(src, srcSize, &frameSizeInfo.compressedSize, &frameSizeInfo.decompressedBound); break; #endif #if (ZSTD_LEGACY_SUPPORT <= 6) case 6 : ZSTDv06_findFrameSizeInfoLegacy(src, srcSize, &frameSizeInfo.compressedSize, &frameSizeInfo.decompressedBound); break; #endif #if (ZSTD_LEGACY_SUPPORT <= 7) case 7 : ZSTDv07_findFrameSizeInfoLegacy(src, srcSize, &frameSizeInfo.compressedSize, &frameSizeInfo.decompressedBound); break; #endif default : frameSizeInfo.compressedSize = ERROR(prefix_unknown); frameSizeInfo.decompressedBound = ZSTD_CONTENTSIZE_ERROR; break; } if (!ZSTD_isError(frameSizeInfo.compressedSize) && frameSizeInfo.compressedSize > srcSize) { frameSizeInfo.compressedSize = ERROR(srcSize_wrong); frameSizeInfo.decompressedBound = ZSTD_CONTENTSIZE_ERROR; } return frameSizeInfo; } MEM_STATIC size_t ZSTD_findFrameCompressedSizeLegacy(const void *src, size_t srcSize) { ZSTD_frameSizeInfo frameSizeInfo = ZSTD_findFrameSizeInfoLegacy(src, srcSize); return frameSizeInfo.compressedSize; } MEM_STATIC size_t ZSTD_freeLegacyStreamContext(void* legacyContext, U32 version) { switch(version) { default : case 1 : case 2 : case 3 : (void)legacyContext; return ERROR(version_unsupported); #if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : return ZBUFFv04_freeDCtx((ZBUFFv04_DCtx*)legacyContext); #endif #if (ZSTD_LEGACY_SUPPORT <= 5) case 5 : return ZBUFFv05_freeDCtx((ZBUFFv05_DCtx*)legacyContext); #endif #if (ZSTD_LEGACY_SUPPORT <= 6) case 6 : return ZBUFFv06_freeDCtx((ZBUFFv06_DCtx*)legacyContext); #endif #if (ZSTD_LEGACY_SUPPORT <= 7) case 7 : return ZBUFFv07_freeDCtx((ZBUFFv07_DCtx*)legacyContext); #endif } } MEM_STATIC size_t ZSTD_initLegacyStream(void** legacyContext, U32 prevVersion, U32 newVersion, const void* dict, size_t dictSize) { DEBUGLOG(5, "ZSTD_initLegacyStream for v0.%u", newVersion); if (prevVersion != newVersion) ZSTD_freeLegacyStreamContext(*legacyContext, prevVersion); switch(newVersion) { default : case 1 : case 2 : case 3 : (void)dict; (void)dictSize; return 0; #if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : { ZBUFFv04_DCtx* dctx = (prevVersion != newVersion) ? ZBUFFv04_createDCtx() : (ZBUFFv04_DCtx*)*legacyContext; if (dctx==NULL) return ERROR(memory_allocation); ZBUFFv04_decompressInit(dctx); ZBUFFv04_decompressWithDictionary(dctx, dict, dictSize); *legacyContext = dctx; return 0; } #endif #if (ZSTD_LEGACY_SUPPORT <= 5) case 5 : { ZBUFFv05_DCtx* dctx = (prevVersion != newVersion) ? ZBUFFv05_createDCtx() : (ZBUFFv05_DCtx*)*legacyContext; if (dctx==NULL) return ERROR(memory_allocation); ZBUFFv05_decompressInitDictionary(dctx, dict, dictSize); *legacyContext = dctx; return 0; } #endif #if (ZSTD_LEGACY_SUPPORT <= 6) case 6 : { ZBUFFv06_DCtx* dctx = (prevVersion != newVersion) ? ZBUFFv06_createDCtx() : (ZBUFFv06_DCtx*)*legacyContext; if (dctx==NULL) return ERROR(memory_allocation); ZBUFFv06_decompressInitDictionary(dctx, dict, dictSize); *legacyContext = dctx; return 0; } #endif #if (ZSTD_LEGACY_SUPPORT <= 7) case 7 : { ZBUFFv07_DCtx* dctx = (prevVersion != newVersion) ? ZBUFFv07_createDCtx() : (ZBUFFv07_DCtx*)*legacyContext; if (dctx==NULL) return ERROR(memory_allocation); ZBUFFv07_decompressInitDictionary(dctx, dict, dictSize); *legacyContext = dctx; return 0; } #endif } } MEM_STATIC size_t ZSTD_decompressLegacyStream(void* legacyContext, U32 version, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { DEBUGLOG(5, "ZSTD_decompressLegacyStream for v0.%u", version); switch(version) { default : case 1 : case 2 : case 3 : (void)legacyContext; (void)output; (void)input; return ERROR(version_unsupported); #if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : { ZBUFFv04_DCtx* dctx = (ZBUFFv04_DCtx*) legacyContext; const void* src = (const char*)input->src + input->pos; size_t readSize = input->size - input->pos; void* dst = (char*)output->dst + output->pos; size_t decodedSize = output->size - output->pos; size_t const hintSize = ZBUFFv04_decompressContinue(dctx, dst, &decodedSize, src, &readSize); output->pos += decodedSize; input->pos += readSize; return hintSize; } #endif #if (ZSTD_LEGACY_SUPPORT <= 5) case 5 : { ZBUFFv05_DCtx* dctx = (ZBUFFv05_DCtx*) legacyContext; const void* src = (const char*)input->src + input->pos; size_t readSize = input->size - input->pos; void* dst = (char*)output->dst + output->pos; size_t decodedSize = output->size - output->pos; size_t const hintSize = ZBUFFv05_decompressContinue(dctx, dst, &decodedSize, src, &readSize); output->pos += decodedSize; input->pos += readSize; return hintSize; } #endif #if (ZSTD_LEGACY_SUPPORT <= 6) case 6 : { ZBUFFv06_DCtx* dctx = (ZBUFFv06_DCtx*) legacyContext; const void* src = (const char*)input->src + input->pos; size_t readSize = input->size - input->pos; void* dst = (char*)output->dst + output->pos; size_t decodedSize = output->size - output->pos; size_t const hintSize = ZBUFFv06_decompressContinue(dctx, dst, &decodedSize, src, &readSize); output->pos += decodedSize; input->pos += readSize; return hintSize; } #endif #if (ZSTD_LEGACY_SUPPORT <= 7) case 7 : { ZBUFFv07_DCtx* dctx = (ZBUFFv07_DCtx*) legacyContext; const void* src = (const char*)input->src + input->pos; size_t readSize = input->size - input->pos; void* dst = (char*)output->dst + output->pos; size_t decodedSize = output->size - output->pos; size_t const hintSize = ZBUFFv07_decompressContinue(dctx, dst, &decodedSize, src, &readSize); output->pos += decodedSize; input->pos += readSize; return hintSize; } #endif } } #if defined (__cplusplus) } #endif #endif /* ZSTD_LEGACY_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/legacy/zstd_v01.c0000644000076500000240000021342514601576577022754 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ /****************************************** * Includes ******************************************/ #include /* size_t, ptrdiff_t */ #include "zstd_v01.h" #include "../common/error_private.h" /****************************************** * Static allocation ******************************************/ /* You can statically allocate FSE CTable/DTable as a table of unsigned using below macro */ #define FSE_DTABLE_SIZE_U32(maxTableLog) (1 + (1<2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) * Increasing memory usage improves compression ratio * Reduced memory usage can improve speed, due to cache effect * Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ #define FSE_MAX_MEMORY_USAGE 14 #define FSE_DEFAULT_MEMORY_USAGE 13 /* FSE_MAX_SYMBOL_VALUE : * Maximum symbol value authorized. * Required for proper stack allocation */ #define FSE_MAX_SYMBOL_VALUE 255 /**************************************************************** * template functions type & suffix ****************************************************************/ #define FSE_FUNCTION_TYPE BYTE #define FSE_FUNCTION_EXTENSION /**************************************************************** * Byte symbol type ****************************************************************/ typedef struct { unsigned short newState; unsigned char symbol; unsigned char nbBits; } FSE_decode_t; /* size == U32 */ /**************************************************************** * Compiler specifics ****************************************************************/ #ifdef _MSC_VER /* Visual Studio */ # define FORCE_INLINE static __forceinline # include /* For Visual 2005 */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ #else # define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ # ifdef __GNUC__ # define FORCE_INLINE static inline __attribute__((always_inline)) # else # define FORCE_INLINE static inline # endif # else # define FORCE_INLINE static # endif /* __STDC_VERSION__ */ #endif /**************************************************************** * Includes ****************************************************************/ #include /* malloc, free, qsort */ #include /* memcpy, memset */ #include /* printf (debug) */ #ifndef MEM_ACCESS_MODULE #define MEM_ACCESS_MODULE /**************************************************************** * Basic Types *****************************************************************/ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ # include typedef uint8_t BYTE; typedef uint16_t U16; typedef int16_t S16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; typedef int64_t S64; #else typedef unsigned char BYTE; typedef unsigned short U16; typedef signed short S16; typedef unsigned int U32; typedef signed int S32; typedef unsigned long long U64; typedef signed long long S64; #endif #endif /* MEM_ACCESS_MODULE */ /**************************************************************** * Memory I/O *****************************************************************/ /* FSE_FORCE_MEMORY_ACCESS * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. * The below switch allow to select different access method for improved performance. * Method 0 (default) : use `memcpy()`. Safe and portable. * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. * Method 2 : direct access. This method is portable but violate C standard. * It can generate buggy code on targets generating assembly depending on alignment. * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. * Prefer these methods in priority order (0 > 1 > 2) */ #ifndef FSE_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ # if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) # define FSE_FORCE_MEMORY_ACCESS 2 # elif (defined(__INTEL_COMPILER) && !defined(WIN32)) || \ (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) )) # define FSE_FORCE_MEMORY_ACCESS 1 # endif #endif static unsigned FSE_32bits(void) { return sizeof(void*)==4; } static unsigned FSE_isLittleEndian(void) { const union { U32 i; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ return one.c[0]; } #if defined(FSE_FORCE_MEMORY_ACCESS) && (FSE_FORCE_MEMORY_ACCESS==2) static U16 FSE_read16(const void* memPtr) { return *(const U16*) memPtr; } static U32 FSE_read32(const void* memPtr) { return *(const U32*) memPtr; } static U64 FSE_read64(const void* memPtr) { return *(const U64*) memPtr; } #elif defined(FSE_FORCE_MEMORY_ACCESS) && (FSE_FORCE_MEMORY_ACCESS==1) /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ /* currently only defined for gcc and icc */ typedef union { U16 u16; U32 u32; U64 u64; } __attribute__((packed)) unalign; static U16 FSE_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } static U32 FSE_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } static U64 FSE_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } #else static U16 FSE_read16(const void* memPtr) { U16 val; memcpy(&val, memPtr, sizeof(val)); return val; } static U32 FSE_read32(const void* memPtr) { U32 val; memcpy(&val, memPtr, sizeof(val)); return val; } static U64 FSE_read64(const void* memPtr) { U64 val; memcpy(&val, memPtr, sizeof(val)); return val; } #endif /* FSE_FORCE_MEMORY_ACCESS */ static U16 FSE_readLE16(const void* memPtr) { if (FSE_isLittleEndian()) return FSE_read16(memPtr); else { const BYTE* p = (const BYTE*)memPtr; return (U16)(p[0] + (p[1]<<8)); } } static U32 FSE_readLE32(const void* memPtr) { if (FSE_isLittleEndian()) return FSE_read32(memPtr); else { const BYTE* p = (const BYTE*)memPtr; return (U32)((U32)p[0] + ((U32)p[1]<<8) + ((U32)p[2]<<16) + ((U32)p[3]<<24)); } } static U64 FSE_readLE64(const void* memPtr) { if (FSE_isLittleEndian()) return FSE_read64(memPtr); else { const BYTE* p = (const BYTE*)memPtr; return (U64)((U64)p[0] + ((U64)p[1]<<8) + ((U64)p[2]<<16) + ((U64)p[3]<<24) + ((U64)p[4]<<32) + ((U64)p[5]<<40) + ((U64)p[6]<<48) + ((U64)p[7]<<56)); } } static size_t FSE_readLEST(const void* memPtr) { if (FSE_32bits()) return (size_t)FSE_readLE32(memPtr); else return (size_t)FSE_readLE64(memPtr); } /**************************************************************** * Constants *****************************************************************/ #define FSE_MAX_TABLELOG (FSE_MAX_MEMORY_USAGE-2) #define FSE_MAX_TABLESIZE (1U< FSE_TABLELOG_ABSOLUTE_MAX #error "FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX is not supported" #endif /**************************************************************** * Error Management ****************************************************************/ #define FSE_STATIC_ASSERT(c) { enum { FSE_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ /**************************************************************** * Complex types ****************************************************************/ typedef struct { int deltaFindState; U32 deltaNbBits; } FSE_symbolCompressionTransform; /* total 8 bytes */ typedef U32 DTable_max_t[FSE_DTABLE_SIZE_U32(FSE_MAX_TABLELOG)]; /**************************************************************** * Internal functions ****************************************************************/ FORCE_INLINE unsigned FSE_highbit32 (U32 val) { # if defined(_MSC_VER) /* Visual */ unsigned long r; _BitScanReverse ( &r, val ); return (unsigned) r; # elif defined(__GNUC__) && (GCC_VERSION >= 304) /* GCC Intrinsic */ return __builtin_clz (val) ^ 31; # else /* Software version */ static const unsigned DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; U32 v = val; unsigned r; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; r = DeBruijnClz[ (U32) (v * 0x07C4ACDDU) >> 27]; return r; # endif } /**************************************************************** * Templates ****************************************************************/ /* designed to be included for type-specific functions (template emulation in C) Objective is to write these functions only once, for improved maintenance */ /* safety checks */ #ifndef FSE_FUNCTION_EXTENSION # error "FSE_FUNCTION_EXTENSION must be defined" #endif #ifndef FSE_FUNCTION_TYPE # error "FSE_FUNCTION_TYPE must be defined" #endif /* Function names */ #define FSE_CAT(X,Y) X##Y #define FSE_FUNCTION_NAME(X,Y) FSE_CAT(X,Y) #define FSE_TYPE_NAME(X,Y) FSE_CAT(X,Y) static U32 FSE_tableStep(U32 tableSize) { return (tableSize>>1) + (tableSize>>3) + 3; } #define FSE_DECODE_TYPE FSE_decode_t typedef struct { U16 tableLog; U16 fastMode; } FSE_DTableHeader; /* sizeof U32 */ static size_t FSE_buildDTable (FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) { void* ptr = dt; FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr; FSE_DECODE_TYPE* const tableDecode = (FSE_DECODE_TYPE*)(ptr) + 1; /* because dt is unsigned, 32-bits aligned on 32-bits */ const U32 tableSize = 1 << tableLog; const U32 tableMask = tableSize-1; const U32 step = FSE_tableStep(tableSize); U16 symbolNext[FSE_MAX_SYMBOL_VALUE+1]; U32 position = 0; U32 highThreshold = tableSize-1; const S16 largeLimit= (S16)(1 << (tableLog-1)); U32 noLarge = 1; U32 s; /* Sanity Checks */ if (maxSymbolValue > FSE_MAX_SYMBOL_VALUE) return (size_t)-FSE_ERROR_maxSymbolValue_tooLarge; if (tableLog > FSE_MAX_TABLELOG) return (size_t)-FSE_ERROR_tableLog_tooLarge; /* Init, lay down lowprob symbols */ DTableH[0].tableLog = (U16)tableLog; for (s=0; s<=maxSymbolValue; s++) { if (normalizedCounter[s]==-1) { tableDecode[highThreshold--].symbol = (FSE_FUNCTION_TYPE)s; symbolNext[s] = 1; } else { if (normalizedCounter[s] >= largeLimit) noLarge=0; symbolNext[s] = normalizedCounter[s]; } } /* Spread symbols */ for (s=0; s<=maxSymbolValue; s++) { int i; for (i=0; i highThreshold) position = (position + step) & tableMask; /* lowprob area */ } } if (position!=0) return (size_t)-FSE_ERROR_GENERIC; /* position must reach all cells once, otherwise normalizedCounter is incorrect */ /* Build Decoding table */ { U32 i; for (i=0; ifastMode = (U16)noLarge; return 0; } /****************************************** * FSE byte symbol ******************************************/ #ifndef FSE_COMMONDEFS_ONLY static unsigned FSE_isError(size_t code) { return (code > (size_t)(-FSE_ERROR_maxCode)); } static short FSE_abs(short a) { return a<0? -a : a; } /**************************************************************** * Header bitstream management ****************************************************************/ static size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr, const void* headerBuffer, size_t hbSize) { const BYTE* const istart = (const BYTE*) headerBuffer; const BYTE* const iend = istart + hbSize; const BYTE* ip = istart; int nbBits; int remaining; int threshold; U32 bitStream; int bitCount; unsigned charnum = 0; int previous0 = 0; if (hbSize < 4) return (size_t)-FSE_ERROR_srcSize_wrong; bitStream = FSE_readLE32(ip); nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */ if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX) return (size_t)-FSE_ERROR_tableLog_tooLarge; bitStream >>= 4; bitCount = 4; *tableLogPtr = nbBits; remaining = (1<1) && (charnum<=*maxSVPtr)) { if (previous0) { unsigned n0 = charnum; while ((bitStream & 0xFFFF) == 0xFFFF) { n0+=24; if (ip < iend-5) { ip+=2; bitStream = FSE_readLE32(ip) >> bitCount; } else { bitStream >>= 16; bitCount+=16; } } while ((bitStream & 3) == 3) { n0+=3; bitStream>>=2; bitCount+=2; } n0 += bitStream & 3; bitCount += 2; if (n0 > *maxSVPtr) return (size_t)-FSE_ERROR_maxSymbolValue_tooSmall; while (charnum < n0) normalizedCounter[charnum++] = 0; if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) { ip += bitCount>>3; bitCount &= 7; bitStream = FSE_readLE32(ip) >> bitCount; } else bitStream >>= 2; } { const short max = (short)((2*threshold-1)-remaining); short count; if ((bitStream & (threshold-1)) < (U32)max) { count = (short)(bitStream & (threshold-1)); bitCount += nbBits-1; } else { count = (short)(bitStream & (2*threshold-1)); if (count >= threshold) count -= max; bitCount += nbBits; } count--; /* extra accuracy */ remaining -= FSE_abs(count); normalizedCounter[charnum++] = count; previous0 = !count; while (remaining < threshold) { nbBits--; threshold >>= 1; } { if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) { ip += bitCount>>3; bitCount &= 7; } else { bitCount -= (int)(8 * (iend - 4 - ip)); ip = iend - 4; } bitStream = FSE_readLE32(ip) >> (bitCount & 31); } } } if (remaining != 1) return (size_t)-FSE_ERROR_GENERIC; *maxSVPtr = charnum-1; ip += (bitCount+7)>>3; if ((size_t)(ip-istart) > hbSize) return (size_t)-FSE_ERROR_srcSize_wrong; return ip-istart; } /********************************************************* * Decompression (Byte symbols) *********************************************************/ static size_t FSE_buildDTable_rle (FSE_DTable* dt, BYTE symbolValue) { void* ptr = dt; FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr; FSE_decode_t* const cell = (FSE_decode_t*)(ptr) + 1; /* because dt is unsigned */ DTableH->tableLog = 0; DTableH->fastMode = 0; cell->newState = 0; cell->symbol = symbolValue; cell->nbBits = 0; return 0; } static size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits) { void* ptr = dt; FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr; FSE_decode_t* const dinfo = (FSE_decode_t*)(ptr) + 1; /* because dt is unsigned */ const unsigned tableSize = 1 << nbBits; const unsigned tableMask = tableSize - 1; const unsigned maxSymbolValue = tableMask; unsigned s; /* Sanity checks */ if (nbBits < 1) return (size_t)-FSE_ERROR_GENERIC; /* min size */ /* Build Decoding Table */ DTableH->tableLog = (U16)nbBits; DTableH->fastMode = 1; for (s=0; s<=maxSymbolValue; s++) { dinfo[s].newState = 0; dinfo[s].symbol = (BYTE)s; dinfo[s].nbBits = (BYTE)nbBits; } return 0; } /* FSE_initDStream * Initialize a FSE_DStream_t. * srcBuffer must point at the beginning of an FSE block. * The function result is the size of the FSE_block (== srcSize). * If srcSize is too small, the function will return an errorCode; */ static size_t FSE_initDStream(FSE_DStream_t* bitD, const void* srcBuffer, size_t srcSize) { if (srcSize < 1) return (size_t)-FSE_ERROR_srcSize_wrong; if (srcSize >= sizeof(size_t)) { U32 contain32; bitD->start = (const char*)srcBuffer; bitD->ptr = (const char*)srcBuffer + srcSize - sizeof(size_t); bitD->bitContainer = FSE_readLEST(bitD->ptr); contain32 = ((const BYTE*)srcBuffer)[srcSize-1]; if (contain32 == 0) return (size_t)-FSE_ERROR_GENERIC; /* stop bit not present */ bitD->bitsConsumed = 8 - FSE_highbit32(contain32); } else { U32 contain32; bitD->start = (const char*)srcBuffer; bitD->ptr = bitD->start; bitD->bitContainer = *(const BYTE*)(bitD->start); switch(srcSize) { case 7: bitD->bitContainer += (size_t)(((const BYTE*)(bitD->start))[6]) << (sizeof(size_t)*8 - 16); /* fallthrough */ case 6: bitD->bitContainer += (size_t)(((const BYTE*)(bitD->start))[5]) << (sizeof(size_t)*8 - 24); /* fallthrough */ case 5: bitD->bitContainer += (size_t)(((const BYTE*)(bitD->start))[4]) << (sizeof(size_t)*8 - 32); /* fallthrough */ case 4: bitD->bitContainer += (size_t)(((const BYTE*)(bitD->start))[3]) << 24; /* fallthrough */ case 3: bitD->bitContainer += (size_t)(((const BYTE*)(bitD->start))[2]) << 16; /* fallthrough */ case 2: bitD->bitContainer += (size_t)(((const BYTE*)(bitD->start))[1]) << 8; /* fallthrough */ default:; } contain32 = ((const BYTE*)srcBuffer)[srcSize-1]; if (contain32 == 0) return (size_t)-FSE_ERROR_GENERIC; /* stop bit not present */ bitD->bitsConsumed = 8 - FSE_highbit32(contain32); bitD->bitsConsumed += (U32)(sizeof(size_t) - srcSize)*8; } return srcSize; } /*!FSE_lookBits * Provides next n bits from the bitContainer. * bitContainer is not modified (bits are still present for next read/look) * On 32-bits, maxNbBits==25 * On 64-bits, maxNbBits==57 * return : value extracted. */ static size_t FSE_lookBits(FSE_DStream_t* bitD, U32 nbBits) { const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1; return ((bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> 1) >> ((bitMask-nbBits) & bitMask); } static size_t FSE_lookBitsFast(FSE_DStream_t* bitD, U32 nbBits) /* only if nbBits >= 1 !! */ { const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1; return (bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> (((bitMask+1)-nbBits) & bitMask); } static void FSE_skipBits(FSE_DStream_t* bitD, U32 nbBits) { bitD->bitsConsumed += nbBits; } /*!FSE_readBits * Read next n bits from the bitContainer. * On 32-bits, don't read more than maxNbBits==25 * On 64-bits, don't read more than maxNbBits==57 * Use the fast variant *only* if n >= 1. * return : value extracted. */ static size_t FSE_readBits(FSE_DStream_t* bitD, U32 nbBits) { size_t value = FSE_lookBits(bitD, nbBits); FSE_skipBits(bitD, nbBits); return value; } static size_t FSE_readBitsFast(FSE_DStream_t* bitD, U32 nbBits) /* only if nbBits >= 1 !! */ { size_t value = FSE_lookBitsFast(bitD, nbBits); FSE_skipBits(bitD, nbBits); return value; } static unsigned FSE_reloadDStream(FSE_DStream_t* bitD) { if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* should never happen */ return FSE_DStream_tooFar; if (bitD->ptr >= bitD->start + sizeof(bitD->bitContainer)) { bitD->ptr -= bitD->bitsConsumed >> 3; bitD->bitsConsumed &= 7; bitD->bitContainer = FSE_readLEST(bitD->ptr); return FSE_DStream_unfinished; } if (bitD->ptr == bitD->start) { if (bitD->bitsConsumed < sizeof(bitD->bitContainer)*8) return FSE_DStream_endOfBuffer; return FSE_DStream_completed; } { U32 nbBytes = bitD->bitsConsumed >> 3; U32 result = FSE_DStream_unfinished; if (bitD->ptr - nbBytes < bitD->start) { nbBytes = (U32)(bitD->ptr - bitD->start); /* ptr > start */ result = FSE_DStream_endOfBuffer; } bitD->ptr -= nbBytes; bitD->bitsConsumed -= nbBytes*8; bitD->bitContainer = FSE_readLEST(bitD->ptr); /* reminder : srcSize > sizeof(bitD) */ return result; } } static void FSE_initDState(FSE_DState_t* DStatePtr, FSE_DStream_t* bitD, const FSE_DTable* dt) { const void* ptr = dt; const FSE_DTableHeader* const DTableH = (const FSE_DTableHeader*)ptr; DStatePtr->state = FSE_readBits(bitD, DTableH->tableLog); FSE_reloadDStream(bitD); DStatePtr->table = dt + 1; } static BYTE FSE_decodeSymbol(FSE_DState_t* DStatePtr, FSE_DStream_t* bitD) { const FSE_decode_t DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state]; const U32 nbBits = DInfo.nbBits; BYTE symbol = DInfo.symbol; size_t lowBits = FSE_readBits(bitD, nbBits); DStatePtr->state = DInfo.newState + lowBits; return symbol; } static BYTE FSE_decodeSymbolFast(FSE_DState_t* DStatePtr, FSE_DStream_t* bitD) { const FSE_decode_t DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state]; const U32 nbBits = DInfo.nbBits; BYTE symbol = DInfo.symbol; size_t lowBits = FSE_readBitsFast(bitD, nbBits); DStatePtr->state = DInfo.newState + lowBits; return symbol; } /* FSE_endOfDStream Tells if bitD has reached end of bitStream or not */ static unsigned FSE_endOfDStream(const FSE_DStream_t* bitD) { return ((bitD->ptr == bitD->start) && (bitD->bitsConsumed == sizeof(bitD->bitContainer)*8)); } static unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr) { return DStatePtr->state == 0; } FORCE_INLINE size_t FSE_decompress_usingDTable_generic( void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt, const unsigned fast) { BYTE* const ostart = (BYTE*) dst; BYTE* op = ostart; BYTE* const omax = op + maxDstSize; BYTE* const olimit = omax-3; FSE_DStream_t bitD; FSE_DState_t state1; FSE_DState_t state2; size_t errorCode; /* Init */ errorCode = FSE_initDStream(&bitD, cSrc, cSrcSize); /* replaced last arg by maxCompressed Size */ if (FSE_isError(errorCode)) return errorCode; FSE_initDState(&state1, &bitD, dt); FSE_initDState(&state2, &bitD, dt); #define FSE_GETSYMBOL(statePtr) fast ? FSE_decodeSymbolFast(statePtr, &bitD) : FSE_decodeSymbol(statePtr, &bitD) /* 4 symbols per loop */ for ( ; (FSE_reloadDStream(&bitD)==FSE_DStream_unfinished) && (op sizeof(bitD.bitContainer)*8) /* This test must be static */ FSE_reloadDStream(&bitD); op[1] = FSE_GETSYMBOL(&state2); if (FSE_MAX_TABLELOG*4+7 > sizeof(bitD.bitContainer)*8) /* This test must be static */ { if (FSE_reloadDStream(&bitD) > FSE_DStream_unfinished) { op+=2; break; } } op[2] = FSE_GETSYMBOL(&state1); if (FSE_MAX_TABLELOG*2+7 > sizeof(bitD.bitContainer)*8) /* This test must be static */ FSE_reloadDStream(&bitD); op[3] = FSE_GETSYMBOL(&state2); } /* tail */ /* note : FSE_reloadDStream(&bitD) >= FSE_DStream_partiallyFilled; Ends at exactly FSE_DStream_completed */ while (1) { if ( (FSE_reloadDStream(&bitD)>FSE_DStream_completed) || (op==omax) || (FSE_endOfDStream(&bitD) && (fast || FSE_endOfDState(&state1))) ) break; *op++ = FSE_GETSYMBOL(&state1); if ( (FSE_reloadDStream(&bitD)>FSE_DStream_completed) || (op==omax) || (FSE_endOfDStream(&bitD) && (fast || FSE_endOfDState(&state2))) ) break; *op++ = FSE_GETSYMBOL(&state2); } /* end ? */ if (FSE_endOfDStream(&bitD) && FSE_endOfDState(&state1) && FSE_endOfDState(&state2)) return op-ostart; if (op==omax) return (size_t)-FSE_ERROR_dstSize_tooSmall; /* dst buffer is full, but cSrc unfinished */ return (size_t)-FSE_ERROR_corruptionDetected; } static size_t FSE_decompress_usingDTable(void* dst, size_t originalSize, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt) { FSE_DTableHeader DTableH; memcpy(&DTableH, dt, sizeof(DTableH)); /* memcpy() into local variable, to avoid strict aliasing warning */ /* select fast mode (static) */ if (DTableH.fastMode) return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 1); return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 0); } static size_t FSE_decompress(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize) { const BYTE* const istart = (const BYTE*)cSrc; const BYTE* ip = istart; short counting[FSE_MAX_SYMBOL_VALUE+1]; DTable_max_t dt; /* Static analyzer seems unable to understand this table will be properly initialized later */ unsigned tableLog; unsigned maxSymbolValue = FSE_MAX_SYMBOL_VALUE; size_t errorCode; if (cSrcSize<2) return (size_t)-FSE_ERROR_srcSize_wrong; /* too small input size */ /* normal FSE decoding mode */ errorCode = FSE_readNCount (counting, &maxSymbolValue, &tableLog, istart, cSrcSize); if (FSE_isError(errorCode)) return errorCode; if (errorCode >= cSrcSize) return (size_t)-FSE_ERROR_srcSize_wrong; /* too small input size */ ip += errorCode; cSrcSize -= errorCode; errorCode = FSE_buildDTable (dt, counting, maxSymbolValue, tableLog); if (FSE_isError(errorCode)) return errorCode; /* always return, even if it is an error code */ return FSE_decompress_usingDTable (dst, maxDstSize, ip, cSrcSize, dt); } /* ******************************************************* * Huff0 : Huffman block compression *********************************************************/ #define HUF_MAX_SYMBOL_VALUE 255 #define HUF_DEFAULT_TABLELOG 12 /* used by default, when not specified */ #define HUF_MAX_TABLELOG 12 /* max possible tableLog; for allocation purpose; can be modified */ #define HUF_ABSOLUTEMAX_TABLELOG 16 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ #if (HUF_MAX_TABLELOG > HUF_ABSOLUTEMAX_TABLELOG) # error "HUF_MAX_TABLELOG is too large !" #endif typedef struct HUF_CElt_s { U16 val; BYTE nbBits; } HUF_CElt ; typedef struct nodeElt_s { U32 count; U16 parent; BYTE byte; BYTE nbBits; } nodeElt; /* ******************************************************* * Huff0 : Huffman block decompression *********************************************************/ typedef struct { BYTE byte; BYTE nbBits; } HUF_DElt; static size_t HUF_readDTable (U16* DTable, const void* src, size_t srcSize) { BYTE huffWeight[HUF_MAX_SYMBOL_VALUE + 1]; U32 rankVal[HUF_ABSOLUTEMAX_TABLELOG + 1]; /* large enough for values from 0 to 16 */ U32 weightTotal; U32 maxBits; const BYTE* ip = (const BYTE*) src; size_t iSize; size_t oSize; U32 n; U32 nextRankStart; void* ptr = DTable+1; HUF_DElt* const dt = (HUF_DElt*)ptr; if (!srcSize) return (size_t)-FSE_ERROR_srcSize_wrong; iSize = ip[0]; FSE_STATIC_ASSERT(sizeof(HUF_DElt) == sizeof(U16)); /* if compilation fails here, assertion is false */ //memset(huffWeight, 0, sizeof(huffWeight)); /* should not be necessary, but some analyzer complain ... */ if (iSize >= 128) /* special header */ { if (iSize >= (242)) /* RLE */ { static int l[14] = { 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 127, 128 }; oSize = l[iSize-242]; memset(huffWeight, 1, sizeof(huffWeight)); iSize = 0; } else /* Incompressible */ { oSize = iSize - 127; iSize = ((oSize+1)/2); if (iSize+1 > srcSize) return (size_t)-FSE_ERROR_srcSize_wrong; ip += 1; for (n=0; n> 4; huffWeight[n+1] = ip[n/2] & 15; } } } else /* header compressed with FSE (normal case) */ { if (iSize+1 > srcSize) return (size_t)-FSE_ERROR_srcSize_wrong; oSize = FSE_decompress(huffWeight, HUF_MAX_SYMBOL_VALUE, ip+1, iSize); /* max 255 values decoded, last one is implied */ if (FSE_isError(oSize)) return oSize; } /* collect weight stats */ memset(rankVal, 0, sizeof(rankVal)); weightTotal = 0; for (n=0; n= HUF_ABSOLUTEMAX_TABLELOG) return (size_t)-FSE_ERROR_corruptionDetected; rankVal[huffWeight[n]]++; weightTotal += (1 << huffWeight[n]) >> 1; } if (weightTotal == 0) return (size_t)-FSE_ERROR_corruptionDetected; /* get last non-null symbol weight (implied, total must be 2^n) */ maxBits = FSE_highbit32(weightTotal) + 1; if (maxBits > DTable[0]) return (size_t)-FSE_ERROR_tableLog_tooLarge; /* DTable is too small */ DTable[0] = (U16)maxBits; { U32 total = 1 << maxBits; U32 rest = total - weightTotal; U32 verif = 1 << FSE_highbit32(rest); U32 lastWeight = FSE_highbit32(rest) + 1; if (verif != rest) return (size_t)-FSE_ERROR_corruptionDetected; /* last value must be a clean power of 2 */ huffWeight[oSize] = (BYTE)lastWeight; rankVal[lastWeight]++; } /* check tree construction validity */ if ((rankVal[1] < 2) || (rankVal[1] & 1)) return (size_t)-FSE_ERROR_corruptionDetected; /* by construction : at least 2 elts of rank 1, must be even */ /* Prepare ranks */ nextRankStart = 0; for (n=1; n<=maxBits; n++) { U32 current = nextRankStart; nextRankStart += (rankVal[n] << (n-1)); rankVal[n] = current; } /* fill DTable */ for (n=0; n<=oSize; n++) { const U32 w = huffWeight[n]; const U32 length = (1 << w) >> 1; U32 i; HUF_DElt D; D.byte = (BYTE)n; D.nbBits = (BYTE)(maxBits + 1 - w); for (i = rankVal[w]; i < rankVal[w] + length; i++) dt[i] = D; rankVal[w] += length; } return iSize+1; } static BYTE HUF_decodeSymbol(FSE_DStream_t* Dstream, const HUF_DElt* dt, const U32 dtLog) { const size_t val = FSE_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */ const BYTE c = dt[val].byte; FSE_skipBits(Dstream, dt[val].nbBits); return c; } static size_t HUF_decompress_usingDTable( /* -3% slower when non static */ void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const U16* DTable) { if (cSrcSize < 6) return (size_t)-FSE_ERROR_srcSize_wrong; { BYTE* const ostart = (BYTE*) dst; BYTE* op = ostart; BYTE* const omax = op + maxDstSize; BYTE* const olimit = maxDstSize < 15 ? op : omax-15; const void* ptr = DTable; const HUF_DElt* const dt = (const HUF_DElt*)(ptr)+1; const U32 dtLog = DTable[0]; size_t errorCode; U32 reloadStatus; /* Init */ const U16* jumpTable = (const U16*)cSrc; const size_t length1 = FSE_readLE16(jumpTable); const size_t length2 = FSE_readLE16(jumpTable+1); const size_t length3 = FSE_readLE16(jumpTable+2); const size_t length4 = cSrcSize - 6 - length1 - length2 - length3; /* check coherency !! */ const char* const start1 = (const char*)(cSrc) + 6; const char* const start2 = start1 + length1; const char* const start3 = start2 + length2; const char* const start4 = start3 + length3; FSE_DStream_t bitD1, bitD2, bitD3, bitD4; if (length1+length2+length3+6 >= cSrcSize) return (size_t)-FSE_ERROR_srcSize_wrong; errorCode = FSE_initDStream(&bitD1, start1, length1); if (FSE_isError(errorCode)) return errorCode; errorCode = FSE_initDStream(&bitD2, start2, length2); if (FSE_isError(errorCode)) return errorCode; errorCode = FSE_initDStream(&bitD3, start3, length3); if (FSE_isError(errorCode)) return errorCode; errorCode = FSE_initDStream(&bitD4, start4, length4); if (FSE_isError(errorCode)) return errorCode; reloadStatus=FSE_reloadDStream(&bitD2); /* 16 symbols per loop */ for ( ; (reloadStatus12)) FSE_reloadDStream(&Dstream) #define HUF_DECODE_SYMBOL_2(n, Dstream) \ op[n] = HUF_decodeSymbol(&Dstream, dt, dtLog); \ if (FSE_32bits()) FSE_reloadDStream(&Dstream) HUF_DECODE_SYMBOL_1( 0, bitD1); HUF_DECODE_SYMBOL_1( 1, bitD2); HUF_DECODE_SYMBOL_1( 2, bitD3); HUF_DECODE_SYMBOL_1( 3, bitD4); HUF_DECODE_SYMBOL_2( 4, bitD1); HUF_DECODE_SYMBOL_2( 5, bitD2); HUF_DECODE_SYMBOL_2( 6, bitD3); HUF_DECODE_SYMBOL_2( 7, bitD4); HUF_DECODE_SYMBOL_1( 8, bitD1); HUF_DECODE_SYMBOL_1( 9, bitD2); HUF_DECODE_SYMBOL_1(10, bitD3); HUF_DECODE_SYMBOL_1(11, bitD4); HUF_DECODE_SYMBOL_0(12, bitD1); HUF_DECODE_SYMBOL_0(13, bitD2); HUF_DECODE_SYMBOL_0(14, bitD3); HUF_DECODE_SYMBOL_0(15, bitD4); } if (reloadStatus!=FSE_DStream_completed) /* not complete : some bitStream might be FSE_DStream_unfinished */ return (size_t)-FSE_ERROR_corruptionDetected; /* tail */ { /* bitTail = bitD1; */ /* *much* slower : -20% !??! */ FSE_DStream_t bitTail; bitTail.ptr = bitD1.ptr; bitTail.bitsConsumed = bitD1.bitsConsumed; bitTail.bitContainer = bitD1.bitContainer; /* required in case of FSE_DStream_endOfBuffer */ bitTail.start = start1; for ( ; (FSE_reloadDStream(&bitTail) < FSE_DStream_completed) && (op= cSrcSize) return (size_t)-FSE_ERROR_srcSize_wrong; ip += errorCode; cSrcSize -= errorCode; return HUF_decompress_usingDTable (dst, maxDstSize, ip, cSrcSize, DTable); } #endif /* FSE_COMMONDEFS_ONLY */ /* zstd - standard compression library Copyright (C) 2014-2015, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - zstd source repository : https://github.com/Cyan4973/zstd - ztsd public forum : https://groups.google.com/forum/#!forum/lz4c */ /**************************************************************** * Tuning parameters *****************************************************************/ /* MEMORY_USAGE : * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) * Increasing memory usage improves compression ratio * Reduced memory usage can improve speed, due to cache effect */ #define ZSTD_MEMORY_USAGE 17 /************************************** CPU Feature Detection **************************************/ /* * Automated efficient unaligned memory access detection * Based on known hardware architectures * This list will be updated thanks to feedbacks */ #if defined(CPU_HAS_EFFICIENT_UNALIGNED_MEMORY_ACCESS) \ || defined(__ARM_FEATURE_UNALIGNED) \ || defined(__i386__) || defined(__x86_64__) \ || defined(_M_IX86) || defined(_M_X64) \ || defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_8__) \ || (defined(_M_ARM) && (_M_ARM >= 7)) # define ZSTD_UNALIGNED_ACCESS 1 #else # define ZSTD_UNALIGNED_ACCESS 0 #endif /******************************************************** * Includes *********************************************************/ #include /* calloc */ #include /* memcpy, memmove */ #include /* debug : printf */ /******************************************************** * Compiler specifics *********************************************************/ #ifdef __AVX2__ # include /* AVX2 intrinsics */ #endif #ifdef _MSC_VER /* Visual Studio */ # include /* For Visual 2005 */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4324) /* disable: C4324: padded structure */ #endif #ifndef MEM_ACCESS_MODULE #define MEM_ACCESS_MODULE /******************************************************** * Basic Types *********************************************************/ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ # include typedef uint8_t BYTE; typedef uint16_t U16; typedef int16_t S16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; #else typedef unsigned char BYTE; typedef unsigned short U16; typedef signed short S16; typedef unsigned int U32; typedef signed int S32; typedef unsigned long long U64; #endif #endif /* MEM_ACCESS_MODULE */ /******************************************************** * Constants *********************************************************/ static const U32 ZSTD_magicNumber = 0xFD2FB51E; /* 3rd version : seqNb header */ #define HASH_LOG (ZSTD_MEMORY_USAGE - 2) #define HASH_TABLESIZE (1 << HASH_LOG) #define HASH_MASK (HASH_TABLESIZE - 1) #define KNUTH 2654435761 #define BIT7 128 #define BIT6 64 #define BIT5 32 #define BIT4 16 #define KB *(1 <<10) #define MB *(1 <<20) #define GB *(1U<<30) #define BLOCKSIZE (128 KB) /* define, for static allocation */ #define WORKPLACESIZE (BLOCKSIZE*3) #define MINMATCH 4 #define MLbits 7 #define LLbits 6 #define Offbits 5 #define MaxML ((1<>3]; #else U32 hashTable[HASH_TABLESIZE]; #endif BYTE buffer[WORKPLACESIZE]; } cctxi_t; /************************************** * Error Management **************************************/ /* published entry point */ unsigned ZSTDv01_isError(size_t code) { return ERR_isError(code); } /************************************** * Tool functions **************************************/ #define ZSTD_VERSION_MAJOR 0 /* for breaking interface changes */ #define ZSTD_VERSION_MINOR 1 /* for new (non-breaking) interface capabilities */ #define ZSTD_VERSION_RELEASE 3 /* for tweaks, bug-fixes, or development */ #define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE) /************************************************************** * Decompression code **************************************************************/ static size_t ZSTDv01_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* bpPtr) { const BYTE* const in = (const BYTE* const)src; BYTE headerFlags; U32 cSize; if (srcSize < 3) return ERROR(srcSize_wrong); headerFlags = *in; cSize = in[2] + (in[1]<<8) + ((in[0] & 7)<<16); bpPtr->blockType = (blockType_t)(headerFlags >> 6); bpPtr->origSize = (bpPtr->blockType == bt_rle) ? cSize : 0; if (bpPtr->blockType == bt_end) return 0; if (bpPtr->blockType == bt_rle) return 1; return cSize; } static size_t ZSTD_copyUncompressedBlock(void* dst, size_t maxDstSize, const void* src, size_t srcSize) { if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall); if (srcSize > 0) { memcpy(dst, src, srcSize); } return srcSize; } static size_t ZSTD_decompressLiterals(void* ctx, void* dst, size_t maxDstSize, const void* src, size_t srcSize) { BYTE* op = (BYTE*)dst; BYTE* const oend = op + maxDstSize; const BYTE* ip = (const BYTE*)src; size_t errorCode; size_t litSize; /* check : minimum 2, for litSize, +1, for content */ if (srcSize <= 3) return ERROR(corruption_detected); litSize = ip[1] + (ip[0]<<8); litSize += ((ip[-3] >> 3) & 7) << 16; /* mmmmh.... */ op = oend - litSize; (void)ctx; if (litSize > maxDstSize) return ERROR(dstSize_tooSmall); errorCode = HUF_decompress(op, litSize, ip+2, srcSize-2); if (FSE_isError(errorCode)) return ERROR(GENERIC); return litSize; } static size_t ZSTDv01_decodeLiteralsBlock(void* ctx, void* dst, size_t maxDstSize, const BYTE** litStart, size_t* litSize, const void* src, size_t srcSize) { const BYTE* const istart = (const BYTE* const)src; const BYTE* ip = istart; BYTE* const ostart = (BYTE* const)dst; BYTE* const oend = ostart + maxDstSize; blockProperties_t litbp; size_t litcSize = ZSTDv01_getcBlockSize(src, srcSize, &litbp); if (ZSTDv01_isError(litcSize)) return litcSize; if (litcSize > srcSize - ZSTD_blockHeaderSize) return ERROR(srcSize_wrong); ip += ZSTD_blockHeaderSize; switch(litbp.blockType) { case bt_raw: *litStart = ip; ip += litcSize; *litSize = litcSize; break; case bt_rle: { size_t rleSize = litbp.origSize; if (rleSize>maxDstSize) return ERROR(dstSize_tooSmall); if (!srcSize) return ERROR(srcSize_wrong); if (rleSize > 0) { memset(oend - rleSize, *ip, rleSize); } *litStart = oend - rleSize; *litSize = rleSize; ip++; break; } case bt_compressed: { size_t decodedLitSize = ZSTD_decompressLiterals(ctx, dst, maxDstSize, ip, litcSize); if (ZSTDv01_isError(decodedLitSize)) return decodedLitSize; *litStart = oend - decodedLitSize; *litSize = decodedLitSize; ip += litcSize; break; } case bt_end: default: return ERROR(GENERIC); } return ip-istart; } static size_t ZSTDv01_decodeSeqHeaders(int* nbSeq, const BYTE** dumpsPtr, size_t* dumpsLengthPtr, FSE_DTable* DTableLL, FSE_DTable* DTableML, FSE_DTable* DTableOffb, const void* src, size_t srcSize) { const BYTE* const istart = (const BYTE* const)src; const BYTE* ip = istart; const BYTE* const iend = istart + srcSize; U32 LLtype, Offtype, MLtype; U32 LLlog, Offlog, MLlog; size_t dumpsLength; /* check */ if (srcSize < 5) return ERROR(srcSize_wrong); /* SeqHead */ *nbSeq = ZSTD_readLE16(ip); ip+=2; LLtype = *ip >> 6; Offtype = (*ip >> 4) & 3; MLtype = (*ip >> 2) & 3; if (*ip & 2) { dumpsLength = ip[2]; dumpsLength += ip[1] << 8; ip += 3; } else { dumpsLength = ip[1]; dumpsLength += (ip[0] & 1) << 8; ip += 2; } *dumpsPtr = ip; ip += dumpsLength; *dumpsLengthPtr = dumpsLength; /* check */ if (ip > iend-3) return ERROR(srcSize_wrong); /* min : all 3 are "raw", hence no header, but at least xxLog bits per type */ /* sequences */ { S16 norm[MaxML+1]; /* assumption : MaxML >= MaxLL and MaxOff */ size_t headerSize; /* Build DTables */ switch(LLtype) { case bt_rle : LLlog = 0; FSE_buildDTable_rle(DTableLL, *ip++); break; case bt_raw : LLlog = LLbits; FSE_buildDTable_raw(DTableLL, LLbits); break; default : { U32 max = MaxLL; headerSize = FSE_readNCount(norm, &max, &LLlog, ip, iend-ip); if (FSE_isError(headerSize)) return ERROR(GENERIC); if (LLlog > LLFSELog) return ERROR(corruption_detected); ip += headerSize; FSE_buildDTable(DTableLL, norm, max, LLlog); } } switch(Offtype) { case bt_rle : Offlog = 0; if (ip > iend-2) return ERROR(srcSize_wrong); /* min : "raw", hence no header, but at least xxLog bits */ FSE_buildDTable_rle(DTableOffb, *ip++); break; case bt_raw : Offlog = Offbits; FSE_buildDTable_raw(DTableOffb, Offbits); break; default : { U32 max = MaxOff; headerSize = FSE_readNCount(norm, &max, &Offlog, ip, iend-ip); if (FSE_isError(headerSize)) return ERROR(GENERIC); if (Offlog > OffFSELog) return ERROR(corruption_detected); ip += headerSize; FSE_buildDTable(DTableOffb, norm, max, Offlog); } } switch(MLtype) { case bt_rle : MLlog = 0; if (ip > iend-2) return ERROR(srcSize_wrong); /* min : "raw", hence no header, but at least xxLog bits */ FSE_buildDTable_rle(DTableML, *ip++); break; case bt_raw : MLlog = MLbits; FSE_buildDTable_raw(DTableML, MLbits); break; default : { U32 max = MaxML; headerSize = FSE_readNCount(norm, &max, &MLlog, ip, iend-ip); if (FSE_isError(headerSize)) return ERROR(GENERIC); if (MLlog > MLFSELog) return ERROR(corruption_detected); ip += headerSize; FSE_buildDTable(DTableML, norm, max, MLlog); } } } return ip-istart; } typedef struct { size_t litLength; size_t offset; size_t matchLength; } seq_t; typedef struct { FSE_DStream_t DStream; FSE_DState_t stateLL; FSE_DState_t stateOffb; FSE_DState_t stateML; size_t prevOffset; const BYTE* dumps; const BYTE* dumpsEnd; } seqState_t; static void ZSTD_decodeSequence(seq_t* seq, seqState_t* seqState) { size_t litLength; size_t prevOffset; size_t offset; size_t matchLength; const BYTE* dumps = seqState->dumps; const BYTE* const de = seqState->dumpsEnd; /* Literal length */ litLength = FSE_decodeSymbol(&(seqState->stateLL), &(seqState->DStream)); prevOffset = litLength ? seq->offset : seqState->prevOffset; seqState->prevOffset = seq->offset; if (litLength == MaxLL) { const U32 add = dumpsstateOffb), &(seqState->DStream)); if (ZSTD_32bits()) FSE_reloadDStream(&(seqState->DStream)); nbBits = offsetCode - 1; if (offsetCode==0) nbBits = 0; /* cmove */ offset = ((size_t)1 << (nbBits & ((sizeof(offset)*8)-1))) + FSE_readBits(&(seqState->DStream), nbBits); if (ZSTD_32bits()) FSE_reloadDStream(&(seqState->DStream)); if (offsetCode==0) offset = prevOffset; } /* MatchLength */ matchLength = FSE_decodeSymbol(&(seqState->stateML), &(seqState->DStream)); if (matchLength == MaxML) { const U32 add = dumpslitLength = litLength; seq->offset = offset; seq->matchLength = matchLength; seqState->dumps = dumps; } static size_t ZSTD_execSequence(BYTE* op, seq_t sequence, const BYTE** litPtr, const BYTE* const litLimit, BYTE* const base, BYTE* const oend) { static const int dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4}; /* added */ static const int dec64table[] = {8, 8, 8, 7, 8, 9,10,11}; /* subtracted */ const BYTE* const ostart = op; const size_t litLength = sequence.litLength; BYTE* const endMatch = op + litLength + sequence.matchLength; /* risk : address space overflow (32-bits) */ const BYTE* const litEnd = *litPtr + litLength; /* check */ if (endMatch > oend) return ERROR(dstSize_tooSmall); /* overwrite beyond dst buffer */ if (litEnd > litLimit) return ERROR(corruption_detected); if (sequence.matchLength > (size_t)(*litPtr-op)) return ERROR(dstSize_tooSmall); /* overwrite literal segment */ /* copy Literals */ if (((size_t)(*litPtr - op) < 8) || ((size_t)(oend-litEnd) < 8) || (op+litLength > oend-8)) memmove(op, *litPtr, litLength); /* overwrite risk */ else ZSTD_wildcopy(op, *litPtr, litLength); op += litLength; *litPtr = litEnd; /* update for next sequence */ /* check : last match must be at a minimum distance of 8 from end of dest buffer */ if (oend-op < 8) return ERROR(dstSize_tooSmall); /* copy Match */ { const U32 overlapRisk = (((size_t)(litEnd - endMatch)) < 12); const BYTE* match = op - sequence.offset; /* possible underflow at op - offset ? */ size_t qutt = 12; U64 saved[2]; /* check */ if (match < base) return ERROR(corruption_detected); if (sequence.offset > (size_t)base) return ERROR(corruption_detected); /* save beginning of literal sequence, in case of write overlap */ if (overlapRisk) { if ((endMatch + qutt) > oend) qutt = oend-endMatch; memcpy(saved, endMatch, qutt); } if (sequence.offset < 8) { const int dec64 = dec64table[sequence.offset]; op[0] = match[0]; op[1] = match[1]; op[2] = match[2]; op[3] = match[3]; match += dec32table[sequence.offset]; ZSTD_copy4(op+4, match); match -= dec64; } else { ZSTD_copy8(op, match); } op += 8; match += 8; if (endMatch > oend-(16-MINMATCH)) { if (op < oend-8) { ZSTD_wildcopy(op, match, (oend-8) - op); match += (oend-8) - op; op = oend-8; } while (opLLTable; U32* DTableML = dctx->MLTable; U32* DTableOffb = dctx->OffTable; BYTE* const base = (BYTE*) (dctx->base); /* Build Decoding Tables */ errorCode = ZSTDv01_decodeSeqHeaders(&nbSeq, &dumps, &dumpsLength, DTableLL, DTableML, DTableOffb, ip, iend-ip); if (ZSTDv01_isError(errorCode)) return errorCode; ip += errorCode; /* Regen sequences */ { seq_t sequence; seqState_t seqState; memset(&sequence, 0, sizeof(sequence)); seqState.dumps = dumps; seqState.dumpsEnd = dumps + dumpsLength; seqState.prevOffset = 1; errorCode = FSE_initDStream(&(seqState.DStream), ip, iend-ip); if (FSE_isError(errorCode)) return ERROR(corruption_detected); FSE_initDState(&(seqState.stateLL), &(seqState.DStream), DTableLL); FSE_initDState(&(seqState.stateOffb), &(seqState.DStream), DTableOffb); FSE_initDState(&(seqState.stateML), &(seqState.DStream), DTableML); for ( ; (FSE_reloadDStream(&(seqState.DStream)) <= FSE_DStream_completed) && (nbSeq>0) ; ) { size_t oneSeqSize; nbSeq--; ZSTD_decodeSequence(&sequence, &seqState); oneSeqSize = ZSTD_execSequence(op, sequence, &litPtr, litEnd, base, oend); if (ZSTDv01_isError(oneSeqSize)) return oneSeqSize; op += oneSeqSize; } /* check if reached exact end */ if ( !FSE_endOfDStream(&(seqState.DStream)) ) return ERROR(corruption_detected); /* requested too much : data is corrupted */ if (nbSeq<0) return ERROR(corruption_detected); /* requested too many sequences : data is corrupted */ /* last literal segment */ { size_t lastLLSize = litEnd - litPtr; if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall); if (lastLLSize > 0) { if (op != litPtr) memmove(op, litPtr, lastLLSize); op += lastLLSize; } } } return op-ostart; } static size_t ZSTD_decompressBlock( void* ctx, void* dst, size_t maxDstSize, const void* src, size_t srcSize) { /* blockType == blockCompressed, srcSize is trusted */ const BYTE* ip = (const BYTE*)src; const BYTE* litPtr = NULL; size_t litSize = 0; size_t errorCode; /* Decode literals sub-block */ errorCode = ZSTDv01_decodeLiteralsBlock(ctx, dst, maxDstSize, &litPtr, &litSize, src, srcSize); if (ZSTDv01_isError(errorCode)) return errorCode; ip += errorCode; srcSize -= errorCode; return ZSTD_decompressSequences(ctx, dst, maxDstSize, ip, srcSize, litPtr, litSize); } size_t ZSTDv01_decompressDCtx(void* ctx, void* dst, size_t maxDstSize, const void* src, size_t srcSize) { const BYTE* ip = (const BYTE*)src; const BYTE* iend = ip + srcSize; BYTE* const ostart = (BYTE* const)dst; BYTE* op = ostart; BYTE* const oend = ostart + maxDstSize; size_t remainingSize = srcSize; U32 magicNumber; size_t errorCode=0; blockProperties_t blockProperties; /* Frame Header */ if (srcSize < ZSTD_frameHeaderSize+ZSTD_blockHeaderSize) return ERROR(srcSize_wrong); magicNumber = ZSTD_readBE32(src); if (magicNumber != ZSTD_magicNumber) return ERROR(prefix_unknown); ip += ZSTD_frameHeaderSize; remainingSize -= ZSTD_frameHeaderSize; /* Loop on each block */ while (1) { size_t blockSize = ZSTDv01_getcBlockSize(ip, iend-ip, &blockProperties); if (ZSTDv01_isError(blockSize)) return blockSize; ip += ZSTD_blockHeaderSize; remainingSize -= ZSTD_blockHeaderSize; if (blockSize > remainingSize) return ERROR(srcSize_wrong); switch(blockProperties.blockType) { case bt_compressed: errorCode = ZSTD_decompressBlock(ctx, op, oend-op, ip, blockSize); break; case bt_raw : errorCode = ZSTD_copyUncompressedBlock(op, oend-op, ip, blockSize); break; case bt_rle : return ERROR(GENERIC); /* not yet supported */ break; case bt_end : /* end of frame */ if (remainingSize) return ERROR(srcSize_wrong); break; default: return ERROR(GENERIC); } if (blockSize == 0) break; /* bt_end */ if (ZSTDv01_isError(errorCode)) return errorCode; op += errorCode; ip += blockSize; remainingSize -= blockSize; } return op-ostart; } size_t ZSTDv01_decompress(void* dst, size_t maxDstSize, const void* src, size_t srcSize) { dctx_t ctx; ctx.base = dst; return ZSTDv01_decompressDCtx(&ctx, dst, maxDstSize, src, srcSize); } /* ZSTD_errorFrameSizeInfoLegacy() : assumes `cSize` and `dBound` are _not_ NULL */ static void ZSTD_errorFrameSizeInfoLegacy(size_t* cSize, unsigned long long* dBound, size_t ret) { *cSize = ret; *dBound = ZSTD_CONTENTSIZE_ERROR; } void ZSTDv01_findFrameSizeInfoLegacy(const void *src, size_t srcSize, size_t* cSize, unsigned long long* dBound) { const BYTE* ip = (const BYTE*)src; size_t remainingSize = srcSize; size_t nbBlocks = 0; U32 magicNumber; blockProperties_t blockProperties; /* Frame Header */ if (srcSize < ZSTD_frameHeaderSize+ZSTD_blockHeaderSize) { ZSTD_errorFrameSizeInfoLegacy(cSize, dBound, ERROR(srcSize_wrong)); return; } magicNumber = ZSTD_readBE32(src); if (magicNumber != ZSTD_magicNumber) { ZSTD_errorFrameSizeInfoLegacy(cSize, dBound, ERROR(prefix_unknown)); return; } ip += ZSTD_frameHeaderSize; remainingSize -= ZSTD_frameHeaderSize; /* Loop on each block */ while (1) { size_t blockSize = ZSTDv01_getcBlockSize(ip, remainingSize, &blockProperties); if (ZSTDv01_isError(blockSize)) { ZSTD_errorFrameSizeInfoLegacy(cSize, dBound, blockSize); return; } ip += ZSTD_blockHeaderSize; remainingSize -= ZSTD_blockHeaderSize; if (blockSize > remainingSize) { ZSTD_errorFrameSizeInfoLegacy(cSize, dBound, ERROR(srcSize_wrong)); return; } if (blockSize == 0) break; /* bt_end */ ip += blockSize; remainingSize -= blockSize; nbBlocks++; } *cSize = ip - (const BYTE*)src; *dBound = nbBlocks * BLOCKSIZE; } /******************************* * Streaming Decompression API *******************************/ size_t ZSTDv01_resetDCtx(ZSTDv01_Dctx* dctx) { dctx->expected = ZSTD_frameHeaderSize; dctx->phase = 0; dctx->previousDstEnd = NULL; dctx->base = NULL; return 0; } ZSTDv01_Dctx* ZSTDv01_createDCtx(void) { ZSTDv01_Dctx* dctx = (ZSTDv01_Dctx*)malloc(sizeof(ZSTDv01_Dctx)); if (dctx==NULL) return NULL; ZSTDv01_resetDCtx(dctx); return dctx; } size_t ZSTDv01_freeDCtx(ZSTDv01_Dctx* dctx) { free(dctx); return 0; } size_t ZSTDv01_nextSrcSizeToDecompress(ZSTDv01_Dctx* dctx) { return ((dctx_t*)dctx)->expected; } size_t ZSTDv01_decompressContinue(ZSTDv01_Dctx* dctx, void* dst, size_t maxDstSize, const void* src, size_t srcSize) { dctx_t* ctx = (dctx_t*)dctx; /* Sanity check */ if (srcSize != ctx->expected) return ERROR(srcSize_wrong); if (dst != ctx->previousDstEnd) /* not contiguous */ ctx->base = dst; /* Decompress : frame header */ if (ctx->phase == 0) { /* Check frame magic header */ U32 magicNumber = ZSTD_readBE32(src); if (magicNumber != ZSTD_magicNumber) return ERROR(prefix_unknown); ctx->phase = 1; ctx->expected = ZSTD_blockHeaderSize; return 0; } /* Decompress : block header */ if (ctx->phase == 1) { blockProperties_t bp; size_t blockSize = ZSTDv01_getcBlockSize(src, ZSTD_blockHeaderSize, &bp); if (ZSTDv01_isError(blockSize)) return blockSize; if (bp.blockType == bt_end) { ctx->expected = 0; ctx->phase = 0; } else { ctx->expected = blockSize; ctx->bType = bp.blockType; ctx->phase = 2; } return 0; } /* Decompress : block content */ { size_t rSize; switch(ctx->bType) { case bt_compressed: rSize = ZSTD_decompressBlock(ctx, dst, maxDstSize, src, srcSize); break; case bt_raw : rSize = ZSTD_copyUncompressedBlock(dst, maxDstSize, src, srcSize); break; case bt_rle : return ERROR(GENERIC); /* not yet handled */ break; case bt_end : /* should never happen (filtered at phase 1) */ rSize = 0; break; default: return ERROR(GENERIC); } ctx->phase = 1; ctx->expected = ZSTD_blockHeaderSize; ctx->previousDstEnd = (void*)( ((char*)dst) + rSize); return rSize; } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/legacy/zstd_v01.h0000644000076500000240000000733414601576577022761 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #ifndef ZSTD_V01_H_28739879432 #define ZSTD_V01_H_28739879432 #if defined (__cplusplus) extern "C" { #endif /* ************************************* * Includes ***************************************/ #include /* size_t */ /* ************************************* * Simple one-step function ***************************************/ /** ZSTDv01_decompress() : decompress ZSTD frames compliant with v0.1.x format compressedSize : is the exact source size maxOriginalSize : is the size of the 'dst' buffer, which must be already allocated. It must be equal or larger than originalSize, otherwise decompression will fail. return : the number of bytes decompressed into destination buffer (originalSize) or an errorCode if it fails (which can be tested using ZSTDv01_isError()) */ size_t ZSTDv01_decompress( void* dst, size_t maxOriginalSize, const void* src, size_t compressedSize); /** ZSTDv01_findFrameSizeInfoLegacy() : get the source length and decompressed bound of a ZSTD frame compliant with v0.1.x format srcSize : The size of the 'src' buffer, at least as large as the frame pointed to by 'src' cSize (output parameter) : the number of bytes that would be read to decompress this frame or an error code if it fails (which can be tested using ZSTDv01_isError()) dBound (output parameter) : an upper-bound for the decompressed size of the data in the frame or ZSTD_CONTENTSIZE_ERROR if an error occurs note : assumes `cSize` and `dBound` are _not_ NULL. */ void ZSTDv01_findFrameSizeInfoLegacy(const void *src, size_t srcSize, size_t* cSize, unsigned long long* dBound); /** ZSTDv01_isError() : tells if the result of ZSTDv01_decompress() is an error */ unsigned ZSTDv01_isError(size_t code); /* ************************************* * Advanced functions ***************************************/ typedef struct ZSTDv01_Dctx_s ZSTDv01_Dctx; ZSTDv01_Dctx* ZSTDv01_createDCtx(void); size_t ZSTDv01_freeDCtx(ZSTDv01_Dctx* dctx); size_t ZSTDv01_decompressDCtx(void* ctx, void* dst, size_t maxOriginalSize, const void* src, size_t compressedSize); /* ************************************* * Streaming functions ***************************************/ size_t ZSTDv01_resetDCtx(ZSTDv01_Dctx* dctx); size_t ZSTDv01_nextSrcSizeToDecompress(ZSTDv01_Dctx* dctx); size_t ZSTDv01_decompressContinue(ZSTDv01_Dctx* dctx, void* dst, size_t maxDstSize, const void* src, size_t srcSize); /** Use above functions alternatively. ZSTD_nextSrcSizeToDecompress() tells how much bytes to provide as 'srcSize' to ZSTD_decompressContinue(). ZSTD_decompressContinue() will use previous data blocks to improve compression if they are located prior to current block. Result is the number of bytes regenerated within 'dst'. It can be zero, which is not an error; it just means ZSTD_decompressContinue() has decoded some header. */ /* ************************************* * Prefix - version detection ***************************************/ #define ZSTDv01_magicNumber 0xFD2FB51E /* Big Endian version */ #define ZSTDv01_magicNumberLE 0x1EB52FFD /* Little Endian version */ #if defined (__cplusplus) } #endif #endif /* ZSTD_V01_H_28739879432 */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711734143.0 borgbackup-1.2.8/src/borg/algorithms/zstd/lib/legacy/zstd_v02.c0000644000076500000240000037216614601576577022765 0ustar00twstaff/* * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #include /* size_t, ptrdiff_t */ #include "zstd_v02.h" #include "../common/error_private.h" /****************************************** * Compiler-specific ******************************************/ #if defined(_MSC_VER) /* Visual Studio */ # include /* _byteswap_ulong */ # include /* _byteswap_* */ #endif /* ****************************************************************** mem.h low-level memory access routines Copyright (C) 2013-2015, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy - Public forum : https://groups.google.com/forum/#!forum/lz4c ****************************************************************** */ #ifndef MEM_H_MODULE #define MEM_H_MODULE #if defined (__cplusplus) extern "C" { #endif /****************************************** * Includes ******************************************/ #include /* size_t, ptrdiff_t */ #include /* memcpy */ /****************************************** * Compiler-specific ******************************************/ #if defined(__GNUC__) # define MEM_STATIC static __attribute__((unused)) #elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # define MEM_STATIC static inline #elif defined(_MSC_VER) # define MEM_STATIC static __inline #else # define MEM_STATIC static /* this version may generate warnings for unused static functions; disable the relevant warning */ #endif /**************************************************************** * Basic Types *****************************************************************/ #if defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # include typedef uint8_t BYTE; typedef uint16_t U16; typedef int16_t S16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; typedef int64_t S64; #else typedef unsigned char BYTE; typedef unsigned short U16; typedef signed short S16; typedef unsigned int U32; typedef signed int S32; typedef unsigned long long U64; typedef signed long long S64; #endif /**************************************************************** * Memory I/O *****************************************************************/ /* MEM_FORCE_MEMORY_ACCESS * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. * The below switch allow to select different access method for improved performance. * Method 0 (default) : use `memcpy()`. Safe and portable. * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. * Method 2 : direct access. This method is portable but violate C standard. * It can generate buggy code on targets generating assembly depending on alignment. * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. * Prefer these methods in priority order (0 > 1 > 2) */ #ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ # if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) # define MEM_FORCE_MEMORY_ACCESS 2 # elif (defined(__INTEL_COMPILER) && !defined(WIN32)) || \ (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) )) # define MEM_FORCE_MEMORY_ACCESS 1 # endif #endif MEM_STATIC unsigned MEM_32bits(void) { return sizeof(void*)==4; } MEM_STATIC unsigned MEM_64bits(void) { return sizeof(void*)==8; } MEM_STATIC unsigned MEM_isLittleEndian(void) { const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ return one.c[0]; } #if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2) /* violates C standard on structure alignment. Only use if no other choice to achieve best performance on target platform */ MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; } MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; } MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; } MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } #elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1) /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ /* currently only defined for gcc and icc */ typedef union { U16 u16; U32 u32; U64 u64; } __attribute__((packed)) unalign; MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } #else /* default method, safe and standard. can sometimes prove slower */ MEM_STATIC U16 MEM_read16(const void* memPtr) { U16 val; memcpy(&val, memPtr, sizeof(val)); return val; } MEM_STATIC U32 MEM_read32(const void* memPtr) { U32 val; memcpy(&val, memPtr, sizeof(val)); return val; } MEM_STATIC U64 MEM_read64(const void* memPtr) { U64 val; memcpy(&val, memPtr, sizeof(val)); return val; } MEM_STATIC void MEM_write16(void* memPtr, U16 value) { memcpy(memPtr, &value, sizeof(value)); } #endif /* MEM_FORCE_MEMORY_ACCESS */ MEM_STATIC U16 MEM_readLE16(const void* memPtr) { if (MEM_isLittleEndian()) return MEM_read16(memPtr); else { const BYTE* p = (const BYTE*)memPtr; return (U16)(p[0] + (p[1]<<8)); } } MEM_STATIC void MEM_writeLE16(void* memPtr, U16 val) { if (MEM_isLittleEndian()) { MEM_write16(memPtr, val); } else { BYTE* p = (BYTE*)memPtr; p[0] = (BYTE)val; p[1] = (BYTE)(val>>8); } } MEM_STATIC U32 MEM_readLE24(const void* memPtr) { return MEM_readLE16(memPtr) + (((const BYTE*)memPtr)[2] << 16); } MEM_STATIC U32 MEM_readLE32(const void* memPtr) { if (MEM_isLittleEndian()) return MEM_read32(memPtr); else { const BYTE* p = (const BYTE*)memPtr; return (U32)((U32)p[0] + ((U32)p[1]<<8) + ((U32)p[2]<<16) + ((U32)p[3]<<24)); } } MEM_STATIC U64 MEM_readLE64(const void* memPtr) { if (MEM_isLittleEndian()) return MEM_read64(memPtr); else { const BYTE* p = (const BYTE*)memPtr; return (U64)((U64)p[0] + ((U64)p[1]<<8) + ((U64)p[2]<<16) + ((U64)p[3]<<24) + ((U64)p[4]<<32) + ((U64)p[5]<<40) + ((U64)p[6]<<48) + ((U64)p[7]<<56)); } } MEM_STATIC size_t MEM_readLEST(const void* memPtr) { if (MEM_32bits()) return (size_t)MEM_readLE32(memPtr); else return (size_t)MEM_readLE64(memPtr); } #if defined (__cplusplus) } #endif #endif /* MEM_H_MODULE */ /* ****************************************************************** bitstream Part of NewGen Entropy library header file (to include) Copyright (C) 2013-2015, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - Source repository : https://github.com/Cyan4973/FiniteStateEntropy - Public forum : https://groups.google.com/forum/#!forum/lz4c ****************************************************************** */ #ifndef BITSTREAM_H_MODULE #define BITSTREAM_H_MODULE #if defined (__cplusplus) extern "C" { #endif /* * This API consists of small unitary functions, which highly benefit from being inlined. * Since link-time-optimization is not available for all compilers, * these functions are defined into a .h to be included. */ /********************************************** * bitStream decompression API (read backward) **********************************************/ typedef struct { size_t bitContainer; unsigned bitsConsumed; const char* ptr; const char* start; } BIT_DStream_t; typedef enum { BIT_DStream_unfinished = 0, BIT_DStream_endOfBuffer = 1, BIT_DStream_completed = 2, BIT_DStream_overflow = 3 } BIT_DStream_status; /* result of BIT_reloadDStream() */ /* 1,2,4,8 would be better for bitmap combinations, but slows down performance a bit ... :( */ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize); MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits); MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD); MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* bitD); /****************************************** * unsafe API ******************************************/ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits); /* faster, but works only if nbBits >= 1 */ /**************************************************************** * Helper functions ****************************************************************/ MEM_STATIC unsigned BIT_highbit32 (U32 val) { # if defined(_MSC_VER) /* Visual */ unsigned long r=0; _BitScanReverse ( &r, val ); return (unsigned) r; # elif defined(__GNUC__) && (__GNUC__ >= 3) /* Use GCC Intrinsic */ return __builtin_clz (val) ^ 31; # else /* Software version */ static const unsigned DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; U32 v = val; unsigned r; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; r = DeBruijnClz[ (U32) (v * 0x07C4ACDDU) >> 27]; return r; # endif } /********************************************************** * bitStream decoding **********************************************************/ /*!BIT_initDStream * Initialize a BIT_DStream_t. * @bitD : a pointer to an already allocated BIT_DStream_t structure * @srcBuffer must point at the beginning of a bitStream * @srcSize must be the exact size of the bitStream * @result : size of stream (== srcSize) or an errorCode if a problem is detected */ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize) { if (srcSize < 1) { memset(bitD, 0, sizeof(*bitD)); return ERROR(srcSize_wrong); } if (srcSize >= sizeof(size_t)) /* normal case */ { U32 contain32; bitD->start = (const char*)srcBuffer; bitD->ptr = (const char*)srcBuffer + srcSize - sizeof(size_t); bitD->bitContainer = MEM_readLEST(bitD->ptr); contain32 = ((const BYTE*)srcBuffer)[srcSize-1]; if (contain32 == 0) return ERROR(GENERIC); /* endMark not present */ bitD->bitsConsumed = 8 - BIT_highbit32(contain32); } else { U32 contain32; bitD->start = (const char*)srcBuffer; bitD->ptr = bitD->start; bitD->bitContainer = *(const BYTE*)(bitD->start); switch(srcSize) { case 7: bitD->bitContainer += (size_t)(((const BYTE*)(bitD->start))[6]) << (sizeof(size_t)*8 - 16); /* fallthrough */ case 6: bitD->bitContainer += (size_t)(((const BYTE*)(bitD->start))[5]) << (sizeof(size_t)*8 - 24); /* fallthrough */ case 5: bitD->bitContainer += (size_t)(((const BYTE*)(bitD->start))[4]) << (sizeof(size_t)*8 - 32); /* fallthrough */ case 4: bitD->bitContainer += (size_t)(((const BYTE*)(bitD->start))[3]) << 24; /* fallthrough */ case 3: bitD->bitContainer += (size_t)(((const BYTE*)(bitD->start))[2]) << 16; /* fallthrough */ case 2: bitD->bitContainer += (size_t)(((const BYTE*)(bitD->start))[1]) << 8; /* fallthrough */ default:; } contain32 = ((const BYTE*)srcBuffer)[srcSize-1]; if (contain32 == 0) return ERROR(GENERIC); /* endMark not present */ bitD->bitsConsumed = 8 - BIT_highbit32(contain32); bitD->bitsConsumed += (U32)(sizeof(size_t) - srcSize)*8; } return srcSize; } MEM_STATIC size_t BIT_lookBits(BIT_DStream_t* bitD, U32 nbBits) { const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1; return ((bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> 1) >> ((bitMask-nbBits) & bitMask); } /*! BIT_lookBitsFast : * unsafe version; only works only if nbBits >= 1 */ MEM_STATIC size_t BIT_lookBitsFast(BIT_DStream_t* bitD, U32 nbBits) { const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1; return (bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> (((bitMask+1)-nbBits) & bitMask); } MEM_STATIC void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits) { bitD->bitsConsumed += nbBits; } MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits) { size_t value = BIT_lookBits(bitD, nbBits); BIT_skipBits(bitD, nbBits); return value; } /*!BIT_readBitsFast : * unsafe version; only works only if nbBits >= 1 */ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits) { size_t value = BIT_lookBitsFast(bitD, nbBits); BIT_skipBits(bitD, nbBits); return value; } MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD) { if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* should never happen */ return BIT_DStream_overflow; if (bitD->ptr >= bitD->start + sizeof(bitD->bitContainer)) { bitD->ptr -= bitD->bitsConsumed >> 3; bitD->bitsConsumed &= 7; bitD->bitContainer = MEM_readLEST(bitD->ptr); return BIT_DStream_unfinished; } if (bitD->ptr == bitD->start) { if (bitD->bitsConsumed < sizeof(bitD->bitContainer)*8) return BIT_DStream_endOfBuffer; return BIT_DStream_completed; } { U32 nbBytes = bitD->bitsConsumed >> 3; BIT_DStream_status result = BIT_DStream_unfinished; if (bitD->ptr - nbBytes < bitD->start) { nbBytes = (U32)(bitD->ptr - bitD->start); /* ptr > start */ result = BIT_DStream_endOfBuffer; } bitD->ptr -= nbBytes; bitD->bitsConsumed -= nbBytes*8; bitD->bitContainer = MEM_readLEST(bitD->ptr); /* reminder : srcSize > sizeof(bitD) */ return result; } } /*! BIT_endOfDStream * @return Tells if DStream has reached its exact end */ MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* DStream) { return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer)*8)); } #if defined (__cplusplus) } #endif #endif /* BITSTREAM_H_MODULE */ /* ****************************************************************** Error codes and messages Copyright (C) 2013-2015, Yann Collet BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - Source repository : https://github.com/Cyan4973/FiniteStateEntropy - Public forum : https://groups.google.com/forum/#!forum/lz4c ****************************************************************** */ #ifndef ERROR_H_MODULE #define ERROR_H_MODULE #if defined (__cplusplus) extern "C" { #endif /****************************************** * Compiler-specific ******************************************/ #if defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # define ERR_STATIC static inline #elif defined(_MSC_VER) # define ERR_STATIC static __inline #elif defined(__GNUC__) # define ERR_STATIC static __attribute__((unused)) #else # define ERR_STATIC static /* this version may generate warnings for unused static functions; disable the relevant warning */ #endif /****************************************** * Error Management ******************************************/ #define PREFIX(name) ZSTD_error_##name #define ERROR(name) (size_t)-PREFIX(name) #define ERROR_LIST(ITEM) \ ITEM(PREFIX(No_Error)) ITEM(PREFIX(GENERIC)) \ ITEM(PREFIX(dstSize_tooSmall)) ITEM(PREFIX(srcSize_wrong)) \ ITEM(PREFIX(prefix_unknown)) ITEM(PREFIX(corruption_detected)) \ ITEM(PREFIX(tableLog_tooLarge)) ITEM(PREFIX(maxSymbolValue_tooLarge)) ITEM(PREFIX(maxSymbolValue_tooSmall)) \ ITEM(PREFIX(maxCode)) #define ERROR_GENERATE_ENUM(ENUM) ENUM, typedef enum { ERROR_LIST(ERROR_GENERATE_ENUM) } ERR_codes; /* enum is exposed, to detect & handle specific errors; compare function result to -enum value */ #define ERROR_CONVERTTOSTRING(STRING) #STRING, #define ERROR_GENERATE_STRING(EXPR) ERROR_CONVERTTOSTRING(EXPR) static const char* ERR_strings[] = { ERROR_LIST(ERROR_GENERATE_STRING) }; ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); } ERR_STATIC const char* ERR_getErrorName(size_t code) { static const char* codeError = "Unspecified error code"; if (ERR_isError(code)) return ERR_strings[-(int)(code)]; return codeError; } #if defined (__cplusplus) } #endif #endif /* ERROR_H_MODULE */ /* Constructor and Destructor of type FSE_CTable Note that its size depends on 'tableLog' and 'maxSymbolValue' */ typedef unsigned FSE_CTable; /* don't allocate that. It's just a way to be more restrictive than void* */ typedef unsigned FSE_DTable; /* don't allocate that. It's just a way to be more restrictive than void* */ /* ****************************************************************** FSE : Finite State Entropy coder header file for static linking (only) Copyright (C) 2013-2015, Yann Collet BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - Source repository : https://github.com/Cyan4973/FiniteStateEntropy - Public forum : https://groups.google.com/forum/#!forum/lz4c ****************************************************************** */ #if defined (__cplusplus) extern "C" { #endif /****************************************** * Static allocation ******************************************/ /* FSE buffer bounds */ #define FSE_NCOUNTBOUND 512 #define FSE_BLOCKBOUND(size) (size + (size>>7)) #define FSE_COMPRESSBOUND(size) (FSE_NCOUNTBOUND + FSE_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ /* You can statically allocate FSE CTable/DTable as a table of unsigned using below macro */ #define FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) (1 + (1<<(maxTableLog-1)) + ((maxSymbolValue+1)*2)) #define FSE_DTABLE_SIZE_U32(maxTableLog) (1 + (1<= 1 (otherwise, result will be corrupted) */ /****************************************** * Implementation of inline functions ******************************************/ /* decompression */ typedef struct { U16 tableLog; U16 fastMode; } FSE_DTableHeader; /* sizeof U32 */ typedef struct { unsigned short newState; unsigned char symbol; unsigned char nbBits; } FSE_decode_t; /* size == U32 */ MEM_STATIC void FSE_initDState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD, const FSE_DTable* dt) { FSE_DTableHeader DTableH; memcpy(&DTableH, dt, sizeof(DTableH)); DStatePtr->state = BIT_readBits(bitD, DTableH.tableLog); BIT_reloadDStream(bitD); DStatePtr->table = dt + 1; } MEM_STATIC BYTE FSE_decodeSymbol(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD) { const FSE_decode_t DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state]; const U32 nbBits = DInfo.nbBits; BYTE symbol = DInfo.symbol; size_t lowBits = BIT_readBits(bitD, nbBits); DStatePtr->state = DInfo.newState + lowBits; return symbol; } MEM_STATIC BYTE FSE_decodeSymbolFast(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD) { const FSE_decode_t DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state]; const U32 nbBits = DInfo.nbBits; BYTE symbol = DInfo.symbol; size_t lowBits = BIT_readBitsFast(bitD, nbBits); DStatePtr->state = DInfo.newState + lowBits; return symbol; } MEM_STATIC unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr) { return DStatePtr->state == 0; } #if defined (__cplusplus) } #endif /* ****************************************************************** Huff0 : Huffman coder, part of New Generation Entropy library header file for static linking (only) Copyright (C) 2013-2015, Yann Collet BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - Source repository : https://github.com/Cyan4973/FiniteStateEntropy - Public forum : https://groups.google.com/forum/#!forum/lz4c ****************************************************************** */ #if defined (__cplusplus) extern "C" { #endif /****************************************** * Static allocation macros ******************************************/ /* Huff0 buffer bounds */ #define HUF_CTABLEBOUND 129 #define HUF_BLOCKBOUND(size) (size + (size>>8) + 8) /* only true if incompressible pre-filtered with fast heuristic */ #define HUF_COMPRESSBOUND(size) (HUF_CTABLEBOUND + HUF_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ /* static allocation of Huff0's DTable */ #define HUF_DTABLE_SIZE(maxTableLog) (1 + (1< /* size_t */ /* ************************************* * Version ***************************************/ #define ZSTD_VERSION_MAJOR 0 /* for breaking interface changes */ #define ZSTD_VERSION_MINOR 2 /* for new (non-breaking) interface capabilities */ #define ZSTD_VERSION_RELEASE 2 /* for tweaks, bug-fixes, or development */ #define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE) /* ************************************* * Advanced functions ***************************************/ typedef struct ZSTD_CCtx_s ZSTD_CCtx; /* incomplete type */ #if defined (__cplusplus) } #endif /* zstd - standard compression library Header File for static linking only Copyright (C) 2014-2015, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - zstd source repository : https://github.com/Cyan4973/zstd - ztsd public forum : https://groups.google.com/forum/#!forum/lz4c */ /* The objects defined into this file should be considered experimental. * They are not labelled stable, as their prototype may change in the future. * You can use them for tests, provide feedback, or if you can endure risk of future changes. */ #if defined (__cplusplus) extern "C" { #endif /* ************************************* * Streaming functions ***************************************/ typedef struct ZSTD_DCtx_s ZSTD_DCtx; /* Use above functions alternatively. ZSTD_nextSrcSizeToDecompress() tells how much bytes to provide as 'srcSize' to ZSTD_decompressContinue(). ZSTD_decompressContinue() will use previous data blocks to improve compression if they are located prior to current block. Result is the number of bytes regenerated within 'dst'. It can be zero, which is not an error; it just means ZSTD_decompressContinue() has decoded some header. */ /* ************************************* * Prefix - version detection ***************************************/ #define ZSTD_magicNumber 0xFD2FB522 /* v0.2 (current)*/ #if defined (__cplusplus) } #endif /* ****************************************************************** FSE : Finite State Entropy coder Copyright (C) 2013-2015, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy - Public forum : https://groups.google.com/forum/#!forum/lz4c ****************************************************************** */ #ifndef FSE_COMMONDEFS_ONLY /**************************************************************** * Tuning parameters ****************************************************************/ /* MEMORY_USAGE : * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) * Increasing memory usage improves compression ratio * Reduced memory usage can improve speed, due to cache effect * Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ #define FSE_MAX_MEMORY_USAGE 14 #define FSE_DEFAULT_MEMORY_USAGE 13 /* FSE_MAX_SYMBOL_VALUE : * Maximum symbol value authorized. * Required for proper stack allocation */ #define FSE_MAX_SYMBOL_VALUE 255 /**************************************************************** * template functions type & suffix ****************************************************************/ #define FSE_FUNCTION_TYPE BYTE #define FSE_FUNCTION_EXTENSION /**************************************************************** * Byte symbol type ****************************************************************/ #endif /* !FSE_COMMONDEFS_ONLY */ /**************************************************************** * Compiler specifics ****************************************************************/ #ifdef _MSC_VER /* Visual Studio */ # define FORCE_INLINE static __forceinline # include /* For Visual 2005 */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ #else # if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ # ifdef __GNUC__ # define FORCE_INLINE static inline __attribute__((always_inline)) # else # define FORCE_INLINE static inline # endif # else # define FORCE_INLINE static # endif /* __STDC_VERSION__ */ #endif /**************************************************************** * Includes ****************************************************************/ #include /* malloc, free, qsort */ #include /* memcpy, memset */ #include /* printf (debug) */ /**************************************************************** * Constants *****************************************************************/ #define FSE_MAX_TABLELOG (FSE_MAX_MEMORY_USAGE-2) #define FSE_MAX_TABLESIZE (1U< FSE_TABLELOG_ABSOLUTE_MAX #error "FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX is not supported" #endif /**************************************************************** * Error Management ****************************************************************/ #define FSE_STATIC_ASSERT(c) { enum { FSE_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ /**************************************************************** * Complex types ****************************************************************/ typedef U32 DTable_max_t[FSE_DTABLE_SIZE_U32(FSE_MAX_TABLELOG)]; /**************************************************************** * Templates ****************************************************************/ /* designed to be included for type-specific functions (template emulation in C) Objective is to write these functions only once, for improved maintenance */ /* safety checks */ #ifndef FSE_FUNCTION_EXTENSION # error "FSE_FUNCTION_EXTENSION must be defined" #endif #ifndef FSE_FUNCTION_TYPE # error "FSE_FUNCTION_TYPE must be defined" #endif /* Function names */ #define FSE_CAT(X,Y) X##Y #define FSE_FUNCTION_NAME(X,Y) FSE_CAT(X,Y) #define FSE_TYPE_NAME(X,Y) FSE_CAT(X,Y) /* Function templates */ #define FSE_DECODE_TYPE FSE_decode_t static U32 FSE_tableStep(U32 tableSize) { return (tableSize>>1) + (tableSize>>3) + 3; } static size_t FSE_buildDTable (FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) { void* ptr = dt+1; FSE_DECODE_TYPE* const tableDecode = (FSE_DECODE_TYPE*)ptr; FSE_DTableHeader DTableH; const U32 tableSize = 1 << tableLog; const U32 tableMask = tableSize-1; const U32 step = FSE_tableStep(tableSize); U16 symbolNext[FSE_MAX_SYMBOL_VALUE+1]; U32 position = 0; U32 highThreshold = tableSize-1; const S16 largeLimit= (S16)(1 << (tableLog-1)); U32 noLarge = 1; U32 s; /* Sanity Checks */ if (maxSymbolValue > FSE_MAX_SYMBOL_VALUE) return ERROR(maxSymbolValue_tooLarge); if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); /* Init, lay down lowprob symbols */ DTableH.tableLog = (U16)tableLog; for (s=0; s<=maxSymbolValue; s++) { if (normalizedCounter[s]==-1) { tableDecode[highThreshold--].symbol = (FSE_FUNCTION_TYPE)s; symbolNext[s] = 1; } else { if (normalizedCounter[s] >= largeLimit) noLarge=0; symbolNext[s] = normalizedCounter[s]; } } /* Spread symbols */ for (s=0; s<=maxSymbolValue; s++) { int i; for (i=0; i highThreshold) position = (position + step) & tableMask; /* lowprob area */ } } if (position!=0) return ERROR(GENERIC); /* position must reach all cells once, otherwise normalizedCounter is incorrect */ /* Build Decoding table */ { U32 i; for (i=0; i FSE_TABLELOG_ABSOLUTE_MAX) return ERROR(tableLog_tooLarge); bitStream >>= 4; bitCount = 4; *tableLogPtr = nbBits; remaining = (1<1) && (charnum<=*maxSVPtr)) { if (previous0) { unsigned n0 = charnum; while ((bitStream & 0xFFFF) == 0xFFFF) { n0+=24; if (ip < iend-5) { ip+=2; bitStream = MEM_readLE32(ip) >> bitCount; } else { bitStream >>= 16; bitCount+=16; } } while ((bitStream & 3) == 3) { n0+=3; bitStream>>=2; bitCount+=2; } n0 += bitStream & 3; bitCount += 2; if (n0 > *maxSVPtr) return ERROR(maxSymbolValue_tooSmall); while (charnum < n0) normalizedCounter[charnum++] = 0; if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) { ip += bitCount>>3; bitCount &= 7; bitStream = MEM_readLE32(ip) >> bitCount; } else bitStream >>= 2; } { const short max = (short)((2*threshold-1)-remaining); short count; if ((bitStream & (threshold-1)) < (U32)max) { count = (short)(bitStream & (threshold-1)); bitCount += nbBits-1; } else { count = (short)(bitStream & (2*threshold-1)); if (count >= threshold) count -= max; bitCount += nbBits; } count--; /* extra accuracy */ remaining -= FSE_abs(count); normalizedCounter[charnum++] = count; previous0 = !count; while (remaining < threshold) { nbBits--; threshold >>= 1; } { if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) { ip += bitCount>>3; bitCount &= 7; } else { bitCount -= (int)(8 * (iend - 4 - ip)); ip = iend - 4; } bitStream = MEM_readLE32(ip) >> (bitCount & 31); } } } if (remaining != 1) return ERROR(GENERIC); *maxSVPtr = charnum-1; ip += (bitCount+7)>>3; if ((size_t)(ip-istart) > hbSize) return ERROR(srcSize_wrong); return ip-istart; } /********************************************************* * Decompression (Byte symbols) *********************************************************/ static size_t FSE_buildDTable_rle (FSE_DTable* dt, BYTE symbolValue) { void* ptr = dt; FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr; FSE_decode_t* const cell = (FSE_decode_t*)(ptr) + 1; /* because dt is unsigned */ DTableH->tableLog = 0; DTableH->fastMode = 0; cell->newState = 0; cell->symbol = symbolValue; cell->nbBits = 0; return 0; } static size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits) { void* ptr = dt; FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr; FSE_decode_t* const dinfo = (FSE_decode_t*)(ptr) + 1; /* because dt is unsigned */ const unsigned tableSize = 1 << nbBits; const unsigned tableMask = tableSize - 1; const unsigned maxSymbolValue = tableMask; unsigned s; /* Sanity checks */ if (nbBits < 1) return ERROR(GENERIC); /* min size */ /* Build Decoding Table */ DTableH->tableLog = (U16)nbBits; DTableH->fastMode = 1; for (s=0; s<=maxSymbolValue; s++) { dinfo[s].newState = 0; dinfo[s].symbol = (BYTE)s; dinfo[s].nbBits = (BYTE)nbBits; } return 0; } FORCE_INLINE size_t FSE_decompress_usingDTable_generic( void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt, const unsigned fast) { BYTE* const ostart = (BYTE*) dst; BYTE* op = ostart; BYTE* const omax = op + maxDstSize; BYTE* const olimit = omax-3; BIT_DStream_t bitD; FSE_DState_t state1; FSE_DState_t state2; size_t errorCode; /* Init */ errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); /* replaced last arg by maxCompressed Size */ if (FSE_isError(errorCode)) return errorCode; FSE_initDState(&state1, &bitD, dt); FSE_initDState(&state2, &bitD, dt); #define FSE_GETSYMBOL(statePtr) fast ? FSE_decodeSymbolFast(statePtr, &bitD) : FSE_decodeSymbol(statePtr, &bitD) /* 4 symbols per loop */ for ( ; (BIT_reloadDStream(&bitD)==BIT_DStream_unfinished) && (op sizeof(bitD.bitContainer)*8) /* This test must be static */ BIT_reloadDStream(&bitD); op[1] = FSE_GETSYMBOL(&state2); if (FSE_MAX_TABLELOG*4+7 > sizeof(bitD.bitContainer)*8) /* This test must be static */ { if (BIT_reloadDStream(&bitD) > BIT_DStream_unfinished) { op+=2; break; } } op[2] = FSE_GETSYMBOL(&state1); if (FSE_MAX_TABLELOG*2+7 > sizeof(bitD.bitContainer)*8) /* This test must be static */ BIT_reloadDStream(&bitD); op[3] = FSE_GETSYMBOL(&state2); } /* tail */ /* note : BIT_reloadDStream(&bitD) >= FSE_DStream_partiallyFilled; Ends at exactly BIT_DStream_completed */ while (1) { if ( (BIT_reloadDStream(&bitD)>BIT_DStream_completed) || (op==omax) || (BIT_endOfDStream(&bitD) && (fast || FSE_endOfDState(&state1))) ) break; *op++ = FSE_GETSYMBOL(&state1); if ( (BIT_reloadDStream(&bitD)>BIT_DStream_completed) || (op==omax) || (BIT_endOfDStream(&bitD) && (fast || FSE_endOfDState(&state2))) ) break; *op++ = FSE_GETSYMBOL(&state2); } /* end ? */ if (BIT_endOfDStream(&bitD) && FSE_endOfDState(&state1) && FSE_endOfDState(&state2)) return op-ostart; if (op==omax) return ERROR(dstSize_tooSmall); /* dst buffer is full, but cSrc unfinished */ return ERROR(corruption_detected); } static size_t FSE_decompress_usingDTable(void* dst, size_t originalSize, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt) { FSE_DTableHeader DTableH; memcpy(&DTableH, dt, sizeof(DTableH)); /* select fast mode (static) */ if (DTableH.fastMode) return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 1); return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 0); } static size_t FSE_decompress(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize) { const BYTE* const istart = (const BYTE*)cSrc; const BYTE* ip = istart; short counting[FSE_MAX_SYMBOL_VALUE+1]; DTable_max_t dt; /* Static analyzer seems unable to understand this table will be properly initialized later */ unsigned tableLog; unsigned maxSymbolValue = FSE_MAX_SYMBOL_VALUE; size_t errorCode; if (cSrcSize<2) return ERROR(srcSize_wrong); /* too small input size */ /* normal FSE decoding mode */ errorCode = FSE_readNCount (counting, &maxSymbolValue, &tableLog, istart, cSrcSize); if (FSE_isError(errorCode)) return errorCode; if (errorCode >= cSrcSize) return ERROR(srcSize_wrong); /* too small input size */ ip += errorCode; cSrcSize -= errorCode; errorCode = FSE_buildDTable (dt, counting, maxSymbolValue, tableLog); if (FSE_isError(errorCode)) return errorCode; /* always return, even if it is an error code */ return FSE_decompress_usingDTable (dst, maxDstSize, ip, cSrcSize, dt); } #endif /* FSE_COMMONDEFS_ONLY */ /* ****************************************************************** Huff0 : Huffman coder, part of New Generation Entropy library Copyright (C) 2013-2015, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - FSE+Huff0 source repository : https://github.com/Cyan4973/FiniteStateEntropy - Public forum : https://groups.google.com/forum/#!forum/lz4c ****************************************************************** */ /**************************************************************** * Compiler specifics ****************************************************************/ #if defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) /* inline is defined */ #elif defined(_MSC_VER) # define inline __inline #else # define inline /* disable inline */ #endif #ifdef _MSC_VER /* Visual Studio */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #endif /**************************************************************** * Includes ****************************************************************/ #include /* malloc, free, qsort */ #include /* memcpy, memset */ #include /* printf (debug) */ /**************************************************************** * Error Management ****************************************************************/ #define HUF_STATIC_ASSERT(c) { enum { HUF_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ /****************************************** * Helper functions ******************************************/ static unsigned HUF_isError(size_t code) { return ERR_isError(code); } #define HUF_ABSOLUTEMAX_TABLELOG 16 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ #define HUF_MAX_TABLELOG 12 /* max configured tableLog (for static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */ #define HUF_DEFAULT_TABLELOG HUF_MAX_TABLELOG /* tableLog by default, when not specified */ #define HUF_MAX_SYMBOL_VALUE 255 #if (HUF_MAX_TABLELOG > HUF_ABSOLUTEMAX_TABLELOG) # error "HUF_MAX_TABLELOG is too large !" #endif /********************************************************* * Huff0 : Huffman block decompression *********************************************************/ typedef struct { BYTE byte; BYTE nbBits; } HUF_DEltX2; /* single-symbol decoding */ typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX4; /* double-symbols decoding */ typedef struct { BYTE symbol; BYTE weight; } sortedSymbol_t; /*! HUF_readStats Read compact Huffman tree, saved by HUF_writeCTable @huffWeight : destination buffer @return : size read from `src` */ static size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr, const void* src, size_t srcSize) { U32 weightTotal; U32 tableLog; const BYTE* ip = (const BYTE*) src; size_t iSize; size_t oSize; U32 n; if (!srcSize) return ERROR(srcSize_wrong); iSize = ip[0]; //memset(huffWeight, 0, hwSize); /* is not necessary, even though some analyzer complain ... */ if (iSize >= 128) /* special header */ { if (iSize >= (242)) /* RLE */ { static int l[14] = { 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 127, 128 }; oSize = l[iSize-242]; memset(huffWeight, 1, hwSize); iSize = 0; } else /* Incompressible */ { oSize = iSize - 127; iSize = ((oSize+1)/2); if (iSize+1 > srcSize) return ERROR(srcSize_wrong); if (oSize >= hwSize) return ERROR(corruption_detected); ip += 1; for (n=0; n> 4; huffWeight[n+1] = ip[n/2] & 15; } } } else /* header compressed with FSE (normal case) */ { if (iSize+1 > srcSize) return ERROR(srcSize_wrong); oSize = FSE_decompress(huffWeight, hwSize-1, ip+1, iSize); /* max (hwSize-1) values decoded, as last one is implied */ if (FSE_isError(oSize)) return oSize; } /* collect weight stats */ memset(rankStats, 0, (HUF_ABSOLUTEMAX_TABLELOG + 1) * sizeof(U32)); weightTotal = 0; for (n=0; n= HUF_ABSOLUTEMAX_TABLELOG) return ERROR(corruption_detected); rankStats[huffWeight[n]]++; weightTotal += (1 << huffWeight[n]) >> 1; } if (weightTotal == 0) return ERROR(corruption_detected); /* get last non-null symbol weight (implied, total must be 2^n) */ tableLog = BIT_highbit32(weightTotal) + 1; if (tableLog > HUF_ABSOLUTEMAX_TABLELOG) return ERROR(corruption_detected); { U32 total = 1 << tableLog; U32 rest = total - weightTotal; U32 verif = 1 << BIT_highbit32(rest); U32 lastWeight = BIT_highbit32(rest) + 1; if (verif != rest) return ERROR(corruption_detected); /* last value must be a clean power of 2 */ huffWeight[oSize] = (BYTE)lastWeight; rankStats[lastWeight]++; } /* check tree construction validity */ if ((rankStats[1] < 2) || (rankStats[1] & 1)) return ERROR(corruption_detected); /* by construction : at least 2 elts of rank 1, must be even */ /* results */ *nbSymbolsPtr = (U32)(oSize+1); *tableLogPtr = tableLog; return iSize+1; } /**************************/ /* single-symbol decoding */ /**************************/ static size_t HUF_readDTableX2 (U16* DTable, const void* src, size_t srcSize) { BYTE huffWeight[HUF_MAX_SYMBOL_VALUE + 1]; U32 rankVal[HUF_ABSOLUTEMAX_TABLELOG + 1]; /* large enough for values from 0 to 16 */ U32 tableLog = 0; const BYTE* ip = (const BYTE*) src; size_t iSize = ip[0]; U32 nbSymbols = 0; U32 n; U32 nextRankStart; void* ptr = DTable+1; HUF_DEltX2* const dt = (HUF_DEltX2*)ptr; HUF_STATIC_ASSERT(sizeof(HUF_DEltX2) == sizeof(U16)); /* if compilation fails here, assertion is false */ //memset(huffWeight, 0, sizeof(huffWeight)); /* is not necessary, even though some analyzer complain ... */ iSize = HUF_readStats(huffWeight, HUF_MAX_SYMBOL_VALUE + 1, rankVal, &nbSymbols, &tableLog, src, srcSize); if (HUF_isError(iSize)) return iSize; /* check result */ if (tableLog > DTable[0]) return ERROR(tableLog_tooLarge); /* DTable is too small */ DTable[0] = (U16)tableLog; /* maybe should separate sizeof DTable, as allocated, from used size of DTable, in case of DTable re-use */ /* Prepare ranks */ nextRankStart = 0; for (n=1; n<=tableLog; n++) { U32 current = nextRankStart; nextRankStart += (rankVal[n] << (n-1)); rankVal[n] = current; } /* fill DTable */ for (n=0; n> 1; U32 i; HUF_DEltX2 D; D.byte = (BYTE)n; D.nbBits = (BYTE)(tableLog + 1 - w); for (i = rankVal[w]; i < rankVal[w] + length; i++) dt[i] = D; rankVal[w] += length; } return iSize; } static BYTE HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, const U32 dtLog) { const size_t val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */ const BYTE c = dt[val].byte; BIT_skipBits(Dstream, dt[val].nbBits); return c; } #define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) \ *ptr++ = HUF_decodeSymbolX2(DStreamPtr, dt, dtLog) #define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \ if (MEM_64bits() || (HUF_MAX_TABLELOG<=12)) \ HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) #define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \ if (MEM_64bits()) \ HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) static inline size_t HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog) { BYTE* const pStart = p; /* up to 4 symbols at a time */ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p <= pEnd-4)) { HUF_DECODE_SYMBOLX2_2(p, bitDPtr); HUF_DECODE_SYMBOLX2_1(p, bitDPtr); HUF_DECODE_SYMBOLX2_2(p, bitDPtr); HUF_DECODE_SYMBOLX2_0(p, bitDPtr); } /* closer to the end */ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p < pEnd)) HUF_DECODE_SYMBOLX2_0(p, bitDPtr); /* no more data to retrieve from bitstream, hence no need to reload */ while (p < pEnd) HUF_DECODE_SYMBOLX2_0(p, bitDPtr); return pEnd-pStart; } static size_t HUF_decompress4X2_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const U16* DTable) { if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ { const BYTE* const istart = (const BYTE*) cSrc; BYTE* const ostart = (BYTE*) dst; BYTE* const oend = ostart + dstSize; const void* ptr = DTable; const HUF_DEltX2* const dt = ((const HUF_DEltX2*)ptr) +1; const U32 dtLog = DTable[0]; size_t errorCode; /* Init */ BIT_DStream_t bitD1; BIT_DStream_t bitD2; BIT_DStream_t bitD3; BIT_DStream_t bitD4; const size_t length1 = MEM_readLE16(istart); const size_t length2 = MEM_readLE16(istart+2); const size_t length3 = MEM_readLE16(istart+4); size_t length4; const BYTE* const istart1 = istart + 6; /* jumpTable */ const BYTE* const istart2 = istart1 + length1; const BYTE* const istart3 = istart2 + length2; const BYTE* const istart4 = istart3 + length3; const size_t segmentSize = (dstSize+3) / 4; BYTE* const opStart2 = ostart + segmentSize; BYTE* const opStart3 = opStart2 + segmentSize; BYTE* const opStart4 = opStart3 + segmentSize; BYTE* op1 = ostart; BYTE* op2 = opStart2; BYTE* op3 = opStart3; BYTE* op4 = opStart4; U32 endSignal; length4 = cSrcSize - (length1 + length2 + length3 + 6); if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ errorCode = BIT_initDStream(&bitD1, istart1, length1); if (HUF_isError(errorCode)) return errorCode; errorCode = BIT_initDStream(&bitD2, istart2, length2); if (HUF_isError(errorCode)) return errorCode; errorCode = BIT_initDStream(&bitD3, istart3, length3); if (HUF_isError(errorCode)) return errorCode; errorCode = BIT_initDStream(&bitD4, istart4, length4); if (HUF_isError(errorCode)) return errorCode; /* 16-32 symbols per loop (4-8 symbols per stream) */ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); for ( ; (endSignal==BIT_DStream_unfinished) && (op4<(oend-7)) ; ) { HUF_DECODE_SYMBOLX2_2(op1, &bitD1); HUF_DECODE_SYMBOLX2_2(op2, &bitD2); HUF_DECODE_SYMBOLX2_2(op3, &bitD3); HUF_DECODE_SYMBOLX2_2(op4, &bitD4); HUF_DECODE_SYMBOLX2_1(op1, &bitD1); HUF_DECODE_SYMBOLX2_1(op2, &bitD2); HUF_DECODE_SYMBOLX2_1(op3, &bitD3); HUF_DECODE_SYMBOLX2_1(op4, &bitD4); HUF_DECODE_SYMBOLX2_2(op1, &bitD1); HUF_DECODE_SYMBOLX2_2(op2, &bitD2); HUF_DECODE_SYMBOLX2_2(op3, &bitD3); HUF_DECODE_SYMBOLX2_2(op4, &bitD4); HUF_DECODE_SYMBOLX2_0(op1, &bitD1); HUF_DECODE_SYMBOLX2_0(op2, &bitD2); HUF_DECODE_SYMBOLX2_0(op3, &bitD3); HUF_DECODE_SYMBOLX2_0(op4, &bitD4); endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); } /* check corruption */ if (op1 > opStart2) return ERROR(corruption_detected); if (op2 > opStart3) return ERROR(corruption_detected); if (op3 > opStart4) return ERROR(corruption_detected); /* note : op4 supposed already verified within main loop */ /* finish bitStreams one by one */ HUF_decodeStreamX2(op1, &bitD1, opStart2, dt, dtLog); HUF_decodeStreamX2(op2, &bitD2, opStart3, dt, dtLog); HUF_decodeStreamX2(op3, &bitD3, opStart4, dt, dtLog); HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog); /* check */ endSignal = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); if (!endSignal) return ERROR(corruption_detected); /* decoded size */ return dstSize; } } static size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_MAX_TABLELOG); const BYTE* ip = (const BYTE*) cSrc; size_t errorCode; errorCode = HUF_readDTableX2 (DTable, cSrc, cSrcSize); if (HUF_isError(errorCode)) return errorCode; if (errorCode >= cSrcSize) return ERROR(srcSize_wrong); ip += errorCode; cSrcSize -= errorCode; return HUF_decompress4X2_usingDTable (dst, dstSize, ip, cSrcSize, DTable); } /***************************/ /* double-symbols decoding */ /***************************/ static void HUF_fillDTableX4Level2(HUF_DEltX4* DTable, U32 sizeLog, const U32 consumed, const U32* rankValOrigin, const int minWeight, const sortedSymbol_t* sortedSymbols, const U32 sortedListSize, U32 nbBitsBaseline, U16 baseSeq) { HUF_DEltX4 DElt; U32 rankVal[HUF_ABSOLUTEMAX_TABLELOG + 1]; U32 s; /* get pre-calculated rankVal */ memcpy(rankVal, rankValOrigin, sizeof(rankVal)); /* fill skipped values */ if (minWeight>1) { U32 i, skipSize = rankVal[minWeight]; MEM_writeLE16(&(DElt.sequence), baseSeq); DElt.nbBits = (BYTE)(consumed); DElt.length = 1; for (i = 0; i < skipSize; i++) DTable[i] = DElt; } /* fill DTable */ for (s=0; s= 1 */ rankVal[weight] += length; } } typedef U32 rankVal_t[HUF_ABSOLUTEMAX_TABLELOG][HUF_ABSOLUTEMAX_TABLELOG + 1]; static void HUF_fillDTableX4(HUF_DEltX4* DTable, const U32 targetLog, const sortedSymbol_t* sortedList, const U32 sortedListSize, const U32* rankStart, rankVal_t rankValOrigin, const U32 maxWeight, const U32 nbBitsBaseline) { U32 rankVal[HUF_ABSOLUTEMAX_TABLELOG + 1]; const int scaleLog = nbBitsBaseline - targetLog; /* note : targetLog >= srcLog, hence scaleLog <= 1 */ const U32 minBits = nbBitsBaseline - maxWeight; U32 s; memcpy(rankVal, rankValOrigin, sizeof(rankVal)); /* fill DTable */ for (s=0; s= minBits) /* enough room for a second symbol */ { U32 sortedRank; int minWeight = nbBits + scaleLog; if (minWeight < 1) minWeight = 1; sortedRank = rankStart[minWeight]; HUF_fillDTableX4Level2(DTable+start, targetLog-nbBits, nbBits, rankValOrigin[nbBits], minWeight, sortedList+sortedRank, sortedListSize-sortedRank, nbBitsBaseline, symbol); } else { U32 i; const U32 end = start + length; HUF_DEltX4 DElt; MEM_writeLE16(&(DElt.sequence), symbol); DElt.nbBits = (BYTE)(nbBits); DElt.length = 1; for (i = start; i < end; i++) DTable[i] = DElt; } rankVal[weight] += length; } } static size_t HUF_readDTableX4 (U32* DTable, const void* src, size_t srcSize) { BYTE weightList[HUF_MAX_SYMBOL_VALUE + 1]; sortedSymbol_t sortedSymbol[HUF_MAX_SYMBOL_VALUE + 1]; U32 rankStats[HUF_ABSOLUTEMAX_TABLELOG + 1] = { 0 }; U32 rankStart0[HUF_ABSOLUTEMAX_TABLELOG + 2] = { 0 }; U32* const rankStart = rankStart0+1; rankVal_t rankVal; U32 tableLog, maxW, sizeOfSort, nbSymbols; const U32 memLog = DTable[0]; const BYTE* ip = (const BYTE*) src; size_t iSize = ip[0]; void* ptr = DTable; HUF_DEltX4* const dt = ((HUF_DEltX4*)ptr) + 1; HUF_STATIC_ASSERT(sizeof(HUF_DEltX4) == sizeof(U32)); /* if compilation fails here, assertion is false */ if (memLog > HUF_ABSOLUTEMAX_TABLELOG) return ERROR(tableLog_tooLarge); //memset(weightList, 0, sizeof(weightList)); /* is not necessary, even though some analyzer complain ... */ iSize = HUF_readStats(weightList, HUF_MAX_SYMBOL_VALUE + 1, rankStats, &nbSymbols, &tableLog, src, srcSize); if (HUF_isError(iSize)) return iSize; /* check result */ if (tableLog > memLog) return ERROR(tableLog_tooLarge); /* DTable can't fit code depth */ /* find maxWeight */ for (maxW = tableLog; rankStats[maxW]==0; maxW--) {if (!maxW) return ERROR(GENERIC); } /* necessarily finds a solution before maxW==0 */ /* Get start index of each weight */ { U32 w, nextRankStart = 0; for (w=1; w<=maxW; w++) { U32 current = nextRankStart; nextRankStart += rankStats[w]; rankStart[w] = current; } rankStart[0] = nextRankStart; /* put all 0w symbols at the end of sorted list*/ sizeOfSort = nextRankStart; } /* sort symbols by weight */ { U32 s; for (s=0; s> consumed; } } } HUF_fillDTableX4(dt, memLog, sortedSymbol, sizeOfSort, rankStart0, rankVal, maxW, tableLog+1); return iSize; } static U32 HUF_decodeSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) { const size_t val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ memcpy(op, dt+val, 2); BIT_skipBits(DStream, dt[val].nbBits); return dt[val].length; } static U32 HUF_decodeLastSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) { const size_t val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ memcpy(op, dt+val, 1); if (dt[val].length==1) BIT_skipBits(DStream, dt[val].nbBits); else { if (DStream->bitsConsumed < (sizeof(DStream->bitContainer)*8)) { BIT_skipBits(DStream, dt[val].nbBits); if (DStream->bitsConsumed > (sizeof(DStream->bitContainer)*8)) DStream->bitsConsumed = (sizeof(DStream->bitContainer)*8); /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */ } } return 1; } #define HUF_DECODE_SYMBOLX4_0(ptr, DStreamPtr) \ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) #define HUF_DECODE_SYMBOLX4_1(ptr, DStreamPtr) \ if (MEM_64bits() || (HUF_MAX_TABLELOG<=12)) \ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) #define HUF_DECODE_SYMBOLX4_2(ptr, DStreamPtr) \ if (MEM_64bits()) \ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) static inline size_t HUF_decodeStreamX4(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, const HUF_DEltX4* const dt, const U32 dtLog) { BYTE* const pStart = p; /* up to 8 symbols at a time */ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p < pEnd-7)) { HUF_DECODE_SYMBOLX4_2(p, bitDPtr); HUF_DECODE_SYMBOLX4_1(p, bitDPtr); HUF_DECODE_SYMBOLX4_2(p, bitDPtr); HUF_DECODE_SYMBOLX4_0(p, bitDPtr); } /* closer to the end */ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p <= pEnd-2)) HUF_DECODE_SYMBOLX4_0(p, bitDPtr); while (p <= pEnd-2) HUF_DECODE_SYMBOLX4_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ if (p < pEnd) p += HUF_decodeLastSymbolX4(p, bitDPtr, dt, dtLog); return p-pStart; } static size_t HUF_decompress4X4_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const U32* DTable) { if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ { const BYTE* const istart = (const BYTE*) cSrc; BYTE* const ostart = (BYTE*) dst; BYTE* const oend = ostart + dstSize; const void* ptr = DTable; const HUF_DEltX4* const dt = ((const HUF_DEltX4*)ptr) +1; const U32 dtLog = DTable[0]; size_t errorCode; /* Init */ BIT_DStream_t bitD1; BIT_DStream_t bitD2; BIT_DStream_t bitD3; BIT_DStream_t bitD4; const size_t length1 = MEM_readLE16(istart); const size_t length2 = MEM_readLE16(istart+2); const size_t length3 = MEM_readLE16(istart+4); size_t length4; const BYTE* const istart1 = istart + 6; /* jumpTable */ const BYTE* const istart2 = istart1 + length1; const BYTE* const istart3 = istart2 + length2; const BYTE* const istart4 = istart3 + length3; const size_t segmentSize = (dstSize+3) / 4; BYTE* const opStart2 = ostart + segmentSize; BYTE* const opStart3 = opStart2 + segmentSize; BYTE* const opStart4 = opStart3 + segmentSize; BYTE* op1 = ostart; BYTE* op2 = opStart2; BYTE* op3 = opStart3; BYTE* op4 = opStart4; U32 endSignal; length4 = cSrcSize - (length1 + length2 + length3 + 6); if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ errorCode = BIT_initDStream(&bitD1, istart1, length1); if (HUF_isError(errorCode)) return errorCode; errorCode = BIT_initDStream(&bitD2, istart2, length2); if (HUF_isError(errorCode)) return errorCode; errorCode = BIT_initDStream(&bitD3, istart3, length3); if (HUF_isError(errorCode)) return errorCode; errorCode = BIT_initDStream(&bitD4, istart4, length4); if (HUF_isError(errorCode)) return errorCode; /* 16-32 symbols per loop (4-8 symbols per stream) */ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); for ( ; (endSignal==BIT_DStream_unfinished) && (op4<(oend-7)) ; ) { HUF_DECODE_SYMBOLX4_2(op1, &bitD1); HUF_DECODE_SYMBOLX4_2(op2, &bitD2); HUF_DECODE_SYMBOLX4_2(op3, &bitD3); HUF_DECODE_SYMBOLX4_2(op4, &bitD4); HUF_DECODE_SYMBOLX4_1(op1, &bitD1); HUF_DECODE_SYMBOLX4_1(op2, &bitD2); HUF_DECODE_SYMBOLX4_1(op3, &bitD3); HUF_DECODE_SYMBOLX4_1(op4, &bitD4); HUF_DECODE_SYMBOLX4_2(op1, &bitD1); HUF_DECODE_SYMBOLX4_2(op2, &bitD2); HUF_DECODE_SYMBOLX4_2(op3, &bitD3); HUF_DECODE_SYMBOLX4_2(op4, &bitD4); HUF_DECODE_SYMBOLX4_0(op1, &bitD1); HUF_DECODE_SYMBOLX4_0(op2, &bitD2); HUF_DECODE_SYMBOLX4_0(op3, &bitD3); HUF_DECODE_SYMBOLX4_0(op4, &bitD4); endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); } /* check corruption */ if (op1 > opStart2) return ERROR(corruption_detected); if (op2 > opStart3) return ERROR(corruption_detected); if (op3 > opStart4) return ERROR(corruption_detected); /* note : op4 supposed already verified within main loop */ /* finish bitStreams one by one */ HUF_decodeStreamX4(op1, &bitD1, opStart2, dt, dtLog); HUF_decodeStreamX4(op2, &bitD2, opStart3, dt, dtLog); HUF_decodeStreamX4(op3, &bitD3, opStart4, dt, dtLog); HUF_decodeStreamX4(op4, &bitD4, oend, dt, dtLog); /* check */ endSignal = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); if (!endSignal) return ERROR(corruption_detected); /* decoded size */ return dstSize; } } static size_t HUF_decompress4X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { HUF_CREATE_STATIC_DTABLEX4(DTable, HUF_MAX_TABLELOG); const BYTE* ip = (const BYTE*) cSrc; size_t hSize = HUF_readDTableX4 (DTable, cSrc, cSrcSize); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; return HUF_decompress4X4_usingDTable (dst, dstSize, ip, cSrcSize, DTable); } /**********************************/ /* quad-symbol decoding */ /**********************************/ typedef struct { BYTE nbBits; BYTE nbBytes; } HUF_DDescX6; typedef union { BYTE byte[4]; U32 sequence; } HUF_DSeqX6; /* recursive, up to level 3; may benefit from