pax_global_header00006660000000000000000000000064147625467450014536gustar00rootroot0000000000000052 comment=33e4ec44e45dcc0d390c39aebabadf6a03fba201 ulid-transform-1.4.0/000077500000000000000000000000001476254674500145065ustar00rootroot00000000000000ulid-transform-1.4.0/.all-contributorsrc000066400000000000000000000004551476254674500203430ustar00rootroot00000000000000{ "projectName": "ulid-transform", "projectOwner": "bluetooth-devices", "repoType": "github", "repoHost": "https://github.com", "files": ["README.md"], "imageSize": 80, "commit": true, "commitConvention": "angular", "contributors": [], "contributorsPerLine": 7, "skipCi": true } ulid-transform-1.4.0/.copier-answers.yml000066400000000000000000000010231476254674500202440ustar00rootroot00000000000000# Changes here will be overwritten by Copier _commit: 5559e3e _src_path: gh:browniebroke/pypackage-template add_me_as_contributor: false copyright_year: '2022' documentation: false email: nick@koston.org full_name: J. Nick Koston github_username: bluetooth-devices initial_commit: true open_source_license: MIT package_name: ulid_transform project_name: Fast ULID transformations project_short_description: Create and transform ULIDs project_slug: ulid-transform run_poetry_install: true setup_github: true setup_pre_commit: true ulid-transform-1.4.0/.editorconfig000066400000000000000000000004441476254674500171650ustar00rootroot00000000000000# http://editorconfig.org root = true [*] indent_style = space indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true charset = utf-8 end_of_line = lf [*.bat] indent_style = tab end_of_line = crlf [LICENSE] insert_final_newline = false [Makefile] indent_style = tab ulid-transform-1.4.0/.flake8000066400000000000000000000000561476254674500156620ustar00rootroot00000000000000[flake8] exclude = docs max-line-length = 120 ulid-transform-1.4.0/.github/000077500000000000000000000000001476254674500160465ustar00rootroot00000000000000ulid-transform-1.4.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001476254674500202315ustar00rootroot00000000000000ulid-transform-1.4.0/.github/ISSUE_TEMPLATE/1-bug_report.md000066400000000000000000000004221476254674500230570ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve labels: bug --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: **Additional context** Add any other context about the problem here. ulid-transform-1.4.0/.github/ISSUE_TEMPLATE/2-feature-request.md000066400000000000000000000006721476254674500240400ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project labels: enhancement --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Additional context** Add any other context or screenshots about the feature request here. ulid-transform-1.4.0/.github/dependabot.yml000066400000000000000000000013441476254674500207000ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" commit-message: prefix: "chore(ci): " groups: github-actions: patterns: - "*" - package-ecosystem: "pip" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "weekly" ulid-transform-1.4.0/.github/workflows/000077500000000000000000000000001476254674500201035ustar00rootroot00000000000000ulid-transform-1.4.0/.github/workflows/ci.yml000066400000000000000000000224641476254674500212310ustar00rootroot00000000000000name: CI on: push: branches: - main pull_request: concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.11" - uses: pre-commit/action@v3.0.1 # Make sure commit messages follow the conventional commits convention: # https://www.conventionalcommits.org commitlint: name: Lint Commit Messages runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: wagoid/commitlint-github-action@v6.2.1 test: strategy: fail-fast: false matrix: python-version: - "3.11" - "3.12" - "3.13" os: - ubuntu-latest - macOS-latest extension: - "skip_cython" - "use_cython" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - uses: snok/install-poetry@v1.4.1 - name: Install Dependencies # the separate `pip install` phase is required because Poetry # appears to hide the output of `pip install` commands (and possibly # doesn't pass all environment variables through to the build) run: | env CYTHON_FORCE_REGEN=1 pip install --no-cache -v -e . poetry install --only=main,dev shell: bash env: REQUIRE_CYTHON: ${{ matrix.extension == 'use_cython' }} SKIP_CYTHON: ${{ matrix.extension == 'skip_cython' }} - name: Test with Pytest run: poetry run pytest -v -Wdefault --cov=ulid_transform --cov-report=term-missing:skip-covered --cov-report=xml tests shell: bash env: REQUIRE_CYTHON: ${{ matrix.extension == 'use_cython' }} - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} test32bit: name: "Test on 32-bit Alpine Linux" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Alpine Linux v3.18 for x86 uses: jirutka/setup-alpine@v1 with: arch: x86 branch: v3.18 - name: Set up Python run: | apk add gcc g++ musl-dev python3-dev py3-pip poetry shell: alpine.sh --root {0} - name: Install Dependencies run: | poetry add Cython REQUIRE_CYTHON=1 poetry -vvv install --only=main,dev shell: alpine.sh --root {0} - name: Test with Pytest run: poetry run pytest -v -Wdefault --cov=ulid_transform --cov-report=term-missing:skip-covered --cov-report=xml tests shell: alpine.sh --root {0} benchmark: strategy: fail-fast: false matrix: python-version: - "3.13" os: - ubuntu-latest runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - uses: snok/install-poetry@v1.4.1 - name: Install Dependencies run: poetry install --only=main,dev,benchmark env: REQUIRE_CYTHON: 1 - name: Run benchmarks run: | poetry run pytest bench --benchmark-autosave echo '# Benchmark Results' >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY poetry run pytest-benchmark compare --columns=mean,ops >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY shell: bash codspeed_benchmark: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Python 3.13 uses: actions/setup-python@v5 with: python-version: 3.13 - uses: snok/install-poetry@v1.4.1 - name: Install Dependencies run: | REQUIRE_CYTHON=1 poetry install --only=main,dev shell: bash - name: Run benchmarks uses: CodSpeedHQ/action@v3 with: token: ${{ secrets.CODSPEED_TOKEN }} run: poetry run pytest --no-cov -vvvvv --codspeed tests/benchmarks release: needs: - test - lint - test32bit - commitlint runs-on: ubuntu-latest environment: release concurrency: release permissions: id-token: write contents: write outputs: released: ${{ steps.release.outputs.released }} newest_release_tag: ${{ steps.release.outputs.tag }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.head_ref || github.ref_name }} # Do a dry run of PSR - name: Test release uses: python-semantic-release/python-semantic-release@v9.21.0 if: github.ref_name != 'main' with: root_options: --noop # On main branch: actual PSR + upload to PyPI & GitHub - name: Release uses: python-semantic-release/python-semantic-release@v9.21.0 id: release if: github.ref_name == 'main' with: github_token: ${{ secrets.GITHUB_TOKEN }} - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 if: steps.release.outputs.released == 'true' - name: Publish package distributions to GitHub Releases uses: python-semantic-release/upload-to-gh-release@main if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} build_wheels: needs: [release] if: needs.release.outputs.released == 'true' name: Wheels for ${{ matrix.os }} (${{ matrix.musl == 'musllinux' && 'musllinux' || 'manylinux' }}) ${{ matrix.qemu }} ${{ matrix.pyver }} runs-on: ${{ matrix.os }} strategy: matrix: os: [ windows-latest, ubuntu-24.04-arm, ubuntu-latest, macos-13, macos-latest, ] qemu: [""] musl: [""] pyver: [""] include: - os: ubuntu-latest musl: "musllinux" - os: ubuntu-24.04-arm musl: "musllinux" # qemu is slow, make a single # runner per Python version - os: ubuntu-latest qemu: armv7l musl: "musllinux" pyver: cp311 - os: ubuntu-latest qemu: armv7l musl: "musllinux" pyver: cp312 - os: ubuntu-latest qemu: armv7l musl: "musllinux" pyver: cp313 # qemu is slow, make a single # runner per Python version - os: ubuntu-latest qemu: armv7l musl: "" pyver: cp311 - os: ubuntu-latest qemu: armv7l musl: "" pyver: cp312 - os: ubuntu-latest qemu: armv7l musl: "" pyver: cp313 steps: - uses: actions/checkout@v4 with: ref: ${{ needs.release.outputs.newest_release_tag }} fetch-depth: 0 # Used to host cibuildwheel - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: Set up QEMU if: ${{ matrix.qemu }} uses: docker/setup-qemu-action@v3 with: platforms: all # This should be temporary # xref https://github.com/docker/setup-qemu-action/issues/188 # xref https://github.com/tonistiigi/binfmt/issues/215 image: tonistiigi/binfmt:qemu-v8.1.5 id: qemu - name: Prepare emulation if: ${{ matrix.qemu }} run: | if [[ -n "${{ matrix.qemu }}" ]]; then # Build emulated architectures only if QEMU is set, # use default "auto" otherwise echo "CIBW_ARCHS_LINUX=${{ matrix.qemu }}" >> $GITHUB_ENV fi - name: Limit to a specific Python version on slow QEMU if: ${{ matrix.pyver }} run: | if [[ -n "${{ matrix.pyver }}" ]]; then echo "CIBW_BUILD=${{ matrix.pyver }}*" >> $GITHUB_ENV fi - name: Build wheels uses: pypa/cibuildwheel@v2.23.0 env: CIBW_SKIP: cp36-* cp37-* cp38-* cp39-* cp310-* pp* ${{ matrix.musl == 'musllinux' && '*manylinux*' || '*musllinux*' }} REQUIRE_CYTHON: 1 - uses: actions/upload-artifact@v4 with: name: wheels-${{ matrix.os }}-${{ matrix.musl }}-${{ matrix.pyver }}-${{ matrix.qemu }} path: ./wheelhouse/*.whl upload_pypi: needs: [build_wheels] runs-on: ubuntu-latest environment: release permissions: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: - uses: actions/download-artifact@v4 with: # unpacks default artifact into dist/ # if `name: artifact` is omitted, the action will create extra parent dir path: dist pattern: wheels-* merge-multiple: true - uses: pypa/gh-action-pypi-publish@v1.12.4 # To test: repository_url: https://test.pypi.org/legacy/ ulid-transform-1.4.0/.github/workflows/hacktoberfest.yml000066400000000000000000000005341476254674500234540ustar00rootroot00000000000000name: Hacktoberfest on: schedule: # Run every day in October - cron: "0 0 * 10 *" # Run on the 1st of November to revert - cron: "0 13 1 11 *" jobs: hacktoberfest: runs-on: ubuntu-latest steps: - uses: browniebroke/hacktoberfest-labeler-action@v2.3.0 with: github_token: ${{ secrets.GH_PAT }} ulid-transform-1.4.0/.github/workflows/issue-manager.yml000066400000000000000000000013401476254674500233640ustar00rootroot00000000000000name: Issue Manager on: schedule: - cron: "0 0 * * *" issue_comment: types: - created issues: types: - labeled pull_request_target: types: - labeled workflow_dispatch: jobs: issue-manager: runs-on: ubuntu-latest steps: - uses: tiangolo/issue-manager@0.5.1 with: token: ${{ secrets.GITHUB_TOKEN }} config: > { "answered": { "message": "Assuming the original issue was solved, it will be automatically closed now." }, "waiting": { "message": "Automatically closing. To re-open, please provide the additional information requested." } } ulid-transform-1.4.0/.github/workflows/labels.yml000066400000000000000000000007741476254674500221000ustar00rootroot00000000000000name: Sync Github labels on: push: branches: - main paths: - ".github/**" jobs: labels: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: 3.8 - name: Install labels run: pip install labels - name: Sync config with Github run: labels -u ${{ github.repository_owner }} -t ${{ secrets.GITHUB_TOKEN }} sync -f .github/labels.toml ulid-transform-1.4.0/.gitignore000066400000000000000000000042551476254674500165040ustar00rootroot00000000000000# Created by .ignore support plugin (hsz.mobi) ### Python template # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder {{package_name}} settings .spyderproject .spyproject # Rope {{package_name}} settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ # Generated Cython files src/ulid_transform/*.cpp src/ulid_transform/*.html # PyCharm files .idea/ ulid-transform-1.4.0/.gitpod.yml000066400000000000000000000003061476254674500165740ustar00rootroot00000000000000tasks: - command: | pip install poetry PIP_USER=false poetry install - command: | pip install pre-commit pre-commit install PIP_USER=false pre-commit install-hooks ulid-transform-1.4.0/.pre-commit-config.yaml000066400000000000000000000042611476254674500207720ustar00rootroot00000000000000# See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks exclude: "CHANGELOG.md|.copier-answers.yml" default_stages: [pre-commit] ci: autofix_commit_msg: "chore(pre-commit.ci): auto fixes" autoupdate_commit_msg: "chore(pre-commit.ci): pre-commit autoupdate" repos: - repo: https://github.com/commitizen-tools/commitizen rev: v4.4.1 hooks: - id: commitizen stages: [commit-msg] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: debug-statements - id: check-builtin-literals - id: check-case-conflict - id: check-docstring-first - id: check-json - id: check-toml - id: check-xml - id: check-yaml - id: detect-private-key - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/python-poetry/poetry rev: 2.1.1 hooks: - id: poetry-check - repo: https://github.com/pre-commit/mirrors-prettier rev: v4.0.0-alpha.8 hooks: - id: prettier args: ["--tab-width", "2"] additional_dependencies: - prettier@3.5.1 - repo: https://github.com/asottile/pyupgrade rev: v3.19.1 hooks: - id: pyupgrade args: [--py39-plus] - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.9.9 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - id: ruff-format - repo: https://github.com/codespell-project/codespell rev: v2.4.1 hooks: - id: codespell args: - --ignore-words-list=ans - repo: https://github.com/PyCQA/flake8 rev: 7.1.2 hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.15.0 hooks: - id: mypy additional_dependencies: [] exclude: "src/ulid_transform/__init__.pyi" - repo: https://github.com/PyCQA/bandit rev: 1.8.3 hooks: - id: bandit args: [-x, "tests,bench"] - repo: https://github.com/pre-commit/mirrors-clang-format rev: v19.1.7 hooks: - id: clang-format types_or: - "c++" - "c" - "cuda" args: [-style=Webkit, -i] ulid-transform-1.4.0/CHANGELOG.md000066400000000000000000000663041476254674500163300ustar00rootroot00000000000000# CHANGELOG ## v1.4.0 (2025-03-07) ### Chores - Enable clang-format ([#91](https://github.com/Bluetooth-Devices/ulid-transform/pull/91), [`655f639`](https://github.com/Bluetooth-Devices/ulid-transform/commit/655f63973c5b3f4bcb1bdb42f7220cffc03a713c)) ### Features - Speed up random data and improve randomness distribution ([#92](https://github.com/Bluetooth-Devices/ulid-transform/pull/92), [`dfb3a3a`](https://github.com/Bluetooth-Devices/ulid-transform/commit/dfb3a3a39132c2395bf2a911304fa8555ebde9c3)) ## v1.3.0 (2025-03-05) ### Chores - Pin prettier to 3.5.1 to fix CI ([#83](https://github.com/Bluetooth-Devices/ulid-transform/pull/83), [`d9b4fc1`](https://github.com/Bluetooth-Devices/ulid-transform/commit/d9b4fc11a5f23ee00e01778726e54cedf368820c)) - Remove unused labels workflow ([#82](https://github.com/Bluetooth-Devices/ulid-transform/pull/82), [`98f2b08`](https://github.com/Bluetooth-Devices/ulid-transform/commit/98f2b08803abf08b69f1e5f5bc25cc8059721504)) - Update wheel build workflow to include armv7l ([#89](https://github.com/Bluetooth-Devices/ulid-transform/pull/89), [`3132a42`](https://github.com/Bluetooth-Devices/ulid-transform/commit/3132a4213e135f60b1552c90daf18be9fd2adc29)) - **ci**: Bump python-semantic-release/python-semantic-release from 9.20.0 to 9.21.0 in the github-actions group ([#85](https://github.com/Bluetooth-Devices/ulid-transform/pull/85), [`332ae0d`](https://github.com/Bluetooth-Devices/ulid-transform/commit/332ae0d5b9ed792816fc5d9ebeb8c9c9d8e08535)) - **ci**: Bump the github-actions group with 7 updates ([#81](https://github.com/Bluetooth-Devices/ulid-transform/pull/81), [`6130f2c`](https://github.com/Bluetooth-Devices/ulid-transform/commit/6130f2ca64dcabec68f5434f0605e47a60695538)) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston - **deps-dev**: Bump pytest from 8.3.4 to 8.3.5 ([#88](https://github.com/Bluetooth-Devices/ulid-transform/pull/88), [`9cef5f7`](https://github.com/Bluetooth-Devices/ulid-transform/commit/9cef5f7968ade50883e55a3ece159ec73e6c6de7)) Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.4 to 8.3.5. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.3.4...8.3.5) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> - **deps-dev**: Bump setuptools from 75.8.0 to 75.8.2 ([#87](https://github.com/Bluetooth-Devices/ulid-transform/pull/87), [`1d38ed2`](https://github.com/Bluetooth-Devices/ulid-transform/commit/1d38ed2817291362109c8d963088ecc4eb2b7228)) Bumps [setuptools](https://github.com/pypa/setuptools) from 75.8.0 to 75.8.2. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/v75.8.0...v75.8.2) --- updated-dependencies: - dependency-name: setuptools dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#84](https://github.com/Bluetooth-Devices/ulid-transform/pull/84), [`6f17818`](https://github.com/Bluetooth-Devices/ulid-transform/commit/6f1781866dcd51d01554f5c6028de5f31bef4386)) - **pre-commit.ci**: Pre-commit autoupdate ([#86](https://github.com/Bluetooth-Devices/ulid-transform/pull/86), [`24d0d69`](https://github.com/Bluetooth-Devices/ulid-transform/commit/24d0d69e2270639823ae67600ba9e88db40c0d4d)) updates: - [github.com/commitizen-tools/commitizen: v4.2.2 → v4.4.1](https://github.com/commitizen-tools/commitizen/compare/v4.2.2...v4.4.1) - [github.com/astral-sh/ruff-pre-commit: v0.9.7 → v0.9.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.7...v0.9.9) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> ### Features - Reduce wheel sizes ([#90](https://github.com/Bluetooth-Devices/ulid-transform/pull/90), [`f2df716`](https://github.com/Bluetooth-Devices/ulid-transform/commit/f2df71640d8a56f9df96104ad78e8e6678821153)) Add -g0 to compile options to reduce wheel sizes ## v1.2.1 (2025-02-22) ### Bug Fixes - Update repo links to use bluetooth-devices ([#80](https://github.com/Bluetooth-Devices/ulid-transform/pull/80), [`3beca28`](https://github.com/Bluetooth-Devices/ulid-transform/commit/3beca289c6f0e728d01461a396ac93de15730d22)) ### Chores - Bump upload/download actions to v4 ([#73](https://github.com/Bluetooth-Devices/ulid-transform/pull/73), [`670b623`](https://github.com/Bluetooth-Devices/ulid-transform/commit/670b623d718d8b6622c5b0840a0dfa175ce7a0af)) - Create dependabot.yml ([`b7ad481`](https://github.com/Bluetooth-Devices/ulid-transform/commit/b7ad4816fe3df9e4681c63ce2825658af7a51251)) - Update dependabot.yml to include GHA ([`3f9f6d7`](https://github.com/Bluetooth-Devices/ulid-transform/commit/3f9f6d7294d600c50a4f8a003c6da794d1cdb294)) - **deps-dev**: Bump cython from 3.0.11 to 3.0.12 ([#79](https://github.com/Bluetooth-Devices/ulid-transform/pull/79), [`36c741a`](https://github.com/Bluetooth-Devices/ulid-transform/commit/36c741a6da458fd7ed92c613e3bdc4fdb7a1726a)) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> - **deps-dev**: Bump pytest from 7.4.4 to 8.3.4 ([#69](https://github.com/Bluetooth-Devices/ulid-transform/pull/69), [`3aad7db`](https://github.com/Bluetooth-Devices/ulid-transform/commit/3aad7db0fb0c75dc7edd8f698565c336f85ba43b)) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> - **deps-dev**: Bump pytest-benchmark from 4.0.0 to 5.1.0 ([#71](https://github.com/Bluetooth-Devices/ulid-transform/pull/71), [`b0fffc6`](https://github.com/Bluetooth-Devices/ulid-transform/commit/b0fffc6908b722349ccfcf147a16f328c7ae5d49)) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> - **deps-dev**: Bump pytest-codspeed from 3.1.2 to 3.2.0 ([#76](https://github.com/Bluetooth-Devices/ulid-transform/pull/76), [`0781d3c`](https://github.com/Bluetooth-Devices/ulid-transform/commit/0781d3c4aff06e5c51f740fd14eb3cc83f7ffbd2)) - **deps-dev**: Bump pytest-cov from 3.0.0 to 6.0.0 ([#67](https://github.com/Bluetooth-Devices/ulid-transform/pull/67), [`690cc84`](https://github.com/Bluetooth-Devices/ulid-transform/commit/690cc84106c2f489e4575a3094748923e9d23cfc)) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> - **deps-dev**: Bump setuptools from 65.7.0 to 75.8.0 ([#68](https://github.com/Bluetooth-Devices/ulid-transform/pull/68), [`1847efa`](https://github.com/Bluetooth-Devices/ulid-transform/commit/1847efaba902ad3d29a840e6c4db6db102189bdc)) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#72](https://github.com/Bluetooth-Devices/ulid-transform/pull/72), [`82c8ed5`](https://github.com/Bluetooth-Devices/ulid-transform/commit/82c8ed5a1504be470febf971a7c27b83d33d3422)) - **pre-commit.ci**: Pre-commit autoupdate ([#74](https://github.com/Bluetooth-Devices/ulid-transform/pull/74), [`ec6b661`](https://github.com/Bluetooth-Devices/ulid-transform/commit/ec6b661e6674a28c8d6bb41e888522340667c62f)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#75](https://github.com/Bluetooth-Devices/ulid-transform/pull/75), [`1744f59`](https://github.com/Bluetooth-Devices/ulid-transform/commit/1744f59c264dad9b448a849f965b7bd238fca735)) - **pre-commit.ci**: Pre-commit autoupdate ([#77](https://github.com/Bluetooth-Devices/ulid-transform/pull/77), [`af69a21`](https://github.com/Bluetooth-Devices/ulid-transform/commit/af69a21b6e3b1b08bd07173f7d1bffb7c8f36bf1)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#78](https://github.com/Bluetooth-Devices/ulid-transform/pull/78), [`76a8ad1`](https://github.com/Bluetooth-Devices/ulid-transform/commit/76a8ad18f3c5b0407ea6ab2824a43b23699b3101)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> ## v1.2.0 (2025-01-17) ### Features - Migrate benchmarks to Python 3.13 ([#66](https://github.com/Bluetooth-Devices/ulid-transform/pull/66), [`d160dd3`](https://github.com/Bluetooth-Devices/ulid-transform/commit/d160dd333020f22e902dab897b0c7839008fce91)) ## v1.1.0 (2025-01-17) ### Chores - Add codspeed benchmarks ([#64](https://github.com/Bluetooth-Devices/ulid-transform/pull/64), [`f7c563c`](https://github.com/Bluetooth-Devices/ulid-transform/commit/f7c563c579241adf69d7c676ca0111c35d11e43b)) - Wheel builds should only happen on release ([#59](https://github.com/Bluetooth-Devices/ulid-transform/pull/59), [`d4cbb09`](https://github.com/Bluetooth-Devices/ulid-transform/commit/d4cbb09b8c547ac9c0b8b9a41971bfadc61e4c7b)) - **pre-commit.ci**: Pre-commit autoupdate ([#54](https://github.com/Bluetooth-Devices/ulid-transform/pull/54), [`b81af0e`](https://github.com/Bluetooth-Devices/ulid-transform/commit/b81af0e631c949607a871adf2e1abd196237bf5c)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#55](https://github.com/Bluetooth-Devices/ulid-transform/pull/55), [`6d5155c`](https://github.com/Bluetooth-Devices/ulid-transform/commit/6d5155c56674b245b3205d6e1491d204e2825a6e)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#56](https://github.com/Bluetooth-Devices/ulid-transform/pull/56), [`c691406`](https://github.com/Bluetooth-Devices/ulid-transform/commit/c69140612d0840e8b40ed2b6ab55687f7746ffa6)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#57](https://github.com/Bluetooth-Devices/ulid-transform/pull/57), [`c30be1a`](https://github.com/Bluetooth-Devices/ulid-transform/commit/c30be1a622544fb8c645b03e848911b09b3d35a3)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#58](https://github.com/Bluetooth-Devices/ulid-transform/pull/58), [`2c2986f`](https://github.com/Bluetooth-Devices/ulid-transform/commit/2c2986f5db4aac03b1416f2cdac50ba0ffdcf418)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#60](https://github.com/Bluetooth-Devices/ulid-transform/pull/60), [`56db2e2`](https://github.com/Bluetooth-Devices/ulid-transform/commit/56db2e2ddb59cd5037c434185ad76ced699690ca)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#61](https://github.com/Bluetooth-Devices/ulid-transform/pull/61), [`e6ea31c`](https://github.com/Bluetooth-Devices/ulid-transform/commit/e6ea31c16334a1979919a751e4e50daab909d154)) - **pre-commit.ci**: Pre-commit autoupdate ([#62](https://github.com/Bluetooth-Devices/ulid-transform/pull/62), [`5fb4ab4`](https://github.com/Bluetooth-Devices/ulid-transform/commit/5fb4ab4d5a61fa2dcb2bdca0a967c47ebd4a4144)) - **pre-commit.ci**: Pre-commit autoupdate ([#63](https://github.com/Bluetooth-Devices/ulid-transform/pull/63), [`86ab538`](https://github.com/Bluetooth-Devices/ulid-transform/commit/86ab538b73bbc534a54c97b36ef31205d2820e5d)) ### Features - Add aarch64 wheels ([#65](https://github.com/Bluetooth-Devices/ulid-transform/pull/65), [`950376b`](https://github.com/Bluetooth-Devices/ulid-transform/commit/950376beb4e3d85a9f1e79a6e1408900910099d9)) ## v1.0.2 (2024-08-24) ### Bug Fixes - Switch to github trusted publishing to fix ci ([#53](https://github.com/Bluetooth-Devices/ulid-transform/pull/53), [`dfaf168`](https://github.com/Bluetooth-Devices/ulid-transform/commit/dfaf1687c32d07ea8e92358a9f1cb8275a6f6b54)) ## v1.0.1 (2024-08-24) ### Bug Fixes - Bump ci to run on python 3.11 ([#52](https://github.com/Bluetooth-Devices/ulid-transform/pull/52), [`857e9b8`](https://github.com/Bluetooth-Devices/ulid-transform/commit/857e9b80787e20565d41029dac5f655b1f152672)) ## v1.0.0 (2024-08-24) ### Features - Drop python 3.10 support ([#51](https://github.com/Bluetooth-Devices/ulid-transform/pull/51), [`b93b779`](https://github.com/Bluetooth-Devices/ulid-transform/commit/b93b77957945c38f334912d64e7c7aa4137863e3)) ## v0.14.0 (2024-08-24) ### Chores - **pre-commit.ci**: Pre-commit autoupdate ([#47](https://github.com/Bluetooth-Devices/ulid-transform/pull/47), [`3192c1c`](https://github.com/Bluetooth-Devices/ulid-transform/commit/3192c1cdf5a9c22a3d63193ee21ca133bc95006a)) updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.5 → v0.5.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.5...v0.5.6) - [github.com/PyCQA/flake8: 7.1.0 → 7.1.1](https://github.com/PyCQA/flake8/compare/7.1.0...7.1.1) - [github.com/pre-commit/mirrors-mypy: v1.11.0 → v1.11.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.0...v1.11.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#48](https://github.com/Bluetooth-Devices/ulid-transform/pull/48), [`48fbe45`](https://github.com/Bluetooth-Devices/ulid-transform/commit/48fbe45e65c5a249a7c1eea3b8519b69f4f71020)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#49](https://github.com/Bluetooth-Devices/ulid-transform/pull/49), [`3946e2f`](https://github.com/Bluetooth-Devices/ulid-transform/commit/3946e2ff626e3034685cf6b483aa6c204dc8bca6)) updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.7 → v0.6.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.7...v0.6.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> ### Features - Python 3.13 support ([#50](https://github.com/Bluetooth-Devices/ulid-transform/pull/50), [`c50e08a`](https://github.com/Bluetooth-Devices/ulid-transform/commit/c50e08ac1c019ee8a3313780a7fd180f60e4db3f)) ## v0.13.1 (2024-07-30) ### Bug Fixes - Add pyi stubs for cython implementation ([#46](https://github.com/Bluetooth-Devices/ulid-transform/pull/46), [`dedaa87`](https://github.com/Bluetooth-Devices/ulid-transform/commit/dedaa87d9e5df4ed037bb0a95c971a6e6e376439)) ### Chores - **pre-commit.ci**: Pre-commit autoupdate ([#45](https://github.com/Bluetooth-Devices/ulid-transform/pull/45), [`d4b3890`](https://github.com/Bluetooth-Devices/ulid-transform/commit/d4b3890a78a3145c84d0a0f72d80e6face310922)) updates: - [github.com/asottile/pyupgrade: v3.16.0 → v3.17.0](https://github.com/asottile/pyupgrade/compare/v3.16.0...v3.17.0) - [github.com/astral-sh/ruff-pre-commit: v0.5.4 → v0.5.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.4...v0.5.5) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> ## v0.13.0 (2024-07-29) ### Chores - Ci fixes (version upgrades, repair extension build in some configs) ([#44](https://github.com/Bluetooth-Devices/ulid-transform/pull/44), [`de99c22`](https://github.com/Bluetooth-Devices/ulid-transform/commit/de99c22c1a0595fb0068d1abc3ffac157b11f3d4)) ### Features - Improve performance of C implementation ([#43](https://github.com/Bluetooth-Devices/ulid-transform/pull/43), [`f07d838`](https://github.com/Bluetooth-Devices/ulid-transform/commit/f07d838169743c4e68f344a04b6541a6a8fee239)) ## v0.12.0 (2024-07-26) ### Features - Improve testability, keep implementations in sync ([#40](https://github.com/Bluetooth-Devices/ulid-transform/pull/40), [`975079d`](https://github.com/Bluetooth-Devices/ulid-transform/commit/975079d682ffb6039454d3c041867e0d14a9f646)) * chore: ignore Cython annotation file * fix: synchronize c/py implementations Each implementation now exposes the same public methods, and their docstrings and signatures are equivalent. * chore: remove hand-decoding ULID timestamp from test code * feat: test C and Python implementations simultaneously * chore: use new-style annotations in Cython too They're exported as annotation strings anyway * chore: add tests to make sure Python and C impls are in sync ## v0.11.1 (2024-07-26) ### Bug Fixes - Cython build, add a test to verify the Cython module is available ([#41](https://github.com/Bluetooth-Devices/ulid-transform/pull/41), [`7187b35`](https://github.com/Bluetooth-Devices/ulid-transform/commit/7187b357b395b9c1716c8beaa5fc4db389fece18)) ### Chores - Add benchmarking tests ([#38](https://github.com/Bluetooth-Devices/ulid-transform/pull/38), [`456b967`](https://github.com/Bluetooth-Devices/ulid-transform/commit/456b967b457200f3001210fa527242f8a7d229b3)) - Remove IDEA configuration from repository ([#39](https://github.com/Bluetooth-Devices/ulid-transform/pull/39), [`1d95563`](https://github.com/Bluetooth-Devices/ulid-transform/commit/1d95563461026a7829b4e85a7f93efd71ed55080)) ## v0.11.0 (2024-07-25) ### Features - Add fast ULID bytes functions ([#37](https://github.com/Bluetooth-Devices/ulid-transform/pull/37), [`57b58ab`](https://github.com/Bluetooth-Devices/ulid-transform/commit/57b58ab269dcb97dbc905925861c3a25daf7e114)) ## v0.10.2 (2024-07-25) ### Bug Fixes - Fix error message in Cython `bytes_to_ulid` ([#36](https://github.com/Bluetooth-Devices/ulid-transform/pull/36), [`d8b5462`](https://github.com/Bluetooth-Devices/ulid-transform/commit/d8b54622d1445916f08b61ff3aad34e61bfbb986)) ### Chores - **pre-commit.ci**: Pre-commit autoupdate ([#33](https://github.com/Bluetooth-Devices/ulid-transform/pull/33), [`4d75f52`](https://github.com/Bluetooth-Devices/ulid-transform/commit/4d75f52e205ec7a4daf3cc1b23769867b9b65996)) updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.0 → v0.5.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.0...v0.5.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#34](https://github.com/Bluetooth-Devices/ulid-transform/pull/34), [`a27b5b0`](https://github.com/Bluetooth-Devices/ulid-transform/commit/a27b5b0ae2b07f1d5990ea4c04788472e921d415)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#35](https://github.com/Bluetooth-Devices/ulid-transform/pull/35), [`bdbb9c5`](https://github.com/Bluetooth-Devices/ulid-transform/commit/bdbb9c58f6d55209416eb7812e31669e14f39171)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> ## v0.10.1 (2024-07-05) ### Bug Fixes - Wheel builds ([#32](https://github.com/Bluetooth-Devices/ulid-transform/pull/32), [`87b511e`](https://github.com/Bluetooth-Devices/ulid-transform/commit/87b511e1b00b8fa07ef7a18a5b5012e8df943441)) ## v0.10.0 (2024-07-05) ### Chores - Add more cover and benchmarks ([#27](https://github.com/Bluetooth-Devices/ulid-transform/pull/27), [`822ea8c`](https://github.com/Bluetooth-Devices/ulid-transform/commit/822ea8c5e69b4a9652c19ae0172c16f8236634f5)) - Switch to ruff ([#31](https://github.com/Bluetooth-Devices/ulid-transform/pull/31), [`98679ea`](https://github.com/Bluetooth-Devices/ulid-transform/commit/98679ea082591ae710188798b594e57c4f05f444)) - Test on 32bit ([#26](https://github.com/Bluetooth-Devices/ulid-transform/pull/26), [`5a62d31`](https://github.com/Bluetooth-Devices/ulid-transform/commit/5a62d31fbb75fd20aa386d44a576e939f665312d)) - **pre-commit.ci**: Pre-commit autoupdate ([#28](https://github.com/Bluetooth-Devices/ulid-transform/pull/28), [`63cc7f9`](https://github.com/Bluetooth-Devices/ulid-transform/commit/63cc7f9f596df98c59a2564a8749f7b999f1407b)) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> - **pre-commit.ci**: Pre-commit autoupdate ([#29](https://github.com/Bluetooth-Devices/ulid-transform/pull/29), [`72c43be`](https://github.com/Bluetooth-Devices/ulid-transform/commit/72c43be55a4597066d33d44a92ecb33dd2a5c806)) updates: - [github.com/pre-commit/mirrors-mypy: v1.10.0 → v1.10.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.0...v1.10.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> ### Features - Add new fast or_none functions ([#30](https://github.com/Bluetooth-Devices/ulid-transform/pull/30), [`4d1d1ee`](https://github.com/Bluetooth-Devices/ulid-transform/commit/4d1d1ee398f791bd35a64941d3eb838cf1ef705e)) ## v0.9.0 (2023-10-18) ### Bug Fixes - Reduce size of wheels by excluding generated .cpp files ([#24](https://github.com/Bluetooth-Devices/ulid-transform/pull/24), [`29e0ff4`](https://github.com/Bluetooth-Devices/ulid-transform/commit/29e0ff474f729adea32c10a3d0adfc7801b5e892)) ### Features - Build wheel with latest cpython release ([#25](https://github.com/Bluetooth-Devices/ulid-transform/pull/25), [`8bc67fe`](https://github.com/Bluetooth-Devices/ulid-transform/commit/8bc67fe8f2c56a9aacdede63331afe6c54a1528d)) ## v0.8.1 (2023-08-27) ### Bug Fixes - Rebuild wheels with cython 3.0.2 ([#22](https://github.com/Bluetooth-Devices/ulid-transform/pull/22), [`8d78432`](https://github.com/Bluetooth-Devices/ulid-transform/commit/8d78432cf81b1a2e5230dd434b50c21365686548)) ### Chores - Bump py3.12 to rc1 ([#23](https://github.com/Bluetooth-Devices/ulid-transform/pull/23), [`e21accd`](https://github.com/Bluetooth-Devices/ulid-transform/commit/e21accd67406f20aca65e754643e282da39ada26)) ## v0.8.0 (2023-07-24) ### Features - Initial cpython 3.12 support ([#21](https://github.com/Bluetooth-Devices/ulid-transform/pull/21), [`7f7e8b9`](https://github.com/Bluetooth-Devices/ulid-transform/commit/7f7e8b90d3a7a529a58d00e66be8494a18476842)) ## v0.7.2 (2023-05-01) ### Bug Fixes - Ensure windows wheel work with older versions ([#20](https://github.com/Bluetooth-Devices/ulid-transform/pull/20), [`8a440e3`](https://github.com/Bluetooth-Devices/ulid-transform/commit/8a440e3f818b3af8a694544755d55f1c221ada3a)) ## v0.7.1 (2023-05-01) ### Bug Fixes - Missing decode for _bytes_to_ulid ([#19](https://github.com/Bluetooth-Devices/ulid-transform/pull/19), [`ddf6433`](https://github.com/Bluetooth-Devices/ulid-transform/commit/ddf6433554ca3d4fef6500b84e43adf475a794bb)) ## v0.7.0 (2023-04-23) ### Features - Add a bytes_to_ulid cpp version ([#18](https://github.com/Bluetooth-Devices/ulid-transform/pull/18), [`fa1c62c`](https://github.com/Bluetooth-Devices/ulid-transform/commit/fa1c62c97be608390a5e42de5712382ee8ec86e9)) ## v0.6.3 (2023-04-10) ### Bug Fixes - Additional 32bit time fixes ([#17](https://github.com/Bluetooth-Devices/ulid-transform/pull/17), [`2f5f1be`](https://github.com/Bluetooth-Devices/ulid-transform/commit/2f5f1be3fe63bb63b57e158c32ab5e9d7ab16b9c)) ## v0.6.2 (2023-04-09) ### Bug Fixes - Apply 32bit cast for for random data ([#16](https://github.com/Bluetooth-Devices/ulid-transform/pull/16), [`1e1d62a`](https://github.com/Bluetooth-Devices/ulid-transform/commit/1e1d62aa961178d6416e4ac82cc0634b06260ad4)) ## v0.6.1 (2023-04-09) ### Bug Fixes - Apply 32bit time fix ([#15](https://github.com/Bluetooth-Devices/ulid-transform/pull/15), [`26347f0`](https://github.com/Bluetooth-Devices/ulid-transform/commit/26347f0067be94ba36607ea9b875b5d1354e6002)) ### Chores - Add test_non_uppercase_b32_data ([#14](https://github.com/Bluetooth-Devices/ulid-transform/pull/14), [`06819d0`](https://github.com/Bluetooth-Devices/ulid-transform/commit/06819d058f2a754a39221027cafb98d0568c5b18)) ## v0.6.0 (2023-04-06) ### Features - Reflect the invalid value when raising ValueError ([#13](https://github.com/Bluetooth-Devices/ulid-transform/pull/13), [`6a022fb`](https://github.com/Bluetooth-Devices/ulid-transform/commit/6a022fb4084e3a007c469e6e2993ccb3621c4271)) ## v0.5.1 (2023-03-22) ### Bug Fixes - Specify python version to build wheels ([#12](https://github.com/Bluetooth-Devices/ulid-transform/pull/12), [`9263de0`](https://github.com/Bluetooth-Devices/ulid-transform/commit/9263de0f70a4198cfcb2ba4a8f44440a7841e407)) ## v0.5.0 (2023-03-22) ### Features - Wheels for macOS and Windows ([#11](https://github.com/Bluetooth-Devices/ulid-transform/pull/11), [`086852e`](https://github.com/Bluetooth-Devices/ulid-transform/commit/086852e57250994d0f3c9faedabf2df6aeb9e789)) Build wheels for macOS and Windows too ## v0.4.2 (2023-03-13) ### Bug Fixes - Ulid_now on 32bit ([#10](https://github.com/Bluetooth-Devices/ulid-transform/pull/10), [`c8f7dd7`](https://github.com/Bluetooth-Devices/ulid-transform/commit/c8f7dd790f987ca4310dd155a78fff263adf0cfc)) ## v0.4.1 (2023-03-13) ### Bug Fixes - Random as 0 on 32 bit arch ([#9](https://github.com/Bluetooth-Devices/ulid-transform/pull/9), [`c9fa4f3`](https://github.com/Bluetooth-Devices/ulid-transform/commit/c9fa4f32c12eabea67da14d0c32d9a5ac65f2842)) ## v0.4.0 (2023-03-01) ### Features - Add bytes_to_ulid ([#8](https://github.com/Bluetooth-Devices/ulid-transform/pull/8), [`c9a57ef`](https://github.com/Bluetooth-Devices/ulid-transform/commit/c9a57ef2d68886d37e03dd9d884a51c35a5c1e12)) ## v0.3.1 (2023-03-01) ### Bug Fixes - Encode timestamps correctly in cpp implementation ([#7](https://github.com/Bluetooth-Devices/ulid-transform/pull/7), [`c7fedc3`](https://github.com/Bluetooth-Devices/ulid-transform/commit/c7fedc350bf9848dc38af362e33819fac4036a9e)) ### Chores - Add timestamp test ([#6](https://github.com/Bluetooth-Devices/ulid-transform/pull/6), [`404487d`](https://github.com/Bluetooth-Devices/ulid-transform/commit/404487dd5879f83c6df6ad230ca6670edaacaa40)) ## v0.3.0 (2023-02-28) ### Features - Add examples ([#5](https://github.com/Bluetooth-Devices/ulid-transform/pull/5), [`be30a13`](https://github.com/Bluetooth-Devices/ulid-transform/commit/be30a133b3a03b1e6704954cf0c59f4fb64d4b7c)) ## v0.2.1 (2023-02-28) ### Bug Fixes - Drop fast-ulid for cython ([#4](https://github.com/Bluetooth-Devices/ulid-transform/pull/4), [`8d72d6b`](https://github.com/Bluetooth-Devices/ulid-transform/commit/8d72d6b58d306d722f096a5b3697d4365eae397d)) ## v0.2.0 (2023-02-28) ### Features - Enable cython builds ([#3](https://github.com/Bluetooth-Devices/ulid-transform/pull/3), [`154bf0c`](https://github.com/Bluetooth-Devices/ulid-transform/commit/154bf0c8d02591508b87c2c154ba877da2aa8f97)) ## v0.1.0 (2023-02-28) ### Bug Fixes - Remove html file ([#2](https://github.com/Bluetooth-Devices/ulid-transform/pull/2), [`c1a5cf4`](https://github.com/Bluetooth-Devices/ulid-transform/commit/c1a5cf4a4a8c5a2c8ba1663b9132d612fe47570d)) ### Chores - Initial commit ([`61fc260`](https://github.com/Bluetooth-Devices/ulid-transform/commit/61fc2606750f879ba7fb69ba3bdd371c5bcac2e8)) ### Features - Init repo ([#1](https://github.com/Bluetooth-Devices/ulid-transform/pull/1), [`0ef9511`](https://github.com/Bluetooth-Devices/ulid-transform/commit/0ef95113cd638617de16be44909b228d0df5f092)) ulid-transform-1.4.0/CONTRIBUTING.md000066400000000000000000000074741476254674500167530ustar00rootroot00000000000000# Contributing Contributions are welcome, and they are greatly appreciated! Every little helps, and credit will always be given. You can contribute in many ways: ## Types of Contributions ### Report Bugs Report bugs to [our issue page][gh-issues]. If you are reporting a bug, please include: - Your operating system name and version. - Any details about your local setup that might be helpful in troubleshooting. - Detailed steps to reproduce the bug. ### Fix Bugs Look through the GitHub issues for bugs. Anything tagged with "bug" and "help wanted" is open to whoever wants to implement it. ### Implement Features Look through the GitHub issues for features. Anything tagged with "enhancement" and "help wanted" is open to whoever wants to implement it. ### Write Documentation Fast ULID transformations could always use more documentation, whether as part of the official Fast ULID transformations docs, in docstrings, or even on the web in blog posts, articles, and such. ### Submit Feedback The best way to send feedback [our issue page][gh-issues] on GitHub. If you are proposing a feature: - Explain in detail how it would work. - Keep the scope as narrow as possible, to make it easier to implement. - Remember that this is a volunteer-driven project, and that contributions are welcome 😊 ## Get Started! Ready to contribute? Here's how to set yourself up for local development. 1. Fork the repo on GitHub. 2. Clone your fork locally: ```shell $ git clone git@github.com:your_name_here/ulid-transform.git ``` 3. Install the project dependencies with [Poetry](https://python-poetry.org): ```shell $ poetry install ``` 4. Create a branch for local development: ```shell $ git checkout -b name-of-your-bugfix-or-feature ``` Now you can make your changes locally. 5. When you're done making changes, check that your changes pass our tests: ```shell $ poetry run pytest ``` 6. Linting is done through [pre-commit](https://pre-commit.com). Provided you have the tool installed globally, you can run them all as one-off: ```shell $ pre-commit run -a ``` Or better, install the hooks once and have them run automatically each time you commit: ```shell $ pre-commit install ``` 7. Commit your changes and push your branch to GitHub: ```shell $ git add . $ git commit -m "feat(something): your detailed description of your changes" $ git push origin name-of-your-bugfix-or-feature ``` Note: the commit message should follow [the conventional commits](https://www.conventionalcommits.org). We run [`commitlint` on CI](https://github.com/marketplace/actions/commit-linter) to validate it, and if you've installed pre-commit hooks at the previous step, the message will be checked at commit time. 8. Submit a pull request through the GitHub website or using the GitHub CLI (if you have it installed): ```shell $ gh pr create --fill ``` ## Pull Request Guidelines We like to have the pull request open as soon as possible, that's a great place to discuss any piece of work, even unfinished. You can use draft pull request if it's still a work in progress. Here are a few guidelines to follow: 1. Include tests for feature or bug fixes. 2. Update the documentation for significant features. 3. Ensure tests are passing on CI. ## Tips To run a subset of tests: ```shell $ pytest tests ``` ## Making a new release The deployment should be automated and can be triggered from the Semantic Release workflow in GitHub. The next version will be based on [the commit logs](https://python-semantic-release.readthedocs.io/en/latest/commit-log-parsing.html#commit-log-parsing). This is done by [python-semantic-release](https://python-semantic-release.readthedocs.io/en/latest/index.html) via a GitHub action. [gh-issues]: https://github.com/bluetooth-devices/ulid-transform/issues ulid-transform-1.4.0/LICENSE000066400000000000000000000020601476254674500155110ustar00rootroot00000000000000 MIT License Copyright (c) 2022 J. Nick Koston Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ulid-transform-1.4.0/README.md000066400000000000000000000107751476254674500157770ustar00rootroot00000000000000# Fast ULID transformations

CI Status Test coverage percentage

Poetry black pre-commit CodSpeed Badge

PyPI Version Supported Python versions License

Create and transform ULIDs This library will use the CPP implementation from https://github.com/suyash/ulid if cython is available, and will fallback to pure python if it is not. ## Example ```python >>> import ulid_transform >>> ulid_transform.ulid_hex() '01869a2ea5fb0b43aa056293e47c0a35' >>> ulid_transform.ulid_now() '0001HZX0NW00GW0X476W5TVBFE' >>> ulid_transform.ulid_at_time(1234) '000000016JC62D620DGYNG2R8H' >>> ulid_transform.ulid_to_bytes('0001HZX0NW00GW0X476W5TVBFE') b'\x00\x00c\xfe\x82\xbc\x00!\xc0t\x877\x0b\xad\xad\xee' >> ulid_transform.bytes_to_ulid(b"\x01\x86\x99?\xe8\xf3\x11\xbc\xed\xef\x86U.9\x03z") '01GTCKZT7K26YEVVW6AMQ3J0VT' >>> ulid_transform.ulid_to_bytes_or_none('0001HZX0NW00GW0X476W5TVBFE') b'\x00\x00c\xfe\x82\xbc\x00!\xc0t\x877\x0b\xad\xad\xee' >>> ulid_transform.ulid_to_bytes_or_none(None) >>> ulid_transform.bytes_to_ulid_or_none(b'\x00\x00c\xfe\x82\xbc\x00!\xc0t\x877\x0b\xad\xad\xee') '0001HZX0NW00GW0X476W5TVBFE' >>> ulid_transform.bytes_to_ulid_or_none(None) ``` ## Installation Install this via pip (or your favourite package manager): `pip install ulid-transform` ## Contributors ✨ Thanks to https://github.com/suyash/ulid which provides the cython implementation guts. Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! ## Credits This package was created with [Copier](https://copier.readthedocs.io/) and the [browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template) project template. ulid-transform-1.4.0/bench/000077500000000000000000000000001476254674500155655ustar00rootroot00000000000000ulid-transform-1.4.0/bench/__init__.py000066400000000000000000000000001476254674500176640ustar00rootroot00000000000000ulid-transform-1.4.0/bench/conftest.py000066400000000000000000000000611476254674500177610ustar00rootroot00000000000000from tests.utils import impl __all__ = ["impl"] ulid-transform-1.4.0/bench/test_timestamp.py000066400000000000000000000014021476254674500211760ustar00rootroot00000000000000import pytest import tests.conftest # noqa example = "01J3YPYJJW00GW0X476W5TVBFE" example_timestamp = 1722238847580 @pytest.mark.benchmark(group="timestamp") def test_ut_timestamp(benchmark, impl): assert benchmark(lambda: impl.ulid_to_timestamp(example)) == example_timestamp @pytest.mark.benchmark(group="timestamp") def test_ulid2_timestamp(benchmark): ulid2 = pytest.importorskip("ulid2") assert ( benchmark(lambda: ulid2.get_ulid_timestamp(example)) == example_timestamp / 1000.0 ) @pytest.mark.benchmark(group="timestamp") def test_ulidpy_uuid(benchmark): ulid = pytest.importorskip("ulid") assert ( benchmark(lambda: ulid.from_str(example).timestamp().timestamp) == example_timestamp / 1000.0 ) ulid-transform-1.4.0/bench/test_ulid_as_uuid.py000066400000000000000000000014451476254674500216500ustar00rootroot00000000000000from uuid import UUID import pytest import tests.conftest # noqa @pytest.mark.benchmark(group="ulid_as_uuid") def test_ut_ulid_now_bytes(benchmark, impl): assert isinstance( benchmark(lambda: UUID(bytes=impl.ulid_now_bytes())), UUID, ) @pytest.mark.benchmark(group="ulid_as_uuid") def test_ut_ulid_hex(benchmark, impl): assert isinstance(benchmark(lambda: UUID(hex=impl.ulid_hex())), UUID) @pytest.mark.benchmark(group="ulid_as_uuid") def test_ulid2_uuid(benchmark): ulid2 = pytest.importorskip("ulid2") assert isinstance(benchmark(lambda: ulid2.generate_ulid_as_uuid()), UUID) @pytest.mark.benchmark(group="ulid_as_uuid") def test_ulidpy_uuid(benchmark): ulid = pytest.importorskip("ulid") assert isinstance(benchmark(lambda: ulid.new().uuid), UUID) ulid-transform-1.4.0/bench/test_ulid_parsing.py000066400000000000000000000012111476254674500216510ustar00rootroot00000000000000import pytest import tests.conftest # noqa example = "01J3MS6XG9XC7X9FR15J9A82ZP" @pytest.mark.benchmark(group="ulid_parsing") def test_ut_decode(benchmark, impl): assert isinstance(benchmark(lambda: impl.ulid_to_bytes(example)), bytes) @pytest.mark.benchmark(group="ulid_parsing") def test_ulid2_decode(benchmark): ulid2 = pytest.importorskip("ulid2") assert isinstance(benchmark(lambda: ulid2.decode_ulid_base32(example)), bytes) @pytest.mark.benchmark(group="ulid_parsing") def test_ulidpy_decode(benchmark): ulid = pytest.importorskip("ulid") assert isinstance(benchmark(lambda: ulid.from_str(example).bytes), bytes) ulid-transform-1.4.0/bench/test_ulid_strings.py000066400000000000000000000022361476254674500217070ustar00rootroot00000000000000import pytest import tests.conftest # noqa @pytest.mark.benchmark(group="ulid_strings") def test_ut_ulid_now(benchmark, impl): assert isinstance(benchmark(lambda: impl.ulid_now()), str) @pytest.mark.benchmark(group="ulid_strings") def test_ut_ulid_at_time(benchmark, impl): assert isinstance(benchmark(lambda: impl.ulid_at_time(1)), str) @pytest.mark.benchmark(group="ulid_strings") def test_ulid2_ulid_now(benchmark): ulid2 = pytest.importorskip("ulid2") assert isinstance(benchmark(lambda: ulid2.generate_ulid_as_base32()), str) @pytest.mark.benchmark(group="ulid_strings") def test_ulid2_ulid_at_time(benchmark): ulid2 = pytest.importorskip("ulid2") assert isinstance( benchmark(lambda: ulid2.generate_ulid_as_base32(timestamp=1)), str ) @pytest.mark.benchmark(group="ulid_strings") def test_ulidpy_uuid_now(benchmark): ulid = pytest.importorskip("ulid") assert isinstance(benchmark(lambda: ulid.new().str), str) @pytest.mark.benchmark(group="ulid_strings") def test_ulidpy_uuid_at_time(benchmark): ulid = pytest.importorskip("ulid") assert isinstance(benchmark(lambda: ulid.from_timestamp(timestamp=1).str), str) ulid-transform-1.4.0/build_ext.py000066400000000000000000000036231476254674500170430ustar00rootroot00000000000000"""Build optional cython modules.""" import logging import os from distutils.command.build_ext import build_ext from os.path import join from typing import Any try: from setuptools import Extension except ImportError: from distutils.core import Extension def getenv_bool(key: str, default: bool = False) -> bool: value = os.environ.get(key, str(default)).lower() if value in ("1", "true", "yes"): return True if value in ("0", "false", "no"): return False raise ValueError(f"Invalid value for boolean envvar {key}: {value}") ulid_module = Extension( "ulid_transform._ulid_impl", [ join("src", "ulid_transform", "_ulid_impl.pyx"), join("src", "ulid_transform", "ulid_wrapper.cpp"), ], language="c++", extra_compile_args=["-std=c++11", "-O3", "-g0"], extra_link_args=["-std=c++11"], ) class BuildExt(build_ext): def build_extensions(self) -> None: try: super().build_extensions() except Exception: # nosec logging.exception("Failed to build extensions") if getenv_bool("REQUIRE_CYTHON"): raise def build(setup_kwargs: Any) -> None: if getenv_bool("SKIP_CYTHON"): return try: from Cython.Build import cythonize setup_kwargs.update( { "ext_modules": cythonize( [ ulid_module, ], compiler_directives={"language_level": "3"}, # Python 3 verbose=True, ), "cmdclass": {"build_ext": BuildExt}, } ) setup_kwargs["exclude_package_data"] = { pkg: ["_ulid_impl.cpp"] for pkg in setup_kwargs["packages"] } except Exception: logging.exception("Failed to configure cython") if getenv_bool("REQUIRE_CYTHON"): raise ulid-transform-1.4.0/commitlint.config.mjs000066400000000000000000000003621476254674500206450ustar00rootroot00000000000000export default { extends: ["@commitlint/config-conventional"], rules: { "header-max-length": [0, "always", Infinity], "body-max-line-length": [0, "always", Infinity], "footer-max-line-length": [0, "always", Infinity], }, }; ulid-transform-1.4.0/poetry.lock000066400000000000000000001315321476254674500167070ustar00rootroot00000000000000# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "cffi" version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] [package.dependencies] pycparser = "*" [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["benchmark", "dev"] markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" version = "7.6.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "cython" version = "3.0.12" description = "The Cython compiler for writing C extensions in the Python language." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" groups = ["dev"] files = [ {file = "Cython-3.0.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba67eee9413b66dd9fbacd33f0bc2e028a2a120991d77b5fd4b19d0b1e4039b9"}, {file = "Cython-3.0.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee2717e5b5f7d966d0c6e27d2efe3698c357aa4d61bb3201997c7a4f9fe485a"}, {file = "Cython-3.0.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cffc3464f641c8d0dda942c7c53015291beea11ec4d32421bed2f13b386b819"}, {file = "Cython-3.0.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d3a8f81980ffbd74e52f9186d8f1654e347d0c44bfea6b5997028977f481a179"}, {file = "Cython-3.0.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8d32856716c369d01f2385ad9177cdd1a11079ac89ea0932dc4882de1aa19174"}, {file = "Cython-3.0.12-cp310-cp310-win32.whl", hash = "sha256:712c3f31adec140dc60d064a7f84741f50e2c25a8edd7ae746d5eb4d3ef7072a"}, {file = "Cython-3.0.12-cp310-cp310-win_amd64.whl", hash = "sha256:d6945694c5b9170cfbd5f2c0d00ef7487a2de7aba83713a64ee4ebce7fad9e05"}, {file = "Cython-3.0.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:feb86122a823937cc06e4c029d80ff69f082ebb0b959ab52a5af6cdd271c5dc3"}, {file = "Cython-3.0.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfdbea486e702c328338314adb8e80f5f9741f06a0ae83aaec7463bc166d12e8"}, {file = "Cython-3.0.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563de1728c8e48869d2380a1b76bbc1b1b1d01aba948480d68c1d05e52d20c92"}, {file = "Cython-3.0.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d4576c1e1f6316282aa0b4a55139254fbed965cba7813e6d9900d3092b128"}, {file = "Cython-3.0.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1e5eadef80143026944ea8f9904715a008f5108d1d644a89f63094cc37351e73"}, {file = "Cython-3.0.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5a93cbda00a5451175b97dea5a9440a3fcee9e54b4cba7a7dbcba9a764b22aec"}, {file = "Cython-3.0.12-cp311-cp311-win32.whl", hash = "sha256:3109e1d44425a2639e9a677b66cd7711721a5b606b65867cb2d8ef7a97e2237b"}, {file = "Cython-3.0.12-cp311-cp311-win_amd64.whl", hash = "sha256:d4b70fc339adba1e2111b074ee6119fe9fd6072c957d8597bce9a0dd1c3c6784"}, {file = "Cython-3.0.12-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fe030d4a00afb2844f5f70896b7f2a1a0d7da09bf3aa3d884cbe5f73fff5d310"}, {file = "Cython-3.0.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7fec4f052b8fe173fe70eae75091389955b9a23d5cec3d576d21c5913b49d47"}, {file = "Cython-3.0.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0faa5e39e5c8cdf6f9c3b1c3f24972826e45911e7f5b99cf99453fca5432f45e"}, {file = "Cython-3.0.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d53de996ed340e9ab0fc85a88aaa8932f2591a2746e1ab1c06e262bd4ec4be7"}, {file = "Cython-3.0.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea3a0e19ab77266c738aa110684a753a04da4e709472cadeff487133354d6ab8"}, {file = "Cython-3.0.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c151082884be468f2f405645858a857298ac7f7592729e5b54788b5c572717ba"}, {file = "Cython-3.0.12-cp312-cp312-win32.whl", hash = "sha256:3083465749911ac3b2ce001b6bf17f404ac9dd35d8b08469d19dc7e717f5877a"}, {file = "Cython-3.0.12-cp312-cp312-win_amd64.whl", hash = "sha256:c0b91c7ebace030dd558ea28730de8c580680b50768e5af66db2904a3716c3e3"}, {file = "Cython-3.0.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4ee6f1ea1bead8e6cbc4e64571505b5d8dbdb3b58e679d31f3a84160cebf1a1a"}, {file = "Cython-3.0.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57aefa6d3341109e46ec1a13e3a763aaa2cbeb14e82af2485b318194be1d9170"}, {file = "Cython-3.0.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:879ae9023958d63c0675015369384642d0afb9c9d1f3473df9186c42f7a9d265"}, {file = "Cython-3.0.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36fcd584dae547de6f095500a380f4a0cce72b7a7e409e9ff03cb9beed6ac7a1"}, {file = "Cython-3.0.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:62b79dcc0de49efe9e84b9d0e2ae0a6fc9b14691a65565da727aa2e2e63c6a28"}, {file = "Cython-3.0.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4aa255781b093a8401109d8f2104bbb2e52de7639d5896aefafddc85c30e0894"}, {file = "Cython-3.0.12-cp313-cp313-win32.whl", hash = "sha256:77d48f2d4bab9fe1236eb753d18f03e8b2619af5b6f05d51df0532a92dfb38ab"}, {file = "Cython-3.0.12-cp313-cp313-win_amd64.whl", hash = "sha256:86c304b20bd57c727c7357e90d5ba1a2b6f1c45492de2373814d7745ef2e63b4"}, {file = "Cython-3.0.12-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ff5c0b6a65b08117d0534941d404833d516dac422eee88c6b4fd55feb409a5ed"}, {file = "Cython-3.0.12-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:680f1d6ed4436ae94805db264d6155ed076d2835d84f20dcb31a7a3ad7f8668c"}, {file = "Cython-3.0.12-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc24609613fa06d0d896309f7164ba168f7e8d71c1e490ed2a08d23351c3f41"}, {file = "Cython-3.0.12-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1879c073e2b34924ce9b7ca64c212705dcc416af4337c45f371242b2e5f6d32"}, {file = "Cython-3.0.12-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:bfb75123dd4ff767baa37d7036da0de2dfb6781ff256eef69b11b88b9a0691d1"}, {file = "Cython-3.0.12-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:f39640f8df0400cde6882e23c734f15bb8196de0a008ae5dc6c8d1ec5957d7c8"}, {file = "Cython-3.0.12-cp36-cp36m-win32.whl", hash = "sha256:8c9efe9a0895abee3cadfdad4130b30f7b5e57f6e6a51ef2a44f9fc66a913880"}, {file = "Cython-3.0.12-cp36-cp36m-win_amd64.whl", hash = "sha256:63d840f2975e44d74512f8f34f1f7cb8121c9428e26a3f6116ff273deb5e60a2"}, {file = "Cython-3.0.12-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:75c5acd40b97cff16fadcf6901a91586cbca5dcdba81f738efaf1f4c6bc8dccb"}, {file = "Cython-3.0.12-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e62564457851db1c40399bd95a5346b9bb99e17a819bf583b362f418d8f3457a"}, {file = "Cython-3.0.12-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ccd1228cc203b1f1b8a3d403f5a20ad1c40e5879b3fbf5851ce09d948982f2c"}, {file = "Cython-3.0.12-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25529ee948f44d9a165ff960c49d4903267c20b5edf2df79b45924802e4cca6e"}, {file = "Cython-3.0.12-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:90cf599372c5a22120609f7d3a963f17814799335d56dd0dcf8fe615980a8ae1"}, {file = "Cython-3.0.12-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:9f8c48748a9c94ea5d59c26ab49ad0fad514d36f894985879cf3c3ca0e600bf4"}, {file = "Cython-3.0.12-cp37-cp37m-win32.whl", hash = "sha256:3e4fa855d98bc7bd6a2049e0c7dc0dcf595e2e7f571a26e808f3efd84d2db374"}, {file = "Cython-3.0.12-cp37-cp37m-win_amd64.whl", hash = "sha256:120681093772bf3600caddb296a65b352a0d3556e962b9b147efcfb8e8c9801b"}, {file = "Cython-3.0.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:731d719423e041242c9303c80cae4327467299b90ffe62d4cc407e11e9ea3160"}, {file = "Cython-3.0.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3238a29f37999e27494d120983eca90d14896b2887a0bd858a381204549137a"}, {file = "Cython-3.0.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b588c0a089a9f4dd316d2f9275230bad4a7271e5af04e1dc41d2707c816be44b"}, {file = "Cython-3.0.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ab9f5198af74eb16502cc143cdde9ca1cbbf66ea2912e67440dd18a36e3b5fa"}, {file = "Cython-3.0.12-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8ee841c0e114efa1e849c281ac9b8df8aa189af10b4a103b1c5fd71cbb799679"}, {file = "Cython-3.0.12-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:43c48b5789398b228ea97499f5b864843ba9b1ab837562a9227c6f58d16ede8b"}, {file = "Cython-3.0.12-cp38-cp38-win32.whl", hash = "sha256:5e5f17c48a4f41557fbcc7ee660ccfebe4536a34c557f553b6893c1b3c83df2d"}, {file = "Cython-3.0.12-cp38-cp38-win_amd64.whl", hash = "sha256:309c081057930bb79dc9ea3061a1af5086c679c968206e9c9c2ec90ab7cb471a"}, {file = "Cython-3.0.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54115fcc126840926ff3b53cfd2152eae17b3522ae7f74888f8a41413bd32f25"}, {file = "Cython-3.0.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:629db614b9c364596d7c975fa3fb3978e8c5349524353dbe11429896a783fc1e"}, {file = "Cython-3.0.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af081838b0f9e12a83ec4c3809a00a64c817f489f7c512b0e3ecaf5f90a2a816"}, {file = "Cython-3.0.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:34ce459808f7d8d5d4007bc5486fe50532529096b43957af6cbffcb4d9cc5c8d"}, {file = "Cython-3.0.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d6c6cd6a75c8393e6805d17f7126b96a894f310a1a9ea91c47d141fb9341bfa8"}, {file = "Cython-3.0.12-cp39-cp39-win32.whl", hash = "sha256:a4032e48d4734d2df68235d21920c715c451ac9de15fa14c71b378e8986b83be"}, {file = "Cython-3.0.12-cp39-cp39-win_amd64.whl", hash = "sha256:dcdc3e5d4ce0e7a4af6903ed580833015641e968d18d528d8371e2435a34132c"}, {file = "Cython-3.0.12-py2.py3-none-any.whl", hash = "sha256:0038c9bae46c459669390e53a1ec115f8096b2e4647ae007ff1bf4e6dee92806"}, {file = "cython-3.0.12.tar.gz", hash = "sha256:b988bb297ce76c671e28c97d017b95411010f7c77fa6623dd0bb47eed1aee1bc"}, ] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" groups = ["benchmark", "dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, ] [package.dependencies] mdurl = ">=0.1,<1.0" [package.extras] benchmarking = ["psutil", "pytest", "pytest-benchmark"] code-style = ["pre-commit (>=3.0,<4.0)"] compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] linkify = ["linkify-it-py (>=1,<3)"] plugins = ["mdit-py-plugins"] profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" groups = ["dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] [[package]] name = "packaging" version = "24.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["benchmark", "dev"] files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] name = "pluggy" version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" groups = ["benchmark", "dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "py-cpuinfo" version = "9.0.0" description = "Get CPU info with pure Python" optional = false python-versions = "*" groups = ["benchmark"] files = [ {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"}, {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, ] [[package]] name = "pycparser" version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] [[package]] name = "pygments" version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, ] [package.extras] windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" groups = ["benchmark", "dev"] files = [ {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" pluggy = ">=1.5,<2" [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-benchmark" version = "5.1.0" description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." optional = false python-versions = ">=3.9" groups = ["benchmark"] files = [ {file = "pytest-benchmark-5.1.0.tar.gz", hash = "sha256:9ea661cdc292e8231f7cd4c10b0319e56a2118e2c09d9f50e1b3d150d2aca105"}, {file = "pytest_benchmark-5.1.0-py3-none-any.whl", hash = "sha256:922de2dfa3033c227c96da942d1878191afa135a29485fb942e85dff1c592c89"}, ] [package.dependencies] py-cpuinfo = "*" pytest = ">=8.1" [package.extras] aspect = ["aspectlib"] elasticsearch = ["elasticsearch"] histogram = ["pygal", "pygaljs", "setuptools"] [[package]] name = "pytest-codspeed" version = "3.2.0" description = "Pytest plugin to create CodSpeed benchmarks" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ {file = "pytest_codspeed-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5165774424c7ab8db7e7acdb539763a0e5657996effefdf0664d7fd95158d34"}, {file = "pytest_codspeed-3.2.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bd55f92d772592c04a55209950c50880413ae46876e66bd349ef157075ca26c"}, {file = "pytest_codspeed-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf6f56067538f4892baa8d7ab5ef4e45bb59033be1ef18759a2c7fc55b32035"}, {file = "pytest_codspeed-3.2.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:39a687b05c3d145642061b45ea78e47e12f13ce510104d1a2cda00eee0e36f58"}, {file = "pytest_codspeed-3.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46a1afaaa1ac4c2ca5b0700d31ac46d80a27612961d031067d73c6ccbd8d3c2b"}, {file = "pytest_codspeed-3.2.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c48ce3af3dfa78413ed3d69d1924043aa1519048dbff46edccf8f35a25dab3c2"}, {file = "pytest_codspeed-3.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66692506d33453df48b36a84703448cb8b22953eea51f03fbb2eb758dc2bdc4f"}, {file = "pytest_codspeed-3.2.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:479774f80d0bdfafa16112700df4dbd31bf2a6757fac74795fd79c0a7b3c389b"}, {file = "pytest_codspeed-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:109f9f4dd1088019c3b3f887d003b7d65f98a7736ca1d457884f5aa293e8e81c"}, {file = "pytest_codspeed-3.2.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2f69a03b52c9bb041aec1b8ee54b7b6c37a6d0a948786effa4c71157765b6da"}, {file = "pytest_codspeed-3.2.0-py3-none-any.whl", hash = "sha256:54b5c2e986d6a28e7b0af11d610ea57bd5531cec8326abe486f1b55b09d91c39"}, {file = "pytest_codspeed-3.2.0.tar.gz", hash = "sha256:f9d1b1a3b2c69cdc0490a1e8b1ced44bffbd0e8e21d81a7160cfdd923f6e8155"}, ] [package.dependencies] cffi = ">=1.17.1" pytest = ">=3.8" rich = ">=13.8.1" [package.extras] compat = ["pytest-benchmark (>=5.0.0,<5.1.0)", "pytest-xdist (>=3.6.1,<3.7.0)"] lint = ["mypy (>=1.11.2,<1.12.0)", "ruff (>=0.6.5,<0.7.0)"] test = ["pytest (>=7.0,<8.0)", "pytest-cov (>=4.0.0,<4.1.0)"] [[package]] name = "pytest-cov" version = "6.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, ] [package.dependencies] coverage = {version = ">=7.5", extras = ["toml"]} pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "rich" version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" groups = ["dev"] files = [ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "setuptools" version = "75.8.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ {file = "setuptools-75.8.2-py3-none-any.whl", hash = "sha256:558e47c15f1811c1fa7adbd0096669bf76c1d3f433f58324df69f3f5ecac4e8f"}, {file = "setuptools-75.8.2.tar.gz", hash = "sha256:4880473a969e5f23f2a2be3646b2dfd84af9028716d398e46192f84bc36900d2"}, ] [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "ulid-py" version = "1.1.0" description = "Universally Unique Lexicographically Sortable Identifier" optional = false python-versions = "*" groups = ["benchmark"] files = [ {file = "ulid-py-1.1.0.tar.gz", hash = "sha256:dc6884be91558df077c3011b9fb0c87d1097cb8fc6534b11f310161afd5738f0"}, {file = "ulid_py-1.1.0-py2.py3-none-any.whl", hash = "sha256:b56a0f809ef90d6020b21b89a87a48edc7c03aea80e5ed5174172e82d76e3987"}, ] [[package]] name = "ulid2" version = "0.3.0" description = "ULID encoding/decoding for Python" optional = false python-versions = "*" groups = ["benchmark"] files = [ {file = "ulid2-0.3.0-py2.py3-none-any.whl", hash = "sha256:eaf0d39ad3cedb644247b69161cc87e32009aebb1420eccfd037b6a55a20329c"}, {file = "ulid2-0.3.0.tar.gz", hash = "sha256:915b18c75b735537d7d39cb1ba8fa8c05073ddef3ed2fe7d2e35426a63c0cf70"}, ] [metadata] lock-version = "2.1" python-versions = "^3.11" content-hash = "910426bb732e97ef4301c96db14751052340bbc2be10792d583eee74559bf073" ulid-transform-1.4.0/pyproject.toml000066400000000000000000000044741476254674500174330ustar00rootroot00000000000000[tool.poetry] name = "ulid-transform" version = "1.4.0" description = "Create and transform ULIDs" authors = ["J. Nick Koston "] license = "MIT" readme = "README.md" repository = "https://github.com/bluetooth-devices/ulid-transform" classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Natural Language :: English", "Operating System :: OS Independent", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", ] packages = [ { include = "ulid_transform", from = "src" }, ] [tool.poetry.build] generate-setup-file = true script = "build_ext.py" [tool.poetry.urls] "Bug Tracker" = "https://github.com/bluetooth-devices/ulid-transform/issues" "Changelog" = "https://github.com/bluetooth-devices/ulid-transform/blob/main/CHANGELOG.md" [tool.poetry.dependencies] python = "^3.11" [tool.poetry.group.dev.dependencies] pytest = ">=7,<9" pytest-cov = ">=3,<7" Cython = ">=3.0.5" setuptools = ">=65.4.1,<76.0.0" pytest-codspeed = "^3.1.2" [tool.poetry.group.benchmark.dependencies] ulid-py = "^1.1.0" ulid2 = "^0.3.0" pytest-benchmark = ">=4,<6" [tool.semantic_release] version_toml = ["pyproject.toml:tool.poetry.version"] version_variables = [ "src/ulid_transform/__init__.py:__version__", ] build_command = "pip install poetry && poetry build" [tool.pytest.ini_options] pythonpath = ["src"] [tool.coverage.run] branch = true [tool.coverage.report] exclude_lines = [ "pragma: no cover", "@overload", "if TYPE_CHECKING", "raise NotImplementedError", 'if __name__ == "__main__":', ] [tool.isort] profile = "black" known_first_party = ["ulid_transform", "tests"] [tool.mypy] check_untyped_defs = true disallow_any_generics = true disallow_incomplete_defs = true disallow_untyped_defs = true mypy_path = "src/" no_implicit_optional = true show_error_codes = true warn_unreachable = true warn_unused_ignores = true exclude = [ 'setup.py', ] [[tool.mypy.overrides]] module = "tests.*" allow_untyped_defs = true [[tool.mypy.overrides]] module = "bench.*" allow_untyped_defs = true [build-system] requires = ['setuptools>=65.4.1', 'wheel', 'Cython>=3.0.2', "poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" ulid-transform-1.4.0/renovate.json000066400000000000000000000001011476254674500172140ustar00rootroot00000000000000{ "extends": ["github>browniebroke/renovate-configs:python"] } ulid-transform-1.4.0/src/000077500000000000000000000000001476254674500152755ustar00rootroot00000000000000ulid-transform-1.4.0/src/ulid_transform/000077500000000000000000000000001476254674500203255ustar00rootroot00000000000000ulid-transform-1.4.0/src/ulid_transform/__init__.py000066400000000000000000000015251476254674500224410ustar00rootroot00000000000000__version__ = "1.4.0" try: from ._ulid_impl import ( bytes_to_ulid, bytes_to_ulid_or_none, ulid_at_time, ulid_at_time_bytes, ulid_hex, ulid_now, ulid_now_bytes, ulid_to_bytes, ulid_to_bytes_or_none, ulid_to_timestamp, ) except ImportError: from ._py_ulid_impl import ( bytes_to_ulid, bytes_to_ulid_or_none, ulid_at_time, ulid_at_time_bytes, ulid_hex, ulid_now, ulid_now_bytes, ulid_to_bytes, ulid_to_bytes_or_none, ulid_to_timestamp, ) __all__ = [ "bytes_to_ulid", "bytes_to_ulid_or_none", "ulid_at_time", "ulid_at_time_bytes", "ulid_hex", "ulid_now", "ulid_now_bytes", "ulid_to_bytes", "ulid_to_bytes_or_none", "ulid_to_timestamp", ] ulid-transform-1.4.0/src/ulid_transform/__init__.pyi000066400000000000000000000007241476254674500226120ustar00rootroot00000000000000def ulid_hex() -> str: ... def ulid_at_time_bytes(timestamp: float) -> bytes: ... def ulid_now_bytes() -> bytes: ... def ulid_now() -> str: ... def ulid_at_time(timestamp: float) -> str: ... def ulid_to_bytes(value: str) -> bytes: ... def bytes_to_ulid(value: bytes) -> str: ... def ulid_to_bytes_or_none(ulid: str | None) -> bytes | None: ... def bytes_to_ulid_or_none(ulid_bytes: bytes | None) -> str | None: ... def ulid_to_timestamp(ulid: str | bytes) -> int: ... ulid-transform-1.4.0/src/ulid_transform/_py_ulid_impl.py000066400000000000000000000240011476254674500235210ustar00rootroot00000000000000import array from random import getrandbits from time import time # From https://github.com/ahawker/ulid/blob/06289583e9de4286b4d80b4ad000d137816502ca/ulid/base32.py#L102 #: Array that maps encoded string char byte values to enable O(1) lookups. _DECODE = array.array( "B", ( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x01, 0x12, 0x13, 0x01, 0x14, 0x15, 0x00, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x01, 0x12, 0x13, 0x01, 0x14, 0x15, 0x00, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ), ) def ulid_hex() -> str: """ Generate a ULID in lowercase hex that will work for a UUID. This ulid should not be used for cryptographically secure operations. This string can be converted with https://github.com/ahawker/ulid ulid.from_uuid(uuid.UUID(ulid_hex)) """ return f"{int(time() * 1000):012x}{getrandbits(80):020x}" def ulid_at_time_bytes(timestamp: float) -> bytes: """ Generate an ULID as 16 bytes that will work for a UUID. uuid.UUID(bytes=ulid_bytes) """ return int(timestamp * 1000).to_bytes(6, byteorder="big") + int( getrandbits(80) ).to_bytes(10, byteorder="big") def ulid_now_bytes() -> bytes: """Generate an ULID as 16 bytes that will work for a UUID.""" return ulid_at_time_bytes(time()) def ulid_now() -> str: """Generate a ULID.""" return ulid_at_time(time()) def ulid_at_time(timestamp: float) -> str: """ Generate a ULID. This ulid should not be used for cryptographically secure operations. 01AN4Z07BY 79KA1307SR9X4MV3 |----------| |----------------| Timestamp Randomness 48bits 80bits This string can be loaded directly with https://github.com/ahawker/ulid import ulid_transform as ulid_util import ulid ulid.parse(ulid_util.ulid()) """ return _encode(ulid_at_time_bytes(timestamp)) def _encode(ulid_bytes: bytes) -> str: # This is base32 crockford encoding with the loop unrolled for performance # # This code is adapted from: # https://github.com/ahawker/ulid/blob/06289583e9de4286b4d80b4ad000d137816502ca/ulid/base32.py#L102 # enc = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" return ( enc[(ulid_bytes[0] & 224) >> 5] + enc[ulid_bytes[0] & 31] + enc[(ulid_bytes[1] & 248) >> 3] + enc[((ulid_bytes[1] & 7) << 2) | ((ulid_bytes[2] & 192) >> 6)] + enc[((ulid_bytes[2] & 62) >> 1)] + enc[((ulid_bytes[2] & 1) << 4) | ((ulid_bytes[3] & 240) >> 4)] + enc[((ulid_bytes[3] & 15) << 1) | ((ulid_bytes[4] & 128) >> 7)] + enc[(ulid_bytes[4] & 124) >> 2] + enc[((ulid_bytes[4] & 3) << 3) | ((ulid_bytes[5] & 224) >> 5)] + enc[ulid_bytes[5] & 31] + enc[(ulid_bytes[6] & 248) >> 3] + enc[((ulid_bytes[6] & 7) << 2) | ((ulid_bytes[7] & 192) >> 6)] + enc[(ulid_bytes[7] & 62) >> 1] + enc[((ulid_bytes[7] & 1) << 4) | ((ulid_bytes[8] & 240) >> 4)] + enc[((ulid_bytes[8] & 15) << 1) | ((ulid_bytes[9] & 128) >> 7)] + enc[(ulid_bytes[9] & 124) >> 2] + enc[((ulid_bytes[9] & 3) << 3) | ((ulid_bytes[10] & 224) >> 5)] + enc[ulid_bytes[10] & 31] + enc[(ulid_bytes[11] & 248) >> 3] + enc[((ulid_bytes[11] & 7) << 2) | ((ulid_bytes[12] & 192) >> 6)] + enc[(ulid_bytes[12] & 62) >> 1] + enc[((ulid_bytes[12] & 1) << 4) | ((ulid_bytes[13] & 240) >> 4)] + enc[((ulid_bytes[13] & 15) << 1) | ((ulid_bytes[14] & 128) >> 7)] + enc[(ulid_bytes[14] & 124) >> 2] + enc[((ulid_bytes[14] & 3) << 3) | ((ulid_bytes[15] & 224) >> 5)] + enc[ulid_bytes[15] & 31] ) def ulid_to_bytes(value: str) -> bytes: """Decode a ulid to bytes.""" if len(value) != 26: raise ValueError(f"ULID must be a 26 character string: {value}") encoded = value.encode("ascii") decoding = _DECODE return bytes( ( ((decoding[encoded[0]] << 5) | decoding[encoded[1]]) & 0xFF, ((decoding[encoded[2]] << 3) | (decoding[encoded[3]] >> 2)) & 0xFF, ( (decoding[encoded[3]] << 6) | (decoding[encoded[4]] << 1) | (decoding[encoded[5]] >> 4) ) & 0xFF, ((decoding[encoded[5]] << 4) | (decoding[encoded[6]] >> 1)) & 0xFF, ( (decoding[encoded[6]] << 7) | (decoding[encoded[7]] << 2) | (decoding[encoded[8]] >> 3) ) & 0xFF, ((decoding[encoded[8]] << 5) | (decoding[encoded[9]])) & 0xFF, ((decoding[encoded[10]] << 3) | (decoding[encoded[11]] >> 2)) & 0xFF, ( (decoding[encoded[11]] << 6) | (decoding[encoded[12]] << 1) | (decoding[encoded[13]] >> 4) ) & 0xFF, ((decoding[encoded[13]] << 4) | (decoding[encoded[14]] >> 1)) & 0xFF, ( (decoding[encoded[14]] << 7) | (decoding[encoded[15]] << 2) | (decoding[encoded[16]] >> 3) ) & 0xFF, ((decoding[encoded[16]] << 5) | (decoding[encoded[17]])) & 0xFF, ((decoding[encoded[18]] << 3) | (decoding[encoded[19]] >> 2)) & 0xFF, ( (decoding[encoded[19]] << 6) | (decoding[encoded[20]] << 1) | (decoding[encoded[21]] >> 4) ) & 0xFF, ((decoding[encoded[21]] << 4) | (decoding[encoded[22]] >> 1)) & 0xFF, ( (decoding[encoded[22]] << 7) | (decoding[encoded[23]] << 2) | (decoding[encoded[24]] >> 3) ) & 0xFF, ((decoding[encoded[24]] << 5) | (decoding[encoded[25]])) & 0xFF, ) ) def bytes_to_ulid(value: bytes) -> str: """Encode bytes to a ulid.""" if len(value) != 16: raise ValueError(f"ULID bytes must be 16 bytes: {value!r}") return _encode(value) def ulid_to_bytes_or_none(ulid: str | None) -> bytes | None: """Convert an ulid to bytes.""" if ulid is None: return None try: return ulid_to_bytes(ulid) except ValueError: return None def bytes_to_ulid_or_none(ulid_bytes: bytes | None) -> str | None: """Convert bytes to a ulid.""" if ulid_bytes is None: return None try: return bytes_to_ulid(ulid_bytes) except ValueError: return None def ulid_to_timestamp(ulid: str | bytes) -> int: """ Get the timestamp from a ULID. The returned value is in milliseconds since the UNIX epoch. """ if not isinstance(ulid, bytes): ulid_bytes = ulid_to_bytes(ulid) else: ulid_bytes = ulid return int.from_bytes(b"\x00\x00" + ulid_bytes[:6], "big") ulid-transform-1.4.0/src/ulid_transform/_ulid_impl.pyx000066400000000000000000000105311476254674500232040ustar00rootroot00000000000000# distutils: language = c++ # cython: language_level=3, c_string_type=str, c_string_encoding=ascii # The `xxx[:N]` syntax is required for two reasons: # 1. When working with "ULID bytes", the buffer may contain NULs. # 2. When working with ULID text, the buffer is exactly 26 bytes long and not NUL-terminated. # See https://github.com/cython/cython/issues/3234 from libc.stdint cimport uint8_t, uint64_t cdef extern from "ulid_wrapper.h": void _cpp_ulid(char dst[26]) nogil void _cpp_ulid_bytes(uint8_t dst[16]) nogil void _cpp_ulid_at_time(double epoch_time, char dst[26]) nogil void _cpp_ulid_at_time_bytes(double epoch_time, uint8_t dst[16]) nogil void _cpp_ulid_to_bytes(const char ulid_string[26], uint8_t dst[16]) nogil void _cpp_bytes_to_ulid(const uint8_t b[16], char * dst) nogil void _cpp_hexlify_16(const uint8_t b[16], char dst[32]) nogil uint64_t _cpp_bytes_to_timestamp(const uint8_t b[16]) nogil def ulid_hex() -> str: """Generate a ULID in lowercase hex that will work for a UUID. This ulid should not be used for cryptographically secure operations. This string can be converted with https://github.com/ahawker/ulid ulid.from_uuid(uuid.UUID(ulid_hex)) """ cdef unsigned char ulid_bytes_buf[16] _cpp_ulid_bytes(ulid_bytes_buf) cdef char ulid_hex_buf[32] _cpp_hexlify_16(ulid_bytes_buf, ulid_hex_buf) return ulid_hex_buf[:32] def ulid_now_bytes() -> bytes: """Generate an ULID as 16 bytes that will work for a UUID.""" cdef unsigned char ulid_bytes_buf[16] _cpp_ulid_bytes(ulid_bytes_buf) return ulid_bytes_buf[:16] def ulid_at_time_bytes(timestamp: float) -> bytes: """Generate an ULID as 16 bytes that will work for a UUID. uuid.UUID(bytes=ulid_bytes) """ cdef unsigned char ulid_bytes_buf[16] _cpp_ulid_at_time_bytes(timestamp, ulid_bytes_buf) return ulid_bytes_buf[:16] def ulid_now() -> str: """Generate a ULID.""" cdef char ulid_text_buf[26] _cpp_ulid(ulid_text_buf) return ulid_text_buf[:26] def ulid_at_time(timestamp: float) -> str: """Generate a ULID. This ulid should not be used for cryptographically secure operations. 01AN4Z07BY 79KA1307SR9X4MV3 |----------| |----------------| Timestamp Randomness 48bits 80bits This string can be loaded directly with https://github.com/ahawker/ulid import ulid_transform as ulid_util import ulid ulid.parse(ulid_util.ulid()) """ cdef char ulid_text_buf[26] _cpp_ulid_at_time(timestamp, ulid_text_buf) return ulid_text_buf[:26] def ulid_to_bytes(value: str) -> bytes: """Decode a ulid to bytes.""" if len(value) != 26: raise ValueError(f"ULID must be a 26 character string: {value}") cdef unsigned char ulid_bytes_buf[16] _cpp_ulid_to_bytes(value, ulid_bytes_buf) return ulid_bytes_buf[:16] def bytes_to_ulid(value: bytes) -> str: """Encode bytes to a ulid.""" if len(value) != 16: raise ValueError(f"ULID bytes must be 16 bytes: {value!r}") cdef char ulid_text_buf[26] _cpp_bytes_to_ulid(value, ulid_text_buf) return ulid_text_buf[:26] def ulid_to_bytes_or_none(ulid: str | None) -> bytes | None: """Convert an ulid to bytes.""" if ulid is None or len(ulid) != 26: return None cdef unsigned char ulid_bytes_buf[16] _cpp_ulid_to_bytes(ulid, ulid_bytes_buf) return ulid_bytes_buf[:16] def bytes_to_ulid_or_none(ulid_bytes: bytes | None) -> str | None: """Convert bytes to a ulid.""" if ulid_bytes is None or len(ulid_bytes) != 16: return None cdef char ulid_text_buf[26] _cpp_bytes_to_ulid(ulid_bytes, ulid_text_buf) return ulid_text_buf[:26] def ulid_to_timestamp(ulid: str | bytes) -> int: """ Get the timestamp from a ULID. The returned value is in milliseconds since the UNIX epoch. """ cdef unsigned char ulid_bytes_buf[16] if not isinstance(ulid, bytes): if len(ulid) != 26: raise ValueError(f"ULID must be a 26 character string: {ulid}") _cpp_ulid_to_bytes(ulid, ulid_bytes_buf) return _cpp_bytes_to_timestamp(ulid_bytes_buf) else: if len(ulid) != 16: raise ValueError(f"ULID bytes must be 16 bytes: {ulid!r}") return _cpp_bytes_to_timestamp(ulid) ulid-transform-1.4.0/src/ulid_transform/py.typed000066400000000000000000000000001476254674500220120ustar00rootroot00000000000000ulid-transform-1.4.0/src/ulid_transform/ulid.hh000066400000000000000000000004471476254674500216100ustar00rootroot00000000000000// Originally from https://github.com/suyash/ulid #ifndef ULID_HH #define ULID_HH // http://stackoverflow.com/a/23981011 #ifdef __SIZEOF_INT128__ #define ULIDUINT128 #endif #ifdef ULIDUINT128 #include "ulid_uint128.hh" #else #include "ulid_struct.hh" #endif // ULIDUINT128 #endif // ULID_HH ulid-transform-1.4.0/src/ulid_transform/ulid_struct.hh000066400000000000000000000534721476254674500232220ustar00rootroot00000000000000#ifndef ULID_STRUCT_HH #define ULID_STRUCT_HH #include #include #include #include #include #include #include #if _MSC_VER > 0 typedef uint32_t rand_t; #else typedef uint8_t rand_t; #endif namespace ulid { /** * ULID is a 16 byte Universally Unique Lexicographically Sortable Identifier * */ struct ULID { uint8_t data[16]; ULID() { // for (int i = 0 ; i < 16 ; i++) { // data[i] = 0; // } // unrolled loop data[0] = 0; data[1] = 0; data[2] = 0; data[3] = 0; data[4] = 0; data[5] = 0; data[6] = 0; data[7] = 0; data[8] = 0; data[9] = 0; data[10] = 0; data[11] = 0; data[12] = 0; data[13] = 0; data[14] = 0; data[15] = 0; } ULID(uint64_t val) { // for (int i = 0 ; i < 16 ; i++) { // data[15 - i] = static_cast(val); // val >>= 8; // } // unrolled loop data[15] = static_cast(val); val >>= 8; data[14] = static_cast(val); val >>= 8; data[13] = static_cast(val); val >>= 8; data[12] = static_cast(val); val >>= 8; data[11] = static_cast(val); val >>= 8; data[10] = static_cast(val); val >>= 8; data[9] = static_cast(val); val >>= 8; data[8] = static_cast(val); data[7] = 0; data[6] = 0; data[5] = 0; data[4] = 0; data[3] = 0; data[2] = 0; data[1] = 0; data[0] = 0; } ULID(const ULID& other) { // for (int i = 0 ; i < 16 ; i++) { // data[i] = other.data[i]; // } // unrolled loop data[0] = other.data[0]; data[1] = other.data[1]; data[2] = other.data[2]; data[3] = other.data[3]; data[4] = other.data[4]; data[5] = other.data[5]; data[6] = other.data[6]; data[7] = other.data[7]; data[8] = other.data[8]; data[9] = other.data[9]; data[10] = other.data[10]; data[11] = other.data[11]; data[12] = other.data[12]; data[13] = other.data[13]; data[14] = other.data[14]; data[15] = other.data[15]; } ULID& operator=(const ULID& other) { // for (int i = 0 ; i < 16 ; i++) { // data[i] = other.data[i]; // } // unrolled loop data[0] = other.data[0]; data[1] = other.data[1]; data[2] = other.data[2]; data[3] = other.data[3]; data[4] = other.data[4]; data[5] = other.data[5]; data[6] = other.data[6]; data[7] = other.data[7]; data[8] = other.data[8]; data[9] = other.data[9]; data[10] = other.data[10]; data[11] = other.data[11]; data[12] = other.data[12]; data[13] = other.data[13]; data[14] = other.data[14]; data[15] = other.data[15]; return *this; } ULID(ULID&& other) { // for (int i = 0 ; i < 16 ; i++) { // data[i] = other.data[i]; // other.data[i] = 0; // } // unrolled loop data[0] = other.data[0]; other.data[0] = 0; data[1] = other.data[1]; other.data[1] = 0; data[2] = other.data[2]; other.data[2] = 0; data[3] = other.data[3]; other.data[3] = 0; data[4] = other.data[4]; other.data[4] = 0; data[5] = other.data[5]; other.data[5] = 0; data[6] = other.data[6]; other.data[6] = 0; data[7] = other.data[7]; other.data[7] = 0; data[8] = other.data[8]; other.data[8] = 0; data[9] = other.data[9]; other.data[9] = 0; data[10] = other.data[10]; other.data[10] = 0; data[11] = other.data[11]; other.data[11] = 0; data[12] = other.data[12]; other.data[12] = 0; data[13] = other.data[13]; other.data[13] = 0; data[14] = other.data[14]; other.data[14] = 0; data[15] = other.data[15]; other.data[15] = 0; } ULID& operator=(ULID&& other) { // for (int i = 0 ; i < 16 ; i++) { // data[i] = other.data[i]; // other.data[i] = 0; // } // unrolled loop data[0] = other.data[0]; other.data[0] = 0; data[1] = other.data[1]; other.data[1] = 0; data[2] = other.data[2]; other.data[2] = 0; data[3] = other.data[3]; other.data[3] = 0; data[4] = other.data[4]; other.data[4] = 0; data[5] = other.data[5]; other.data[5] = 0; data[6] = other.data[6]; other.data[6] = 0; data[7] = other.data[7]; other.data[7] = 0; data[8] = other.data[8]; other.data[8] = 0; data[9] = other.data[9]; other.data[9] = 0; data[10] = other.data[10]; other.data[10] = 0; data[11] = other.data[11]; other.data[11] = 0; data[12] = other.data[12]; other.data[12] = 0; data[13] = other.data[13]; other.data[13] = 0; data[14] = other.data[14]; other.data[14] = 0; data[15] = other.data[15]; other.data[15] = 0; return *this; } }; /** * EncodeTimestamp will encode the int64_t timestamp to the passed ulid * */ inline void EncodeTimestamp(int64_t timestamp, ULID& ulid) { ulid.data[0] = static_cast(timestamp >> 40); ulid.data[1] = static_cast(timestamp >> 32); ulid.data[2] = static_cast(timestamp >> 24); ulid.data[3] = static_cast(timestamp >> 16); ulid.data[4] = static_cast(timestamp >> 8); ulid.data[5] = static_cast(timestamp); } /** * EncodeTime will encode the time point to the passed ulid * */ inline void EncodeTime(std::chrono::time_point time_point, ULID& ulid) { auto time_ms = std::chrono::time_point_cast(time_point); int64_t timestamp = time_ms.time_since_epoch().count(); EncodeTimestamp(timestamp, ulid); } /** * EncodeTimeNow will encode a ULID using the time obtained using std::time(nullptr) * */ inline void EncodeTimeNow(ULID& ulid) { auto time_now = std::chrono::system_clock::from_time_t(time(nullptr)); EncodeTime(time_now, ulid); } /** * EncodeTimeSystemClockNow will encode a ULID using the time obtained using * std::chrono::system_clock::now() by taking the timestamp in milliseconds. * */ inline void EncodeTimeSystemClockNow(ULID& ulid) { EncodeTime(std::chrono::system_clock::now(), ulid); } /** * EncodeEntropy will encode the last 10 bytes of the passed uint8_t array with * the values generated using the passed random number generator. * */ inline void EncodeEntropy(const std::function& rng, ULID& ulid) { ulid.data[6] = rng(); ulid.data[7] = rng(); ulid.data[8] = rng(); ulid.data[9] = rng(); ulid.data[10] = rng(); ulid.data[11] = rng(); ulid.data[12] = rng(); ulid.data[13] = rng(); ulid.data[14] = rng(); ulid.data[15] = rng(); } /** * EncodeEntropyRand will encode a ulid using std::rand * * std::rand returns values in [0, RAND_MAX] * */ inline void EncodeEntropyRand(ULID& ulid) { ulid.data[6] = static_cast((std::rand() * 255ull) / RAND_MAX); ulid.data[7] = static_cast((std::rand() * 255ull) / RAND_MAX); ulid.data[8] = static_cast((std::rand() * 255ull) / RAND_MAX); ulid.data[9] = static_cast((std::rand() * 255ull) / RAND_MAX); ulid.data[10] = static_cast((std::rand() * 255ull) / RAND_MAX); ulid.data[11] = static_cast((std::rand() * 255ull) / RAND_MAX); ulid.data[12] = static_cast((std::rand() * 255ull) / RAND_MAX); ulid.data[13] = static_cast((std::rand() * 255ull) / RAND_MAX); ulid.data[14] = static_cast((std::rand() * 255ull) / RAND_MAX); ulid.data[15] = static_cast((std::rand() * 255ull) / RAND_MAX); } /** * EncodeEntropyMt19937Fast will encode using std::mt19937 * with only 3 generated values. * */ inline void EncodeEntropyMt19937Fast(ULID& ulid) { static thread_local std::mt19937 gen([]() { // Use multiple entropy sources for seeding std::array seed_data = { static_cast(std::chrono::high_resolution_clock::now().time_since_epoch().count()), static_cast(std::random_device {}()), static_cast(reinterpret_cast(&gen) & 0xFFFFFFFF) }; std::seed_seq seed_seq(seed_data.begin(), seed_data.end()); return std::mt19937(seed_seq); }()); uint64_t high = (static_cast(gen()) << 32) | gen(); uint32_t low = gen(); ulid.data[6] = (high >> 40) & 0xFF; ulid.data[7] = (high >> 32) & 0xFF; ulid.data[8] = (high >> 24) & 0xFF; ulid.data[9] = (high >> 16) & 0xFF; ulid.data[10] = (high >> 8) & 0xFF; ulid.data[11] = high & 0xFF; ulid.data[12] = (low >> 24) & 0xFF; ulid.data[13] = (low >> 16) & 0xFF; ulid.data[14] = (low >> 8) & 0xFF; ulid.data[15] = low & 0xFF; } static std::uniform_int_distribution Distribution_0_255(0, 255); /** * EncodeEntropyMt19937 will encode a ulid using std::mt19937 * * It also creates a std::uniform_int_distribution to generate values in [0, 255] * */ inline void EncodeEntropyMt19937(std::mt19937& generator, ULID& ulid) { ulid.data[6] = Distribution_0_255(generator); ulid.data[7] = Distribution_0_255(generator); ulid.data[8] = Distribution_0_255(generator); ulid.data[9] = Distribution_0_255(generator); ulid.data[10] = Distribution_0_255(generator); ulid.data[11] = Distribution_0_255(generator); ulid.data[12] = Distribution_0_255(generator); ulid.data[13] = Distribution_0_255(generator); ulid.data[14] = Distribution_0_255(generator); ulid.data[15] = Distribution_0_255(generator); } /** * Encode will create an encoded ULID with a timestamp and a generator. * */ inline void Encode(std::chrono::time_point timestamp, const std::function& rng, ULID& ulid) { EncodeTime(timestamp, ulid); EncodeEntropy(rng, ulid); } /** * EncodeNowRand = EncodeTimeNow + EncodeEntropyRand. * */ inline void EncodeNowRand(ULID& ulid) { EncodeTimeNow(ulid); EncodeEntropyRand(ulid); } /** * Create will create a ULID with a timestamp and a generator. * */ inline ULID Create(std::chrono::time_point timestamp, const std::function& rng) { ULID ulid; Encode(timestamp, rng, ulid); return ulid; } /** * CreateNowRand:EncodeNowRand = Create:Encode. * */ inline ULID CreateNowRand() { ULID ulid; EncodeNowRand(ulid); return ulid; } /** * Crockford's Base32 * */ static const char Encoding[33] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; /** * MarshalTo will marshal a ULID to the passed character array. * * Implementation taken directly from oklog/ulid * (https://sourcegraph.com/github.com/oklog/ulid@0774f81f6e44af5ce5e91c8d7d76cf710e889ebb/-/blob/ulid.go#L162-190) * * timestamp:
* dst[0]: first 3 bits of data[0]
* dst[1]: last 5 bits of data[0]
* dst[2]: first 5 bits of data[1]
* dst[3]: last 3 bits of data[1] + first 2 bits of data[2]
* dst[4]: bits 3-7 of data[2]
* dst[5]: last bit of data[2] + first 4 bits of data[3]
* dst[6]: last 4 bits of data[3] + first bit of data[4]
* dst[7]: bits 2-6 of data[4]
* dst[8]: last 2 bits of data[4] + first 3 bits of data[5]
* dst[9]: last 5 bits of data[5]
* * entropy: * follows similarly, except now all components are set to 5 bits. * */ inline void MarshalTo(const ULID& ulid, char dst[26]) { // 10 byte timestamp dst[0] = Encoding[(ulid.data[0] & 224) >> 5]; dst[1] = Encoding[ulid.data[0] & 31]; dst[2] = Encoding[(ulid.data[1] & 248) >> 3]; dst[3] = Encoding[((ulid.data[1] & 7) << 2) | ((ulid.data[2] & 192) >> 6)]; dst[4] = Encoding[(ulid.data[2] & 62) >> 1]; dst[5] = Encoding[((ulid.data[2] & 1) << 4) | ((ulid.data[3] & 240) >> 4)]; dst[6] = Encoding[((ulid.data[3] & 15) << 1) | ((ulid.data[4] & 128) >> 7)]; dst[7] = Encoding[(ulid.data[4] & 124) >> 2]; dst[8] = Encoding[((ulid.data[4] & 3) << 3) | ((ulid.data[5] & 224) >> 5)]; dst[9] = Encoding[ulid.data[5] & 31]; // 16 bytes of entropy dst[10] = Encoding[(ulid.data[6] & 248) >> 3]; dst[11] = Encoding[((ulid.data[6] & 7) << 2) | ((ulid.data[7] & 192) >> 6)]; dst[12] = Encoding[(ulid.data[7] & 62) >> 1]; dst[13] = Encoding[((ulid.data[7] & 1) << 4) | ((ulid.data[8] & 240) >> 4)]; dst[14] = Encoding[((ulid.data[8] & 15) << 1) | ((ulid.data[9] & 128) >> 7)]; dst[15] = Encoding[(ulid.data[9] & 124) >> 2]; dst[16] = Encoding[((ulid.data[9] & 3) << 3) | ((ulid.data[10] & 224) >> 5)]; dst[17] = Encoding[ulid.data[10] & 31]; dst[18] = Encoding[(ulid.data[11] & 248) >> 3]; dst[19] = Encoding[((ulid.data[11] & 7) << 2) | ((ulid.data[12] & 192) >> 6)]; dst[20] = Encoding[(ulid.data[12] & 62) >> 1]; dst[21] = Encoding[((ulid.data[12] & 1) << 4) | ((ulid.data[13] & 240) >> 4)]; dst[22] = Encoding[((ulid.data[13] & 15) << 1) | ((ulid.data[14] & 128) >> 7)]; dst[23] = Encoding[(ulid.data[14] & 124) >> 2]; dst[24] = Encoding[((ulid.data[14] & 3) << 3) | ((ulid.data[15] & 224) >> 5)]; dst[25] = Encoding[ulid.data[15] & 31]; } /** * Marshal will marshal a ULID to a std::string. * */ inline std::string Marshal(const ULID& ulid) { char data[27]; data[26] = '\0'; MarshalTo(ulid, data); return std::string(data); } /** * MarshalBinaryTo will Marshal a ULID to the passed byte array * */ inline void MarshalBinaryTo(const ULID& ulid, uint8_t dst[16]) { // timestamp dst[0] = ulid.data[0]; dst[1] = ulid.data[1]; dst[2] = ulid.data[2]; dst[3] = ulid.data[3]; dst[4] = ulid.data[4]; dst[5] = ulid.data[5]; // entropy dst[6] = ulid.data[6]; dst[7] = ulid.data[7]; dst[8] = ulid.data[8]; dst[9] = ulid.data[9]; dst[10] = ulid.data[10]; dst[11] = ulid.data[11]; dst[12] = ulid.data[12]; dst[13] = ulid.data[13]; dst[14] = ulid.data[14]; dst[15] = ulid.data[15]; } /** * MarshalBinary will Marshal a ULID to a byte vector. * */ inline std::vector MarshalBinary(const ULID& ulid) { std::vector dst(16); MarshalBinaryTo(ulid, dst.data()); return dst; } /** * dec storesdecimal encodings for characters. * 0xFF indicates invalid character. * 48-57 are digits. * 65-90 are capital alphabets. * */ static const uint8_t dec[256] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0 1 2 3 4 5 6 7 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 8 9 */ 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 10(A) 11(B) 12(C) 13(D) 14(E) 15(F) 16(G) */ 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, /*17(H) 18(J) 19(K) 20(M) 21(N) */ 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF, /*22(P)23(Q)24(R) 25(S) 26(T) 27(V) 28(W) */ 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, /*29(X)30(Y)31(Z) */ 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; /** * UnmarshalFrom will unmarshal a ULID from the passed character array. * */ inline void UnmarshalFrom(const char str[26], ULID& ulid) { // timestamp ulid.data[0] = (dec[int(str[0])] << 5) | dec[int(str[1])]; ulid.data[1] = (dec[int(str[2])] << 3) | (dec[int(str[3])] >> 2); ulid.data[2] = (dec[int(str[3])] << 6) | (dec[int(str[4])] << 1) | (dec[int(str[5])] >> 4); ulid.data[3] = (dec[int(str[5])] << 4) | (dec[int(str[6])] >> 1); ulid.data[4] = (dec[int(str[6])] << 7) | (dec[int(str[7])] << 2) | (dec[int(str[8])] >> 3); ulid.data[5] = (dec[int(str[8])] << 5) | dec[int(str[9])]; // entropy ulid.data[6] = (dec[int(str[10])] << 3) | (dec[int(str[11])] >> 2); ulid.data[7] = (dec[int(str[11])] << 6) | (dec[int(str[12])] << 1) | (dec[int(str[13])] >> 4); ulid.data[8] = (dec[int(str[13])] << 4) | (dec[int(str[14])] >> 1); ulid.data[9] = (dec[int(str[14])] << 7) | (dec[int(str[15])] << 2) | (dec[int(str[16])] >> 3); ulid.data[10] = (dec[int(str[16])] << 5) | dec[int(str[17])]; ulid.data[11] = (dec[int(str[18])] << 3) | (dec[int(str[19])] >> 2); ulid.data[12] = (dec[int(str[19])] << 6) | (dec[int(str[20])] << 1) | (dec[int(str[21])] >> 4); ulid.data[13] = (dec[int(str[21])] << 4) | (dec[int(str[22])] >> 1); ulid.data[14] = (dec[int(str[22])] << 7) | (dec[int(str[23])] << 2) | (dec[int(str[24])] >> 3); ulid.data[15] = (dec[int(str[24])] << 5) | dec[int(str[25])]; } /** * Unmarshal will create a new ULID by unmarshaling the passed string. * */ inline ULID Unmarshal(const std::string& str) { ULID ulid; UnmarshalFrom(str.c_str(), ulid); return ulid; } /** * UnmarshalBinaryFrom will unmarshal a ULID from the passed byte array. * */ inline void UnmarshalBinaryFrom(const uint8_t b[16], ULID& ulid) { // timestamp ulid.data[0] = b[0]; ulid.data[1] = b[1]; ulid.data[2] = b[2]; ulid.data[3] = b[3]; ulid.data[4] = b[4]; ulid.data[5] = b[5]; // entropy ulid.data[6] = b[6]; ulid.data[7] = b[7]; ulid.data[8] = b[8]; ulid.data[9] = b[9]; ulid.data[10] = b[10]; ulid.data[11] = b[11]; ulid.data[12] = b[12]; ulid.data[13] = b[13]; ulid.data[14] = b[14]; ulid.data[15] = b[15]; } /** * Unmarshal will create a new ULID by unmarshaling the passed byte vector. * */ inline ULID UnmarshalBinary(const std::vector& b) { ULID ulid; UnmarshalBinaryFrom(b.data(), ulid); return ulid; } /** * CompareULIDs will compare two ULIDs. * returns: * -1 if ulid1 is Lexicographically before ulid2 * 1 if ulid1 is Lexicographically after ulid2 * 0 if ulid1 is same as ulid2 * */ inline int CompareULIDs(const ULID& ulid1, const ULID& ulid2) { // for (int i = 0 ; i < 16 ; i++) { // if (ulid1.data[i] != ulid2.data[i]) { // return (ulid1.data[i] < ulid2.data[i]) * -2 + 1; // } // } // unrolled loop if (ulid1.data[0] != ulid2.data[0]) { return (ulid1.data[0] < ulid2.data[0]) * -2 + 1; } if (ulid1.data[1] != ulid2.data[1]) { return (ulid1.data[1] < ulid2.data[1]) * -2 + 1; } if (ulid1.data[2] != ulid2.data[2]) { return (ulid1.data[2] < ulid2.data[2]) * -2 + 1; } if (ulid1.data[3] != ulid2.data[3]) { return (ulid1.data[3] < ulid2.data[3]) * -2 + 1; } if (ulid1.data[4] != ulid2.data[4]) { return (ulid1.data[4] < ulid2.data[4]) * -2 + 1; } if (ulid1.data[5] != ulid2.data[5]) { return (ulid1.data[5] < ulid2.data[5]) * -2 + 1; } if (ulid1.data[6] != ulid2.data[6]) { return (ulid1.data[6] < ulid2.data[6]) * -2 + 1; } if (ulid1.data[7] != ulid2.data[7]) { return (ulid1.data[7] < ulid2.data[7]) * -2 + 1; } if (ulid1.data[8] != ulid2.data[8]) { return (ulid1.data[8] < ulid2.data[8]) * -2 + 1; } if (ulid1.data[9] != ulid2.data[9]) { return (ulid1.data[9] < ulid2.data[9]) * -2 + 1; } if (ulid1.data[10] != ulid2.data[10]) { return (ulid1.data[10] < ulid2.data[10]) * -2 + 1; } if (ulid1.data[11] != ulid2.data[11]) { return (ulid1.data[11] < ulid2.data[11]) * -2 + 1; } if (ulid1.data[12] != ulid2.data[12]) { return (ulid1.data[12] < ulid2.data[12]) * -2 + 1; } if (ulid1.data[13] != ulid2.data[13]) { return (ulid1.data[13] < ulid2.data[13]) * -2 + 1; } if (ulid1.data[14] != ulid2.data[14]) { return (ulid1.data[14] < ulid2.data[14]) * -2 + 1; } if (ulid1.data[15] != ulid2.data[15]) { return (ulid1.data[15] < ulid2.data[15]) * -2 + 1; } return 0; } /** * Time will extract the timestamp used to generate a ULID * */ inline std::chrono::time_point Time(const ULID& ulid) { int64_t ans = 0; ans |= ulid.data[0]; ans <<= 8; ans |= ulid.data[1]; ans <<= 8; ans |= ulid.data[2]; ans <<= 8; ans |= ulid.data[3]; ans <<= 8; ans |= ulid.data[4]; ans <<= 8; ans |= ulid.data[5]; return std::chrono::time_point(std::chrono::milliseconds { ans }); } }; // namespace ulid #endif // ULID_STRUCT_HH ulid-transform-1.4.0/src/ulid_transform/ulid_uint128.hh000066400000000000000000000401411476254674500230750ustar00rootroot00000000000000#ifndef ULID_UINT128_HH #define ULID_UINT128_HH #include #include #include #include #include #include #include #if _MSC_VER > 0 typedef uint32_t rand_t; #else typedef uint8_t rand_t; #endif namespace ulid { /** * ULID is a 16 byte Universally Unique Lexicographically Sortable Identifier * */ typedef __uint128_t ULID; /** * EncodeTimestamp will encode the int64_t timestamp to the passed ulid * */ inline void EncodeTimestamp(int64_t timestamp, ULID& ulid) { ULID t = static_cast(timestamp >> 40); t <<= 8; t |= static_cast(timestamp >> 32); t <<= 8; t |= static_cast(timestamp >> 24); t <<= 8; t |= static_cast(timestamp >> 16); t <<= 8; t |= static_cast(timestamp >> 8); t <<= 8; t |= static_cast(timestamp); t <<= 80; ULID mask = 1; mask <<= 80; mask--; ulid = t | (ulid & mask); } /** * EncodeTime will encode the time point to the passed ulid * */ inline void EncodeTime(std::chrono::time_point time_point, ULID& ulid) { auto time_ms = std::chrono::time_point_cast(time_point); int64_t timestamp = time_ms.time_since_epoch().count(); EncodeTimestamp(timestamp, ulid); } /** * EncodeTimeNow will encode a ULID using the time obtained using std::time(nullptr) * */ inline void EncodeTimeNow(ULID& ulid) { auto time_now = std::chrono::system_clock::from_time_t(time(nullptr)); EncodeTime(time_now, ulid); } /** * EncodeTimeSystemClockNow will encode a ULID using the time obtained using * std::chrono::system_clock::now() by taking the timestamp in milliseconds. * */ inline void EncodeTimeSystemClockNow(ULID& ulid) { EncodeTime(std::chrono::system_clock::now(), ulid); } /** * EncodeEntropy will encode the last 10 bytes of the passed uint8_t array with * the values generated using the passed random number generator. * */ inline void EncodeEntropy(const std::function& rng, ULID& ulid) { ulid = (ulid >> 80) << 80; ULID e = rng(); e <<= 8; e |= rng(); e <<= 8; e |= rng(); e <<= 8; e |= rng(); e <<= 8; e |= rng(); e <<= 8; e |= rng(); e <<= 8; e |= rng(); e <<= 8; e |= rng(); e <<= 8; e |= rng(); e <<= 8; e |= rng(); ulid |= e; } /** * EncodeEntropyRand will encode a ulid using std::rand * * std::rand returns values in [0, RAND_MAX] * */ inline void EncodeEntropyRand(ULID& ulid) { ulid = (ulid >> 80) << 80; ULID e = (std::rand() * 255ull) / RAND_MAX; e <<= 8; e |= (std::rand() * 255ull) / RAND_MAX; e <<= 8; e |= (std::rand() * 255ull) / RAND_MAX; e <<= 8; e |= (std::rand() * 255ull) / RAND_MAX; e <<= 8; e |= (std::rand() * 255ull) / RAND_MAX; e <<= 8; e |= (std::rand() * 255ull) / RAND_MAX; e <<= 8; e |= (std::rand() * 255ull) / RAND_MAX; e <<= 8; e |= (std::rand() * 255ull) / RAND_MAX; e <<= 8; e |= (std::rand() * 255ull) / RAND_MAX; e <<= 8; e |= (std::rand() * 255ull) / RAND_MAX; ulid |= e; } /** * EncodeEntropyMt19937Fast will encode using std::mt19937 * with only 3 generated values. * */ inline void EncodeEntropyMt19937Fast(ULID& ulid) { static thread_local std::mt19937 gen([]() { // Use multiple entropy sources for seeding std::array seed_data = { static_cast(std::chrono::high_resolution_clock::now().time_since_epoch().count()), static_cast(std::random_device {}()), static_cast(reinterpret_cast(&gen) & 0xFFFFFFFF) }; std::seed_seq seed_seq(seed_data.begin(), seed_data.end()); return std::mt19937(seed_seq); }()); ulid = (ulid >> 80) << 80; // Clear lower 80 bits ulid |= (static_cast((static_cast(gen()) << 32) | gen()) << 16) | (gen() & 0xFFFF); } static std::uniform_int_distribution Distribution_0_255(0, 255); /** * EncodeEntropyMt19937 will encode a ulid using std::mt19937 * * It also creates a std::uniform_int_distribution to generate values in [0, 255] * */ inline void EncodeEntropyMt19937(std::mt19937& generator, ULID& ulid) { ulid = (ulid >> 80) << 80; ULID e = Distribution_0_255(generator); e <<= 8; e |= Distribution_0_255(generator); e <<= 8; e |= Distribution_0_255(generator); e <<= 8; e |= Distribution_0_255(generator); e <<= 8; e |= Distribution_0_255(generator); e <<= 8; e |= Distribution_0_255(generator); e <<= 8; e |= Distribution_0_255(generator); e <<= 8; e |= Distribution_0_255(generator); e <<= 8; e |= Distribution_0_255(generator); e <<= 8; e |= Distribution_0_255(generator); ulid |= e; } /** * Encode will create an encoded ULID with a timestamp and a generator. * */ inline void Encode(std::chrono::time_point timestamp, const std::function& rng, ULID& ulid) { EncodeTime(timestamp, ulid); EncodeEntropy(rng, ulid); } /** * EncodeNowRand = EncodeTimeNow + EncodeEntropyRand. * */ inline void EncodeNowRand(ULID& ulid) { EncodeTimeNow(ulid); EncodeEntropyRand(ulid); } /** * Create will create a ULID with a timestamp and a generator. * */ inline ULID Create(std::chrono::time_point timestamp, const std::function& rng) { ULID ulid = 0; Encode(timestamp, rng, ulid); return ulid; } /** * CreateNowRand:EncodeNowRand = Create:Encode. * */ inline ULID CreateNowRand() { ULID ulid = 0; EncodeNowRand(ulid); return ulid; } /** * Crockford's Base32 * */ static const char Encoding[33] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; /** * MarshalTo will marshal a ULID to the passed character array. * * Implementation taken directly from oklog/ulid * (https://sourcegraph.com/github.com/oklog/ulid@0774f81f6e44af5ce5e91c8d7d76cf710e889ebb/-/blob/ulid.go#L162-190) * * timestamp: * dst[0]: first 3 bits of data[0] * dst[1]: last 5 bits of data[0] * dst[2]: first 5 bits of data[1] * dst[3]: last 3 bits of data[1] + first 2 bits of data[2] * dst[4]: bits 3-7 of data[2] * dst[5]: last bit of data[2] + first 4 bits of data[3] * dst[6]: last 4 bits of data[3] + first bit of data[4] * dst[7]: bits 2-6 of data[4] * dst[8]: last 2 bits of data[4] + first 3 bits of data[5] * dst[9]: last 5 bits of data[5] * * entropy: * follows similarly, except now all components are set to 5 bits. * */ inline void MarshalTo(const ULID& ulid, char dst[26]) { // 10 byte timestamp dst[0] = Encoding[(static_cast(ulid >> 120) & 224) >> 5]; dst[1] = Encoding[static_cast(ulid >> 120) & 31]; dst[2] = Encoding[(static_cast(ulid >> 112) & 248) >> 3]; dst[3] = Encoding[((static_cast(ulid >> 112) & 7) << 2) | ((static_cast(ulid >> 104) & 192) >> 6)]; dst[4] = Encoding[(static_cast(ulid >> 104) & 62) >> 1]; dst[5] = Encoding[((static_cast(ulid >> 104) & 1) << 4) | ((static_cast(ulid >> 96) & 240) >> 4)]; dst[6] = Encoding[((static_cast(ulid >> 96) & 15) << 1) | ((static_cast(ulid >> 88) & 128) >> 7)]; dst[7] = Encoding[(static_cast(ulid >> 88) & 124) >> 2]; dst[8] = Encoding[((static_cast(ulid >> 88) & 3) << 3) | ((static_cast(ulid >> 80) & 224) >> 5)]; dst[9] = Encoding[static_cast(ulid >> 80) & 31]; // 16 bytes of entropy dst[10] = Encoding[(static_cast(ulid >> 72) & 248) >> 3]; dst[11] = Encoding[((static_cast(ulid >> 72) & 7) << 2) | ((static_cast(ulid >> 64) & 192) >> 6)]; dst[12] = Encoding[(static_cast(ulid >> 64) & 62) >> 1]; dst[13] = Encoding[((static_cast(ulid >> 64) & 1) << 4) | ((static_cast(ulid >> 56) & 240) >> 4)]; dst[14] = Encoding[((static_cast(ulid >> 56) & 15) << 1) | ((static_cast(ulid >> 48) & 128) >> 7)]; dst[15] = Encoding[(static_cast(ulid >> 48) & 124) >> 2]; dst[16] = Encoding[((static_cast(ulid >> 48) & 3) << 3) | ((static_cast(ulid >> 40) & 224) >> 5)]; dst[17] = Encoding[static_cast(ulid >> 40) & 31]; dst[18] = Encoding[(static_cast(ulid >> 32) & 248) >> 3]; dst[19] = Encoding[((static_cast(ulid >> 32) & 7) << 2) | ((static_cast(ulid >> 24) & 192) >> 6)]; dst[20] = Encoding[(static_cast(ulid >> 24) & 62) >> 1]; dst[21] = Encoding[((static_cast(ulid >> 24) & 1) << 4) | ((static_cast(ulid >> 16) & 240) >> 4)]; dst[22] = Encoding[((static_cast(ulid >> 16) & 15) << 1) | ((static_cast(ulid >> 8) & 128) >> 7)]; dst[23] = Encoding[(static_cast(ulid >> 8) & 124) >> 2]; dst[24] = Encoding[((static_cast(ulid >> 8) & 3) << 3) | (((static_cast(ulid)) & 224) >> 5)]; dst[25] = Encoding[(static_cast(ulid)) & 31]; } /** * Marshal will marshal a ULID to a std::string. * */ inline std::string Marshal(const ULID& ulid) { char data[27]; data[26] = '\0'; MarshalTo(ulid, data); return std::string(data); } /** * MarshalBinaryTo will Marshal a ULID to the passed byte array * */ inline void MarshalBinaryTo(const ULID& ulid, uint8_t dst[16]) { // timestamp dst[0] = static_cast(ulid >> 120); dst[1] = static_cast(ulid >> 112); dst[2] = static_cast(ulid >> 104); dst[3] = static_cast(ulid >> 96); dst[4] = static_cast(ulid >> 88); dst[5] = static_cast(ulid >> 80); // entropy dst[6] = static_cast(ulid >> 72); dst[7] = static_cast(ulid >> 64); dst[8] = static_cast(ulid >> 56); dst[9] = static_cast(ulid >> 48); dst[10] = static_cast(ulid >> 40); dst[11] = static_cast(ulid >> 32); dst[12] = static_cast(ulid >> 24); dst[13] = static_cast(ulid >> 16); dst[14] = static_cast(ulid >> 8); dst[15] = static_cast(ulid); } /** * MarshalBinary will Marshal a ULID to a byte vector. * */ inline std::vector MarshalBinary(const ULID& ulid) { std::vector dst(16); MarshalBinaryTo(ulid, dst.data()); return dst; } /** * dec storesdecimal encodings for characters. * 0xFF indicates invalid character. * 48-57 are digits. * 65-90 are capital alphabets. * */ static const uint8_t dec[256] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0 1 2 3 4 5 6 7 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 8 9 */ 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 10(A) 11(B) 12(C) 13(D) 14(E) 15(F) 16(G) */ 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, /*17(H) 18(J) 19(K) 20(M) 21(N) */ 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF, /*22(P)23(Q)24(R) 25(S) 26(T) 27(V) 28(W) */ 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, /*29(X)30(Y)31(Z) */ 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; /** * UnmarshalFrom will unmarshal a ULID from the passed character array. * */ inline void UnmarshalFrom(const char str[26], ULID& ulid) { // timestamp ulid = (dec[int(str[0])] << 5) | dec[int(str[1])]; ulid <<= 8; ulid |= (dec[int(str[2])] << 3) | (dec[int(str[3])] >> 2); ulid <<= 8; ulid |= (dec[int(str[3])] << 6) | (dec[int(str[4])] << 1) | (dec[int(str[5])] >> 4); ulid <<= 8; ulid |= (dec[int(str[5])] << 4) | (dec[int(str[6])] >> 1); ulid <<= 8; ulid |= (dec[int(str[6])] << 7) | (dec[int(str[7])] << 2) | (dec[int(str[8])] >> 3); ulid <<= 8; ulid |= (dec[int(str[8])] << 5) | dec[int(str[9])]; // entropy ulid <<= 8; ulid |= (dec[int(str[10])] << 3) | (dec[int(str[11])] >> 2); ulid <<= 8; ulid |= (dec[int(str[11])] << 6) | (dec[int(str[12])] << 1) | (dec[int(str[13])] >> 4); ulid <<= 8; ulid |= (dec[int(str[13])] << 4) | (dec[int(str[14])] >> 1); ulid <<= 8; ulid |= (dec[int(str[14])] << 7) | (dec[int(str[15])] << 2) | (dec[int(str[16])] >> 3); ulid <<= 8; ulid |= (dec[int(str[16])] << 5) | dec[int(str[17])]; ulid <<= 8; ulid |= (dec[int(str[18])] << 3) | (dec[int(str[19])] >> 2); ulid <<= 8; ulid |= (dec[int(str[19])] << 6) | (dec[int(str[20])] << 1) | (dec[int(str[21])] >> 4); ulid <<= 8; ulid |= (dec[int(str[21])] << 4) | (dec[int(str[22])] >> 1); ulid <<= 8; ulid |= (dec[int(str[22])] << 7) | (dec[int(str[23])] << 2) | (dec[int(str[24])] >> 3); ulid <<= 8; ulid |= (dec[int(str[24])] << 5) | dec[int(str[25])]; } /** * Unmarshal will create a new ULID by unmarshaling the passed string. * */ inline ULID Unmarshal(const std::string& str) { ULID ulid; UnmarshalFrom(str.c_str(), ulid); return ulid; } /** * UnmarshalBinaryFrom will unmarshal a ULID from the passed byte array. * */ inline void UnmarshalBinaryFrom(const uint8_t b[16], ULID& ulid) { // timestamp ulid = b[0]; ulid <<= 8; ulid |= b[1]; ulid <<= 8; ulid |= b[2]; ulid <<= 8; ulid |= b[3]; ulid <<= 8; ulid |= b[4]; ulid <<= 8; ulid |= b[5]; // entropy ulid <<= 8; ulid |= b[6]; ulid <<= 8; ulid |= b[7]; ulid <<= 8; ulid |= b[8]; ulid <<= 8; ulid |= b[9]; ulid <<= 8; ulid |= b[10]; ulid <<= 8; ulid |= b[11]; ulid <<= 8; ulid |= b[12]; ulid <<= 8; ulid |= b[13]; ulid <<= 8; ulid |= b[14]; ulid <<= 8; ulid |= b[15]; } /** * Unmarshal will create a new ULID by unmarshaling the passed byte vector. * */ inline ULID UnmarshalBinary(const std::vector& b) { ULID ulid; UnmarshalBinaryFrom(b.data(), ulid); return ulid; } /** * CompareULIDs will compare two ULIDs. * returns: * -1 if ulid1 is Lexicographically before ulid2 * 1 if ulid1 is Lexicographically after ulid2 * 0 if ulid1 is same as ulid2 * */ inline int CompareULIDs(const ULID& ulid1, const ULID& ulid2) { return -2 * (ulid1 < ulid2) - 1 * (ulid1 == ulid2) + 1; } /** * Time will extract the timestamp used to generate a ULID * */ inline std::chrono::time_point Time(const ULID& ulid) { int64_t ans = 0; ans |= static_cast(ulid >> 120); ans <<= 8; ans |= static_cast(ulid >> 112); ans <<= 8; ans |= static_cast(ulid >> 104); ans <<= 8; ans |= static_cast(ulid >> 96); ans <<= 8; ans |= static_cast(ulid >> 88); ans <<= 8; ans |= static_cast(ulid >> 80); return std::chrono::time_point(std::chrono::milliseconds { ans }); } }; // namespace ulid #endif // ULID_UINT128_HH ulid-transform-1.4.0/src/ulid_transform/ulid_wrapper.cpp000066400000000000000000000055201476254674500235300ustar00rootroot00000000000000#include "ulid_wrapper.h" #include "ulid.hh" /** * Generate a new text ULID and write it to the provided buffer. * The buffer is NOT null-terminated. */ void _cpp_ulid(char dst[26]) { ulid::ULID ulid; ulid::EncodeTimeSystemClockNow(ulid); ulid::EncodeEntropyMt19937Fast(ulid); ulid::MarshalTo(ulid, dst); } /** * Generate a new binary ULID and write it to the provided buffer. */ void _cpp_ulid_bytes(uint8_t dst[16]) { ulid::ULID ulid; ulid::EncodeTimeSystemClockNow(ulid); ulid::EncodeEntropyMt19937Fast(ulid); ulid::MarshalBinaryTo(ulid, dst); } /** * Generate a new text ULID at the provided epoch time and write it to the provided buffer. * The buffer is NOT null-terminated. */ void _cpp_ulid_at_time(double epoch_time, char dst[26]) { ulid::ULID ulid; ulid::EncodeTimestamp(static_cast(epoch_time * 1000), ulid); ulid::EncodeEntropyMt19937Fast(ulid); ulid::MarshalTo(ulid, dst); } /** * Generate a new binary ULID at the provided epoch time and write it to the provided buffer. */ void _cpp_ulid_at_time_bytes(double epoch_time, uint8_t dst[16]) { ulid::ULID ulid; ulid::EncodeTimestamp(static_cast(epoch_time * 1000), ulid); ulid::EncodeEntropyMt19937Fast(ulid); ulid::MarshalBinaryTo(ulid, dst); } /** * Convert a text ULID to a binary ULID. * The buffer passed in must contain at least 26 bytes. * Invalid data will result in undefined behavior. */ void _cpp_ulid_to_bytes(const char* ulid_string, uint8_t dst[16]) { ulid::ULID ulid; ulid::UnmarshalFrom(ulid_string, ulid); ulid::MarshalBinaryTo(ulid, dst); } /** * Convert a binary ULID to a text ULID. * The buffer passed in must contain at least 16 bytes. * The output buffer will NOT be null-terminated. */ void _cpp_bytes_to_ulid(const uint8_t b[16], char dst[26]) { ulid::ULID ulid; ulid::UnmarshalBinaryFrom(b, ulid); ulid::MarshalTo(ulid, dst); } /** * Convert a buffer of exactly 16 bytes to 32 hex characters. * The output buffer will NOT be null-terminated. */ void _cpp_hexlify_16(const uint8_t b[16], char dst[32]) { static const char hexdigits[17] = "0123456789abcdef"; int in_index, out_index; for (in_index = out_index = 0; in_index < 16; in_index++) { uint8_t c = b[in_index]; dst[out_index++] = hexdigits[c >> 4]; dst[out_index++] = hexdigits[c & 0x0f]; } } /** * Interpret the first 6 bytes of a binary ULID as a timestamp. */ uint64_t _cpp_bytes_to_timestamp(const uint8_t b[16]) { uint64_t timestamp = 0; timestamp |= static_cast(b[0]) << 40; timestamp |= static_cast(b[1]) << 32; timestamp |= static_cast(b[2]) << 24; timestamp |= static_cast(b[3]) << 16; timestamp |= static_cast(b[4]) << 8; timestamp |= static_cast(b[5]); return timestamp; } ulid-transform-1.4.0/src/ulid_transform/ulid_wrapper.h000066400000000000000000000007711476254674500232000ustar00rootroot00000000000000#ifndef ULID_WRAPPER_H #define ULID_WRAPPER_H #include void _cpp_ulid(char dst[26]); void _cpp_ulid_bytes(uint8_t dst[16]); void _cpp_ulid_at_time(double epoch_time, char dst[26]); void _cpp_ulid_at_time_bytes(double epoch_time, uint8_t dst[16]); void _cpp_ulid_to_bytes(const char* ulid_string, uint8_t dst[16]); void _cpp_bytes_to_ulid(const uint8_t b[16], char dst[26]); void _cpp_hexlify_16(const uint8_t b[16], char dst[32]); uint64_t _cpp_bytes_to_timestamp(const uint8_t b[16]); #endif ulid-transform-1.4.0/tests/000077500000000000000000000000001476254674500156505ustar00rootroot00000000000000ulid-transform-1.4.0/tests/__init__.py000066400000000000000000000000001476254674500177470ustar00rootroot00000000000000ulid-transform-1.4.0/tests/benchmarks/000077500000000000000000000000001476254674500177655ustar00rootroot00000000000000ulid-transform-1.4.0/tests/benchmarks/test_ulid.py000066400000000000000000000006651476254674500223420ustar00rootroot00000000000000from pytest_codspeed import BenchmarkFixture from ulid_transform import ulid_at_time, ulid_at_time_bytes, ulid_now def test_ulid_now(benchmark: BenchmarkFixture) -> None: benchmark(ulid_now) def test_ulid_at_time(benchmark: BenchmarkFixture) -> None: @benchmark def _(): ulid_at_time(1) def test_ulid_at_time_bytes(benchmark: BenchmarkFixture) -> None: @benchmark def _(): ulid_at_time_bytes(1) ulid-transform-1.4.0/tests/conftest.py000066400000000000000000000000611476254674500200440ustar00rootroot00000000000000from tests.utils import impl __all__ = ["impl"] ulid-transform-1.4.0/tests/test_cython.py000066400000000000000000000005661476254674500205740ustar00rootroot00000000000000import os import pytest def test_require_cython(): """Fail if REQUIRE_CYTHON is set and the Cython module is not available.""" if os.environ.get("REQUIRE_CYTHON", "").lower() not in ("1", "true", "yes"): pytest.skip("REQUIRE_CYTHON is not truthy") import ulid_transform._ulid_impl as c_impl assert repr(c_impl.ulid_now).startswith("