├── .github ├── dependabot.yml └── workflows │ ├── build.yml │ └── test.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.rst ├── LICENSE ├── MANIFEST.in ├── Pipfile ├── Pipfile.lock ├── README.rst ├── bench.sh ├── mypy.ini ├── pyproject.toml ├── setup.py ├── src └── _xxhash.c ├── tests ├── __init__.py ├── test.py ├── test_algorithms_available.py ├── test_name.py ├── test_xxh32.py ├── test_xxh3_128.py ├── test_xxh3_64.py └── test_xxh64.py ├── typecheck.sh └── xxhash ├── __init__.py ├── __init__.pyi ├── py.typed └── version.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build_wheels: 7 | name: Build ${{ matrix.archs }} ${{ matrix.build }} wheels on ${{ matrix.os }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [ubuntu-22.04] 12 | archs: ["x86_64, i686", "aarch64", "ppc64le", "s390x"] 13 | build: ["manylinux", "musllinux"] 14 | include: 15 | - os: windows-2019 16 | archs: "AMD64" 17 | - os: windows-2019 18 | archs: "x86" 19 | - os: windows-2019 20 | archs: "ARM64" 21 | - os: macos-13 22 | archs: "x86_64, arm64" 23 | 24 | steps: 25 | - name: Disable git autocrlf 26 | run: | 27 | git config --global core.autocrlf false 28 | git config --global core.eol lf 29 | 30 | - uses: actions/checkout@v4 31 | with: 32 | fetch-depth: 0 33 | submodules: recursive 34 | 35 | - name: Set up QEMU 36 | uses: docker/setup-qemu-action@v3 37 | if: runner.os == 'Linux' 38 | 39 | - name: Build wheels 40 | uses: pypa/cibuildwheel@v2.23.3 41 | env: 42 | CIBW_ARCHS: "${{ matrix.archs }}" 43 | CIBW_BUILD: "${{ matrix.build && '*-' || ''}}${{ matrix.build }}*" 44 | CIBW_PRERELEASE_PYTHONS: "${{ !startsWith(github.ref, 'refs/tags/v') }}" 45 | 46 | - uses: actions/upload-artifact@v4 47 | with: 48 | name: wheels-${{ matrix.os }}-${{ matrix.build }}-${{ matrix.archs }} 49 | path: ./wheelhouse/*.whl 50 | 51 | 52 | build_sdist: 53 | name: Build sdist 54 | runs-on: ubuntu-22.04 55 | strategy: 56 | matrix: 57 | python-version: ['3.10'] 58 | steps: 59 | - uses: actions/checkout@v4 60 | with: 61 | fetch-depth: 0 62 | submodules: recursive 63 | 64 | - name: Set up Python ${{ matrix.python-version }} 65 | uses: actions/setup-python@v5 66 | with: 67 | python-version: ${{ matrix.python-version }} 68 | 69 | - name: Install dependencies 70 | run: python -m pip install -U pip 'setuptools>=45' 'setuptools-scm>=6.2' 71 | 72 | - name: Build sdist 73 | run: python setup.py build sdist 74 | 75 | - uses: actions/upload-artifact@v4 76 | with: 77 | name: wheels-dist 78 | path: dist/*.tar.gz 79 | 80 | 81 | upload_test_pypi: 82 | name: Upload to Test PyPI 83 | needs: [build_wheels, build_sdist] 84 | runs-on: ubuntu-22.04 85 | if: github.event_name == 'push' && github.repository == 'ifduyue/python-xxhash' && !startsWith(github.ref, 'refs/tags/v') 86 | permissions: 87 | id-token: write 88 | environment: 89 | name: test-pypi 90 | url: https://test.pypi.org/project/xxhash/ 91 | steps: 92 | - uses: actions/download-artifact@v4 93 | with: 94 | pattern: wheels-* 95 | path: dist 96 | merge-multiple: true 97 | 98 | - name: Upload to Test PyPI 99 | uses: pypa/gh-action-pypi-publish@v1.12.4 100 | with: 101 | skip-existing: true 102 | repository-url: https://test.pypi.org/legacy/ 103 | 104 | upload_pypi: 105 | name: Upload to PyPI 106 | needs: [build_wheels, build_sdist] 107 | runs-on: ubuntu-22.04 108 | if: github.event_name == 'push' && github.repository == 'ifduyue/python-xxhash' && startsWith(github.ref, 'refs/tags/v') 109 | environment: 110 | name: pypi 111 | url: https://pypi.org/project/xxhash/ 112 | permissions: 113 | id-token: write 114 | steps: 115 | - uses: actions/download-artifact@v4 116 | with: 117 | pattern: wheels-* 118 | path: dist 119 | merge-multiple: true 120 | 121 | - name: Upload to PyPI 122 | if: startsWith(github.ref, 'refs/tags/v') 123 | uses: pypa/gh-action-pypi-publish@v1.12.4 124 | with: 125 | skip-existing: true 126 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-22.04 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.13t', 'pypy3.9', 'pypy3.10'] 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | submodules: recursive 18 | 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | 24 | - name: Install dependencies 25 | run: python -m pip install -U pip 'setuptools>=45' 'setuptools-scm>=6.2' 26 | 27 | - name: Run tests 28 | run: | 29 | python setup.py build_ext --inplace 30 | python -m unittest discover -vv tests 31 | ./bench.sh 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | *.so 3 | *.o 4 | __pycache__/ 5 | build 6 | /xxhash.egg-info/ 7 | /xxhash/version.py 8 | .dmypy.json 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "xxhash"] 2 | path = deps/xxhash 3 | url = https://github.com/Cyan4973/xxHash.git 4 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ----------- 3 | 4 | v3.5.0 2024-08-17 5 | ~~~~~~~~~~~~~~~~~ 6 | 7 | - Build wheels for Python 3.13 8 | 9 | v3.4.1 2023-10-05 10 | ~~~~~~~~~~~~~~~~~ 11 | 12 | - Build wheels for Python 3.12 13 | - Remove setuptools_scm 14 | 15 | v3.4.0 2023-10-05 16 | ~~~~~~~~~~~~~~~~~ 17 | 18 | *Yanked* due to wheels building problem. 19 | 20 | v3.3.0 2023-07-29 21 | ~~~~~~~~~~~~~~~~~ 22 | 23 | - Upgrade xxHash to v0.8.2 24 | - Drop support for Python 3.6 25 | 26 | v3.2.0 2022-12-28 27 | ~~~~~~~~~~~~~~~~~ 28 | 29 | This is the last version to support Python 3.6 30 | 31 | - Build Python 3.11 wheels. 32 | - Remove setup.py test_suites, call unittest directly 33 | 34 | v3.1.0 2022-10-19 35 | ~~~~~~~~~~~~~~~~~ 36 | 37 | - Type annotations. 38 | - Enabled muslinux wheels building. 39 | 40 | v3.0.0 2022-02-25 41 | ~~~~~~~~~~~~~~~~~ 42 | 43 | - New set `algorithms_available` lists all implemented algorithms in `xxhash` 44 | package. 45 | - Upgrade xxHash to v0.8.1. 46 | - Drop support for EOL Python versions, require python >= 3.6 from now on. 47 | - Migrate to github actions and build arm64 wheels for macOS. 48 | - Always release GIL. 49 | 50 | 51 | v2.0.2 2021-04-15 52 | ~~~~~~~~~~~~~~~~~ 53 | 54 | - Fix Travis CI OSX dpl python2.7 get-pip.py error 55 | 56 | v2.0.1 2021-04-15 57 | ~~~~~~~~~~~~~~~~~ 58 | 59 | - Only to trigger Python 3.9 wheels building. 60 | 61 | v2.0.0 2020-08-03 62 | ~~~~~~~~~~~~~~~~~ 63 | 64 | - **Require xxHash version >= v0.8.0** 65 | - Upgrade xxHash to v0.8.0 66 | - XXH3 hashes: `xxh3_64`, `xxh3_128`, and their oneshot functions 67 | 68 | v1.4.4 2020-06-20 69 | ~~~~~~~~~~~~~~~~~ 70 | 71 | - Upgrade xxHash to v0.7.3 72 | - Stop using PEP393 deprecated APIs 73 | - Use XXH(32|64)_canonicalFromHash to replace u2bytes and ull2bytes 74 | 75 | v1.4.3 2019-11-12 76 | ~~~~~~~~~~~~~~~~~ 77 | 78 | - Upgrade xxHash to v0.7.2 79 | - Python 3.8 wheels 80 | 81 | v1.4.2 2019-10-13 82 | ~~~~~~~~~~~~~~~~~ 83 | 84 | - Fixed: setup.py fails when reading README.rst and the default encoding is not UTF-8 85 | 86 | v1.4.1 2019-08-27 87 | ~~~~~~~~~~~~~~~~~ 88 | 89 | - Fixed: xxh3.h in missing from source tarball 90 | 91 | v1.4.0 2019-08-25 92 | ~~~~~~~~~~~~~~~~~ 93 | 94 | - Upgrade xxHash to v0.7.1 95 | 96 | v1.3.0 2018-10-21 97 | ~~~~~~~~~~~~~~~~~ 98 | 99 | - Wheels are now built automatically 100 | - Split CFFI variant into a separate package `ifduyue/python-xxhash-cffi `_ 101 | 102 | v1.2.0 2018-07-13 103 | ~~~~~~~~~~~~~~~~~ 104 | 105 | - Add oneshot functions xxh{32,64}_{,int,hex}digest 106 | 107 | v1.1.0 2018-07-05 108 | ~~~~~~~~~~~~~~~~~ 109 | 110 | - Allow input larger than 2GB 111 | - Release the GIL on sufficiently large input 112 | - Drop support for Python 3.2 113 | 114 | v1.0.1 2017-03-02 115 | ~~~~~~~~~~~~~~~~~~ 116 | 117 | - Free state actively, instead of delegating it to ffi.gc 118 | 119 | v1.0.0 2017-02-10 120 | ~~~~~~~~~~~~~~~~~~ 121 | 122 | - Fixed copy() segfault 123 | - Added CFFI variant 124 | 125 | v0.6.3 2017-02-10 126 | ~~~~~~~~~~~~~~~~~~ 127 | 128 | - Fixed copy() segfault 129 | 130 | v0.6.2 2017-02-10 131 | ~~~~~~~~~~~~~~~~~~ 132 | 133 | - Upgrade xxHash to v0.6.2 134 | 135 | v0.6.1 2016-06-26 136 | ~~~~~~~~~~~~~~~~~~ 137 | 138 | - Upgrade xxHash to v0.6.1 139 | 140 | v0.5.0 2016-03-02 141 | ~~~~~~~~~~~~~~~~~~ 142 | 143 | - Upgrade xxHash to v0.5.0 144 | 145 | v0.4.3 2015-08-21 146 | ~~~~~~~~~~~~~~~~~~ 147 | 148 | - Upgrade xxHash to r42 149 | 150 | v0.4.1 2015-08-16 151 | ~~~~~~~~~~~~~~~~~~ 152 | 153 | - Upgrade xxHash to r41 154 | 155 | v0.4.0 2015-08-05 156 | ~~~~~~~~~~~~~~~~~~ 157 | 158 | - Added method reset 159 | - Upgrade xxHash to r40 160 | 161 | v0.3.2 2015-01-27 162 | ~~~~~~~~~~~~~~~~~~ 163 | 164 | - Fixed some typos in docstrings 165 | 166 | v0.3.1 2015-01-24 167 | ~~~~~~~~~~~~~~~~~~ 168 | 169 | - Upgrade xxHash to r39 170 | 171 | v0.3.0 2014-11-11 172 | ~~~~~~~~~~~~~~~~~~ 173 | 174 | - Change digest() from little-endian representation to big-endian representation of the integer digest. 175 | This change breaks compatibility (digest() results are different). 176 | 177 | v0.2.0 2014-10-25 178 | ~~~~~~~~~~~~~~~~~~ 179 | 180 | - Make this package hashlib-compliant 181 | 182 | v0.1.3 2014-10-23 183 | ~~~~~~~~~~~~~~~~~~ 184 | 185 | - Update xxHash to r37 186 | 187 | v0.1.2 2014-10-19 188 | ~~~~~~~~~~~~~~~~~~ 189 | 190 | - Improve: Check XXHnn_init() return value. 191 | - Update xxHash to r36 192 | 193 | v0.1.1 2014-08-07 194 | ~~~~~~~~~~~~~~~~~~ 195 | 196 | - Improve: Can now be built with Visual C++ Compiler. 197 | 198 | v0.1.0 2014-08-05 199 | ~~~~~~~~~~~~~~~~~~ 200 | 201 | - New: XXH32 and XXH64 type, which support partially update. 202 | - Fix: build under Python 3.4 203 | 204 | v0.0.2 2014-08-03 205 | ~~~~~~~~~~~~~~~~~~ 206 | 207 | - NEW: Support Python 3 208 | 209 | v0.0.1 2014-07-30 210 | ~~~~~~~~~~~~~~~~~~ 211 | 212 | - NEW: xxh32 and xxh64 213 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2024, Yue Du 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include CHANGELOG.rst 3 | include LICENSE 4 | include deps/xxhash/xxh3.h 5 | include deps/xxhash/xxhash.h 6 | include deps/xxhash/xxhash.c 7 | include deps/xxhash/LICENSE 8 | graft tests 9 | global-exclude __pycache__ 10 | global-exclude *.py[co] 11 | prune .github 12 | exclude .gitignore .gitmodules 13 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | 8 | [dev-packages] 9 | mypy = "*" 10 | 11 | [requires] 12 | python_version = "3.9" 13 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "83fe814f67b2b1c52cb7d081ab85c1e9b530407e5c77e345af86192075ea0a82" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": {}, 19 | "develop": { 20 | "mypy": { 21 | "hashes": [ 22 | "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9", 23 | "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a", 24 | "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9", 25 | "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e", 26 | "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2", 27 | "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212", 28 | "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b", 29 | "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885", 30 | "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150", 31 | "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703", 32 | "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072", 33 | "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457", 34 | "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e", 35 | "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0", 36 | "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb", 37 | "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97", 38 | "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8", 39 | "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811", 40 | "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6", 41 | "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de", 42 | "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504", 43 | "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921", 44 | "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d" 45 | ], 46 | "index": "pypi", 47 | "version": "==0.910" 48 | }, 49 | "mypy-extensions": { 50 | "hashes": [ 51 | "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", 52 | "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" 53 | ], 54 | "version": "==0.4.3" 55 | }, 56 | "toml": { 57 | "hashes": [ 58 | "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", 59 | "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" 60 | ], 61 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'", 62 | "version": "==0.10.2" 63 | }, 64 | "typing-extensions": { 65 | "hashes": [ 66 | "sha256:2cdf80e4e04866a9b3689a51869016d36db0814d84b8d8a568d22781d45d27ed", 67 | "sha256:829704698b22e13ec9eaf959122315eabb370b0884400e9818334d8b677023d9" 68 | ], 69 | "markers": "python_version >= '3.6'", 70 | "version": "==4.0.0" 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | python-xxhash 2 | ============= 3 | 4 | .. image:: https://github.com/ifduyue/python-xxhash/actions/workflows/test.yml/badge.svg 5 | :target: https://github.com/ifduyue/python-xxhash/actions/workflows/test.yml 6 | :alt: Github Actions Status 7 | 8 | .. image:: https://img.shields.io/pypi/v/xxhash.svg 9 | :target: https://pypi.org/project/xxhash/ 10 | :alt: Latest Version 11 | 12 | .. image:: https://img.shields.io/pypi/pyversions/xxhash.svg 13 | :target: https://pypi.org/project/xxhash/ 14 | :alt: Supported Python versions 15 | 16 | .. image:: https://img.shields.io/pypi/l/xxhash.svg 17 | :target: https://pypi.org/project/xxhash/ 18 | :alt: License 19 | 20 | 21 | .. _HMAC: http://en.wikipedia.org/wiki/Hash-based_message_authentication_code 22 | .. _xxHash: https://github.com/Cyan4973/xxHash 23 | .. _Cyan4973: https://github.com/Cyan4973 24 | 25 | 26 | xxhash is a Python binding for the xxHash_ library by `Yann Collet`__. 27 | 28 | __ Cyan4973_ 29 | 30 | Installation 31 | ------------ 32 | 33 | .. code-block:: bash 34 | 35 | $ pip install xxhash 36 | 37 | You can also install using conda: 38 | 39 | .. code-block:: bash 40 | 41 | $ conda install -c conda-forge python-xxhash 42 | 43 | 44 | Installing From Source 45 | ~~~~~~~~~~~~~~~~~~~~~~~ 46 | 47 | .. code-block:: bash 48 | 49 | $ pip install --no-binary xxhash xxhash 50 | 51 | Prerequisites 52 | ++++++++++++++ 53 | 54 | On Debian/Ubuntu: 55 | 56 | .. code-block:: bash 57 | 58 | $ apt-get install python-dev gcc 59 | 60 | On CentOS/Fedora: 61 | 62 | .. code-block:: bash 63 | 64 | $ yum install python-devel gcc redhat-rpm-config 65 | 66 | Linking to libxxhash.so 67 | ~~~~~~~~~~~~~~~~~~~~~~~~ 68 | 69 | By default python-xxhash will use bundled xxHash, 70 | we can change this by specifying ENV var ``XXHASH_LINK_SO``: 71 | 72 | .. code-block:: bash 73 | 74 | $ XXHASH_LINK_SO=1 pip install --no-binary xxhash xxhash 75 | 76 | Usage 77 | -------- 78 | 79 | Module version and its backend xxHash library version can be retrieved using 80 | the module properties ``VERSION`` AND ``XXHASH_VERSION`` respectively. 81 | 82 | .. code-block:: python 83 | 84 | >>> import xxhash 85 | >>> xxhash.VERSION 86 | '2.0.0' 87 | >>> xxhash.XXHASH_VERSION 88 | '0.8.0' 89 | 90 | This module is hashlib-compliant, which means you can use it in the same way as ``hashlib.md5``. 91 | 92 | | update() -- update the current digest with an additional string 93 | | digest() -- return the current digest value 94 | | hexdigest() -- return the current digest as a string of hexadecimal digits 95 | | intdigest() -- return the current digest as an integer 96 | | copy() -- return a copy of the current xxhash object 97 | | reset() -- reset state 98 | 99 | md5 digest returns bytes, but the original xxh32 and xxh64 C APIs return integers. 100 | While this module is made hashlib-compliant, ``intdigest()`` is also provided to 101 | get the integer digest. 102 | 103 | Constructors for hash algorithms provided by this module are ``xxh32()`` and ``xxh64()``. 104 | 105 | For example, to obtain the digest of the byte string ``b'Nobody inspects the spammish repetition'``: 106 | 107 | .. code-block:: python 108 | 109 | >>> import xxhash 110 | >>> x = xxhash.xxh32() 111 | >>> x.update(b'Nobody inspects') 112 | >>> x.update(b' the spammish repetition') 113 | >>> x.digest() 114 | b'\xe2);/' 115 | >>> x.digest_size 116 | 4 117 | >>> x.block_size 118 | 16 119 | 120 | More condensed: 121 | 122 | .. code-block:: python 123 | 124 | >>> xxhash.xxh32(b'Nobody inspects the spammish repetition').hexdigest() 125 | 'e2293b2f' 126 | >>> xxhash.xxh32(b'Nobody inspects the spammish repetition').digest() == x.digest() 127 | True 128 | 129 | An optional seed (default is 0) can be used to alter the result predictably: 130 | 131 | .. code-block:: python 132 | 133 | >>> import xxhash 134 | >>> xxhash.xxh64('xxhash').hexdigest() 135 | '32dd38952c4bc720' 136 | >>> xxhash.xxh64('xxhash', seed=20141025).hexdigest() 137 | 'b559b98d844e0635' 138 | >>> x = xxhash.xxh64(seed=20141025) 139 | >>> x.update('xxhash') 140 | >>> x.hexdigest() 141 | 'b559b98d844e0635' 142 | >>> x.intdigest() 143 | 13067679811253438005 144 | 145 | Be careful that xxh32 takes an unsigned 32-bit integer as seed, while xxh64 146 | takes an unsigned 64-bit integer. Although unsigned integer overflow is 147 | defined behavior, it's better not to make it happen: 148 | 149 | .. code-block:: python 150 | 151 | >>> xxhash.xxh32('I want an unsigned 32-bit seed!', seed=0).hexdigest() 152 | 'f7a35af8' 153 | >>> xxhash.xxh32('I want an unsigned 32-bit seed!', seed=2**32).hexdigest() 154 | 'f7a35af8' 155 | >>> xxhash.xxh32('I want an unsigned 32-bit seed!', seed=1).hexdigest() 156 | 'd8d4b4ba' 157 | >>> xxhash.xxh32('I want an unsigned 32-bit seed!', seed=2**32+1).hexdigest() 158 | 'd8d4b4ba' 159 | >>> 160 | >>> xxhash.xxh64('I want an unsigned 64-bit seed!', seed=0).hexdigest() 161 | 'd4cb0a70a2b8c7c1' 162 | >>> xxhash.xxh64('I want an unsigned 64-bit seed!', seed=2**64).hexdigest() 163 | 'd4cb0a70a2b8c7c1' 164 | >>> xxhash.xxh64('I want an unsigned 64-bit seed!', seed=1).hexdigest() 165 | 'ce5087f12470d961' 166 | >>> xxhash.xxh64('I want an unsigned 64-bit seed!', seed=2**64+1).hexdigest() 167 | 'ce5087f12470d961' 168 | 169 | 170 | ``digest()`` returns bytes of the **big-endian** representation of the integer 171 | digest: 172 | 173 | .. code-block:: python 174 | 175 | >>> import xxhash 176 | >>> h = xxhash.xxh64() 177 | >>> h.digest() 178 | b'\xefF\xdb7Q\xd8\xe9\x99' 179 | >>> h.intdigest().to_bytes(8, 'big') 180 | b'\xefF\xdb7Q\xd8\xe9\x99' 181 | >>> h.hexdigest() 182 | 'ef46db3751d8e999' 183 | >>> format(h.intdigest(), '016x') 184 | 'ef46db3751d8e999' 185 | >>> h.intdigest() 186 | 17241709254077376921 187 | >>> int(h.hexdigest(), 16) 188 | 17241709254077376921 189 | 190 | Besides xxh32/xxh64 mentioned above, oneshot functions are also provided, 191 | so we can avoid allocating XXH32/64 state on heap: 192 | 193 | | xxh32_digest(bytes, seed=0) 194 | | xxh32_intdigest(bytes, seed=0) 195 | | xxh32_hexdigest(bytes, seed=0) 196 | | xxh64_digest(bytes, seed=0) 197 | | xxh64_intdigest(bytes, seed=0) 198 | | xxh64_hexdigest(bytes, seed=0) 199 | 200 | .. code-block:: python 201 | 202 | >>> import xxhash 203 | >>> xxhash.xxh64('a').digest() == xxhash.xxh64_digest('a') 204 | True 205 | >>> xxhash.xxh64('a').intdigest() == xxhash.xxh64_intdigest('a') 206 | True 207 | >>> xxhash.xxh64('a').hexdigest() == xxhash.xxh64_hexdigest('a') 208 | True 209 | >>> xxhash.xxh64_hexdigest('xxhash', seed=20141025) 210 | 'b559b98d844e0635' 211 | >>> xxhash.xxh64_intdigest('xxhash', seed=20141025) 212 | 13067679811253438005L 213 | >>> xxhash.xxh64_digest('xxhash', seed=20141025) 214 | '\xb5Y\xb9\x8d\x84N\x065' 215 | 216 | .. code-block:: python 217 | 218 | In [1]: import xxhash 219 | 220 | In [2]: %timeit xxhash.xxh64_hexdigest('xxhash') 221 | 268 ns ± 24.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 222 | 223 | In [3]: %timeit xxhash.xxh64('xxhash').hexdigest() 224 | 416 ns ± 17.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 225 | 226 | 227 | XXH3 hashes are available since v2.0.0 (xxHash v0.8.0), they are: 228 | 229 | Streaming classes: 230 | 231 | | xxh3_64 232 | | xxh3_128 233 | 234 | Oneshot functions: 235 | 236 | | xxh3_64_digest(bytes, seed=0) 237 | | xxh3_64_intdigest(bytes, seed=0) 238 | | xxh3_64_hexdigest(bytes, seed=0) 239 | | xxh3_128_digest(bytes, seed=0) 240 | | xxh3_128_intdigest(bytes, seed=0) 241 | | xxh3_128_hexdigest(bytes, seed=0) 242 | 243 | And aliases: 244 | 245 | | xxh128 = xxh3_128 246 | | xxh128_digest = xxh3_128_digest 247 | | xxh128_intdigest = xxh3_128_intdigest 248 | | xxh128_hexdigest = xxh3_128_hexdigest 249 | 250 | Caveats 251 | ------- 252 | 253 | SEED OVERFLOW 254 | ~~~~~~~~~~~~~~ 255 | 256 | xxh32 takes an unsigned 32-bit integer as seed, and xxh64 takes 257 | an unsigned 64-bit integer as seed. Make sure that the seed is greater than 258 | or equal to ``0``. 259 | 260 | ENDIANNESS 261 | ~~~~~~~~~~~ 262 | 263 | As of python-xxhash 0.3.0, ``digest()`` returns bytes of the 264 | **big-endian** representation of the integer digest. It used 265 | to be little-endian. 266 | 267 | DONT USE XXHASH IN HMAC 268 | ~~~~~~~~~~~~~~~~~~~~~~~ 269 | Though you can use xxhash as an HMAC_ hash function, but it's 270 | highly recommended not to. 271 | 272 | xxhash is **NOT** a cryptographic hash function, it is a 273 | non-cryptographic hash algorithm aimed at speed and quality. 274 | Do not put xxhash in any position where cryptographic hash 275 | functions are required. 276 | 277 | 278 | Copyright and License 279 | --------------------- 280 | 281 | Copyright (c) 2014-2024 Yue Du - https://github.com/ifduyue 282 | 283 | Licensed under `BSD 2-Clause License `_ 284 | -------------------------------------------------------------------------------- /bench.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PYTHON=${PYTHON-`which python`} 4 | 5 | echo Benchmarking ... 6 | 7 | echo -n " xxh32_intdigest 1000B: " 8 | $PYTHON -mtimeit -s 'from xxhash import xxh32_intdigest' \ 9 | -s 'import os' \ 10 | -s 'import random' \ 11 | -s 'seed = random.randint(0, 0xffffffff)' \ 12 | -s 'data = os.urandom(1000)' \ 13 | 'xxh32_intdigest(data, seed=seed)' 14 | 15 | echo -n " xxh32_intdigest 10000B: " 16 | $PYTHON -mtimeit -s 'from xxhash import xxh32_intdigest' \ 17 | -s 'import os' \ 18 | -s 'import random' \ 19 | -s 'seed = random.randint(0, 0xffffffff)' \ 20 | -s 'data = os.urandom(10000)' \ 21 | 'xxh32_intdigest(data, seed=seed)' 22 | 23 | 24 | echo -n " xxh64_intdigest 1000B: " 25 | $PYTHON -mtimeit -s 'from xxhash import xxh64_intdigest' \ 26 | -s 'import os' \ 27 | -s 'import random' \ 28 | -s 'seed = random.randint(0, 0xffffffffffffffff)' \ 29 | -s 'data = os.urandom(1000)' \ 30 | 'xxh64_intdigest(data, seed=seed)' 31 | 32 | echo -n " xxh64_intdigest 10000B: " 33 | $PYTHON -mtimeit -s 'from xxhash import xxh64_intdigest' \ 34 | -s 'import os' \ 35 | -s 'import random' \ 36 | -s 'seed = random.randint(0, 0xffffffffffffffff)' \ 37 | -s 'data = os.urandom(10000)' \ 38 | 'xxh64_intdigest(data, seed=seed)' 39 | 40 | 41 | echo -n " xxh3_64_intdigest 1000B: " 42 | $PYTHON -mtimeit -s 'from xxhash import xxh3_64_intdigest' \ 43 | -s 'import os' \ 44 | -s 'import random' \ 45 | -s 'seed = random.randint(0, 0xffffffffffffffff)' \ 46 | -s 'data = os.urandom(1000)' \ 47 | 'xxh3_64_intdigest(data, seed=seed)' 48 | 49 | echo -n " xxh3_64_intdigest 10000B: " 50 | $PYTHON -mtimeit -s 'from xxhash import xxh3_64_intdigest' \ 51 | -s 'import os' \ 52 | -s 'import random' \ 53 | -s 'seed = random.randint(0, 0xffffffffffffffff)' \ 54 | -s 'data = os.urandom(10000)' \ 55 | 'xxh3_64_intdigest(data, seed=seed)' 56 | 57 | 58 | echo -n " xxh3_128_intdigest 1000B: " 59 | $PYTHON -mtimeit -s 'from xxhash import xxh3_128_intdigest' \ 60 | -s 'import os' \ 61 | -s 'import random' \ 62 | -s 'seed = random.randint(0, 0xffffffffffffffff)' \ 63 | -s 'data = os.urandom(1000)' \ 64 | 'xxh3_128_intdigest(data, seed=seed)' 65 | 66 | echo -n " xxh3_128_intdigest 10000B: " 67 | $PYTHON -mtimeit -s 'from xxhash import xxh3_128_intdigest' \ 68 | -s 'import os' \ 69 | -s 'import random' \ 70 | -s 'seed = random.randint(0, 0xffffffffffffffff)' \ 71 | -s 'data = os.urandom(10000)' \ 72 | 'xxh3_128_intdigest(data, seed=seed)' 73 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | warn_unused_configs = True 3 | disallow_any_generics = True 4 | disallow_subclassing_any = True 5 | disallow_untyped_calls = True 6 | # disallow_untyped_defs = True 7 | disallow_incomplete_defs = True 8 | check_untyped_defs = True 9 | disallow_untyped_decorators = True 10 | no_implicit_optional = True 11 | warn_redundant_casts = True 12 | warn_unused_ignores = True 13 | warn_return_any = True 14 | implicit_reexport = False 15 | strict_equality = True 16 | 17 | # Options above stolen from 18 | # mypy --help | grep -A 9 -- '--strict ' 19 | 20 | pretty = True 21 | error_summary = False 22 | 23 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=45"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | from setuptools import setup, Extension 5 | import os 6 | from pathlib import Path 7 | 8 | 9 | if os.getenv('XXHASH_LINK_SO'): 10 | libraries = ['xxhash'] 11 | source = ['src/_xxhash.c'] 12 | include_dirs = [] 13 | else: 14 | libraries = [] 15 | source = ['src/_xxhash.c', 'deps/xxhash/xxhash.c'] 16 | include_dirs = ['deps/xxhash'] 17 | 18 | ext_modules = [ 19 | Extension( 20 | '_xxhash', 21 | source, 22 | include_dirs=include_dirs, 23 | libraries=libraries, 24 | ) 25 | ] 26 | 27 | d = Path(__file__).parent 28 | long_description = d.joinpath('README.rst').read_text() + '\n' + d.joinpath('CHANGELOG.rst').read_text() 29 | 30 | version_dict = {} 31 | exec(d.joinpath("xxhash", "version.py").read_text(), {}, version_dict) 32 | version = version_dict["VERSION"] 33 | 34 | setup( 35 | name='xxhash', 36 | version=version, 37 | description="Python binding for xxHash", 38 | long_description=long_description, 39 | long_description_content_type="text/x-rst", 40 | author='Yue Du', 41 | author_email='ifduyue@gmail.com', 42 | url='https://github.com/ifduyue/python-xxhash', 43 | license='BSD', 44 | packages=['xxhash'], 45 | ext_package='xxhash', 46 | classifiers=[ 47 | 'Development Status :: 5 - Production/Stable', 48 | 'License :: OSI Approved :: BSD License', 49 | 'Intended Audience :: Developers', 50 | 'Programming Language :: Python', 51 | 'Programming Language :: Python :: 3', 52 | 'Programming Language :: Python :: 3 :: Only', 53 | 'Programming Language :: Python :: 3.7', 54 | 'Programming Language :: Python :: 3.8', 55 | 'Programming Language :: Python :: 3.9', 56 | 'Programming Language :: Python :: 3.10', 57 | 'Programming Language :: Python :: 3.11', 58 | 'Programming Language :: Python :: 3.12', 59 | 'Programming Language :: Python :: 3.13', 60 | 'Programming Language :: Python :: Implementation :: CPython', 61 | ], 62 | python_requires=">=3.7", 63 | ext_modules=ext_modules, 64 | package_data={"xxhash": ["py.typed", "**.pyi"]}, 65 | ) 66 | -------------------------------------------------------------------------------- /src/_xxhash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024, Yue Du 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | */ 26 | 27 | /* This module provides an interface to xxhash, an extremely fast 28 | non-cryptographic hash algorithm algorithm */ 29 | 30 | 31 | #include 32 | #include 33 | 34 | #include "xxhash.h" 35 | 36 | #define TOSTRING(x) #x 37 | #define VALUE_TO_STRING(x) TOSTRING(x) 38 | #define XXHASH_VERSION XXH_VERSION_MAJOR.XXH_VERSION_MINOR.XXH_VERSION_RELEASE 39 | 40 | #define XXH32_DIGESTSIZE 4 41 | #define XXH32_BLOCKSIZE 16 42 | #define XXH64_DIGESTSIZE 8 43 | #define XXH64_BLOCKSIZE 32 44 | #define XXH128_DIGESTSIZE 16 45 | #define XXH128_BLOCKSIZE 64 46 | 47 | 48 | /***************************************************************************** 49 | * Module Functions *********************************************************** 50 | ****************************************************************************/ 51 | 52 | /* XXH32 */ 53 | 54 | static PyObject *xxh32_digest(PyObject *self, PyObject *args, PyObject *kwargs) 55 | { 56 | XXH32_hash_t seed = 0; 57 | XXH32_hash_t intdigest; 58 | char *keywords[] = {"input", "seed", NULL}; 59 | Py_buffer buf; 60 | char retbuf[XXH32_DIGESTSIZE]; 61 | 62 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|I:xxh32_digest", keywords, &buf, &seed)) { 63 | return NULL; 64 | } 65 | 66 | intdigest = XXH32(buf.buf, buf.len, seed); 67 | PyBuffer_Release(&buf); 68 | 69 | XXH32_canonicalFromHash((XXH32_canonical_t *)retbuf, intdigest); 70 | 71 | return PyBytes_FromStringAndSize(retbuf, sizeof(retbuf)); 72 | } 73 | 74 | static PyObject *xxh32_intdigest(PyObject *self, PyObject *args, PyObject *kwargs) 75 | { 76 | XXH32_hash_t seed = 0; 77 | XXH32_hash_t intdigest; 78 | char *keywords[] = {"input", "seed", NULL}; 79 | Py_buffer buf; 80 | buf.buf = buf.obj = NULL; 81 | 82 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|I:xxh32_intdigest", keywords, &buf, &seed)) { 83 | return NULL; 84 | } 85 | 86 | intdigest = XXH32(buf.buf, buf.len, seed); 87 | PyBuffer_Release(&buf); 88 | 89 | return Py_BuildValue("I", intdigest); 90 | } 91 | 92 | static PyObject *xxh32_hexdigest(PyObject *self, PyObject *args, PyObject *kwargs) 93 | { 94 | XXH32_hash_t seed = 0; 95 | XXH32_hash_t intdigest; 96 | char digest[XXH32_DIGESTSIZE]; 97 | char *keywords[] = {"input", "seed", NULL}; 98 | Py_buffer buf; 99 | char retbuf[XXH32_DIGESTSIZE * 2]; 100 | int i, j; 101 | 102 | buf.buf = buf.obj = NULL; 103 | 104 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|I:xxh32_hexdigest", keywords, &buf, &seed)) { 105 | return NULL; 106 | } 107 | 108 | intdigest = XXH32(buf.buf, buf.len, seed); 109 | PyBuffer_Release(&buf); 110 | 111 | XXH32_canonicalFromHash((XXH32_canonical_t *)digest, intdigest); 112 | 113 | for (i = j = 0; i < XXH32_DIGESTSIZE; i++) { 114 | unsigned char c; 115 | c = (digest[i] >> 4) & 0xf; 116 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 117 | retbuf[j++] = c; 118 | c = (digest[i] & 0xf); 119 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 120 | retbuf[j++] = c; 121 | } 122 | 123 | return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf)); 124 | } 125 | 126 | /* XXH64 */ 127 | 128 | static PyObject *xxh64_digest(PyObject *self, PyObject *args, PyObject *kwargs) 129 | { 130 | XXH64_hash_t seed = 0; 131 | XXH64_hash_t intdigest; 132 | char *keywords[] = {"input", "seed", NULL}; 133 | Py_buffer buf; 134 | char retbuf[XXH64_DIGESTSIZE]; 135 | 136 | buf.buf = buf.obj = NULL; 137 | 138 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|K:xxh64_digest", keywords, &buf, &seed)) { 139 | return NULL; 140 | } 141 | 142 | intdigest = XXH64(buf.buf, buf.len, seed); 143 | PyBuffer_Release(&buf); 144 | 145 | XXH64_canonicalFromHash((XXH64_canonical_t *)retbuf, intdigest); 146 | 147 | return PyBytes_FromStringAndSize(retbuf, sizeof(retbuf)); 148 | } 149 | 150 | static PyObject *xxh64_intdigest(PyObject *self, PyObject *args, PyObject *kwargs) 151 | { 152 | XXH64_hash_t seed = 0; 153 | XXH64_hash_t intdigest; 154 | char *keywords[] = {"input", "seed", NULL}; 155 | Py_buffer buf; 156 | 157 | buf.buf = buf.obj = NULL; 158 | 159 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|K:xxh64_intdigest", keywords, &buf, &seed)) { 160 | return NULL; 161 | } 162 | 163 | intdigest = XXH64(buf.buf, buf.len, seed); 164 | PyBuffer_Release(&buf); 165 | 166 | return Py_BuildValue("K", intdigest); 167 | } 168 | 169 | static PyObject *xxh64_hexdigest(PyObject *self, PyObject *args, PyObject *kwargs) 170 | { 171 | XXH64_hash_t seed = 0; 172 | XXH64_hash_t intdigest; 173 | char digest[XXH64_DIGESTSIZE]; 174 | char *keywords[] = {"input", "seed", NULL}; 175 | Py_buffer buf; 176 | char retbuf[XXH64_DIGESTSIZE * 2]; 177 | int i, j; 178 | 179 | buf.buf = buf.obj = NULL; 180 | 181 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|K:xxh64_hexdigest", keywords, &buf, &seed)) { 182 | return NULL; 183 | } 184 | 185 | intdigest = XXH64(buf.buf, buf.len, seed); 186 | PyBuffer_Release(&buf); 187 | 188 | XXH64_canonicalFromHash((XXH64_canonical_t *)digest, intdigest); 189 | 190 | for (i = j = 0; i < XXH64_DIGESTSIZE; i++) { 191 | unsigned char c; 192 | c = (digest[i] >> 4) & 0xf; 193 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 194 | retbuf[j++] = c; 195 | c = (digest[i] & 0xf); 196 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 197 | retbuf[j++] = c; 198 | } 199 | 200 | return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf)); 201 | } 202 | 203 | /* XXH3_64 */ 204 | 205 | static PyObject *xxh3_64_digest(PyObject *self, PyObject *args, PyObject *kwargs) 206 | { 207 | XXH64_hash_t seed = 0; 208 | XXH64_hash_t intdigest = 0; 209 | char *keywords[] = {"input", "seed", NULL}; 210 | Py_buffer buf; 211 | char retbuf[XXH64_DIGESTSIZE]; 212 | 213 | buf.buf = buf.obj = NULL; 214 | 215 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|K:xxh3_64_digest", keywords, &buf, &seed)) { 216 | return NULL; 217 | } 218 | 219 | intdigest = XXH3_64bits_withSeed(buf.buf, buf.len, seed); 220 | PyBuffer_Release(&buf); 221 | 222 | XXH64_canonicalFromHash((XXH64_canonical_t *)retbuf, intdigest); 223 | 224 | return PyBytes_FromStringAndSize(retbuf, sizeof(retbuf)); 225 | } 226 | 227 | static PyObject *xxh3_64_intdigest(PyObject *self, PyObject *args, PyObject *kwargs) 228 | { 229 | XXH64_hash_t seed = 0; 230 | XXH64_hash_t intdigest = 0; 231 | char *keywords[] = {"input", "seed", NULL}; 232 | Py_buffer buf; 233 | 234 | buf.buf = buf.obj = NULL; 235 | 236 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|K:xxh3_64_intdigest", keywords, &buf, &seed)) { 237 | return NULL; 238 | } 239 | 240 | intdigest = XXH3_64bits_withSeed(buf.buf, buf.len, seed); 241 | PyBuffer_Release(&buf); 242 | 243 | return Py_BuildValue("K", intdigest); 244 | } 245 | 246 | static PyObject *xxh3_64_hexdigest(PyObject *self, PyObject *args, PyObject *kwargs) 247 | { 248 | XXH64_hash_t seed = 0; 249 | XXH64_hash_t intdigest; 250 | char digest[XXH64_DIGESTSIZE]; 251 | char *keywords[] = {"input", "seed", NULL}; 252 | Py_buffer buf; 253 | char retbuf[XXH64_DIGESTSIZE * 2]; 254 | int i, j; 255 | 256 | buf.buf = buf.obj = NULL; 257 | 258 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|K:xxh3_64_hexdigest", keywords, &buf, &seed)) { 259 | return NULL; 260 | } 261 | 262 | intdigest = XXH3_64bits_withSeed(buf.buf, buf.len, seed); 263 | PyBuffer_Release(&buf); 264 | 265 | 266 | XXH64_canonicalFromHash((XXH64_canonical_t *)digest, intdigest); 267 | 268 | for (i = j = 0; i < XXH64_DIGESTSIZE; i++) { 269 | unsigned char c; 270 | c = (digest[i] >> 4) & 0xf; 271 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 272 | retbuf[j++] = c; 273 | c = (digest[i] & 0xf); 274 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 275 | retbuf[j++] = c; 276 | } 277 | 278 | return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf)); 279 | } 280 | 281 | /* XXH3_128 */ 282 | 283 | static PyObject *xxh3_128_digest(PyObject *self, PyObject *args, PyObject *kwargs) 284 | { 285 | XXH64_hash_t seed = 0; 286 | XXH128_hash_t intdigest; 287 | char *keywords[] = {"input", "seed", NULL}; 288 | Py_buffer buf; 289 | char retbuf[XXH128_DIGESTSIZE]; 290 | 291 | buf.buf = buf.obj = NULL; 292 | 293 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|K:xxh3_128_digest", keywords, &buf, &seed)) { 294 | return NULL; 295 | } 296 | 297 | intdigest = XXH3_128bits_withSeed(buf.buf, buf.len, seed); 298 | PyBuffer_Release(&buf); 299 | 300 | XXH128_canonicalFromHash((XXH128_canonical_t *)retbuf, intdigest); 301 | 302 | return PyBytes_FromStringAndSize(retbuf, sizeof(retbuf)); 303 | } 304 | 305 | static PyObject *xxh3_128_intdigest(PyObject *self, PyObject *args, PyObject *kwargs) 306 | { 307 | XXH64_hash_t seed = 0; 308 | XXH128_hash_t intdigest; 309 | char *keywords[] = {"input", "seed", NULL}; 310 | Py_buffer buf; 311 | PyObject *result, *low, *high, *sixtyfour; 312 | 313 | buf.buf = buf.obj = NULL; 314 | 315 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|K:xxh3_128_intdigest", keywords, &buf, &seed)) { 316 | return NULL; 317 | } 318 | 319 | intdigest = XXH3_128bits_withSeed(buf.buf, buf.len, seed); 320 | PyBuffer_Release(&buf); 321 | 322 | sixtyfour = PyLong_FromLong(64); 323 | low = PyLong_FromUnsignedLongLong(intdigest.low64); 324 | high = PyLong_FromUnsignedLongLong(intdigest.high64); 325 | 326 | result = PyNumber_Lshift(high, sixtyfour); 327 | Py_DECREF(high); 328 | high = result; 329 | result = PyNumber_Add(high, low); 330 | Py_DECREF(high); 331 | Py_DECREF(low); 332 | Py_DECREF(sixtyfour); 333 | 334 | return result; 335 | } 336 | 337 | static PyObject *xxh3_128_hexdigest(PyObject *self, PyObject *args, PyObject *kwargs) 338 | { 339 | XXH64_hash_t seed = 0; 340 | XXH128_hash_t intdigest; 341 | char digest[XXH128_DIGESTSIZE]; 342 | char *keywords[] = {"input", "seed", NULL}; 343 | Py_buffer buf; 344 | char retbuf[XXH128_DIGESTSIZE * 2]; 345 | int i, j; 346 | 347 | buf.buf = buf.obj = NULL; 348 | 349 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|K:xxh3_128_hexdigest", keywords, &buf, &seed)) { 350 | return NULL; 351 | } 352 | 353 | intdigest = XXH3_128bits_withSeed(buf.buf, buf.len, seed); 354 | PyBuffer_Release(&buf); 355 | 356 | XXH128_canonicalFromHash((XXH128_canonical_t *)digest, intdigest); 357 | 358 | for (i = j = 0; i < XXH128_DIGESTSIZE; i++) { 359 | unsigned char c; 360 | c = (digest[i] >> 4) & 0xf; 361 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 362 | retbuf[j++] = c; 363 | c = (digest[i] & 0xf); 364 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 365 | retbuf[j++] = c; 366 | } 367 | 368 | return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf)); 369 | } 370 | 371 | /***************************************************************************** 372 | * Module Types *************************************************************** 373 | ****************************************************************************/ 374 | 375 | /* XXH32 */ 376 | 377 | typedef struct { 378 | PyObject_HEAD 379 | /* Type-specific fields go here. */ 380 | XXH32_state_t *xxhash_state; 381 | XXH32_hash_t seed; 382 | } PYXXH32Object; 383 | 384 | static PyTypeObject PYXXH32Type; 385 | 386 | static void PYXXH32_dealloc(PYXXH32Object *self) 387 | { 388 | XXH32_freeState(self->xxhash_state); 389 | PyObject_Del(self); 390 | } 391 | 392 | static void PYXXH32_do_update(PYXXH32Object *self, Py_buffer *buf) 393 | { 394 | Py_BEGIN_ALLOW_THREADS 395 | XXH32_update(self->xxhash_state, buf->buf, buf->len); 396 | Py_END_ALLOW_THREADS 397 | 398 | PyBuffer_Release(buf); 399 | } 400 | 401 | /* XXH32 methods */ 402 | 403 | static PyObject *PYXXH32_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) 404 | { 405 | PYXXH32Object *self; 406 | 407 | if ((self = PyObject_New(PYXXH32Object, &PYXXH32Type)) == NULL) { 408 | return NULL; 409 | } 410 | 411 | if ((self->xxhash_state = XXH32_createState()) == NULL) { 412 | return NULL; 413 | } 414 | 415 | return (PyObject *)self; 416 | } 417 | 418 | static int PYXXH32_init(PYXXH32Object *self, PyObject *args, PyObject *kwargs) 419 | { 420 | XXH32_hash_t seed = 0; 421 | char *keywords[] = {"input", "seed", NULL}; 422 | Py_buffer buf; 423 | 424 | buf.buf = buf.obj = NULL; 425 | 426 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s*I:__init__", keywords, &buf, &seed)) { 427 | return -1; 428 | } 429 | 430 | self->seed = seed; 431 | XXH32_reset(self->xxhash_state, seed); 432 | 433 | if (buf.buf) { 434 | PYXXH32_do_update(self, &buf); 435 | } 436 | 437 | return 0; 438 | } 439 | 440 | PyDoc_STRVAR( 441 | PYXXH32_update_doc, 442 | "update (input)\n\n" 443 | "Update the xxh32 object with the string input. Repeated calls are\n" 444 | "equivalent to a single call with the concatenation of all the arguments."); 445 | 446 | static PyObject *PYXXH32_update(PYXXH32Object *self, PyObject *args) 447 | { 448 | Py_buffer buf; 449 | 450 | buf.buf = buf.obj = NULL; 451 | 452 | if (!PyArg_ParseTuple(args, "s*:update", &buf)) { 453 | return NULL; 454 | } 455 | 456 | PYXXH32_do_update(self, &buf); 457 | 458 | Py_RETURN_NONE; 459 | } 460 | 461 | 462 | PyDoc_STRVAR( 463 | PYXXH32_digest_doc, 464 | "digest() -> string\n\n" 465 | "Return the digest of the strings passed to the update() method so\n" 466 | "far. This is a 4-byte string which may contain non-ASCII characters,\n" 467 | "including null bytes."); 468 | 469 | static PyObject *PYXXH32_digest(PYXXH32Object *self) 470 | { 471 | char retbuf[XXH32_DIGESTSIZE]; 472 | XXH32_hash_t intdigest; 473 | 474 | intdigest = XXH32_digest(self->xxhash_state); 475 | XXH32_canonicalFromHash((XXH32_canonical_t *)retbuf, intdigest); 476 | 477 | return PyBytes_FromStringAndSize(retbuf, sizeof(retbuf)); 478 | } 479 | 480 | PyDoc_STRVAR( 481 | PYXXH32_hexdigest_doc, 482 | "hexdigest() -> string\n\n" 483 | "Like digest(), but returns the digest as a string of hexadecimal digits."); 484 | 485 | static PyObject *PYXXH32_hexdigest(PYXXH32Object *self) 486 | { 487 | XXH32_hash_t intdigest; 488 | char digest[XXH32_DIGESTSIZE]; 489 | char retbuf[XXH32_DIGESTSIZE * 2]; 490 | int i, j; 491 | 492 | intdigest = XXH32_digest(self->xxhash_state); 493 | XXH32_canonicalFromHash((XXH32_canonical_t *)digest, intdigest); 494 | 495 | for (i = j = 0; i < XXH32_DIGESTSIZE; i++) { 496 | unsigned char c; 497 | c = (digest[i] >> 4) & 0xf; 498 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 499 | retbuf[j++] = c; 500 | c = (digest[i] & 0xf); 501 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 502 | retbuf[j++] = c; 503 | } 504 | 505 | return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf)); 506 | } 507 | 508 | PyDoc_STRVAR( 509 | PYXXH32_intdigest_doc, 510 | "intdigest() -> int\n\n" 511 | "Like digest(), but returns the digest as an integer, which is the integer\n" 512 | "returned by xxhash C API"); 513 | 514 | static PyObject *PYXXH32_intdigest(PYXXH32Object *self) 515 | { 516 | XXH32_hash_t digest = XXH32_digest(self->xxhash_state); 517 | return Py_BuildValue("I", digest); 518 | } 519 | 520 | PyDoc_STRVAR( 521 | PYXXH32_copy_doc, 522 | "copy() -> xxh32 object\n\n" 523 | "Return a copy (``clone'') of the xxh32 object."); 524 | 525 | static PyObject *PYXXH32_copy(PYXXH32Object *self) 526 | { 527 | PYXXH32Object *p; 528 | 529 | if ((p = PyObject_New(PYXXH32Object, &PYXXH32Type)) == NULL) { 530 | return NULL; 531 | } 532 | 533 | if ((p->xxhash_state = XXH32_createState()) == NULL) { 534 | return NULL; 535 | } 536 | 537 | p->seed = self->seed; 538 | XXH32_copyState(p->xxhash_state, self->xxhash_state); 539 | 540 | return (PyObject *)p; 541 | } 542 | 543 | PyDoc_STRVAR( 544 | PYXXH32_reset_doc, 545 | "reset()\n\n" 546 | "Reset state."); 547 | 548 | static PyObject *PYXXH32_reset(PYXXH32Object *self) 549 | { 550 | XXH32_reset(self->xxhash_state, self->seed); 551 | Py_RETURN_NONE; 552 | } 553 | 554 | static PyMethodDef PYXXH32_methods[] = { 555 | {"update", (PyCFunction)PYXXH32_update, METH_VARARGS, PYXXH32_update_doc}, 556 | {"digest", (PyCFunction)PYXXH32_digest, METH_NOARGS, PYXXH32_digest_doc}, 557 | {"hexdigest", (PyCFunction)PYXXH32_hexdigest, METH_NOARGS, PYXXH32_hexdigest_doc}, 558 | {"intdigest", (PyCFunction)PYXXH32_intdigest, METH_NOARGS, PYXXH32_intdigest_doc}, 559 | {"copy", (PyCFunction)PYXXH32_copy, METH_NOARGS, PYXXH32_copy_doc}, 560 | {"reset", (PyCFunction)PYXXH32_reset, METH_NOARGS, PYXXH32_reset_doc}, 561 | {NULL, NULL, 0, NULL} 562 | }; 563 | 564 | static PyObject *PYXXH32_get_block_size(PYXXH32Object *self, void *closure) 565 | { 566 | return PyLong_FromLong(XXH32_BLOCKSIZE); 567 | } 568 | 569 | static PyObject * 570 | PYXXH32_get_digest_size(PYXXH32Object *self, void *closure) 571 | { 572 | return PyLong_FromLong(XXH32_DIGESTSIZE); 573 | } 574 | 575 | static PyObject * 576 | PYXXH32_get_name(PYXXH32Object *self, void *closure) 577 | { 578 | return PyUnicode_FromStringAndSize("XXH32", strlen("XXH32")); 579 | } 580 | 581 | static PyObject * 582 | PYXXH32_get_seed(PYXXH32Object *self, void *closure) 583 | { 584 | return Py_BuildValue("I", self->seed); 585 | } 586 | 587 | static PyGetSetDef PYXXH32_getseters[] = { 588 | { 589 | "digest_size", 590 | (getter)PYXXH32_get_digest_size, NULL, 591 | "Digest size.", 592 | NULL 593 | }, 594 | { 595 | "block_size", 596 | (getter)PYXXH32_get_block_size, NULL, 597 | "Block size.", 598 | NULL 599 | }, 600 | { 601 | "name", 602 | (getter)PYXXH32_get_name, NULL, 603 | "Name. Always XXH32.", 604 | NULL 605 | }, 606 | { 607 | "digestsize", 608 | (getter)PYXXH32_get_digest_size, NULL, 609 | "Digest size.", 610 | NULL 611 | }, 612 | { 613 | "seed", 614 | (getter)PYXXH32_get_seed, NULL, 615 | "Seed.", 616 | NULL 617 | }, 618 | {NULL} /* Sentinel */ 619 | }; 620 | 621 | PyDoc_STRVAR( 622 | PYXXH32Type_doc, 623 | "An xxh32 represents the object used to calculate the XXH32 hash of a\n" 624 | "string of information.\n" 625 | "\n" 626 | "Methods:\n" 627 | "\n" 628 | "update(input) -- updates the current digest with the provided string.\n" 629 | "digest() -- return the current digest value\n" 630 | "hexdigest() -- return the current digest as a string of hexadecimal digits\n" 631 | "intdigest() -- return the current digest as an integer\n" 632 | "copy() -- return a copy of the current xxh32 object"); 633 | 634 | static PyTypeObject PYXXH32Type = { 635 | PyVarObject_HEAD_INIT(NULL, 0) 636 | "xxhash.xxh32", /* tp_name */ 637 | sizeof(PYXXH32Object), /* tp_basicsize */ 638 | 0, /* tp_itemsize */ 639 | (destructor)PYXXH32_dealloc, /* tp_dealloc */ 640 | 0, /* tp_print */ 641 | 0, /* tp_getattr */ 642 | 0, /* tp_setattr */ 643 | 0, /* tp_compare */ 644 | 0, /* tp_repr */ 645 | 0, /* tp_as_number */ 646 | 0, /* tp_as_sequence */ 647 | 0, /* tp_as_mapping */ 648 | 0, /* tp_hash */ 649 | 0, /* tp_call */ 650 | 0, /* tp_str */ 651 | 0, /* tp_getattro */ 652 | 0, /* tp_setattro */ 653 | 0, /* tp_as_buffer */ 654 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 655 | PYXXH32Type_doc, /* tp_doc */ 656 | 0, /* tp_traverse */ 657 | 0, /* tp_clear */ 658 | 0, /* tp_richcompare */ 659 | 0, /* tp_weaklistoffset */ 660 | 0, /* tp_iter */ 661 | 0, /* tp_iternext */ 662 | PYXXH32_methods, /* tp_methods */ 663 | 0, /* tp_members */ 664 | PYXXH32_getseters, /* tp_getset */ 665 | 0, /* tp_base */ 666 | 0, /* tp_dict */ 667 | 0, /* tp_descr_get */ 668 | 0, /* tp_descr_set */ 669 | 0, /* tp_dictoffset */ 670 | (initproc)PYXXH32_init, /* tp_init */ 671 | 0, /* tp_alloc */ 672 | PYXXH32_new, /* tp_new */ 673 | }; 674 | 675 | 676 | /* XXH64 */ 677 | 678 | typedef struct { 679 | PyObject_HEAD 680 | /* Type-specific fields go here. */ 681 | XXH64_state_t *xxhash_state; 682 | XXH64_hash_t seed; 683 | } PYXXH64Object; 684 | 685 | static PyTypeObject PYXXH64Type; 686 | 687 | static void PYXXH64_dealloc(PYXXH64Object *self) 688 | { 689 | XXH64_freeState(self->xxhash_state); 690 | PyObject_Del(self); 691 | } 692 | 693 | static void PYXXH64_do_update(PYXXH64Object *self, Py_buffer *buf) 694 | { 695 | Py_BEGIN_ALLOW_THREADS 696 | XXH64_update(self->xxhash_state, buf->buf, buf->len); 697 | Py_END_ALLOW_THREADS 698 | 699 | PyBuffer_Release(buf); 700 | } 701 | 702 | static PyObject *PYXXH64_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) 703 | { 704 | PYXXH64Object *self; 705 | 706 | if ((self = PyObject_New(PYXXH64Object, &PYXXH64Type)) == NULL) { 707 | return NULL; 708 | } 709 | 710 | if ((self->xxhash_state = XXH64_createState()) == NULL) { 711 | return NULL; 712 | } 713 | 714 | return (PyObject *)self; 715 | } 716 | 717 | static int PYXXH64_init(PYXXH64Object *self, PyObject *args, PyObject *kwargs) 718 | { 719 | XXH64_hash_t seed = 0; 720 | char *keywords[] = {"input", "seed", NULL}; 721 | Py_buffer buf; 722 | 723 | buf.buf = buf.obj = NULL; 724 | 725 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s*K:__init__", keywords, &buf, &seed)) { 726 | return -1; 727 | } 728 | 729 | self->seed = seed; 730 | XXH64_reset(self->xxhash_state, seed); 731 | 732 | if (buf.buf) { 733 | PYXXH64_do_update(self, &buf); 734 | } 735 | 736 | return 0; 737 | } 738 | 739 | PyDoc_STRVAR( 740 | PYXXH64_update_doc, 741 | "update (input)\n\n" 742 | "Update the xxh64 object with the string input. Repeated calls are\n" 743 | "equivalent to a single call with the concatenation of all the arguments."); 744 | 745 | static PyObject *PYXXH64_update(PYXXH64Object *self, PyObject *args) 746 | { 747 | Py_buffer buf; 748 | 749 | buf.buf = buf.obj = NULL; 750 | 751 | if (!PyArg_ParseTuple(args, "s*:update", &buf)) { 752 | return NULL; 753 | } 754 | 755 | PYXXH64_do_update(self, &buf); 756 | 757 | Py_RETURN_NONE; 758 | } 759 | 760 | PyDoc_STRVAR( 761 | PYXXH64_digest_doc, 762 | "digest() -> string\n\n" 763 | "Return the digest of the strings passed to the update() method so\n" 764 | "far. This is a 8-byte string which may contain non-ASCII characters,\n" 765 | "including null bytes."); 766 | 767 | static PyObject *PYXXH64_digest(PYXXH64Object *self) 768 | { 769 | char retbuf[XXH64_DIGESTSIZE]; 770 | XXH64_hash_t intdigest; 771 | 772 | intdigest = XXH64_digest(self->xxhash_state); 773 | XXH64_canonicalFromHash((XXH64_canonical_t *)retbuf, intdigest); 774 | 775 | return PyBytes_FromStringAndSize(retbuf, sizeof(retbuf)); 776 | } 777 | 778 | PyDoc_STRVAR( 779 | PYXXH64_hexdigest_doc, 780 | "hexdigest() -> string\n\n" 781 | "Like digest(), but returns the digest as a string of hexadecimal digits."); 782 | 783 | static PyObject *PYXXH64_hexdigest(PYXXH64Object *self) 784 | { 785 | XXH64_hash_t intdigest; 786 | char digest[XXH64_DIGESTSIZE]; 787 | char retbuf[XXH64_DIGESTSIZE * 2]; 788 | int i, j; 789 | 790 | intdigest = XXH64_digest(self->xxhash_state); 791 | XXH64_canonicalFromHash((XXH64_canonical_t *)digest, intdigest); 792 | 793 | for (i = j = 0; i < XXH64_DIGESTSIZE; i++) { 794 | unsigned char c; 795 | c = (digest[i] >> 4) & 0xf; 796 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 797 | retbuf[j++] = c; 798 | c = (digest[i] & 0xf); 799 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 800 | retbuf[j++] = c; 801 | } 802 | 803 | return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf)); 804 | } 805 | 806 | 807 | PyDoc_STRVAR( 808 | PYXXH64_intdigest_doc, 809 | "intdigest() -> int\n\n" 810 | "Like digest(), but returns the digest as an integer, which is the integer\n" 811 | "returned by xxhash C API"); 812 | 813 | static PyObject *PYXXH64_intdigest(PYXXH64Object *self) 814 | { 815 | XXH64_hash_t digest = XXH64_digest(self->xxhash_state); 816 | return Py_BuildValue("K", digest); 817 | } 818 | 819 | PyDoc_STRVAR( 820 | PYXXH64_copy_doc, 821 | "copy() -> xxh64 object\n\n" 822 | "Return a copy (``clone'') of the xxh64 object."); 823 | 824 | static PyObject *PYXXH64_copy(PYXXH64Object *self) 825 | { 826 | PYXXH64Object *p; 827 | 828 | if ((p = PyObject_New(PYXXH64Object, &PYXXH64Type)) == NULL) { 829 | return NULL; 830 | } 831 | 832 | if ((p->xxhash_state = XXH64_createState()) == NULL) { 833 | return NULL; 834 | } 835 | 836 | p->seed = self->seed; 837 | XXH64_copyState(p->xxhash_state, self->xxhash_state); 838 | 839 | return (PyObject *)p; 840 | } 841 | 842 | PyDoc_STRVAR( 843 | PYXXH64_reset_doc, 844 | "reset()\n\n" 845 | "Reset state."); 846 | 847 | static PyObject *PYXXH64_reset(PYXXH64Object *self) 848 | { 849 | XXH64_reset(self->xxhash_state, self->seed); 850 | Py_RETURN_NONE; 851 | } 852 | 853 | static PyMethodDef PYXXH64_methods[] = { 854 | {"update", (PyCFunction)PYXXH64_update, METH_VARARGS, PYXXH64_update_doc}, 855 | {"digest", (PyCFunction)PYXXH64_digest, METH_NOARGS, PYXXH64_digest_doc}, 856 | {"hexdigest", (PyCFunction)PYXXH64_hexdigest, METH_NOARGS, PYXXH64_hexdigest_doc}, 857 | {"intdigest", (PyCFunction)PYXXH64_intdigest, METH_NOARGS, PYXXH64_intdigest_doc}, 858 | {"copy", (PyCFunction)PYXXH64_copy, METH_NOARGS, PYXXH64_copy_doc}, 859 | {"reset", (PyCFunction)PYXXH64_reset, METH_NOARGS, PYXXH64_reset_doc}, 860 | {NULL, NULL, 0, NULL} 861 | }; 862 | 863 | static PyObject *PYXXH64_get_block_size(PYXXH64Object *self, void *closure) 864 | { 865 | return PyLong_FromLong(XXH64_BLOCKSIZE); 866 | } 867 | 868 | static PyObject * 869 | PYXXH64_get_digest_size(PYXXH64Object *self, void *closure) 870 | { 871 | return PyLong_FromLong(XXH64_DIGESTSIZE); 872 | } 873 | 874 | static PyObject * 875 | PYXXH64_get_name(PYXXH64Object *self, void *closure) 876 | { 877 | return PyUnicode_FromStringAndSize("XXH64", strlen("XXH64")); 878 | } 879 | 880 | static PyObject * 881 | PYXXH64_get_seed(PYXXH64Object *self, void *closure) 882 | { 883 | return Py_BuildValue("K", self->seed); 884 | } 885 | 886 | static PyGetSetDef PYXXH64_getseters[] = { 887 | { 888 | "digest_size", 889 | (getter)PYXXH64_get_digest_size, NULL, 890 | "Digest size.", 891 | NULL 892 | }, 893 | { 894 | "block_size", 895 | (getter)PYXXH64_get_block_size, NULL, 896 | "Block size.", 897 | NULL 898 | }, 899 | { 900 | "name", 901 | (getter)PYXXH64_get_name, NULL, 902 | "Name. Always XXH64.", 903 | NULL 904 | }, 905 | { 906 | "digestsize", 907 | (getter)PYXXH64_get_digest_size, NULL, 908 | "Digest size.", 909 | NULL 910 | }, 911 | { 912 | "seed", 913 | (getter)PYXXH64_get_seed, NULL, 914 | "Seed.", 915 | NULL 916 | }, 917 | {NULL} /* Sentinel */ 918 | }; 919 | 920 | PyDoc_STRVAR( 921 | PYXXH64Type_doc, 922 | "An xxh64 represents the object used to calculate the XXH64 hash of a\n" 923 | "string of information.\n" 924 | "\n" 925 | "Methods:\n" 926 | "\n" 927 | "update(input) -- updates the current digest with an additional string\n" 928 | "digest() -- return the current digest value\n" 929 | "hexdigest() -- return the current digest as a string of hexadecimal digits\n" 930 | "intdigest() -- return the current digest as an integer\n" 931 | "copy() -- return a copy of the current xxh64 object"); 932 | 933 | static PyTypeObject PYXXH64Type = { 934 | PyVarObject_HEAD_INIT(NULL, 0) 935 | "xxhash.xxh64", /* tp_name */ 936 | sizeof(PYXXH64Object), /* tp_basicsize */ 937 | 0, /* tp_itemsize */ 938 | (destructor)PYXXH64_dealloc, /* tp_dealloc */ 939 | 0, /* tp_print */ 940 | 0, /* tp_getattr */ 941 | 0, /* tp_setattr */ 942 | 0, /* tp_compare */ 943 | 0, /* tp_repr */ 944 | 0, /* tp_as_number */ 945 | 0, /* tp_as_sequence */ 946 | 0, /* tp_as_mapping */ 947 | 0, /* tp_hash */ 948 | 0, /* tp_call */ 949 | 0, /* tp_str */ 950 | 0, /* tp_getattro */ 951 | 0, /* tp_setattro */ 952 | 0, /* tp_as_buffer */ 953 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 954 | PYXXH64Type_doc, /* tp_doc */ 955 | 0, /* tp_traverse */ 956 | 0, /* tp_clear */ 957 | 0, /* tp_richcompare */ 958 | 0, /* tp_weaklistoffset */ 959 | 0, /* tp_iter */ 960 | 0, /* tp_iternext */ 961 | PYXXH64_methods, /* tp_methods */ 962 | 0, /* tp_members */ 963 | PYXXH64_getseters, /* tp_getset */ 964 | 0, /* tp_base */ 965 | 0, /* tp_dict */ 966 | 0, /* tp_descr_get */ 967 | 0, /* tp_descr_set */ 968 | 0, /* tp_dictoffset */ 969 | (initproc)PYXXH64_init, /* tp_init */ 970 | 0, /* tp_alloc */ 971 | PYXXH64_new, /* tp_new */ 972 | }; 973 | 974 | /* XXH3_64 */ 975 | 976 | typedef struct { 977 | PyObject_HEAD 978 | /* Type-specific fields go here. */ 979 | XXH3_state_t *xxhash_state; 980 | XXH64_hash_t seed; 981 | } PYXXH3_64Object; 982 | 983 | static PyTypeObject PYXXH3_64Type; 984 | 985 | static void PYXXH3_64_dealloc(PYXXH3_64Object *self) 986 | { 987 | XXH3_freeState(self->xxhash_state); 988 | PyObject_Del(self); 989 | } 990 | 991 | static void PYXXH3_64_do_update(PYXXH3_64Object *self, Py_buffer *buf) 992 | { 993 | Py_BEGIN_ALLOW_THREADS 994 | XXH3_64bits_update(self->xxhash_state, buf->buf, buf->len); 995 | Py_END_ALLOW_THREADS 996 | 997 | PyBuffer_Release(buf); 998 | } 999 | 1000 | static PyObject *PYXXH3_64_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) 1001 | { 1002 | PYXXH3_64Object *self; 1003 | 1004 | if ((self = PyObject_New(PYXXH3_64Object, &PYXXH3_64Type)) == NULL) { 1005 | return NULL; 1006 | } 1007 | 1008 | if ((self->xxhash_state = XXH3_createState()) == NULL) { 1009 | return NULL; 1010 | } 1011 | XXH3_64bits_reset_withSeed(self->xxhash_state, 0); 1012 | 1013 | return (PyObject *)self; 1014 | } 1015 | 1016 | static int PYXXH3_64_init(PYXXH3_64Object *self, PyObject *args, PyObject *kwargs) 1017 | { 1018 | XXH64_hash_t seed = 0; 1019 | char *keywords[] = {"input", "seed", NULL}; 1020 | Py_buffer buf; 1021 | 1022 | buf.buf = buf.obj = NULL; 1023 | 1024 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s*K:__init__", keywords, &buf, &seed)) { 1025 | return -1; 1026 | } 1027 | 1028 | self->seed = seed; 1029 | XXH3_64bits_reset_withSeed(self->xxhash_state, seed); 1030 | 1031 | if (buf.buf) { 1032 | PYXXH3_64_do_update(self, &buf); 1033 | } 1034 | 1035 | return 0; 1036 | } 1037 | 1038 | PyDoc_STRVAR( 1039 | PYXXH3_64_update_doc, 1040 | "update (input)\n\n" 1041 | "Update the xxh3_64 object with the string input. Repeated calls are\n" 1042 | "equivalent to a single call with the concatenation of all the arguments."); 1043 | 1044 | static PyObject *PYXXH3_64_update(PYXXH3_64Object *self, PyObject *args) 1045 | { 1046 | Py_buffer buf; 1047 | 1048 | buf.buf = buf.obj = NULL; 1049 | 1050 | if (!PyArg_ParseTuple(args, "s*:update", &buf)) { 1051 | return NULL; 1052 | } 1053 | 1054 | PYXXH3_64_do_update(self, &buf); 1055 | 1056 | Py_RETURN_NONE; 1057 | } 1058 | 1059 | PyDoc_STRVAR( 1060 | PYXXH3_64_digest_doc, 1061 | "digest() -> string\n\n" 1062 | "Return the digest of the strings passed to the update() method so\n" 1063 | "far. This is a 8-byte string which may contain non-ASCII characters,\n" 1064 | "including null bytes."); 1065 | 1066 | static PyObject *PYXXH3_64_digest(PYXXH3_64Object *self) 1067 | { 1068 | char retbuf[XXH64_DIGESTSIZE]; 1069 | XXH64_hash_t intdigest; 1070 | 1071 | intdigest = XXH3_64bits_digest(self->xxhash_state); 1072 | XXH64_canonicalFromHash((XXH64_canonical_t *)retbuf, intdigest); 1073 | 1074 | return PyBytes_FromStringAndSize(retbuf, sizeof(retbuf)); 1075 | } 1076 | 1077 | PyDoc_STRVAR( 1078 | PYXXH3_64_hexdigest_doc, 1079 | "hexdigest() -> string\n\n" 1080 | "Like digest(), but returns the digest as a string of hexadecimal digits."); 1081 | 1082 | static PyObject *PYXXH3_64_hexdigest(PYXXH3_64Object *self) 1083 | { 1084 | XXH64_hash_t intdigest; 1085 | char digest[XXH64_DIGESTSIZE]; 1086 | char retbuf[XXH64_DIGESTSIZE * 2]; 1087 | int i, j; 1088 | 1089 | 1090 | intdigest = XXH3_64bits_digest(self->xxhash_state); 1091 | XXH64_canonicalFromHash((XXH64_canonical_t *)digest, intdigest); 1092 | 1093 | for (i = j = 0; i < XXH64_DIGESTSIZE; i++) { 1094 | unsigned char c; 1095 | c = (digest[i] >> 4) & 0xf; 1096 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 1097 | retbuf[j++] = c; 1098 | c = (digest[i] & 0xf); 1099 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 1100 | retbuf[j++] = c; 1101 | } 1102 | 1103 | return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf)); 1104 | } 1105 | 1106 | 1107 | PyDoc_STRVAR( 1108 | PYXXH3_64_intdigest_doc, 1109 | "intdigest() -> int\n\n" 1110 | "Like digest(), but returns the digest as an integer, which is the integer\n" 1111 | "returned by xxhash C API"); 1112 | 1113 | static PyObject *PYXXH3_64_intdigest(PYXXH3_64Object *self) 1114 | { 1115 | XXH64_hash_t intdigest = XXH3_64bits_digest(self->xxhash_state); 1116 | return Py_BuildValue("K", intdigest); 1117 | } 1118 | 1119 | PyDoc_STRVAR( 1120 | PYXXH3_64_copy_doc, 1121 | "copy() -> xxh64 object\n\n" 1122 | "Return a copy (``clone'') of the xxh64 object."); 1123 | 1124 | static PyObject *PYXXH3_64_copy(PYXXH3_64Object *self) 1125 | { 1126 | PYXXH3_64Object *p; 1127 | 1128 | if ((p = PyObject_New(PYXXH3_64Object, &PYXXH3_64Type)) == NULL) { 1129 | return NULL; 1130 | } 1131 | 1132 | if ((p->xxhash_state = XXH3_createState()) == NULL) { 1133 | return NULL; 1134 | } 1135 | 1136 | p->seed = self->seed; 1137 | XXH3_copyState(p->xxhash_state, self->xxhash_state); 1138 | #if XXH_VERSION_NUMBER < 704 1139 | // v0.7.3 and earlier have a bug where states reset with a seed 1140 | // will have a wild pointer to the original state when copied, 1141 | // causing a use-after-free if the original is freed. 1142 | if (p->xxhash_state->secret == &self->xxhash_state->customSecret[0]) 1143 | p->xxhash_state->secret = &p->xxhash_state->customSecret[0]; 1144 | #endif 1145 | 1146 | return (PyObject *)p; 1147 | } 1148 | 1149 | PyDoc_STRVAR( 1150 | PYXXH3_64_reset_doc, 1151 | "reset()\n\n" 1152 | "Reset state."); 1153 | 1154 | static PyObject *PYXXH3_64_reset(PYXXH3_64Object *self) 1155 | { 1156 | XXH3_64bits_reset_withSeed(self->xxhash_state, self->seed); 1157 | Py_RETURN_NONE; 1158 | } 1159 | 1160 | static PyMethodDef PYXXH3_64_methods[] = { 1161 | {"update", (PyCFunction)PYXXH3_64_update, METH_VARARGS, PYXXH3_64_update_doc}, 1162 | {"digest", (PyCFunction)PYXXH3_64_digest, METH_NOARGS, PYXXH3_64_digest_doc}, 1163 | {"hexdigest", (PyCFunction)PYXXH3_64_hexdigest, METH_NOARGS, PYXXH3_64_hexdigest_doc}, 1164 | {"intdigest", (PyCFunction)PYXXH3_64_intdigest, METH_NOARGS, PYXXH3_64_intdigest_doc}, 1165 | {"copy", (PyCFunction)PYXXH3_64_copy, METH_NOARGS, PYXXH3_64_copy_doc}, 1166 | {"reset", (PyCFunction)PYXXH3_64_reset, METH_NOARGS, PYXXH3_64_reset_doc}, 1167 | {NULL, NULL, 0, NULL} 1168 | }; 1169 | 1170 | static PyObject *PYXXH3_64_get_block_size(PYXXH3_64Object *self, void *closure) 1171 | { 1172 | return PyLong_FromLong(XXH64_BLOCKSIZE); 1173 | } 1174 | 1175 | static PyObject * 1176 | PYXXH3_64_get_digest_size(PYXXH3_64Object *self, void *closure) 1177 | { 1178 | return PyLong_FromLong(XXH64_DIGESTSIZE); 1179 | } 1180 | 1181 | static PyObject * 1182 | PYXXH3_64_get_name(PYXXH3_64Object *self, void *closure) 1183 | { 1184 | return PyUnicode_FromStringAndSize("XXH3_64", strlen("XXH3_64")); 1185 | } 1186 | 1187 | static PyObject * 1188 | PYXXH3_64_get_seed(PYXXH3_64Object *self, void *closure) 1189 | { 1190 | return Py_BuildValue("K", self->seed); 1191 | } 1192 | 1193 | static PyGetSetDef PYXXH3_64_getseters[] = { 1194 | { 1195 | "digest_size", 1196 | (getter)PYXXH3_64_get_digest_size, NULL, 1197 | "Digest size.", 1198 | NULL 1199 | }, 1200 | { 1201 | "block_size", 1202 | (getter)PYXXH3_64_get_block_size, NULL, 1203 | "Block size.", 1204 | NULL 1205 | }, 1206 | { 1207 | "name", 1208 | (getter)PYXXH3_64_get_name, NULL, 1209 | "Name. Always XXH3_64.", 1210 | NULL 1211 | }, 1212 | { 1213 | "digestsize", 1214 | (getter)PYXXH3_64_get_digest_size, NULL, 1215 | "Digest size.", 1216 | NULL 1217 | }, 1218 | { 1219 | "seed", 1220 | (getter)PYXXH3_64_get_seed, NULL, 1221 | "Seed.", 1222 | NULL 1223 | }, 1224 | {NULL} /* Sentinel */ 1225 | }; 1226 | 1227 | PyDoc_STRVAR( 1228 | PYXXH3_64Type_doc, 1229 | "An xxh3_64 represents the object used to calculate the XXH3_64 hash of a\n" 1230 | "string of information.\n" 1231 | "\n" 1232 | "Methods:\n" 1233 | "\n" 1234 | "update(input) -- updates the current digest with an additional string\n" 1235 | "digest() -- return the current digest value\n" 1236 | "hexdigest() -- return the current digest as a string of hexadecimal digits\n" 1237 | "intdigest() -- return the current digest as an integer\n" 1238 | "copy() -- return a copy of the current xxh64 object"); 1239 | 1240 | static PyTypeObject PYXXH3_64Type = { 1241 | PyVarObject_HEAD_INIT(NULL, 0) 1242 | "xxhash.xxh3_64", /* tp_name */ 1243 | sizeof(PYXXH3_64Object), /* tp_basicsize */ 1244 | 0, /* tp_itemsize */ 1245 | (destructor)PYXXH3_64_dealloc, /* tp_dealloc */ 1246 | 0, /* tp_print */ 1247 | 0, /* tp_getattr */ 1248 | 0, /* tp_setattr */ 1249 | 0, /* tp_compare */ 1250 | 0, /* tp_repr */ 1251 | 0, /* tp_as_number */ 1252 | 0, /* tp_as_sequence */ 1253 | 0, /* tp_as_mapping */ 1254 | 0, /* tp_hash */ 1255 | 0, /* tp_call */ 1256 | 0, /* tp_str */ 1257 | 0, /* tp_getattro */ 1258 | 0, /* tp_setattro */ 1259 | 0, /* tp_as_buffer */ 1260 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 1261 | PYXXH3_64Type_doc, /* tp_doc */ 1262 | 0, /* tp_traverse */ 1263 | 0, /* tp_clear */ 1264 | 0, /* tp_richcompare */ 1265 | 0, /* tp_weaklistoffset */ 1266 | 0, /* tp_iter */ 1267 | 0, /* tp_iternext */ 1268 | PYXXH3_64_methods, /* tp_methods */ 1269 | 0, /* tp_members */ 1270 | PYXXH3_64_getseters, /* tp_getset */ 1271 | 0, /* tp_base */ 1272 | 0, /* tp_dict */ 1273 | 0, /* tp_descr_get */ 1274 | 0, /* tp_descr_set */ 1275 | 0, /* tp_dictoffset */ 1276 | (initproc)PYXXH3_64_init, /* tp_init */ 1277 | 0, /* tp_alloc */ 1278 | PYXXH3_64_new, /* tp_new */ 1279 | }; 1280 | 1281 | 1282 | /* XXH3_128 */ 1283 | 1284 | typedef struct { 1285 | PyObject_HEAD 1286 | /* Type-specific fields go here. */ 1287 | XXH3_state_t *xxhash_state; 1288 | XXH64_hash_t seed; 1289 | } PYXXH3_128Object; 1290 | 1291 | static PyTypeObject PYXXH3_128Type; 1292 | 1293 | static void PYXXH3_128_dealloc(PYXXH3_128Object *self) 1294 | { 1295 | XXH3_freeState(self->xxhash_state); 1296 | PyObject_Del(self); 1297 | } 1298 | 1299 | static void PYXXH3_128_do_update(PYXXH3_128Object *self, Py_buffer *buf) 1300 | { 1301 | Py_BEGIN_ALLOW_THREADS 1302 | XXH3_128bits_update(self->xxhash_state, buf->buf, buf->len); 1303 | Py_END_ALLOW_THREADS 1304 | 1305 | PyBuffer_Release(buf); 1306 | } 1307 | 1308 | static PyObject *PYXXH3_128_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) 1309 | { 1310 | PYXXH3_128Object *self; 1311 | 1312 | if ((self = PyObject_New(PYXXH3_128Object, &PYXXH3_128Type)) == NULL) { 1313 | return NULL; 1314 | } 1315 | 1316 | if ((self->xxhash_state = XXH3_createState()) == NULL) { 1317 | return NULL; 1318 | } 1319 | 1320 | self->seed = 0; 1321 | XXH3_128bits_reset_withSeed(self->xxhash_state, 0); 1322 | 1323 | return (PyObject *)self; 1324 | } 1325 | 1326 | static int PYXXH3_128_init(PYXXH3_128Object *self, PyObject *args, PyObject *kwargs) 1327 | { 1328 | XXH64_hash_t seed = 0; 1329 | char *keywords[] = {"input", "seed", NULL}; 1330 | Py_buffer buf; 1331 | 1332 | buf.buf = buf.obj = NULL; 1333 | 1334 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s*K:__init__", keywords, &buf, &seed)) { 1335 | return -1; 1336 | } 1337 | 1338 | self->seed = seed; 1339 | XXH3_128bits_reset_withSeed(self->xxhash_state, seed); 1340 | 1341 | if (buf.buf) { 1342 | PYXXH3_128_do_update(self, &buf); 1343 | } 1344 | 1345 | return 0; 1346 | } 1347 | 1348 | PyDoc_STRVAR( 1349 | PYXXH3_128_update_doc, 1350 | "update (input)\n\n" 1351 | "Update the xxh3_128 object with the string input. Repeated calls are\n" 1352 | "equivalent to a single call with the concatenation of all the arguments."); 1353 | 1354 | static PyObject *PYXXH3_128_update(PYXXH3_128Object *self, PyObject *args) 1355 | { 1356 | Py_buffer buf; 1357 | buf.buf = buf.obj = NULL; 1358 | 1359 | if (!PyArg_ParseTuple(args, "s*:update", &buf)) { 1360 | return NULL; 1361 | } 1362 | 1363 | PYXXH3_128_do_update(self, &buf); 1364 | 1365 | Py_RETURN_NONE; 1366 | } 1367 | 1368 | PyDoc_STRVAR( 1369 | PYXXH3_128_digest_doc, 1370 | "digest() -> string\n\n" 1371 | "Return the digest of the strings passed to the update() method so\n" 1372 | "far. This is a 8-byte string which may contain non-ASCII characters,\n" 1373 | "including null bytes."); 1374 | 1375 | static PyObject *PYXXH3_128_digest(PYXXH3_128Object *self) 1376 | { 1377 | char retbuf[XXH128_DIGESTSIZE]; 1378 | XXH128_hash_t intdigest; 1379 | 1380 | intdigest = XXH3_128bits_digest(self->xxhash_state); 1381 | XXH128_canonicalFromHash((XXH128_canonical_t *)retbuf, intdigest); 1382 | 1383 | return PyBytes_FromStringAndSize(retbuf, sizeof(retbuf)); 1384 | } 1385 | 1386 | PyDoc_STRVAR( 1387 | PYXXH3_128_hexdigest_doc, 1388 | "hexdigest() -> string\n\n" 1389 | "Like digest(), but returns the digest as a string of hexadecimal digits."); 1390 | 1391 | static PyObject *PYXXH3_128_hexdigest(PYXXH3_128Object *self) 1392 | { 1393 | XXH128_hash_t intdigest; 1394 | char digest[XXH128_DIGESTSIZE]; 1395 | char retbuf[XXH128_DIGESTSIZE * 2]; 1396 | int i, j; 1397 | 1398 | intdigest = XXH3_128bits_digest(self->xxhash_state); 1399 | XXH128_canonicalFromHash((XXH128_canonical_t *)digest, intdigest); 1400 | 1401 | for (i = j = 0; i < XXH128_DIGESTSIZE; i++) { 1402 | unsigned char c; 1403 | c = (digest[i] >> 4) & 0xf; 1404 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 1405 | retbuf[j++] = c; 1406 | c = (digest[i] & 0xf); 1407 | c = (c > 9) ? c + 'a' - 10 : c + '0'; 1408 | retbuf[j++] = c; 1409 | } 1410 | 1411 | return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf)); 1412 | } 1413 | 1414 | 1415 | PyDoc_STRVAR( 1416 | PYXXH3_128_intdigest_doc, 1417 | "intdigest() -> int\n\n" 1418 | "Like digest(), but returns the digest as an integer, which is the integer\n" 1419 | "returned by xxhash C API"); 1420 | 1421 | static PyObject *PYXXH3_128_intdigest(PYXXH3_128Object *self) 1422 | { 1423 | XXH128_hash_t intdigest; 1424 | PyObject *result, *high, *low, *sixtyfour; 1425 | 1426 | intdigest = XXH3_128bits_digest(self->xxhash_state); 1427 | 1428 | sixtyfour = PyLong_FromLong(64); 1429 | low = PyLong_FromUnsignedLongLong(intdigest.low64); 1430 | high = PyLong_FromUnsignedLongLong(intdigest.high64); 1431 | 1432 | result = PyNumber_Lshift(high, sixtyfour); 1433 | Py_DECREF(high); 1434 | high = result; 1435 | result = PyNumber_Add(high, low); 1436 | Py_DECREF(high); 1437 | Py_DECREF(low); 1438 | Py_DECREF(sixtyfour); 1439 | return result; 1440 | } 1441 | 1442 | PyDoc_STRVAR( 1443 | PYXXH3_128_copy_doc, 1444 | "copy() -> xxh3_128 object\n\n" 1445 | "Return a copy (``clone'') of the xxh3_128 object."); 1446 | 1447 | static PyObject *PYXXH3_128_copy(PYXXH3_128Object *self) 1448 | { 1449 | PYXXH3_128Object *p; 1450 | 1451 | if ((p = PyObject_New(PYXXH3_128Object, &PYXXH3_128Type)) == NULL) { 1452 | return NULL; 1453 | } 1454 | 1455 | if ((p->xxhash_state = XXH3_createState()) == NULL) { 1456 | return NULL; 1457 | } 1458 | 1459 | p->seed = self->seed; 1460 | XXH3_copyState(p->xxhash_state, self->xxhash_state); 1461 | #if XXH_VERSION_NUMBER < 704 1462 | // v0.7.3 and earlier have a bug where states reset with a seed 1463 | // will have a wild pointer to the original state when copied, 1464 | // causing a use-after-free if the original is freed. 1465 | if (p->xxhash_state->secret == &self->xxhash_state->customSecret[0]) 1466 | p->xxhash_state->secret = &p->xxhash_state->customSecret[0]; 1467 | #endif 1468 | 1469 | return (PyObject *)p; 1470 | } 1471 | 1472 | PyDoc_STRVAR( 1473 | PYXXH3_128_reset_doc, 1474 | "reset()\n\n" 1475 | "Reset state."); 1476 | 1477 | static PyObject *PYXXH3_128_reset(PYXXH3_128Object *self) 1478 | { 1479 | XXH3_128bits_reset_withSeed(self->xxhash_state, self->seed); 1480 | Py_RETURN_NONE; 1481 | } 1482 | 1483 | static PyMethodDef PYXXH3_128_methods[] = { 1484 | {"update", (PyCFunction)PYXXH3_128_update, METH_VARARGS, PYXXH3_128_update_doc}, 1485 | {"digest", (PyCFunction)PYXXH3_128_digest, METH_NOARGS, PYXXH3_128_digest_doc}, 1486 | {"hexdigest", (PyCFunction)PYXXH3_128_hexdigest, METH_NOARGS, PYXXH3_128_hexdigest_doc}, 1487 | {"intdigest", (PyCFunction)PYXXH3_128_intdigest, METH_NOARGS, PYXXH3_128_intdigest_doc}, 1488 | {"copy", (PyCFunction)PYXXH3_128_copy, METH_NOARGS, PYXXH3_128_copy_doc}, 1489 | {"reset", (PyCFunction)PYXXH3_128_reset, METH_NOARGS, PYXXH3_128_reset_doc}, 1490 | {NULL, NULL, 0, NULL} 1491 | }; 1492 | 1493 | static PyObject *PYXXH3_128_get_block_size(PYXXH3_128Object *self, void *closure) 1494 | { 1495 | return PyLong_FromLong(XXH128_BLOCKSIZE); 1496 | } 1497 | 1498 | static PyObject * 1499 | PYXXH3_128_get_digest_size(PYXXH3_128Object *self, void *closure) 1500 | { 1501 | return PyLong_FromLong(XXH128_DIGESTSIZE); 1502 | } 1503 | 1504 | static PyObject * 1505 | PYXXH3_128_get_name(PYXXH3_128Object *self, void *closure) 1506 | { 1507 | return PyUnicode_FromStringAndSize("XXH3_128", strlen("XXH3_128")); 1508 | } 1509 | 1510 | static PyObject * 1511 | PYXXH3_128_get_seed(PYXXH3_128Object *self, void *closure) 1512 | { 1513 | return Py_BuildValue("K", self->seed); 1514 | } 1515 | 1516 | static PyGetSetDef PYXXH3_128_getseters[] = { 1517 | { 1518 | "digest_size", 1519 | (getter)PYXXH3_128_get_digest_size, NULL, 1520 | "Digest size.", 1521 | NULL 1522 | }, 1523 | { 1524 | "block_size", 1525 | (getter)PYXXH3_128_get_block_size, NULL, 1526 | "Block size.", 1527 | NULL 1528 | }, 1529 | { 1530 | "name", 1531 | (getter)PYXXH3_128_get_name, NULL, 1532 | "Name. Always XXH3_128.", 1533 | NULL 1534 | }, 1535 | { 1536 | "digestsize", 1537 | (getter)PYXXH3_128_get_digest_size, NULL, 1538 | "Digest size.", 1539 | NULL 1540 | }, 1541 | { 1542 | "seed", 1543 | (getter)PYXXH3_128_get_seed, NULL, 1544 | "Seed.", 1545 | NULL 1546 | }, 1547 | {NULL} /* Sentinel */ 1548 | }; 1549 | 1550 | PyDoc_STRVAR( 1551 | PYXXH3_128Type_doc, 1552 | "An xxh3_128 represents the object used to calculate the XXH3_128 hash of a\n" 1553 | "string of information.\n" 1554 | "\n" 1555 | "Methods:\n" 1556 | "\n" 1557 | "update(input) -- updates the current digest with an additional string\n" 1558 | "digest() -- return the current digest value\n" 1559 | "hexdigest() -- return the current digest as a string of hexadecimal digits\n" 1560 | "intdigest() -- return the current digest as an integer\n" 1561 | "copy() -- return a copy of the current xxh3_128 object"); 1562 | 1563 | static PyTypeObject PYXXH3_128Type = { 1564 | PyVarObject_HEAD_INIT(NULL, 0) 1565 | "xxhash.xxh3_128", /* tp_name */ 1566 | sizeof(PYXXH3_128Object), /* tp_basicsize */ 1567 | 0, /* tp_itemsize */ 1568 | (destructor)PYXXH3_128_dealloc, /* tp_dealloc */ 1569 | 0, /* tp_print */ 1570 | 0, /* tp_getattr */ 1571 | 0, /* tp_setattr */ 1572 | 0, /* tp_compare */ 1573 | 0, /* tp_repr */ 1574 | 0, /* tp_as_number */ 1575 | 0, /* tp_as_sequence */ 1576 | 0, /* tp_as_mapping */ 1577 | 0, /* tp_hash */ 1578 | 0, /* tp_call */ 1579 | 0, /* tp_str */ 1580 | 0, /* tp_getattro */ 1581 | 0, /* tp_setattro */ 1582 | 0, /* tp_as_buffer */ 1583 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 1584 | PYXXH3_128Type_doc, /* tp_doc */ 1585 | 0, /* tp_traverse */ 1586 | 0, /* tp_clear */ 1587 | 0, /* tp_richcompare */ 1588 | 0, /* tp_weaklistoffset */ 1589 | 0, /* tp_iter */ 1590 | 0, /* tp_iternext */ 1591 | PYXXH3_128_methods, /* tp_methods */ 1592 | 0, /* tp_members */ 1593 | PYXXH3_128_getseters, /* tp_getset */ 1594 | 0, /* tp_base */ 1595 | 0, /* tp_dict */ 1596 | 0, /* tp_descr_get */ 1597 | 0, /* tp_descr_set */ 1598 | 0, /* tp_dictoffset */ 1599 | (initproc)PYXXH3_128_init, /* tp_init */ 1600 | 0, /* tp_alloc */ 1601 | PYXXH3_128_new, /* tp_new */ 1602 | }; 1603 | 1604 | /***************************************************************************** 1605 | * Module Init **************************************************************** 1606 | ****************************************************************************/ 1607 | 1608 | /* ref: https://docs.python.org/2/howto/cporting.html */ 1609 | 1610 | static PyMethodDef methods[] = { 1611 | {"xxh32_digest", (PyCFunction)xxh32_digest, METH_VARARGS | METH_KEYWORDS, "xxh32_digest"}, 1612 | {"xxh32_intdigest", (PyCFunction)xxh32_intdigest, METH_VARARGS | METH_KEYWORDS, "xxh32_intdigest"}, 1613 | {"xxh32_hexdigest", (PyCFunction)xxh32_hexdigest, METH_VARARGS | METH_KEYWORDS, "xxh32_hexdigest"}, 1614 | {"xxh64_digest", (PyCFunction)xxh64_digest, METH_VARARGS | METH_KEYWORDS, "xxh64_digest"}, 1615 | {"xxh64_intdigest", (PyCFunction)xxh64_intdigest, METH_VARARGS | METH_KEYWORDS, "xxh64_intdigest"}, 1616 | {"xxh64_hexdigest", (PyCFunction)xxh64_hexdigest, METH_VARARGS | METH_KEYWORDS, "xxh64_hexdigest"}, 1617 | {"xxh3_64_digest", (PyCFunction)xxh3_64_digest, METH_VARARGS | METH_KEYWORDS, "xxh3_64_digest"}, 1618 | {"xxh3_64_intdigest", (PyCFunction)xxh3_64_intdigest, METH_VARARGS | METH_KEYWORDS, "xxh3_64_intdigest"}, 1619 | {"xxh3_64_hexdigest", (PyCFunction)xxh3_64_hexdigest, METH_VARARGS | METH_KEYWORDS, "xxh3_64_hexdigest"}, 1620 | {"xxh3_128_digest", (PyCFunction)xxh3_128_digest, METH_VARARGS | METH_KEYWORDS, "xxh3_128_digest"}, 1621 | {"xxh3_128_intdigest", (PyCFunction)xxh3_128_intdigest, METH_VARARGS | METH_KEYWORDS, "xxh3_128_intdigest"}, 1622 | {"xxh3_128_hexdigest", (PyCFunction)xxh3_128_hexdigest, METH_VARARGS | METH_KEYWORDS, "xxh3_128_hexdigest"}, 1623 | {NULL, NULL, 0, NULL} 1624 | }; 1625 | 1626 | 1627 | static struct PyModuleDef moduledef = { 1628 | PyModuleDef_HEAD_INIT, 1629 | "_xxhash", 1630 | NULL, 1631 | -1, 1632 | methods, 1633 | NULL, 1634 | NULL, 1635 | NULL, 1636 | NULL 1637 | }; 1638 | 1639 | #define INITERROR return NULL 1640 | 1641 | PyMODINIT_FUNC PyInit__xxhash(void) 1642 | { 1643 | PyObject *module; 1644 | 1645 | module = PyModule_Create(&moduledef); 1646 | 1647 | if (module == NULL) { 1648 | INITERROR; 1649 | } 1650 | 1651 | /* xxh32 */ 1652 | if (PyType_Ready(&PYXXH32Type) < 0) { 1653 | INITERROR; 1654 | } 1655 | 1656 | Py_INCREF(&PYXXH32Type); 1657 | PyModule_AddObject(module, "xxh32", (PyObject *)&PYXXH32Type); 1658 | 1659 | 1660 | /* xxh64 */ 1661 | if (PyType_Ready(&PYXXH64Type) < 0) { 1662 | INITERROR; 1663 | } 1664 | 1665 | Py_INCREF(&PYXXH64Type); 1666 | PyModule_AddObject(module, "xxh64", (PyObject *)&PYXXH64Type); 1667 | 1668 | /* xxh3_64 */ 1669 | if (PyType_Ready(&PYXXH3_64Type) < 0) { 1670 | INITERROR; 1671 | } 1672 | 1673 | Py_INCREF(&PYXXH3_64Type); 1674 | PyModule_AddObject(module, "xxh3_64", (PyObject *)&PYXXH3_64Type); 1675 | 1676 | /* xxh3_128 */ 1677 | if (PyType_Ready(&PYXXH3_128Type) < 0) { 1678 | INITERROR; 1679 | } 1680 | 1681 | Py_INCREF(&PYXXH3_128Type); 1682 | PyModule_AddObject(module, "xxh3_128", (PyObject *)&PYXXH3_128Type); 1683 | 1684 | /* version */ 1685 | PyModule_AddStringConstant(module, "XXHASH_VERSION", VALUE_TO_STRING(XXHASH_VERSION)); 1686 | 1687 | #ifdef Py_GIL_DISABLED 1688 | PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED); 1689 | #endif 1690 | 1691 | return module; 1692 | } 1693 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifduyue/python-xxhash/b3cc1b8bc1c27da0a0ed8bce2c1d92ae86966961/tests/__init__.py -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | import array 2 | import os 3 | import unittest 4 | import random 5 | import struct 6 | import sys 7 | from typing import Union, List, TYPE_CHECKING 8 | import xxhash 9 | 10 | if TYPE_CHECKING: 11 | InputType = Union[str, bytes, bytearray, memoryview, array.ArrayType[int]] 12 | else: 13 | InputType = None 14 | 15 | 16 | def getrefcount(obj): 17 | if hasattr(sys, "getrefcount"): 18 | return sys.getrefcount(obj) 19 | else: 20 | # Non-CPython implementation 21 | return 0 22 | 23 | 24 | class TestXXH(unittest.TestCase): 25 | def test_version(self): 26 | self.assertTrue(xxhash.VERSION) 27 | self.assertTrue(xxhash.XXHASH_VERSION) 28 | 29 | def test_buffer_types(self): 30 | # Various buffer-like objects are accepted, and they give similar values 31 | args: List[InputType] = [ 32 | b'ab\x00c', 33 | bytearray(b'ab\x00c'), 34 | array.array('b', b'ab\x00c'), 35 | ] 36 | # An array object with non-1 itemsize 37 | a = array.array('i', struct.unpack('i', b'ab\x00c')) 38 | assert a.itemsize == 4 39 | args.append(a) 40 | # A memoryview, where supported 41 | if sys.version_info >= (2, 7): 42 | args.append(memoryview(b'ab\x00c')) 43 | 44 | for func in [xxhash.xxh32, xxhash.xxh64, xxhash.xxh3_64, xxhash.xxh3_128]: 45 | old_refcounts = list(map(getrefcount, args)) 46 | # With constructor 47 | values = set(func(arg).hexdigest() for arg in args) 48 | self.assertEqual(len(values), 1, values) 49 | # With update() 50 | values = set() 51 | for arg in args: 52 | x = func() 53 | x.update(arg) 54 | values.add(x.hexdigest()) 55 | self.assertEqual(len(values), 1, values) 56 | # No reference leak in CPython extension 57 | del arg 58 | self.assertEqual(list(map(getrefcount, args)), old_refcounts) 59 | 60 | 61 | if __name__ == '__main__': 62 | unittest.main() 63 | -------------------------------------------------------------------------------- /tests/test_algorithms_available.py: -------------------------------------------------------------------------------- 1 | import xxhash 2 | import unittest 3 | 4 | 5 | class TestAlgorithmExists(unittest.TestCase): 6 | def test_xxh32(self): 7 | xxhash.xxh32 8 | assert "xxh32" in xxhash.algorithms_available 9 | 10 | def test_xxh64(self): 11 | xxhash.xxh64 12 | assert "xxh64" in xxhash.algorithms_available 13 | 14 | def test_xxh3_64(self): 15 | xxhash.xxh3_64 16 | assert "xxh3_64" in xxhash.algorithms_available 17 | 18 | def test_xxh128(self): 19 | xxhash.xxh128 20 | assert "xxh128" in xxhash.algorithms_available 21 | 22 | def test_xxh3_128(self): 23 | xxhash.xxh3_128 24 | assert "xxh3_128" in xxhash.algorithms_available 25 | 26 | 27 | if __name__ == '__main__': 28 | unittest.main() 29 | -------------------------------------------------------------------------------- /tests/test_name.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | import random 4 | import xxhash 5 | 6 | 7 | class TestName(unittest.TestCase): 8 | def test_xxh32(self): 9 | self.assertEqual(xxhash.xxh32().name, "XXH32") 10 | 11 | def test_xxh64(self): 12 | self.assertEqual(xxhash.xxh64().name, "XXH64") 13 | 14 | def test_xxh3_64(self): 15 | self.assertEqual(xxhash.xxh3_64().name, "XXH3_64") 16 | 17 | def test_xxh128(self): 18 | self.assertEqual(xxhash.xxh128().name, "XXH3_128") 19 | 20 | def test_xxh3_128(self): 21 | self.assertEqual(xxhash.xxh3_128().name, "XXH3_128") 22 | 23 | if __name__ == '__main__': 24 | unittest.main() 25 | -------------------------------------------------------------------------------- /tests/test_xxh32.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | import random 4 | import xxhash 5 | 6 | 7 | class TestXXH(unittest.TestCase): 8 | def test_xxh32(self): 9 | self.assertEqual(xxhash.xxh32('a').intdigest(), 1426945110) 10 | self.assertEqual(xxhash.xxh32('a', 0).intdigest(), 1426945110) 11 | self.assertEqual(xxhash.xxh32('a', 1).intdigest(), 4111757423) 12 | self.assertEqual(xxhash.xxh32('a', 2**32-1).intdigest(), 3443684653) 13 | 14 | def test_xxh32_intdigest(self): 15 | self.assertEqual(xxhash.xxh32_intdigest('a'), 1426945110) 16 | self.assertEqual(xxhash.xxh32_intdigest('a', 0), 1426945110) 17 | self.assertEqual(xxhash.xxh32_intdigest('a', 1), 4111757423) 18 | self.assertEqual(xxhash.xxh32_intdigest('a', 2**32-1), 3443684653) 19 | 20 | def test_xxh32_update(self): 21 | x = xxhash.xxh32() 22 | x.update('a') 23 | self.assertEqual(xxhash.xxh32('a').digest(), x.digest()) 24 | self.assertEqual(xxhash.xxh32_digest('a'), x.digest()) 25 | x.update('b') 26 | self.assertEqual(xxhash.xxh32('ab').digest(), x.digest()) 27 | self.assertEqual(xxhash.xxh32_digest('ab'), x.digest()) 28 | x.update('c') 29 | self.assertEqual(xxhash.xxh32('abc').digest(), x.digest()) 30 | self.assertEqual(xxhash.xxh32_digest('abc'), x.digest()) 31 | 32 | seed = random.randint(0, 2**32) 33 | x = xxhash.xxh32(seed=seed) 34 | x.update('a') 35 | self.assertEqual(xxhash.xxh32('a', seed).digest(), x.digest()) 36 | self.assertEqual(xxhash.xxh32_digest('a', seed), x.digest()) 37 | x.update('b') 38 | self.assertEqual(xxhash.xxh32('ab', seed).digest(), x.digest()) 39 | self.assertEqual(xxhash.xxh32_digest('ab', seed), x.digest()) 40 | x.update('c') 41 | self.assertEqual(xxhash.xxh32('abc', seed).digest(), x.digest()) 42 | self.assertEqual(xxhash.xxh32_digest('abc', seed), x.digest()) 43 | 44 | def test_xxh32_reset(self): 45 | x = xxhash.xxh32() 46 | h = x.intdigest() 47 | 48 | for i in range(10, 50): 49 | x.update(os.urandom(i)) 50 | 51 | x.reset() 52 | 53 | self.assertEqual(h, x.intdigest()) 54 | 55 | def test_xxh32_copy(self): 56 | a = xxhash.xxh32() 57 | a.update('xxhash') 58 | 59 | b = a.copy() 60 | self.assertEqual(a.digest(), b.digest()) 61 | self.assertEqual(a.intdigest(), b.intdigest()) 62 | self.assertEqual(a.hexdigest(), b.hexdigest()) 63 | 64 | b.update('xxhash') 65 | self.assertNotEqual(a.digest(), b.digest()) 66 | self.assertNotEqual(a.intdigest(), b.intdigest()) 67 | self.assertNotEqual(a.hexdigest(), b.hexdigest()) 68 | 69 | a.update('xxhash') 70 | self.assertEqual(a.digest(), b.digest()) 71 | self.assertEqual(a.intdigest(), b.intdigest()) 72 | self.assertEqual(a.hexdigest(), b.hexdigest()) 73 | 74 | def test_xxh32_overflow(self): 75 | s = 'I want an unsigned 32-bit seed!' 76 | a = xxhash.xxh32(s, seed=0) 77 | b = xxhash.xxh32(s, seed=2**32) 78 | self.assertEqual(a.seed, b.seed) 79 | self.assertEqual(a.intdigest(), b.intdigest()) 80 | self.assertEqual(a.hexdigest(), b.hexdigest()) 81 | self.assertEqual(a.digest(), b.digest()) 82 | self.assertEqual(a.intdigest(), xxhash.xxh32_intdigest(s, seed=0)) 83 | self.assertEqual(a.intdigest(), xxhash.xxh32_intdigest(s, seed=2**32)) 84 | self.assertEqual(a.digest(), xxhash.xxh32_digest(s, seed=0)) 85 | self.assertEqual(a.digest(), xxhash.xxh32_digest(s, seed=2**32)) 86 | self.assertEqual(a.hexdigest(), xxhash.xxh32_hexdigest(s, seed=0)) 87 | self.assertEqual(a.hexdigest(), xxhash.xxh32_hexdigest(s, seed=2**32)) 88 | 89 | 90 | a = xxhash.xxh32(s, seed=1) 91 | b = xxhash.xxh32(s, seed=2**32+1) 92 | self.assertEqual(a.seed, b.seed) 93 | self.assertEqual(a.intdigest(), b.intdigest()) 94 | self.assertEqual(a.hexdigest(), b.hexdigest()) 95 | self.assertEqual(a.digest(), b.digest()) 96 | self.assertEqual(a.intdigest(), xxhash.xxh32_intdigest(s, seed=1)) 97 | self.assertEqual(a.intdigest(), xxhash.xxh32_intdigest(s, seed=2**32+1)) 98 | self.assertEqual(a.digest(), xxhash.xxh32_digest(s, seed=1)) 99 | self.assertEqual(a.digest(), xxhash.xxh32_digest(s, seed=2**32+1)) 100 | self.assertEqual(a.hexdigest(), xxhash.xxh32_hexdigest(s, seed=1)) 101 | self.assertEqual(a.hexdigest(), xxhash.xxh32_hexdigest(s, seed=2**32+1)) 102 | 103 | a = xxhash.xxh32(s, seed=2**33-1) 104 | b = xxhash.xxh32(s, seed=2**34-1) 105 | self.assertEqual(a.seed, b.seed) 106 | self.assertEqual(a.intdigest(), b.intdigest()) 107 | self.assertEqual(a.hexdigest(), b.hexdigest()) 108 | self.assertEqual(a.digest(), b.digest()) 109 | self.assertEqual(a.intdigest(), xxhash.xxh32_intdigest(s, seed=2**33-1)) 110 | self.assertEqual(a.intdigest(), xxhash.xxh32_intdigest(s, seed=2**34-1)) 111 | self.assertEqual(a.digest(), xxhash.xxh32_digest(s, seed=2**33-1)) 112 | self.assertEqual(a.digest(), xxhash.xxh32_digest(s, seed=2**34-1)) 113 | self.assertEqual(a.hexdigest(), xxhash.xxh32_hexdigest(s, seed=2**33-1)) 114 | self.assertEqual(a.hexdigest(), xxhash.xxh32_hexdigest(s, seed=2**34-1)) 115 | 116 | a = xxhash.xxh32(s, seed=2**65-1) 117 | b = xxhash.xxh32(s, seed=2**66-1) 118 | self.assertEqual(a.seed, b.seed) 119 | self.assertEqual(a.intdigest(), b.intdigest()) 120 | self.assertEqual(a.hexdigest(), b.hexdigest()) 121 | self.assertEqual(a.digest(), b.digest()) 122 | self.assertEqual(a.intdigest(), xxhash.xxh32_intdigest(s, seed=2**65-1)) 123 | self.assertEqual(a.intdigest(), xxhash.xxh32_intdigest(s, seed=2**66-1)) 124 | self.assertEqual(a.digest(), xxhash.xxh32_digest(s, seed=2**65-1)) 125 | self.assertEqual(a.digest(), xxhash.xxh32_digest(s, seed=2**66-1)) 126 | self.assertEqual(a.hexdigest(), xxhash.xxh32_hexdigest(s, seed=2**65-1)) 127 | self.assertEqual(a.hexdigest(), xxhash.xxh32_hexdigest(s, seed=2**66-1)) 128 | 129 | if __name__ == '__main__': 130 | unittest.main() 131 | -------------------------------------------------------------------------------- /tests/test_xxh3_128.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | import random 4 | import xxhash 5 | 6 | 7 | class TestXXH(unittest.TestCase): 8 | def test_xxh3_128(self): 9 | self.assertEqual(xxhash.xxh3_128('a').intdigest(), 225219434562328483135862406050043285023) 10 | self.assertEqual(xxhash.xxh3_128('a', 0).intdigest(), 225219434562328483135862406050043285023) 11 | self.assertEqual(xxhash.xxh3_128('a', 1).intdigest(), 337425133163118381928709500770786453280) 12 | self.assertEqual(xxhash.xxh3_128('a', 2**64-1).intdigest(), 198297796855923085494266857744987477846) 13 | 14 | def test_xxh3_128_intdigest(self): 15 | self.assertEqual(xxhash.xxh3_128_intdigest('a'), 225219434562328483135862406050043285023) 16 | self.assertEqual(xxhash.xxh3_128_intdigest('a', 0), 225219434562328483135862406050043285023) 17 | self.assertEqual(xxhash.xxh3_128_intdigest('a', 1), 337425133163118381928709500770786453280) 18 | self.assertEqual(xxhash.xxh3_128_intdigest('a', 2**64-1), 198297796855923085494266857744987477846) 19 | 20 | def test_xxh3_128_update(self): 21 | x = xxhash.xxh3_128() 22 | x.update('a') 23 | self.assertEqual(xxhash.xxh3_128('a').digest(), x.digest()) 24 | self.assertEqual(xxhash.xxh3_128_digest('a'), x.digest()) 25 | x.update('b') 26 | self.assertEqual(xxhash.xxh3_128('ab').digest(), x.digest()) 27 | self.assertEqual(xxhash.xxh3_128_digest('ab'), x.digest()) 28 | x.update('c') 29 | self.assertEqual(xxhash.xxh3_128('abc').digest(), x.digest()) 30 | self.assertEqual(xxhash.xxh3_128_digest('abc'), x.digest()) 31 | 32 | seed = random.randint(0, 2**64) 33 | x = xxhash.xxh3_128(seed=seed) 34 | x.update('a') 35 | self.assertEqual(xxhash.xxh3_128('a', seed).digest(), x.digest()) 36 | self.assertEqual(xxhash.xxh3_128_digest('a', seed), x.digest()) 37 | x.update('b') 38 | self.assertEqual(xxhash.xxh3_128('ab', seed).digest(), x.digest()) 39 | self.assertEqual(xxhash.xxh3_128_digest('ab', seed), x.digest()) 40 | x.update('c') 41 | self.assertEqual(xxhash.xxh3_128('abc', seed).digest(), x.digest()) 42 | self.assertEqual(xxhash.xxh3_128_digest('abc', seed), x.digest()) 43 | 44 | def test_xxh3_128_reset(self): 45 | x = xxhash.xxh3_128() 46 | h = x.intdigest() 47 | x.update('x' * 10240) 48 | x.reset() 49 | self.assertEqual(h, x.intdigest()) 50 | 51 | def test_xxh3_128_seed_reset(self): 52 | seed = random.randint(0, 2**64-1) 53 | x = xxhash.xxh3_128(seed=seed) 54 | h = x.intdigest() 55 | x.update('x' * 10240) 56 | x.reset() 57 | self.assertEqual(h, x.intdigest()) 58 | 59 | def test_xxh3_128_reset_more(self): 60 | x = xxhash.xxh3_128() 61 | h = x.intdigest() 62 | 63 | for i in range(random.randint(100, 200)): 64 | x.reset() 65 | 66 | for i in range(10, 1000): 67 | x.update(os.urandom(i)) 68 | x.reset() 69 | 70 | self.assertEqual(h, x.intdigest()) 71 | 72 | for i in range(10, 1000): 73 | x.update(os.urandom(100)) 74 | x.reset() 75 | 76 | self.assertEqual(h, x.intdigest()) 77 | 78 | def test_xxh3_128_seed_reset_more(self): 79 | seed = random.randint(0, 2**64-1) 80 | x = xxhash.xxh3_128(seed=seed) 81 | h = x.intdigest() 82 | 83 | for i in range(random.randint(100, 200)): 84 | x.reset() 85 | 86 | for i in range(10, 1000): 87 | x.update(os.urandom(i)) 88 | x.reset() 89 | 90 | self.assertEqual(h, x.intdigest()) 91 | 92 | for i in range(10, 1000): 93 | x.update(os.urandom(100)) 94 | x.reset() 95 | 96 | self.assertEqual(h, x.intdigest()) 97 | 98 | def test_xxh3_128_copy(self): 99 | a = xxhash.xxh3_128() 100 | a.update('xxhash') 101 | 102 | b = a.copy() 103 | self.assertEqual(a.digest(), b.digest()) 104 | self.assertEqual(a.intdigest(), b.intdigest()) 105 | self.assertEqual(a.hexdigest(), b.hexdigest()) 106 | 107 | b.update('xxhash') 108 | self.assertNotEqual(a.digest(), b.digest()) 109 | self.assertNotEqual(a.intdigest(), b.intdigest()) 110 | self.assertNotEqual(a.hexdigest(), b.hexdigest()) 111 | 112 | a.update('xxhash') 113 | self.assertEqual(a.digest(), b.digest()) 114 | self.assertEqual(a.intdigest(), b.intdigest()) 115 | self.assertEqual(a.hexdigest(), b.hexdigest()) 116 | 117 | def test_xxh3_128_overflow(self): 118 | s = 'I want an unsigned 64-bit seed!' 119 | a = xxhash.xxh3_128(s, seed=0) 120 | b = xxhash.xxh3_128(s, seed=2**64) 121 | self.assertEqual(a.seed, b.seed) 122 | self.assertEqual(a.intdigest(), b.intdigest()) 123 | self.assertEqual(a.hexdigest(), b.hexdigest()) 124 | self.assertEqual(a.digest(), b.digest()) 125 | self.assertEqual(a.intdigest(), xxhash.xxh3_128_intdigest(s, seed=0)) 126 | self.assertEqual(a.intdigest(), xxhash.xxh3_128_intdigest(s, seed=2**64)) 127 | self.assertEqual(a.digest(), xxhash.xxh3_128_digest(s, seed=0)) 128 | self.assertEqual(a.digest(), xxhash.xxh3_128_digest(s, seed=2**64)) 129 | self.assertEqual(a.hexdigest(), xxhash.xxh3_128_hexdigest(s, seed=0)) 130 | self.assertEqual(a.hexdigest(), xxhash.xxh3_128_hexdigest(s, seed=2**64)) 131 | 132 | a = xxhash.xxh3_128(s, seed=1) 133 | b = xxhash.xxh3_128(s, seed=2**64+1) 134 | self.assertEqual(a.seed, b.seed) 135 | self.assertEqual(a.intdigest(), b.intdigest()) 136 | self.assertEqual(a.hexdigest(), b.hexdigest()) 137 | self.assertEqual(a.digest(), b.digest()) 138 | self.assertEqual(a.intdigest(), xxhash.xxh3_128_intdigest(s, seed=1)) 139 | self.assertEqual(a.intdigest(), xxhash.xxh3_128_intdigest(s, seed=2**64+1)) 140 | self.assertEqual(a.digest(), xxhash.xxh3_128_digest(s, seed=1)) 141 | self.assertEqual(a.digest(), xxhash.xxh3_128_digest(s, seed=2**64+1)) 142 | self.assertEqual(a.hexdigest(), xxhash.xxh3_128_hexdigest(s, seed=1)) 143 | self.assertEqual(a.hexdigest(), xxhash.xxh3_128_hexdigest(s, seed=2**64+1)) 144 | 145 | a = xxhash.xxh3_128(s, seed=2**65-1) 146 | b = xxhash.xxh3_128(s, seed=2**66-1) 147 | self.assertEqual(a.seed, b.seed) 148 | self.assertEqual(a.intdigest(), b.intdigest()) 149 | self.assertEqual(a.hexdigest(), b.hexdigest()) 150 | self.assertEqual(a.digest(), b.digest()) 151 | self.assertEqual(a.intdigest(), xxhash.xxh3_128_intdigest(s, seed=2**65-1)) 152 | self.assertEqual(a.intdigest(), xxhash.xxh3_128_intdigest(s, seed=2**66-1)) 153 | self.assertEqual(a.digest(), xxhash.xxh3_128_digest(s, seed=2**65-1)) 154 | self.assertEqual(a.digest(), xxhash.xxh3_128_digest(s, seed=2**66-1)) 155 | self.assertEqual(a.hexdigest(), xxhash.xxh3_128_hexdigest(s, seed=2**65-1)) 156 | self.assertEqual(a.hexdigest(), xxhash.xxh3_128_hexdigest(s, seed=2**66-1)) 157 | 158 | 159 | 160 | if __name__ == '__main__': 161 | unittest.main() 162 | -------------------------------------------------------------------------------- /tests/test_xxh3_64.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import os 3 | import sys 4 | import unittest 5 | import random 6 | import xxhash 7 | 8 | 9 | class TestXXH(unittest.TestCase): 10 | def test_xxh3_64(self): 11 | self.assertEqual(xxhash.xxh3_64('a').intdigest(), 16629034431890738719) 12 | self.assertEqual(xxhash.xxh3_64('a', 0).intdigest(), 16629034431890738719) 13 | self.assertEqual(xxhash.xxh3_64('a', 1).intdigest(), 15201566949650179872) 14 | self.assertEqual(xxhash.xxh3_64('a', 2**64-1).intdigest(), 4875116479388997462) 15 | 16 | def test_xxh3_64_intdigest(self): 17 | self.assertEqual(xxhash.xxh3_64_intdigest('a'), 16629034431890738719) 18 | self.assertEqual(xxhash.xxh3_64_intdigest('a', 0), 16629034431890738719) 19 | self.assertEqual(xxhash.xxh3_64_intdigest('a', 1), 15201566949650179872) 20 | self.assertEqual(xxhash.xxh3_64_intdigest('a', 2**64-1), 4875116479388997462) 21 | 22 | def test_xxh3_64_update(self): 23 | x = xxhash.xxh3_64() 24 | x.update('a') 25 | self.assertEqual(xxhash.xxh3_64('a').digest(), x.digest()) 26 | self.assertEqual(xxhash.xxh3_64_digest('a'), x.digest()) 27 | x.update('b') 28 | self.assertEqual(xxhash.xxh3_64('ab').digest(), x.digest()) 29 | self.assertEqual(xxhash.xxh3_64_digest('ab'), x.digest()) 30 | x.update('c') 31 | self.assertEqual(xxhash.xxh3_64('abc').digest(), x.digest()) 32 | self.assertEqual(xxhash.xxh3_64_digest('abc'), x.digest()) 33 | 34 | seed = random.randint(0, 2**64) 35 | x = xxhash.xxh3_64(seed=seed) 36 | x.update('a') 37 | self.assertEqual(xxhash.xxh3_64('a', seed).digest(), x.digest()) 38 | self.assertEqual(xxhash.xxh3_64_digest('a', seed), x.digest()) 39 | x.update('b') 40 | self.assertEqual(xxhash.xxh3_64('ab', seed).digest(), x.digest()) 41 | self.assertEqual(xxhash.xxh3_64_digest('ab', seed), x.digest()) 42 | x.update('c') 43 | self.assertEqual(xxhash.xxh3_64('abc', seed).digest(), x.digest()) 44 | self.assertEqual(xxhash.xxh3_64_digest('abc', seed), x.digest()) 45 | 46 | def test_xxh3_64_reset(self): 47 | x = xxhash.xxh3_64() 48 | h = x.intdigest() 49 | 50 | x.update('x' * 10240) 51 | x.reset() 52 | 53 | self.assertEqual(h, x.intdigest()) 54 | 55 | def test_xxh3_64_seed_reset(self): 56 | seed = random.randint(0, 2**64-1) 57 | x = xxhash.xxh3_64(seed=seed) 58 | h = x.intdigest() 59 | x.update('x' * 10240) 60 | x.reset() 61 | self.assertEqual(h, x.intdigest()) 62 | 63 | def test_xxh3_64_reset_more(self): 64 | x = xxhash.xxh3_64() 65 | h = x.intdigest() 66 | 67 | for i in range(random.randint(100, 200)): 68 | x.reset() 69 | 70 | self.assertEqual(h, x.intdigest()) 71 | 72 | for i in range(10, 1000): 73 | x.update(os.urandom(i)) 74 | x.reset() 75 | 76 | self.assertEqual(h, x.intdigest()) 77 | 78 | for i in range(10, 1000): 79 | x.update(os.urandom(100)) 80 | x.reset() 81 | 82 | self.assertEqual(h, x.intdigest()) 83 | 84 | def test_xxh3_64_seed_reset_more(self): 85 | seed = random.randint(0, 2**64-1) 86 | x = xxhash.xxh3_64(seed=seed) 87 | h = x.intdigest() 88 | 89 | for i in range(random.randint(100, 200)): 90 | x.reset() 91 | 92 | self.assertEqual(h, x.intdigest()) 93 | 94 | for i in range(10, 1000): 95 | x.update(os.urandom(i)) 96 | x.reset() 97 | 98 | self.assertEqual(h, x.intdigest()) 99 | 100 | for i in range(10, 1000): 101 | x.update(os.urandom(100)) 102 | x.reset() 103 | 104 | self.assertEqual(h, x.intdigest()) 105 | 106 | def test_xxh3_64_copy(self): 107 | a = xxhash.xxh3_64() 108 | a.update('xxhash') 109 | 110 | b = a.copy() 111 | self.assertEqual(a.digest(), b.digest()) 112 | self.assertEqual(a.intdigest(), b.intdigest()) 113 | self.assertEqual(a.hexdigest(), b.hexdigest()) 114 | 115 | b.update('xxhash') 116 | self.assertNotEqual(a.digest(), b.digest()) 117 | self.assertNotEqual(a.intdigest(), b.intdigest()) 118 | self.assertNotEqual(a.hexdigest(), b.hexdigest()) 119 | 120 | a.update('xxhash') 121 | self.assertEqual(a.digest(), b.digest()) 122 | self.assertEqual(a.intdigest(), b.intdigest()) 123 | self.assertEqual(a.hexdigest(), b.hexdigest()) 124 | 125 | def test_xxh3_64_overflow(self): 126 | s = 'I want an unsigned 64-bit seed!' 127 | a = xxhash.xxh3_64(s, seed=0) 128 | b = xxhash.xxh3_64(s, seed=2**64) 129 | self.assertEqual(a.seed, b.seed) 130 | self.assertEqual(a.intdigest(), b.intdigest()) 131 | self.assertEqual(a.hexdigest(), b.hexdigest()) 132 | self.assertEqual(a.digest(), b.digest()) 133 | self.assertEqual(a.intdigest(), xxhash.xxh3_64_intdigest(s, seed=0)) 134 | self.assertEqual(a.intdigest(), xxhash.xxh3_64_intdigest(s, seed=2**64)) 135 | self.assertEqual(a.digest(), xxhash.xxh3_64_digest(s, seed=0)) 136 | self.assertEqual(a.digest(), xxhash.xxh3_64_digest(s, seed=2**64)) 137 | self.assertEqual(a.hexdigest(), xxhash.xxh3_64_hexdigest(s, seed=0)) 138 | self.assertEqual(a.hexdigest(), xxhash.xxh3_64_hexdigest(s, seed=2**64)) 139 | 140 | a = xxhash.xxh3_64(s, seed=1) 141 | b = xxhash.xxh3_64(s, seed=2**64+1) 142 | self.assertEqual(a.seed, b.seed) 143 | self.assertEqual(a.intdigest(), b.intdigest()) 144 | self.assertEqual(a.hexdigest(), b.hexdigest()) 145 | self.assertEqual(a.digest(), b.digest()) 146 | self.assertEqual(a.intdigest(), xxhash.xxh3_64_intdigest(s, seed=1)) 147 | self.assertEqual(a.intdigest(), xxhash.xxh3_64_intdigest(s, seed=2**64+1)) 148 | self.assertEqual(a.digest(), xxhash.xxh3_64_digest(s, seed=1)) 149 | self.assertEqual(a.digest(), xxhash.xxh3_64_digest(s, seed=2**64+1)) 150 | self.assertEqual(a.hexdigest(), xxhash.xxh3_64_hexdigest(s, seed=1)) 151 | self.assertEqual(a.hexdigest(), xxhash.xxh3_64_hexdigest(s, seed=2**64+1)) 152 | 153 | a = xxhash.xxh3_64(s, seed=2**65-1) 154 | b = xxhash.xxh3_64(s, seed=2**66-1) 155 | self.assertEqual(a.seed, b.seed) 156 | self.assertEqual(a.intdigest(), b.intdigest()) 157 | self.assertEqual(a.hexdigest(), b.hexdigest()) 158 | self.assertEqual(a.digest(), b.digest()) 159 | self.assertEqual(a.intdigest(), xxhash.xxh3_64_intdigest(s, seed=2**65-1)) 160 | self.assertEqual(a.intdigest(), xxhash.xxh3_64_intdigest(s, seed=2**66-1)) 161 | self.assertEqual(a.digest(), xxhash.xxh3_64_digest(s, seed=2**65-1)) 162 | self.assertEqual(a.digest(), xxhash.xxh3_64_digest(s, seed=2**66-1)) 163 | self.assertEqual(a.hexdigest(), xxhash.xxh3_64_hexdigest(s, seed=2**65-1)) 164 | self.assertEqual(a.hexdigest(), xxhash.xxh3_64_hexdigest(s, seed=2**66-1)) 165 | 166 | 167 | if __name__ == '__main__': 168 | unittest.main() 169 | -------------------------------------------------------------------------------- /tests/test_xxh64.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | import random 4 | import xxhash 5 | 6 | class TestXXH(unittest.TestCase): 7 | def test_xxh64(self): 8 | self.assertEqual(xxhash.xxh64('a').intdigest(), 15154266338359012955) 9 | self.assertEqual(xxhash.xxh64('a', 0).intdigest(), 15154266338359012955) 10 | self.assertEqual(xxhash.xxh64('a', 1).intdigest(), 16051599287423682246) 11 | self.assertEqual(xxhash.xxh64('a', 2**64-1).intdigest(), 6972758980737027682) 12 | 13 | def test_xxh64_intdigest(self): 14 | self.assertEqual(xxhash.xxh64_intdigest('a'), 15154266338359012955) 15 | self.assertEqual(xxhash.xxh64_intdigest('a', 0), 15154266338359012955) 16 | self.assertEqual(xxhash.xxh64_intdigest('a', 1), 16051599287423682246) 17 | self.assertEqual(xxhash.xxh64_intdigest('a', 2**64-1), 6972758980737027682) 18 | 19 | def test_xxh64_update(self): 20 | x = xxhash.xxh64() 21 | x.update('a') 22 | self.assertEqual(xxhash.xxh64('a').digest(), x.digest()) 23 | self.assertEqual(xxhash.xxh64_digest('a'), x.digest()) 24 | x.update('b') 25 | self.assertEqual(xxhash.xxh64('ab').digest(), x.digest()) 26 | self.assertEqual(xxhash.xxh64_digest('ab'), x.digest()) 27 | x.update('c') 28 | self.assertEqual(xxhash.xxh64('abc').digest(), x.digest()) 29 | self.assertEqual(xxhash.xxh64_digest('abc'), x.digest()) 30 | 31 | seed = random.randint(0, 2**64) 32 | x = xxhash.xxh64(seed=seed) 33 | x.update('a') 34 | self.assertEqual(xxhash.xxh64('a', seed).digest(), x.digest()) 35 | self.assertEqual(xxhash.xxh64_digest('a', seed), x.digest()) 36 | x.update('b') 37 | self.assertEqual(xxhash.xxh64('ab', seed).digest(), x.digest()) 38 | self.assertEqual(xxhash.xxh64_digest('ab', seed), x.digest()) 39 | x.update('c') 40 | self.assertEqual(xxhash.xxh64('abc', seed).digest(), x.digest()) 41 | self.assertEqual(xxhash.xxh64_digest('abc', seed), x.digest()) 42 | 43 | def test_xxh64_reset(self): 44 | x = xxhash.xxh64() 45 | h = x.intdigest() 46 | 47 | for i in range(10, 50): 48 | x.update(os.urandom(i)) 49 | 50 | x.reset() 51 | 52 | self.assertEqual(h, x.intdigest()) 53 | 54 | def test_xxh64_copy(self): 55 | a = xxhash.xxh64() 56 | a.update('xxhash') 57 | 58 | b = a.copy() 59 | self.assertEqual(a.digest(), b.digest()) 60 | self.assertEqual(a.intdigest(), b.intdigest()) 61 | self.assertEqual(a.hexdigest(), b.hexdigest()) 62 | 63 | b.update('xxhash') 64 | self.assertNotEqual(a.digest(), b.digest()) 65 | self.assertNotEqual(a.intdigest(), b.intdigest()) 66 | self.assertNotEqual(a.hexdigest(), b.hexdigest()) 67 | 68 | a.update('xxhash') 69 | self.assertEqual(a.digest(), b.digest()) 70 | self.assertEqual(a.intdigest(), b.intdigest()) 71 | self.assertEqual(a.hexdigest(), b.hexdigest()) 72 | 73 | def test_xxh64_overflow(self): 74 | s = 'I want an unsigned 64-bit seed!' 75 | a = xxhash.xxh64(s, seed=0) 76 | b = xxhash.xxh64(s, seed=2**64) 77 | self.assertEqual(a.seed, b.seed) 78 | self.assertEqual(a.intdigest(), b.intdigest()) 79 | self.assertEqual(a.hexdigest(), b.hexdigest()) 80 | self.assertEqual(a.digest(), b.digest()) 81 | self.assertEqual(a.intdigest(), xxhash.xxh64_intdigest(s, seed=0)) 82 | self.assertEqual(a.intdigest(), xxhash.xxh64_intdigest(s, seed=2**64)) 83 | self.assertEqual(a.digest(), xxhash.xxh64_digest(s, seed=0)) 84 | self.assertEqual(a.digest(), xxhash.xxh64_digest(s, seed=2**64)) 85 | self.assertEqual(a.hexdigest(), xxhash.xxh64_hexdigest(s, seed=0)) 86 | self.assertEqual(a.hexdigest(), xxhash.xxh64_hexdigest(s, seed=2**64)) 87 | 88 | a = xxhash.xxh64(s, seed=1) 89 | b = xxhash.xxh64(s, seed=2**64+1) 90 | self.assertEqual(a.seed, b.seed) 91 | self.assertEqual(a.intdigest(), b.intdigest()) 92 | self.assertEqual(a.hexdigest(), b.hexdigest()) 93 | self.assertEqual(a.digest(), b.digest()) 94 | self.assertEqual(a.intdigest(), xxhash.xxh64_intdigest(s, seed=1)) 95 | self.assertEqual(a.intdigest(), xxhash.xxh64_intdigest(s, seed=2**64+1)) 96 | self.assertEqual(a.digest(), xxhash.xxh64_digest(s, seed=1)) 97 | self.assertEqual(a.digest(), xxhash.xxh64_digest(s, seed=2**64+1)) 98 | self.assertEqual(a.hexdigest(), xxhash.xxh64_hexdigest(s, seed=1)) 99 | self.assertEqual(a.hexdigest(), xxhash.xxh64_hexdigest(s, seed=2**64+1)) 100 | 101 | a = xxhash.xxh64(s, seed=2**65-1) 102 | b = xxhash.xxh64(s, seed=2**66-1) 103 | self.assertEqual(a.seed, b.seed) 104 | self.assertEqual(a.intdigest(), b.intdigest()) 105 | self.assertEqual(a.hexdigest(), b.hexdigest()) 106 | self.assertEqual(a.digest(), b.digest()) 107 | self.assertEqual(a.intdigest(), xxhash.xxh64_intdigest(s, seed=2**65-1)) 108 | self.assertEqual(a.intdigest(), xxhash.xxh64_intdigest(s, seed=2**66-1)) 109 | self.assertEqual(a.digest(), xxhash.xxh64_digest(s, seed=2**65-1)) 110 | self.assertEqual(a.digest(), xxhash.xxh64_digest(s, seed=2**66-1)) 111 | self.assertEqual(a.hexdigest(), xxhash.xxh64_hexdigest(s, seed=2**65-1)) 112 | self.assertEqual(a.hexdigest(), xxhash.xxh64_hexdigest(s, seed=2**66-1)) 113 | 114 | 115 | if __name__ == '__main__': 116 | unittest.main() 117 | -------------------------------------------------------------------------------- /typecheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pipenv run python -m mypy tests/* 4 | -------------------------------------------------------------------------------- /xxhash/__init__.py: -------------------------------------------------------------------------------- 1 | from ._xxhash import ( 2 | xxh32, 3 | xxh32_digest, 4 | xxh32_intdigest, 5 | xxh32_hexdigest, 6 | xxh64, 7 | xxh64_digest, 8 | xxh64_intdigest, 9 | xxh64_hexdigest, 10 | xxh3_64, 11 | xxh3_64_digest, 12 | xxh3_64_intdigest, 13 | xxh3_64_hexdigest, 14 | xxh3_128, 15 | xxh3_128_digest, 16 | xxh3_128_intdigest, 17 | xxh3_128_hexdigest, 18 | XXHASH_VERSION, 19 | ) 20 | 21 | from .version import VERSION, VERSION_TUPLE 22 | 23 | 24 | xxh128 = xxh3_128 25 | xxh128_hexdigest = xxh3_128_hexdigest 26 | xxh128_intdigest = xxh3_128_intdigest 27 | xxh128_digest = xxh3_128_digest 28 | 29 | algorithms_available = set([ 30 | "xxh32", 31 | "xxh64", 32 | "xxh3_64", 33 | "xxh128", 34 | "xxh3_128", 35 | ]) 36 | 37 | 38 | __all__ = [ 39 | "xxh32", 40 | "xxh32_digest", 41 | "xxh32_intdigest", 42 | "xxh32_hexdigest", 43 | "xxh64", 44 | "xxh64_digest", 45 | "xxh64_intdigest", 46 | "xxh64_hexdigest", 47 | "xxh3_64", 48 | "xxh3_64_digest", 49 | "xxh3_64_intdigest", 50 | "xxh3_64_hexdigest", 51 | "xxh3_128", 52 | "xxh3_128_digest", 53 | "xxh3_128_intdigest", 54 | "xxh3_128_hexdigest", 55 | "xxh128", 56 | "xxh128_digest", 57 | "xxh128_intdigest", 58 | "xxh128_hexdigest", 59 | "VERSION", 60 | "VERSION_TUPLE", 61 | "XXHASH_VERSION", 62 | "algorithms_available", 63 | ] 64 | -------------------------------------------------------------------------------- /xxhash/__init__.pyi: -------------------------------------------------------------------------------- 1 | import array 2 | from typing import Union 3 | from typing_extensions import final 4 | 5 | _InputType = Union[str, bytes, bytearray, memoryview, array.ArrayType[int]] 6 | 7 | VERSION: str 8 | XXHASH_VERSION: str 9 | VERSION_TUPLE: tuple[int, ...] 10 | 11 | algorithms_available: set[str] 12 | 13 | class _Hasher: 14 | def __init__(self, input: _InputType = ..., seed: int = ...) -> None: ... 15 | def update(self, input: _InputType) -> None: ... 16 | def digest(self) -> bytes: ... 17 | def hexdigest(self) -> str: ... 18 | def intdigest(self) -> int: ... 19 | def copy(self) -> _Hasher: ... 20 | def reset(self) -> None: ... 21 | @property 22 | def digestsize(self) -> int: ... 23 | @property 24 | def digest_size(self) -> int: ... 25 | @property 26 | def block_size(self) -> int: ... 27 | @property 28 | def name(self) -> str: ... 29 | @property 30 | def seed(self) -> int: ... 31 | 32 | @final 33 | class xxh32(_Hasher): ... 34 | 35 | @final 36 | class xxh3_64(_Hasher): ... 37 | 38 | @final 39 | class xxh3_128(_Hasher): ... 40 | 41 | xxh64 = xxh3_64 42 | xxh128 = xxh3_128 43 | 44 | def xxh32_digest(args: _InputType, seed: int = ...) -> bytes: ... 45 | def xxh32_hexdigest(args: _InputType, seed: int = ...) -> str: ... 46 | def xxh32_intdigest(args: _InputType, seed: int = ...) -> int: ... 47 | 48 | def xxh3_64_digest(args: _InputType, seed: int = ...) -> bytes: ... 49 | def xxh3_64_hexdigest(args: _InputType, seed: int = ...) -> str: ... 50 | def xxh3_64_intdigest(args: _InputType, seed: int = ...) -> int: ... 51 | 52 | def xxh3_128_digest(args: _InputType, seed: int = ...) -> bytes: ... 53 | def xxh3_128_hexdigest(args: _InputType, seed: int = ...) -> str: ... 54 | def xxh3_128_intdigest(args: _InputType, seed: int = ...) -> int: ... 55 | 56 | xxh64_digest = xxh3_64_digest 57 | xxh64_hexdigest = xxh3_64_hexdigest 58 | xxh64_intdigest = xxh3_64_intdigest 59 | 60 | xxh128_digest = xxh3_128_digest 61 | xxh128_hexdigest = xxh3_128_hexdigest 62 | xxh128_intdigest = xxh3_128_intdigest 63 | -------------------------------------------------------------------------------- /xxhash/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifduyue/python-xxhash/b3cc1b8bc1c27da0a0ed8bce2c1d92ae86966961/xxhash/py.typed -------------------------------------------------------------------------------- /xxhash/version.py: -------------------------------------------------------------------------------- 1 | VERSION = "3.5.0" 2 | VERSION_TUPLE = (3, 5, 0) 3 | --------------------------------------------------------------------------------