From a49c122ab0d2cf4b3e5773cae1eb35684699d837 Mon Sep 17 00:00:00 2001 From: CodingAngel1 Date: Tue, 30 Jun 2026 08:12:29 +0000 Subject: [PATCH 1/3] security: replace pull_request_target with two-workflow pattern (fixes #472) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original auto-review.yml used pull_request_target, which runs in the context of the base repository and therefore has access to repository secrets (OWNER_PAT, GROQ_API_KEY) even when the triggering PR comes from a fork. An attacker could craft a PR that exfiltrates those secrets by manipulating the workflow steps that execute untrusted code from the fork. This commit implements the recommended two-workflow defense: 1. pr-collector.yml — trigger: pull_request (no secrets) Runs entirely without repository secrets. Uses only the ephemeral GITHUB_TOKEN (read-only) to fetch PR metadata and the diff, then uploads everything as a GitHub Actions artifact (retention: 7 days). No untrusted code is ever executed. 2. pr-review.yml — trigger: pull_request_review (secrets gated) Runs in the base-repo context (safe for secrets). Before touching any secret it enforces three independent guards: a. The review must be a submitted review (not a dismissal). b. The review body must contain the trigger phrase /run-auto-review. c. The reviewer must appear in the TRUSTED_REVIEWERS repository variable OR hold at least write/admin collaborator permission. Only after all three pass does the workflow download the artifact and call the Groq API (GROQ_API_KEY) or perform PR actions (OWNER_PAT). The old auto-review.yml.disabled file is deleted; its logic is fully preserved and hardened in the two new files. References: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ --- .github/workflows/pr-collector.yml | 219 ++++++++++ ...auto-review.yml.disabled => pr-review.yml} | 378 +++++++++++------- 2 files changed, 446 insertions(+), 151 deletions(-) create mode 100644 .github/workflows/pr-collector.yml rename .github/workflows/{auto-review.yml.disabled => pr-review.yml} (50%) diff --git a/.github/workflows/pr-collector.yml b/.github/workflows/pr-collector.yml new file mode 100644 index 0000000..3f157a9 --- /dev/null +++ b/.github/workflows/pr-collector.yml @@ -0,0 +1,219 @@ +# Workflow 1 of 2 — PR Metadata Collector (UNPRIVILEGED) +# +# Security model (fixes #472) +# ─────────────────────────── +# This workflow runs on the `pull_request` event, which means it executes in +# the context of the *fork/contributor's* commit — it has NO access to +# repository secrets. Its only job is to gather the PR diff + metadata and +# upload them as a GitHub Actions artifact. No secret is ever injected into +# this job. +# +# The companion privileged workflow (pr-review.yml) is triggered separately +# by a trusted maintainer, downloads this artifact, and only then calls the +# Groq API and acts on the PR using the OWNER_PAT. Because that second +# workflow runs on `pull_request_review`, it executes in the context of the +# *base* repository and has full access to secrets. +# +# References +# ────────── +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ +# https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions + +name: PR Metadata Collector + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + branches: [main] + +# Absolute minimum permissions — read-only on pull-requests so we can fetch +# the diff; write is NOT needed here. +permissions: + contents: read + pull-requests: read + +jobs: + collect: + name: Collect PR metadata and diff + # Skip draft PRs; the review workflow would ignore them anyway. + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + + steps: + - name: Check out the *base* repo at HEAD (not the fork's code) + uses: actions/checkout@v4 + with: + # Explicitly check out the base branch, not the PR head, so that + # untrusted code from the fork never runs in this step. + ref: ${{ github.event.pull_request.base.sha }} + + # ── Gather PR metadata ──────────────────────────────────────────────── + - name: Gather PR metadata + id: meta + env: + # GITHUB_TOKEN is the ephemeral, read-only installation token + # automatically provided by GitHub Actions. It is NOT a secret + # you configured; it cannot write to this repo beyond what the + # `permissions` block above grants (read-only). + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUM: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + run: | + set -euo pipefail + + PR_DATA=$(gh pr view "$PR_NUM" \ + --repo "$REPO" \ + --json title,body,author,files,additions,deletions,changedFiles) + + AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login') + TITLE=$(echo "$PR_DATA" | jq -r '.title') + BODY=$(echo "$PR_DATA" | jq -r '.body // ""') + FILES=$(echo "$PR_DATA" | jq -r '.changedFiles') + ADDS=$(echo "$PR_DATA" | jq -r '.additions') + DELS=$(echo "$PR_DATA" | jq -r '.deletions') + + echo "author=$AUTHOR" >> "$GITHUB_OUTPUT" + echo "files=$FILES" >> "$GITHUB_OUTPUT" + echo "adds=$ADDS" >> "$GITHUB_OUTPUT" + echo "dels=$DELS" >> "$GITHUB_OUTPUT" + + { + echo "title<> "$GITHUB_OUTPUT" + + { + echo "pr_body<> "$GITHUB_OUTPUT" + + # ── Fetch and truncate the diff ─────────────────────────────────────── + - name: Fetch PR diff + id: diff + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUM: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + run: | + set -euo pipefail + + gh pr diff "$PR_NUM" --repo "$REPO" > /tmp/pr-diff.txt 2>/dev/null || true + + DIFF_BYTES=$(wc -c < /tmp/pr-diff.txt | tr -d ' ') + REAL_CHANGE_LINES=$(grep -E '^[+-]' /tmp/pr-diff.txt \ + | grep -vE '^(\+\+\+|---)' | wc -l | tr -d ' ') + + echo "diff_bytes=$DIFF_BYTES" >> "$GITHUB_OUTPUT" + echo "real_change_lines=$REAL_CHANGE_LINES" >> "$GITHUB_OUTPUT" + + # Keep the first 3 000 lines to stay within reasonable payload size. + head -3000 /tmp/pr-diff.txt > /tmp/pr-diff-truncated.txt + + # ── Fetch linked issue context ──────────────────────────────────────── + - name: Fetch linked issue context + id: issues + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + PR_BODY: ${{ steps.meta.outputs.pr_body }} + run: | + set -euo pipefail + + ISSUE_NUMS=$(echo "$PR_BODY" | \ + grep -oiE '(close[sd]?|fix(e[sd])?|resolve[sd]?)\s+#[0-9]+' | \ + grep -oE '[0-9]+' | sort -u | tr '\n' ' ' || true) + + : > /tmp/issues-context.txt + + if [ -n "${ISSUE_NUMS:-}" ]; then + for NUM in $ISSUE_NUMS; do + ISSUE_DATA=$(gh issue view "$NUM" --repo "$REPO" \ + --json number,title,body 2>/dev/null || echo "") + if [ -n "$ISSUE_DATA" ]; then + ISSUE_TITLE=$(echo "$ISSUE_DATA" | jq -r '.title // ""') + ISSUE_BODY=$(echo "$ISSUE_DATA" | jq -r '.body // ""' | head -c 2000) + { + echo "--- Issue #$NUM: $ISSUE_TITLE ---" + echo "$ISSUE_BODY" + echo "" + } >> /tmp/issues-context.txt + fi + done + fi + + if [ ! -s /tmp/issues-context.txt ]; then + echo "(No issues linked or issues could not be fetched)" \ + > /tmp/issues-context.txt + echo "has_issue=false" >> "$GITHUB_OUTPUT" + else + echo "has_issue=true" >> "$GITHUB_OUTPUT" + fi + + # ── Pre-flight empty-diff detection ─────────────────────────────────── + - name: Pre-flight empty-diff detection + id: preflight + env: + DIFF_BYTES: ${{ steps.diff.outputs.diff_bytes }} + REAL_CHANGE_LINES: ${{ steps.diff.outputs.real_change_lines }} + ADDS: ${{ steps.meta.outputs.adds }} + run: | + if [ "$DIFF_BYTES" -lt 50 ] \ + || [ "$ADDS" -eq 0 ] \ + || [ "$REAL_CHANGE_LINES" -lt 1 ]; then + echo "auto_fraud=true" >> "$GITHUB_OUTPUT" + echo "fraud_type=empty" >> "$GITHUB_OUTPUT" + else + echo "auto_fraud=false" >> "$GITHUB_OUTPUT" + echo "fraud_type=none" >> "$GITHUB_OUTPUT" + fi + + # ── Serialise metadata to a file for the artifact ───────────────────── + - name: Write metadata JSON + run: | + set -euo pipefail + + jq -n \ + --arg pr_number "${{ github.event.pull_request.number }}" \ + --arg pr_title "${{ steps.meta.outputs.title }}" \ + --arg pr_author "${{ steps.meta.outputs.author }}" \ + --arg pr_body "${{ steps.meta.outputs.pr_body }}" \ + --arg files "${{ steps.meta.outputs.files }}" \ + --arg adds "${{ steps.meta.outputs.adds }}" \ + --arg dels "${{ steps.meta.outputs.dels }}" \ + --arg has_issue "${{ steps.issues.outputs.has_issue }}" \ + --arg diff_bytes "${{ steps.diff.outputs.diff_bytes }}" \ + --arg real_change_lines "${{ steps.diff.outputs.real_change_lines }}" \ + --arg auto_fraud "${{ steps.preflight.outputs.auto_fraud }}" \ + --arg fraud_type "${{ steps.preflight.outputs.fraud_type }}" \ + '{ + pr_number: ($pr_number | tonumber), + pr_title: $pr_title, + pr_author: $pr_author, + pr_body: $pr_body, + files: ($files | tonumber), + adds: ($adds | tonumber), + dels: ($dels | tonumber), + has_issue: $has_issue, + diff_bytes: ($diff_bytes | tonumber), + real_change_lines: ($real_change_lines | tonumber), + auto_fraud: $auto_fraud, + fraud_type: $fraud_type + }' > /tmp/pr-meta.json + + # ── Upload the artifact ─────────────────────────────────────────────── + # The artifact is keyed by PR number so the privileged workflow can find + # it deterministically via the `pr_number` input it receives. + - name: Upload PR artifact + uses: actions/upload-artifact@v4 + with: + # Name pattern consumed by pr-review.yml + name: pr-${{ github.event.pull_request.number }} + path: | + /tmp/pr-meta.json + /tmp/pr-diff-truncated.txt + /tmp/issues-context.txt + # Artifacts older than the review window are useless; keep storage lean. + retention-days: 7 + if-no-files-found: error diff --git a/.github/workflows/auto-review.yml.disabled b/.github/workflows/pr-review.yml similarity index 50% rename from .github/workflows/auto-review.yml.disabled rename to .github/workflows/pr-review.yml index 7ead50f..493b793 100644 --- a/.github/workflows/auto-review.yml.disabled +++ b/.github/workflows/pr-review.yml @@ -1,106 +1,200 @@ -name: Auto Review +# Workflow 2 of 2 — Privileged AI Review (SECRETS GATED BEHIND TRUSTED APPROVAL) +# +# Security model (fixes #472) +# ─────────────────────────── +# This workflow is triggered by the `pull_request_review` event, NOT by +# `pull_request_target`. `pull_request_review` always runs in the context of +# the *base* repository, regardless of whether the PR comes from a fork, so +# it safely has access to repository secrets. +# +# Secrets are only consumed AFTER verifying that: +# 1. The review that triggered this run was **submitted** (not dismissed). +# 2. The reviewer is a member of the repository's CODEOWNER / reviewer team +# (enforced via the `gh api` team-membership check below). +# 3. The review body contains the exact trigger phrase: +# /run-auto-review +# This prevents the Groq call from firing on every random review comment. +# +# No code from the fork is ever executed. The PR diff arrives as a +# pre-built artifact uploaded by pr-collector.yml (the unprivileged sibling). +# +# References +# ────────── +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ +# https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions + +name: AI PR Review (privileged) on: - pull_request_target: - types: [opened, synchronize, reopened, ready_for_review] + pull_request_review: + types: [submitted] permissions: - contents: write + contents: read pull-requests: write jobs: review: - if: github.event.pull_request.state == 'open' && github.event.pull_request.draft == false + name: Run Groq review and act on PR runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - name: Gather PR metadata, diff, and linked issue context - id: ctx + steps: + # ── Guard: only proceed when a trusted reviewer requests it ─────────── + - name: Validate trigger conditions + id: gate env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUM: ${{ github.event.pull_request.number }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REVIEWER: ${{ github.event.review.user.login }} + REVIEW_STATE: ${{ github.event.review.state }} + REVIEW_BODY: ${{ github.event.review.body }} + REPO: ${{ github.repository }} + REPO_OWNER: ${{ github.repository_owner }} + # Comma-separated list of GitHub logins that may trigger the review. + # If you prefer team-based membership, set TRUSTED_TEAM instead and + # flip the check below. + TRUSTED_REVIEWERS: ${{ vars.TRUSTED_REVIEWERS }} run: | - PR_DATA=$(gh pr view "$PR_NUM" --json title,body,author,files,additions,deletions,changedFiles) - AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login') - TITLE=$(echo "$PR_DATA" | jq -r '.title') - BODY=$(echo "$PR_DATA" | jq -r '.body // ""') - FILES=$(echo "$PR_DATA" | jq -r '.changedFiles') - ADDS=$(echo "$PR_DATA" | jq -r '.additions') - DELS=$(echo "$PR_DATA" | jq -r '.deletions') - echo "author=$AUTHOR" >> $GITHUB_OUTPUT - echo "files=$FILES" >> $GITHUB_OUTPUT - echo "adds=$ADDS" >> $GITHUB_OUTPUT - echo "dels=$DELS" >> $GITHUB_OUTPUT - { - echo "title<> $GITHUB_OUTPUT - { - echo "pr_body<> $GITHUB_OUTPUT - gh pr diff "$PR_NUM" > /tmp/pr-diff.txt 2>/dev/null || true - DIFF_BYTES=$(wc -c < /tmp/pr-diff.txt | tr -d ' ') - REAL_CHANGE_LINES=$(grep -E '^[+-]' /tmp/pr-diff.txt | grep -vE '^(\+\+\+|---)' | wc -l | tr -d ' ') - echo "diff_bytes=$DIFF_BYTES" >> $GITHUB_OUTPUT - echo "real_change_lines=$REAL_CHANGE_LINES" >> $GITHUB_OUTPUT - head -3000 /tmp/pr-diff.txt > /tmp/pr-diff-truncated.txt - ISSUE_NUMS=$(echo "$BODY" | \ - grep -oiE '(close[sd]?|fix(e[sd])?|resolve[sd]?)\s+#[0-9]+' | \ - grep -oE '[0-9]+' | sort -u | tr '\n' ' ' || true) - : > /tmp/issues-context.txt - if [ -n "${ISSUE_NUMS:-}" ]; then - for NUM in $ISSUE_NUMS; do - ISSUE_DATA=$(gh issue view "$NUM" --repo "${{ github.repository }}" \ - --json number,title,body 2>/dev/null || echo "") - if [ -n "$ISSUE_DATA" ]; then - ISSUE_TITLE=$(echo "$ISSUE_DATA" | jq -r '.title // ""') - ISSUE_BODY=$(echo "$ISSUE_DATA" | jq -r '.body // ""' | head -c 2000) - { - echo "--- Issue #$NUM: $ISSUE_TITLE ---" - echo "$ISSUE_BODY" - echo "" - } >> /tmp/issues-context.txt + set -euo pipefail + + # 1. The review must be a real submission (not a dismissal). + if [ "$REVIEW_STATE" != "approved" ] \ + && [ "$REVIEW_STATE" != "changes_requested" ] \ + && [ "$REVIEW_STATE" != "commented" ]; then + echo "skip=true" >> "$GITHUB_OUTPUT" + echo "skip_reason=review_state_not_actionable ($REVIEW_STATE)" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # 2. The review body must contain the exact trigger phrase. + if ! echo "$REVIEW_BODY" | grep -qF '/run-auto-review'; then + echo "skip=true" >> "$GITHUB_OUTPUT" + echo "skip_reason=trigger_phrase_absent" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # 3. The reviewer must be in the trusted list (TRUSTED_REVIEWERS var) + # OR be a repository admin / collaborator with push access. + TRUSTED=false + + if [ -n "${TRUSTED_REVIEWERS:-}" ]; then + # TRUSTED_REVIEWERS is a comma-separated list stored as a repo var. + IFS=',' read -ra TRUSTED_LIST <<< "$TRUSTED_REVIEWERS" + for LOGIN in "${TRUSTED_LIST[@]}"; do + TRIMMED=$(echo "$LOGIN" | tr -d '[:space:]') + if [ "$REVIEWER" = "$TRIMMED" ]; then + TRUSTED=true + break fi done fi - if [ ! -s /tmp/issues-context.txt ]; then - echo "(No issues linked or issues could not be fetched)" > /tmp/issues-context.txt - echo "has_issue=false" >> $GITHUB_OUTPUT - else - echo "has_issue=true" >> $GITHUB_OUTPUT + + # Fallback: check if the reviewer has at least write (push) access. + if [ "$TRUSTED" = "false" ]; then + PERM=$(gh api \ + "repos/$REPO/collaborators/$REVIEWER/permission" \ + --jq '.permission' 2>/dev/null || echo "none") + if [ "$PERM" = "admin" ] || [ "$PERM" = "write" ]; then + TRUSTED=true + fi + fi + + if [ "$TRUSTED" = "false" ]; then + echo "skip=true" >> "$GITHUB_OUTPUT" + echo "skip_reason=reviewer_not_trusted ($REVIEWER)" >> "$GITHUB_OUTPUT" + exit 0 fi - - name: Pre-flight empty-diff fraud check - id: preflight + echo "skip=false" >> "$GITHUB_OUTPUT" + echo "skip_reason=none" >> "$GITHUB_OUTPUT" + echo "reviewer=$REVIEWER" >> "$GITHUB_OUTPUT" + + # ── Skip the rest if gate conditions not met ────────────────────────── + - name: Log skip reason (informational) + if: steps.gate.outputs.skip == 'true' + run: | + echo "Skipping privileged review: ${{ steps.gate.outputs.skip_reason }}" + + # ── Download the artifact produced by pr-collector.yml ──────────────── + - name: Download PR artifact + if: steps.gate.outputs.skip != 'true' + uses: actions/download-artifact@v4 + with: + name: pr-${{ github.event.pull_request.number }} + path: /tmp/pr-artifact + + # ── Load metadata and handle empty-diff case ────────────────────────── + - name: Load metadata + if: steps.gate.outputs.skip != 'true' + id: meta + run: | + set -euo pipefail + + META=/tmp/pr-artifact/pr-meta.json + + echo "pr_number=$(jq -r '.pr_number' "$META")" >> "$GITHUB_OUTPUT" + echo "pr_title=$(jq -r '.pr_title' "$META")" >> "$GITHUB_OUTPUT" + echo "pr_author=$(jq -r '.pr_author' "$META")" >> "$GITHUB_OUTPUT" + echo "files=$(jq -r '.files' "$META")" >> "$GITHUB_OUTPUT" + echo "adds=$(jq -r '.adds' "$META")" >> "$GITHUB_OUTPUT" + echo "dels=$(jq -r '.dels' "$META")" >> "$GITHUB_OUTPUT" + echo "has_issue=$(jq -r '.has_issue' "$META")" >> "$GITHUB_OUTPUT" + echo "auto_fraud=$(jq -r '.auto_fraud' "$META")" >> "$GITHUB_OUTPUT" + echo "fraud_type=$(jq -r '.fraud_type' "$META")" >> "$GITHUB_OUTPUT" + + { + echo "pr_body<> "$GITHUB_OUTPUT" + + # ── Handle pre-flight detected empty PR ─────────────────────────────── + - name: Handle empty-diff PR (auto-close) + if: | + steps.gate.outputs.skip != 'true' + && steps.meta.outputs.auto_fraud == 'true' env: - DIFF_BYTES: ${{ steps.ctx.outputs.diff_bytes }} - REAL_CHANGE_LINES: ${{ steps.ctx.outputs.real_change_lines }} - ADDS: ${{ steps.ctx.outputs.adds }} + GH_TOKEN: ${{ secrets.OWNER_PAT }} + PR_NUM: ${{ steps.meta.outputs.pr_number }} + PR_AUTHOR: ${{ steps.meta.outputs.pr_author }} run: | - if [ "$DIFF_BYTES" -lt 50 ] || [ "$ADDS" -eq 0 ] || [ "$REAL_CHANGE_LINES" -lt 1 ]; then - echo "auto_fraud=true" >> $GITHUB_OUTPUT - echo "fraud_type=empty" >> $GITHUB_OUTPUT - exit 0 + set -euo pipefail + + PAT_USER=$(gh api user --jq '.login' 2>/dev/null || echo "") + BODY=$(printf '%s\n' \ + "This PR has an empty or whitespace-only diff — no real code changes detected." \ + "" \ + "If you intended to push code, please commit it. Closing automatically; reopen once you have actual changes.") + + if [ "$PR_AUTHOR" = "$PAT_USER" ]; then + gh pr comment "$PR_NUM" --body "$BODY" + else + gh pr review "$PR_NUM" --request-changes --body "$BODY" \ + || gh pr comment "$PR_NUM" --body "$BODY" fi - echo "auto_fraud=false" >> $GITHUB_OUTPUT - - name: Lenient fraud-detection review (Groq gpt-oss-120b) - if: steps.preflight.outputs.auto_fraud != 'true' + gh pr close "$PR_NUM" \ + --comment "Closed: empty diff. Reopen with real changes." || true + + # ── Groq fraud-detection review ─────────────────────────────────────── + - name: Lenient fraud-detection review (Groq) + if: | + steps.gate.outputs.skip != 'true' + && steps.meta.outputs.auto_fraud != 'true' id: review env: + # GROQ_API_KEY is only injected here, in the privileged workflow, + # after the trusted-reviewer gate has been passed. GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - PR_TITLE: ${{ steps.ctx.outputs.title }} - PR_AUTHOR: ${{ steps.ctx.outputs.author }} - PR_BODY: ${{ steps.ctx.outputs.pr_body }} - PR_FILES: ${{ steps.ctx.outputs.files }} - PR_ADDS: ${{ steps.ctx.outputs.adds }} - PR_DELS: ${{ steps.ctx.outputs.dels }} - HAS_ISSUE: ${{ steps.ctx.outputs.has_issue }} + PR_TITLE: ${{ steps.meta.outputs.pr_title }} + PR_AUTHOR: ${{ steps.meta.outputs.pr_author }} + PR_BODY: ${{ steps.meta.outputs.pr_body }} + PR_FILES: ${{ steps.meta.outputs.files }} + PR_ADDS: ${{ steps.meta.outputs.adds }} + PR_DELS: ${{ steps.meta.outputs.dels }} + HAS_ISSUE: ${{ steps.meta.outputs.has_issue }} run: | + set -euo pipefail + SYSTEM_PROMPT=$(cat <<'SYSTEM_EOF' You are an EXTREMELY LENIENT code reviewer for Lernza, a hackathon project. Interns are shipping fast. CI has been disabled to maximise velocity. @@ -189,27 +283,27 @@ jobs: ) PAYLOAD=$(jq -n \ - --arg system "$SYSTEM_PROMPT" \ - --arg title "$PR_TITLE" \ - --arg author "$PR_AUTHOR" \ - --arg pr_body "$PR_BODY" \ - --arg files "$PR_FILES" \ - --arg adds "$PR_ADDS" \ - --arg dels "$PR_DELS" \ - --arg has_iss "$HAS_ISSUE" \ - --rawfile diff /tmp/pr-diff-truncated.txt \ - --rawfile issues /tmp/issues-context.txt \ + --arg system "$SYSTEM_PROMPT" \ + --arg title "$PR_TITLE" \ + --arg author "$PR_AUTHOR" \ + --arg pr_body "$PR_BODY" \ + --arg files "$PR_FILES" \ + --arg adds "$PR_ADDS" \ + --arg dels "$PR_DELS" \ + --arg has_iss "$HAS_ISSUE" \ + --rawfile diff /tmp/pr-artifact/pr-diff-truncated.txt \ + --rawfile issues /tmp/pr-artifact/issues-context.txt \ '{ model: "openai/gpt-oss-120b", messages: [ { role: "system", content: $system }, { role: "user", content: ( "## PR metadata\n\n" + - "Title: " + $title + "\n" + - "Author: " + $author + "\n" + - "Files changed: " + $files + "\n" + - "Additions: " + $adds + "\n" + - "Deletions: " + $dels + "\n" + + "Title: " + $title + "\n" + + "Author: " + $author + "\n" + + "Files changed: " + $files + "\n" + + "Additions: " + $adds + "\n" + + "Deletions: " + $dels + "\n" + "Has linked issue: " + $has_iss + "\n\n" + "## PR description\n\n" + $pr_body + "\n\n" + "## Linked issue(s) full content\n\n" + $issues + "\n\n" + @@ -230,7 +324,7 @@ jobs: echo "Response top-level keys: $(echo "$RESPONSE" | jq -r 'keys | join(", ")' 2>/dev/null || echo '')" - API_ERROR=$(echo "$RESPONSE" | jq -r '.error.message // ""' 2>/dev/null || echo "") + API_ERROR=$(echo "$RESPONSE" | jq -r '.error.message // ""' 2>/dev/null || echo "") REVIEW_JSON=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // ""' 2>/dev/null || echo "") DECISION="UNKNOWN" @@ -249,11 +343,11 @@ jobs: echo "Model output is not valid JSON. Raw: $REVIEW_JSON" MODEL_SUMMARY="Auto-review returned malformed output. Leaving PR for human review." else - DECISION=$(echo "$REVIEW_JSON" | jq -r '.decision // "UNKNOWN"') - CONFIDENCE=$(echo "$REVIEW_JSON" | jq -r '.confidence // 0') - FRAUD_TYPE=$(echo "$REVIEW_JSON" | jq -r '.fraud_type // "none"') - EVIDENCE=$(echo "$REVIEW_JSON" | jq -r '.evidence // ""') - MODEL_SUMMARY=$(echo "$REVIEW_JSON" | jq -r '.summary // ""') + DECISION=$(echo "$REVIEW_JSON" | jq -r '.decision // "UNKNOWN"') + CONFIDENCE=$(echo "$REVIEW_JSON" | jq -r '.confidence // 0') + FRAUD_TYPE=$(echo "$REVIEW_JSON" | jq -r '.fraud_type // "none"') + EVIDENCE=$(echo "$REVIEW_JSON" | jq -r '.evidence // ""') + MODEL_SUMMARY=$(echo "$REVIEW_JSON" | jq -r '.summary // ""') if [ "$DECISION" != "APPROVE" ] && [ "$DECISION" != "FLAG_FRAUD" ]; then echo "Unrecognised decision value: $DECISION — failing closed" @@ -262,8 +356,6 @@ jobs: fi fi - # Log full details to Actions run output for your debugging. - # These do NOT get posted to the PR. echo "::group::Auto-review details (run-only, not posted)" echo "Decision: $DECISION" echo "Confidence: $CONFIDENCE" @@ -271,70 +363,48 @@ jobs: echo "Evidence: $EVIDENCE" echo "::endgroup::" - # The PR comment body is JUST the model summary. Nothing appended. if [ -z "$MODEL_SUMMARY" ]; then MODEL_SUMMARY="Looks good." fi - REVIEW_BODY="$MODEL_SUMMARY" - echo "decision=$DECISION" >> $GITHUB_OUTPUT - echo "confidence=$CONFIDENCE" >> $GITHUB_OUTPUT - echo "fraud_type=$FRAUD_TYPE" >> $GITHUB_OUTPUT + echo "decision=$DECISION" >> "$GITHUB_OUTPUT" + echo "confidence=$CONFIDENCE" >> "$GITHUB_OUTPUT" + echo "fraud_type=$FRAUD_TYPE" >> "$GITHUB_OUTPUT" { echo "body<> $GITHUB_OUTPUT + } >> "$GITHUB_OUTPUT" - - name: Handle pre-flight detected empty PR (auto-close) - if: steps.preflight.outputs.auto_fraud == 'true' - env: - GH_TOKEN: ${{ secrets.OWNER_PAT }} - PR_NUM: ${{ github.event.pull_request.number }} - PR_AUTHOR: ${{ steps.ctx.outputs.author }} - run: | - PAT_USER=$(gh api user --jq '.login' 2>/dev/null || echo "") - BODY=$(printf '%s\n' \ - "This PR has an empty or whitespace-only diff — no real code changes detected." \ - "" \ - "If you intended to push code, please commit it. Closing automatically; reopen once you have actual changes.") - - if [ "$PR_AUTHOR" = "$PAT_USER" ]; then - gh pr comment "$PR_NUM" --body "$BODY" - else - gh pr review "$PR_NUM" --request-changes --body "$BODY" || \ - gh pr comment "$PR_NUM" --body "$BODY" - fi - - gh pr close "$PR_NUM" --comment "Closed: empty diff." || true - - - name: Submit review and act on model decision - if: steps.preflight.outputs.auto_fraud != 'true' + # ── Submit review and act on decision ───────────────────────────────── + - name: Submit review and act on decision + if: | + steps.gate.outputs.skip != 'true' + && steps.meta.outputs.auto_fraud != 'true' id: act env: - GH_TOKEN: ${{ secrets.OWNER_PAT }} - PR_NUM: ${{ github.event.pull_request.number }} - PR_AUTHOR: ${{ steps.ctx.outputs.author }} - DECISION: ${{ steps.review.outputs.decision }} + # OWNER_PAT is only injected here, after the trusted-reviewer gate. + GH_TOKEN: ${{ secrets.OWNER_PAT }} + PR_NUM: ${{ steps.meta.outputs.pr_number }} + PR_AUTHOR: ${{ steps.meta.outputs.pr_author }} + DECISION: ${{ steps.review.outputs.decision }} CONFIDENCE: ${{ steps.review.outputs.confidence }} FRAUD_TYPE: ${{ steps.review.outputs.fraud_type }} - BODY: ${{ steps.review.outputs.body }} + BODY: ${{ steps.review.outputs.body }} run: | + set -euo pipefail + PAT_USER=$(gh api user --jq '.login' 2>/dev/null || echo "") IS_SELF_PR="false" - if [ "$PR_AUTHOR" = "$PAT_USER" ]; then - IS_SELF_PR="true" - fi + [ "$PR_AUTHOR" = "$PAT_USER" ] && IS_SELF_PR="true" - MERGE_OK=$(awk -v c="$CONFIDENCE" 'BEGIN { print (c+0 >= 0.7) ? "true" : "false" }') + MERGE_OK=$(awk -v c="$CONFIDENCE" 'BEGIN { print (c+0 >= 0.7) ? "true" : "false" }') CLOSE_OK=$(awk -v c="$CONFIDENCE" 'BEGIN { print (c+0 >= 0.85) ? "true" : "false" }') - echo "merge_ok=$MERGE_OK" >> $GITHUB_OUTPUT - echo "close_ok=$CLOSE_OK" >> $GITHUB_OUTPUT + echo "merge_ok=$MERGE_OK" >> "$GITHUB_OUTPUT" + echo "close_ok=$CLOSE_OK" >> "$GITHUB_OUTPUT" - if [ -z "$BODY" ]; then - BODY="Looks good." - fi + [ -z "$BODY" ] && BODY="Looks good." case "$DECISION" in APPROVE) @@ -353,7 +423,9 @@ jobs: fi if [ "$CLOSE_OK" = "true" ]; then - gh pr close "$PR_NUM" --comment "Closed: $FRAUD_TYPE. Reopen with real changes if this was a mistake." || true + gh pr close "$PR_NUM" \ + --comment "Closed: $FRAUD_TYPE. Reopen with real changes if this was a mistake." \ + || true fi ;; @@ -362,16 +434,20 @@ jobs: ;; esac + # ── Auto-merge approved PRs at high confidence ──────────────────────── - name: Auto-merge approved PRs at high confidence if: | - steps.preflight.outputs.auto_fraud != 'true' + steps.gate.outputs.skip != 'true' + && steps.meta.outputs.auto_fraud != 'true' && steps.review.outputs.decision == 'APPROVE' && steps.act.outputs.merge_ok == 'true' env: GH_TOKEN: ${{ secrets.OWNER_PAT }} - PR_NUM: ${{ github.event.pull_request.number }} - PR_TITLE: ${{ steps.ctx.outputs.title }} + PR_NUM: ${{ steps.meta.outputs.pr_number }} + PR_TITLE: ${{ steps.meta.outputs.pr_title }} run: | + set -euo pipefail + COMMIT_BODY=$(gh pr view "$PR_NUM" --json commits \ --jq '[.commits[].messageBody] | join("\n")' \ | grep -viE 'co-authored-by:.*(claude|qwen|copilot|github-actions|bot|\[bot\]|gpt|openai|anthropic)' \ @@ -384,4 +460,4 @@ jobs: --squash \ --admin \ --subject "$PR_TITLE" \ - --body "$MERGE_BODY" + --body "$MERGE_BODY" From 136b8c78d34dc4b11d0d922bf55a61aca9e6489c Mon Sep 17 00:00:00 2001 From: CodingAngel1 Date: Sat, 4 Jul 2026 21:27:38 +0000 Subject: [PATCH 2/3] fix: address pre-merge review feedback on PR #570 - fix(pr-review): replace actions/download-artifact@v4 with dawidd6/action-download-artifact@v6 (search_artifacts: true) so the privileged review workflow can find artifacts uploaded by the separate pr-collector run instead of failing silently - fix(pr-collector): move pr_title and pr_body out of inline ${{ }} interpolation in the jq command and into env: vars to prevent script injection via attacker-controlled PR title/body - fix(pr-review): remove leftover hackathon/Lernza/CI-disabled text from the Groq system prompt; replace with ProofOfHeart identity --- .github/workflows/pr-collector.yml | 40 +++++++++++++++++++++--------- .github/workflows/pr-review.yml | 14 ++++++++--- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/.github/workflows/pr-collector.yml b/.github/workflows/pr-collector.yml index 3f157a9..af13131 100644 --- a/.github/workflows/pr-collector.yml +++ b/.github/workflows/pr-collector.yml @@ -171,22 +171,38 @@ jobs: # ── Serialise metadata to a file for the artifact ───────────────────── - name: Write metadata JSON + # Fix: pr_title and pr_body are passed via env vars, not interpolated + # directly into the jq command, to prevent script injection from + # attacker-controlled PR title/body content. + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_TITLE: ${{ steps.meta.outputs.title }} + PR_AUTHOR: ${{ steps.meta.outputs.author }} + PR_BODY: ${{ steps.meta.outputs.pr_body }} + PR_FILES: ${{ steps.meta.outputs.files }} + PR_ADDS: ${{ steps.meta.outputs.adds }} + PR_DELS: ${{ steps.meta.outputs.dels }} + HAS_ISSUE: ${{ steps.issues.outputs.has_issue }} + DIFF_BYTES: ${{ steps.diff.outputs.diff_bytes }} + REAL_CHANGE_LINES: ${{ steps.diff.outputs.real_change_lines }} + AUTO_FRAUD: ${{ steps.preflight.outputs.auto_fraud }} + FRAUD_TYPE: ${{ steps.preflight.outputs.fraud_type }} run: | set -euo pipefail jq -n \ - --arg pr_number "${{ github.event.pull_request.number }}" \ - --arg pr_title "${{ steps.meta.outputs.title }}" \ - --arg pr_author "${{ steps.meta.outputs.author }}" \ - --arg pr_body "${{ steps.meta.outputs.pr_body }}" \ - --arg files "${{ steps.meta.outputs.files }}" \ - --arg adds "${{ steps.meta.outputs.adds }}" \ - --arg dels "${{ steps.meta.outputs.dels }}" \ - --arg has_issue "${{ steps.issues.outputs.has_issue }}" \ - --arg diff_bytes "${{ steps.diff.outputs.diff_bytes }}" \ - --arg real_change_lines "${{ steps.diff.outputs.real_change_lines }}" \ - --arg auto_fraud "${{ steps.preflight.outputs.auto_fraud }}" \ - --arg fraud_type "${{ steps.preflight.outputs.fraud_type }}" \ + --arg pr_number "$PR_NUMBER" \ + --arg pr_title "$PR_TITLE" \ + --arg pr_author "$PR_AUTHOR" \ + --arg pr_body "$PR_BODY" \ + --arg files "$PR_FILES" \ + --arg adds "$PR_ADDS" \ + --arg dels "$PR_DELS" \ + --arg has_issue "$HAS_ISSUE" \ + --arg diff_bytes "$DIFF_BYTES" \ + --arg real_change_lines "$REAL_CHANGE_LINES" \ + --arg auto_fraud "$AUTO_FRAUD" \ + --arg fraud_type "$FRAUD_TYPE" \ '{ pr_number: ($pr_number | tonumber), pr_title: $pr_title, diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml index 493b793..017cb66 100644 --- a/.github/workflows/pr-review.yml +++ b/.github/workflows/pr-review.yml @@ -115,12 +115,18 @@ jobs: echo "Skipping privileged review: ${{ steps.gate.outputs.skip_reason }}" # ── Download the artifact produced by pr-collector.yml ──────────────── + # Fix: actions/download-artifact@v4 only searches the *current* run. + # The artifact was uploaded by a separate pull_request run (pr-collector), + # so we use dawidd6/action-download-artifact which searches across runs. - name: Download PR artifact if: steps.gate.outputs.skip != 'true' - uses: actions/download-artifact@v4 + uses: dawidd6/action-download-artifact@v6 with: + github_token: ${{ secrets.GITHUB_TOKEN }} name: pr-${{ github.event.pull_request.number }} path: /tmp/pr-artifact + search_artifacts: true + if_no_artifact_found: fail # ── Load metadata and handle empty-diff case ────────────────────────── - name: Load metadata @@ -196,10 +202,10 @@ jobs: set -euo pipefail SYSTEM_PROMPT=$(cat <<'SYSTEM_EOF' - You are an EXTREMELY LENIENT code reviewer for Lernza, a hackathon project. - Interns are shipping fast. CI has been disabled to maximise velocity. + You are a code reviewer for the ProofOfHeart open-source project on GitHub. + Your role is to evaluate pull requests for genuine contribution quality. - ## Default behaviour: APPROVE almost everything + ## Default behaviour: APPROVE real work IGNORE all of: code style, formatting, naming conventions, missing tests, untyped code, imperfect patterns, unhandled edge cases, performance, From b3d146cc1d55cb09d684155604cecb9e4da34fa1 Mon Sep 17 00:00:00 2001 From: CodingAngel1 Date: Sat, 11 Jul 2026 23:01:09 +0000 Subject: [PATCH 3/3] fix(ci): vendor ethnum 1.5.0 with upstream 1.5.3 patch to fix Rust 1.80+ compile soroban-sdk 20.1.0 pins ethnum 1.5.0, which fails on current stable Rust due to a transmute size mismatch with TryFromIntError. Vendor the crate and apply the upstream fix from ethnum v1.5.3 so clippy and tests pass. - Patch crates-io ethnum to vendor/ethnum - Regenerate Cargo.lock Closes CI clippy/test failures on PR #570. --- Cargo.lock | 172 +- Cargo.toml | 8 + vendor/ethnum/.cargo-ok | 1 + vendor/ethnum/.cargo_vcs_info.json | 6 + vendor/ethnum/.devcontainer/devcontainer.json | 4 + vendor/ethnum/.github/dependabot.yml | 7 + vendor/ethnum/.github/workflows/release.yml | 19 + vendor/ethnum/.github/workflows/rust.yml | 43 + vendor/ethnum/.gitignore | 3 + vendor/ethnum/Cargo.toml | 48 + vendor/ethnum/Cargo.toml.orig | 31 + vendor/ethnum/LICENSE-APACHE | 177 ++ vendor/ethnum/LICENSE-MIT | 25 + vendor/ethnum/README.md | 169 ++ vendor/ethnum/src/error.rs | 65 + vendor/ethnum/src/fmt.rs | 152 ++ vendor/ethnum/src/int.rs | 286 +++ vendor/ethnum/src/int/api.rs | 2263 +++++++++++++++++ vendor/ethnum/src/int/cmp.rs | 57 + vendor/ethnum/src/int/convert.rs | 199 ++ vendor/ethnum/src/int/fmt.rs | 69 + vendor/ethnum/src/int/iter.rs | 13 + vendor/ethnum/src/int/ops.rs | 326 +++ vendor/ethnum/src/int/parse.rs | 151 ++ vendor/ethnum/src/intrinsics.rs | 37 + vendor/ethnum/src/intrinsics/cast.rs | 17 + vendor/ethnum/src/intrinsics/llvm.rs | 92 + vendor/ethnum/src/intrinsics/native.rs | 13 + vendor/ethnum/src/intrinsics/native/add.rs | 36 + vendor/ethnum/src/intrinsics/native/ctz.rs | 16 + vendor/ethnum/src/intrinsics/native/divmod.rs | 571 +++++ vendor/ethnum/src/intrinsics/native/mul.rs | 119 + vendor/ethnum/src/intrinsics/native/rot.rs | 15 + vendor/ethnum/src/intrinsics/native/shl.rs | 34 + vendor/ethnum/src/intrinsics/native/shr.rs | 70 + vendor/ethnum/src/intrinsics/native/sub.rs | 36 + vendor/ethnum/src/intrinsics/signed.rs | 85 + vendor/ethnum/src/lib.rs | 133 + vendor/ethnum/src/macros/cmp.rs | 42 + vendor/ethnum/src/macros/fmt.rs | 86 + vendor/ethnum/src/macros/iter.rs | 31 + vendor/ethnum/src/macros/ops.rs | 542 ++++ vendor/ethnum/src/macros/parse.rs | 34 + vendor/ethnum/src/parse.rs | 205 ++ vendor/ethnum/src/serde.rs | 1579 ++++++++++++ vendor/ethnum/src/uint.rs | 289 +++ vendor/ethnum/src/uint/api.rs | 1866 ++++++++++++++ vendor/ethnum/src/uint/cmp.rs | 48 + vendor/ethnum/src/uint/convert.rs | 198 ++ vendor/ethnum/src/uint/fmt.rs | 51 + vendor/ethnum/src/uint/iter.rs | 7 + vendor/ethnum/src/uint/ops.rs | 305 +++ vendor/ethnum/src/uint/parse.rs | 115 + 53 files changed, 10906 insertions(+), 60 deletions(-) create mode 100644 vendor/ethnum/.cargo-ok create mode 100644 vendor/ethnum/.cargo_vcs_info.json create mode 100644 vendor/ethnum/.devcontainer/devcontainer.json create mode 100644 vendor/ethnum/.github/dependabot.yml create mode 100644 vendor/ethnum/.github/workflows/release.yml create mode 100644 vendor/ethnum/.github/workflows/rust.yml create mode 100644 vendor/ethnum/.gitignore create mode 100644 vendor/ethnum/Cargo.toml create mode 100644 vendor/ethnum/Cargo.toml.orig create mode 100644 vendor/ethnum/LICENSE-APACHE create mode 100644 vendor/ethnum/LICENSE-MIT create mode 100644 vendor/ethnum/README.md create mode 100644 vendor/ethnum/src/error.rs create mode 100644 vendor/ethnum/src/fmt.rs create mode 100644 vendor/ethnum/src/int.rs create mode 100644 vendor/ethnum/src/int/api.rs create mode 100644 vendor/ethnum/src/int/cmp.rs create mode 100644 vendor/ethnum/src/int/convert.rs create mode 100644 vendor/ethnum/src/int/fmt.rs create mode 100644 vendor/ethnum/src/int/iter.rs create mode 100644 vendor/ethnum/src/int/ops.rs create mode 100644 vendor/ethnum/src/int/parse.rs create mode 100644 vendor/ethnum/src/intrinsics.rs create mode 100644 vendor/ethnum/src/intrinsics/cast.rs create mode 100644 vendor/ethnum/src/intrinsics/llvm.rs create mode 100644 vendor/ethnum/src/intrinsics/native.rs create mode 100644 vendor/ethnum/src/intrinsics/native/add.rs create mode 100644 vendor/ethnum/src/intrinsics/native/ctz.rs create mode 100644 vendor/ethnum/src/intrinsics/native/divmod.rs create mode 100644 vendor/ethnum/src/intrinsics/native/mul.rs create mode 100644 vendor/ethnum/src/intrinsics/native/rot.rs create mode 100644 vendor/ethnum/src/intrinsics/native/shl.rs create mode 100644 vendor/ethnum/src/intrinsics/native/shr.rs create mode 100644 vendor/ethnum/src/intrinsics/native/sub.rs create mode 100644 vendor/ethnum/src/intrinsics/signed.rs create mode 100644 vendor/ethnum/src/lib.rs create mode 100644 vendor/ethnum/src/macros/cmp.rs create mode 100644 vendor/ethnum/src/macros/fmt.rs create mode 100644 vendor/ethnum/src/macros/iter.rs create mode 100644 vendor/ethnum/src/macros/ops.rs create mode 100644 vendor/ethnum/src/macros/parse.rs create mode 100644 vendor/ethnum/src/parse.rs create mode 100644 vendor/ethnum/src/serde.rs create mode 100644 vendor/ethnum/src/uint.rs create mode 100644 vendor/ethnum/src/uint/api.rs create mode 100644 vendor/ethnum/src/uint/cmp.rs create mode 100644 vendor/ethnum/src/uint/convert.rs create mode 100644 vendor/ethnum/src/uint/fmt.rs create mode 100644 vendor/ethnum/src/uint/iter.rs create mode 100644 vendor/ethnum/src/uint/ops.rs create mode 100644 vendor/ethnum/src/uint/parse.rs diff --git a/Cargo.lock b/Cargo.lock index b55f279..9c83bbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,9 +37,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "backtrace" @@ -103,9 +103,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "block-buffer" @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "byteorder" @@ -142,9 +142,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.58" +version = "1.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" +checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" dependencies = [ "find-msvc-tools", "shlex", @@ -158,9 +158,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "num-traits", @@ -384,9 +384,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "elliptic-curve" @@ -432,14 +432,12 @@ checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" [[package]] name = "ethnum" version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "ff" @@ -469,6 +467,30 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + [[package]] name = "generic-array" version = "0.14.9" @@ -501,10 +523,21 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", ] +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", +] + [[package]] name = "gimli" version = "0.28.1" @@ -633,11 +666,12 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.92" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4c90f45aa2e6eacbe8645f77fdea542ac97a494bcd117a67df9ff4d611f995" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ - "once_cell", + "cfg-if", + "futures-util", "wasm-bindgen", ] @@ -666,9 +700,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.183" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" @@ -684,15 +718,15 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "miniz_oxide" @@ -771,6 +805,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + [[package]] name = "pkcs8" version = "0.10.2" @@ -783,9 +823,9 @@ dependencies = [ [[package]] name = "platforms" -version = "3.10.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6001d2ac55b4eb1ca634c65fc06555068b8dd89c9f20fd92064e5341a436e63" +checksum = "9245c6e7c5a6bcdd7977fdf6d1e1c67f4cc2d0d58c041df0ea5940953033e6ca" [[package]] name = "powerfmt" @@ -840,7 +880,7 @@ dependencies = [ "bit-vec", "bitflags", "num-traits", - "rand 0.9.2", + "rand 0.9.5", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", @@ -870,6 +910,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" version = "0.8.5" @@ -883,9 +929,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -940,9 +986,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rfc6979" @@ -956,9 +1002,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" [[package]] name = "rustc_version" @@ -984,9 +1030,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "rusty-fork" @@ -1022,9 +1068,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -1110,9 +1156,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signature" @@ -1124,11 +1170,17 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "soroban-builtin-sdk-macros" @@ -1386,7 +1438,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys", @@ -1445,9 +1497,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "unarray" @@ -1484,18 +1536,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.115" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6523d69017b7633e396a89c5efab138161ed5aafcbc8d3e5c5a42ae38f50495a" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -1506,9 +1558,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.115" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3a6c758eb2f701ed3d052ff5737f5bfe6614326ea7f3bbac7156192dc32e67" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1516,9 +1568,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.115" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921de2737904886b52bcbb237301552d05969a6f9c40d261eb0533c8b055fedf" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", @@ -1529,9 +1581,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.115" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93e946af942b58934c604527337bad9ae33ba1d5c6900bbb41c2c07c2364a93" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] @@ -1642,9 +1694,9 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.51.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "zerocopy" @@ -1669,6 +1721,6 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" diff --git a/Cargo.toml b/Cargo.toml index 58a45d2..666024b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,14 @@ derive_arbitrary = "=1.3.2" proptest = "1" soroban-sdk = { version = "20.1.0", features = ["testutils"] } +[patch.crates-io] +# soroban-sdk 20.1.0 pins ethnum 1.5.0, which fails to compile on Rust 1.80+ +# due to a transmute size mismatch with TryFromIntError. We vendor the crate and +# apply the upstream fix from ethnum v1.5.3 to restore compatibility. +# TODO: Remove this vendored patch once the project upgrades to a soroban-sdk +# version that depends on ethnum >= 1.5.3 (or drops the exact 1.5.0 pin). +ethnum = { path = "vendor/ethnum" } + [profile.release] opt-level = "z" overflow-checks = true diff --git a/vendor/ethnum/.cargo-ok b/vendor/ethnum/.cargo-ok new file mode 100644 index 0000000..5f8b795 --- /dev/null +++ b/vendor/ethnum/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/vendor/ethnum/.cargo_vcs_info.json b/vendor/ethnum/.cargo_vcs_info.json new file mode 100644 index 0000000..a86a29b --- /dev/null +++ b/vendor/ethnum/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "1cd9388a0a6c4e8b7e7e0e5dc49bda6c68f2d44b" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/vendor/ethnum/.devcontainer/devcontainer.json b/vendor/ethnum/.devcontainer/devcontainer.json new file mode 100644 index 0000000..77d2a49 --- /dev/null +++ b/vendor/ethnum/.devcontainer/devcontainer.json @@ -0,0 +1,4 @@ +{ + "name": "Rust", + "image": "mcr.microsoft.com/devcontainers/rust:1-1-bookworm" +} diff --git a/vendor/ethnum/.github/dependabot.yml b/vendor/ethnum/.github/dependabot.yml new file mode 100644 index 0000000..f94a35e --- /dev/null +++ b/vendor/ethnum/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 + +updates: +- package-ecosystem: cargo + directory: "/" + schedule: + interval: daily diff --git a/vendor/ethnum/.github/workflows/release.yml b/vendor/ethnum/.github/workflows/release.yml new file mode 100644 index 0000000..676c032 --- /dev/null +++ b/vendor/ethnum/.github/workflows/release.yml @@ -0,0 +1,19 @@ +name: Release + +on: + push: + tags: + - "v*" + +jobs: + publish: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Publish + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: | + cargo publish diff --git a/vendor/ethnum/.github/workflows/rust.yml b/vendor/ethnum/.github/workflows/rust.yml new file mode 100644 index 0000000..6f7b515 --- /dev/null +++ b/vendor/ethnum/.github/workflows/rust.yml @@ -0,0 +1,43 @@ +name: Rust + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + CARGO_TERM_COLOR: always + +jobs: + check: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Install Clang + run: | + LLVM_VERSION=$(rustc --version --verbose | sed -n 's/^LLVM version: \([0-9]*\).*$/\1/p') + curl -s https://apt.llvm.org/llvm.sh | sudo bash -s -- ${LLVM_VERSION} + echo "CLANG=clang-${LLVM_VERSION}" >> ${GITHUB_ENV} + - name: Lint + run: | + cargo fmt --all -- --check + cargo clippy --workspace --all-targets -- -D warnings + - name: Test Native Intrinsics + run: | + cargo test + cargo test --release + - name: Test LLVM Intrinsics + run: | + cargo clippy --features llvm-intrinsics --all-targets -- -D warnings + cargo test --features llvm-intrinsics + cargo test --features llvm-intrinsics --release + - name: Test Additional Features + run: | + cargo clippy --features serde --all-targets -- -D warnings + cargo test --features serde + cargo test --features serde --release + - name: Build Deprecated Features + run: | + cargo clippy --features macros --all-targets -- -D warnings diff --git a/vendor/ethnum/.gitignore b/vendor/ethnum/.gitignore new file mode 100644 index 0000000..53cca9d --- /dev/null +++ b/vendor/ethnum/.gitignore @@ -0,0 +1,3 @@ +target/ +Cargo.lock +.idea \ No newline at end of file diff --git a/vendor/ethnum/Cargo.toml b/vendor/ethnum/Cargo.toml new file mode 100644 index 0000000..5ee56aa --- /dev/null +++ b/vendor/ethnum/Cargo.toml @@ -0,0 +1,48 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "ethnum" +version = "1.5.0" +authors = ["Nicholas Rodrigues Lordello "] +description = "256-bit integer implementation" +homepage = "https://github.com/nlordell/ethnum-rs" +documentation = "https://docs.rs/ethnum" +readme = "README.md" +keywords = [ + "integer", + "u256", + "ethereum", +] +categories = [ + "cryptography::cryptocurrencies", + "mathematics", + "no-std", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/nlordell/ethnum-rs" + +[package.metadata.docs.rs] +features = ["serde"] + +[dependencies.ethnum-intrinsics] +version = "=1.2.0" +optional = true + +[dependencies.serde] +version = "1" +optional = true +default-features = false + +[features] +llvm-intrinsics = ["ethnum-intrinsics"] +macros = [] diff --git a/vendor/ethnum/Cargo.toml.orig b/vendor/ethnum/Cargo.toml.orig new file mode 100644 index 0000000..8fd0596 --- /dev/null +++ b/vendor/ethnum/Cargo.toml.orig @@ -0,0 +1,31 @@ +[package] +name = "ethnum" +version = "1.5.0" +authors = ["Nicholas Rodrigues Lordello "] +edition = "2021" +description = "256-bit integer implementation" +documentation = "https://docs.rs/ethnum" +readme = "README.md" +homepage = "https://github.com/nlordell/ethnum-rs" +repository = "https://github.com/nlordell/ethnum-rs" +license = "MIT OR Apache-2.0" +keywords = ["integer", "u256", "ethereum"] +categories = ["cryptography::cryptocurrencies", "mathematics", "no-std"] + +[package.metadata.docs.rs] +features = ["serde"] + +[workspace] +members = [ + "bench", + "fuzz", + "intrinsics", +] + +[features] +llvm-intrinsics = ["ethnum-intrinsics"] +macros = [] # deprecated + +[dependencies] +ethnum-intrinsics = { version = "=1.2.0", path = "intrinsics", optional = true } +serde = { version = "1", default-features = false, optional = true } diff --git a/vendor/ethnum/LICENSE-APACHE b/vendor/ethnum/LICENSE-APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/vendor/ethnum/LICENSE-APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/vendor/ethnum/LICENSE-MIT b/vendor/ethnum/LICENSE-MIT new file mode 100644 index 0000000..9d45714 --- /dev/null +++ b/vendor/ethnum/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2020-2022 Nicholas Rodrigues Lordello + +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. diff --git a/vendor/ethnum/README.md b/vendor/ethnum/README.md new file mode 100644 index 0000000..e9aecc0 --- /dev/null +++ b/vendor/ethnum/README.md @@ -0,0 +1,169 @@ +# `ethnum` + +This crate provides implementations for 256-bit integers, the primitive integer +type in Ethereum. This implementation is meant to be as close as possible to +Rust integer primitives, implementing the same methods and traits. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +ethnum = "1" +``` + +The API follows the Rust `{i,u}N` primitive types as close as possible. + +### Macros + +This crate provides `const fn` based macros for 256-bit integer literals. This +allows you to specify 256-bit signed and unsigned integer literals (that can, +for example, be used as `const`s) that are larger than the largest native +integer literal (`i128::MIN` and `i128::MAX` for signed integers and `u128::MAX` +for unsigned integers): + +```rust +int!("-57896044618658097711785492504343953926634992332820282019728792003956564819968"); +int!("57896044618658097711785492504343953926634992332820282019728792003956564819967"); +uint!("115792089237316195423570985008687907853269984665640564039457584007913129639935"); +``` + +Note that these literals support prefixes (`0b` for binary, `0o` for octal, and +`0x` for hexadecimal) as well as `_` and whitespace separators: + +```rust +int!("-0b1010101010101010101010101010101010101010101010101010101010101010 + 0101010101010101010101010101010101010101010101010101010101010101"); +int!("0o 0123 4567"); +uint!("0xffff_ffff"); +``` + +## Features + +### `macros` + +The `macros` feature used to enable 256-bit integer literals via procedural +macros. However, this crate now implements these macros with `const fn`, so the +feature is now deprecated and the macros are now always available. The feature +is still around to not break semantic versioning, but will be removed in a +version `2`. + +### `serde` + +The `serde` feature adds support for `serde` serialization and deserialization. +By default, the 256-bit integer types are serialized as prefixed hexadecimal +strings. Various serialization helpers are also provided for more fine-grained +control over how serialization is performed. + +## Intrinsics + +The 256-bit integers uses intrinsics based on two implementations: + +### Native Rust Implementation + +The integer intrinsics are implemented using standard Rust. The more complicated +operations such as multiplication and division are ported from C compiler +intrinsics for implementing equivalent 128-bit operations on 64-bit systems (or +64-bit operations on 32-bit systems). In general, these are ported from the +Clang `compiler-rt` support routines. + +This is the default implementation used by the crate, and in general is quite +well optimized. When using native the implementation, there are no additional +dependencies for this crate. + +### LLVM Generated Implementation + +Alternatively, `ethnum` can use LLVM-generated intrinsics for base 256-bit +integer operations. This takes advantage of the fact that LLVM IR supports +arbitrarily sized integer operations (such as `@llvm.uadd.with.overflow.i256` +for overflowing unsigned addition). This will produce more optimized assembly +for things like addition and multiplication. + +However, there are a couple downsides to using LLVM-generated intrinsics. First +of all, Clang is required in order to compile the LLVM IR. Additionally, Rust +usually optimizes when compiling and linking Rust code (and not externally +linked code), this means that these intrinsics cannot be inlined adding an extra +function call overhead in some cases which make it perform worse than the native +Rust implementation despite having more optimized assembly. Luckily, Rust +currently has support for linker plugin LTO to enable optimizations during the +link step, enabling optimizations with Clang-compiled LLVM IR. + +In order to use LLVM-generated intrinsics, enable the `llvm-intrinsics` feature: + +```toml +ethnum = { version = "1", features = ["llvm-intrinsics"] } +``` + +And, genererally it is a good idea to compile with `linker-plugin-lto` enabled +in order to actually take advantage of the the optimized assembly: + +```sh +RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build +``` + +Note that **the `clang` version must match the `rustc` LLVM version**. If not, +it is possible to encounter errors when running the `ethnum-intrinsics` build +script. You can verify the LLVM version used by `rustc` with: + +```sh +rustc --version --verbose | grep LLVM +``` + +In particular, this affects macOS which ships its own `clang` binary. The +`ethnum-intrinsics` build script accepts a `CLANG` environment variable to +specity a specific `clang` executable path to use. Using the major LLVM version +from the command above: + +``` +brew install llvm@${LLVM_VERSION} +CLANG=/opt/homebrew/opt/llvm@${LLVM_VERSION}/bin/clang cargo build +``` + +### API Stability + +The instinsics are exported under `ethnum::intrinsics`. That being said, be +careful when using these intrinsics directly. Semantic versioning API +compatibility is **not guaranteed** for any of these intrinsics. + +If you do you use these in your projects, it is recommended to use strict +versioning: + +```toml +[dependencies] +ethnum = "=x.y.z" +``` + +This will ensure commands like `cargo update` won't change the version of the +`ethnum` dependency. + +## Benchmarking + +The `ethnum-bench` crate implements `criterion` benchmarks for performance of +integer intrinsics: + +```sh +cargo bench -p ethnum-bench +RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo bench -p ethnum-bench --features llvm-intrinsics +``` + +## Fuzzing + +The `ethnum-fuzz` crate implements an AFL fuzzing target (as well as some +utilities for working with `cargo afl`). Internally, it converts the signed +256-bit integer types to `num::BigInt` and uses its operation implementations as +a reference. + +In order to start fuzzing: + +```sh +cargo install --force cargo-afl +cargo run -p ethnum-fuzz --bin init target/fuzz +cargo afl build -p ethnum-fuzz --bin fuzz +cargo afl fuzz -i target/fuzz/in -o target/fuzz/out target/debug/fuzz +``` + +In order to replay crashes: + +```sh +cargo run -p ethnum-fuzz --bin dump target/fuzz/out/default/crashes/FILE +``` diff --git a/vendor/ethnum/src/error.rs b/vendor/ethnum/src/error.rs new file mode 100644 index 0000000..117dd5a --- /dev/null +++ b/vendor/ethnum/src/error.rs @@ -0,0 +1,65 @@ +//! Module with safe helpers for creating error variants for standard library +//! errors without public constructors. + +use core::num::{IntErrorKind, ParseIntError, TryFromIntError}; + +/// Returns a `ParseIntError` with the specified `IntErrorKind`. +pub const fn pie(kind: IntErrorKind) -> ParseIntError { + match kind { + IntErrorKind::Empty => u8_parse_error(""), + IntErrorKind::InvalidDigit => u8_parse_error("?"), + IntErrorKind::PosOverflow => u8_parse_error("256"), + IntErrorKind::NegOverflow => i8_parse_error("-129"), + _ => unreachable!(), + } +} + +const fn u8_parse_error(s: &str) -> ParseIntError { + let Err(err) = u8::from_str_radix(s, 10) else { + panic!("not a parse error!"); + }; + err +} + +const fn i8_parse_error(s: &str) -> ParseIntError { + let Err(err) = i8::from_str_radix(s, 10) else { + panic!("not a parse error!"); + }; + err +} + +/// Returns a `TryFromIntError`. +pub fn tfie() -> TryFromIntError { + u8::try_from(-1i8).unwrap_err() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[allow(clippy::from_str_radix_10)] + fn parse_int_error() { + assert_eq!( + pie(IntErrorKind::Empty), + u8::from_str_radix("", 2).unwrap_err(), + ); + assert_eq!( + pie(IntErrorKind::InvalidDigit), + u8::from_str_radix("?", 2).unwrap_err(), + ); + assert_eq!( + pie(IntErrorKind::PosOverflow), + u8::from_str_radix("zzz", 36).unwrap_err(), + ); + assert_eq!( + pie(IntErrorKind::NegOverflow), + i8::from_str_radix("-1337", 10).unwrap_err(), + ); + } + + #[test] + fn try_from_int_error() { + assert_eq!(tfie(), u8::try_from(-1).unwrap_err()); + } +} diff --git a/vendor/ethnum/src/fmt.rs b/vendor/ethnum/src/fmt.rs new file mode 100644 index 0000000..7c76f41 --- /dev/null +++ b/vendor/ethnum/src/fmt.rs @@ -0,0 +1,152 @@ +//! Module with common integer formatting logic for implementing the standard +//! library `core::fmt` traits. +//! +//! Most of these implementations were ported from the Rust standard library's +//! implementation for primitive integer types: +//! + +use crate::uint::U256; +use core::{fmt, mem::MaybeUninit, ptr, slice, str}; + +pub(crate) trait GenericRadix: Sized { + const BASE: u8; + const PREFIX: &'static str; + fn digit(x: u8) -> u8; + fn fmt_u256(&self, mut x: U256, is_nonnegative: bool, f: &mut fmt::Formatter) -> fmt::Result { + // The radix can be as low as 2, so we need a buffer of at least 256 + // characters for a base 2 number. + let zero = U256::ZERO; + let mut buf = [MaybeUninit::::uninit(); 256]; + let mut curr = buf.len(); + let base = U256::from(Self::BASE); + // Accumulate each digit of the number from the least significant + // to the most significant figure. + for byte in buf.iter_mut().rev() { + let n = x % base; // Get the current place value. + x /= base; // Deaccumulate the number. + byte.write(Self::digit(n.as_u8())); // Store the digit in the buffer. + curr -= 1; + if x == zero { + // No more digits left to accumulate. + break; + }; + } + let buf = &buf[curr..]; + // SAFETY: The only chars in `buf` are created by `Self::digit` which are assumed to be + // valid UTF-8 + let buf = unsafe { + str::from_utf8_unchecked(slice::from_raw_parts( + &buf[0] as *const _ as *const u8, + buf.len(), + )) + }; + f.pad_integral(is_nonnegative, Self::PREFIX, buf) + } +} + +/// A binary (base 2) radix +#[derive(Clone, PartialEq)] +pub(crate) struct Binary; + +/// An octal (base 8) radix +#[derive(Clone, PartialEq)] +pub(crate) struct Octal; + +/// A hexadecimal (base 16) radix, formatted with lower-case characters +#[derive(Clone, PartialEq)] +pub(crate) struct LowerHex; + +/// A hexadecimal (base 16) radix, formatted with upper-case characters +#[derive(Clone, PartialEq)] +pub(crate) struct UpperHex; + +macro_rules! radix { + ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => { + impl GenericRadix for $T { + const BASE: u8 = $base; + const PREFIX: &'static str = $prefix; + fn digit(x: u8) -> u8 { + match x { + $($x => $conv,)+ + x => panic!("number not in the range 0..={}: {}", Self::BASE - 1, x), + } + } + } + } +} + +radix! { Binary, 2, "0b", x @ 0 ..= 1 => b'0' + x } +radix! { Octal, 8, "0o", x @ 0 ..= 7 => b'0' + x } +radix! { LowerHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'a' + (x - 10) } +radix! { UpperHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'A' + (x - 10) } + +const DEC_DIGITS_LUT: &[u8; 200] = b"\ + 0001020304050607080910111213141516171819\ + 2021222324252627282930313233343536373839\ + 4041424344454647484950515253545556575859\ + 6061626364656667686970717273747576777879\ + 8081828384858687888990919293949596979899"; + +pub(crate) fn fmt_u256(mut n: U256, is_nonnegative: bool, f: &mut fmt::Formatter) -> fmt::Result { + // 2^256 is about 1*10^78, so 79 gives an extra byte of space + let mut buf = [MaybeUninit::::uninit(); 79]; + let mut curr = buf.len() as isize; + let buf_ptr = &mut buf[0] as *mut _ as *mut u8; + let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + + // SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we + // can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show + // that it's OK to copy into `buf_ptr`, notice that at the beginning + // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at + // each step this is kept the same as `n` is divided. Since `n` is always + // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` + // is safe to access. + unsafe { + // eagerly decode 4 characters at a time + while n >= 10000 { + let rem = (n % 10000).as_isize(); + n /= 10000; + + let d1 = (rem / 100) << 1; + let d2 = (rem % 100) << 1; + curr -= 4; + + // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since + // otherwise `curr < 0`. But then `n` was originally at least `10000^10` + // which is `10^40 > 2^128 > n`. + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2); + } + + // if we reach here numbers are <= 9999, so at most 4 chars long + let mut n = n.as_isize(); // possibly reduce 64bit math + + // decode 2 more chars, if > 2 chars + if n >= 100 { + let d1 = (n % 100) << 1; + n /= 100; + curr -= 2; + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + } + + // decode last 1 or 2 chars + if n < 10 { + curr -= 1; + *buf_ptr.offset(curr) = (n as u8) + b'0'; + } else { + let d1 = n << 1; + curr -= 2; + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + } + } + + // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid + // UTF-8 since `DEC_DIGITS_LUT` is + let buf_slice = unsafe { + str::from_utf8_unchecked(slice::from_raw_parts( + buf_ptr.offset(curr), + buf.len() - curr as usize, + )) + }; + f.pad_integral(is_nonnegative, "", buf_slice) +} diff --git a/vendor/ethnum/src/int.rs b/vendor/ethnum/src/int.rs new file mode 100644 index 0000000..316e1e5 --- /dev/null +++ b/vendor/ethnum/src/int.rs @@ -0,0 +1,286 @@ +//! Root module for 256-bit signed integer type. + +mod api; +mod cmp; +mod convert; +mod fmt; +mod iter; +mod ops; +mod parse; + +pub use self::convert::AsI256; +use crate::uint::U256; +use core::num::ParseIntError; + +/// A 256-bit signed integer type. +#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)] +#[repr(transparent)] +pub struct I256(pub [i128; 2]); + +impl I256 { + /// The additive identity for this integer type, i.e. `0`. + pub const ZERO: Self = I256([0; 2]); + + /// The multiplicative identity for this integer type, i.e. `1`. + pub const ONE: Self = I256::new(1); + + /// The multiplicative inverse for this integer type, i.e. `-1`. + pub const MINUS_ONE: Self = I256::new(-1); + + /// Creates a new 256-bit integer value from a primitive `i128` integer. + #[inline] + pub const fn new(value: i128) -> Self { + I256::from_words(value >> 127, value) + } + + /// Creates a new 256-bit integer value from high and low words. + #[inline] + pub const fn from_words(hi: i128, lo: i128) -> Self { + #[cfg(target_endian = "little")] + { + I256([lo, hi]) + } + #[cfg(target_endian = "big")] + { + I256([hi, lo]) + } + } + + /// Splits a 256-bit integer into high and low words. + #[inline] + pub const fn into_words(self) -> (i128, i128) { + #[cfg(target_endian = "little")] + { + let I256([lo, hi]) = self; + (hi, lo) + } + #[cfg(target_endian = "big")] + { + let I256([hi, lo]) = self; + (hi, lo) + } + } + + /// Get the low 128-bit word for this signed integer. + #[inline] + pub fn low(&self) -> &i128 { + #[cfg(target_endian = "little")] + { + &self.0[0] + } + #[cfg(target_endian = "big")] + { + &self.0[1] + } + } + + /// Get the low 128-bit word for this signed integer as a mutable reference. + #[inline] + pub fn low_mut(&mut self) -> &mut i128 { + #[cfg(target_endian = "little")] + { + &mut self.0[0] + } + #[cfg(target_endian = "big")] + { + &mut self.0[1] + } + } + + /// Get the high 128-bit word for this signed integer. + #[inline] + pub fn high(&self) -> &i128 { + #[cfg(target_endian = "little")] + { + &self.0[1] + } + #[cfg(target_endian = "big")] + { + &self.0[0] + } + } + + /// Get the high 128-bit word for this signed integer as a mutable + /// reference. + #[inline] + pub fn high_mut(&mut self) -> &mut i128 { + #[cfg(target_endian = "little")] + { + &mut self.0[1] + } + #[cfg(target_endian = "big")] + { + &mut self.0[0] + } + } + + /// Converts a prefixed string slice in base 16 to an integer. + /// + /// The string is expected to be an optional `+` or `-` sign followed by + /// the `0x` prefix and finally the digits. Leading and trailing whitespace + /// represent an error. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::from_str_hex("0x2A"), Ok(I256::new(42))); + /// assert_eq!(I256::from_str_hex("-0xa"), Ok(I256::new(-10))); + /// ``` + pub fn from_str_hex(src: &str) -> Result { + crate::parse::from_str_radix(src, 16, Some("0x")) + } + + /// Converts a prefixed string slice in a base determined by the prefix to + /// an integer. + /// + /// The string is expected to be an optional `+` or `-` sign followed by + /// the one of the supported prefixes and finally the digits. Leading and + /// trailing whitespace represent an error. The base is determined based + /// on the prefix: + /// + /// * `0b`: base `2` + /// * `0o`: base `8` + /// * `0x`: base `16` + /// * no prefix: base `10` + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::from_str_prefixed("-0b101"), Ok(I256::new(-0b101))); + /// assert_eq!(I256::from_str_prefixed("0o17"), Ok(I256::new(0o17))); + /// assert_eq!(I256::from_str_prefixed("-0xa"), Ok(I256::new(-0xa))); + /// assert_eq!(I256::from_str_prefixed("42"), Ok(I256::new(42))); + /// ``` + pub fn from_str_prefixed(src: &str) -> Result { + crate::parse::from_str_prefixed(src) + } + + /// Same as [`I256::from_str_prefixed`] but as a `const fn`. This method is + /// not intended to be used directly but rather through the [`crate::int`] + /// macro. + #[doc(hidden)] + pub const fn const_from_str_prefixed(src: &str) -> Self { + parse::const_from_str_prefixed(src) + } + + /// Cast to a primitive `i8`. + #[inline] + pub const fn as_i8(self) -> i8 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `i16`. + #[inline] + pub const fn as_i16(self) -> i16 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `i32`. + #[inline] + pub const fn as_i32(self) -> i32 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `i64`. + #[inline] + pub const fn as_i64(self) -> i64 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `i128`. + #[inline] + pub const fn as_i128(self) -> i128 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `u8`. + #[inline] + pub const fn as_u8(self) -> u8 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `u16`. + #[inline] + pub const fn as_u16(self) -> u16 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `u32`. + #[inline] + pub const fn as_u32(self) -> u32 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `u64`. + #[inline] + pub const fn as_u64(self) -> u64 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `u128`. + #[inline] + pub const fn as_u128(self) -> u128 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a `U256`. + #[inline] + pub const fn as_u256(self) -> U256 { + let Self([a, b]) = self; + U256([a as _, b as _]) + } + + /// Cast to a primitive `isize`. + #[inline] + pub const fn as_isize(self) -> isize { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `usize`. + #[inline] + pub const fn as_usize(self) -> usize { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `f32`. + #[inline] + pub fn as_f32(self) -> f32 { + self.as_f64() as _ + } + + /// Cast to a primitive `f64`. + #[inline] + pub fn as_f64(self) -> f64 { + let sign = self.signum128() as f64; + self.unsigned_abs().as_f64() * sign + } +} + +#[cfg(test)] +mod tests { + use crate::I256; + + #[test] + #[allow(clippy::float_cmp)] + fn converts_to_f64() { + assert_eq!((-I256::from_words(1, 0)).as_f64(), -(2.0f64.powi(128))) + } +} diff --git a/vendor/ethnum/src/int/api.rs b/vendor/ethnum/src/int/api.rs new file mode 100644 index 0000000..bf2b143 --- /dev/null +++ b/vendor/ethnum/src/int/api.rs @@ -0,0 +1,2263 @@ +//! Module containing integer aritimetic methods closely following the Rust +//! standard library API for `iN` types. + +use crate::{intrinsics, I256, U256}; +use core::{ + mem::{self, MaybeUninit}, + num::ParseIntError, +}; + +impl I256 { + /// The smallest value that can be represented by this integer type, + /// -2255. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!( + /// I256::MIN.to_string(), + /// "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + /// ); + /// ``` + pub const MIN: Self = Self::from_words(i128::MIN, 0); + + /// The largest value that can be represented by this integer type, + /// 2255 - 1. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!( + /// I256::MAX.to_string(), + /// "57896044618658097711785492504343953926634992332820282019728792003956564819967", + /// ); + /// ``` + pub const MAX: Self = Self::from_words(i128::MAX, -1); + + /// The size of this integer type in bits. + /// + /// # Examples + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::BITS, 256); + /// ``` + pub const BITS: u32 = 256; + + /// Converts a string slice in a given base to an integer. + /// + /// The string is expected to be an optional `+` or `-` sign followed by + /// digits. Leading and trailing whitespace represent an error. Digits are a + /// subset of these characters, depending on `radix`: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::from_str_radix("A", 16), Ok(I256::new(10))); + /// ``` + pub fn from_str_radix(src: &str, radix: u32) -> Result { + crate::parse::from_str_radix(src, radix, None) + } + + /// Returns the number of ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::new(0b100_0000); + /// + /// assert_eq!(n.count_ones(), 1); + /// ``` + /// + #[doc(alias = "popcount")] + #[doc(alias = "popcnt")] + #[inline] + pub const fn count_ones(self) -> u32 { + let Self([a, b]) = self; + a.count_ones() + b.count_ones() + } + + /// Returns the number of zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::MAX.count_zeros(), 1); + /// ``` + #[inline] + pub const fn count_zeros(self) -> u32 { + let Self([a, b]) = self; + a.count_zeros() + b.count_zeros() + } + + /// Returns the number of leading zeros in the binary representation of + /// `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::new(-1); + /// + /// assert_eq!(n.leading_zeros(), 0); + /// ``` + #[inline(always)] + pub fn leading_zeros(self) -> u32 { + intrinsics::signed::ictlz(&self) + } + + /// Returns the number of trailing zeros in the binary representation of + /// `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::new(-4); + /// + /// assert_eq!(n.trailing_zeros(), 2); + /// ``` + #[inline(always)] + pub fn trailing_zeros(self) -> u32 { + intrinsics::signed::icttz(&self) + } + + /// Returns the number of leading ones in the binary representation of + /// `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::new(-1); + /// + /// assert_eq!(n.leading_ones(), 256); + /// ``` + #[inline] + pub fn leading_ones(self) -> u32 { + (!self).leading_zeros() + } + + /// Returns the number of trailing ones in the binary representation of + /// `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::new(3); + /// + /// assert_eq!(n.trailing_ones(), 2); + /// ``` + #[inline] + pub fn trailing_ones(self) -> u32 { + (!self).trailing_zeros() + } + + /// Shifts the bits to the left by a specified amount, `n`, + /// wrapping the truncated bits to the end of the resulting integer. + /// + /// Please note this isn't the same operation as the `<<` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::from_words( + /// 0x13f40000000000000000000000000000, + /// 0x00000000000000000000000000004f76, + /// ); + /// let m = I256::new(0x4f7613f4); + /// + /// assert_eq!(n.rotate_left(16), m); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn rotate_left(self, n: u32) -> Self { + let mut r = MaybeUninit::uninit(); + intrinsics::signed::irol3(&mut r, &self, n); + unsafe { r.assume_init() } + } + + /// Shifts the bits to the right by a specified amount, `n`, + /// wrapping the truncated bits to the beginning of the resulting + /// integer. + /// + /// Please note this isn't the same operation as the `>>` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::new(0x4f7613f4); + /// let m = I256::from_words( + /// 0x13f40000000000000000000000000000, + /// 0x00000000000000000000000000004f76, + /// ); + /// + /// assert_eq!(n.rotate_right(16), m); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn rotate_right(self, n: u32) -> Self { + let mut r = MaybeUninit::uninit(); + intrinsics::signed::iror3(&mut r, &self, n); + unsafe { r.assume_init() } + } + + /// Reverses the byte order of the integer. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ); + /// + /// assert_eq!( + /// n.swap_bytes(), + /// I256::from_words( + /// 0x1f1e1d1c_1b1a1918_17161514_13121110, + /// 0x0f0e0d0c_0b0a0908_07060504_03020100, + /// ), + /// ); + /// ``` + #[inline] + pub const fn swap_bytes(self) -> Self { + let Self([a, b]) = self; + Self([b.swap_bytes(), a.swap_bytes()]) + } + + /// Reverses the order of bits in the integer. The least significant bit + /// becomes the most significant bit, second least-significant bit becomes + /// second most-significant bit, etc. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ); + /// + /// assert_eq!( + /// n.reverse_bits(), + /// I256::from_words( + /// 0xf878b838_d8589818_e868a828_c8488808_u128 as _, + /// 0xf070b030_d0509010_e060a020_c0408000_u128 as _, + /// ), + /// ); + /// ``` + #[inline] + #[must_use] + pub const fn reverse_bits(self) -> Self { + let Self([a, b]) = self; + Self([b.reverse_bits(), a.reverse_bits()]) + } + + /// Converts an integer from big endian to the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::new(0x1A); + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(I256::from_be(n), n) + /// } else { + /// assert_eq!(I256::from_be(n), n.swap_bytes()) + /// } + /// ``` + #[inline(always)] + pub const fn from_be(x: Self) -> Self { + #[cfg(target_endian = "big")] + { + x + } + #[cfg(not(target_endian = "big"))] + { + x.swap_bytes() + } + } + + /// Converts an integer from little endian to the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::new(0x1A); + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(I256::from_le(n), n) + /// } else { + /// assert_eq!(I256::from_le(n), n.swap_bytes()) + /// } + /// ``` + #[inline(always)] + pub const fn from_le(x: Self) -> Self { + #[cfg(target_endian = "little")] + { + x + } + #[cfg(not(target_endian = "little"))] + { + x.swap_bytes() + } + } + + /// Converts `self` to big endian from the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::new(0x1A); + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(n.to_be(), n) + /// } else { + /// assert_eq!(n.to_be(), n.swap_bytes()) + /// } + /// ``` + #[inline(always)] + pub const fn to_be(self) -> Self { + // or not to be? + #[cfg(target_endian = "big")] + { + self + } + #[cfg(not(target_endian = "big"))] + { + self.swap_bytes() + } + } + + /// Converts `self` to little endian from the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let n = I256::new(0x1A); + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(n.to_le(), n) + /// } else { + /// assert_eq!(n.to_le(), n.swap_bytes()) + /// } + /// ``` + #[inline(always)] + pub const fn to_le(self) -> Self { + #[cfg(target_endian = "little")] + { + self + } + #[cfg(not(target_endian = "little"))] + { + self.swap_bytes() + } + } + + /// Checked integer addition. Computes `self + rhs`, returning `None` + /// if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!((I256::MAX - 2).checked_add(I256::new(1)), Some(I256::MAX - 1)); + /// assert_eq!((I256::MAX - 2).checked_add(I256::new(3)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_add(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_add(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked addition with an unsigned integer. Computes `self + rhs`, + /// returning `None` if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(I256::new(1).checked_add_unsigned(U256::new(2)), Some(I256::new(3))); + /// assert_eq!((I256::MAX - 2).checked_add_unsigned(U256::new(3)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_add_unsigned(self, rhs: U256) -> Option { + let (a, b) = self.overflowing_add_unsigned(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked integer subtraction. Computes `self - rhs`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!((I256::MIN + 2).checked_sub(I256::new(1)), Some(I256::MIN + 1)); + /// assert_eq!((I256::MIN + 2).checked_sub(I256::new(3)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_sub(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_sub(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked subtraction with an unsigned integer. Computes `self - rhs`, + /// returning `None` if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(I256::new(1).checked_sub_unsigned(U256::new(2)), Some(I256::new(-1))); + /// assert_eq!((I256::MIN + 2).checked_sub_unsigned(U256::new(3)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_sub_unsigned(self, rhs: U256) -> Option { + let (a, b) = self.overflowing_sub_unsigned(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked integer multiplication. Computes `self * rhs`, returning `None` + /// if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::MAX.checked_mul(I256::new(1)), Some(I256::MAX)); + /// assert_eq!(I256::MAX.checked_mul(I256::new(2)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_mul(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_mul(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked integer division. Computes `self / rhs`, returning `None` if + /// `rhs == 0` or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!((I256::MIN + 1).checked_div(I256::new(-1)), Some(I256::MAX)); + /// assert_eq!(I256::MIN.checked_div(I256::new(-1)), None); + /// assert_eq!(I256::new(1).checked_div(I256::new(0)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_div(self, rhs: Self) -> Option { + if rhs == 0 || (self == Self::MIN && rhs == -1) { + None + } else { + let mut result = MaybeUninit::uninit(); + intrinsics::signed::idiv3(&mut result, &self, &rhs); + Some(unsafe { result.assume_init() }) + } + } + + /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, + /// returning `None` if `rhs == 0` or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!((I256::MIN + 1).checked_div_euclid(I256::new(-1)), Some(I256::MAX)); + /// assert_eq!(I256::MIN.checked_div_euclid(I256::new(-1)), None); + /// assert_eq!(I256::new(1).checked_div_euclid(I256::new(0)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_div_euclid(self, rhs: Self) -> Option { + if rhs == 0 || (self == Self::MIN && rhs == -1) { + None + } else { + Some(self.div_euclid(rhs)) + } + } + + /// Checked integer remainder. Computes `self % rhs`, returning `None` if + /// `rhs == 0` or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(5).checked_rem(I256::new(2)), Some(I256::new(1))); + /// assert_eq!(I256::new(5).checked_rem(I256::new(0)), None); + /// assert_eq!(I256::MIN.checked_rem(I256::new(-1)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_rem(self, rhs: Self) -> Option { + if rhs == 0 || (self == Self::MIN && rhs == -1) { + None + } else { + let mut result = MaybeUninit::uninit(); + intrinsics::signed::irem3(&mut result, &self, &rhs); + Some(unsafe { result.assume_init() }) + } + } + + /// Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning + /// `None` if `rhs == 0` or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(5).checked_rem_euclid(I256::new(2)), Some(I256::new(1))); + /// assert_eq!(I256::new(5).checked_rem_euclid(I256::new(0)), None); + /// assert_eq!(I256::MIN.checked_rem_euclid(I256::new(-1)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_rem_euclid(self, rhs: Self) -> Option { + if rhs == 0 || (self == Self::MIN && rhs == -1) { + None + } else { + Some(self.rem_euclid(rhs)) + } + } + + /// Checked negation. Computes `-self`, returning `None` if `self == MIN`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(5).checked_neg(), Some(I256::new(-5))); + /// assert_eq!(I256::MIN.checked_neg(), None); + /// ``` + #[inline] + pub fn checked_neg(self) -> Option { + let (a, b) = self.overflowing_neg(); + if b { + None + } else { + Some(a) + } + } + + /// Checked shift left. Computes `self << rhs`, returning `None` if `rhs` + /// is larger than or equal to the number of bits in `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(0x1).checked_shl(4), Some(I256::new(0x10))); + /// assert_eq!(I256::new(0x1).checked_shl(257), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_shl(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shl(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` + /// is larger than or equal to the number of bits in `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(0x10).checked_shr(4), Some(I256::new(0x1))); + /// assert_eq!(I256::new(0x10).checked_shr(256), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_shr(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shr(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked absolute value. Computes `self.abs()`, returning `None` if + /// `self == MIN`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(-5).checked_abs(), Some(I256::new(5))); + /// assert_eq!(I256::MIN.checked_abs(), None); + /// ``` + #[inline] + pub fn checked_abs(self) -> Option { + if self.is_negative() { + self.checked_neg() + } else { + Some(self) + } + } + + /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(8).checked_pow(2), Some(I256::new(64))); + /// assert_eq!(I256::MAX.checked_pow(2), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_pow(self, mut exp: u32) -> Option { + if exp == 0 { + return Some(Self::ONE); + } + let mut base = self; + let mut acc = Self::ONE; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.checked_mul(base)?; + } + exp /= 2; + base = base.checked_mul(base)?; + } + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc.checked_mul(base) + } + + /// Saturating integer addition. Computes `self + rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(100).saturating_add(I256::new(1)), 101); + /// assert_eq!(I256::MAX.saturating_add(I256::new(100)), I256::MAX); + /// assert_eq!(I256::MIN.saturating_add(I256::new(-1)), I256::MIN); + /// ``` + + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn saturating_add(self, rhs: Self) -> Self { + match self.checked_add(rhs) { + Some(x) => x, + None => { + if rhs > 0 { + Self::MAX + } else { + Self::MIN + } + } + } + } + + /// Saturating addition with an unsigned integer. Computes `self + rhs`, + /// saturating at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(I256::new(1).saturating_add_unsigned(U256::new(2)), 3); + /// assert_eq!(I256::MAX.saturating_add_unsigned(U256::new(100)), I256::MAX); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn saturating_add_unsigned(self, rhs: U256) -> Self { + // Overflow can only happen at the upper bound + match self.checked_add_unsigned(rhs) { + Some(x) => x, + None => Self::MAX, + } + } + + /// Saturating integer subtraction. Computes `self - rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(100).saturating_sub(I256::new(127)), -27); + /// assert_eq!(I256::MIN.saturating_sub(I256::new(100)), I256::MIN); + /// assert_eq!(I256::MAX.saturating_sub(I256::new(-1)), I256::MAX); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn saturating_sub(self, rhs: Self) -> Self { + match self.checked_sub(rhs) { + Some(x) => x, + None => { + if rhs > 0 { + Self::MIN + } else { + Self::MAX + } + } + } + } + + /// Saturating subtraction with an unsigned integer. Computes `self - rhs`, + /// saturating at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(I256::new(100).saturating_sub_unsigned(U256::new(127)), -27); + /// assert_eq!(I256::MIN.saturating_sub_unsigned(U256::new(100)), I256::MIN); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn saturating_sub_unsigned(self, rhs: U256) -> Self { + // Overflow can only happen at the lower bound + match self.checked_sub_unsigned(rhs) { + Some(x) => x, + None => Self::MIN, + } + } + + /// Saturating integer negation. Computes `-self`, returning `MAX` if + /// `self == MIN` instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(100).saturating_neg(), -100); + /// assert_eq!(I256::new(-100).saturating_neg(), 100); + /// assert_eq!(I256::MIN.saturating_neg(), I256::MAX); + /// assert_eq!(I256::MAX.saturating_neg(), I256::MIN + 1); + /// ``` + + #[inline(always)] + pub fn saturating_neg(self) -> Self { + I256::ZERO.saturating_sub(self) + } + + /// Saturating absolute value. Computes `self.abs()`, returning `MAX` if + /// `self == MIN` instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(100).saturating_abs(), 100); + /// assert_eq!(I256::new(-100).saturating_abs(), 100); + /// assert_eq!(I256::MIN.saturating_abs(), I256::MAX); + /// assert_eq!((I256::MIN + 1).saturating_abs(), I256::MAX); + /// ``` + + #[inline] + pub fn saturating_abs(self) -> Self { + if self.is_negative() { + self.saturating_neg() + } else { + self + } + } + + /// Saturating integer multiplication. Computes `self * rhs`, saturating at + /// the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(10).saturating_mul(I256::new(12)), 120); + /// assert_eq!(I256::MAX.saturating_mul(I256::new(10)), I256::MAX); + /// assert_eq!(I256::MIN.saturating_mul(I256::new(10)), I256::MIN); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn saturating_mul(self, rhs: Self) -> Self { + match self.checked_mul(rhs) { + Some(x) => x, + None => { + if (self < 0) == (rhs < 0) { + Self::MAX + } else { + Self::MIN + } + } + } + } + + /// Saturating integer division. Computes `self / rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(5).saturating_div(I256::new(2)), 2); + /// assert_eq!(I256::MAX.saturating_div(I256::new(-1)), I256::MIN + 1); + /// assert_eq!(I256::MIN.saturating_div(I256::new(-1)), I256::MAX); + /// ``` + /// + /// ```should_panic + /// # use ethnum::I256;; + /// let _ = I256::new(1).saturating_div(I256::new(0)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn saturating_div(self, rhs: Self) -> Self { + match self.overflowing_div(rhs) { + (result, false) => result, + (_result, true) => Self::MAX, // MIN / -1 is the only possible saturating overflow + } + } + + /// Saturating integer exponentiation. Computes `self.pow(exp)`, + /// saturating at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(-4).saturating_pow(3), -64); + /// assert_eq!(I256::MIN.saturating_pow(2), I256::MAX); + /// assert_eq!(I256::MIN.saturating_pow(3), I256::MIN); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Some(x) => x, + None if self < 0 && exp % 2 == 1 => Self::MIN, + None => Self::MAX, + } + } + + /// Wrapping (modular) addition. Computes `self + rhs`, wrapping around at + /// the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(100).wrapping_add(I256::new(27)), 127); + /// assert_eq!(I256::MAX.wrapping_add(I256::new(2)), I256::MIN + 1); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_add(self, rhs: Self) -> Self { + let mut result = MaybeUninit::uninit(); + intrinsics::signed::iadd3(&mut result, &self, &rhs); + unsafe { result.assume_init() } + } + + /// Wrapping (modular) addition with an unsigned integer. Computes + /// `self + rhs`, wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(I256::new(100).wrapping_add_unsigned(U256::new(27)), 127); + /// assert_eq!(I256::MAX.wrapping_add_unsigned(U256::new(2)), I256::MIN + 1); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_add_unsigned(self, rhs: U256) -> Self { + self.wrapping_add(rhs.as_i256()) + } + + /// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around + /// at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(0).wrapping_sub(I256::new(127)), -127); + /// assert_eq!(I256::new(-2).wrapping_sub(I256::MAX), I256::MAX); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_sub(self, rhs: Self) -> Self { + let mut result = MaybeUninit::uninit(); + intrinsics::signed::isub3(&mut result, &self, &rhs); + unsafe { result.assume_init() } + } + + /// Wrapping (modular) subtraction with an unsigned integer. Computes + /// `self - rhs`, wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(I256::new(0).wrapping_sub_unsigned(U256::new(127)), -127); + /// assert_eq!(I256::new(-2).wrapping_sub_unsigned(U256::MAX), -1); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_sub_unsigned(self, rhs: U256) -> Self { + self.wrapping_sub(rhs.as_i256()) + } + + /// Wrapping (modular) multiplication. Computes `self * rhs`, wrapping + /// around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(10).wrapping_mul(I256::new(12)), 120); + /// assert_eq!(I256::MAX.wrapping_mul(I256::new(2)), -2); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_mul(self, rhs: Self) -> Self { + let mut result = MaybeUninit::uninit(); + intrinsics::signed::imul3(&mut result, &self, &rhs); + unsafe { result.assume_init() } + } + + /// Wrapping (modular) division. Computes `self / rhs`, wrapping around at + /// the boundary of the type. + /// + /// The only case where such wrapping can occur is when one divides + /// `MIN / -1` on a signed type (where `MIN` is the negative minimal value + /// for the type); this is equivalent to `-MIN`, a positive value that is + /// too large to represent in the type. In such a case, this function + /// returns `MIN` itself. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(100).wrapping_div(I256::new(10)), 10); + /// assert_eq!(I256::MIN.wrapping_div(I256::new(-1)), I256::MIN); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn wrapping_div(self, rhs: Self) -> Self { + self.overflowing_div(rhs).0 + } + + /// Wrapping Euclidean division. Computes `self.div_euclid(rhs)`, + /// wrapping around at the boundary of the type. + /// + /// Wrapping will only occur in `MIN / -1` on a signed type (where `MIN` is + /// the negative minimal value for the type). This is equivalent to `-MIN`, + /// a positive value that is too large to represent in the type. In this + /// case, this method returns `MIN` itself. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(100).wrapping_div_euclid(I256::new(10)), 10); + /// assert_eq!(I256::MIN.wrapping_div_euclid(I256::new(-1)), I256::MIN); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn wrapping_div_euclid(self, rhs: Self) -> Self { + self.overflowing_div_euclid(rhs).0 + } + + /// Wrapping (modular) remainder. Computes `self % rhs`, wrapping around at + /// the boundary of the type. + /// + /// Such wrap-around never actually occurs mathematically; implementation + /// artifacts make `x % y` invalid for `MIN / -1` on a signed type (where + /// MIN` is the negative minimal value). In such a case, this function + /// returns `0`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(100).wrapping_rem(I256::new(10)), 0); + /// assert_eq!(I256::MIN.wrapping_rem(I256::new(-1)), 0); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn wrapping_rem(self, rhs: Self) -> Self { + self.overflowing_rem(rhs).0 + } + + /// Wrapping Euclidean remainder. Computes `self.rem_euclid(rhs)`, wrapping + /// around at the boundary of the type. + /// + /// Wrapping will only occur in `MIN % -1` on a signed type (where `MIN` is + /// the negative minimal value for the type). In this case, this method + /// returns 0. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(100).wrapping_rem_euclid(I256::new(10)), 0); + /// assert_eq!(I256::MIN.wrapping_rem_euclid(I256::new(-1)), 0); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn wrapping_rem_euclid(self, rhs: Self) -> Self { + self.overflowing_rem_euclid(rhs).0 + } + + /// Wrapping (modular) negation. Computes `-self`, wrapping around at the + /// boundary of the type. + /// + /// The only case where such wrapping can occur is when one negates `MIN` on + /// a signed type (where `MIN` is the negative minimal value for the type); + /// this is a positive value that is too large to represent in the type. In + /// such a case, this function returns `MIN` itself. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(100).wrapping_neg(), -100); + /// assert_eq!(I256::MIN.wrapping_neg(), I256::MIN); + /// ``` + #[inline(always)] + pub fn wrapping_neg(self) -> Self { + Self::ZERO.wrapping_sub(self) + } + + /// Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` + /// removes any high-order bits of `rhs` that would cause the shift to + /// exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-left; the RHS of a wrapping + /// shift-left is restricted to the range of the type, rather than the bits + /// shifted out of the LHS being returned to the other end. The primitive + /// integer types all implement a [`rotate_left`](Self::rotate_left) + /// function, which may be what you want instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(-1).wrapping_shl(7), -128); + /// assert_eq!(I256::new(-1).wrapping_shl(128), I256::from_words(-1, 0)); + /// assert_eq!(I256::new(-1).wrapping_shl(256), -1); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_shl(self, rhs: u32) -> Self { + let mut result = MaybeUninit::uninit(); + intrinsics::signed::ishl3(&mut result, &self, rhs & 0xff); + unsafe { result.assume_init() } + } + + /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`, where `mask` + /// removes any high-order bits of `rhs` that would cause the shift to + /// exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-right; the RHS of a + /// wrapping shift-right is restricted to the range of the type, rather than + /// the bits shifted out of the LHS being returned to the other end. The + /// primitive integer types all implement a + /// [`rotate_right`](Self::rotate_right) function, which may be what you + /// want instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(-128).wrapping_shr(7), -1); + /// assert_eq!((-128i16).wrapping_shr(64), -128); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_shr(self, rhs: u32) -> Self { + let mut result = MaybeUninit::uninit(); + intrinsics::signed::isar3(&mut result, &self, rhs & 0xff); + unsafe { result.assume_init() } + } + + /// Wrapping (modular) absolute value. Computes `self.abs()`, wrapping + /// around at the boundary of the type. + /// + /// The only case where such wrapping can occur is when one takes the + /// absolute value of the negative minimal value for the type; this is a + /// positive value that is too large to represent in the type. In such a + /// case, this function returns `MIN` itself. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(I256::new(100).wrapping_abs(), 100); + /// assert_eq!(I256::new(-100).wrapping_abs(), 100); + /// assert_eq!(I256::MIN.wrapping_abs(), I256::MIN); + /// assert_eq!( + /// I256::MIN.wrapping_abs().as_u256(), + /// U256::from_words( + /// 0x80000000000000000000000000000000, + /// 0x00000000000000000000000000000000, + /// ), + /// ); + /// ``` + #[allow(unused_attributes)] + #[inline] + pub fn wrapping_abs(self) -> Self { + if self.is_negative() { + self.wrapping_neg() + } else { + self + } + } + + /// Computes the absolute value of `self` without any wrapping + /// or panicking. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(I256::new(100).unsigned_abs(), 100); + /// assert_eq!(I256::new(-100).unsigned_abs(), 100); + /// assert_eq!( + /// I256::MIN.unsigned_abs(), + /// U256::from_words( + /// 0x80000000000000000000000000000000, + /// 0x00000000000000000000000000000000, + /// ), + /// ); + /// ``` + #[inline(always)] + pub fn unsigned_abs(self) -> U256 { + self.wrapping_abs().as_u256() + } + + /// Wrapping (modular) exponentiation. Computes `self.pow(exp)`, + /// wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(3).wrapping_pow(4), 81); + /// assert_eq!(3i8.wrapping_pow(5), -13); + /// assert_eq!(3i8.wrapping_pow(6), -39); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn wrapping_pow(self, mut exp: u32) -> Self { + if exp == 0 { + return Self::ONE; + } + let mut base = self; + let mut acc = Self::ONE; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + } + exp /= 2; + base = base.wrapping_mul(base); + } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc.wrapping_mul(base) + } + + /// Calculates `self` + `rhs` + /// + /// Returns a tuple of the addition along with a boolean indicating whether + /// an arithmetic overflow would occur. If an overflow would have occurred + /// then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(5).overflowing_add(I256::new(2)), (I256::new(7), false)); + /// assert_eq!(I256::MAX.overflowing_add(I256::new(1)), (I256::MIN, true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn overflowing_add(self, rhs: Self) -> (Self, bool) { + let mut result = MaybeUninit::uninit(); + let overflow = intrinsics::signed::iaddc(&mut result, &self, &rhs); + (unsafe { result.assume_init() }, overflow) + } + + /// Calculates `self` + `rhs` with an unsigned `rhs` + /// + /// Returns a tuple of the addition along with a boolean indicating + /// whether an arithmetic overflow would occur. If an overflow would + /// have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(I256::new(1).overflowing_add_unsigned(U256::new(2)), (I256::new(3), false)); + /// assert_eq!((I256::MIN).overflowing_add_unsigned(U256::MAX), (I256::MAX, false)); + /// assert_eq!((I256::MAX - 2).overflowing_add_unsigned(U256::new(3)), (I256::MIN, true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn overflowing_add_unsigned(self, rhs: U256) -> (Self, bool) { + let rhs = rhs.as_i256(); + let (res, overflowed) = self.overflowing_add(rhs); + (res, overflowed ^ (rhs < 0)) + } + + /// Calculates `self` - `rhs` + /// + /// Returns a tuple of the subtraction along with a boolean indicating + /// whether an arithmetic overflow would occur. If an overflow would have + /// occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(5).overflowing_sub(I256::new(2)), (I256::new(3), false)); + /// assert_eq!(I256::MIN.overflowing_sub(I256::new(1)), (I256::MAX, true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn overflowing_sub(self, rhs: Self) -> (Self, bool) { + let mut result = MaybeUninit::uninit(); + let overflow = intrinsics::signed::isubc(&mut result, &self, &rhs); + (unsafe { result.assume_init() }, overflow) + } + + /// Calculates `self` - `rhs` with an unsigned `rhs` + /// + /// Returns a tuple of the subtraction along with a boolean indicating + /// whether an arithmetic overflow would occur. If an overflow would + /// have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(I256::new(1).overflowing_sub_unsigned(U256::new(2)), (I256::new(-1), false)); + /// assert_eq!((I256::MAX).overflowing_sub_unsigned(U256::MAX), (I256::MIN, false)); + /// assert_eq!((I256::MIN + 2).overflowing_sub_unsigned(U256::new(3)), (I256::MAX, true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn overflowing_sub_unsigned(self, rhs: U256) -> (Self, bool) { + let rhs = rhs.as_i256(); + let (res, overflowed) = self.overflowing_sub(rhs); + (res, overflowed ^ (rhs < 0)) + } + + /// Computes the absolute difference between `self` and `other`. + /// + /// This function always returns the correct answer without overflow or + /// panics by returning an unsigned integer. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(I256::new(100).abs_diff(I256::new(80)), 20); + /// assert_eq!(I256::new(100).abs_diff(I256::new(110)), 10); + /// assert_eq!(I256::new(-100).abs_diff(I256::new(80)), 180); + /// assert_eq!(I256::new(-100).abs_diff(I256::new(-120)), 20); + /// assert_eq!(I256::MIN.abs_diff(I256::MAX), U256::MAX); + /// assert_eq!(I256::MAX.abs_diff(I256::MIN), U256::MAX); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn abs_diff(self, other: Self) -> U256 { + if self < other { + // Converting a non-negative x from signed to unsigned by using + // `x as U` is left unchanged, but a negative x is converted + // to value x + 2^N. Thus if `s` and `o` are binary variables + // respectively indicating whether `self` and `other` are + // negative, we are computing the mathematical value: + // + // (other + o*2^N) - (self + s*2^N) mod 2^N + // other - self + (o-s)*2^N mod 2^N + // other - self mod 2^N + // + // Finally, taking the mod 2^N of the mathematical value of + // `other - self` does not change it as it already is + // in the range [0, 2^N). + other.as_u256().wrapping_sub(self.as_u256()) + } else { + self.as_u256().wrapping_sub(other.as_u256()) + } + } + + /// Calculates the multiplication of `self` and `rhs`. + /// + /// Returns a tuple of the multiplication along with a boolean indicating + /// whether an arithmetic overflow would occur. If an overflow would have + /// occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(5).overflowing_mul(I256::new(2)), (I256::new(10), false)); + /// assert_eq!(I256::MAX.overflowing_mul(I256::new(2)), (I256::new(-2), true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn overflowing_mul(self, rhs: Self) -> (Self, bool) { + let mut result = MaybeUninit::uninit(); + let overflow = intrinsics::signed::imulc(&mut result, &self, &rhs); + (unsafe { result.assume_init() }, overflow) + } + + /// Calculates the divisor when `self` is divided by `rhs`. + /// + /// Returns a tuple of the divisor along with a boolean indicating whether + /// an arithmetic overflow would occur. If an overflow would occur then self + /// is returned. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(5).overflowing_div(I256::new(2)), (I256::new(2), false)); + /// assert_eq!(I256::MIN.overflowing_div(I256::new(-1)), (I256::MIN, true)); + /// ``` + #[inline] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub fn overflowing_div(self, rhs: Self) -> (Self, bool) { + if self == Self::MIN && rhs == -1 { + (self, true) + } else { + (self / rhs, false) + } + } + + /// Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. + /// + /// Returns a tuple of the divisor along with a boolean indicating whether + /// an arithmetic overflow would occur. If an overflow would occur then + /// `self` is returned. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(5).overflowing_div_euclid(I256::new(2)), (I256::new(2), false)); + /// assert_eq!(I256::MIN.overflowing_div_euclid(I256::new(-1)), (I256::MIN, true)); + /// ``` + #[inline] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { + if self == Self::MIN && rhs == -1 { + (self, true) + } else { + (self.div_euclid(rhs), false) + } + } + + /// Calculates the remainder when `self` is divided by `rhs`. + /// + /// Returns a tuple of the remainder after dividing along with a boolean + /// indicating whether an arithmetic overflow would occur. If an overflow + /// would occur then 0 is returned. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(5).overflowing_rem(I256::new(2)), (I256::new(1), false)); + /// assert_eq!(I256::MIN.overflowing_rem(I256::new(-1)), (I256::new(0), true)); + /// ``` + #[inline] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub fn overflowing_rem(self, rhs: Self) -> (Self, bool) { + if self == Self::MIN && rhs == -1 { + (Self::ZERO, true) + } else { + (self % rhs, false) + } + } + + /// Overflowing Euclidean remainder. Calculates `self.rem_euclid(rhs)`. + /// + /// Returns a tuple of the remainder after dividing along with a boolean + /// indicating whether an arithmetic overflow would occur. If an overflow + /// would occur then 0 is returned. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(5).overflowing_rem_euclid(I256::new(2)), (I256::new(1), false)); + /// assert_eq!(I256::MIN.overflowing_rem_euclid(I256::new(-1)), (I256::new(0), true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { + if self == Self::MIN && rhs == -1 { + (Self::ZERO, true) + } else { + (self.rem_euclid(rhs), false) + } + } + + /// Negates self, overflowing if this is equal to the minimum value. + /// + /// Returns a tuple of the negated version of self along with a boolean + /// indicating whether an overflow happened. If `self` is the minimum value + /// (e.g., `i32::MIN` for values of type `i32`), then the minimum value will + /// be returned again and `true` will be returned for an overflow happening. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(2).overflowing_neg(), (I256::new(-2), false)); + /// assert_eq!(I256::MIN.overflowing_neg(), (I256::MIN, true)); + /// ``` + #[inline] + pub fn overflowing_neg(self) -> (Self, bool) { + if self == Self::MIN { + (Self::MIN, true) + } else { + (-self, false) + } + } + + /// Shifts self left by `rhs` bits. + /// + /// Returns a tuple of the shifted version of self along with a boolean + /// indicating whether the shift value was larger than or equal to the + /// number of bits. If the shift value is too large, then value is masked + /// (N-1) where N is the number of bits, and this value is then used to + /// perform the shift. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(1).overflowing_shl(4), (I256::new(0x10), false)); + /// assert_eq!(I256::new(1).overflowing_shl(260), (I256::new(0x10), true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn overflowing_shl(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shl(rhs), (rhs > 255)) + } + + /// Shifts self right by `rhs` bits. + /// + /// Returns a tuple of the shifted version of self along with a boolean + /// indicating whether the shift value was larger than or equal to the + /// number of bits. If the shift value is too large, then value is masked + /// (N-1) where N is the number of bits, and this value is then used to + /// perform the shift. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(0x10).overflowing_shr(4), (I256::new(0x1), false)); + /// assert_eq!(I256::new(0x10).overflowing_shr(260), (I256::new(0x1), true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn overflowing_shr(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shr(rhs), (rhs > 255)) + } + + /// Computes the absolute value of `self`. + /// + /// Returns a tuple of the absolute version of self along with a boolean + /// indicating whether an overflow happened. If self is the minimum value + /// (e.g., I256::MIN for values of type I256), then the minimum value will + /// be returned again and true will be returned for an overflow happening. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(10).overflowing_abs(), (I256::new(10), false)); + /// assert_eq!(I256::new(-10).overflowing_abs(), (I256::new(10), false)); + /// assert_eq!(I256::MIN.overflowing_abs(), (I256::MIN, true)); + /// ``` + #[inline] + pub fn overflowing_abs(self) -> (Self, bool) { + (self.wrapping_abs(), self == Self::MIN) + } + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// Returns a tuple of the exponentiation along with a bool indicating + /// whether an overflow happened. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(3).overflowing_pow(4), (I256::new(81), false)); + /// assert_eq!( + /// I256::new(10).overflowing_pow(77), + /// ( + /// I256::from_words( + /// -46408779215366586471190473126206792002, + /// -113521875028918879454725857041952276480, + /// ), + /// true, + /// ) + /// ); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { + if exp == 0 { + return (Self::ONE, false); + } + let mut base = self; + let mut acc = Self::ONE; + let mut overflown = false; + // Scratch space for storing results of overflowing_mul. + let mut r; + + while exp > 1 { + if (exp & 1) == 1 { + r = acc.overflowing_mul(base); + acc = r.0; + overflown |= r.1; + } + exp /= 2; + r = base.overflowing_mul(base); + base = r.0; + overflown |= r.1; + } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + r = acc.overflowing_mul(base); + r.1 |= overflown; + r + } + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// + /// assert_eq!(I256::new(2).pow(5), 32); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn pow(self, mut exp: u32) -> Self { + if exp == 0 { + return Self::ONE; + } + let mut base = self; + let mut acc = Self::ONE; + + while exp > 1 { + if (exp & 1) == 1 { + acc *= base; + } + exp /= 2; + base = base * base; + } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc * base + } + + /// Calculates the quotient of Euclidean division of `self` by `rhs`. + /// + /// This computes the integer `q` such that `self = q * rhs + r`, with + /// `r = self.rem_euclid(rhs)` and `0 <= r < abs(rhs)`. + /// + /// In other words, the result is `self / rhs` rounded to the integer `q` + /// such that `self >= q * rhs`. + /// If `self > 0`, this is equal to round towards zero (the default in + /// Rust); if `self < 0`, this is equal to round towards +/- infinity. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0 or the division results in + /// overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let a = I256::new(7); + /// let b = I256::new(4); + /// + /// assert_eq!(a.div_euclid(b), 1); // 7 >= 4 * 1 + /// assert_eq!(a.div_euclid(-b), -1); // 7 >= -4 * -1 + /// assert_eq!((-a).div_euclid(b), -2); // -7 >= 4 * -2 + /// assert_eq!((-a).div_euclid(-b), 2); // -7 >= -4 * 2 + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn div_euclid(self, rhs: Self) -> Self { + let q = self / rhs; + if self % rhs < 0 { + return if rhs > 0 { q - 1 } else { q + 1 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// This is done as if by the Euclidean division algorithm -- given + /// `r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r`, and + /// `0 <= r < abs(rhs)`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0 or the division results in + /// overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// let a = I256::new(7); + /// let b = I256::new(4); + /// + /// assert_eq!(a.rem_euclid(b), 3); + /// assert_eq!((-a).rem_euclid(b), 1); + /// assert_eq!(a.rem_euclid(-b), 3); + /// assert_eq!((-a).rem_euclid(-b), 1); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn rem_euclid(self, rhs: Self) -> Self { + let r = self % rhs; + if r < 0 { + if rhs < 0 { + r - rhs + } else { + r + rhs + } + } else { + r + } + } + + /// Computes the absolute value of `self`. + /// + /// # Overflow behavior + /// + /// The absolute value of + /// `I256::MIN` + /// cannot be represented as an + /// `I256`, + /// and attempting to calculate it will cause an overflow. This means + /// that code in debug mode will trigger a panic on this case and + /// optimized code will return + /// `I256::MIN` + /// without a panic. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(10).abs(), 10); + /// assert_eq!(I256::new(-10).abs(), 10); + /// ``` + #[allow(unused_attributes)] + #[inline] + pub fn abs(self) -> Self { + if self.is_negative() { + -self + } else { + self + } + } + + /// Returns a number representing sign of `self`. + /// + /// - `0` if the number is zero + /// - `1` if the number is positive + /// - `-1` if the number is negative + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(10).signum(), 1); + /// assert_eq!(I256::new(0).signum(), 0); + /// assert_eq!(I256::new(-10).signum(), -1); + /// ``` + #[inline(always)] + pub const fn signum(self) -> Self { + I256::new(self.signum128()) + } + + /// Returns a number representing sign of `self` as a 64-bit signed integer. + /// + /// - `0` if the number is zero + /// - `1` if the number is positive + /// - `-1` if the number is negative + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert_eq!(I256::new(10).signum128(), 1i128); + /// assert_eq!(I256::new(0).signum128(), 0i128); + /// assert_eq!(I256::new(-10).signum128(), -1i128); + /// ``` + #[inline] + pub const fn signum128(self) -> i128 { + let (hi, lo) = self.into_words(); + hi.signum() | (lo != 0) as i128 + } + + /// Returns `true` if `self` is positive and `false` if the number is zero + /// or negative. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert!(I256::new(10).is_positive()); + /// assert!(!I256::new(-10).is_positive()); + /// ``` + #[inline] + pub const fn is_positive(self) -> bool { + self.signum128() > 0 + } + + /// Returns `true` if `self` is negative and `false` if the number is zero + /// or positive. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::I256; + /// assert!(I256::new(-10).is_negative()); + /// assert!(!I256::new(10).is_negative()); + /// ``` + #[inline] + pub const fn is_negative(self) -> bool { + self.signum128() < 0 + } + + /// Return the memory representation of this integer as a byte array in + /// big-endian (network) byte order. + /// + /// # Examples + /// + /// ``` + /// # use ethnum::I256; + /// let bytes = I256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ); + /// assert_eq!( + /// bytes.to_be_bytes(), + /// [ + /// 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + /// 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + /// ], + /// ); + /// ``` + #[inline] + pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { + self.to_be().to_ne_bytes() + } + + /// Return the memory representation of this integer as a byte array in + /// little-endian byte order. + /// + /// # Examples + /// + /// ``` + /// # use ethnum::I256; + /// let bytes = I256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ); + /// assert_eq!( + /// bytes.to_le_bytes(), + /// [ + /// 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + /// 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + /// ], + /// ); + /// ``` + #[inline] + pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { + self.to_le().to_ne_bytes() + } + + /// Return the memory representation of this integer as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, + /// instead. + /// + /// [`to_be_bytes`]: Self::to_be_bytes + /// [`to_le_bytes`]: Self::to_le_bytes + /// + /// # Examples + /// + /// ``` + /// # use ethnum::I256; + /// let bytes = I256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ); + /// assert_eq!( + /// bytes.to_ne_bytes(), + /// if cfg!(target_endian = "big") { + /// [ + /// 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + /// 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + /// ] + /// } else { + /// [ + /// 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + /// 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + /// ] + /// } + /// ); + /// ``` + #[inline] + pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { mem::transmute(self) } + } + + /// Create an integer value from its representation as a byte array in + /// big endian. + /// + /// # Examples + /// + /// ``` + /// # use ethnum::I256; + /// let value = I256::from_be_bytes([ + /// 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + /// 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + /// ]); + /// assert_eq!( + /// value, + /// I256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ), + /// ); + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion + /// APIs can be used: + /// + /// ``` + /// # use ethnum::I256; + /// fn read_be_i256(input: &mut &[u8]) -> I256 { + /// let (int_bytes, rest) = input.split_at(std::mem::size_of::()); + /// *input = rest; + /// I256::from_be_bytes(int_bytes.try_into().unwrap()) + /// } + /// ``` + #[inline] + pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_be(Self::from_ne_bytes(bytes)) + } + + /// Create an integer value from its representation as a byte array in + /// little endian. + /// + /// # Examples + /// + /// ``` + /// # use ethnum::I256; + /// let value = I256::from_le_bytes([ + /// 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + /// 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + /// ]); + /// assert_eq!( + /// value, + /// I256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ), + /// ); + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion + /// APIs can be used: + /// + /// ``` + /// # use ethnum::I256; + /// fn read_le_i256(input: &mut &[u8]) -> I256 { + /// let (int_bytes, rest) = input.split_at(std::mem::size_of::()); + /// *input = rest; + /// I256::from_le_bytes(int_bytes.try_into().unwrap()) + /// } + /// ``` + #[inline] + pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_le(Self::from_ne_bytes(bytes)) + } + + /// Create an integer value from its memory representation as a byte + /// array in native endianness. + /// + /// As the target platform's native endianness is used, portable code + /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as + /// appropriate instead. + /// + /// [`from_be_bytes`]: Self::from_be_bytes + /// [`from_le_bytes`]: Self::from_le_bytes + /// + /// # Examples + /// + /// ``` + /// # use ethnum::I256; + /// let value = I256::from_ne_bytes(if cfg!(target_endian = "big") { + /// [ + /// 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + /// 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + /// ] + /// } else { + /// [ + /// 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + /// 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + /// ] + /// }); + /// assert_eq!( + /// value, + /// I256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ), + /// ); + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion + /// APIs can be used: + /// + /// ``` + /// # use ethnum::I256; + /// fn read_ne_i256(input: &mut &[u8]) -> I256 { + /// let (int_bytes, rest) = input.split_at(std::mem::size_of::()); + /// *input = rest; + /// I256::from_ne_bytes(int_bytes.try_into().unwrap()) + /// } + /// ``` + #[inline] + pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + // SAFETY: integers are plain old datatypes so we can always transmute to them + unsafe { mem::transmute(bytes) } + } +} diff --git a/vendor/ethnum/src/int/cmp.rs b/vendor/ethnum/src/int/cmp.rs new file mode 100644 index 0000000..d99ecb3 --- /dev/null +++ b/vendor/ethnum/src/int/cmp.rs @@ -0,0 +1,57 @@ +//! Module with comparison implementations for `I256`. +//! +//! `PartialEq` and `PartialOrd` implementations for `i128` are also provided +//! to allow notation such as: +//! +//! ``` +//! # use ethnum::I256; +//! assert_eq!(I256::new(42), 42); +//! assert_eq!(42, I256::new(42)); +//! assert!(I256::ONE > 0 && I256::ZERO == 0); +//! assert!(0 < I256::ONE && 0 == I256::ZERO); +//! ``` + +use super::I256; +use core::cmp::Ordering; + +impl Ord for I256 { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + let (ahi, alo) = self.into_words(); + let (bhi, blo) = other.into_words(); + (ahi, alo as u128).cmp(&(bhi, blo as u128)) + } +} + +impl_cmp! { + impl Cmp for I256 (i128); +} + +#[cfg(test)] +mod tests { + use super::*; + use core::cmp::Ordering; + + #[test] + fn cmp() { + // 1e38 + let x = I256::from_words(0, 100000000000000000000000000000000000000); + // 1e48 + let y = I256::from_words(2938735877, 18960114910927365649471927446130393088); + assert!(x < y); + assert_eq!(x.cmp(&y), Ordering::Less); + assert!(y > x); + assert_eq!(y.cmp(&x), Ordering::Greater); + + let x = I256::new(100); + let y = I256::new(100); + assert!(x <= y); + assert_eq!(x.cmp(&y), Ordering::Equal); + + assert!(I256::ZERO > I256::MIN); + assert!(I256::ZERO < I256::MAX); + + assert!(I256::MAX > I256::MIN); + assert!(I256::MIN < I256::MAX); + } +} diff --git a/vendor/ethnum/src/int/convert.rs b/vendor/ethnum/src/int/convert.rs new file mode 100644 index 0000000..c16e554 --- /dev/null +++ b/vendor/ethnum/src/int/convert.rs @@ -0,0 +1,199 @@ +//! Module contains conversions for [`I256`] to and from primimitive types. + +use super::I256; +use crate::{error::tfie, uint::U256}; +use core::num::TryFromIntError; + +macro_rules! impl_from { + ($($t:ty),* $(,)?) => {$( + impl From<$t> for I256 { + #[inline] + fn from(value: $t) -> Self { + value.as_i256() + } + } + )*}; +} + +impl_from! { + bool, + i8, i16, i32, i64, i128, + u8, u16, u32, u64, u128, +} + +impl TryFrom for I256 { + type Error = TryFromIntError; + + fn try_from(value: U256) -> Result { + if value > I256::MAX.as_u256() { + return Err(tfie()); + } + Ok(value.as_i256()) + } +} + +/// This trait defines `as` conversions (casting) from primitive types to +/// [`I256`]. +/// +/// [`I256`]: struct.I256.html +/// +/// # Examples +/// +/// Casting a floating point value to an integer is a saturating operation, +/// with `NaN` converting to `0`. So: +/// +/// ``` +/// # use ethnum::{I256, AsI256}; +/// assert_eq!((-1i32).as_i256(), -I256::ONE); +/// assert_eq!(u32::MAX.as_i256(), 0xffffffff); +/// +/// assert_eq!(-13.37f64.as_i256(), -13); +/// assert_eq!(42.0f64.as_i256(), 42); +/// assert_eq!( +/// f32::MAX.as_i256(), +/// 0xffffff00000000000000000000000000u128.as_i256(), +/// ); +/// assert_eq!( +/// f32::MIN.as_i256(), +/// -0xffffff00000000000000000000000000u128.as_i256(), +/// ); +/// +/// assert_eq!(f64::NEG_INFINITY.as_i256(), I256::MIN); +/// assert_eq!((-2.0f64.powi(256)).as_i256(), I256::MIN); +/// assert_eq!(f64::INFINITY.as_i256(), I256::MAX); +/// assert_eq!(2.0f64.powi(256).as_i256(), I256::MAX); +/// assert_eq!(f64::NAN.as_i256(), 0); +/// ``` +pub trait AsI256 { + /// Perform an `as` conversion to a [`I256`]. + /// + /// [`I256`]: struct.I256.html + #[allow(clippy::wrong_self_convention)] + fn as_i256(self) -> I256; +} + +impl AsI256 for I256 { + #[inline] + fn as_i256(self) -> I256 { + self + } +} + +impl AsI256 for U256 { + #[inline] + fn as_i256(self) -> I256 { + U256::as_i256(self) + } +} + +macro_rules! impl_as_i256 { + ($($t:ty),* $(,)?) => {$( + impl AsI256 for $t { + #[inline] + fn as_i256(self) -> I256 { + #[allow(unused_comparisons)] + let hi = if self >= 0 { 0 } else { !0 }; + I256::from_words(hi, self as _) + } + } + )*}; +} + +impl_as_i256! { + i8, i16, i32, i64, i128, + u8, u16, u32, u64, u128, + isize, usize, +} + +impl AsI256 for bool { + #[inline] + fn as_i256(self) -> I256 { + I256::new(self as _) + } +} + +macro_rules! impl_as_i256_float { + ($($t:ty [$b:ty]),* $(,)?) => {$( + impl AsI256 for $t { + #[inline] + fn as_i256(self) -> I256 { + // The conversion follows roughly the same rules as converting + // `f64` to other primitive integer types: + // - `NaN` => `0` + // - `(-∞, I256::MIN]` => `I256::MIN` + // - `(I256::MIN, I256::MAX]` => `value as I256` + // - `(I256::MAX, +∞)` => `I256::MAX` + + const M: $b = (<$t>::MANTISSA_DIGITS - 1) as _; + const MAN_MASK: $b = !(!0 << M); + const MAN_ONE: $b = 1 << M; + const EXP_MASK: $b = !0 >> <$t>::MANTISSA_DIGITS; + const EXP_OFFSET: $b = EXP_MASK / 2; + const ABS_MASK: $b = !0 >> 1; + const SIG_MASK: $b = !ABS_MASK; + + let abs = <$t>::from_bits(self.to_bits() & ABS_MASK); + let sign = -(((self.to_bits() & SIG_MASK) >> (<$b>::BITS - 2)) as i128) + .wrapping_sub(1); // if self >= 0. { 1 } else { -1 } + if abs >= 1.0 { + let bits = abs.to_bits(); + let exponent = ((bits >> M) & EXP_MASK) - EXP_OFFSET; + let mantissa = (bits & MAN_MASK) | MAN_ONE; + if exponent <= M { + (I256::from(mantissa >> (M - exponent))) * sign + } else if exponent < 255 { + (I256::from(mantissa) << (exponent - M)) * sign + } else if sign > 0 { + I256::MAX + } else { + I256::MIN + } + } else { + I256::ZERO + } + } + } + )*}; +} + +impl_as_i256_float! { + f32[u32], f64[u64], +} + +macro_rules! impl_try_into { + ($($t:ty),* $(,)?) => {$( + impl TryFrom for $t { + type Error = TryFromIntError; + + #[inline] + fn try_from(x: I256) -> Result { + if x <= <$t>::MAX.as_i256() { + Ok(*x.low() as _) + } else { + Err(tfie()) + } + } + } + )*}; +} + +impl_try_into! { + i8, i16, i32, i64, i128, + u8, u16, u32, u64, u128, + isize, usize, +} + +macro_rules! impl_into_float { + ($($t:ty => $f:ident),* $(,)?) => {$( + impl From for $t { + #[inline] + fn from(x: I256) -> $t { + x.$f() + } + } + )*}; +} + +impl_into_float! { + f32 => as_f32, f64 => as_f64, +} diff --git a/vendor/ethnum/src/int/fmt.rs b/vendor/ethnum/src/int/fmt.rs new file mode 100644 index 0000000..4c88305 --- /dev/null +++ b/vendor/ethnum/src/int/fmt.rs @@ -0,0 +1,69 @@ +//! Module implementing formatting for `I256` type. + +use crate::int::I256; + +impl_fmt! { + impl Fmt for I256; +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::format; + + #[test] + fn from_str() { + assert_eq!("42".parse::().unwrap(), 42); + } + + #[test] + fn debug() { + assert_eq!( + format!("{:?}", I256::MAX), + "57896044618658097711785492504343953926634992332820282019728792003956564819967", + ); + assert_eq!( + format!("{:x?}", I256::MIN), + "8000000000000000000000000000000000000000000000000000000000000000", + ); + assert_eq!( + format!("{:#X?}", I256::MAX), + "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + ); + } + + #[test] + fn display() { + assert_eq!( + format!("{}", I256::MIN), + "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + ); + assert_eq!( + format!( + "{}", + I256::from_words(0, -1329227995784915854529085396220968961) + ), + "338953138925153547608845522035547242495", + ); + } + + #[test] + fn radix() { + assert_eq!(format!("{:b}", I256::new(42)), "101010"); + assert_eq!(format!("{:o}", I256::new(42)), "52"); + assert_eq!(format!("{:x}", I256::new(42)), "2a"); + + // Note that there is no '-' sign for binary, octal or hex formatting! + // This is the same behaviour for the standard iN types. + assert_eq!(format!("{:b}", I256::MINUS_ONE), "1".repeat(256)); + assert_eq!(format!("{:o}", I256::MINUS_ONE), format!("{:7<86}", "1")); + assert_eq!(format!("{:x}", I256::MINUS_ONE), "f".repeat(64)); + } + + #[test] + fn exp() { + assert_eq!(format!("{:e}", I256::new(42)), "4.2e1"); + assert_eq!(format!("{:e}", I256::new(10).pow(76)), "1e76"); + assert_eq!(format!("{:E}", -I256::new(10).pow(39) * 1337), "-1.337E42"); + } +} diff --git a/vendor/ethnum/src/int/iter.rs b/vendor/ethnum/src/int/iter.rs new file mode 100644 index 0000000..a228a42 --- /dev/null +++ b/vendor/ethnum/src/int/iter.rs @@ -0,0 +1,13 @@ +//! Module contains iterator specific trait implementations. +//! +//! ``` +//! # use ethnum::I256; +//! assert_eq!((1..=3).map(I256::new).sum::(), 6); +//! assert_eq!([I256::new(6), I256::new(7)].iter().product::(), 42); +//! ``` + +use super::I256; + +impl_iter! { + impl Iter for I256; +} diff --git a/vendor/ethnum/src/int/ops.rs b/vendor/ethnum/src/int/ops.rs new file mode 100644 index 0000000..b9c7ba6 --- /dev/null +++ b/vendor/ethnum/src/int/ops.rs @@ -0,0 +1,326 @@ +//! Module `core::ops` trait implementations. +//! +//! Trait implementations for `i128` are also provided to allow notation such +//! as: +//! +//! ``` +//! # use ethnum::I256; +//! +//! let a = 1 + I256::ONE; +//! let b = I256::ONE + 1; +//! dbg!(a, b); +//! ``` + +use super::I256; +use crate::intrinsics::signed::*; + +impl_ops! { + for I256 | i128 { + add => iadd2, iadd3, iaddc; + mul => imul2, imul3, imulc; + sub => isub2, isub3, isubc; + + div => idiv2, idiv3; + rem => irem2, irem3; + + shl => ishl2, ishl3; + shr => isar2, isar3; + } +} + +impl_ops_neg! { + for I256 { + add => iadd2; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::uint::U256; + use core::ops::*; + + #[test] + fn trait_implementations() { + trait Implements {} + impl Implements for I256 {} + impl Implements for &'_ I256 {} + + fn assert_ops() + where + for<'a> T: Implements + + Add<&'a i128> + + Add<&'a I256> + + Add + + Add + + AddAssign<&'a i128> + + AddAssign<&'a I256> + + AddAssign + + AddAssign + + BitAnd<&'a i128> + + BitAnd<&'a I256> + + BitAnd + + BitAnd + + BitAndAssign<&'a i128> + + BitAndAssign<&'a I256> + + BitAndAssign + + BitAndAssign + + BitOr<&'a i128> + + BitOr<&'a I256> + + BitOr + + BitOr + + BitOrAssign<&'a i128> + + BitOrAssign<&'a I256> + + BitOrAssign + + BitOrAssign + + BitXor<&'a i128> + + BitXor<&'a I256> + + BitXor + + BitXor + + BitXorAssign<&'a i128> + + BitXorAssign<&'a I256> + + BitXorAssign + + BitXorAssign + + Div<&'a i128> + + Div<&'a I256> + + Div + + Div + + DivAssign<&'a i128> + + DivAssign<&'a I256> + + DivAssign + + DivAssign + + Mul<&'a i128> + + Mul<&'a I256> + + Mul + + Mul + + MulAssign<&'a i128> + + MulAssign<&'a I256> + + MulAssign + + MulAssign + + Neg + + Not + + Rem<&'a i128> + + Rem<&'a I256> + + Rem + + Rem + + RemAssign<&'a i128> + + RemAssign<&'a I256> + + RemAssign + + RemAssign + + Shl<&'a i128> + + Shl<&'a i16> + + Shl<&'a I256> + + Shl<&'a i32> + + Shl<&'a i64> + + Shl<&'a i8> + + Shl<&'a isize> + + Shl<&'a u128> + + Shl<&'a u16> + + Shl<&'a U256> + + Shl<&'a u32> + + Shl<&'a u64> + + Shl<&'a u8> + + Shl<&'a usize> + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + ShlAssign<&'a i128> + + ShlAssign<&'a i16> + + ShlAssign<&'a I256> + + ShlAssign<&'a i32> + + ShlAssign<&'a i64> + + ShlAssign<&'a i8> + + ShlAssign<&'a isize> + + ShlAssign<&'a u128> + + ShlAssign<&'a u16> + + ShlAssign<&'a U256> + + ShlAssign<&'a u32> + + ShlAssign<&'a u64> + + ShlAssign<&'a u8> + + ShlAssign<&'a usize> + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + Shr<&'a i128> + + Shr<&'a i16> + + Shr<&'a I256> + + Shr<&'a i32> + + Shr<&'a i64> + + Shr<&'a i8> + + Shr<&'a isize> + + Shr<&'a u128> + + Shr<&'a u16> + + Shr<&'a U256> + + Shr<&'a u32> + + Shr<&'a u64> + + Shr<&'a u8> + + Shr<&'a usize> + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + ShrAssign<&'a i128> + + ShrAssign<&'a i16> + + ShrAssign<&'a I256> + + ShrAssign<&'a i32> + + ShrAssign<&'a i64> + + ShrAssign<&'a i8> + + ShrAssign<&'a isize> + + ShrAssign<&'a u128> + + ShrAssign<&'a u16> + + ShrAssign<&'a U256> + + ShrAssign<&'a u32> + + ShrAssign<&'a u64> + + ShrAssign<&'a u8> + + ShrAssign<&'a usize> + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + Sub<&'a i128> + + Sub<&'a I256> + + Sub + + Sub + + SubAssign<&'a i128> + + SubAssign<&'a I256> + + SubAssign + + SubAssign, + for<'a> &'a T: Implements + + Add<&'a i128> + + Add<&'a I256> + + Add + + Add + + BitAnd<&'a i128> + + BitAnd<&'a I256> + + BitAnd + + BitAnd + + BitOr<&'a i128> + + BitOr<&'a I256> + + BitOr + + BitOr + + BitXor<&'a i128> + + BitXor<&'a I256> + + BitXor + + BitXor + + Div<&'a i128> + + Div<&'a I256> + + Div + + Div + + Mul<&'a i128> + + Mul<&'a I256> + + Mul + + Mul + + Neg + + Not + + Rem<&'a i128> + + Rem<&'a I256> + + Rem + + Rem + + Shl<&'a i128> + + Shl<&'a i16> + + Shl<&'a I256> + + Shl<&'a i32> + + Shl<&'a i64> + + Shl<&'a i8> + + Shl<&'a isize> + + Shl<&'a u128> + + Shl<&'a u16> + + Shl<&'a U256> + + Shl<&'a u32> + + Shl<&'a u64> + + Shl<&'a u8> + + Shl<&'a usize> + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shr<&'a i128> + + Shr<&'a i16> + + Shr<&'a I256> + + Shr<&'a i32> + + Shr<&'a i64> + + Shr<&'a i8> + + Shr<&'a isize> + + Shr<&'a u128> + + Shr<&'a u16> + + Shr<&'a U256> + + Shr<&'a u32> + + Shr<&'a u64> + + Shr<&'a u8> + + Shr<&'a usize> + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Sub<&'a i128> + + Sub<&'a I256> + + Sub + + Sub, + { + } + + assert_ops::(); + } +} diff --git a/vendor/ethnum/src/int/parse.rs b/vendor/ethnum/src/int/parse.rs new file mode 100644 index 0000000..d8ddecc --- /dev/null +++ b/vendor/ethnum/src/int/parse.rs @@ -0,0 +1,151 @@ +//! Module implementing parsing for `I256` type. + +use crate::int::I256; + +impl_from_str! { + impl FromStr for I256; +} + +pub const fn const_from_str_prefixed(src: &str) -> I256 { + assert!(!src.is_empty(), "empty string"); + + let bytes = src.as_bytes(); + let (negate, start) = match bytes[0] { + b'+' => (false, 1), + b'-' => (true, 1), + _ => (false, 0), + }; + let uint = crate::parse::const_from_str_prefixed(bytes, start as _); + + let int = { + let (hi, lo) = if negate { + let (hi, lo) = uint.into_words(); + let (lo, carry) = (!lo).overflowing_add(1); + let hi = (!hi).wrapping_add(carry as _); + (hi, lo) + } else { + uint.into_words() + }; + I256::from_words(hi as _, lo as _) + }; + + if matches!((negate, int.signum128()), (false, -1) | (true, 1)) { + panic!("overflows integer type"); + } + + int +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parse::from_str_radix; + use core::num::IntErrorKind; + + #[test] + fn from_str() { + assert_eq!("42".parse::().unwrap(), 42); + } + + #[test] + fn from_str_prefixed() { + assert_eq!(from_str_radix::("0b101", 2, Some("0b")).unwrap(), 5); + assert_eq!(from_str_radix::("-0xf", 16, Some("0x")).unwrap(), -15); + } + + #[test] + fn from_str_errors() { + assert_eq!( + from_str_radix::("", 2, None).unwrap_err().kind(), + &IntErrorKind::Empty, + ); + assert_eq!( + from_str_radix::("?", 2, None).unwrap_err().kind(), + &IntErrorKind::InvalidDigit, + ); + assert_eq!( + from_str_radix::("1", 16, Some("0x")) + .unwrap_err() + .kind(), + &IntErrorKind::InvalidDigit, + ); + assert_eq!( + from_str_radix::( + "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", + 36, + None + ) + .unwrap_err() + .kind(), + &IntErrorKind::PosOverflow, + ); + assert_eq!( + from_str_radix::( + "-zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", + 36, + None + ) + .unwrap_err() + .kind(), + &IntErrorKind::NegOverflow, + ); + } + + #[test] + fn const_parse() { + assert_eq!(const_from_str_prefixed("-0b1101"), -0b1101); + assert_eq!(const_from_str_prefixed("0o777"), 0o777); + assert_eq!(const_from_str_prefixed("-0x1f"), -0x1f); + assert_eq!(const_from_str_prefixed("+42"), 42); + + assert_eq!( + const_from_str_prefixed( + "0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_fffe\ + baae_dce6_af48_a03b_bfd2_5e8c_d036_4141" + ), + I256::from_words( + 0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_fffe, + 0xbaae_dce6_af48_a03b_bfd2_5e8c_d036_4141_u128 as _, + ), + ); + + assert_eq!( + const_from_str_prefixed( + "-0x8000_0000_0000_0000_0000_0000_0000_0000\ + 0000_0000_0000_0000_0000_0000_0000_0000" + ), + I256::MIN, + ); + assert_eq!( + const_from_str_prefixed( + "+0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff\ + ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff" + ), + I256::MAX, + ); + } + + #[test] + #[should_panic] + fn const_parse_overflow() { + const_from_str_prefixed( + "0x8000_0000_0000_0000_0000_0000_0000_0000\ + 0000_0000_0000_0000_0000_0000_0000_0000", + ); + } + + #[test] + #[should_panic] + fn const_parse_negative_overflow() { + const_from_str_prefixed( + "-0x8000_0000_0000_0000_0000_0000_0000_0000\ + 0000_0000_0000_0000_0000_0000_0000_0001", + ); + } + + #[test] + #[should_panic] + fn const_parse_invalid() { + const_from_str_prefixed("invalid"); + } +} diff --git a/vendor/ethnum/src/intrinsics.rs b/vendor/ethnum/src/intrinsics.rs new file mode 100644 index 0000000..5eb172a --- /dev/null +++ b/vendor/ethnum/src/intrinsics.rs @@ -0,0 +1,37 @@ +//! This module contains intrinsics used by the [`I256`](struct@crate::I256) and +//! [`U256`](struct@crate::U256) implementations. +//! +//! # Stability +//! +//! Be careful when using these intrinsics directly. Semantic versioning API +//! compatibility is **not guaranteed** for any of these intrinsics. + +#![allow(missing_docs)] + +#[macro_use] +mod cast; + +#[cfg(feature = "llvm-intrinsics")] +mod llvm; +#[cfg(not(feature = "llvm-intrinsics"))] +mod native; +pub mod signed; + +#[cfg(feature = "llvm-intrinsics")] +pub use self::llvm::*; +#[cfg(not(feature = "llvm-intrinsics"))] +pub use self::native::*; + +#[cfg(test)] +mod tests { + use super::*; + use crate::uint::U256; + use core::mem::MaybeUninit; + + #[test] + fn unchecked_addition() { + let mut res = MaybeUninit::uninit(); + add3(&mut res, &U256([1, 2]), &U256([3, 0])); + assert_eq!(unsafe { res.assume_init() }, U256([4, 2])); + } +} diff --git a/vendor/ethnum/src/intrinsics/cast.rs b/vendor/ethnum/src/intrinsics/cast.rs new file mode 100644 index 0000000..356d70e --- /dev/null +++ b/vendor/ethnum/src/intrinsics/cast.rs @@ -0,0 +1,17 @@ +//! Module with casting helpers. + +/// Cast references of `U256` to `I256` for intrinsic implementations. +macro_rules! cast { + (mut: $x:expr) => { + unsafe { &mut *($x as *mut $crate::int::I256).cast::<$crate::uint::U256>() } + }; + (ref: $x:expr) => { + unsafe { &*($x as *const $crate::int::I256).cast::<$crate::uint::U256>() } + }; + (uninit: $x:expr) => { + unsafe { &mut *($x).as_mut_ptr().cast::>() } + }; + (optuninit: $x:expr) => { + unsafe { ::core::mem::transmute(::core::ptr::read(&$x as *const _)) } + }; +} diff --git a/vendor/ethnum/src/intrinsics/llvm.rs b/vendor/ethnum/src/intrinsics/llvm.rs new file mode 100644 index 0000000..51d8190 --- /dev/null +++ b/vendor/ethnum/src/intrinsics/llvm.rs @@ -0,0 +1,92 @@ +//! This module contains definitions for LLVM IR generated intrinsics. + +// NOTE: LLVM IR generated intrinsics for `{i,u}div i256`, `{i,u}rem i256`, and +// `imul i256` produce an error when compiling. Use the native implementations +// even when generated intrinsics are enabled. +#[path = "native/divmod.rs"] +mod divmod; +#[path = "native/mul.rs"] +#[allow(dead_code)] +mod mul; + +pub use self::{divmod::*, mul::imulc}; +use crate::{int::I256, uint::U256}; +use core::mem::{self, MaybeUninit}; + +macro_rules! def { + ($( + $(#[$a:meta])* + pub fn $name:ident( + $($p:ident : $t:ty),* + ) $(-> $ret:ty)?; + )*) => {$( + $(#[$a])* + pub fn $name( + $($p: $t,)* + ) $(-> $ret)? { + unsafe { + ethnum_intrinsics::$name($( + #[allow(clippy::transmute_ptr_to_ptr, clippy::useless_transmute)] + mem::transmute($p) + ),*) + } + } + )*}; +} + +def! { + pub fn add2(r: &mut U256, a: &U256); + pub fn add3(r: &mut MaybeUninit, a: &U256, b: &U256); + pub fn uaddc(r: &mut MaybeUninit, a: &U256, b: &U256) -> bool; + pub fn iaddc(r: &mut MaybeUninit, a: &I256, b: &I256) -> bool; + + pub fn sub2(r: &mut U256, a: &U256); + pub fn sub3(r: &mut MaybeUninit, a: &U256, b: &U256); + pub fn usubc(r: &mut MaybeUninit, a: &U256, b: &U256) -> bool; + pub fn isubc(r: &mut MaybeUninit, a: &I256, b: &I256) -> bool; + + pub fn mul2(r: &mut U256, a: &U256); + pub fn mul3(r: &mut MaybeUninit, a: &U256, b: &U256); + pub fn umulc(r: &mut MaybeUninit, a: &U256, b: &U256) -> bool; + + pub fn shl2(r: &mut U256, a: u32); + pub fn shl3(r: &mut MaybeUninit, a: &U256, b: u32); + + pub fn sar2(r: &mut I256, a: u32); + pub fn sar3(r: &mut MaybeUninit, a: &I256, b: u32); + pub fn shr2(r: &mut U256, a: u32); + pub fn shr3(r: &mut MaybeUninit, a: &U256, b: u32); + + pub fn rol3(r: &mut MaybeUninit, a: &U256, b: u32); + pub fn ror3(r: &mut MaybeUninit, a: &U256, b: u32); + + pub fn ctlz(a: &U256) -> u32; + pub fn cttz(a: &U256) -> u32; +} + +#[cfg(test)] +mod tests { + use super::*; + use core::{alloc::Layout, mem}; + + #[test] + fn layout() { + // Small note on alignment: Since we pass in pointers to our wide + // integer types we need only to make sure that the alignment of + // `ethnum::{I256, U256}` types are larger than the FFI-safe type (i.e. + // the alignment is compatible with the FFI type). + + assert_eq!( + Layout::new::(), + Layout::new::() + .align_to(mem::align_of::()) + .unwrap(), + ); + assert_eq!( + Layout::new::(), + Layout::new::() + .align_to(mem::align_of::()) + .unwrap(), + ); + } +} diff --git a/vendor/ethnum/src/intrinsics/native.rs b/vendor/ethnum/src/intrinsics/native.rs new file mode 100644 index 0000000..04abe09 --- /dev/null +++ b/vendor/ethnum/src/intrinsics/native.rs @@ -0,0 +1,13 @@ +//! This module contains native implementations for intrinsics. These are used +//! when generated IR intrinsics are disabled. + +mod add; +mod ctz; +mod divmod; +mod mul; +mod rot; +mod shl; +mod shr; +mod sub; + +pub use self::{add::*, ctz::*, divmod::*, mul::*, rot::*, shl::*, shr::*, sub::*}; diff --git a/vendor/ethnum/src/intrinsics/native/add.rs b/vendor/ethnum/src/intrinsics/native/add.rs new file mode 100644 index 0000000..dc65058 --- /dev/null +++ b/vendor/ethnum/src/intrinsics/native/add.rs @@ -0,0 +1,36 @@ +//! Module implementing addition intrinsics. + +use crate::{int::I256, uint::U256}; +use core::mem::MaybeUninit; + +#[inline] +pub fn add2(r: &mut U256, a: &U256) { + let (lo, carry) = r.low().overflowing_add(*a.low()); + *r.low_mut() = lo; + *r.high_mut() = r.high().wrapping_add(carry as _).wrapping_add(*a.high()); +} + +#[inline] +pub fn add3(r: &mut MaybeUninit, a: &U256, b: &U256) { + let (lo, carry) = a.low().overflowing_add(*b.low()); + let hi = a.high().wrapping_add(carry as _).wrapping_add(*b.high()); + + r.write(U256::from_words(hi, lo)); +} + +#[inline] +pub fn uaddc(r: &mut MaybeUninit, a: &U256, b: &U256) -> bool { + let (lo, carry_lo) = a.low().overflowing_add(*b.low()); + let (hi, carry_c) = a.high().overflowing_add(carry_lo as _); + let (hi, carry_hi) = hi.overflowing_add(*b.high()); + + r.write(U256::from_words(hi, lo)); + carry_c || carry_hi +} + +#[inline] +pub fn iaddc(r: &mut MaybeUninit, a: &I256, b: &I256) -> bool { + add3(cast!(uninit: r), cast!(ref: a), cast!(ref: b)); + let s = unsafe { r.assume_init_ref() }; + (*b >= 0 && s < a) || (*b < 0 && s >= a) +} diff --git a/vendor/ethnum/src/intrinsics/native/ctz.rs b/vendor/ethnum/src/intrinsics/native/ctz.rs new file mode 100644 index 0000000..f830e13 --- /dev/null +++ b/vendor/ethnum/src/intrinsics/native/ctz.rs @@ -0,0 +1,16 @@ +//! This module implements intrinsics for counting trailing and leading zeros +//! for 256-bit integers. + +use crate::uint::U256; + +#[inline] +pub fn ctlz(a: &U256) -> u32 { + let f = -((*a.high() == 0) as i128) as u128; + ((a.high() & !f) | (a.low() & f)).leading_zeros() + ((f as u32) & 128) +} + +#[inline] +pub fn cttz(a: &U256) -> u32 { + let f = -((*a.low() == 0) as i128) as u128; + ((a.high() & f) | (a.low() & !f)).trailing_zeros() + ((f as u32) & 128) +} diff --git a/vendor/ethnum/src/intrinsics/native/divmod.rs b/vendor/ethnum/src/intrinsics/native/divmod.rs new file mode 100644 index 0000000..0ff3a1d --- /dev/null +++ b/vendor/ethnum/src/intrinsics/native/divmod.rs @@ -0,0 +1,571 @@ +//! This module contains a Rust port of the `__u?divmodti4` compiler builtins +//! that are typically used for implementing 64-bit signed and unsigned division +//! on 32-bit platforms. +//! +//! This port is adapted to use 128-bit high and low words in order to implement +//! 256-bit division. +//! +//! This source is ported from LLVM project from C: +//! - signed division: +//! - unsigned division: + +use crate::{int::I256, uint::U256}; +use core::mem::MaybeUninit; + +#[inline(always)] +fn udiv256_by_128_to_128(u1: u128, u0: u128, mut v: u128, r: &mut u128) -> u128 { + const N_UDWORD_BITS: u32 = 128; + const B: u128 = 1 << (N_UDWORD_BITS / 2); // Number base (128 bits) + let (un1, un0): (u128, u128); // Norm. dividend LSD's + let (vn1, vn0): (u128, u128); // Norm. divisor digits + let (mut q1, mut q0): (u128, u128); // Quotient digits + let (un128, un21, un10): (u128, u128, u128); // Dividend digit pairs + + let s = v.leading_zeros(); + if s > 0 { + // Normalize the divisor. + v <<= s; + un128 = (u1 << s) | (u0 >> (N_UDWORD_BITS - s)); + un10 = u0 << s; // Shift dividend left + } else { + // Avoid undefined behavior of (u0 >> 64). + un128 = u1; + un10 = u0; + } + + // Break divisor up into two 64-bit digits. + vn1 = v >> (N_UDWORD_BITS / 2); + vn0 = v & 0xFFFF_FFFF_FFFF_FFFF; + + // Break right half of dividend into two digits. + un1 = un10 >> (N_UDWORD_BITS / 2); + un0 = un10 & 0xFFFF_FFFF_FFFF_FFFF; + + // Compute the first quotient digit, q1. + q1 = un128 / vn1; + let mut rhat = un128 - q1 * vn1; + + // q1 has at most error 2. No more than 2 iterations. + while q1 >= B || q1 * vn0 > B * rhat + un1 { + q1 -= 1; + rhat += vn1; + if rhat >= B { + break; + } + } + + un21 = un128 + .wrapping_mul(B) + .wrapping_add(un1) + .wrapping_sub(q1.wrapping_mul(v)); + + // Compute the second quotient digit. + q0 = un21 / vn1; + rhat = un21 - q0 * vn1; + + // q0 has at most error 2. No more than 2 iterations. + while q0 >= B || q0 * vn0 > B * rhat + un0 { + q0 -= 1; + rhat += vn1; + if rhat >= B { + break; + } + } + + *r = (un21 + .wrapping_mul(B) + .wrapping_add(un0) + .wrapping_sub(q0.wrapping_mul(v))) + >> s; + q1 * B + q0 +} + +#[allow(clippy::many_single_char_names)] +pub fn udivmod4( + res: &mut MaybeUninit, + a: &U256, + b: &U256, + rem: Option<&mut MaybeUninit>, +) { + // In the LLVM version on the x86_64 platform, `udiv256_by_128_to_128` would + // defer to `divq` instruction, which divides a 128-bit value by a 64-bit + // one returning a 64-bit value, making it very performant when dividing + // small values: + // ``` + // du_int result; + // __asm__("divq %[v]" + // : "=a"(result), "=d"(*r) + // : [ v ] "r"(v), "a"(u0), "d"(u1)); + // return result; + // ``` + // Unfortunately, there is no 256-bit equivalent on x86_64, but we can still + // shortcut if the high and low values of the operands are 0: + if a.high() | b.high() == 0 { + if let Some(rem) = rem { + rem.write(U256::from_words(0, a.low() % b.low())); + } + res.write(U256::from_words(0, a.low() / b.low())); + return; + } + + let dividend = *a; + let divisor = *b; + let quotient: U256; + let mut remainder: U256; + + if divisor > dividend { + if let Some(rem) = rem { + rem.write(dividend); + } + res.write(U256::ZERO); + return; + } + // When the divisor fits in 128 bits, we can use an optimized path. + if *divisor.high() == 0 { + remainder = U256::ZERO; + if dividend.high() < divisor.low() { + // The result fits in 128 bits. + quotient = U256::from_words( + 0, + udiv256_by_128_to_128( + *dividend.high(), + *dividend.low(), + *divisor.low(), + remainder.low_mut(), + ), + ); + } else { + // First, divide with the high part to get the remainder in dividend.s.high. + // After that dividend.s.high < divisor.s.low. + quotient = U256::from_words( + dividend.high() / divisor.low(), + udiv256_by_128_to_128( + dividend.high() % divisor.low(), + *dividend.low(), + *divisor.low(), + remainder.low_mut(), + ), + ); + } + if let Some(rem) = rem { + rem.write(remainder); + } + res.write(quotient); + return; + } + + (quotient, remainder) = div_mod_knuth(÷nd, &divisor); + + if let Some(rem) = rem { + rem.write(remainder); + } + res.write(quotient); +} + +// See Knuth, TAOCP, Volume 2, section 4.3.1, Algorithm D. +// https://skanthak.homepage.t-online.de/division.html +#[inline] +pub fn div_mod_knuth(u: &U256, v: &U256) -> (U256, U256) { + const N_UDWORD_BITS: u32 = 128; + + #[inline] + fn full_shl(a: &U256, shift: u32) -> [u128; 3] { + debug_assert!(shift < N_UDWORD_BITS); + let mut u = [0_u128; 3]; + let u_lo = a.low() << shift; + let u_hi = a >> (N_UDWORD_BITS - shift); + u[0] = u_lo; + u[1] = *u_hi.low(); + u[2] = *u_hi.high(); + + u + } + + #[inline] + fn full_shr(u: &[u128; 3], shift: u32) -> U256 { + debug_assert!(shift < N_UDWORD_BITS); + let mut res = U256::ZERO; + *res.low_mut() = u[0] >> shift; + *res.high_mut() = u[1] >> shift; + // carry + if shift > 0 { + let sh = N_UDWORD_BITS - shift; + *res.low_mut() |= u[1] << sh; + *res.high_mut() |= u[2] << sh; + } + + res + } + + // returns (lo, hi) + #[inline] + const fn split_u128_to_u128(a: u128) -> (u128, u128) { + (a & 0xFFFFFFFFFFFFFFFF, a >> (N_UDWORD_BITS / 2)) + } + + // returns (lo, hi) + #[inline] + const fn fullmul_u128(a: u128, b: u128) -> (u128, u128) { + let (a0, a1) = split_u128_to_u128(a); + let (b0, b1) = split_u128_to_u128(b); + + let mut t = a0 * b0; + let mut k: u128; + let w3: u128; + (w3, k) = split_u128_to_u128(t); + + t = a1 * b0 + k; + let (w1, w2) = split_u128_to_u128(t); + t = a0 * b1 + w1; + k = t >> 64; + + let w_hi = a1 * b1 + w2 + k; + let w_lo = (t << 64) + w3; + + (w_lo, w_hi) + } + + #[inline] + fn fullmul_u256_u128(a: &U256, b: u128) -> [u128; 3] { + let mut acc = [0_u128; 3]; + let mut lo: u128; + let mut carry: u128; + let c: bool; + if b != 0 { + (lo, carry) = fullmul_u128(*a.low(), b); + acc[0] = lo; + acc[1] = carry; + (lo, carry) = fullmul_u128(*a.high(), b); + (acc[1], c) = acc[1].overflowing_add(lo); + acc[2] = carry + c as u128; + } + + acc + } + + #[inline] + const fn add_carry(a: u128, b: u128, c: bool) -> (u128, bool) { + let (res1, overflow1) = b.overflowing_add(c as u128); + let (res2, overflow2) = u128::overflowing_add(a, res1); + + (res2, overflow1 || overflow2) + } + + #[inline] + const fn sub_carry(a: u128, b: u128, c: bool) -> (u128, bool) { + let (res1, overflow1) = b.overflowing_add(c as u128); + let (res2, overflow2) = u128::overflowing_sub(a, res1); + + (res2, overflow1 || overflow2) + } + + // D1. + // Make sure 128th bit in v's highest word is set. + // If we shift both u and v, it won't affect the quotient + // and the remainder will only need to be shifted back. + let shift = v.high().leading_zeros(); + debug_assert!(shift < N_UDWORD_BITS); + let v = v << shift; + debug_assert!(v.high() >> (N_UDWORD_BITS - 1) == 1); + // u will store the remainder (shifted) + let mut u = full_shl(u, shift); + + // quotient + let mut q = U256::ZERO; + let v_n_1 = *v.high(); + let v_n_2 = *v.low(); + + // D2. D7. - unrolled loop j == 0, n == 2, m == 0 (only one possible iteration) + let mut r_hat: u128 = 0; + let u_jn = u[2]; + + // D3. + // q_hat is our guess for the j-th quotient digit + // q_hat = min(b - 1, (u_{j+n} * b + u_{j+n-1}) / v_{n-1}) + // b = 1 << WORD_BITS + // Theorem B: q_hat >= q_j >= q_hat - 2 + let mut q_hat = if u_jn < v_n_1 { + //let (mut q_hat, mut r_hat) = _div_mod_u128(u_jn, u[j + n - 1], v_n_1); + let mut q_hat = udiv256_by_128_to_128(u_jn, u[1], v_n_1, &mut r_hat); + let mut overflow: bool; + // this loop takes at most 2 iterations + loop { + let another_iteration = { + // check if q_hat * v_{n-2} > b * r_hat + u_{j+n-2} + let (lo, hi) = fullmul_u128(q_hat, v_n_2); + hi > r_hat || (hi == r_hat && lo > u[0]) + }; + if !another_iteration { + break; + } + q_hat -= 1; + (r_hat, overflow) = r_hat.overflowing_add(v_n_1); + // if r_hat overflowed, we're done + if overflow { + break; + } + } + q_hat + } else { + // here q_hat >= q_j >= q_hat - 1 + u128::MAX + }; + + // ex. 20: + // since q_hat * v_{n-2} <= b * r_hat + u_{j+n-2}, + // either q_hat == q_j, or q_hat == q_j + 1 + + // D4. + // let's assume optimistically q_hat == q_j + // subtract (q_hat * v) from u[j..] + let q_hat_v = fullmul_u256_u128(&v, q_hat); + // u[j..] -= q_hat_v; + let mut c = false; + (u[0], c) = sub_carry(u[0], q_hat_v[0], c); + (u[1], c) = sub_carry(u[1], q_hat_v[1], c); + (u[2], c) = sub_carry(u[2], q_hat_v[2], c); + + // D6. + // actually, q_hat == q_j + 1 and u[j..] has overflowed + // highly unlikely ~ (1 / 2^127) + if c { + q_hat -= 1; + // add v to u[j..] + c = false; + (u[0], c) = add_carry(u[0], *v.low(), c); + (u[1], c) = add_carry(u[1], *v.high(), c); + u[2] = u[2].wrapping_add(c as u128); + } + + // D5. + *q.low_mut() = q_hat; + + // D8. + let remainder = full_shr(&u, shift); + + (q, remainder) +} + +#[inline] +pub fn udiv2(r: &mut U256, a: &U256) { + let (a, b) = (*r, a); + // SAFETY: `udivmod4` does not write `MaybeUninit::uninit()` to `res` and + // `U256` does not implement `Drop`. + let res = unsafe { &mut *(r as *mut U256).cast() }; + udivmod4(res, &a, b, None); +} + +#[inline] +pub fn udiv3(r: &mut MaybeUninit, a: &U256, b: &U256) { + udivmod4(r, a, b, None); +} + +#[inline] +pub fn urem2(r: &mut U256, a: &U256) { + let mut res = MaybeUninit::uninit(); + let (a, b) = (*r, a); + // SAFETY: `udivmod4` does not write `MaybeUninit::uninit()` to `rem` and + // `U256` does not implement `Drop`. + let r = unsafe { &mut *(r as *mut U256).cast() }; + udivmod4(&mut res, &a, b, Some(r)); +} + +#[inline] +pub fn urem3(r: &mut MaybeUninit, a: &U256, b: &U256) { + let mut res = MaybeUninit::uninit(); + udivmod4(&mut res, a, b, Some(r)); +} + +pub fn idivmod4( + res: &mut MaybeUninit, + a: &I256, + b: &I256, + rem: Option<&mut MaybeUninit>, +) { + const BITS_IN_TWORD_M1: u32 = 255; + let s_a = a >> BITS_IN_TWORD_M1; // s_a = a < 0 ? -1 : 0 + let mut s_b = b >> BITS_IN_TWORD_M1; // s_b = b < 0 ? -1 : 0 + let a = (a ^ s_a).wrapping_sub(s_a); // negate if s_a == -1 + let b = (b ^ s_b).wrapping_sub(s_b); // negate if s_b == -1 + s_b ^= s_a; // sign of quotient + udivmod4( + cast!(uninit: res), + cast!(ref: &a), + cast!(ref: &b), + cast!(optuninit: rem), + ); + let q = unsafe { res.assume_init_ref() }; + let q = (q ^ s_b).wrapping_sub(s_b); // negate if s_b == -1 + res.write(q); + if let Some(rem) = rem { + let r = unsafe { rem.assume_init_ref() }; + let r = (r ^ s_a).wrapping_sub(s_a); + rem.write(r); + } +} + +#[inline] +pub fn idiv2(r: &mut I256, a: &I256) { + let (a, b) = (*r, a); + // SAFETY: `udivmod4` does not write `MaybeUninit::uninit()` to `res` and + // `U256` does not implement `Drop`. + let res = unsafe { &mut *(r as *mut I256).cast() }; + idivmod4(res, &a, b, None); +} + +#[inline] +pub fn idiv3(r: &mut MaybeUninit, a: &I256, b: &I256) { + idivmod4(r, a, b, None); +} + +#[inline] +pub fn irem2(r: &mut I256, a: &I256) { + let mut res = MaybeUninit::uninit(); + let (a, b) = (*r, a); + // SAFETY: `udivmod4` does not write `MaybeUninit::uninit()` to `rem` and + // `U256` does not implement `Drop`. + let r = unsafe { &mut *(r as *mut I256).cast() }; + idivmod4(&mut res, &a, b, Some(r)); +} + +#[inline] +pub fn irem3(r: &mut MaybeUninit, a: &I256, b: &I256) { + let mut res = MaybeUninit::uninit(); + idivmod4(&mut res, a, b, Some(r)); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::AsU256; + + fn udiv(a: impl AsU256, b: impl AsU256) -> U256 { + let mut r = MaybeUninit::uninit(); + udiv3(&mut r, &a.as_u256(), &b.as_u256()); + unsafe { r.assume_init() } + } + + fn urem(a: impl AsU256, b: impl AsU256) -> U256 { + let mut r = MaybeUninit::uninit(); + urem3(&mut r, &a.as_u256(), &b.as_u256()); + unsafe { r.assume_init() } + } + + #[test] + fn division() { + // 0 X + // --- + // 0 X + assert_eq!(udiv(100, 9), 11); + + // 0 X + // --- + // K X + assert_eq!(udiv(!0u128, U256::ONE << 128u32), 0); + + // K 0 + // --- + // K 0 + assert_eq!(udiv(U256::from_words(100, 0), U256::from_words(10, 0)), 10); + + // K K + // --- + // K 0 + assert_eq!(udiv(U256::from_words(100, 1337), U256::ONE << 130u32), 25); + assert_eq!( + udiv(U256::from_words(1337, !0), U256::from_words(63, 0)), + 21 + ); + + // K X + // --- + // 0 K + assert_eq!( + udiv(U256::from_words(42, 0), U256::ONE), + U256::from_words(42, 0), + ); + assert_eq!( + udiv(U256::from_words(42, 42), U256::ONE << 42), + 42u128 << (128 - 42), + ); + assert_eq!( + udiv(U256::from_words(1337, !0), 0xc0ffee), + 35996389033280467545299711090127855, + ); + assert_eq!( + udiv(U256::from_words(42, 0), 99), + 144362216269489045105674075880144089708, + ); + + // K X + // --- + // K K + assert_eq!( + udiv(U256::from_words(100, 100), U256::from_words(1000, 1000)), + 0, + ); + assert_eq!( + udiv(U256::from_words(1337, !0), U256::from_words(43, !0)), + 30, + ); + } + + #[test] + #[should_panic] + fn division_by_zero() { + udiv(1, 0); + } + + #[test] + fn remainder() { + // 0 X + // --- + // 0 X + assert_eq!(urem(100, 9), 1); + + // 0 X + // --- + // K X + assert_eq!(urem(!0u128, U256::ONE << 128u32), !0u128); + + // K 0 + // --- + // K 0 + assert_eq!(urem(U256::from_words(100, 0), U256::from_words(10, 0)), 0); + + // K K + // --- + // K 0 + assert_eq!(urem(U256::from_words(100, 1337), U256::ONE << 130u32), 1337); + assert_eq!( + urem(U256::from_words(1337, !0), U256::from_words(63, 0)), + U256::from_words(14, !0), + ); + + // K X + // --- + // 0 K + assert_eq!(urem(U256::from_words(42, 0), U256::ONE), 0); + assert_eq!(urem(U256::from_words(42, 42), U256::ONE << 42), 42); + assert_eq!(urem(U256::from_words(1337, !0), 0xc0ffee), 1910477); + assert_eq!(urem(U256::from_words(42, 0), 99), 60); + + // K X + // --- + // K K + assert_eq!( + urem(U256::from_words(100, 100), U256::from_words(1000, 1000)), + U256::from_words(100, 100), + ); + assert_eq!( + urem(U256::from_words(1337, !0), U256::from_words(43, !0)), + U256::from_words(18, 29), + ); + } + + #[test] + #[should_panic] + fn remainder_by_zero() { + urem(1, 0); + } +} diff --git a/vendor/ethnum/src/intrinsics/native/mul.rs b/vendor/ethnum/src/intrinsics/native/mul.rs new file mode 100644 index 0000000..27fa064 --- /dev/null +++ b/vendor/ethnum/src/intrinsics/native/mul.rs @@ -0,0 +1,119 @@ +//! This module contains a Rust port of the `__multi3` compiler builtin that is +//! typically used for implementing 64-bit multiplication on 32-bit platforms. +//! +//! This port is adapted to use 128-bit high and low words and return carry +//! information in order to implement 256-bit overflowing multiplication. +//! +//! This source is ported from LLVM project from C: +//! + +use crate::{int::I256, uint::U256}; +use core::mem::MaybeUninit; + +#[inline] +pub fn umulddi3(a: &u128, b: &u128) -> U256 { + const BITS_IN_DWORD_2: u32 = 64; + const LOWER_MASK: u128 = u128::MAX >> BITS_IN_DWORD_2; + + let mut low = (a & LOWER_MASK) * (b & LOWER_MASK); + let mut t = low >> BITS_IN_DWORD_2; + low &= LOWER_MASK; + t += (a >> BITS_IN_DWORD_2) * (b & LOWER_MASK); + low += (t & LOWER_MASK) << BITS_IN_DWORD_2; + let mut high = t >> BITS_IN_DWORD_2; + t = low >> BITS_IN_DWORD_2; + low &= LOWER_MASK; + t += (b >> BITS_IN_DWORD_2) * (a & LOWER_MASK); + low += (t & LOWER_MASK) << BITS_IN_DWORD_2; + high += t >> BITS_IN_DWORD_2; + high += (a >> BITS_IN_DWORD_2) * (b >> BITS_IN_DWORD_2); + + U256::from_words(high, low) +} + +#[inline] +pub fn mul2(r: &mut U256, a: &U256) { + let (a, b) = (*r, a); + // SAFETY: `multi3` does not write `MaybeUninit::uninit()` to `res` and + // `U256` does not implement `Drop`. + let res = unsafe { &mut *(r as *mut U256).cast() }; + mul3(res, &a, b); +} + +#[inline] +pub fn mul3(res: &mut MaybeUninit, a: &U256, b: &U256) { + let mut r = umulddi3(a.low(), b.low()); + + let hi_lo = a.high().wrapping_mul(*b.low()); + let lo_hi = a.low().wrapping_mul(*b.high()); + *r.high_mut() = r.high().wrapping_add(hi_lo.wrapping_add(lo_hi)); + + res.write(r); +} + +#[inline] +pub fn umulc(r: &mut MaybeUninit, a: &U256, b: &U256) -> bool { + let mut res = umulddi3(a.low(), b.low()); + + let (hi_lo, overflow_hi_lo) = a.high().overflowing_mul(*b.low()); + let (lo_hi, overflow_lo_hi) = a.low().overflowing_mul(*b.high()); + let (hi, overflow_hi) = hi_lo.overflowing_add(lo_hi); + let (high, overflow_high) = res.high().overflowing_add(hi); + *res.high_mut() = high; + + let overflow_hi_hi = (*a.high() != 0) & (*b.high() != 0); + + r.write(res); + overflow_hi_lo | overflow_lo_hi | overflow_hi | overflow_high | overflow_hi_hi +} + +#[inline] +pub fn imulc(res: &mut MaybeUninit, a: &I256, b: &I256) -> bool { + mul3(cast!(uninit: res), cast!(ref: a), cast!(ref: b)); + if *a == I256::MIN { + return *b != 0 && *b != 1; + } + if *b == I256::MIN { + return *a != 0 && *a != 1; + } + let sa = a >> (I256::BITS - 1); + let abs_a = (a ^ sa).wrapping_sub(sa); + let sb = b >> (I256::BITS - 1); + let abs_b = (b ^ sb).wrapping_sub(sb); + if abs_a < 2 || abs_b < 2 { + return false; + } + if sa == sb { + abs_a > I256::MAX / abs_b + } else { + abs_a > I256::MIN / -abs_b + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::AsU256; + + fn umul(a: impl AsU256, b: impl AsU256) -> (U256, bool) { + let mut r = MaybeUninit::uninit(); + let overflow = umulc(&mut r, &a.as_u256(), &b.as_u256()); + (unsafe { r.assume_init() }, overflow) + } + + #[test] + fn multiplication() { + assert_eq!(umul(6, 7), (42.as_u256(), false)); + + assert_eq!(umul(U256::MAX, 1), (U256::MAX, false)); + assert_eq!(umul(1, U256::MAX), (U256::MAX, false)); + assert_eq!(umul(U256::MAX, 0), (U256::ZERO, false)); + assert_eq!(umul(0, U256::MAX), (U256::ZERO, false)); + + assert_eq!(umul(U256::MAX, 5), (U256::MAX ^ 4, true)); + assert_eq!( + umul(u128::MAX, u128::MAX), + (U256::from_words(!0 << 1, 1), false), + ); + } +} diff --git a/vendor/ethnum/src/intrinsics/native/rot.rs b/vendor/ethnum/src/intrinsics/native/rot.rs new file mode 100644 index 0000000..8a584bc --- /dev/null +++ b/vendor/ethnum/src/intrinsics/native/rot.rs @@ -0,0 +1,15 @@ +//! This module implements right and left rotation (**not** shifting) intrinsics +//! for 256-bit integers. + +use crate::uint::U256; +use core::mem::MaybeUninit; + +#[inline] +pub fn rol3(r: &mut MaybeUninit, a: &U256, b: u32) { + r.write((a << (b & 0xff)) | (a >> ((256 - b) & 0xff))); +} + +#[inline] +pub fn ror3(r: &mut MaybeUninit, a: &U256, b: u32) { + r.write((a >> (b & 0xff)) | (a << ((256 - b) & 0xff))); +} diff --git a/vendor/ethnum/src/intrinsics/native/shl.rs b/vendor/ethnum/src/intrinsics/native/shl.rs new file mode 100644 index 0000000..f4f291d --- /dev/null +++ b/vendor/ethnum/src/intrinsics/native/shl.rs @@ -0,0 +1,34 @@ +//! Module containing arithmetic left shift intrinsic. + +use crate::uint::U256; +use core::mem::MaybeUninit; + +#[inline] +pub fn shl2(r: &mut U256, a: u32) { + debug_assert!(a < 256, "shl intrinsic called with overflowing shift"); + + let (hi, lo) = if a == 0 { + return; + } else if a < 128 { + ((r.high() << a) | (r.low() >> (128 - a)), r.low() << a) + } else { + (r.low() << (a & 0x7f), 0) + }; + + *r = U256::from_words(hi, lo); +} + +#[inline] +pub fn shl3(r: &mut MaybeUninit, a: &U256, b: u32) { + debug_assert!(b < 256, "shl intrinsic called with overflowing shift"); + + let (hi, lo) = if b == 0 { + (*a.high(), *a.low()) + } else if b < 128 { + ((a.high() << b) | (a.low() >> (128 - b)), a.low() << b) + } else { + (a.low() << (b & 0x7f), 0) + }; + + r.write(U256::from_words(hi, lo)); +} diff --git a/vendor/ethnum/src/intrinsics/native/shr.rs b/vendor/ethnum/src/intrinsics/native/shr.rs new file mode 100644 index 0000000..38b4f7a --- /dev/null +++ b/vendor/ethnum/src/intrinsics/native/shr.rs @@ -0,0 +1,70 @@ +//! Module containing logical right shift intrinsic. + +use crate::{int::I256, uint::U256}; +use core::mem::MaybeUninit; + +#[inline] +pub fn sar2(r: &mut I256, a: u32) { + debug_assert!(a < 256, "shr intrinsic called with overflowing shift"); + + let (hi, lo) = if a == 0 { + return; + } else if a < 128 { + ( + r.high() >> a, + ((*r.low() as u128) >> a | ((*r.high() as u128) << (128 - a))) as i128, + ) + } else { + (r.high() >> 127, (r.high() >> (a & 0x7f))) + }; + + *r = I256::from_words(hi, lo); +} + +#[inline] +pub fn sar3(r: &mut MaybeUninit, a: &I256, b: u32) { + debug_assert!(b < 256, "shr intrinsic called with overflowing shift"); + + let (hi, lo) = if b == 0 { + (*a.high(), *a.low()) + } else if b < 128 { + ( + a.high() >> b, + ((*a.low() as u128) >> b | ((*a.high() as u128) << (128 - b))) as i128, + ) + } else { + (a.high() >> 127, a.high() >> (b & 0x7f)) + }; + + r.write(I256::from_words(hi, lo)); +} + +#[inline] +pub fn shr2(r: &mut U256, a: u32) { + debug_assert!(a < 256, "shr intrinsic called with overflowing shift"); + + let (hi, lo) = if a == 0 { + return; + } else if a < 128 { + (r.high() >> a, r.low() >> a | (r.high() << (128 - a))) + } else { + (0, r.high() >> (a & 0x7f)) + }; + + *r = U256::from_words(hi, lo); +} + +#[inline] +pub fn shr3(r: &mut MaybeUninit, a: &U256, b: u32) { + debug_assert!(b < 256, "shr intrinsic called with overflowing shift"); + + let (hi, lo) = if b == 0 { + (*a.high(), *a.low()) + } else if b < 128 { + (a.high() >> b, a.low() >> b | (a.high() << (128 - b))) + } else { + (0, a.high() >> (b & 0x7f)) + }; + + r.write(U256::from_words(hi, lo)); +} diff --git a/vendor/ethnum/src/intrinsics/native/sub.rs b/vendor/ethnum/src/intrinsics/native/sub.rs new file mode 100644 index 0000000..2fbce20 --- /dev/null +++ b/vendor/ethnum/src/intrinsics/native/sub.rs @@ -0,0 +1,36 @@ +//! Module implementing subtraction intrinsics. + +use crate::{int::I256, uint::U256}; +use core::mem::MaybeUninit; + +#[inline] +pub fn sub2(r: &mut U256, a: &U256) { + let (lo, carry) = r.low().overflowing_sub(*a.low()); + *r.low_mut() = lo; + *r.high_mut() = r.high().wrapping_sub(carry as _).wrapping_sub(*a.high()); +} + +#[inline] +pub fn sub3(r: &mut MaybeUninit, a: &U256, b: &U256) { + let (lo, carry) = a.low().overflowing_sub(*b.low()); + let hi = a.high().wrapping_sub(carry as _).wrapping_sub(*b.high()); + + r.write(U256::from_words(hi, lo)); +} + +#[inline] +pub fn usubc(r: &mut MaybeUninit, a: &U256, b: &U256) -> bool { + let (lo, carry_lo) = a.low().overflowing_sub(*b.low()); + let (hi, carry_c) = a.high().overflowing_sub(carry_lo as _); + let (hi, carry_hi) = hi.overflowing_sub(*b.high()); + + r.write(U256::from_words(hi, lo)); + carry_c || carry_hi +} + +#[inline] +pub fn isubc(r: &mut MaybeUninit, a: &I256, b: &I256) -> bool { + sub3(cast!(uninit: r), cast!(ref: a), cast!(ref: b)); + let s = unsafe { r.assume_init_ref() }; + (*b >= 0 && s > a) || (*b < 0 && s <= a) +} diff --git a/vendor/ethnum/src/intrinsics/signed.rs b/vendor/ethnum/src/intrinsics/signed.rs new file mode 100644 index 0000000..a28b94d --- /dev/null +++ b/vendor/ethnum/src/intrinsics/signed.rs @@ -0,0 +1,85 @@ +//! Module containing signed wrapping functions around various intrinsics that +//! are agnostic to sign. +//! +//! This module can be helpful when using intrinsics directly. + +pub use super::{ + add2 as uadd2, add3 as uadd3, ctlz as uctlz, cttz as ucttz, iaddc, idiv2, idiv3, imulc, irem2, + irem3, isubc, mul2 as umul2, mul3 as umul3, rol3 as urol3, ror3 as uror3, sar2 as isar2, + sar3 as isar3, shl2 as ushl2, shl3 as ushl3, shr2 as ushr2, shr3 as ushr3, sub2 as usub2, + sub3 as usub3, uaddc, udiv2, udiv3, umulc, urem2, urem3, usubc, +}; +use crate::int::I256; +use core::mem::MaybeUninit; + +#[inline] +pub fn iadd2(r: &mut I256, a: &I256) { + super::add2(cast!(mut: r), cast!(ref: a)); +} + +#[inline] +pub fn iadd3(r: &mut MaybeUninit, a: &I256, b: &I256) { + super::add3(cast!(uninit: r), cast!(ref: a), cast!(ref: b)); +} + +#[inline] +pub fn isub2(r: &mut I256, a: &I256) { + super::sub2(cast!(mut: r), cast!(ref: a)); +} + +#[inline] +pub fn isub3(r: &mut MaybeUninit, a: &I256, b: &I256) { + super::sub3(cast!(uninit: r), cast!(ref: a), cast!(ref: b)); +} + +#[inline] +pub fn imul2(r: &mut I256, a: &I256) { + super::mul2(cast!(mut: r), cast!(ref: a)); +} + +#[inline] +pub fn imul3(r: &mut MaybeUninit, a: &I256, b: &I256) { + super::mul3(cast!(uninit: r), cast!(ref: a), cast!(ref: b)); +} + +#[inline] +pub fn ishl2(r: &mut I256, a: u32) { + super::shl2(cast!(mut: r), a); +} + +#[inline] +pub fn ishl3(r: &mut MaybeUninit, a: &I256, b: u32) { + super::shl3(cast!(uninit: r), cast!(ref: a), b); +} + +#[inline] +pub fn irol3(r: &mut MaybeUninit, a: &I256, b: u32) { + super::rol3(cast!(uninit: r), cast!(ref: a), b); +} + +#[inline] +pub fn iror3(r: &mut MaybeUninit, a: &I256, b: u32) { + super::ror3(cast!(uninit: r), cast!(ref: a), b); +} + +#[inline] +pub fn ictlz(a: &I256) -> u32 { + super::ctlz(cast!(ref: a)) +} + +#[inline] +pub fn icttz(a: &I256) -> u32 { + super::cttz(cast!(ref: a)) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::uint::U256; + use core::alloc::Layout; + + #[test] + fn layout() { + assert_eq!(Layout::new::(), Layout::new::()); + } +} diff --git a/vendor/ethnum/src/lib.rs b/vendor/ethnum/src/lib.rs new file mode 100644 index 0000000..ccc53ef --- /dev/null +++ b/vendor/ethnum/src/lib.rs @@ -0,0 +1,133 @@ +//! This crate implements 256-bit integer types. +//! +//! The implementation tries to follow as closely as possible to primitive +//! integer types, and should implement all the common methods and traits as the +//! primitive integer types. + +#![deny(missing_docs)] +#![no_std] + +#[cfg(test)] +extern crate alloc; + +#[macro_use] +mod macros { + #[macro_use] + pub mod cmp; + #[macro_use] + pub mod fmt; + #[macro_use] + pub mod iter; + #[macro_use] + pub mod ops; + #[macro_use] + pub mod parse; +} + +mod error; +mod fmt; +mod int; +pub mod intrinsics; +mod parse; +#[cfg(feature = "serde")] +pub mod serde; +mod uint; + +/// Macro for 256-bit signed integer literal. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # use ethnum::{int, I256}; +/// assert_eq!( +/// int!( +/// "-57896044618658097711785492504343953926634992332820282019728792003956564819968" +/// ), +/// I256::MIN, +/// ); +/// ``` +/// +/// Additionally, this macro accepts `0b` for binary, `0o` for octal, and `0x` +/// for hexadecimal literals. Using `_` for spacing is also permitted. +/// +/// ``` +/// # use ethnum::{int, I256}; +/// assert_eq!( +/// int!( +/// "0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff +/// ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff" +/// ), +/// I256::MAX, +/// ); +/// assert_eq!(int!("0b101010"), 42); +/// assert_eq!(int!("-0o52"), -42); +/// ``` +#[macro_export] +macro_rules! int { + ($integer:literal) => {{ + const VALUE: $crate::I256 = $crate::I256::const_from_str_prefixed($integer); + VALUE + }}; +} + +/// Macro for 256-bit unsigned integer literal. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # use ethnum::{uint, U256}; +/// assert_eq!( +/// uint!( +/// "115792089237316195423570985008687907852837564279074904382605163141518161494337" +/// ), +/// U256::from_words( +/// 0xfffffffffffffffffffffffffffffffe, +/// 0xbaaedce6af48a03bbfd25e8cd0364141, +/// ), +/// ); +/// ``` +/// +/// Additionally, this macro accepts `0b` for binary, `0o` for octal, and `0x` +/// for hexadecimal literals. Using `_` for spacing is also permitted. +/// +/// ``` +/// # use ethnum::{uint, U256}; +/// assert_eq!( +/// uint!( +/// "0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff +/// ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff" +/// ), +/// U256::MAX, +/// ); +/// assert_eq!(uint!("0b101010"), 42); +/// assert_eq!(uint!("0o52"), 42); +/// ``` +#[macro_export] +macro_rules! uint { + ($integer:literal) => {{ + const VALUE: $crate::U256 = $crate::U256::const_from_str_prefixed($integer); + VALUE + }}; +} + +/// Convenience re-export of 256-integer types and as- conversion traits. +pub mod prelude { + pub use crate::{AsI256, AsU256, I256, U256}; +} + +pub use crate::{ + int::{AsI256, I256}, + uint::{AsU256, U256}, +}; + +/// A 256-bit signed integer type. +#[allow(non_camel_case_types)] +pub type i256 = I256; + +/// A 256-bit unsigned integer type. +#[allow(non_camel_case_types)] +pub type u256 = U256; diff --git a/vendor/ethnum/src/macros/cmp.rs b/vendor/ethnum/src/macros/cmp.rs new file mode 100644 index 0000000..21e2183 --- /dev/null +++ b/vendor/ethnum/src/macros/cmp.rs @@ -0,0 +1,42 @@ +//! Module containing macros for implementing `core::ops` traits. + +macro_rules! impl_cmp { + ( + impl Cmp for $int:ident ($prim:ident); + ) => { + impl PartialOrd for $int { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> { + Some(self.cmp(other)) + } + } + + impl PartialEq<$prim> for $int { + #[inline] + fn eq(&self, other: &$prim) -> bool { + *self == $int::new(*other) + } + } + + impl PartialEq<$int> for $prim { + #[inline] + fn eq(&self, other: &$int) -> bool { + $int::new(*self) == *other + } + } + + impl PartialOrd<$prim> for $int { + #[inline] + fn partial_cmp(&self, rhs: &$prim) -> Option<::core::cmp::Ordering> { + Some(self.cmp(&$int::new(*rhs))) + } + } + + impl PartialOrd<$int> for $prim { + #[inline] + fn partial_cmp(&self, rhs: &$int) -> Option<::core::cmp::Ordering> { + Some($int::new(*self).cmp(rhs)) + } + } + }; +} diff --git a/vendor/ethnum/src/macros/fmt.rs b/vendor/ethnum/src/macros/fmt.rs new file mode 100644 index 0000000..d9d4290 --- /dev/null +++ b/vendor/ethnum/src/macros/fmt.rs @@ -0,0 +1,86 @@ +//! Module containing macros for implementing `core::fmt` traits. + +macro_rules! impl_fmt { + (impl Fmt for $int:ident;) => { + __impl_fmt_base! { Binary for $int } + __impl_fmt_base! { Octal for $int } + __impl_fmt_base! { LowerHex for $int } + __impl_fmt_base! { UpperHex for $int } + + impl ::core::fmt::Debug for $int { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + // NOTE: Work around `Formatter::debug_{lower,upper}_hex` being private + // and not stabilized. + #[allow(deprecated)] + let flags = f.flags(); + const DEBUG_LOWER_HEX: u32 = 1 << 4; + const DEBUG_UPPER_HEX: u32 = 1 << 5; + + if flags & DEBUG_LOWER_HEX != 0 { + ::core::fmt::LowerHex::fmt(self, f) + } else if flags & DEBUG_UPPER_HEX != 0 { + ::core::fmt::UpperHex::fmt(self, f) + } else { + ::core::fmt::Display::fmt(self, f) + } + } + } + + impl ::core::fmt::Display for $int { + #[allow(unused_comparisons, unused_imports)] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use $crate::uint::AsU256; + + let is_nonnegative = *self >= 0; + let n = if is_nonnegative { + self.as_u256() + } else { + // convert the negative num to positive by summing 1 to it's 2 complement + (!self.as_u256()).wrapping_add($crate::uint::U256::ONE) + }; + $crate::fmt::fmt_u256(n, is_nonnegative, f) + } + } + + impl ::core::fmt::LowerExp for $int { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + // TODO(nlordell): Ideally this should be implemented similarly + // to the primitive integer types as seen here: + // https://doc.rust-lang.org/src/core/fmt/num.rs.html#274 + // Unfortunately, just porting this implementation is not + // possible as it requires private standard library items. For + // now, just convert to a `f64` as an approximation. + ::core::fmt::LowerExp::fmt(&self.as_f64(), f) + } + } + + impl ::core::fmt::UpperExp for $int { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + ::core::fmt::UpperExp::fmt(&self.as_f64(), f) + } + } + }; +} + +macro_rules! __impl_fmt_base { + ($base:ident for $int:ident) => { + impl ::core::fmt::$base for $int { + #[allow(unused_imports)] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use $crate::{fmt::GenericRadix, uint::AsU256}; + let (abs, is_nonnegative) = if *self < 0 && f.sign_minus() { + // NOTE(nlordell): This is non-standard break from the Rust + // standard integer types, but allows `format!("{val:-#x")` + // notation for formating a number as `-0x...` (and in + // in general prefix with a `-` sign for negative numbers + // with radix formatting. + (self.wrapping_neg(), false) + } else { + (*self, true) + }; + $crate::fmt::$base.fmt_u256(abs.as_u256(), is_nonnegative, f) + } + } + }; +} diff --git a/vendor/ethnum/src/macros/iter.rs b/vendor/ethnum/src/macros/iter.rs new file mode 100644 index 0000000..496cccf --- /dev/null +++ b/vendor/ethnum/src/macros/iter.rs @@ -0,0 +1,31 @@ +//! Module containing macros for implementing iterator specific traits. + +macro_rules! impl_iter { + ( + impl Iter for $int:ident; + ) => { + impl ::core::iter::Sum for $int { + fn sum>(iter: I) -> Self { + iter.fold($int::ZERO, ::core::ops::Add::add) + } + } + + impl ::core::iter::Product for $int { + fn product>(iter: I) -> Self { + iter.fold($int::ONE, ::core::ops::Mul::mul) + } + } + + impl<'a> ::core::iter::Sum<&'a $int> for $int { + fn sum>(iter: I) -> Self { + iter.fold($int::ZERO, ::core::ops::Add::add) + } + } + + impl<'a> ::core::iter::Product<&'a $int> for $int { + fn product>(iter: I) -> Self { + iter.fold($int::ONE, ::core::ops::Mul::mul) + } + } + }; +} diff --git a/vendor/ethnum/src/macros/ops.rs b/vendor/ethnum/src/macros/ops.rs new file mode 100644 index 0000000..e735359 --- /dev/null +++ b/vendor/ethnum/src/macros/ops.rs @@ -0,0 +1,542 @@ +//! Module containing macros for implementing `core::ops` traits. + +macro_rules! impl_ops { + ( + for $int:ident | $prim:ident { + add => $add2:ident, $add3:ident, $addc:ident; + mul => $mul2:ident, $mul3:ident, $mulc:ident; + sub => $sub2:ident, $sub3:ident, $subc:ident; + div => $div2:ident, $div3:ident; + rem => $rem2:ident, $rem3:ident; + shl => $shl2:ident, $shl3:ident; + shr => $shr2:ident, $shr3:ident; + } + ) => { + __impl_ops_binop! { + for $int | $prim + + impl Add { + + add => $add3, $addc; "add with overflow" + } + impl Mul { + * mul => $mul3, $mulc; "multiply with overflow" + } + impl Sub { + - sub => $sub3, $subc; "subtract with overflow" + } + } + + __impl_ops_divmod! { + for $int | $prim + + impl Div { + / div => $div3; "divide by zero" + } + impl Rem { + % rem => $rem3; "calculate the remainder with a divisor of zero" + } + } + + __impl_ops_shift! { + for $int + + impl Shl { + << shl => $shl3; "shift left with overflow" + } + impl Shr { + >> shr => $shr3; "shift right with overflow" + } + } + + __impl_ops_unop! { + impl Not for $int { + not(x) { + let $int([a, b]) = x; + $int([!a, !b]) + } + } + } + + __impl_ops_bitwise! { + for $int | $prim + + impl BitAnd { + & bitand; + } + impl BitOr { + | bitor; + } + impl BitXor { + ^ bitxor; + } + } + + __impl_ops_binop_assign! { + for $int | $prim + + impl AddAssign { + += add_assign => $add2, +; + } + impl DivAssign { + /= div_assign => $div2, /; + } + impl MulAssign { + *= mul_assign => $mul2, *; + } + impl RemAssign { + %= rem_assign => $rem2, %; + } + impl SubAssign { + -= sub_assign => $sub2, -; + } + } + + __impl_ops_shift_assign! { + for $int + + impl ShlAssign { + <<= shl_assign => $shl2, <<; + } + impl ShrAssign { + >>= shr_assign => $shr2, >>; + } + } + + __impl_ops_bitwise_assign! { + for $int | $prim + + impl BitAndAssign { + &= bitand_assign; + } + impl BitOrAssign { + |= bitor_assign; + } + impl BitXorAssign { + ^= bitxor_assign; + } + } + }; +} + +macro_rules! impl_ops_neg { + ( + for $int:ident { + add => $add2:ident; + } + ) => { + __impl_ops_unop! { + impl Neg for $int { + neg(x) { + #[cfg(debug_assertions)] + { + if x.eq(&I256::MIN) { + panic!("attempt to negate with overflow"); + } + } + let mut result = !x; + $add2(&mut result, &$int::ONE); + result + } + } + } + }; +} + +macro_rules! __impl_ops_binop { + ( + for $int:ident | $prim:ident + $( + impl $op:ident { + $x:tt $method:ident => $op3:path, $opc:path; $msg:expr + } + )* + ) => {$( + impl ::core::ops::$op for &'_ $int { + type Output = $int; + + #[inline] + fn $method(self, rhs: Self) -> Self::Output { + let mut result = ::core::mem::MaybeUninit::uninit(); + #[cfg(not(debug_assertions))] + { + $op3(&mut result, self, rhs); + } + #[cfg(debug_assertions)] + { + if $opc(&mut result, self, rhs) { + panic!(concat!("attempt to ", $msg)); + } + } + unsafe { result.assume_init() } + } + } + + __impl_ops_binop_extra_variants! { + impl $op for $int | $prim { $method = $x } + } + )*}; +} + +macro_rules! __impl_ops_binop_extra_variants { + ( + impl $op:ident for $int:ident | $prim:ident { $method:ident = $x:tt } + ) => { + __impl_ops_binop_ref! { + impl $op for $int { + $method(a: &'_ $int, b: $int) { a $x &b }; + $method(a: $int, b: &'_ $int) { &a $x b }; + $method(a: $int, b: $int) { &a $x &b }; + + $method(a: &'_ $int, b: $prim) { a $x $int::new(b) }; + $method(a: &'_ $int, b: &'_ $prim) { a $x *b }; + $method(a: $int, b: &'_ $prim) { &a $x *b }; + $method(a: $int, b: $prim) { &a $x b }; + + $method(a: $prim, b: &'_ $int) { $int::new(a) $x b }; + $method(a: &'_ $prim, b: &'_ $int) { *a $x b }; + $method(a: &'_ $prim, b: $int) { *a $x &b }; + $method(a: $prim, b: $int) { a $x &b }; + } + } + }; +} + +macro_rules! __impl_ops_binop_ref { + ( + impl $op:ident for $int:ident {$( + $method:ident($lhs:ident: $lhst:ty, $rhs:ident: $rhst:ty) $impl:block; + )*} + ) => {$( + impl ::core::ops::$op<$rhst> for $lhst { + type Output = $int; + + #[inline] + fn $method(self, rhs: $rhst) -> Self::Output { + let ($lhs, $rhs) = (self, rhs); + $impl + } + } + )*} +} + +macro_rules! __impl_ops_divmod { + ( + for $int:ident | $prim:ident + $( + impl $op:ident { + $x:tt $method:ident => $op3:path; $msg:expr + } + )* + ) => {$( + impl ::core::ops::$op for &'_ $int { + type Output = $int; + + #[inline] + fn $method(self, rhs: Self) -> Self::Output { + if *rhs == 0 { + panic!(concat!("attempt to ", $msg)); + } + + let mut result = ::core::mem::MaybeUninit::uninit(); + $op3(&mut result, self, rhs); + unsafe { result.assume_init() } + } + } + + __impl_ops_binop_extra_variants! { + impl $op for $int | $prim { $method = $x } + } + )*}; +} + +macro_rules! __impl_ops_shift { + ( + for $int:ident + $( + impl $op:ident { + $x:tt $method:ident => $op3:path; $msg:expr + } + )* + ) => {$( + impl ::core::ops::$op for &'_ $int { + type Output = $int; + + #[inline] + fn $method(self, rhs: u32) -> Self::Output { + #[cfg(debug_assertions)] + if rhs > 0xff { + panic!(concat!("attempt to ", $msg)); + } + + let mut result = ::core::mem::MaybeUninit::uninit(); + $op3(&mut result, self, rhs); + + unsafe { result.assume_init() } + } + } + + __impl_ops_shift_extra_variants! { + impl $op for $int { $method = $x } + } + )*}; +} + +macro_rules! __impl_ops_shift_extra_variants { + ( + impl $op:ident for $int:ident { $method:ident = $x:tt } + ) => { + __impl_ops_binop_ref! { + impl $op for $int { + $method(a: &'_ $int, b: &'_ u32) { a $x *b }; + $method(a: $int, b: &'_ u32) { &a $x *b }; + $method(a: $int, b: u32) { &a $x b }; + } + } + + __impl_ops_shift_extra_variants! { __inner: + impl $op<$crate::int::I256, $crate::uint::U256> for $int { + $method = $x + |b| { b.as_u32() } + } + } + + __impl_ops_shift_extra_variants! { __inner: + impl $op for $int { + $method = $x + |b| { b as u32 } + } + } + }; + + (__inner: + impl $op:ident <$($lhst:ty),*> for $int:ident + { $method:ident = $x:tt |$lhs:ident| $conv:block } + ) => {$( + __impl_ops_binop_ref! { + impl $op for $int { + $method(a: &'_ $int, $lhs: $lhst) { + #[cfg(not(debug_assertions))] + let b = $conv; + #[cfg(debug_assertions)] + let b = u32::try_from($lhs).unwrap_or(u32::MAX); + a $x b + }; + $method(a: &'_ $int, b: &'_ $lhst) { a $x *b }; + $method(a: $int, b: &'_ $lhst) { &a $x *b }; + $method(a: $int, b: $lhst) { &a $x b }; + } + } + )*}; +} + +macro_rules! __impl_ops_unop { + ( + impl $op:ident for $int:ident { + $method:ident($self:ident) $impl:block + } + ) => { + impl ::core::ops::$op for $int { + type Output = $int; + + #[inline] + fn $method(self) -> Self::Output { + let $self = self; + $impl + } + } + + impl ::core::ops::$op for &'_ $int { + type Output = $int; + + #[inline] + fn $method(self) -> Self::Output { + let $self = self; + $impl + } + } + }; +} + +macro_rules! __impl_ops_bitwise { + ( + for $int:ident | $prim:ident + $( + impl $op:ident { + $x:tt $method:ident; + } + )* + ) => {$( + impl ::core::ops::$op<&'_ $int> for &'_ $int { + type Output = $int; + + #[inline] + fn $method(self, rhs: &'_ $int) -> Self::Output { + let $int([a0, a1]) = self; + let $int([b0, b1]) = rhs; + $int([a0 $x b0, a1 $x b1]) + } + } + + __impl_ops_binop_extra_variants! { + impl $op for $int | $prim { $method = $x } + } + )*}; +} + +macro_rules! __impl_ops_binop_assign { + ( + for $int:ident | $prim:ident + $( + impl $op:ident { + $x:tt $method:ident => $op2:path, $y:tt; + } + )* + ) => {$( + impl ::core::ops::$op<&'_ $int> for $int { + #[inline] + fn $method(&mut self, rhs: &'_ $int) { + #[cfg(not(debug_assertions))] + { + $op2(self, rhs); + } + #[cfg(debug_assertions)] + { + *self = &*self $y rhs; + } + } + } + + __impl_ops_binop_assign_extra_variants! { + impl $op for $int | $prim { $method = $x } + } + )*}; +} + +macro_rules! __impl_ops_binop_assign_extra_variants { + ( + impl $op:ident for $int:ident | $prim:ident { $method:ident = $x:tt } + ) => { + __impl_ops_binop_assign_ref! { + impl $op for $int { + $method(a, b: $int) { *a $x &b }; + + $method(a, b: $prim) { *a $x $int::new(b) }; + $method(a, b: &'_ $prim) { *a $x *b }; + } + } + }; +} + +macro_rules! __impl_ops_binop_assign_ref { + ( + impl $op:ident for $int:ident {$( + $method:ident($self:ident, $rhs:ident: $rhst:ty) $impl:block; + )*} + ) => {$( + impl ::core::ops::$op<$rhst> for $int { + #[inline] + fn $method(&mut self, rhs: $rhst) { + let ($self, $rhs) = (self, rhs); + $impl + } + } + )*} +} + +macro_rules! __impl_ops_shift_assign { + ( + for $int:ident + $( + impl $op:ident { + $x:tt $method:ident => $op2:path, $y:tt; + } + )* + ) => {$( + impl ::core::ops::$op for $int { + #[inline] + fn $method(&mut self, rhs: u32) { + #[cfg(not(debug_assertions))] + { + $op2(self, rhs); + } + #[cfg(debug_assertions)] + { + *self = &*self $y rhs; + } + } + } + + __impl_ops_shift_assign_extra_variants! { + impl $op for $int { $method = $x } + } + )*}; +} + +macro_rules! __impl_ops_shift_assign_extra_variants { + ( + impl $op:ident for $int:ident { $method:ident = $x:tt } + ) => { + __impl_ops_binop_assign_ref! { + impl $op for $int { + $method(a, b: &'_ u32) { *a $x *b }; + } + } + + __impl_ops_shift_assign_extra_variants! { __inner: + impl $op<$crate::int::I256, $crate::uint::U256> for $int { + $method = $x + |b| { b.as_u32() } + } + } + + __impl_ops_shift_assign_extra_variants! { __inner: + impl $op for $int { + $method = $x + |b| { b as u32 } + } + } + }; + + (__inner: + impl $op:ident <$($lhst:ty),*> for $int:ident + { $method:ident = $x:tt |$lhs:ident| $conv:block } + ) => {$( + __impl_ops_binop_assign_ref! { + impl $op for $int { + $method(a, $lhs: $lhst) { + #[cfg(not(debug_assertions))] + let b = $conv; + #[cfg(debug_assertions)] + let b = u32::try_from($lhs).unwrap_or(u32::MAX); + *a $x b + }; + $method(a, b: &'_ $lhst) { *a $x *b }; + } + } + )*}; +} + +macro_rules! __impl_ops_bitwise_assign { + ( + for $int:ident | $prim:ident + $( + impl $op:ident { + $x:tt $method:ident; + } + )* + ) => {$( + impl ::core::ops::$op<&'_ $int> for $int { + #[inline] + fn $method(&mut self, rhs: &'_ $int) { + let $int([a0, a1]) = self; + let $int([b0, b1]) = rhs; + *a0 $x b0; + *a1 $x b1; + } + } + + __impl_ops_binop_assign_extra_variants! { + impl $op for $int | $prim { $method = $x } + } + )*}; +} diff --git a/vendor/ethnum/src/macros/parse.rs b/vendor/ethnum/src/macros/parse.rs new file mode 100644 index 0000000..b2ee93c --- /dev/null +++ b/vendor/ethnum/src/macros/parse.rs @@ -0,0 +1,34 @@ +//! Module containing macros for implementing string to integer parsing. + +macro_rules! impl_from_str { + (impl FromStr for $int:ident;) => { + impl $crate::parse::FromStrRadixHelper for $int { + const MIN: Self = Self::MIN; + #[inline] + fn from_u32(u: u32) -> Self { + Self::from(u) + } + #[inline] + fn checked_mul(&self, other: u32) -> Option { + Self::checked_mul(*self, Self::from(other)) + } + #[inline] + fn checked_sub(&self, other: u32) -> Option { + Self::checked_sub(*self, Self::from(other)) + } + #[inline] + fn checked_add(&self, other: u32) -> Option { + Self::checked_add(*self, Self::from(other)) + } + } + + impl ::core::str::FromStr for $int { + type Err = ::core::num::ParseIntError; + + #[inline] + fn from_str(s: &str) -> Result { + $crate::parse::from_str_radix(s, 10, None) + } + } + }; +} diff --git a/vendor/ethnum/src/parse.rs b/vendor/ethnum/src/parse.rs new file mode 100644 index 0000000..dca813c --- /dev/null +++ b/vendor/ethnum/src/parse.rs @@ -0,0 +1,205 @@ +//! Module with common integer parsing logic. +//! +//! Most of these implementations were ported from the Rust standard library's +//! implementation for primitive integer types: +//! + +use crate::U256; +use core::{ + mem, + num::{IntErrorKind, ParseIntError}, + ops::{Add, Mul, Sub}, +}; + +#[doc(hidden)] +pub(crate) trait FromStrRadixHelper: + PartialOrd + Copy + Add + Sub + Mul +{ + const MIN: Self; + fn from_u32(u: u32) -> Self; + fn checked_mul(&self, other: u32) -> Option; + fn checked_sub(&self, other: u32) -> Option; + fn checked_add(&self, other: u32) -> Option; +} + +#[inline(always)] +fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { + radix <= 16 && digits.len() <= mem::size_of::() * 2 - is_signed_ty as usize +} + +pub(crate) fn from_str_radix( + src: &str, + radix: u32, + prefix: Option<&str>, +) -> Result { + use self::IntErrorKind::*; + use crate::error::pie; + + assert!( + (2..=36).contains(&radix), + "from_str_radix_int: must lie in the range `[2, 36]` - found {}", + radix + ); + + if src.is_empty() { + return Err(pie(Empty)); + } + + let is_signed_ty = T::from_u32(0) > T::MIN; + + // all valid digits are ascii, so we will just iterate over the utf8 bytes + // and cast them to chars. .to_digit() will safely return None for anything + // other than a valid ascii digit for the given radix, including the first-byte + // of multi-byte sequences + let src = src.as_bytes(); + + let (is_positive, prefixed_digits) = match src[0] { + b'+' | b'-' if src[1..].is_empty() => { + return Err(pie(InvalidDigit)); + } + b'+' => (true, &src[1..]), + b'-' if is_signed_ty => (false, &src[1..]), + _ => (true, src), + }; + + let digits = match prefix { + Some(prefix) => prefixed_digits + .strip_prefix(prefix.as_bytes()) + .ok_or(pie(InvalidDigit))?, + None => prefixed_digits, + }; + if digits.is_empty() { + return Err(pie(InvalidDigit)); + } + + let mut result = T::from_u32(0); + + if can_not_overflow::(radix, is_signed_ty, digits) { + // If the len of the str is short compared to the range of the type + // we are parsing into, then we can be certain that an overflow will not occur. + // This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition + // above is a faster (conservative) approximation of this. + // + // Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest: + // `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow. + // `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow. + macro_rules! run_unchecked_loop { + ($unchecked_additive_op:expr) => { + for &c in digits { + result = result * T::from_u32(radix); + let x = (c as char).to_digit(radix).ok_or(pie(InvalidDigit))?; + result = $unchecked_additive_op(result, T::from_u32(x)); + } + }; + } + if is_positive { + run_unchecked_loop!(::add) + } else { + run_unchecked_loop!(::sub) + }; + } else { + macro_rules! run_checked_loop { + ($checked_additive_op:ident, $overflow_err:expr) => { + for &c in digits { + // When `radix` is passed in as a literal, rather than doing a slow `imul` + // the compiler can use shifts if `radix` can be expressed as a + // sum of powers of 2 (x*10 can be written as x*8 + x*2). + // When the compiler can't use these optimisations, + // the latency of the multiplication can be hidden by issuing it + // before the result is needed to improve performance on + // modern out-of-order CPU as multiplication here is slower + // than the other instructions, we can get the end result faster + // doing multiplication first and let the CPU spends other cycles + // doing other computation and get multiplication result later. + let mul = result.checked_mul(radix); + let x = (c as char).to_digit(radix).ok_or(pie(InvalidDigit))?; + result = mul.ok_or_else($overflow_err)?; + result = T::$checked_additive_op(&result, x).ok_or_else($overflow_err)?; + } + }; + } + if is_positive { + run_checked_loop!(checked_add, || pie(PosOverflow)) + } else { + run_checked_loop!(checked_sub, || pie(NegOverflow)) + }; + } + Ok(result) +} + +pub(crate) fn from_str_prefixed(src: &str) -> Result { + from_str_radix(src, 2, Some("0b")) + .or_else(|_| from_str_radix(src, 8, Some("0o"))) + .or_else(|_| from_str_radix(src, 16, Some("0x"))) + .or_else(|_| from_str_radix(src, 10, None)) +} + +pub(crate) const fn const_from_str_prefixed(bytes: &[u8], start: usize) -> U256 { + const fn check(overflow: bool) { + assert!(!overflow, "overflows integer type"); + } + + const fn add(a: U256, b: u8) -> U256 { + let (hi, lo) = a.into_words(); + + let (lo, carry) = lo.overflowing_add(b as _); + let (hi, overflow) = hi.overflowing_add(carry as _); + check(overflow); + + U256::from_words(hi, lo) + } + + const fn mul(a: U256, r: u128) -> U256 { + let (hi, lo) = a.into_words(); + let (lh, ll) = (lo >> 64, lo & u64::MAX as u128); + + let ll = ll * r; + let lh = lh * r; + let (hi, overflow) = hi.overflowing_mul(r); + check(overflow); + + let (lo, overflow) = ll.overflowing_add(lh << 64); + check(overflow); + let (hi, overflow) = hi.overflowing_add(lh >> 64); + check(overflow); + + U256::from_words(hi, lo) + } + + assert!(bytes.len() > start, "missing number"); + + let (radix, mut i) = if bytes.len() - start > 2 { + match (bytes[start], bytes[start + 1]) { + (b'0', b'b') => (2, start + 2), + (b'0', b'o') => (8, start + 2), + (b'0', b'x') => (16, start + 2), + _ => (10, start), + } + } else { + (10, start) + }; + + let mut value = U256::ZERO; + + while i < bytes.len() { + let byte = bytes[i]; + i += 1; + + if byte == b'_' || byte.is_ascii_whitespace() { + continue; + } + + let next = match (byte, radix) { + (b'0'..=b'1', 2 | 8 | 10 | 16) => byte - b'0', + (b'2'..=b'7', 8 | 10 | 16) => byte - b'0', + (b'8'..=b'9', 10 | 16) => byte - b'0', + (b'a'..=b'f', 16) => byte - b'a' + 0xa, + (b'A'..=b'F', 16) => byte - b'A' + 0xa, + (b'_', _) => continue, + _ => panic!("invalid digit"), + }; + value = add(mul(value, radix), next); + } + + value +} diff --git a/vendor/ethnum/src/serde.rs b/vendor/ethnum/src/serde.rs new file mode 100644 index 0000000..2c283f0 --- /dev/null +++ b/vendor/ethnum/src/serde.rs @@ -0,0 +1,1579 @@ +//! Serde serialization implementation for 256-bit integer types. +//! +//! This implementation is very JSON-centric in that it serializes the integer +//! types as `QUANTITIES` as specified in the Ethereum RPC. That is, integers +//! are encoded as `"0x"` prefixed strings without extrenuous leading `0`s. For +//! negative signed integers, the string is prefixed with a `"-"` sign. +//! +//! Note that this module contains alternative serialization schemes that can +//! be used with `#[serde(with = "...")]`. +//! +//! # Examples +//! +//! Basic usage: +//! +//! ```text +//! #[derive(Deserialize, Serialize)] +//! struct Example { +//! a: U256, // "0x2a" +//! #[serde(with = "ethnum::serde::decimal")] +//! b: I256, // "-42" +//! #[serde(with = "ethnum::serde::prefixed")] +//! c: U256, // "0x2a" or "42" +//! #[serde(with = "ethnum::serde::permissive")] +//! d: I256, // "-0x2a" or "-42" or -42 +//! #[serde(with = "ethnum::serde::bytes::be")] +//! e: U256, // [0x2a, 0x00, ..., 0x00] +//! #[serde(with = "ethnum::serde::bytes::le")] +//! f: I256, // [0xd6, 0xff, ..., 0xff] +//! #[serde(with = "ethnum::serde::compressed_bytes::be")] +//! g: U256, // [0x2a] +//! #[serde(with = "ethnum::serde::compressed_bytes::le")] +//! h: I256, // [0xd6] +//! } +//! ``` + +use crate::{int::I256, uint::U256}; +use core::{ + fmt::{self, Display, Formatter, Write}, + mem::MaybeUninit, + ptr, slice, str, +}; +use serde::{ + de::{self, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; + +impl Serialize for I256 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut f = FormatBuffer::hex(); + write!(f, "{self:-#x}").expect("unexpected formatting failure"); + serializer.serialize_str(f.as_str()) + } +} + +impl Serialize for U256 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut f = FormatBuffer::hex(); + write!(f, "{self:#x}").expect("unexpected formatting failure"); + serializer.serialize_str(f.as_str()) + } +} + +impl<'de> Deserialize<'de> for I256 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(FormatVisitor(Self::from_str_hex)) + } +} + +impl<'de> Deserialize<'de> for U256 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(FormatVisitor(Self::from_str_hex)) + } +} + +/// Module for use with `#[serde(with = "ethnum::serde::decimal")]` to specify +/// decimal string serialization for 256-bit integer types. +pub mod decimal { + use super::*; + use core::num::ParseIntError; + + #[doc(hidden)] + pub trait Decimal: Sized { + fn from_str_decimal(src: &str) -> Result; + fn write_decimal(&self, f: &mut impl Write); + } + + impl Decimal for I256 { + fn from_str_decimal(src: &str) -> Result { + Self::from_str_radix(src, 10) + } + fn write_decimal(&self, f: &mut impl Write) { + write!(f, "{self}").expect("unexpected formatting error") + } + } + + impl Decimal for U256 { + fn from_str_decimal(src: &str) -> Result { + Self::from_str_radix(src, 10) + } + fn write_decimal(&self, f: &mut impl Write) { + write!(f, "{self}").expect("unexpected formatting error") + } + } + + #[doc(hidden)] + pub fn serialize(value: &T, serializer: S) -> Result + where + T: Decimal, + S: Serializer, + { + let mut f = FormatBuffer::decimal(); + value.write_decimal(&mut f); + serializer.serialize_str(f.as_str()) + } + + #[doc(hidden)] + pub fn deserialize<'de, T, D>(deserializer: D) -> Result + where + T: Decimal, + D: Deserializer<'de>, + { + deserializer.deserialize_str(FormatVisitor(T::from_str_decimal)) + } +} + +/// Module for use with `#[serde(with = "ethnum::serde::prefixed")]` to specify +/// prefixed string serialization for 256-bit integer types. +/// +/// This allows serialization to look for an optional `0x` prefix to determine +/// if it is a hexadecimal string or decimal string. +pub mod prefixed { + use super::*; + use core::num::ParseIntError; + + #[doc(hidden)] + pub trait Prefixed: Serialize + Sized { + fn from_str_prefixed(src: &str) -> Result; + } + + impl Prefixed for I256 { + fn from_str_prefixed(src: &str) -> Result { + Self::from_str_prefixed(src) + } + } + + impl Prefixed for U256 { + fn from_str_prefixed(src: &str) -> Result { + Self::from_str_prefixed(src) + } + } + + #[doc(hidden)] + pub fn serialize(value: &T, serializer: S) -> Result + where + T: Prefixed, + S: Serializer, + { + value.serialize(serializer) + } + + #[doc(hidden)] + pub fn deserialize<'de, T, D>(deserializer: D) -> Result + where + T: Prefixed, + D: Deserializer<'de>, + { + deserializer.deserialize_str(FormatVisitor(T::from_str_prefixed)) + } +} + +/// Module for use with `#[serde(with = "ethnum::serde::permissive")]` to +/// specify extremely permissive serialization for 256-bit integer types. +/// +/// This allows serialization to also accept standard numerical types as values +/// in addition to prefixed strings. +pub mod permissive { + use super::{prefixed::Prefixed, FormatVisitor}; + use crate::{AsI256 as _, I256, U256}; + use core::fmt::{self, Formatter}; + use core::marker::PhantomData; + use serde::{ + de::{self, Deserializer, Visitor}, + Serializer, + }; + + #[doc(hidden)] + pub trait Permissive: Prefixed { + fn cast(value: I256) -> Self; + } + + impl Permissive for I256 { + fn cast(value: I256) -> Self { + value + } + } + + impl Permissive for U256 { + fn cast(value: I256) -> Self { + value.as_u256() + } + } + + #[doc(hidden)] + pub fn serialize(value: &T, serializer: S) -> Result + where + T: Permissive, + S: Serializer, + { + value.serialize(serializer) + } + + struct PermissiveVisitor(PhantomData); + + impl<'de, T> Visitor<'de> for PermissiveVisitor + where + T: Permissive, + { + type Value = T; + + fn expecting(&self, f: &mut Formatter) -> fmt::Result { + f.write_str("number, decimal string or '0x-' prefixed hexadecimal string") + } + + fn visit_i64(self, v: i64) -> Result + where + E: de::Error, + { + Ok(T::cast(v.as_i256())) + } + + fn visit_u64(self, v: u64) -> Result + where + E: de::Error, + { + Ok(T::cast(v.as_i256())) + } + + fn visit_i128(self, v: i128) -> Result + where + E: de::Error, + { + Ok(T::cast(v.as_i256())) + } + + fn visit_u128(self, v: u128) -> Result + where + E: de::Error, + { + Ok(T::cast(v.as_i256())) + } + + fn visit_f32(self, v: f32) -> Result + where + E: de::Error, + { + const N: f32 = (1_u64 << 24) as _; + if !(-N..N).contains(&v) { + return Err(de::Error::custom( + "invalid conversion from single precision floating point \ + number outside of valid integer range (-2^24, 2^24)", + )); + } + + self.visit_f64(v as _) + } + + fn visit_f64(self, v: f64) -> Result + where + E: de::Error, + { + const N: f64 = (1_u64 << 53) as _; + if !(-N..N).contains(&v) { + return Err(de::Error::custom( + "invalid conversion from double precision floating point \ + number outside of valid integer range (-2^53, 2^53)", + )); + } + + // SOUNDNESS: `#[no_std]` does not have `f64::fract`, so work around + // it by casting to and from an integer type. This is sound because + // we already verified that the `f64` is within a "safe" range. + let i = v as i64; + if i as f64 != v { + return Err(de::Error::custom( + "invalid conversion from floating point number \ + with fractional part to 256-bit integer", + )); + } + + Ok(T::cast(i.as_i256())) + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + FormatVisitor(T::from_str_prefixed).visit_str(v) + } + } + + #[doc(hidden)] + pub fn deserialize<'de, T, D>(deserializer: D) -> Result + where + T: Permissive, + D: Deserializer<'de>, + { + deserializer.deserialize_any(PermissiveVisitor(PhantomData)) + } +} + +/// Serde byte serialization for 256-bit integer types. +pub mod bytes { + macro_rules! endianness { + ($name:literal; $to:ident, $from:ident) => { + use crate::{I256, U256}; + use core::{ + fmt::{self, Formatter}, + marker::PhantomData, + mem::{self, MaybeUninit}, + }; + use serde::{ + de::{self, Deserializer, Visitor}, + Serializer, + }; + + #[doc(hidden)] + pub trait Bytes: Sized + Copy { + fn to_bytes(self) -> [u8; 32]; + fn from_bytes(bytes: [u8; 32]) -> Self; + } + + impl Bytes for I256 { + fn to_bytes(self) -> [u8; 32] { + self.$to() + } + + fn from_bytes(bytes: [u8; 32]) -> Self { + I256::$from(bytes) + } + } + + impl Bytes for U256 { + fn to_bytes(self) -> [u8; 32] { + self.$to() + } + + fn from_bytes(bytes: [u8; 32]) -> Self { + U256::$from(bytes) + } + } + + #[doc(hidden)] + pub fn serialize(value: &T, serializer: S) -> Result + where + T: Bytes, + S: Serializer, + { + let bytes = value.to_bytes(); + serializer.serialize_bytes(&bytes) + } + + struct BytesVisitor(PhantomData); + + impl<'de, T> Visitor<'de> for BytesVisitor + where + T: Bytes, + { + type Value = T; + + fn expecting(&self, f: &mut Formatter) -> fmt::Result { + f.write_str(concat!("32 bytes in ", $name, " endian")) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: de::Error, + { + let bytes = v + .try_into() + .map_err(|_| E::invalid_length(v.len(), &self))?; + + Ok(T::from_bytes(bytes)) + } + + fn visit_seq(self, mut seq: S) -> Result + where + S: de::SeqAccess<'de>, + { + match seq.size_hint() { + Some(len) if len != 32 => { + return Err(de::Error::invalid_length(len, &self)) + } + _ => {} + } + + let mut bytes = [MaybeUninit::::uninit(); 32]; + for i in 0..32 { + bytes[i].write( + seq.next_element()? + .ok_or(de::Error::invalid_length(i, &self))?, + ); + } + if seq.next_element::()?.is_some() { + return Err(de::Error::invalid_length(33, &self)); + } + + // SAFETY: all bytes have been initialized in for loop. + let bytes = unsafe { mem::transmute(bytes) }; + + Ok(T::from_bytes(bytes)) + } + } + + #[doc(hidden)] + pub fn deserialize<'de, T, D>(deserializer: D) -> Result + where + T: Bytes, + D: Deserializer<'de>, + { + deserializer.deserialize_bytes(BytesVisitor(PhantomData)) + } + }; + } + + /// Module for use with `#[serde(with = "ethnum::serde::bytes::le")]` to + /// specify little endian byte serialization for 256-bit integer types. + pub mod le { + endianness!("little"; to_le_bytes, from_le_bytes); + } + + /// Module for use with `#[serde(with = "ethnum::serde::bytes::be")]` to + /// specify big endian byte serialization for 256-bit integer types. + pub mod be { + endianness!("big"; to_be_bytes, from_be_bytes); + } + + /// Module for use with `#[serde(with = "ethnum::serde::bytes::ne")]` to + /// specify native endian byte serialization for 256-bit integer types. + pub mod ne { + #[cfg(target_endian = "little")] + #[doc(hidden)] + pub use super::le::{deserialize, serialize}; + + #[cfg(target_endian = "big")] + #[doc(hidden)] + pub use super::be::{deserialize, serialize}; + } +} + +/// Serde compressed byte serialization for 256-bit integer types. +pub mod compressed_bytes { + use crate::{I256, U256}; + + #[doc(hidden)] + pub trait CompressedBytes { + fn leading_bits(&self) -> u32; + fn extend(msb: u8) -> u8; + } + + impl CompressedBytes for I256 { + fn leading_bits(&self) -> u32 { + match self.is_negative() { + true => self.leading_ones() - 1, + false => self.leading_zeros(), + } + } + + fn extend(msb: u8) -> u8 { + ((msb as i8) >> 7) as _ + } + } + + impl CompressedBytes for U256 { + fn leading_bits(&self) -> u32 { + self.leading_zeros() + } + + fn extend(_: u8) -> u8 { + 0 + } + } + + macro_rules! endianness { + ($name:literal; $parent:ident, |$tb:ident| $to:block, |$fb:ident| $from:block) => { + use super::CompressedBytes; + use crate::serde::bytes::$parent::Bytes; + use core::{ + fmt::{self, Formatter}, + marker::PhantomData, + mem::MaybeUninit, + }; + use serde::{ + de::{self, Deserializer, Visitor}, + Serializer, + }; + + #[doc(hidden)] + pub fn serialize(value: &T, serializer: S) -> Result + where + T: Bytes + CompressedBytes, + S: Serializer, + { + let bytes = value.to_bytes(); + let $tb = (value.leading_bits() as usize) / 8; + let index = { $to }; + serializer.serialize_bytes(&bytes[index]) + } + + struct CompressedBytesVisitor(PhantomData); + + impl<'de, T> Visitor<'de> for CompressedBytesVisitor + where + T: Bytes + CompressedBytes, + { + type Value = T; + + fn expecting(&self, f: &mut Formatter) -> fmt::Result { + f.write_str(concat!("bytes in ", $name, " endian")) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: de::Error, + { + if v.len() > 32 { + return Err(E::invalid_length(v.len(), &self)); + } + + let extend = T::extend(v.last().copied().unwrap_or_default()); + let mut bytes = [extend; 32]; + let $fb = v.len(); + let index = { $from }; + bytes[index].copy_from_slice(v); + + Ok(T::from_bytes(bytes)) + } + + fn visit_seq(self, mut seq: S) -> Result + where + S: de::SeqAccess<'de>, + { + match seq.size_hint() { + Some(len) if len > 32 => return Err(de::Error::invalid_length(len, &self)), + _ => {} + } + + let mut bytes = [MaybeUninit::::uninit(); 32]; + let mut i = 0; + while i < 32 { + let b = match seq.next_element()? { + Some(b) => b, + None => break, + }; + bytes[i].write(b); + i += 1; + } + if i == 32 && seq.next_element::()?.is_some() { + return Err(de::Error::invalid_length(33, &self)); + } + + // SAFETY: bytes up to `i` have been initialized in while + // loop. + let bytes = unsafe { &*(&bytes[..i] as *const _ as *const _) }; + + self.visit_bytes(bytes) + } + } + + #[doc(hidden)] + pub fn deserialize<'de, T, D>(deserializer: D) -> Result + where + T: Bytes + CompressedBytes, + D: Deserializer<'de>, + { + deserializer.deserialize_bytes(CompressedBytesVisitor(PhantomData)) + } + }; + } + + /// Module for `#[serde(with = "ethnum::serde::compressed_bytes::le")]` + /// to specify compressed little endian byte serialization for 256-bit + /// integer types. This will serialize integer types with as few bytes as + /// possible. + pub mod le { + endianness!("little"; le, |l| { ..32 - l }, |l| { ..l }); + } + + /// Module for `#[serde(with = "ethnum::serde::compressed_bytes::be")]` + /// to specify compressed big endian byte serialization for 256-bit + /// integer types. This will serialize integer types with as few bytes as + /// possible. + pub mod be { + endianness!("big"; be, |l| { l.. }, |l| { 32 - l.. }); + } + + /// Module for `#[serde(with = "ethnum::serde::compressed_bytes::ne")]` + /// to specify compressed native endian byte serialization for 256-bit + /// integer types. This will serialize integer types with as few bytes as + /// possible. + pub mod ne { + #[cfg(target_endian = "little")] + #[doc(hidden)] + pub use super::le::{deserialize, serialize}; + + #[cfg(target_endian = "big")] + #[doc(hidden)] + pub use super::be::{deserialize, serialize}; + } +} + +/// Internal visitor struct implementation to facilitate implementing different +/// serialization formats. +struct FormatVisitor(F); + +impl<'de, T, E, F> Visitor<'de> for FormatVisitor +where + E: Display, + F: FnOnce(&str) -> Result, +{ + type Value = T; + + fn expecting(&self, f: &mut Formatter) -> fmt::Result { + f.write_str("a formatted 256-bit integer") + } + + fn visit_str(self, v: &str) -> Result + where + E_: de::Error, + { + self.0(v).map_err(de::Error::custom) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E_: de::Error, + { + let string = str::from_utf8(v) + .map_err(|_| de::Error::invalid_value(de::Unexpected::Bytes(v), &self))?; + self.visit_str(string) + } +} + +/// A stack-allocated buffer that can be used for writing formatted strings. +/// +/// This allows us to leverage existing `fmt` implementations on integer types +/// without requiring heap allocations (i.e. writing to a `String` buffer). +struct FormatBuffer { + offset: usize, + buffer: [MaybeUninit; N], +} + +impl FormatBuffer { + /// Creates a new formatting buffer. + fn new() -> Self { + Self { + offset: 0, + buffer: [MaybeUninit::uninit(); N], + } + } + + /// Returns a `str` to the currently written data. + fn as_str(&self) -> &str { + // SAFETY: We only ever write valid UTF-8 strings to the buffer, so the + // resulting string will always be valid. + unsafe { + let buffer = slice::from_raw_parts(self.buffer[0].as_ptr(), self.offset); + str::from_utf8_unchecked(buffer) + } + } +} + +impl FormatBuffer<78> { + /// Allocates a formatting buffer large enough to hold any possible decimal + /// encoded 256-bit value. + fn decimal() -> Self { + Self::new() + } +} + +impl FormatBuffer<67> { + /// Allocates a formatting buffer large enough to hold any possible + /// hexadecimal encoded 256-bit value. + fn hex() -> Self { + Self::new() + } +} + +impl Write for FormatBuffer { + fn write_str(&mut self, s: &str) -> fmt::Result { + let end = self.offset.checked_add(s.len()).ok_or(fmt::Error)?; + + // Make sure there is enough space in the buffer. + if end > N { + return Err(fmt::Error); + } + + // SAFETY: We checked that there is enough space in the buffer to fit + // the string `s` starting from `offset`, and the pointers cannot be + // overlapping because of Rust ownership semantics (i.e. `s` cannot + // overlap with `buffer` because we have a mutable reference to `self` + // and by extension `buffer`). + unsafe { + let buffer = self.buffer[0].as_mut_ptr().add(self.offset); + ptr::copy_nonoverlapping(s.as_ptr(), buffer, s.len()); + } + self.offset = end; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::{ + boxed::Box, + fmt::{Display, LowerHex}, + format, + string::String, + vec, + vec::Vec, + }; + use serde::{ + de::{value, IntoDeserializer}, + ser::Impossible, + }; + + #[test] + fn serialize_integers() { + macro_rules! ser { + ($method:expr, $value:expr) => {{ + let value = $value; + ($method)(&value, StringSerializer).unwrap() + }}; + } + + macro_rules! bin_ser { + ($method:expr, $value:expr) => {{ + let value = $value; + ($method)(&value, BytesSerializer).unwrap() + }}; + } + + assert_eq!( + ser!(I256::serialize, I256::MIN), + "-0x8000000000000000000000000000000000000000000000000000000000000000", + ); + assert_eq!(ser!(I256::serialize, I256::new(-1)), "-0x1"); + assert_eq!(ser!(I256::serialize, I256::new(0)), "0x0"); + assert_eq!(ser!(I256::serialize, I256::new(42)), "0x2a"); + + assert_eq!(ser!(U256::serialize, U256::new(0)), "0x0"); + assert_eq!(ser!(U256::serialize, U256::new(4919)), "0x1337"); + assert_eq!( + ser!(U256::serialize, U256::MAX), + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + + assert_eq!( + ser!(decimal::serialize, I256::MIN), + "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + ); + assert_eq!(ser!(decimal::serialize, I256::new(-1)), "-1"); + assert_eq!(ser!(decimal::serialize, I256::new(0)), "0"); + assert_eq!(ser!(decimal::serialize, I256::new(42)), "42"); + + assert_eq!(ser!(decimal::serialize, U256::new(0)), "0"); + assert_eq!(ser!(decimal::serialize, U256::new(4919)), "4919"); + assert_eq!( + ser!(decimal::serialize, U256::MAX), + "115792089237316195423570985008687907853269984665640564039457584007913129639935", + ); + + assert_eq!(ser!(prefixed::serialize, I256::new(42)), "0x2a"); + assert_eq!(ser!(permissive::serialize, I256::new(42)), "0x2a"); + + assert_eq!(bin_ser!(bytes::le::serialize, U256::ZERO), vec![0x00; 32]); + assert_eq!(bin_ser!(bytes::le::serialize, U256::MAX), vec![0xff; 32]); + assert_eq!(bin_ser!(bytes::le::serialize, U256::new(0x4215)), { + let mut v = vec![0x15, 0x42]; + v.resize(32, 0x00); + v + }); + + assert_eq!( + bin_ser!(bytes::le::serialize, I256::new(-1)), + vec![0xff; 32] + ); + assert_eq!(bin_ser!(bytes::le::serialize, I256::new(-424242)), { + let mut v = vec![0xce, 0x86, 0xf9]; + v.resize(32, 0xff); + v + }); + + assert_eq!(bin_ser!(bytes::be::serialize, U256::ZERO), vec![0x00; 32]); + assert_eq!(bin_ser!(bytes::be::serialize, U256::MAX), vec![0xff; 32]); + assert_eq!(bin_ser!(bytes::be::serialize, U256::new(0x4215)), { + let mut v = vec![0x00; 32]; + v[30..].copy_from_slice(&[0x42, 0x15]); + v + }); + + assert_eq!( + bin_ser!(bytes::be::serialize, I256::new(-1)), + vec![0xff; 32] + ); + assert_eq!(bin_ser!(bytes::be::serialize, I256::new(-424242)), { + let mut v = vec![0xff; 32]; + v[29..].copy_from_slice(&[0xf9, 0x86, 0xce]); + v + }); + + assert_eq!( + bin_ser!(compressed_bytes::le::serialize, U256::ZERO), + vec![] + ); + assert_eq!( + bin_ser!(compressed_bytes::le::serialize, U256::MAX), + vec![0xff; 32], + ); + assert_eq!( + bin_ser!(compressed_bytes::le::serialize, U256::new(0x4215)), + vec![0x15, 0x42], + ); + + assert_eq!(bin_ser!(compressed_bytes::le::serialize, I256::MIN), { + let mut v = vec![0; 32]; + v[31] = 0x80; + v + }); + assert_eq!( + bin_ser!(compressed_bytes::le::serialize, I256::ZERO), + vec![] + ); + assert_eq!( + bin_ser!(compressed_bytes::le::serialize, I256::new(-1)), + vec![0xff], + ); + assert_eq!( + bin_ser!(compressed_bytes::le::serialize, I256::new(-0x8000)), + vec![0x00, 0x80], + ); + assert_eq!( + bin_ser!(compressed_bytes::le::serialize, I256::new(-424242)), + vec![0xce, 0x86, 0xf9], + ); + + assert_eq!( + bin_ser!(compressed_bytes::be::serialize, U256::ZERO), + vec![] + ); + assert_eq!( + bin_ser!(compressed_bytes::be::serialize, U256::MAX), + vec![0xff; 32], + ); + assert_eq!( + bin_ser!(compressed_bytes::be::serialize, U256::new(0x4215)), + vec![0x42, 0x15], + ); + + assert_eq!(bin_ser!(compressed_bytes::be::serialize, I256::MIN), { + let mut v = vec![0; 32]; + v[0] = 0x80; + v + }); + assert_eq!( + bin_ser!(compressed_bytes::be::serialize, I256::ZERO), + vec![] + ); + assert_eq!( + bin_ser!(compressed_bytes::be::serialize, I256::new(-1)), + vec![0xff], + ); + assert_eq!( + bin_ser!(compressed_bytes::be::serialize, I256::new(-0x8000)), + vec![0x80, 0x00], + ); + assert_eq!( + bin_ser!(compressed_bytes::be::serialize, I256::new(-424242)), + vec![0xf9, 0x86, 0xce], + ); + } + + #[test] + fn deserialize_integers() { + macro_rules! de { + ($method:expr, $src:expr) => {{ + let deserializer = IntoDeserializer::::into_deserializer($src); + ($method)(deserializer).unwrap() + }}; + (err; $method:expr, $src:expr) => {{ + let deserializer = IntoDeserializer::::into_deserializer($src); + ($method)(deserializer).is_err() + }}; + } + + macro_rules! assert_de_bytes { + ($method:expr, $src:expr; eq: $exp:expr) => {{ + let src = $src; + let exp = $exp; + + assert_eq!(de!($method, src.as_slice()), exp); + + let seq = + value::SeqDeserializer::<_, value::Error>::new(src.into_iter()); + assert_eq!(($method)(seq).unwrap(), exp); + }}; + ($method:expr, $src:expr; err) => {{ + let src = $src; + assert!(de!(err; $method, src.as_slice())); + let seq = + value::SeqDeserializer::<_, value::Error>::new(src.into_iter()); + assert!(($method)(seq).is_err()); + }}; + } + + assert_eq!( + de!( + I256::deserialize, + "-0x8000000000000000000000000000000000000000000000000000000000000000" + ), + I256::MIN + ); + assert_eq!(de!(I256::deserialize, "-0x1337"), I256::new(-4919)); + assert_eq!(de!(I256::deserialize, "0x0"), I256::new(0)); + assert_eq!(de!(I256::deserialize, "0x2a"), I256::new(42)); + assert_eq!(de!(I256::deserialize, "0x2A"), I256::new(42)); + + assert_eq!(de!(U256::deserialize, "0x0"), U256::new(0)); + assert_eq!(de!(U256::deserialize, "0x2a"), U256::new(42)); + assert_eq!(de!(U256::deserialize, "0x2A"), U256::new(42)); + assert_eq!( + de!( + U256::deserialize, + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ), + U256::MAX + ); + + assert_eq!( + de!( + decimal::deserialize::, + "-57896044618658097711785492504343953926634992332820282019728792003956564819968" + ), + I256::MIN + ); + assert_eq!(de!(decimal::deserialize::, "-1"), I256::new(-1)); + assert_eq!(de!(decimal::deserialize::, "0"), I256::new(0)); + assert_eq!(de!(decimal::deserialize::, "42"), I256::new(42)); + + assert_eq!(de!(decimal::deserialize::, "0"), U256::new(0)); + assert_eq!(de!(decimal::deserialize::, "42"), U256::new(42)); + assert_eq!( + de!( + decimal::deserialize::, + "115792089237316195423570985008687907853269984665640564039457584007913129639935" + ), + U256::MAX + ); + + assert_eq!(de!(prefixed::deserialize::, "-1"), I256::new(-1)); + assert_eq!(de!(prefixed::deserialize::, "-0x1"), I256::new(-1)); + assert_eq!(de!(prefixed::deserialize::, "42"), I256::new(42)); + assert_eq!(de!(prefixed::deserialize::, "0x2a"), I256::new(42)); + assert_eq!(de!(prefixed::deserialize::, "0x2A"), I256::new(42)); + + assert_eq!(de!(prefixed::deserialize::, "42"), U256::new(42)); + assert_eq!(de!(prefixed::deserialize::, "0x2a"), U256::new(42)); + assert_eq!(de!(prefixed::deserialize::, "0x2A"), U256::new(42)); + + assert_eq!( + de!(permissive::deserialize::, -42_i64), + I256::new(-42) + ); + assert_eq!( + de!(permissive::deserialize::, 42_u64), + I256::new(42) + ); + assert_eq!( + de!(permissive::deserialize::, -1337_i128), + I256::new(-1337) + ); + assert_eq!( + de!(permissive::deserialize::, 1337_u128), + I256::new(1337) + ); + assert_eq!( + de!(permissive::deserialize::, 100.0_f32), + I256::new(100) + ); + assert_eq!( + de!(permissive::deserialize::, -100.0_f64), + I256::new(-100) + ); + assert_eq!(de!(permissive::deserialize::, "-1"), I256::new(-1)); + assert_eq!( + de!(permissive::deserialize::, "1000"), + I256::new(1000) + ); + assert_eq!( + de!(permissive::deserialize::, "0x42"), + I256::new(0x42) + ); + assert_eq!( + de!(permissive::deserialize::, "-0x2a"), + I256::new(-42) + ); + assert_eq!( + de!( + permissive::deserialize::, + "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ), + I256::MAX + ); + assert_eq!( + de!( + permissive::deserialize::, + "-0x8000000000000000000000000000000000000000000000000000000000000000" + ), + I256::MIN + ); + + assert_eq!( + de!(permissive::deserialize::, 42_u64), + U256::new(42) + ); + assert_eq!( + de!(permissive::deserialize::, 1337_u128), + U256::new(1337) + ); + assert_eq!( + de!(permissive::deserialize::, 100.0_f32), + U256::new(100) + ); + assert_eq!( + de!(permissive::deserialize::, 100.0_f64), + U256::new(100) + ); + assert_eq!( + de!(permissive::deserialize::, "1000"), + U256::new(1000) + ); + assert_eq!( + de!(permissive::deserialize::, "0x42"), + U256::new(0x42) + ); + assert_eq!( + de!( + permissive::deserialize::, + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ), + U256::MAX + ); + + assert!(de!(err; permissive::deserialize::, 4.2_f32)); + assert!(de!(err; permissive::deserialize::, 16777216.0_f32)); + assert!(de!(err; permissive::deserialize::, -13.37_f64)); + assert!(de!(err; permissive::deserialize::, 9007199254740992.0_f32)); + assert!( + de!(err; permissive::deserialize::, "0x8000000000000000000000000000000000000000000000000000000000000000") + ); + assert!( + de!(err; permissive::deserialize::, "-0x8000000000000000000000000000000000000000000000000000000000000001" + ) + ); + + assert!(de!(err; permissive::deserialize::, 4.2_f32)); + assert!(de!(err; permissive::deserialize::, 16777216.0_f32)); + assert!(de!(err; permissive::deserialize::, 13.37_f64)); + assert!(de!(err; permissive::deserialize::, 9007199254740992.0_f32)); + assert!( + de!(err; permissive::deserialize::, "0x10000000000000000000000000000000000000000000000000000000000000000") + ); + + assert_de_bytes!( + bytes::le::deserialize::, [0x00; 32]; + eq: U256::ZERO + ); + assert_de_bytes!( + bytes::le::deserialize::, [0xff; 32]; + eq: U256::MAX + ); + + assert_de_bytes!( + bytes::le::deserialize::, [0x00; 32]; + eq: I256::ZERO + ); + assert_de_bytes!( + bytes::le::deserialize::, [0xff; 32]; + eq: I256::new(-1) + ); + + let forty_two = { + let mut v = [0x00; 32]; + v[0] = 0x2a; + v + }; + assert_de_bytes!( + bytes::le::deserialize::, forty_two; + eq: U256::new(42) + ); + assert_de_bytes!( + bytes::le::deserialize::, forty_two; + eq: I256::new(42) + ); + + assert_de_bytes!( + bytes::le::deserialize::, [0xff; 31]; + err + ); + assert_de_bytes!( + bytes::le::deserialize::, [0xff; 33]; + err + ); + assert_de_bytes!( + bytes::le::deserialize::, [0xff; 31]; + err + ); + assert_de_bytes!( + bytes::le::deserialize::, [0xff; 33]; + err + ); + + assert_de_bytes!( + bytes::be::deserialize::, [0x00; 32]; + eq: U256::ZERO + ); + assert_de_bytes!( + bytes::be::deserialize::, [0xff; 32]; + eq: U256::MAX + ); + + assert_de_bytes!( + bytes::be::deserialize::, [0x00; 32]; + eq: I256::ZERO + ); + assert_de_bytes!( + bytes::be::deserialize::, [0xff; 32]; + eq: I256::new(-1) + ); + + let forty_two = { + let mut v = [0x00; 32]; + v[31] = 0x2a; + v + }; + assert_de_bytes!( + bytes::be::deserialize::, forty_two; + eq: U256::new(42) + ); + assert_de_bytes!( + bytes::be::deserialize::, forty_two; + eq: I256::new(42) + ); + + assert_de_bytes!( + bytes::be::deserialize::, [0xff; 31]; + err + ); + assert_de_bytes!( + bytes::be::deserialize::, [0xff; 33]; + err + ); + assert_de_bytes!( + bytes::be::deserialize::, [0xff; 31]; + err + ); + assert_de_bytes!( + bytes::be::deserialize::, [0xff; 33]; + err + ); + + assert_de_bytes!( + compressed_bytes::le::deserialize::, []; + eq: U256::ZERO + ); + assert_de_bytes!( + compressed_bytes::le::deserialize::, [0xff; 32]; + eq: U256::MAX + ); + + assert_de_bytes!( + compressed_bytes::le::deserialize::, [0x2a]; + eq: U256::new(42) + ); + assert_de_bytes!( + compressed_bytes::le::deserialize::, [0xee, 0xff]; + eq: U256::new(0xffee) + ); + assert_de_bytes!( + compressed_bytes::le::deserialize::, []; + eq: I256::ZERO + ); + assert_de_bytes!( + compressed_bytes::le::deserialize::, [0xff]; + eq: I256::new(-1) + ); + + assert_de_bytes!( + compressed_bytes::le::deserialize::, [0xff; 33]; + err + ); + assert_de_bytes!( + compressed_bytes::le::deserialize::, [0xff; 33]; + err + ); + + assert_de_bytes!( + compressed_bytes::be::deserialize::, []; + eq: U256::ZERO + ); + assert_de_bytes!( + compressed_bytes::be::deserialize::, [0xff; 32]; + eq: U256::MAX + ); + + assert_de_bytes!( + compressed_bytes::be::deserialize::, [0x2a]; + eq: U256::new(42) + ); + assert_de_bytes!( + compressed_bytes::be::deserialize::, [0xff, 0xee]; + eq: U256::new(0xffee) + ); + assert_de_bytes!( + compressed_bytes::be::deserialize::, []; + eq: I256::ZERO + ); + assert_de_bytes!( + compressed_bytes::be::deserialize::, [0xfe]; + eq: I256::new(-2) + ); + + assert_de_bytes!( + compressed_bytes::be::deserialize::, [0xff; 33]; + err + ); + assert_de_bytes!( + compressed_bytes::be::deserialize::, [0xff; 33]; + err + ); + } + + #[test] + fn formatting_buffer() { + for value in [ + Box::new(I256::MIN) as Box, + Box::new(I256::MAX), + Box::new(U256::MIN), + Box::new(U256::MAX), + ] { + let mut f = FormatBuffer::decimal(); + write!(f, "{value}").unwrap(); + assert_eq!(f.as_str(), format!("{value}")); + } + + for value in [ + Box::new(I256::MIN) as Box, + Box::new(I256::MAX), + Box::new(U256::MIN), + Box::new(U256::MAX), + ] { + let mut f = FormatBuffer::hex(); + let value = &*value; + write!(f, "{value:-#x}").unwrap(); + assert_eq!(f.as_str(), format!("{value:-#x}")); + } + } + + /// A string serializer used for testing. + struct StringSerializer; + + impl Serializer for StringSerializer { + type Ok = String; + type Error = fmt::Error; + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + fn serialize_bool(self, _: bool) -> Result { + unimplemented!() + } + fn serialize_i8(self, _: i8) -> Result { + unimplemented!() + } + fn serialize_i16(self, _: i16) -> Result { + unimplemented!() + } + fn serialize_i32(self, _: i32) -> Result { + unimplemented!() + } + fn serialize_i64(self, _: i64) -> Result { + unimplemented!() + } + fn serialize_u8(self, _: u8) -> Result { + unimplemented!() + } + fn serialize_u16(self, _: u16) -> Result { + unimplemented!() + } + fn serialize_u32(self, _: u32) -> Result { + unimplemented!() + } + fn serialize_u64(self, _: u64) -> Result { + unimplemented!() + } + fn serialize_f32(self, _: f32) -> Result { + unimplemented!() + } + fn serialize_f64(self, _: f64) -> Result { + unimplemented!() + } + fn serialize_char(self, _: char) -> Result { + unimplemented!() + } + fn serialize_str(self, v: &str) -> Result { + Ok(v.into()) + } + fn serialize_bytes(self, _: &[u8]) -> Result { + unimplemented!() + } + fn serialize_none(self) -> Result { + unimplemented!() + } + fn serialize_some(self, _: &T) -> Result + where + T: Serialize, + { + unimplemented!() + } + fn serialize_unit(self) -> Result { + unimplemented!() + } + fn serialize_unit_struct(self, _: &'static str) -> Result { + unimplemented!() + } + fn serialize_unit_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + ) -> Result { + unimplemented!() + } + fn serialize_newtype_struct( + self, + _: &'static str, + _: &T, + ) -> Result + where + T: Serialize, + { + unimplemented!() + } + fn serialize_newtype_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: &T, + ) -> Result + where + T: Serialize, + { + unimplemented!() + } + fn serialize_seq(self, _: Option) -> Result { + unimplemented!() + } + fn serialize_tuple(self, _: usize) -> Result { + unimplemented!() + } + fn serialize_tuple_struct( + self, + _: &'static str, + _: usize, + ) -> Result { + unimplemented!() + } + fn serialize_tuple_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + unimplemented!() + } + fn serialize_map(self, _: Option) -> Result { + unimplemented!() + } + fn serialize_struct( + self, + _: &'static str, + _: usize, + ) -> Result { + unimplemented!() + } + fn serialize_struct_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + unimplemented!() + } + fn collect_str(self, _: &T) -> Result + where + T: Display + ?Sized, + { + unimplemented!() + } + } + + /// A string serializer used for testing. + struct BytesSerializer; + + impl Serializer for BytesSerializer { + type Ok = Vec; + type Error = fmt::Error; + type SerializeSeq = Impossible, fmt::Error>; + type SerializeTuple = Impossible, fmt::Error>; + type SerializeTupleStruct = Impossible, fmt::Error>; + type SerializeTupleVariant = Impossible, fmt::Error>; + type SerializeMap = Impossible, fmt::Error>; + type SerializeStruct = Impossible, fmt::Error>; + type SerializeStructVariant = Impossible, fmt::Error>; + fn serialize_bool(self, _: bool) -> Result { + unimplemented!() + } + fn serialize_i8(self, _: i8) -> Result { + unimplemented!() + } + fn serialize_i16(self, _: i16) -> Result { + unimplemented!() + } + fn serialize_i32(self, _: i32) -> Result { + unimplemented!() + } + fn serialize_i64(self, _: i64) -> Result { + unimplemented!() + } + fn serialize_u8(self, _: u8) -> Result { + unimplemented!() + } + fn serialize_u16(self, _: u16) -> Result { + unimplemented!() + } + fn serialize_u32(self, _: u32) -> Result { + unimplemented!() + } + fn serialize_u64(self, _: u64) -> Result { + unimplemented!() + } + fn serialize_f32(self, _: f32) -> Result { + unimplemented!() + } + fn serialize_f64(self, _: f64) -> Result { + unimplemented!() + } + fn serialize_char(self, _: char) -> Result { + unimplemented!() + } + fn serialize_str(self, _: &str) -> Result { + unimplemented!() + } + fn serialize_bytes(self, v: &[u8]) -> Result { + Ok(v.to_vec()) + } + fn serialize_none(self) -> Result { + unimplemented!() + } + fn serialize_some(self, _: &T) -> Result + where + T: Serialize, + { + unimplemented!() + } + fn serialize_unit(self) -> Result { + unimplemented!() + } + fn serialize_unit_struct(self, _: &'static str) -> Result { + unimplemented!() + } + fn serialize_unit_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + ) -> Result { + unimplemented!() + } + fn serialize_newtype_struct( + self, + _: &'static str, + _: &T, + ) -> Result + where + T: Serialize, + { + unimplemented!() + } + fn serialize_newtype_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: &T, + ) -> Result + where + T: Serialize, + { + unimplemented!() + } + fn serialize_seq(self, _: Option) -> Result { + unimplemented!() + } + fn serialize_tuple(self, _: usize) -> Result { + unimplemented!() + } + fn serialize_tuple_struct( + self, + _: &'static str, + _: usize, + ) -> Result { + unimplemented!() + } + fn serialize_tuple_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + unimplemented!() + } + fn serialize_map(self, _: Option) -> Result { + unimplemented!() + } + fn serialize_struct( + self, + _: &'static str, + _: usize, + ) -> Result { + unimplemented!() + } + fn serialize_struct_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + unimplemented!() + } + fn collect_str(self, _: &T) -> Result + where + T: Display + ?Sized, + { + unimplemented!() + } + } +} diff --git a/vendor/ethnum/src/uint.rs b/vendor/ethnum/src/uint.rs new file mode 100644 index 0000000..d446b67 --- /dev/null +++ b/vendor/ethnum/src/uint.rs @@ -0,0 +1,289 @@ +//! Root module for 256-bit unsigned integer type. + +mod api; +mod cmp; +mod convert; +mod fmt; +mod iter; +mod ops; +mod parse; + +pub use self::convert::AsU256; +use crate::I256; +use core::num::ParseIntError; + +/// A 256-bit unsigned integer type. +#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)] +#[repr(transparent)] +pub struct U256(pub [u128; 2]); + +impl U256 { + /// The additive identity for this integer type, i.e. `0`. + pub const ZERO: Self = U256([0; 2]); + + /// The multiplicative identity for this integer type, i.e. `1`. + pub const ONE: Self = U256::new(1); + + /// Creates a new 256-bit integer value from a primitive `u128` integer. + #[inline] + pub const fn new(value: u128) -> Self { + U256::from_words(0, value) + } + + /// Creates a new 256-bit integer value from high and low words. + #[inline] + pub const fn from_words(hi: u128, lo: u128) -> Self { + #[cfg(target_endian = "little")] + { + U256([lo, hi]) + } + #[cfg(target_endian = "big")] + { + U256([hi, lo]) + } + } + + /// Splits a 256-bit integer into high and low words. + #[inline] + pub const fn into_words(self) -> (u128, u128) { + #[cfg(target_endian = "little")] + { + let U256([lo, hi]) = self; + (hi, lo) + } + #[cfg(target_endian = "big")] + { + let U256([hi, lo]) = self; + (hi, lo) + } + } + + /// Get the low 128-bit word for this unsigned integer. + #[inline] + pub fn low(&self) -> &u128 { + #[cfg(target_endian = "little")] + { + &self.0[0] + } + #[cfg(target_endian = "big")] + { + &self.0[1] + } + } + + /// Get the low 128-bit word for this unsigned integer as a mutable + /// reference. + #[inline] + pub fn low_mut(&mut self) -> &mut u128 { + #[cfg(target_endian = "little")] + { + &mut self.0[0] + } + #[cfg(target_endian = "big")] + { + &mut self.0[1] + } + } + + /// Get the high 128-bit word for this unsigned integer. + #[inline] + pub fn high(&self) -> &u128 { + #[cfg(target_endian = "little")] + { + &self.0[1] + } + #[cfg(target_endian = "big")] + { + &self.0[0] + } + } + + /// Get the high 128-bit word for this unsigned integer as a mutable + /// reference. + #[inline] + pub fn high_mut(&mut self) -> &mut u128 { + #[cfg(target_endian = "little")] + { + &mut self.0[1] + } + #[cfg(target_endian = "big")] + { + &mut self.0[0] + } + } + + /// Converts a prefixed string slice in base 16 to an integer. + /// + /// The string is expected to be an optional `+` sign followed by the `0x` + /// prefix and finally the digits. Leading and trailing whitespace represent + /// an error. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::from_str_hex("0x2A"), Ok(U256::new(42))); + /// ``` + pub fn from_str_hex(src: &str) -> Result { + crate::parse::from_str_radix(src, 16, Some("0x")) + } + + /// Converts a prefixed string slice in a base determined by the prefix to + /// an integer. + /// + /// The string is expected to be an optional `+` sign followed by the one of + /// the supported prefixes and finally the digits. Leading and trailing + /// whitespace represent an error. The base is determined based on the + /// prefix: + /// + /// * `0b`: base `2` + /// * `0o`: base `8` + /// * `0x`: base `16` + /// * no prefix: base `10` + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::from_str_prefixed("0b101"), Ok(U256::new(0b101))); + /// assert_eq!(U256::from_str_prefixed("0o17"), Ok(U256::new(0o17))); + /// assert_eq!(U256::from_str_prefixed("0xa"), Ok(U256::new(0xa))); + /// assert_eq!(U256::from_str_prefixed("42"), Ok(U256::new(42))); + /// ``` + pub fn from_str_prefixed(src: &str) -> Result { + crate::parse::from_str_prefixed(src) + } + + /// Same as [`U256::from_str_prefixed`] but as a `const fn`. This method is + /// not intended to be used directly but rather through the [`crate::uint`] + /// macro. + #[doc(hidden)] + pub const fn const_from_str_prefixed(src: &str) -> Self { + parse::const_from_str_prefixed(src) + } + + /// Cast to a primitive `i8`. + #[inline] + pub const fn as_i8(self) -> i8 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `i16`. + #[inline] + pub const fn as_i16(self) -> i16 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `i32`. + #[inline] + pub const fn as_i32(self) -> i32 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `i64`. + #[inline] + pub const fn as_i64(self) -> i64 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `i128`. + #[inline] + pub const fn as_i128(self) -> i128 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a `I256`. + #[inline] + pub const fn as_i256(self) -> I256 { + let Self([a, b]) = self; + I256([a as _, b as _]) + } + + /// Cast to a primitive `u8`. + #[inline] + pub const fn as_u8(self) -> u8 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `u16`. + #[inline] + pub const fn as_u16(self) -> u16 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `u32`. + #[inline] + pub const fn as_u32(self) -> u32 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `u64`. + #[inline] + pub const fn as_u64(self) -> u64 { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `u128`. + #[inline] + pub const fn as_u128(self) -> u128 { + let (_, lo) = self.into_words(); + lo + } + + /// Cast to a primitive `isize`. + #[inline] + pub const fn as_isize(self) -> isize { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `usize`. + #[inline] + pub const fn as_usize(self) -> usize { + let (_, lo) = self.into_words(); + lo as _ + } + + /// Cast to a primitive `f32`. + #[inline] + pub fn as_f32(self) -> f32 { + match self.into_words() { + (0, lo) => lo as _, + _ => f32::INFINITY, + } + } + + /// Cast to a primitive `f64`. + #[inline] + pub fn as_f64(self) -> f64 { + // NOTE: Binary representation of 2**128. This is used because `powi` is + // neither `const` nor `no_std`. + const HI: u64 = 0x47f0000000000000; + let (hi, lo) = self.into_words(); + (hi as f64) * f64::from_bits(HI) + (lo as f64) + } +} + +#[cfg(test)] +mod tests { + use crate::uint::U256; + + #[test] + #[allow(clippy::float_cmp)] + fn converts_to_f64() { + assert_eq!(U256::from_words(1, 0).as_f64(), 2.0f64.powi(128)) + } +} diff --git a/vendor/ethnum/src/uint/api.rs b/vendor/ethnum/src/uint/api.rs new file mode 100644 index 0000000..31df63f --- /dev/null +++ b/vendor/ethnum/src/uint/api.rs @@ -0,0 +1,1866 @@ +//! Module containing integer aritimetic methods closely following the Rust +//! standard library API for `uN` types. + +use super::U256; +use crate::{intrinsics, I256}; +use core::{ + mem::{self, MaybeUninit}, + num::ParseIntError, +}; + +impl U256 { + /// The smallest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::MIN, U256::new(0)); + /// ``` + pub const MIN: Self = Self([0; 2]); + + /// The largest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!( + /// U256::MAX.to_string(), + /// "115792089237316195423570985008687907853269984665640564039457584007913129639935", + /// ); + /// ``` + pub const MAX: Self = Self([!0; 2]); + + /// The size of this integer type in bits. + /// + /// # Examples + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::BITS, 256); + /// ``` + pub const BITS: u32 = 256; + + /// Converts a string slice in a given base to an integer. + /// + /// The string is expected to be an optional `+` sign followed by digits. + /// Leading and trailing whitespace represent an error. Digits are a subset + /// of these characters, depending on `radix`: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::from_str_radix("A", 16), Ok(U256::new(10))); + /// ``` + #[inline] + pub fn from_str_radix(src: &str, radix: u32) -> Result { + crate::parse::from_str_radix(src, radix, None) + } + + /// Returns the number of ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = U256::new(0b01001100); + /// assert_eq!(n.count_ones(), 3); + /// ``` + #[inline] + pub const fn count_ones(self) -> u32 { + let Self([a, b]) = self; + a.count_ones() + b.count_ones() + } + + /// Returns the number of zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::MIN.count_zeros(), 256); + /// assert_eq!(U256::MAX.count_zeros(), 0); + /// ``` + #[inline] + pub const fn count_zeros(self) -> u32 { + let Self([a, b]) = self; + a.count_zeros() + b.count_zeros() + } + + /// Returns the number of leading zeros in the binary representation of + /// `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = U256::MAX >> 2u32; + /// assert_eq!(n.leading_zeros(), 2); + /// ``` + #[inline(always)] + pub fn leading_zeros(self) -> u32 { + intrinsics::signed::uctlz(&self) + } + + /// Returns the number of trailing zeros in the binary representation of + /// `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = U256::new(0b0101000); + /// assert_eq!(n.trailing_zeros(), 3); + /// ``` + #[inline(always)] + pub fn trailing_zeros(self) -> u32 { + intrinsics::signed::ucttz(&self) + } + + /// Returns the number of leading ones in the binary representation of + /// `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = !(U256::MAX >> 2u32); + /// assert_eq!(n.leading_ones(), 2); + /// ``` + #[inline] + pub fn leading_ones(self) -> u32 { + (!self).leading_zeros() + } + + /// Returns the number of trailing ones in the binary representation of + /// `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = U256::new(0b1010111); + /// assert_eq!(n.trailing_ones(), 3); + /// ``` + #[inline] + pub fn trailing_ones(self) -> u32 { + (!self).trailing_zeros() + } + + /// Shifts the bits to the left by a specified amount, `n`, wrapping the + /// truncated bits to the end of the resulting integer. + /// + /// Please note this isn't the same operation as the `<<` shifting + /// operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = U256::from_words( + /// 0x13f40000000000000000000000000000, + /// 0x00000000000000000000000000004f76, + /// ); + /// let m = U256::new(0x4f7613f4); + /// assert_eq!(n.rotate_left(16), m); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn rotate_left(self, n: u32) -> Self { + let mut r = MaybeUninit::uninit(); + intrinsics::signed::urol3(&mut r, &self, n); + unsafe { r.assume_init() } + } + + /// Shifts the bits to the right by a specified amount, `n`, wrapping the + /// truncated bits to the beginning of the resulting integer. + /// + /// Please note this isn't the same operation as the `>>` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = U256::new(0x4f7613f4); + /// let m = U256::from_words( + /// 0x13f40000000000000000000000000000, + /// 0x00000000000000000000000000004f76, + /// ); + /// + /// assert_eq!(n.rotate_right(16), m); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn rotate_right(self, n: u32) -> Self { + let mut r = MaybeUninit::uninit(); + intrinsics::signed::uror3(&mut r, &self, n); + unsafe { r.assume_init() } + } + + /// Reverses the byte order of the integer. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = U256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ); + /// assert_eq!( + /// n.swap_bytes(), + /// U256::from_words( + /// 0x1f1e1d1c_1b1a1918_17161514_13121110, + /// 0x0f0e0d0c_0b0a0908_07060504_03020100, + /// ), + /// ); + /// ``` + #[inline] + pub const fn swap_bytes(self) -> Self { + let Self([a, b]) = self; + Self([b.swap_bytes(), a.swap_bytes()]) + } + + /// Reverses the bit pattern of the integer. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = U256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ); + /// assert_eq!( + /// n.reverse_bits(), + /// U256::from_words( + /// 0xf878b838_d8589818_e868a828_c8488808, + /// 0xf070b030_d0509010_e060a020_c0408000, + /// ), + /// ); + /// ``` + #[inline] + pub const fn reverse_bits(self) -> Self { + let Self([a, b]) = self; + Self([b.reverse_bits(), a.reverse_bits()]) + } + + /// Converts an integer from big endian to the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = U256::new(0x1A); + /// if cfg!(target_endian = "big") { + /// assert_eq!(U256::from_be(n), n); + /// } else { + /// assert_eq!(U256::from_be(n), n.swap_bytes()); + /// } + /// ``` + #[inline(always)] + #[allow(clippy::wrong_self_convention)] + pub const fn from_be(x: Self) -> Self { + #[cfg(target_endian = "big")] + { + x + } + #[cfg(not(target_endian = "big"))] + { + x.swap_bytes() + } + } + + /// Converts an integer from little endian to the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = U256::new(0x1A); + /// if cfg!(target_endian = "little") { + /// assert_eq!(U256::from_le(n), n) + /// } else { + /// assert_eq!(U256::from_le(n), n.swap_bytes()) + /// } + /// ``` + #[inline(always)] + #[allow(clippy::wrong_self_convention)] + pub const fn from_le(x: Self) -> Self { + #[cfg(target_endian = "little")] + { + x + } + #[cfg(not(target_endian = "little"))] + { + x.swap_bytes() + } + } + + /// Converts `self` to big endian from the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = U256::new(0x1A); + /// if cfg!(target_endian = "big") { + /// assert_eq!(n.to_be(), n) + /// } else { + /// assert_eq!(n.to_be(), n.swap_bytes()) + /// } + /// ``` + #[inline(always)] + pub const fn to_be(self) -> Self { + #[cfg(target_endian = "big")] + { + self + } + #[cfg(not(target_endian = "big"))] + { + self.swap_bytes() + } + } + + /// Converts `self` to little endian from the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// let n = U256::new(0x1A); + /// if cfg!(target_endian = "little") { + /// assert_eq!(n.to_le(), n) + /// } else { + /// assert_eq!(n.to_le(), n.swap_bytes()) + /// } + /// ``` + #[inline(always)] + pub const fn to_le(self) -> Self { + #[cfg(target_endian = "little")] + { + self + } + #[cfg(not(target_endian = "little"))] + { + self.swap_bytes() + } + } + + /// Checked integer addition. Computes `self + rhs`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!((U256::MAX - 2).checked_add(U256::new(1)), Some(U256::MAX - 1)); + /// assert_eq!((U256::MAX - 2).checked_add(U256::new(3)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_add(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_add(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked addition with a signed integer. Computes `self + rhs`, + /// returning `None` if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(U256::new(1).checked_add_signed(I256::new(2)), Some(U256::new(3))); + /// assert_eq!(U256::new(1).checked_add_signed(I256::new(-2)), None); + /// assert_eq!((U256::MAX - 2).checked_add_signed(I256::new(3)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_add_signed(self, rhs: I256) -> Option { + let (a, b) = self.overflowing_add_signed(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked integer subtraction. Computes `self - rhs`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(1).checked_sub(U256::new(1)), Some(U256::ZERO)); + /// assert_eq!(U256::new(0).checked_sub(U256::new(1)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_sub(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_sub(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked integer multiplication. Computes `self * rhs`, returning `None` + /// if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(5).checked_mul(U256::new(1)), Some(U256::new(5))); + /// assert_eq!(U256::MAX.checked_mul(U256::new(2)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_mul(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_mul(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked integer division. Computes `self / rhs`, returning `None` if + /// `rhs == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(128).checked_div(U256::new(2)), Some(U256::new(64))); + /// assert_eq!(U256::new(1).checked_div(U256::new(0)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_div(self, rhs: Self) -> Option { + if rhs == U256::ZERO { + None + } else { + Some(self / rhs) + } + } + + /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, returning + /// `None` if `rhs == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(128).checked_div_euclid(U256::new(2)), Some(U256::new(64))); + /// assert_eq!(U256::new(1).checked_div_euclid(U256::new(0)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_div_euclid(self, rhs: Self) -> Option { + if rhs == U256::ZERO { + None + } else { + Some(self.div_euclid(rhs)) + } + } + + /// Checked integer remainder. Computes `self % rhs`, returning `None` if + /// `rhs == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(5).checked_rem(U256::new(2)), Some(U256::new(1))); + /// assert_eq!(U256::new(5).checked_rem(U256::new(0)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_rem(self, rhs: Self) -> Option { + if rhs == U256::ZERO { + None + } else { + Some(self % rhs) + } + } + + /// Checked Euclidean modulo. Computes `self.rem_euclid(rhs)`, returning + /// `None` if `rhs == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(5).checked_rem_euclid(U256::new(2)), Some(U256::new(1))); + /// assert_eq!(U256::new(5).checked_rem_euclid(U256::new(0)), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_rem_euclid(self, rhs: Self) -> Option { + if rhs == U256::ZERO { + None + } else { + Some(self.rem_euclid(rhs)) + } + } + + /// Checked negation. Computes `-self`, returning `None` unless `self == 0`. + /// + /// Note that negating any positive integer will overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::ZERO.checked_neg(), Some(U256::ZERO)); + /// assert_eq!(U256::new(1).checked_neg(), None); + /// ``` + #[inline] + pub fn checked_neg(self) -> Option { + let (a, b) = self.overflowing_neg(); + if b { + None + } else { + Some(a) + } + } + + /// Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is + /// larger than or equal to the number of bits in `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(0x1).checked_shl(4), Some(U256::new(0x10))); + /// assert_eq!(U256::new(0x10).checked_shl(257), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_shl(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shl(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` + /// is larger than or equal to the number of bits in `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(0x10).checked_shr(4), Some(U256::new(0x1))); + /// assert_eq!(U256::new(0x10).checked_shr(257), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_shr(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shr(rhs); + if b { + None + } else { + Some(a) + } + } + + /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(2).checked_pow(5), Some(U256::new(32))); + /// assert_eq!(U256::MAX.checked_pow(2), None); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn checked_pow(self, mut exp: u32) -> Option { + let mut base = self; + let mut acc = U256::ONE; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.checked_mul(base)?; + } + exp /= 2; + base = base.checked_mul(base)?; + } + + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + if exp == 1 { + acc = acc.checked_mul(base)?; + } + + Some(acc) + } + + /// Saturating integer addition. Computes `self + rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(100).saturating_add(U256::new(1)), U256::new(101)); + /// assert_eq!(U256::MAX.saturating_add(U256::new(127)), U256::MAX); + /// ``` + + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn saturating_add(self, rhs: Self) -> Self { + self.checked_add(rhs).unwrap_or(U256::MAX) + } + + /// Saturating addition with a signed integer. Computes `self + rhs`, + /// saturating at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(U256::new(1).saturating_add_signed(I256::new(2)), U256::new(3)); + /// assert_eq!(U256::new(1).saturating_add_signed(I256::new(-2)), U256::new(0)); + /// assert_eq!((U256::MAX - 2).saturating_add_signed(I256::new(4)), U256::MAX); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn saturating_add_signed(self, rhs: I256) -> Self { + let (res, overflow) = self.overflowing_add(rhs.as_u256()); + if overflow == (rhs < 0) { + res + } else if overflow { + Self::MAX + } else { + Self::ZERO + } + } + + /// Saturating integer subtraction. Computes `self - rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(100).saturating_sub(U256::new(27)), U256::new(73)); + /// assert_eq!(U256::new(13).saturating_sub(U256::new(127)), U256::new(0)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn saturating_sub(self, rhs: Self) -> Self { + self.checked_sub(rhs).unwrap_or(U256::MIN) + } + + /// Saturating integer multiplication. Computes `self * rhs`, saturating at + /// the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(2).saturating_mul(U256::new(10)), U256::new(20)); + /// assert_eq!((U256::MAX).saturating_mul(U256::new(10)), U256::MAX); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn saturating_mul(self, rhs: Self) -> Self { + match self.checked_mul(rhs) { + Some(x) => x, + None => Self::MAX, + } + } + + /// Saturating integer division. Computes `self / rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(5).saturating_div(U256::new(2)), U256::new(2)); + /// ``` + /// + /// ```should_panic + /// # use ethnum::U256; + /// let _ = U256::new(1).saturating_div(U256::ZERO); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn saturating_div(self, rhs: Self) -> Self { + // on unsigned types, there is no overflow in integer division + self.wrapping_div(rhs) + } + + /// Saturating integer exponentiation. Computes `self.pow(exp)`, saturating + /// at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(4).saturating_pow(3), U256::new(64)); + /// assert_eq!(U256::MAX.saturating_pow(2), U256::MAX); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Some(x) => x, + None => Self::MAX, + } + } + + /// Wrapping (modular) addition. Computes `self + rhs`, wrapping around at + /// the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(200).wrapping_add(U256::new(55)), U256::new(255)); + /// assert_eq!(U256::new(200).wrapping_add(U256::MAX), U256::new(199)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_add(self, rhs: Self) -> Self { + let mut result = MaybeUninit::uninit(); + intrinsics::signed::uadd3(&mut result, &self, &rhs); + unsafe { result.assume_init() } + } + + /// Wrapping (modular) addition with a signed integer. Computes + /// `self + rhs`, wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(U256::new(1).wrapping_add_signed(I256::new(2)), U256::new(3)); + /// assert_eq!(U256::new(1).wrapping_add_signed(I256::new(-2)), U256::MAX); + /// assert_eq!((U256::MAX - 2).wrapping_add_signed(I256::new(4)), U256::new(1)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn wrapping_add_signed(self, rhs: I256) -> Self { + self.wrapping_add(rhs.as_u256()) + } + + /// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around + /// at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(100).wrapping_sub(U256::new(100)), U256::new(0)); + /// assert_eq!(U256::new(100).wrapping_sub(U256::MAX), U256::new(101)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_sub(self, rhs: Self) -> Self { + let mut result = MaybeUninit::uninit(); + intrinsics::signed::usub3(&mut result, &self, &rhs); + unsafe { result.assume_init() } + } + + /// Wrapping (modular) multiplication. Computes `self * rhs`, wrapping + /// around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `u8` is used here. + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(10).wrapping_mul(U256::new(12)), U256::new(120)); + /// assert_eq!(U256::MAX.wrapping_mul(U256::new(2)), U256::MAX - 1); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_mul(self, rhs: Self) -> Self { + let mut result = MaybeUninit::uninit(); + intrinsics::signed::umul3(&mut result, &self, &rhs); + unsafe { result.assume_init() } + } + + /// Wrapping (modular) division. Computes `self / rhs`. Wrapped division on + /// unsigned types is just normal division. There's no way wrapping could + /// ever happen. This function exists, so that all operations are accounted + /// for in the wrapping operations. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(100).wrapping_div(U256::new(10)), U256::new(10)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_div(self, rhs: Self) -> Self { + self / rhs + } + + /// Wrapping Euclidean division. Computes `self.div_euclid(rhs)`. Wrapped + /// division on unsigned types is just normal division. There's no way + /// wrapping could ever happen. This function exists, so that all operations + /// are accounted for in the wrapping operations. Since, for the positive + /// integers, all common definitions of division are equal, this is exactly + /// equal to `self.wrapping_div(rhs)`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(100).wrapping_div_euclid(U256::new(10)), U256::new(10)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_div_euclid(self, rhs: Self) -> Self { + self / rhs + } + + /// Wrapping (modular) remainder. Computes `self % rhs`. Wrapped remainder + /// calculation on unsigned types is just the regular remainder calculation. + /// There's no way wrapping could ever happen. This function exists, so that + /// all operations are accounted for in the wrapping operations. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(100).wrapping_rem(U256::new(10)), U256::new(0)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_rem(self, rhs: Self) -> Self { + self % rhs + } + + /// Wrapping Euclidean modulo. Computes `self.rem_euclid(rhs)`. Wrapped + /// modulo calculation on unsigned types is just the regular remainder + /// calculation. There's no way wrapping could ever happen. This function + /// exists, so that all operations are accounted for in the wrapping + /// operations. Since, for the positive integers, all common definitions of + /// division are equal, this is exactly equal to `self.wrapping_rem(rhs)`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(100).wrapping_rem_euclid(U256::new(10)), U256::new(0)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_rem_euclid(self, rhs: Self) -> Self { + self % rhs + } + + /// Wrapping (modular) negation. Computes `-self`, wrapping around at the + /// boundary of the type. + /// + /// Since unsigned types do not have negative equivalents all applications + /// of this function will wrap (except for `-0`). For values smaller than + /// the corresponding signed type's maximum the result is the same as + /// casting the corresponding signed value. Any larger values are equivalent + /// to `MAX + 1 - (val - MAX - 1)` where `MAX` is the corresponding signed + /// type's maximum. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `i8` is used here. + /// + /// ``` + /// # use ethnum::{U256, AsU256}; + /// assert_eq!(U256::new(100).wrapping_neg(), (-100i128).as_u256()); + /// assert_eq!( + /// U256::from_words(i128::MIN as _, 0).wrapping_neg(), + /// U256::from_words(i128::MIN as _, 0), + /// ); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn wrapping_neg(self) -> Self { + self.overflowing_neg().0 + } + + /// Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` + /// removes any high-order bits of `rhs` that would cause the shift to + /// exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-left; the RHS of a wrapping + /// shift-left is restricted to the range of the type, rather than the bits + /// shifted out of the LHS being returned to the other end. The primitive + /// integer types all implement a `rotate_left` function, which maybe what + /// you want instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(1).wrapping_shl(7), U256::new(128)); + /// assert_eq!(U256::new(1).wrapping_shl(128), U256::from_words(1, 0)); + /// assert_eq!(U256::new(1).wrapping_shl(256), U256::new(1)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_shl(self, rhs: u32) -> Self { + let mut result = MaybeUninit::uninit(); + intrinsics::signed::ushl3(&mut result, &self, rhs & 0xff); + unsafe { result.assume_init() } + } + + /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`, where `mask` + /// removes any high-order bits of `rhs` that would cause the shift to + /// exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-right; the RHS of a + /// wrapping shift-right is restricted to the range of the type, rather than + /// the bits shifted out of the LHS being returned to the other end. The + /// primitive integer types all implement a `rotate_right` function, which + /// may be what you want instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(128).wrapping_shr(7), U256::new(1)); + /// assert_eq!(U256::from_words(128, 0).wrapping_shr(128), U256::new(128)); + /// assert_eq!(U256::new(128).wrapping_shr(256), U256::new(128)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn wrapping_shr(self, rhs: u32) -> Self { + let mut result = MaybeUninit::uninit(); + intrinsics::signed::ushr3(&mut result, &self, rhs & 0xff); + unsafe { result.assume_init() } + } + + /// Wrapping (modular) exponentiation. Computes `self.pow(exp)`, wrapping + /// around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(3).wrapping_pow(5), U256::new(243)); + /// assert_eq!( + /// U256::new(1337).wrapping_pow(42), + /// U256::from_words( + /// 45367329835866155830012179193722278514, + /// 159264946433345088039815329994094210673, + /// ), + /// ); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn wrapping_pow(self, mut exp: u32) -> Self { + let mut base = self; + let mut acc = U256::ONE; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + } + exp /= 2; + base = base.wrapping_mul(base); + } + + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + if exp == 1 { + acc = acc.wrapping_mul(base); + } + + acc + } + + /// Calculates `self` + `rhs` + /// + /// Returns a tuple of the addition along with a boolean indicating whether + /// an arithmetic overflow would occur. If an overflow would have occurred + /// then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(5).overflowing_add(U256::new(2)), (U256::new(7), false)); + /// assert_eq!(U256::MAX.overflowing_add(U256::new(1)), (U256::new(0), true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn overflowing_add(self, rhs: Self) -> (Self, bool) { + let mut result = MaybeUninit::uninit(); + let overflow = intrinsics::signed::uaddc(&mut result, &self, &rhs); + (unsafe { result.assume_init() }, overflow) + } + + /// Calculates `self` + `rhs` with a signed `rhs` + /// + /// Returns a tuple of the addition along with a boolean indicating + /// whether an arithmetic overflow would occur. If an overflow would + /// have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::{I256, U256}; + /// assert_eq!(U256::new(1).overflowing_add_signed(I256::new(2)), (U256::new(3), false)); + /// assert_eq!(U256::new(1).overflowing_add_signed(I256::new(-2)), (U256::MAX, true)); + /// assert_eq!((U256::MAX - 2).overflowing_add_signed(I256::new(4)), (U256::new(1), true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn overflowing_add_signed(self, rhs: I256) -> (Self, bool) { + let (res, overflowed) = self.overflowing_add(rhs.as_u256()); + (res, overflowed ^ (rhs < 0)) + } + + /// Calculates `self` - `rhs` + /// + /// Returns a tuple of the subtraction along with a boolean indicating + /// whether an arithmetic overflow would occur. If an overflow would have + /// occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(5).overflowing_sub(U256::new(2)), (U256::new(3), false)); + /// assert_eq!(U256::new(0).overflowing_sub(U256::new(1)), (U256::MAX, true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn overflowing_sub(self, rhs: Self) -> (Self, bool) { + let mut result = MaybeUninit::uninit(); + let overflow = intrinsics::signed::usubc(&mut result, &self, &rhs); + (unsafe { result.assume_init() }, overflow) + } + + /// Computes the absolute difference between `self` and `other`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(100).abs_diff(U256::new(80)), 20); + /// assert_eq!(U256::new(100).abs_diff(U256::new(110)), 10); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn abs_diff(self, other: Self) -> Self { + if self < other { + other - self + } else { + self - other + } + } + + /// Calculates the multiplication of `self` and `rhs`. + /// + /// Returns a tuple of the multiplication along with a boolean indicating + /// whether an arithmetic overflow would occur. If an overflow would have + /// occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `u32` is used here. + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(5).overflowing_mul(U256::new(2)), (U256::new(10), false)); + /// assert_eq!( + /// U256::MAX.overflowing_mul(U256::new(2)), + /// (U256::MAX - 1, true), + /// ); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn overflowing_mul(self, rhs: Self) -> (Self, bool) { + let mut result = MaybeUninit::uninit(); + let overflow = intrinsics::signed::umulc(&mut result, &self, &rhs); + (unsafe { result.assume_init() }, overflow) + } + + /// Calculates the divisor when `self` is divided by `rhs`. + /// + /// Returns a tuple of the divisor along with a boolean indicating whether + /// an arithmetic overflow would occur. Note that for unsigned integers + /// overflow never occurs, so the second value is always `false`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(5).overflowing_div(U256::new(2)), (U256::new(2), false)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn overflowing_div(self, rhs: Self) -> (Self, bool) { + (self / rhs, false) + } + + /// Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. + /// + /// Returns a tuple of the divisor along with a boolean indicating whether + /// an arithmetic overflow would occur. Note that for unsigned integers + /// overflow never occurs, so the second value is always `false`. Since, + /// for the positive integers, all common definitions of division are equal, + /// this is exactly equal to `self.overflowing_div(rhs)`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(5).overflowing_div_euclid(U256::new(2)), (U256::new(2), false)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { + (self / rhs, false) + } + + /// Calculates the remainder when `self` is divided by `rhs`. + /// + /// Returns a tuple of the remainder after dividing along with a boolean + /// indicating whether an arithmetic overflow would occur. Note that for + /// unsigned integers overflow never occurs, so the second value is always + /// `false`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(5).overflowing_rem(U256::new(2)), (U256::new(1), false)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn overflowing_rem(self, rhs: Self) -> (Self, bool) { + (self % rhs, false) + } + + /// Calculates the remainder `self.rem_euclid(rhs)` as if by Euclidean + /// division. + /// + /// Returns a tuple of the modulo after dividing along with a boolean + /// indicating whether an arithmetic overflow would occur. Note that for + /// unsigned integers overflow never occurs, so the second value is always + /// `false`. Since, for the positive integers, all common definitions of + /// division are equal, this operation is exactly equal to + /// `self.overflowing_rem(rhs)`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(5).overflowing_rem_euclid(U256::new(2)), (U256::new(1), false)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { + (self % rhs, false) + } + + /// Negates self in an overflowing fashion. + /// + /// Returns `!self + 1` using wrapping operations to return the value that + /// represents the negation of this unsigned value. Note that for positive + /// unsigned values overflow always occurs, but negating 0 does not + /// overflow. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// # use ethnum::{U256, AsU256}; + /// assert_eq!(U256::new(0).overflowing_neg(), (U256::new(0), false)); + /// assert_eq!(U256::new(2).overflowing_neg(), ((-2i32).as_u256(), true)); + /// ``` + #[inline] + pub fn overflowing_neg(self) -> (Self, bool) { + ((!self).wrapping_add(U256::ONE), self != U256::ZERO) + } + + /// Shifts self left by `rhs` bits. + /// + /// Returns a tuple of the shifted version of self along with a boolean + /// indicating whether the shift value was larger than or equal to the + /// number of bits. If the shift value is too large, then value is masked + /// (N-1) where N is the number of bits, and this value is then used to + /// perform the shift. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(0x1).overflowing_shl(4), (U256::new(0x10), false)); + /// assert_eq!(U256::new(0x1).overflowing_shl(260), (U256::new(0x10), true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn overflowing_shl(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shl(rhs), rhs > 255) + } + + /// Shifts self right by `rhs` bits. + /// + /// Returns a tuple of the shifted version of self along with a boolean + /// indicating whether the shift value was larger than or equal to the + /// number of bits. If the shift value is too large, then value is masked + /// (N-1) where N is the number of bits, and this value is then used to + /// perform the shift. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(0x10).overflowing_shr(4), (U256::new(0x1), false)); + /// assert_eq!(U256::new(0x10).overflowing_shr(260), (U256::new(0x1), true)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn overflowing_shr(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shr(rhs), rhs > 255) + } + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// Returns a tuple of the exponentiation along with a bool indicating + /// whether an overflow happened. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(3).overflowing_pow(5), (U256::new(243), false)); + /// assert_eq!( + /// U256::new(1337).overflowing_pow(42), + /// ( + /// U256::from_words( + /// 45367329835866155830012179193722278514, + /// 159264946433345088039815329994094210673, + /// ), + /// true, + /// ) + /// ); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { + let mut base = self; + let mut acc = U256::ONE; + let mut overflown = false; + // Scratch space for storing results of overflowing_mul. + let mut r; + + while exp > 1 { + if (exp & 1) == 1 { + r = acc.overflowing_mul(base); + acc = r.0; + overflown |= r.1; + } + exp /= 2; + r = base.overflowing_mul(base); + base = r.0; + overflown |= r.1; + } + + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + if exp == 1 { + r = acc.overflowing_mul(base); + acc = r.0; + overflown |= r.1; + } + + (acc, overflown) + } + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(2).pow(5), U256::new(32)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn pow(self, mut exp: u32) -> Self { + let mut base = self; + let mut acc = U256::ONE; + + while exp > 1 { + if (exp & 1) == 1 { + acc *= base; + } + exp /= 2; + base = base * base; + } + + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + if exp == 1 { + acc *= base; + } + + acc + } + + /// Performs Euclidean division. + /// + /// Since, for the positive integers, all common definitions of division are + /// equal, this is exactly equal to `self / rhs`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(7).div_euclid(U256::new(4)), U256::new(1)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn div_euclid(self, rhs: Self) -> Self { + self / rhs + } + + /// Calculates the least remainder of `self (mod rhs)`. + /// + /// Since, for the positive integers, all common definitions of division are + /// equal, this is exactly equal to `self % rhs`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(7).rem_euclid(U256::new(4)), U256::new(3)); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub fn rem_euclid(self, rhs: Self) -> Self { + self % rhs + } + + /// Returns `true` if and only if `self == 2^k` for some `k`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert!(U256::new(16).is_power_of_two()); + /// assert!(!U256::new(10).is_power_of_two()); + /// ``` + #[inline] + pub fn is_power_of_two(self) -> bool { + self.count_ones() == 1 + } + + /// Returns one less than next power of two. (For 8u8 next power of two is + /// 8u8 and for 6u8 it is 8u8). + /// + /// 8u8.one_less_than_next_power_of_two() == 7 + /// 6u8.one_less_than_next_power_of_two() == 7 + /// + /// This method cannot overflow, as in the `next_power_of_two` overflow + /// cases it instead ends up returning the maximum value of the type, and + /// can return 0 for 0. + #[inline] + fn one_less_than_next_power_of_two(self) -> Self { + if self <= 1 { + return U256::ZERO; + } + + let p = self - 1; + let z = p.leading_zeros(); + U256::MAX >> z + } + + /// Returns the smallest power of two greater than or equal to `self`. + /// + /// When return value overflows (i.e., `self > (1 << (N-1))` for type `uN`), + /// it panics in debug mode and return value is wrapped to 0 in release mode + /// (the only situation in which method can return 0). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(2).next_power_of_two(), U256::new(2)); + /// assert_eq!(U256::new(3).next_power_of_two(), U256::new(4)); + /// ``` + #[inline] + pub fn next_power_of_two(self) -> Self { + self.one_less_than_next_power_of_two() + 1 + } + + /// Returns the smallest power of two greater than or equal to `n`. If the + /// next power of two is greater than the type's maximum value, `None` is + /// returned, otherwise the power of two is wrapped in `Some`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use ethnum::U256; + /// assert_eq!(U256::new(2).checked_next_power_of_two(), Some(U256::new(2))); + /// assert_eq!(U256::new(3).checked_next_power_of_two(), Some(U256::new(4))); + /// assert_eq!(U256::MAX.checked_next_power_of_two(), None); + /// ``` + #[inline] + pub fn checked_next_power_of_two(self) -> Option { + self.one_less_than_next_power_of_two() + .checked_add(U256::ONE) + } + + /// Return the memory representation of this integer as a byte array in big + /// endian (network) byte order. + /// + /// # Examples + /// + /// ``` + /// # use ethnum::U256; + /// let bytes = U256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ); + /// assert_eq!( + /// bytes.to_be_bytes(), + /// [ + /// 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + /// 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + /// ], + /// ); + /// ``` + #[inline] + pub fn to_be_bytes(self) -> [u8; mem::size_of::()] { + self.to_be().to_ne_bytes() + } + + /// Return the memory representation of this integer as a byte array in + /// little endian byte order. + /// + /// # Examples + /// + /// ``` + /// # use ethnum::U256; + /// let bytes = U256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ); + /// assert_eq!( + /// bytes.to_le_bytes(), + /// [ + /// 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + /// 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + /// ], + /// ); + /// ``` + #[inline] + pub fn to_le_bytes(self) -> [u8; mem::size_of::()] { + self.to_le().to_ne_bytes() + } + + /// Return the memory representation of this integer as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code should + /// use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead. + /// + /// [`to_be_bytes`]: #method.to_be_bytes + /// [`to_le_bytes`]: #method.to_le_bytes + /// + /// # Examples + /// + /// ``` + /// # use ethnum::U256; + /// let bytes = U256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ); + /// assert_eq!( + /// bytes.to_ne_bytes(), + /// if cfg!(target_endian = "big") { + /// [ + /// 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + /// 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + /// ] + /// } else { + /// [ + /// 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + /// 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + /// ] + /// } + /// ); + /// ``` + #[inline] + pub fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + unsafe { mem::transmute(self) } + } + + /// Create an integer value from its representation as a byte array in big + /// endian. + /// + /// # Examples + /// + /// ``` + /// # use ethnum::U256; + /// let value = U256::from_be_bytes([ + /// 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + /// 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + /// ]); + /// assert_eq!( + /// value, + /// U256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ), + /// ); + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion + /// APIs can be used: + /// + /// ``` + /// # use ethnum::U256; + /// use std::convert::TryInto; + /// + /// fn read_be_u256(input: &mut &[u8]) -> U256 { + /// let (int_bytes, rest) = input.split_at(std::mem::size_of::()); + /// *input = rest; + /// U256::from_be_bytes(int_bytes.try_into().unwrap()) + /// } + /// ``` + #[inline] + pub fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_be(Self::from_ne_bytes(bytes)) + } + + /// Create an integer value from its representation as a byte array in + /// little endian. + /// + /// # Examples + /// + /// ``` + /// # use ethnum::U256; + /// let value = U256::from_le_bytes([ + /// 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + /// 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + /// ]); + /// assert_eq!( + /// value, + /// U256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ), + /// ); + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion + /// APIs can be used: + /// + /// ``` + /// # use ethnum::U256; + /// use std::convert::TryInto; + /// + /// fn read_be_u256(input: &mut &[u8]) -> U256 { + /// let (int_bytes, rest) = input.split_at(std::mem::size_of::()); + /// *input = rest; + /// U256::from_le_bytes(int_bytes.try_into().unwrap()) + /// } + /// ``` + #[inline] + pub fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_le(Self::from_ne_bytes(bytes)) + } + + /// Create an integer value from its memory representation as a byte array + /// in native endianness. + /// + /// As the target platform's native endianness is used, portable code likely + /// wants to use [`from_be_bytes`] or [`from_le_bytes`], as appropriate + /// instead. + /// + /// [`from_be_bytes`]: #method.from_be_bytes + /// [`from_le_bytes`]: #method.from_le_bytes + /// + /// # Examples + /// + /// ``` + /// # use ethnum::U256; + /// let value = U256::from_ne_bytes(if cfg!(target_endian = "big") { + /// [ + /// 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + /// 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + /// ] + /// } else { + /// [ + /// 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + /// 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + /// ] + /// }); + /// assert_eq!( + /// value, + /// U256::from_words( + /// 0x00010203_04050607_08090a0b_0c0d0e0f, + /// 0x10111213_14151617_18191a1b_1c1d1e1f, + /// ), + /// ); + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion + /// APIs can be used: + /// + /// ``` + /// # use ethnum::U256; + /// use std::convert::TryInto; + /// + /// fn read_be_u256(input: &mut &[u8]) -> U256 { + /// let (int_bytes, rest) = input.split_at(std::mem::size_of::()); + /// *input = rest; + /// U256::from_ne_bytes(int_bytes.try_into().unwrap()) + /// } + /// ``` + #[inline] + pub fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + unsafe { mem::transmute(bytes) } + } +} diff --git a/vendor/ethnum/src/uint/cmp.rs b/vendor/ethnum/src/uint/cmp.rs new file mode 100644 index 0000000..aaae572 --- /dev/null +++ b/vendor/ethnum/src/uint/cmp.rs @@ -0,0 +1,48 @@ +//! Module with comparison implementations for `U256`. +//! +//! `PartialEq` and `PartialOrd` implementations for `u128` are also provided +//! to allow notation such as: +//! +//! ``` +//! # use ethnum::U256; +//! +//! assert_eq!(U256::new(42), 42); +//! assert!(U256::ONE > 0 && U256::ZERO == 0); +//! ``` + +use crate::uint::U256; +use core::cmp::Ordering; + +impl Ord for U256 { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.into_words().cmp(&other.into_words()) + } +} + +impl_cmp! { + impl Cmp for U256 (u128); +} + +#[cfg(test)] +mod tests { + use super::*; + use core::cmp::Ordering; + + #[test] + fn cmp() { + // 1e38 + let x = U256::from_words(0, 100000000000000000000000000000000000000); + // 1e48 + let y = U256::from_words(2938735877, 18960114910927365649471927446130393088); + assert!(x < y); + assert_eq!(x.cmp(&y), Ordering::Less); + assert!(y > x); + assert_eq!(y.cmp(&x), Ordering::Greater); + + let x = U256::new(100); + let y = U256::new(100); + assert!(x <= y); + assert_eq!(x.cmp(&y), Ordering::Equal); + } +} diff --git a/vendor/ethnum/src/uint/convert.rs b/vendor/ethnum/src/uint/convert.rs new file mode 100644 index 0000000..da2bdd3 --- /dev/null +++ b/vendor/ethnum/src/uint/convert.rs @@ -0,0 +1,198 @@ +//! Module contains conversions for [`U256`] to and from primimitive types. + +use super::U256; +use crate::{error::tfie, int::I256}; +use core::{convert::TryFrom, num::TryFromIntError}; + +macro_rules! impl_from { + ($($t:ty),* $(,)?) => {$( + impl From<$t> for U256 { + #[inline] + fn from(value: $t) -> Self { + U256::new(value.into()) + } + } + )*}; +} + +impl_from! { + bool, u8, u16, u32, u64, u128, +} + +macro_rules! impl_try_from { + ($($t:ty),* $(,)?) => {$( + impl TryFrom<$t> for U256 { + type Error = TryFromIntError; + + #[inline] + fn try_from(value: $t) -> Result { + Ok(U256::new(u128::try_from(value)?)) + } + } + )*}; +} + +impl_try_from! { + i8, i16, i32, i64, i128, + isize, usize, +} + +impl TryFrom for U256 { + type Error = TryFromIntError; + + fn try_from(value: I256) -> Result { + if value < 0 { + return Err(tfie()); + } + Ok(value.as_u256()) + } +} + +/// This trait defines `as` conversions (casting) from primitive types to +/// [`U256`]. +/// +/// [`U256`]: struct.U256.html +/// +/// # Examples +/// +/// Note that in Rust casting from a negative signed integer sign to a larger +/// unsigned interger sign extends. Additionally casting a floating point value +/// to an integer is a saturating operation, with `NaN` converting to `0`. So: +/// +/// ``` +/// # use ethnum::{U256, AsU256}; +/// assert_eq!((-1i32).as_u256(), U256::MAX); +/// assert_eq!(u32::MAX.as_u256(), 0xffffffff); +/// +/// assert_eq!(f64::NEG_INFINITY.as_u256(), 0); +/// assert_eq!((-1.0f64).as_u256(), 0); +/// assert_eq!(f64::INFINITY.as_u256(), U256::MAX); +/// assert_eq!(2.0f64.powi(257).as_u256(), U256::MAX); +/// assert_eq!(f64::NAN.as_u256(), 0); +/// ``` +pub trait AsU256 { + /// Perform an `as` conversion to a [`U256`]. + /// + /// [`U256`]: struct.U256.html + #[allow(clippy::wrong_self_convention)] + fn as_u256(self) -> U256; +} + +impl AsU256 for U256 { + #[inline] + fn as_u256(self) -> U256 { + self + } +} + +impl AsU256 for I256 { + #[inline] + fn as_u256(self) -> U256 { + I256::as_u256(self) + } +} + +macro_rules! impl_as_u256 { + ($($t:ty),* $(,)?) => {$( + impl AsU256 for $t { + #[inline] + fn as_u256(self) -> U256 { + #[allow(unused_comparisons)] + let hi = if self >= 0 { 0 } else { !0 }; + U256::from_words(hi, self as _) + } + } + )*}; +} + +impl_as_u256! { + i8, i16, i32, i64, i128, + u8, u16, u32, u64, u128, + isize, usize, +} + +impl AsU256 for bool { + #[inline] + fn as_u256(self) -> U256 { + U256::new(self as _) + } +} + +macro_rules! impl_as_u256_float { + ($($t:ty [$b:ty]),* $(,)?) => {$( + impl AsU256 for $t { + #[inline] + fn as_u256(self) -> U256 { + // The conversion follows roughly the same rules as converting + // `f64` to other primitive integer types: + // - `NaN` => `0` + // - `(-∞, 0]` => `0` + // - `(0, U256::MAX]` => `value as U256` + // - `(U256::MAX, +∞)` => `U256::MAX` + + const M: $b = (<$t>::MANTISSA_DIGITS - 1) as _; + const MAN_MASK: $b = !(!0 << M); + const MAN_ONE: $b = 1 << M; + const EXP_MASK: $b = !0 >> <$t>::MANTISSA_DIGITS; + const EXP_OFFSET: $b = EXP_MASK / 2; + + if self >= 1.0 { + let bits = self.to_bits(); + let exponent = ((bits >> M) & EXP_MASK) - EXP_OFFSET; + let mantissa = (bits & MAN_MASK) | MAN_ONE; + if exponent <= M { + U256::from(mantissa >> (M - exponent)) + } else if exponent < 256 { + U256::from(mantissa) << (exponent - M) + } else { + U256::MAX + } + } else { + U256::ZERO + } + } + } + )*}; +} + +impl_as_u256_float! { + f32[u32], f64[u64], +} + +macro_rules! impl_try_into { + ($($t:ty),* $(,)?) => {$( + impl TryFrom for $t { + type Error = TryFromIntError; + + #[inline] + fn try_from(x: U256) -> Result { + if x <= <$t>::MAX.as_u256() { + Ok(*x.low() as _) + } else { + Err(tfie()) + } + } + } + )*}; +} + +impl_try_into! { + i8, i16, i32, i64, i128, + u8, u16, u32, u64, u128, + isize, usize, +} + +macro_rules! impl_into_float { + ($($t:ty => $f:ident),* $(,)?) => {$( + impl From for $t { + #[inline] + fn from(x: U256) -> $t { + x.$f() + } + } + )*}; +} + +impl_into_float! { + f32 => as_f32, f64 => as_f64, +} diff --git a/vendor/ethnum/src/uint/fmt.rs b/vendor/ethnum/src/uint/fmt.rs new file mode 100644 index 0000000..aa8bf9e --- /dev/null +++ b/vendor/ethnum/src/uint/fmt.rs @@ -0,0 +1,51 @@ +//! Module implementing formatting for `U256` type. + +use crate::uint::U256; + +impl_fmt! { + impl Fmt for U256; +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::format; + + #[test] + fn debug() { + assert_eq!( + format!("{:?}", U256::MAX), + "115792089237316195423570985008687907853269984665640564039457584007913129639935", + ); + assert_eq!( + format!("{:x?}", U256::MAX), + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + assert_eq!( + format!("{:#X?}", U256::MAX), + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + ); + } + + #[test] + fn display() { + assert_eq!( + format!("{}", U256::MAX), + "115792089237316195423570985008687907853269984665640564039457584007913129639935", + ); + } + + #[test] + fn radix() { + assert_eq!(format!("{:b}", U256::new(42)), "101010"); + assert_eq!(format!("{:o}", U256::new(42)), "52"); + assert_eq!(format!("{:x}", U256::new(42)), "2a"); + } + + #[test] + fn exp() { + assert_eq!(format!("{:e}", U256::new(42)), "4.2e1"); + assert_eq!(format!("{:e}", U256::new(10).pow(77)), "1e77"); + assert_eq!(format!("{:E}", U256::new(10).pow(39) * 1337), "1.337E42"); + } +} diff --git a/vendor/ethnum/src/uint/iter.rs b/vendor/ethnum/src/uint/iter.rs new file mode 100644 index 0000000..f2e2f14 --- /dev/null +++ b/vendor/ethnum/src/uint/iter.rs @@ -0,0 +1,7 @@ +//! Module contains iterator specific trait implementations. + +use super::U256; + +impl_iter! { + impl Iter for U256; +} diff --git a/vendor/ethnum/src/uint/ops.rs b/vendor/ethnum/src/uint/ops.rs new file mode 100644 index 0000000..cbfbfe7 --- /dev/null +++ b/vendor/ethnum/src/uint/ops.rs @@ -0,0 +1,305 @@ +//! Module `core::ops` trait implementations. +//! +//! Trait implementations for `i128` are also provided to allow notation such +//! as: +//! +//! ``` +//! # use ethnum::U256; +//! +//! let a = 1 + U256::ONE; +//! let b = U256::ONE + 1; +//! dbg!(a, b); +//! ``` + +use super::U256; +use crate::intrinsics::signed::*; + +impl_ops! { + for U256 | u128 { + add => uadd2, uadd3, uaddc; + mul => umul2, umul3, umulc; + sub => usub2, usub3, usubc; + + div => udiv2, udiv3; + rem => urem2, urem3; + + shl => ushl2, ushl3; + shr => ushr2, ushr3; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::ops::*; + + #[test] + fn trait_implementations() { + trait Implements {} + impl Implements for U256 {} + impl Implements for &'_ U256 {} + + fn assert_ops() + where + for<'a> T: Implements + + Add<&'a u128> + + Add<&'a U256> + + Add + + Add + + AddAssign<&'a u128> + + AddAssign<&'a U256> + + AddAssign + + AddAssign + + BitAnd<&'a u128> + + BitAnd<&'a U256> + + BitAnd + + BitAnd + + BitAndAssign<&'a u128> + + BitAndAssign<&'a U256> + + BitAndAssign + + BitAndAssign + + BitOr<&'a u128> + + BitOr<&'a U256> + + BitOr + + BitOr + + BitOrAssign<&'a u128> + + BitOrAssign<&'a U256> + + BitOrAssign + + BitOrAssign + + BitXor<&'a u128> + + BitXor<&'a U256> + + BitXor + + BitXor + + BitXorAssign<&'a u128> + + BitXorAssign<&'a U256> + + BitXorAssign + + BitXorAssign + + Div<&'a u128> + + Div<&'a U256> + + Div + + Div + + DivAssign<&'a u128> + + DivAssign<&'a U256> + + DivAssign + + DivAssign + + Mul<&'a u128> + + Mul<&'a U256> + + Mul + + Mul + + MulAssign<&'a u128> + + MulAssign<&'a U256> + + MulAssign + + MulAssign + + Not + + Rem<&'a u128> + + Rem<&'a U256> + + Rem + + Rem + + RemAssign<&'a u128> + + RemAssign<&'a U256> + + RemAssign + + RemAssign + + Shl<&'a i128> + + Shl<&'a i16> + + Shl<&'a i32> + + Shl<&'a i64> + + Shl<&'a i8> + + Shl<&'a isize> + + Shl<&'a u128> + + Shl<&'a u16> + + Shl<&'a U256> + + Shl<&'a u32> + + Shl<&'a u64> + + Shl<&'a u8> + + Shl<&'a usize> + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + ShlAssign<&'a i128> + + ShlAssign<&'a i16> + + ShlAssign<&'a i32> + + ShlAssign<&'a i64> + + ShlAssign<&'a i8> + + ShlAssign<&'a isize> + + ShlAssign<&'a u128> + + ShlAssign<&'a u16> + + ShlAssign<&'a U256> + + ShlAssign<&'a u32> + + ShlAssign<&'a u64> + + ShlAssign<&'a u8> + + ShlAssign<&'a usize> + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + ShlAssign + + Shr<&'a i128> + + Shr<&'a i16> + + Shr<&'a i32> + + Shr<&'a i64> + + Shr<&'a i8> + + Shr<&'a isize> + + Shr<&'a u128> + + Shr<&'a u16> + + Shr<&'a U256> + + Shr<&'a u32> + + Shr<&'a u64> + + Shr<&'a u8> + + Shr<&'a usize> + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + ShrAssign<&'a i128> + + ShrAssign<&'a i16> + + ShrAssign<&'a i32> + + ShrAssign<&'a i64> + + ShrAssign<&'a i8> + + ShrAssign<&'a isize> + + ShrAssign<&'a u128> + + ShrAssign<&'a u16> + + ShrAssign<&'a U256> + + ShrAssign<&'a u32> + + ShrAssign<&'a u64> + + ShrAssign<&'a u8> + + ShrAssign<&'a usize> + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + ShrAssign + + Sub<&'a u128> + + Sub<&'a U256> + + Sub + + Sub + + SubAssign<&'a u128> + + SubAssign<&'a U256> + + SubAssign + + SubAssign, + for<'a> &'a T: Implements + + Add<&'a u128> + + Add<&'a U256> + + Add + + Add + + BitAnd<&'a u128> + + BitAnd<&'a U256> + + BitAnd + + BitAnd + + BitOr<&'a u128> + + BitOr<&'a U256> + + BitOr + + BitOr + + BitXor<&'a u128> + + BitXor<&'a U256> + + BitXor + + BitXor + + Div<&'a u128> + + Div<&'a U256> + + Div + + Div + + Mul<&'a u128> + + Mul<&'a U256> + + Mul + + Mul + + Not + + Rem<&'a u128> + + Rem<&'a U256> + + Rem + + Rem + + Shl<&'a i128> + + Shl<&'a i16> + + Shl<&'a i32> + + Shl<&'a i64> + + Shl<&'a i8> + + Shl<&'a isize> + + Shl<&'a u128> + + Shl<&'a u16> + + Shl<&'a U256> + + Shl<&'a u32> + + Shl<&'a u64> + + Shl<&'a u8> + + Shl<&'a usize> + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shl + + Shr<&'a i128> + + Shr<&'a i16> + + Shr<&'a i32> + + Shr<&'a i64> + + Shr<&'a i8> + + Shr<&'a isize> + + Shr<&'a u128> + + Shr<&'a u16> + + Shr<&'a U256> + + Shr<&'a u32> + + Shr<&'a u64> + + Shr<&'a u8> + + Shr<&'a usize> + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Shr + + Sub<&'a u128> + + Sub<&'a U256> + + Sub + + Sub, + { + } + + assert_ops::(); + } +} diff --git a/vendor/ethnum/src/uint/parse.rs b/vendor/ethnum/src/uint/parse.rs new file mode 100644 index 0000000..3c4dcbe --- /dev/null +++ b/vendor/ethnum/src/uint/parse.rs @@ -0,0 +1,115 @@ +//! Module implementing parsing for `U256` type. + +use crate::uint::U256; + +impl_from_str! { + impl FromStr for U256; +} + +pub const fn const_from_str_prefixed(src: &str) -> U256 { + assert!(!src.is_empty(), "empty string"); + + let bytes = src.as_bytes(); + let start = bytes[0] == b'+'; + crate::parse::const_from_str_prefixed(bytes, start as _) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parse::from_str_radix; + use core::num::IntErrorKind; + + #[test] + fn from_str() { + assert_eq!("42".parse::().unwrap(), 42); + } + + #[test] + fn from_str_prefixed() { + assert_eq!(from_str_radix::("0b101", 2, Some("0b")).unwrap(), 5); + assert_eq!(from_str_radix::("0xf", 16, Some("0x")).unwrap(), 15); + } + + #[test] + fn from_str_errors() { + assert_eq!( + from_str_radix::("", 2, None).unwrap_err().kind(), + &IntErrorKind::Empty, + ); + assert_eq!( + from_str_radix::("?", 2, None).unwrap_err().kind(), + &IntErrorKind::InvalidDigit, + ); + assert_eq!( + from_str_radix::("1", 16, Some("0x")) + .unwrap_err() + .kind(), + &IntErrorKind::InvalidDigit, + ); + assert_eq!( + from_str_radix::("-1", 10, None).unwrap_err().kind(), + &IntErrorKind::InvalidDigit, + ); + assert_eq!( + from_str_radix::( + "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", + 36, + None + ) + .unwrap_err() + .kind(), + &IntErrorKind::PosOverflow, + ); + } + + #[test] + fn const_parse() { + assert_eq!(const_from_str_prefixed("+0b1101"), 0b1101); + assert_eq!(const_from_str_prefixed("0o777"), 0o777); + assert_eq!(const_from_str_prefixed("+0x1f"), 0x1f); + assert_eq!(const_from_str_prefixed("42"), 42); + + assert_eq!( + const_from_str_prefixed( + "0xffff_ffff_ffff_ffff_ffff_ffff_ffff_fffe\ + baae_dce6_af48_a03b_bfd2_5e8c_d036_4141" + ), + U256::from_words( + 0xffff_ffff_ffff_ffff_ffff_ffff_ffff_fffe, + 0xbaae_dce6_af48_a03b_bfd2_5e8c_d036_4141, + ), + ); + + assert_eq!( + const_from_str_prefixed( + "0x0000_0000_0000_0000_0000_0000_0000_0000\ + 0000_0000_0000_0000_0000_0000_0000_0000" + ), + U256::MIN, + ); + assert_eq!( + const_from_str_prefixed( + "+0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff\ + ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff" + ), + U256::MAX, + ); + } + + #[test] + #[should_panic] + fn const_parse_overflow() { + const_from_str_prefixed( + "0x1\ + 0000_0000_0000_0000_0000_0000_0000_0000\ + 0000_0000_0000_0000_0000_0000_0000_0000", + ); + } + + #[test] + #[should_panic] + fn const_parse_invalid() { + const_from_str_prefixed("invalid"); + } +}