pax_global_header00006660000000000000000000000064146522710070014516gustar00rootroot0000000000000052 comment=a5291fbb725b477c24722126d31d30e1bda3262d ulid-transform-0.13.1/000077500000000000000000000000001465227100700145465ustar00rootroot00000000000000ulid-transform-0.13.1/.all-contributorsrc000066400000000000000000000004421465227100700203770ustar00rootroot00000000000000{ "projectName": "ulid-transform", "projectOwner": "bdraco", "repoType": "github", "repoHost": "https://github.com", "files": ["README.md"], "imageSize": 80, "commit": true, "commitConvention": "angular", "contributors": [], "contributorsPerLine": 7, "skipCi": true } ulid-transform-0.13.1/.copier-answers.yml000066400000000000000000000010101465227100700203000ustar00rootroot00000000000000# 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: bdraco 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-0.13.1/.editorconfig000066400000000000000000000004441465227100700172250ustar00rootroot00000000000000# 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-0.13.1/.flake8000066400000000000000000000000561465227100700157220ustar00rootroot00000000000000[flake8] exclude = docs max-line-length = 120 ulid-transform-0.13.1/.github/000077500000000000000000000000001465227100700161065ustar00rootroot00000000000000ulid-transform-0.13.1/.github/FUNDING.yml000066400000000000000000000000231465227100700177160ustar00rootroot00000000000000github: ["bdraco"] ulid-transform-0.13.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001465227100700202715ustar00rootroot00000000000000ulid-transform-0.13.1/.github/ISSUE_TEMPLATE/1-bug_report.md000066400000000000000000000004221465227100700231170ustar00rootroot00000000000000--- 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-0.13.1/.github/ISSUE_TEMPLATE/2-feature-request.md000066400000000000000000000006721465227100700241000ustar00rootroot00000000000000--- 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-0.13.1/.github/labels.toml000066400000000000000000000035151465227100700202510ustar00rootroot00000000000000[breaking] color = "ffcc00" name = "breaking" description = "Breaking change." [bug] color = "d73a4a" name = "bug" description = "Something isn't working" [dependencies] color = "0366d6" name = "dependencies" description = "Pull requests that update a dependency file" [github_actions] color = "000000" name = "github_actions" description = "Update of github actions" [documentation] color = "1bc4a5" name = "documentation" description = "Improvements or additions to documentation" [duplicate] color = "cfd3d7" name = "duplicate" description = "This issue or pull request already exists" [enhancement] color = "a2eeef" name = "enhancement" description = "New feature or request" ["good first issue"] color = "7057ff" name = "good first issue" description = "Good for newcomers" ["help wanted"] color = "008672" name = "help wanted" description = "Extra attention is needed" [invalid] color = "e4e669" name = "invalid" description = "This doesn't seem right" [nochangelog] color = "555555" name = "nochangelog" description = "Exclude pull requests from changelog" [question] color = "d876e3" name = "question" description = "Further information is requested" [removed] color = "e99695" name = "removed" description = "Removed piece of functionalities." [tests] color = "bfd4f2" name = "tests" description = "CI, CD and testing related changes" [wontfix] color = "ffffff" name = "wontfix" description = "This will not be worked on" [discussion] color = "c2e0c6" name = "discussion" description = "Some discussion around the project" [hacktoberfest] color = "ffa663" name = "hacktoberfest" description = "Good issues for Hacktoberfest" [answered] color = "0ee2b6" name = "answered" description = "Automatically closes as answered after a delay" [waiting] color = "5f7972" name = "waiting" description = "Automatically closes if no answer after a delay" ulid-transform-0.13.1/.github/workflows/000077500000000000000000000000001465227100700201435ustar00rootroot00000000000000ulid-transform-0.13.1/.github/workflows/ci.yml000066400000000000000000000146471465227100700212750ustar00rootroot00000000000000name: 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.10" - 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@v5.3.1 test: strategy: fail-fast: false matrix: python-version: - "3.10" - "3.11" - "3.12" 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 }} - uses: snok/install-poetry@v1.3.3 - 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@v4 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.12" 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.3.3 - 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 release: runs-on: ubuntu-latest environment: release if: github.ref == 'refs/heads/main' needs: - test - test32bit - lint - commitlint steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # Run semantic release: # - Update CHANGELOG.md # - Update version in code # - Create git tag # - Create GitHub release # - Publish to PyPI - name: Python Semantic Release uses: python-semantic-release/python-semantic-release@v7.34.6 with: github_token: ${{ secrets.GITHUB_TOKEN }} pypi_token: ${{ secrets.PYPI_TOKEN }} build_wheels: needs: [release] name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: os: - macos-latest - ubuntu-latest - windows-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 ref: "main" # Used to host cibuildwheel - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install python-semantic-release run: python3 -m pip install python-semantic-release==7.34.6 - name: Get Release Tag id: release_tag env: PYTHONUTF8: 1 shell: bash run: | echo "::set-output name=newest_release_tag::$(semantic-release print-version --current)" - uses: actions/checkout@v4 with: ref: "v${{ steps.release_tag.outputs.newest_release_tag }}" fetch-depth: 0 - name: Install cibuildwheel run: python -m pip install cibuildwheel==2.19.2 - name: Build wheels run: python -m cibuildwheel --output-dir wheelhouse # to supply options, put them in 'env', like: env: CIBW_SKIP: cp36-* cp37-* cp38-* cp39-* pp36-* pp37-* pp38-* pp39-* CIBW_BEFORE_ALL_LINUX: apt-get install -y gcc || yum install -y gcc || apk add gcc CIBW_BUILD_VERBOSITY: 3 REQUIRE_CYTHON: 1 - uses: actions/upload-artifact@v3 with: path: ./wheelhouse/*.whl upload_pypi: needs: [build_wheels] runs-on: ubuntu-latest environment: release steps: - uses: actions/download-artifact@v3 with: # unpacks default artifact into dist/ # if `name: artifact` is omitted, the action will create extra parent dir name: artifact path: dist - uses: pypa/gh-action-pypi-publish@v1.5.0 with: user: __token__ password: ${{ secrets.PYPI_TOKEN }} # To test: repository_url: https://test.pypi.org/legacy/ ulid-transform-0.13.1/.github/workflows/hacktoberfest.yml000066400000000000000000000005341465227100700235140ustar00rootroot00000000000000name: 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-0.13.1/.github/workflows/issue-manager.yml000066400000000000000000000013401465227100700234240ustar00rootroot00000000000000name: 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.4.0 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-0.13.1/.github/workflows/labels.yml000066400000000000000000000007741465227100700221400ustar00rootroot00000000000000name: 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-0.13.1/.gitignore000066400000000000000000000042551465227100700165440ustar00rootroot00000000000000# 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-0.13.1/.gitpod.yml000066400000000000000000000003061465227100700166340ustar00rootroot00000000000000tasks: - 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-0.13.1/.pre-commit-config.yaml000066400000000000000000000036271465227100700210370ustar00rootroot00000000000000# 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: [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: v3.28.0 hooks: - id: commitizen stages: [commit-msg] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.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: 1.8.0 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"] - repo: https://github.com/asottile/pyupgrade rev: v3.17.0 hooks: - id: pyupgrade args: [--py39-plus] - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.5.5 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - id: ruff-format - repo: https://github.com/codespell-project/codespell rev: v2.3.0 hooks: - id: codespell args: - --ignore-words-list=ans - repo: https://github.com/PyCQA/flake8 rev: 7.1.0 hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.11.0 hooks: - id: mypy additional_dependencies: [] exclude: "src/ulid_transform/__init__.pyi" - repo: https://github.com/PyCQA/bandit rev: 1.7.9 hooks: - id: bandit args: [-x, "tests,bench"] ulid-transform-0.13.1/CHANGELOG.md000066400000000000000000000155221465227100700163640ustar00rootroot00000000000000# Changelog ## v0.13.1 (2024-07-30) ### Fix * Add pyi stubs for cython implementation ([#46](https://github.com/bdraco/ulid-transform/issues/46)) ([`dedaa87`](https://github.com/bdraco/ulid-transform/commit/dedaa87d9e5df4ed037bb0a95c971a6e6e376439)) ## v0.13.0 (2024-07-29) ### Feature * Improve performance of C implementation ([#43](https://github.com/bdraco/ulid-transform/issues/43)) ([`f07d838`](https://github.com/bdraco/ulid-transform/commit/f07d838169743c4e68f344a04b6541a6a8fee239)) ## v0.12.0 (2024-07-26) ### Feature * Improve testability, keep implementations in sync ([#40](https://github.com/bdraco/ulid-transform/issues/40)) ([`975079d`](https://github.com/bdraco/ulid-transform/commit/975079d682ffb6039454d3c041867e0d14a9f646)) ## v0.11.1 (2024-07-26) ### Fix * Cython build, add a test to verify the Cython module is available ([#41](https://github.com/bdraco/ulid-transform/issues/41)) ([`7187b35`](https://github.com/bdraco/ulid-transform/commit/7187b357b395b9c1716c8beaa5fc4db389fece18)) ## v0.11.0 (2024-07-25) ### Feature * Add fast ULID bytes functions ([#37](https://github.com/bdraco/ulid-transform/issues/37)) ([`57b58ab`](https://github.com/bdraco/ulid-transform/commit/57b58ab269dcb97dbc905925861c3a25daf7e114)) ## v0.10.2 (2024-07-25) ### Fix * Fix error message in Cython `bytes_to_ulid` ([#36](https://github.com/bdraco/ulid-transform/issues/36)) ([`d8b5462`](https://github.com/bdraco/ulid-transform/commit/d8b54622d1445916f08b61ff3aad34e61bfbb986)) ## v0.10.1 (2024-07-05) ### Fix * Wheel builds ([#32](https://github.com/bdraco/ulid-transform/issues/32)) ([`87b511e`](https://github.com/bdraco/ulid-transform/commit/87b511e1b00b8fa07ef7a18a5b5012e8df943441)) ## v0.10.0 (2024-07-05) ### Feature * Add new fast or_none functions ([#30](https://github.com/bdraco/ulid-transform/issues/30)) ([`4d1d1ee`](https://github.com/bdraco/ulid-transform/commit/4d1d1ee398f791bd35a64941d3eb838cf1ef705e)) ## v0.9.0 (2023-10-18) ### Feature * Build wheel with latest cpython release ([#25](https://github.com/bdraco/ulid-transform/issues/25)) ([`8bc67fe`](https://github.com/bdraco/ulid-transform/commit/8bc67fe8f2c56a9aacdede63331afe6c54a1528d)) ### Fix * Reduce size of wheels by excluding generated .cpp files ([#24](https://github.com/bdraco/ulid-transform/issues/24)) ([`29e0ff4`](https://github.com/bdraco/ulid-transform/commit/29e0ff474f729adea32c10a3d0adfc7801b5e892)) ## v0.8.1 (2023-08-27) ### Fix * Rebuild wheels with cython 3.0.2 ([#22](https://github.com/bdraco/ulid-transform/issues/22)) ([`8d78432`](https://github.com/bdraco/ulid-transform/commit/8d78432cf81b1a2e5230dd434b50c21365686548)) ## v0.8.0 (2023-07-24) ### Feature * Initial cpython 3.12 support ([#21](https://github.com/bdraco/ulid-transform/issues/21)) ([`7f7e8b9`](https://github.com/bdraco/ulid-transform/commit/7f7e8b90d3a7a529a58d00e66be8494a18476842)) ## v0.7.2 (2023-05-01) ### Fix * Ensure windows wheel work with older versions ([#20](https://github.com/bdraco/ulid-transform/issues/20)) ([`8a440e3`](https://github.com/bdraco/ulid-transform/commit/8a440e3f818b3af8a694544755d55f1c221ada3a)) ## v0.7.1 (2023-05-01) ### Fix * Missing decode for _bytes_to_ulid ([#19](https://github.com/bdraco/ulid-transform/issues/19)) ([`ddf6433`](https://github.com/bdraco/ulid-transform/commit/ddf6433554ca3d4fef6500b84e43adf475a794bb)) ## v0.7.0 (2023-04-23) ### Feature * Add a bytes_to_ulid cpp version ([#18](https://github.com/bdraco/ulid-transform/issues/18)) ([`fa1c62c`](https://github.com/bdraco/ulid-transform/commit/fa1c62c97be608390a5e42de5712382ee8ec86e9)) ## v0.6.3 (2023-04-10) ### Fix * Additional 32bit time fixes ([#17](https://github.com/bdraco/ulid-transform/issues/17)) ([`2f5f1be`](https://github.com/bdraco/ulid-transform/commit/2f5f1be3fe63bb63b57e158c32ab5e9d7ab16b9c)) ## v0.6.2 (2023-04-09) ### Fix * Apply 32bit cast for for random data ([#16](https://github.com/bdraco/ulid-transform/issues/16)) ([`1e1d62a`](https://github.com/bdraco/ulid-transform/commit/1e1d62aa961178d6416e4ac82cc0634b06260ad4)) ## v0.6.1 (2023-04-09) ### Fix * Apply 32bit time fix ([#15](https://github.com/bdraco/ulid-transform/issues/15)) ([`26347f0`](https://github.com/bdraco/ulid-transform/commit/26347f0067be94ba36607ea9b875b5d1354e6002)) ## v0.6.0 (2023-04-06) ### Feature * Reflect the invalid value when raising ValueError ([#13](https://github.com/bdraco/ulid-transform/issues/13)) ([`6a022fb`](https://github.com/bdraco/ulid-transform/commit/6a022fb4084e3a007c469e6e2993ccb3621c4271)) ## v0.5.1 (2023-03-22) ### Fix * Specify python version to build wheels ([#12](https://github.com/bdraco/ulid-transform/issues/12)) ([`9263de0`](https://github.com/bdraco/ulid-transform/commit/9263de0f70a4198cfcb2ba4a8f44440a7841e407)) ## v0.5.0 (2023-03-22) ### Feature * Wheels for macOS and Windows ([#11](https://github.com/bdraco/ulid-transform/issues/11)) ([`086852e`](https://github.com/bdraco/ulid-transform/commit/086852e57250994d0f3c9faedabf2df6aeb9e789)) ## v0.4.2 (2023-03-13) ### Fix * Ulid_now on 32bit ([#10](https://github.com/bdraco/ulid-transform/issues/10)) ([`c8f7dd7`](https://github.com/bdraco/ulid-transform/commit/c8f7dd790f987ca4310dd155a78fff263adf0cfc)) ## v0.4.1 (2023-03-13) ### Fix * Random as 0 on 32 bit arch ([#9](https://github.com/bdraco/ulid-transform/issues/9)) ([`c9fa4f3`](https://github.com/bdraco/ulid-transform/commit/c9fa4f32c12eabea67da14d0c32d9a5ac65f2842)) ## v0.4.0 (2023-03-01) ### Feature * Add bytes_to_ulid ([#8](https://github.com/bdraco/ulid-transform/issues/8)) ([`c9a57ef`](https://github.com/bdraco/ulid-transform/commit/c9a57ef2d68886d37e03dd9d884a51c35a5c1e12)) ## v0.3.1 (2023-03-01) ### Fix * Encode timestamps correctly in cpp implementation ([#7](https://github.com/bdraco/ulid-transform/issues/7)) ([`c7fedc3`](https://github.com/bdraco/ulid-transform/commit/c7fedc350bf9848dc38af362e33819fac4036a9e)) ## v0.3.0 (2023-02-28) ### Feature * Add examples ([#5](https://github.com/bdraco/ulid-transform/issues/5)) ([`be30a13`](https://github.com/bdraco/ulid-transform/commit/be30a133b3a03b1e6704954cf0c59f4fb64d4b7c)) ## v0.2.1 (2023-02-28) ### Fix * Drop fast-ulid for cython ([#4](https://github.com/bdraco/ulid-transform/issues/4)) ([`8d72d6b`](https://github.com/bdraco/ulid-transform/commit/8d72d6b58d306d722f096a5b3697d4365eae397d)) ## v0.2.0 (2023-02-28) ### Feature * Enable cython builds ([#3](https://github.com/bdraco/ulid-transform/issues/3)) ([`154bf0c`](https://github.com/bdraco/ulid-transform/commit/154bf0c8d02591508b87c2c154ba877da2aa8f97)) ## v0.1.0 (2023-02-28) ### Feature * Init repo ([#1](https://github.com/bdraco/ulid-transform/issues/1)) ([`0ef9511`](https://github.com/bdraco/ulid-transform/commit/0ef95113cd638617de16be44909b228d0df5f092)) ### Fix * Remove html file ([#2](https://github.com/bdraco/ulid-transform/issues/2)) ([`c1a5cf4`](https://github.com/bdraco/ulid-transform/commit/c1a5cf4a4a8c5a2c8ba1663b9132d612fe47570d)) ulid-transform-0.13.1/CONTRIBUTING.md000066400000000000000000000074611465227100700170070ustar00rootroot00000000000000# 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/bdraco/ulid-transform/issues ulid-transform-0.13.1/LICENSE000066400000000000000000000020601465227100700155510ustar00rootroot00000000000000 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-0.13.1/README.md000066400000000000000000000104471465227100700160330ustar00rootroot00000000000000# Fast ULID transformations

CI Status Test coverage percentage

Poetry black pre-commit

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-0.13.1/bench/000077500000000000000000000000001465227100700156255ustar00rootroot00000000000000ulid-transform-0.13.1/bench/__init__.py000066400000000000000000000000001465227100700177240ustar00rootroot00000000000000ulid-transform-0.13.1/bench/conftest.py000066400000000000000000000000611465227100700200210ustar00rootroot00000000000000from tests.utils import impl __all__ = ["impl"] ulid-transform-0.13.1/bench/test_timestamp.py000066400000000000000000000014031465227100700212370ustar00rootroot00000000000000import 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-0.13.1/bench/test_ulid_as_uuid.py000066400000000000000000000014451465227100700217100ustar00rootroot00000000000000import pytest from uuid import UUID 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-0.13.1/bench/test_ulid_parsing.py000066400000000000000000000012121465227100700217120ustar00rootroot00000000000000import 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-0.13.1/bench/test_ulid_strings.py000066400000000000000000000022371465227100700217500ustar00rootroot00000000000000import 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-0.13.1/build_ext.py000066400000000000000000000036041465227100700171020ustar00rootroot00000000000000"""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"], 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( dict( ext_modules=cythonize( [ ulid_module, ], compiler_directives={"language_level": "3"}, # Python 3 verbose=True, ), cmdclass=dict(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-0.13.1/commitlint.config.js000066400000000000000000000003641465227100700205320ustar00rootroot00000000000000module.exports = { 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-0.13.1/poetry.lock000066400000000000000000000615701465227100700167530ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[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" 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.3.2" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli"] [[package]] name = "cython" version = "3.0.5" description = "The Cython compiler for writing C extensions in the Python language." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "Cython-3.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4faf17ea6e8fc3065a862d4b24be84048fd58ed7abe59aa2f9141446a7a72335"}, {file = "Cython-3.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1cab30c11880f38a27911b569ea38b0bd67fcf32f8a8a8519b613c70562dae2"}, {file = "Cython-3.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4d4d92182002b2878adb3329de1ccb7f3f7571d3586f92602e790bfeab45d0"}, {file = "Cython-3.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b94f58e05e69e1a43da551c8f532e9fad057df1641f0f8ae8f103d4ede5a80fe"}, {file = "Cython-3.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a90f9c7b6635967eacafebe439d518b7dc720aaaf19cb9553f5aad03c13296f4"}, {file = "Cython-3.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c95bd21d87b08c88fe5296381a5f39cd979a775bf1a1d7379a6ff87c703e510b"}, {file = "Cython-3.0.5-cp310-cp310-win32.whl", hash = "sha256:ebc901131057c115a8868e14c1df6e56b9190df774b72664c03ebd858296bb81"}, {file = "Cython-3.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:0759868b4a4d103578375e031e89abd578c26774d957ee4f591764ef8003b363"}, {file = "Cython-3.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3679a6693456f5f7ccca9ab2a91408e99ee257e145024fe380da7c78a07e98b6"}, {file = "Cython-3.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ad4eb2608661d63fb57c674dafb9955f5141d748d4579c7722c1a3c6b86a0c2"}, {file = "Cython-3.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b37f4b0d983316242b4b9241ecbbe55220aa92af93ff04626441fe0ea90a54f9"}, {file = "Cython-3.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34059c3ea6e342ba388cd9774a61761bb4200ca18bd475de65c9cc70ef4e0204"}, {file = "Cython-3.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4db9eea298e982aee7ba12b3432c66eb2e91bb2f5d026bbd57c35698ea0f557f"}, {file = "Cython-3.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:452679284c15a7d5a88bff675e1dd660349360f0665aea50be2d98b7650925f8"}, {file = "Cython-3.0.5-cp311-cp311-win32.whl", hash = "sha256:2d6bb318ddce8b978c81cf78caf8b3836db84f6235d721952685e87871f506e4"}, {file = "Cython-3.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:fcfd2255458a5779dbab813550695331d541b24f0ef831ace83f04f9516ddf26"}, {file = "Cython-3.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0d9fcfc09d67218fce114fe9fd97bba4f9d56add0f775c588d8c626ed47f1aef"}, {file = "Cython-3.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ac1cf1f2ed01656b33618352f7e42bf75d027425b83cc96cfe13ce4b6cba5de"}, {file = "Cython-3.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9d17a6ceb301c5dbd3820e62c1b10a4ad3a6eea3e07e7afaf736b5f490c2e32"}, {file = "Cython-3.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd9cab3b862bec2b110886aedb11765e9deda363c4c7ab5ea205f3d8f143c411"}, {file = "Cython-3.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:45277bb54c35b11bcdd6cf0f56eb950eb280b67146db0cb57853fb6334c6d11d"}, {file = "Cython-3.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:77f4d001fb7a03a68b53be20571acd17452d1dda7907d9c325dff0cc704b1ef9"}, {file = "Cython-3.0.5-cp312-cp312-win32.whl", hash = "sha256:57b44f789794d74c1feddae054dd045b9f601bdffd7288e069b4ca7ed607ec61"}, {file = "Cython-3.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:05c4efd36879ff8020af00407e4c14246b894558ea41dc6486f60dd71601fc67"}, {file = "Cython-3.0.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:048fe89c2c753c24e1a7a05496907173dab17e238c8dc3c7cad90b3679b0d846"}, {file = "Cython-3.0.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c016b3e859b41cf4ce1b8107f364ad5a83c086f75ea4d8d3990b24691f626a1"}, {file = "Cython-3.0.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f31d02b831d0fa9bf099b1b714b5a8252eabd8db34b7d33c48e7e808a2dabf9"}, {file = "Cython-3.0.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:485f8a3087392e2287e2869adc0385b439f69b9cfbd262fdf39b00417690c988"}, {file = "Cython-3.0.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:063220a6dc0909b576ef068c7e2acf5c608d64423a6d231aacb72d06352cd95b"}, {file = "Cython-3.0.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:abb2362783521bd9a22fe69b2141abab4db97237665a36a034b161ddee5b3e72"}, {file = "Cython-3.0.5-cp36-cp36m-win32.whl", hash = "sha256:a993002fe28c840dc29805fde7341c775b7878b311b85f21560fdebf220c247b"}, {file = "Cython-3.0.5-cp36-cp36m-win_amd64.whl", hash = "sha256:13491f1bfcf020fd02751c4a55294aa8957e21b7ecd2542b0521a7aa50c58bb2"}, {file = "Cython-3.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:45aaceb082ad89365f2f783a40db42359591ad55914fb298841196edf88afdc5"}, {file = "Cython-3.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3e011fa2ae9e953fe1ab8394329a21bdb54357c7fe509bcfb02b88bc15bffbb"}, {file = "Cython-3.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f18c13d5ed6fde5efd3b1c039f6a34296d1a0409bb00fbf45bec6f9bcf63ddf5"}, {file = "Cython-3.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:039877e57dc10abf0d30d2de2c7476f0881d8ecef1f29bdeed1a6a06a8d89141"}, {file = "Cython-3.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4fbc8f62b8d50f9a2eef99927a9dcb8d0a42e5a801ab14c2e4aeab622c88f54b"}, {file = "Cython-3.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3cffbba1888f795de2103e6fb1482c8ea8d457e638fa813e090fe747f9e549bb"}, {file = "Cython-3.0.5-cp37-cp37m-win32.whl", hash = "sha256:c18e125537a96e76c8c34201e5a9aad8625e3d872dd26a63155573462e54e185"}, {file = "Cython-3.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:93502f45948ae8d7f874ba4c113b50cb6fb4ee664caa82e1ddc398500ee0ffb3"}, {file = "Cython-3.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0a9206b0720f0cad3e70c018722f6d10e81b32e65477e14ffedd3fbfadfaddca"}, {file = "Cython-3.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:530a474a79fa6c2127bb7e3ba00857b1f26a563615863f17b7434331aa3fe404"}, {file = "Cython-3.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:115e76fbe9288119526b66963f614042222d1587f1ba5ddb90088215a3d2a25a"}, {file = "Cython-3.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:035cb6930a8534f865a3f4616543f78bd27e4de9c3e117b2826e395085ffc4c0"}, {file = "Cython-3.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:077d9a61e6042174dabf68b8e92c0a80f5aff529439ed314aa6e6a233af80b95"}, {file = "Cython-3.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ba3f7b433c1721a43674c0889d5fad746bf608416c8f849343859e6d4d3a7113"}, {file = "Cython-3.0.5-cp38-cp38-win32.whl", hash = "sha256:a95ed0e6f481462a3ff2be4c2e4ffffc5d00fc3884d4ccd1fe5b702d4938ec09"}, {file = "Cython-3.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:f687539ead9fbc17f499e33ee20c1dc41598f70ad95edb4990c576447cec9d23"}, {file = "Cython-3.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f6fcfef825edb44cf3c6ba2c091ad76a83da62ac9c79553e80e0c2a1f75eda2e"}, {file = "Cython-3.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0d9431101f600d5a557d55989658cbfd02b7c0dcd1e4675dac8ad7e0da8ea5b"}, {file = "Cython-3.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db21997270e943aee9cb7694112d24a4702fbe1977fbe53b3cb4db3d02be73d9"}, {file = "Cython-3.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:808f56d4cd0511723b787c341f3cc995fd72893e608102268298c188f4a4f2e7"}, {file = "Cython-3.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:dee39967168d6326ea2df56ad215a4d5049aa52f44cd5aad45bfb63d5b4fb9e5"}, {file = "Cython-3.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b77f2b45535bcf3741592fa03183558bd42198b872c1584b896fa0ba5f2ac68d"}, {file = "Cython-3.0.5-cp39-cp39-win32.whl", hash = "sha256:5742ef31e1e2c9a4824ef6b05af0f4949047a6f73af1d4b238ce12935174aede"}, {file = "Cython-3.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:ada4852db0e33dbdd1425322db081d22b9725cb9f5eba42885467b4e2c4f2ac0"}, {file = "Cython-3.0.5-py2.py3-none-any.whl", hash = "sha256:75206369504fc442c10a86ecf57b91592dca744e4592af22a47e9a774d53dd10"}, {file = "Cython-3.0.5.tar.gz", hash = "sha256:39318348db488a2f24e7c84e08bdc82f2624853c0fea8b475ea0b70b27176492"}, ] [[package]] name = "exceptiongroup" version = "1.1.3" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" 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 = "packaging" version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] name = "pluggy" version = "1.3.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, ] [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 = "*" 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 = "pytest" version = "7.4.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-benchmark" version = "4.0.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.7" files = [ {file = "pytest-benchmark-4.0.0.tar.gz", hash = "sha256:fb0785b83efe599a6a956361c0691ae1dbb5318018561af10f3e915caa0048d1"}, {file = "pytest_benchmark-4.0.0-py3-none-any.whl", hash = "sha256:fdb7db64e31c8b277dff9850d2a2556d8b60bcb0ea6524e36e28ffd7c87f71d6"}, ] [package.dependencies] py-cpuinfo = "*" pytest = ">=3.8" [package.extras] aspect = ["aspectlib"] elasticsearch = ["elasticsearch"] histogram = ["pygal", "pygaljs"] [[package]] name = "pytest-cov" version = "3.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.6" files = [ {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, ] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] [[package]] name = "setuptools" version = "65.7.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.7" files = [ {file = "setuptools-65.7.0-py3-none-any.whl", hash = "sha256:8ab4f1dbf2b4a65f7eec5ad0c620e84c34111a68d3349833494b9088212214dd"}, {file = "setuptools-65.7.0.tar.gz", hash = "sha256:4d3c92fac8f1118bb77a22181355e29c239cabfe2b9effdaa665c66b711136d7"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "ulid-py" version = "1.1.0" description = "Universally Unique Lexicographically Sortable Identifier" optional = false python-versions = "*" 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 = "*" 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.0" python-versions = "^3.10" content-hash = "8363abbb26501965cbc190d91e70d5a771c68db2e373c7295fd57118c985916b" ulid-transform-0.13.1/pyproject.toml000066400000000000000000000041461465227100700174670ustar00rootroot00000000000000[tool.poetry] name = "ulid-transform" version = "0.13.1" description = "Create and transform ULIDs" authors = ["J. Nick Koston "] license = "MIT" readme = "README.md" repository = "https://github.com/bdraco/ulid-transform" classifiers = [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "Natural Language :: English", "Operating System :: OS Independent", "Topic :: Software Development :: Libraries", ] 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/bdraco/ulid-transform/issues" "Changelog" = "https://github.com/bdraco/ulid-transform/blob/main/CHANGELOG.md" [tool.poetry.dependencies] python = "^3.10" [tool.poetry.group.dev.dependencies] pytest = "^7.0" pytest-cov = "^3.0" Cython = ">=3.0.5" setuptools = "^65.4.1" [tool.poetry.group.benchmark.dependencies] ulid-py = "^1.1.0" ulid2 = "^0.3.0" pytest-benchmark = "^4.0.0" [tool.semantic_release] branch = "main" version_toml = "pyproject.toml:tool.poetry.version" version_variable = "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-0.13.1/renovate.json000066400000000000000000000001011465227100700172540ustar00rootroot00000000000000{ "extends": ["github>browniebroke/renovate-configs:python"] } ulid-transform-0.13.1/src/000077500000000000000000000000001465227100700153355ustar00rootroot00000000000000ulid-transform-0.13.1/src/ulid_transform/000077500000000000000000000000001465227100700203655ustar00rootroot00000000000000ulid-transform-0.13.1/src/ulid_transform/__init__.py000066400000000000000000000015261465227100700225020ustar00rootroot00000000000000__version__ = "0.13.1" 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-0.13.1/src/ulid_transform/__init__.pyi000066400000000000000000000007241465227100700226520ustar00rootroot00000000000000def 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-0.13.1/src/ulid_transform/_py_ulid_impl.py000066400000000000000000000237601465227100700235740ustar00rootroot00000000000000import 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-0.13.1/src/ulid_transform/_ulid_impl.pyx000066400000000000000000000105311465227100700232440ustar00rootroot00000000000000# 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-0.13.1/src/ulid_transform/py.typed000066400000000000000000000000001465227100700220520ustar00rootroot00000000000000ulid-transform-0.13.1/src/ulid_transform/ulid.hh000066400000000000000000000004471465227100700216500ustar00rootroot00000000000000// 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-0.13.1/src/ulid_transform/ulid_struct.hh000066400000000000000000000455551465227100700232650ustar00rootroot00000000000000#ifndef ULID_STRUCT_HH #define ULID_STRUCT_HH #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); } 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-0.13.1/src/ulid_transform/ulid_uint128.hh000066400000000000000000000347371465227100700231530ustar00rootroot00000000000000#ifndef ULID_UINT128_HH #define ULID_UINT128_HH #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; } 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-0.13.1/src/ulid_transform/ulid_wrapper.cpp000066400000000000000000000053561465227100700235770ustar00rootroot00000000000000#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::EncodeEntropyRand(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::EncodeEntropyRand(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::EncodeEntropyRand(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::EncodeEntropyRand(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-0.13.1/src/ulid_transform/ulid_wrapper.h000066400000000000000000000007721465227100700232410ustar00rootroot00000000000000#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-0.13.1/tests/000077500000000000000000000000001465227100700157105ustar00rootroot00000000000000ulid-transform-0.13.1/tests/__init__.py000066400000000000000000000000001465227100700200070ustar00rootroot00000000000000ulid-transform-0.13.1/tests/conftest.py000066400000000000000000000000611465227100700201040ustar00rootroot00000000000000from tests.utils import impl __all__ = ["impl"] ulid-transform-0.13.1/tests/test_cython.py000066400000000000000000000006001465227100700206210ustar00rootroot00000000000000import 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("