From 0282e1c624ff811d069163f5dc9a47369731e73a Mon Sep 17 00:00:00 2001 From: moralpriest Date: Wed, 15 Jul 2026 11:41:20 -0600 Subject: [PATCH] chore(deps): bump github.com/fxamacker/cbor/v2 to v2.5.0 Vendored dependency bump (prep for eventual Go toolchain bump). - github.com/fxamacker/cbor/v2: v2.5.0 (go 1.12, latest go<=1.17). Used by p2p (chunk_server, rpc_cbor_codec) and rpc packages for CBOR encoding. Note: vendored at vendor/github.com/fxamacker/cbor/v2 (the /v2 major-version suffix must stay in the import path). Verification: - go build ./... green. - go test ./...: cbor's own suite and the p2p/rpc importers pass. Only pre-existing failure is Test_Payload_TX (walletapi, reproduced on clean community-dev). - derod --testnet starts, logs cleanly (no errors/panics). --- .../fxamacker/cbor/v2/.github/dependabot.yml | 12 + .../cbor/v2/.github/pull_request_template.md | 6 +- .../cbor/v2/.github/workflows/ci-go-cover.yml | 28 +- .../cbor/v2/.github/workflows/ci.yml | 42 +- .../v2/.github/workflows/codeql-analysis.yml | 40 +- .../cbor/v2/.github/workflows/govulncheck.yml | 47 + .../.github/workflows/safer-golangci-lint.yml | 87 +- .../fxamacker/cbor/v2/.golangci.yml | 22 +- .../fxamacker/cbor/v2/CBOR_BENCHMARKS.md | 264 ---- .../fxamacker/cbor/v2/CBOR_GOLANG.md | 32 - .../fxamacker/cbor/v2/CODE_OF_CONDUCT.md | 153 ++- .../fxamacker/cbor/v2/CONTRIBUTING.md | 40 +- vendor/github.com/fxamacker/cbor/v2/README.md | 1153 ++++------------- .../fxamacker/cbor/v2/bench_test.go | 77 +- .../fxamacker/cbor/v2/bytestring.go | 62 + .../fxamacker/cbor/v2/bytestring_test.go | 101 ++ vendor/github.com/fxamacker/cbor/v2/decode.go | 392 +++++- .../fxamacker/cbor/v2/decode_test.go | 960 +++++++++++--- .../github.com/fxamacker/cbor/v2/diagnose.go | 741 +++++++++++ .../fxamacker/cbor/v2/diagnose_test.go | 1104 ++++++++++++++++ vendor/github.com/fxamacker/cbor/v2/doc.go | 13 +- vendor/github.com/fxamacker/cbor/v2/encode.go | 50 +- .../fxamacker/cbor/v2/encode_test.go | 149 ++- .../fxamacker/cbor/v2/simplevalue.go | 17 + vendor/github.com/fxamacker/cbor/v2/stream.go | 120 +- .../fxamacker/cbor/v2/stream_test.go | 743 ++++++++++- vendor/github.com/fxamacker/cbor/v2/tag.go | 6 - vendor/github.com/fxamacker/cbor/v2/valid.go | 56 +- .../fxamacker/cbor/v2/valid_test.go | 57 +- 29 files changed, 4849 insertions(+), 1725 deletions(-) create mode 100644 vendor/github.com/fxamacker/cbor/v2/.github/dependabot.yml create mode 100644 vendor/github.com/fxamacker/cbor/v2/.github/workflows/govulncheck.yml delete mode 100644 vendor/github.com/fxamacker/cbor/v2/CBOR_BENCHMARKS.md delete mode 100644 vendor/github.com/fxamacker/cbor/v2/CBOR_GOLANG.md create mode 100644 vendor/github.com/fxamacker/cbor/v2/bytestring.go create mode 100644 vendor/github.com/fxamacker/cbor/v2/bytestring_test.go create mode 100644 vendor/github.com/fxamacker/cbor/v2/diagnose.go create mode 100644 vendor/github.com/fxamacker/cbor/v2/diagnose_test.go create mode 100644 vendor/github.com/fxamacker/cbor/v2/simplevalue.go diff --git a/vendor/github.com/fxamacker/cbor/v2/.github/dependabot.yml b/vendor/github.com/fxamacker/cbor/v2/.github/dependabot.yml new file mode 100644 index 00000000..cdf65ddb --- /dev/null +++ b/vendor/github.com/fxamacker/cbor/v2/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" diff --git a/vendor/github.com/fxamacker/cbor/v2/.github/pull_request_template.md b/vendor/github.com/fxamacker/cbor/v2/.github/pull_request_template.md index 70ef8ca7..3d81b87e 100644 --- a/vendor/github.com/fxamacker/cbor/v2/.github/pull_request_template.md +++ b/vendor/github.com/fxamacker/cbor/v2/.github/pull_request_template.md @@ -2,16 +2,16 @@ Thank you for your interest in contributing to fxamacker/cbor! --> -#### Description (motivation) +### Description #### PR Was Proposed and Welcomed in Currently Open Issue -This PR was proposed and welcomed by maintainer(s) in issue # -Closes # +- [ ] This PR was proposed and welcomed by maintainer(s) in issue #___ +- [ ] Closes or Updates Issue #___ #### Checklist (for code PR only, ignore for docs PR) diff --git a/vendor/github.com/fxamacker/cbor/v2/.github/workflows/ci-go-cover.yml b/vendor/github.com/fxamacker/cbor/v2/.github/workflows/ci-go-cover.yml index deb20355..4283875e 100644 --- a/vendor/github.com/fxamacker/cbor/v2/.github/workflows/ci-go-cover.yml +++ b/vendor/github.com/fxamacker/cbor/v2/.github/workflows/ci-go-cover.yml @@ -1,7 +1,7 @@ -# Copyright 2020-present Montgomery Edwards⁴⁴⁸ (github.com/x448). +# Copyright 2020-2023 Montgomery Edwards⁴⁴⁸ (github.com/x448). # This file is licensed under the MIT License. See LICENSE at https://github.com/x448/workflows for the full text. # -# CI Go Cover 2020.1.28. +# CI Go Cover 2023.5.14. # This GitHub Actions workflow checks if Go (Golang) code coverage satisfies the required minimum. # The required minimum is specified in the workflow name to keep badge.svg and verified minimum in sync. # @@ -14,18 +14,36 @@ # 1. Change workflow name from "cover 100%" to "cover ≥92.5%". Script will automatically use 92.5%. # 2. Update README.md to use the new path to badge.svg because the path includes the workflow name. -name: cover ≥98% -on: [push] +name: cover ≥96% + +# Remove default permissions. +permissions: {} + +on: + workflow_dispatch: + pull_request: + push: + branches: [main, master] + jobs: # Verify minimum coverage is reached using `go test -short -cover` on latest-ubuntu with default version of Go. # The grep expression can't be too strict, it needed to be relaxed to work with different versions of Go. cover: name: Coverage + permissions: + contents: read runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.19 + check-latest: true + - name: Install x448/float16 + run: go get github.com/x448/float16@v0.8.4 - name: Go Coverage run: | go version diff --git a/vendor/github.com/fxamacker/cbor/v2/.github/workflows/ci.yml b/vendor/github.com/fxamacker/cbor/v2/.github/workflows/ci.yml index 91cdbd6b..56fe16bd 100644 --- a/vendor/github.com/fxamacker/cbor/v2/.github/workflows/ci.yml +++ b/vendor/github.com/fxamacker/cbor/v2/.github/workflows/ci.yml @@ -1,26 +1,54 @@ -# GitHub Actions - CI for Go to build & test. See ci-go-cover.yml and linters.yml for code coverage and linters. +# GitHub Actions - CI for Go to build & test. # https://github.com/fxamacker/cbor/workflows/ci.yml +# See ci-go-cover.yml for coverage and safer-golangci-lint.yml for linting. name: ci -on: [push] -jobs: +# Revoke default permissions. +permissions: {} + +on: + workflow_dispatch: + pull_request: + push: + branches: + - 'master' + - 'release*' + - 'feature/stream-mode' + tags: + - 'v*' +jobs: # Test on various OS with default Go version. tests: - name: Test on ${{matrix.os}} + name: test ${{matrix.os}} go-${{ matrix.go-version }} runs-on: ${{ matrix.os }} + permissions: + contents: read strategy: matrix: - os: [macos-latest, ubuntu-latest] + os: [macos-latest, ubuntu-latest, windows-latest] + go-version: [1.17, 1.18, 1.19] steps: + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} + check-latest: true + - name: Checkout code - uses: actions/checkout@v1 + uses: actions/checkout@v3 with: fetch-depth: 1 + + - name: Print Go version + run: go version + - name: Get dependencies run: go get -v -t -d ./... + - name: Build project run: go build ./... + - name: Run tests run: | go version - go test -short -race -v ./... + go test -race -v ./... diff --git a/vendor/github.com/fxamacker/cbor/v2/.github/workflows/codeql-analysis.yml b/vendor/github.com/fxamacker/cbor/v2/.github/workflows/codeql-analysis.yml index af3accd8..b577ad00 100644 --- a/vendor/github.com/fxamacker/cbor/v2/.github/workflows/codeql-analysis.yml +++ b/vendor/github.com/fxamacker/cbor/v2/.github/workflows/codeql-analysis.yml @@ -1,16 +1,8 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# name: "CodeQL" +# Remove default permissions +permissions: {} + on: push: branches: [ master ] @@ -33,39 +25,21 @@ jobs: fail-fast: false matrix: language: [ 'go' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release + uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/vendor/github.com/fxamacker/cbor/v2/.github/workflows/govulncheck.yml b/vendor/github.com/fxamacker/cbor/v2/.github/workflows/govulncheck.yml new file mode 100644 index 00000000..ade2d1f4 --- /dev/null +++ b/vendor/github.com/fxamacker/cbor/v2/.github/workflows/govulncheck.yml @@ -0,0 +1,47 @@ +# GitHub Actions workflow for govulncheck. +# This file is licensed under MIT License. +# https://github.com/fxamacker/cbor + +name: govulncheck + +# Revoke default permissions and grant what's needed in each job. +permissions: {} + +on: + workflow_dispatch: + pull_request: + paths: + - '**' + - '!**.md' + push: + paths: + - '**' + - '!**.md' + branches: + - 'main' + - 'master' + - 'release*' + - 'feature/stream-mode' + tags: + - 'v*' + +jobs: + Check: + runs-on: ubuntu-latest + permissions: + # Grant permission to read content. + contents: read + steps: + - name: Checkout source + uses: actions/checkout@v3 + with: + fetch-depth: 1 + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: 1.20.x + check-latest: true + - name: Install latest from golang.org + run: go install golang.org/x/vuln/cmd/govulncheck@f69de671333b611ab6b6f21f8ff0ab53f6d96c61 # v1.0.0 + - name: Run govulncheck + run: govulncheck -show=traces ./... diff --git a/vendor/github.com/fxamacker/cbor/v2/.github/workflows/safer-golangci-lint.yml b/vendor/github.com/fxamacker/cbor/v2/.github/workflows/safer-golangci-lint.yml index 6115c5e1..6722fa80 100644 --- a/vendor/github.com/fxamacker/cbor/v2/.github/workflows/safer-golangci-lint.yml +++ b/vendor/github.com/fxamacker/cbor/v2/.github/workflows/safer-golangci-lint.yml @@ -1,4 +1,4 @@ -# Copyright © 2021 Montgomery Edwards⁴⁴⁸ (github.com/x448). +# Copyright © 2021-2023 Montgomery Edwards⁴⁴⁸ (github.com/x448). # This file is licensed under MIT License. # # Safer GitHub Actions Workflow for golangci-lint. @@ -6,62 +6,74 @@ # # safer-golangci-lint.yml # -# 100% of the script for downloading, installing, and running golangci-lint -# is embedded in this file. The embedded SHA384 is used to verify the -# downloaded golangci-lint tarball (golangci-lint-1.40.1-linux-amd64.tar.gz). -# -# Why? -# 1. Avoid downloading and executing unverified wrapper scripts or actions each time a workflow runs. -# See https://www.securityweek.com/codecov-bash-uploader-dev-tool-compromised-supply-chain-hack -# 2. Use openssl instead of sha256sum because it's easier to change hash algo to BLAKE2s, SHA3-256, etc. -# 3. Use SHA384 instead of SHA256 to avoid debating strangers about length extension attacks and gzip file format. -# 4. Use embedded SHA384 instead of downloading CHECKSUM because CHECKSUM file isn't digitally signed. -# 5. Use binary instead of building from source because it's probably easier to detect backdoors in one binary -# than all the combined source code of dozens of linters and all their required 3rd-party packages. +# This workflow downloads, verifies, and runs golangci-lint in a +# deterministic, reviewable, and safe manner. # # To use: -# Copy this file into [github_repo]/.github/workflows/ +# Step 1. Copy this file into [your_github_repo]/.github/workflows/ +# Step 2. There's no step 2 if you like the default settings. +# +# See golangci-lint docs for more info at +# https://github.com/golangci/golangci-lint # -# Configure [github_repo]/.golangci.yml normally as instructed in golangci-lint docs. +# 100% of the script for downloading, installing, and running golangci-lint +# is embedded in this file. The embedded SHA-256 digest is used to verify the +# downloaded golangci-lint tarball (golangci-lint-1.xx.x-linux-amd64.tar.gz). +# +# The embedded SHA-256 digest matches golangci-lint-1.xx.x-checksums.txt at +# https://github.com/golangci/golangci-lint/releases # # To use a newer version of golangci-lint, change these values: # 1. GOLINTERS_VERSION -# 2. GOLINTERS_TGZ_HASH +# 2. GOLINTERS_TGZ_DGST +# +# Release v1.52.2 (May 14, 2023) +# - Bump Go to 1.20 +# - Bump actions/setup-go to v4 +# - Bump golangci-lint to 1.52.2 +# - Hash of golangci-lint-1.52.2-linux-amd64.tar.gz +# - SHA-256: c9cf72d12058a131746edd409ed94ccd578fbd178899d1ed41ceae3ce5f54501 +# This SHA-256 digest matches golangci-lint-1.52.2-checksums.txt at +# https://github.com/golangci/golangci-lint/releases # -# 2021-05-16 Created. Use golangci-lint 1.40.1, Go 1.15.x, and ubuntu-latest. -# sha256(tar.gz) is 7c133b4b39c0a46cf8d67265da651f169079d137ae71aee9b5934e2281bd18d3 -# sha384(tar.gz) is d0b9e9c0eac5c5e03b9feb546d181918fca9abc94656824badccacc77aa91bc78ab99fd22094d634d3a58a91353fb1b9 +name: linters -name: Lint +# Remove default permissions and grant only what is required in each job. +permissions: {} -on: +on: + workflow_dispatch: pull_request: - types: [opened, synchronize, closed] push: branches: [main, master] env: - GOLINTERS_VERSION: 1.40.1 + GO_VERSION: '1.20' + GOLINTERS_VERSION: 1.52.2 GOLINTERS_ARCH: linux-amd64 - GOLINTERS_TGZ_DGST: d0b9e9c0eac5c5e03b9feb546d181918fca9abc94656824badccacc77aa91bc78ab99fd22094d634d3a58a91353fb1b9 - OPENSSL_DGST_CMD: openssl dgst -sha384 -r + GOLINTERS_TGZ_DGST: c9cf72d12058a131746edd409ed94ccd578fbd178899d1ed41ceae3ce5f54501 + GOLINTERS_TIMEOUT: 15m + OPENSSL_DGST_CMD: openssl dgst -sha256 -r CURL_CMD: curl --proto =https --tlsv1.2 --location --silent --show-error --fail - + jobs: main: name: Lint runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout source - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 1 - + - name: Setup Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v4 with: - go-version: 1.15.x - + go-version: ${{ env.GO_VERSION }} + check-latest: true + - name: Install golangci-lint run: | GOLINTERS_URL_PREFIX="https://github.com/golangci/golangci-lint/releases/download/v${GOLINTERS_VERSION}/" @@ -84,13 +96,8 @@ jobs: tar --no-same-owner -xzf "${GOLINTERS_TGZ}" --strip-components 1 install golangci-lint $(go env GOPATH)/bin shell: bash - - # Run required linters enabled in .golangci.yml - - name: Run required linters in .golangci.yml - run: $(go env GOPATH)/bin/golangci-lint run --timeout=5m - shell: bash - - # Run noisy linters as optional (enable them using command line parameters) - - name: Run optional linters (not required to pass) - run: $(go env GOPATH)/bin/golangci-lint run --timeout=5m --issues-exit-code=0 -E dupl -E gocritic -E gosimple -E lll -E nilerr -E prealloc -E revive + + # Run required linters enabled in .golangci.yml (or default linters if yml doesn't exist) + - name: Run golangci-lint + run: $(go env GOPATH)/bin/golangci-lint run --timeout="${GOLINTERS_TIMEOUT}" shell: bash diff --git a/vendor/github.com/fxamacker/cbor/v2/.golangci.yml b/vendor/github.com/fxamacker/cbor/v2/.golangci.yml index 64a0f02d..4c31d564 100644 --- a/vendor/github.com/fxamacker/cbor/v2/.golangci.yml +++ b/vendor/github.com/fxamacker/cbor/v2/.golangci.yml @@ -22,9 +22,9 @@ linters-settings: - octalLiteral - paramTypeCombine - whyNoLint - - wrapperFunc + - wrapperFunc gofmt: - simplify: false + simplify: false goimports: local-prefixes: github.com/fxamacker/cbor golint: @@ -41,25 +41,20 @@ linters-settings: linters: disable-all: true enable: - - deadcode - errcheck - goconst - gocyclo - - gofmt - - goimports + # - gofmt # handled by safer-golangci-lint.yml + # - goimports # handled by safer-golangci-lint.yml - gosec - govet - ineffassign - misspell + # - revive # temporarily disabled to reduce noise in golangci-lint 1.52.2 - staticcheck - - structcheck - typecheck - unconvert - unused - - varcheck - # noisy linters such as gocritic are enabled by - # command line as optional linters inside - # .github/workflows/safer-golangci-lint.yml issues: # max-issues-per-linter default is 50. Set to 0 to disable limit. @@ -73,14 +68,9 @@ issues: - goconst - dupl - gomnd - - lll + - lll - path: doc\.go linters: - goimports - gomnd - lll - -# golangci.com configuration -# https://github.com/golangci/golangci/wiki/Configuration -service: - golangci-lint-version: 1.23.x # use the fixed version to not introduce new linters unexpectedly diff --git a/vendor/github.com/fxamacker/cbor/v2/CBOR_BENCHMARKS.md b/vendor/github.com/fxamacker/cbor/v2/CBOR_BENCHMARKS.md deleted file mode 100644 index d4ea1899..00000000 --- a/vendor/github.com/fxamacker/cbor/v2/CBOR_BENCHMARKS.md +++ /dev/null @@ -1,264 +0,0 @@ -# CBOR Benchmarks for fxamacker/cbor - -See [bench_test.go](bench_test.go). - -Benchmarks on Feb. 22, 2020 with cbor v2.2.0: -* [Go builtin types](#go-builtin-types) -* [Go structs](#go-structs) -* [Go structs with "keyasint" struct tag](#go-structs-with-keyasint-struct-tag) -* [Go structs with "toarray" struct tag](#go-structs-with-toarray-struct-tag) -* [COSE data](#cose-data) -* [CWT claims data](#cwt-claims-data) -* [SenML data](#SenML-data) - -## Go builtin types - -Benchmarks use data representing the following values: - -* Boolean: `true` -* Positive integer: `18446744073709551615` -* Negative integer: `-1000` -* Float: `-4.1` -* Byte string: `h'0102030405060708090a0b0c0d0e0f101112131415161718191a'` -* Text string: `"The quick brown fox jumps over the lazy dog"` -* Array: `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]` -* Map: `{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"}}` - -Decoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkUnmarshal/CBOR_bool_to_Go_interface_{}-2 | 110 ns/op | 16 B/op | 1 allocs/op -BenchmarkUnmarshal/CBOR_bool_to_Go_bool-2 | 99.3 ns/op | 1 B/op | 1 allocs/op -BenchmarkUnmarshal/CBOR_positive_int_to_Go_interface_{}-2 | 135 ns/op | 24 B/op | 2 allocs/op -BenchmarkUnmarshal/CBOR_positive_int_to_Go_uint64-2 | 116 ns/op | 8 B/op | 1 allocs/op -BenchmarkUnmarshal/CBOR_negative_int_to_Go_interface_{}-2 | 133 ns/op | 24 B/op | 2 allocs/op -BenchmarkUnmarshal/CBOR_negative_int_to_Go_int64-2 | 113 ns/op | 8 B/op | 1 allocs/op -BenchmarkUnmarshal/CBOR_float_to_Go_interface_{}-2 | 137 ns/op | 24 B/op | 2 allocs/op -BenchmarkUnmarshal/CBOR_float_to_Go_float64-2 | 115 ns/op | 8 B/op | 1 allocs/op -BenchmarkUnmarshal/CBOR_bytes_to_Go_interface_{}-2 | 179 ns/op | 80 B/op | 3 allocs/op -BenchmarkUnmarshal/CBOR_bytes_to_Go_[]uint8-2 | 194 ns/op | 64 B/op | 2 allocs/op -BenchmarkUnmarshal/CBOR_text_to_Go_interface_{}-2 | 209 ns/op | 80 B/op | 3 allocs/op -BenchmarkUnmarshal/CBOR_text_to_Go_string-2 | 193 ns/op | 64 B/op | 2 allocs/op -BenchmarkUnmarshal/CBOR_array_to_Go_interface_{}-2 |1068 ns/op | 672 B/op | 29 allocs/op -BenchmarkUnmarshal/CBOR_array_to_Go_[]int-2 | 1073 ns/op | 272 B/op | 3 allocs/op -BenchmarkUnmarshal/CBOR_map_to_Go_interface_{}-2 | 2926 ns/op | 1420 B/op | 30 allocs/op -BenchmarkUnmarshal/CBOR_map_to_Go_map[string]interface_{}-2 | 3755 ns/op | 965 B/op | 19 allocs/op -BenchmarkUnmarshal/CBOR_map_to_Go_map[string]string-2 | 2586 ns/op | 740 B/op | 5 allocs/op - -Encoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkMarshal/Go_bool_to_CBOR_bool-2 | 86.1 ns/op | 1 B/op | 1 allocs/op -BenchmarkMarshal/Go_uint64_to_CBOR_positive_int-2 | 97.0 ns/op | 16 B/op | 1 allocs/op -BenchmarkMarshal/Go_int64_to_CBOR_negative_int-2 | 90.3 ns/op | 3 B/op | 1 allocs/op -BenchmarkMarshal/Go_float64_to_CBOR_float-2 | 97.9 ns/op | 16 B/op | 1 allocs/op -BenchmarkMarshal/Go_[]uint8_to_CBOR_bytes-2 | 121 ns/op | 32 B/op | 1 allocs/op -BenchmarkMarshal/Go_string_to_CBOR_text-2 | 115 ns/op | 48 B/op | 1 allocs/op -BenchmarkMarshal/Go_[]int_to_CBOR_array-2 | 529 ns/op | 32 B/op | 1 allocs/op -BenchmarkMarshal/Go_map[string]string_to_CBOR_map-2 | 2115 ns/op | 576 B/op | 28 allocs/op - -## Go structs - -Benchmarks use struct and map[string]interface{} representing the following value: - -``` -{ - "T": true, - "Ui": uint(18446744073709551615), - "I": -1000, - "F": -4.1, - "B": []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}, - "S": "The quick brown fox jumps over the lazy dog", - "Slci": []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}, - "Mss": map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"}, -} -``` - -Decoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkUnmarshal/CBOR_map_to_Go_map[string]interface{}-2 | 6221 ns/op | 2621 B/op | 73 allocs/op -BenchmarkUnmarshal/CBOR_map_to_Go_struct-2 | 4458 ns/op | 1172 B/op | 10 allocs/op - -Encoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkMarshal/Go_map[string]interface{}_to_CBOR_map-2 | 4441 ns/op | 1072 B/op | 45 allocs/op -BenchmarkMarshal/Go_struct_to_CBOR_map-2 | 2866 ns/op | 720 B/op | 28 allocs/op - -## Go structs with "keyasint" struct tag - -Benchmarks use struct (with keyasint struct tag) and map[int]interface{} representing the following value: - -``` -{ - 1: true, - 2: uint(18446744073709551615), - 3: -1000, - 4: -4.1, - 5: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}, - 6: "The quick brown fox jumps over the lazy dog", - 7: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}, - 8: map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"}, -} -``` - -Struct type with keyasint struct tag is used to handle CBOR map with integer keys. - -``` -type T struct { - T bool `cbor:"1,keyasint"` - Ui uint `cbor:"2,keyasint"` - I int `cbor:"3,keyasint"` - F float64 `cbor:"4,keyasint"` - B []byte `cbor:"5,keyasint"` - S string `cbor:"6,keyasint"` - Slci []int `cbor:"7,keyasint"` - Mss map[string]string `cbor:"8,keyasint"` -} -``` - -Decoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkUnmarshal/CBOR_map_to_Go_map[int]interface{}-2| 6030 ns/op | 2517 B/op | 70 allocs/op -BenchmarkUnmarshal/CBOR_map_to_Go_struct_keyasint-2 | 4332 ns/op | 1173 B/op | 10 allocs/op - -Encoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkMarshal/Go_map[int]interface{}_to_CBOR_map-2 | 4348 ns/op | 992 B/op | 45 allocs/op -BenchmarkMarshal/Go_struct_keyasint_to_CBOR_map-2 | 2847 ns/op | 704 B/op | 28 allocs/op - -## Go structs with "toarray" struct tag - -Benchmarks use struct (with toarray struct tag) and []interface{} representing the following value: - -``` -[ - true, - uint(18446744073709551615), - -1000, - -4.1, - []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}, - "The quick brown fox jumps over the lazy dog", - []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}, - map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"} -] -``` - -Struct type with toarray struct tag is used to handle CBOR array. - -``` -type T struct { - _ struct{} `cbor:",toarray"` - T bool - Ui uint - I int - F float64 - B []byte - S string - Slci []int - Mss map[string]string -} -``` - -Decoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkUnmarshal/CBOR_array_to_Go_[]interface{}-2 | 4863 ns/op | 2404 B/op | 67 allocs/op -BenchmarkUnmarshal/CBOR_array_to_Go_struct_toarray-2 | 4173 ns/op | 1164 B/op | 9 allocs/op - -Encoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkMarshal/Go_[]interface{}_to_CBOR_map-2 | 3240 ns/op | 704 B/op | 28 allocs/op -BenchmarkMarshal/Go_struct_toarray_to_CBOR_array-2 | 2823 ns/op | 704 B/op | 28 allocs/op - -## COSE data - -Benchmarks use COSE data from https://tools.ietf.org/html/rfc8392#appendix-A section A.2 - -``` -// 128-Bit Symmetric COSE_Key -{ - / k / -1: h'231f4c4d4d3051fdc2ec0a3851d5b383' - / kty / 1: 4 / Symmetric /, - / kid / 2: h'53796d6d6574726963313238' / 'Symmetric128' /, - / alg / 3: 10 / AES-CCM-16-64-128 / -} -// 256-Bit Symmetric COSE_Key -{ - / k / -1: h'403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1 - ec99192d79569388' - / kty / 1: 4 / Symmetric /, - / kid / 4: h'53796d6d6574726963323536' / 'Symmetric256' /, - / alg / 3: 4 / HMAC 256/64 / -} -// ECDSA 256-Bit COSE Key -{ - / d / -4: h'6c1382765aec5358f117733d281c1c7bdc39884d04a45a1e - 6c67c858bc206c19', - / y / -3: h'60f7f1a780d8a783bfb7a2dd6b2796e8128dbbcef9d3d168 - db9529971a36e7b9', - / x / -2: h'143329cce7868e416927599cf65a34f3ce2ffda55a7eca69 - ed8919a394d42f0f', - / crv / -1: 1 / P-256 /, - / kty / 1: 2 / EC2 /, - / kid / 2: h'4173796d6d657472696345434453413 - 23536' / 'AsymmetricECDSA256' /, - / alg / 3: -7 / ECDSA 256 / -} -``` - -Decoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkUnmarshalCOSE/128-Bit_Symmetric_Key-2 | 562 ns/op | 240 B/op | 4 allocs/op -BenchmarkUnmarshalCOSE/256-Bit_Symmetric_Key-2 | 568 ns/op | 256 B/op | 4 allocs/op -BenchmarkUnmarshalCOSE/ECDSA_P256_256-Bit_Key-2 | 968 ns/op | 360 B/op | 7 allocs/op - -Encoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkMarshalCOSE/128-Bit_Symmetric_Key-2 | 523 ns/op | 224 B/op | 2 allocs/op -BenchmarkMarshalCOSE/256-Bit_Symmetric_Key-2 | 521 ns/op | 240 B/op | 2 allocs/op -BenchmarkMarshalCOSE/ECDSA_P256_256-Bit_Key-2 | 668 ns/op | 320 B/op | 2 allocs/op - -## CWT claims data - -Benchmarks use CTW claims data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1 - -``` -{ - / iss / 1: "coap://as.example.com", - / sub / 2: "erikw", - / aud / 3: "coap://light.example.com", - / exp / 4: 1444064944, - / nbf / 5: 1443944944, - / iat / 6: 1443944944, - / cti / 7: h'0b71' -} -``` - -Decoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkUnmarshalCWTClaims-2 | 765 ns/op | 176 B/op | 6 allocs/op - -Encoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkMarshalCWTClaims-2 | 451 ns/op | 176 B/op | 2 allocs/op - -## SenML data - -Benchmarks use SenML data from https://tools.ietf.org/html/rfc8428#section-6 - -``` -[ - {-2: "urn:dev:ow:10e2073a0108006:", -3: 1276020076.001, -4: "A", -1: 5, 0: "voltage", 1: "V", 2: 120.1}, - {0: "current", 6: -5, 2: 1.2}, - {0: "current", 6: -4, 2: 1.3}, - {0: "current", 6: -3, 2: 1.4}, - {0: "current", 6: -2, 2: 1.5}, - {0: "current", 6: -1, 2: 1.6}, - {0: "current", 6: 0, 2: 1.7} -] -``` - -Decoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkUnmarshalSenML-2 | 3106 ns/op | 1544 B/op | 18 allocs/op - -Encoding Benchmark | Time | Memory | Allocs ---- | ---: | ---: | ---: -BenchmarkMarshalSenML-2 | 2976 ns/op | 272 B/op | 2 allocs/op diff --git a/vendor/github.com/fxamacker/cbor/v2/CBOR_GOLANG.md b/vendor/github.com/fxamacker/cbor/v2/CBOR_GOLANG.md deleted file mode 100644 index 5949d617..00000000 --- a/vendor/github.com/fxamacker/cbor/v2/CBOR_GOLANG.md +++ /dev/null @@ -1,32 +0,0 @@ -👉 [Comparisons](https://github.com/fxamacker/cbor#comparisons) • [Status](https://github.com/fxamacker/cbor#current-status) • [Design Goals](https://github.com/fxamacker/cbor#design-goals) • [Features](https://github.com/fxamacker/cbor#features) • [Standards](https://github.com/fxamacker/cbor#standards) • [Fuzzing](https://github.com/fxamacker/cbor#fuzzing-and-code-coverage) • [Usage](https://github.com/fxamacker/cbor#usage) • [Security Policy](https://github.com/fxamacker/cbor#security-policy) • [License](https://github.com/fxamacker/cbor#license) - -# CBOR -[CBOR](https://en.wikipedia.org/wiki/CBOR) is a data format designed to allow small code size and small message size. CBOR is defined in [RFC 8949 Concise Binary Object Representation](https://tools.ietf.org/html/rfc8949) (previously [RFC 7049](https://tools.ietf.org/html/rfc7049)), an [IETF](http://ietf.org/) Internet Standards Document. - -CBOR is also designed to be stable for decades, be extensible without need for version negotiation, and not require a schema. - -While JSON uses text, CBOR uses binary. CDDL can be used to express CBOR (and JSON) in an easy and unambiguous way. CDDL is defined in (RFC 8610 Concise Data Definition Language). - -## CBOR in Golang (Go) -[Golang](https://golang.org/) is a nickname for the Go programming language. Go is specified in [The Go Programming Language Specification](https://golang.org/ref/spec). - -__[fxamacker/cbor](https://github.com/fxamacker/cbor)__ is a library (written in Go) that encodes and decodes CBOR. The API design of fxamacker/cbor is based on Go's [`encoding/json`](https://golang.org/pkg/encoding/json/). The design and reliability of fxamacker/cbor makes it ideal for encoding and decoding COSE. - -## COSE -COSE is a protocol using CBOR for basic security services. COSE is defined in ([RFC 8152 CBOR Object Signing and Encryption](https://tools.ietf.org/html/rfc8152)). - -COSE describes how to create and process signatures, message authentication codes, and encryption using CBOR for serialization. COSE specification also describes how to represent cryptographic keys using CBOR. COSE is used by WebAuthn. - -## CWT -CBOR Web Token (CWT) is defined in [RFC 8392](http://tools.ietf.org/html/rfc8392). CWT is based on COSE and was derived in part from JSON Web Token (JWT). CWT is a compact way to securely represent claims to be transferred between two parties. - -## WebAuthn -[WebAuthn](https://en.wikipedia.org/wiki/WebAuthn) (Web Authentication) is a web standard for authenticating users to web-based apps and services. It's a core component of FIDO2, the successor of FIDO U2F legacy protocol. - -__[fxamacker/webauthn](https://github.com/fxamacker/webauthn)__ is a library (written in Go) that performs server-side authentication for clients using FIDO2 keys, legacy FIDO U2F keys, tpm, and etc. - -Copyright (c) Faye Amacker and contributors. - -
- -👉 [Comparisons](https://github.com/fxamacker/cbor#comparisons) • [Status](https://github.com/fxamacker/cbor#current-status) • [Design Goals](https://github.com/fxamacker/cbor#design-goals) • [Features](https://github.com/fxamacker/cbor#features) • [Standards](https://github.com/fxamacker/cbor#standards) • [Fuzzing](https://github.com/fxamacker/cbor#fuzzing-and-code-coverage) • [Usage](https://github.com/fxamacker/cbor#usage) • [Security Policy](https://github.com/fxamacker/cbor#security-policy) • [License](https://github.com/fxamacker/cbor#license) diff --git a/vendor/github.com/fxamacker/cbor/v2/CODE_OF_CONDUCT.md b/vendor/github.com/fxamacker/cbor/v2/CODE_OF_CONDUCT.md index bc1f077b..c794b2b0 100644 --- a/vendor/github.com/fxamacker/cbor/v2/CODE_OF_CONDUCT.md +++ b/vendor/github.com/fxamacker/cbor/v2/CODE_OF_CONDUCT.md @@ -1,76 +1,133 @@ + # Contributor Covenant Code of Conduct ## Our Pledge -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. ## Our Standards -Examples of behavior that contributes to creating a positive environment -include: +Examples of behavior that contributes to a positive environment for our +community include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community -Examples of unacceptable behavior by participants include: +Examples of unacceptable behavior include: -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission +* Publishing others' private information, such as a physical or email address, + without their explicit permission * Other conduct which could reasonably be considered inappropriate in a - professional setting + professional setting -## Our Responsibilities +## Enforcement Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. ## Scope -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at faye.github@gmail.com. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. +reported to the community leaders responsible for enforcement at +faye.github@gmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. -[homepage]: https://www.contributor-covenant.org +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/vendor/github.com/fxamacker/cbor/v2/CONTRIBUTING.md b/vendor/github.com/fxamacker/cbor/v2/CONTRIBUTING.md index 1a2321c5..de0965e1 100644 --- a/vendor/github.com/fxamacker/cbor/v2/CONTRIBUTING.md +++ b/vendor/github.com/fxamacker/cbor/v2/CONTRIBUTING.md @@ -1,47 +1,41 @@ # How to contribute -This project started because I needed an easy, small, and crash-proof CBOR library for my [WebAuthn (FIDO2) server library](https://github.com/fxamacker/webauthn). I believe this was the first and still only standalone CBOR library (in Go) that is fuzz tested as of November 10, 2019. +You can contribute by using the library, opening issues, or opening pull requests. -To my surprise, Stefan Tatschner (rumpelsepp) submitted the first 2 issues when I didn't expect this project to be noticed. So I decided to make it more full-featured for others by announcing releases and asking for feedback. Even this document exists because Montgomery Edwards⁴⁴⁸ (x448) opened [issue #22](https://github.com/fxamacker/cbor/issues/22). In other words, you can contribute by opening an issue that helps the project improve. Especially in the early stages. +## Bug reports and security vulnerabilities -When I announced v1.2 on Go Forum, Jakob Borg (calmh) responded with a thumbs up and encouragement. Another project of equal priority needed my time and Jakob's kind words tipped the scale for me to work on this one (speedups for [milestone v1.3](https://github.com/fxamacker/cbor/issues?q=is%3Aopen+is%3Aissue+milestone%3Av1.3.0).) So words of appreciation or encouragement is nice way to contribute to open source projects. +Most issues are tracked publicly on [GitHub](https://github.com/fxamacker/cbor/issues). -Another way is by using this library in your project. It can lead to features that benefit both projects, which is what happened when oasislabs/oasis-core switched to this CBOR libary -- thanks Yawning Angel (yawning) for requesting BinaryMarshaler/BinaryUnmarshaler and Jernej Kos (kostco) for requesting RawMessage! +To report security vulnerabilities, please email faye.github@gmail.com and allow time for the problem to be resolved before disclosing it to the public. For more info, see [Security Policy](https://github.com/fxamacker/cbor#security-policy). -If you'd like to contribute code or send CBOR data, please read on (it can save you time!) +Please do not send data that might contain personally identifiable information, even if you think you have permission. That type of support requires payment and a signed contract where I'm indemnified, held harmless, and defended by you for any data you send to me. -## Private reports -Usually, all issues are tracked publicly on [GitHub](https://github.com/fxamacker/cbor/issues). +## Pull requests -To report security vulnerabilities, please email faye.github@gmail.com and allow time for the problem to be resolved before disclosing it to the public. For more info, see [Security Policy](https://github.com/fxamacker/cbor#security-policy). +Please [create an issue](https://github.com/fxamacker/cbor/issues/new/choose) before you begin work on a PR. The improvement may have already been considered, etc. -Please do not send data that might contain personally identifiable information, even if you think you have permission. That type of support requires payment and a contract where I'm indemnified, held harmless, and defended for any data you send to me. +Pull requests have signing requirements and must not be anonymous. Exceptions are usually made for docs and CI scripts. -## Prerequisites to pull requests -Please [create an issue](https://github.com/fxamacker/cbor/issues/new/choose), if one doesn't already exist, and describe your concern. You'll need a [GitHub account](https://github.com/signup/free) to do this. +See the [Pull Request Template](https://github.com/fxamacker/cbor/blob/master/.github/pull_request_template.md) for details. -If you submit a pull request without creating an issue and getting a response, you risk having your work unused because the bugfix or feature was already done by others and being reviewed before reaching Github. +Pull requests have a greater chance of being approved if: +- it does not reduce speed, increase memory use, reduce security, etc. for people not using the new option or feature. +- it has > 97% code coverage. ## Describe your issue + Clearly describe the issue: * If it's a bug, please provide: **version of this library** and **Go** (`go version`), **unmodified error message**, and describe **how to reproduce it**. Also state **what you expected to happen** instead of the error. * If you propose a change or addition, try to give an example how the improved code could look like or how to use it. * If you found a compilation error, please confirm you're using a supported version of Go. If you are, then provide the output of `go version` first, followed by the complete error message. ## Please don't -Please don't send data containing personally identifiable information, even if you think you have permission. That type of support requires payment and a contract where I'm indemnified, held harmless, and defended for any data you send to me. -Please don't send CBOR data larger than 512 bytes. If you want to send crash-producing CBOR data > 512 bytes, please get my permission before sending it to me. +Please don't send data containing personally identifiable information, even if you think you have permission. That type of support requires payment and a contract where I'm indemnified, held harmless, and defended for any data you send to me. -## Wanted -* Opening issues that are helpful to the project -* Using this library in your project and letting me know -* Sending well-formed CBOR data (<= 512 bytes) that causes crashes (none found yet). -* Sending malformed CBOR data (<= 512 bytes) that causes crashes (none found yet, but bad actors are better than me at breaking things). -* Sending tests or data for unit tests that increase code coverage (currently at 97.8% for v1.2.) -* Pull requests with small changes that are well-documented and easily understandable. -* Sponsors, donations, bounties, subscriptions: I'd like to run uninterrupted fuzzing between releases on a server with dedicated CPUs (after v1.3 or v1.4.) +Please don't send CBOR data larger than 1024 bytes by email. If you want to send crash-producing CBOR data > 1024 bytes by email, please get my permission before sending it to me. ## Credits -This guide used nlohmann/json contribution guidelines for inspiration as suggested in issue #22. +- This guide used nlohmann/json contribution guidelines for inspiration as suggested in issue #22. +- Special thanks to @lukseven for pointing out the contribution guidelines didn't mention signing requirements. diff --git a/vendor/github.com/fxamacker/cbor/v2/README.md b/vendor/github.com/fxamacker/cbor/v2/README.md index 7f513f8f..f6409495 100644 --- a/vendor/github.com/fxamacker/cbor/v2/README.md +++ b/vendor/github.com/fxamacker/cbor/v2/README.md @@ -1,430 +1,342 @@ -# fxamacker/cbor +# CBOR Codec in Go -[![](https://github.com/fxamacker/cbor/workflows/ci/badge.svg)](https://github.com/fxamacker/cbor/actions?query=workflow%3Aci) -[![](https://github.com/fxamacker/cbor/workflows/cover%20%E2%89%A598%25/badge.svg)](https://github.com/fxamacker/cbor/actions?query=workflow%3A%22cover+%E2%89%A598%25%22) -[![](https://github.com/fxamacker/cbor/workflows/linters/badge.svg)](https://github.com/fxamacker/cbor/actions?query=workflow%3Alinters) -[![Go Report Card](https://goreportcard.com/badge/github.com/fxamacker/cbor)](https://goreportcard.com/report/github.com/fxamacker/cbor) -[![](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/release_version_badge.svg?sanitize=1)](https://github.com/fxamacker/cbor/releases) -[![](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/license_badge.svg?sanitize=1)](https://raw.githubusercontent.com/fxamacker/cbor/master/LICENSE) + -[__fxamacker/cbor__](https://github.com/fxamacker/cbor) is a CBOR library in [Go](https://golang.org). It's designed to be safe, fast, small, and easy to use. +[fxamacker/cbor](https://github.com/fxamacker/cbor) is a library for encoding and decoding [CBOR](https://www.rfc-editor.org/info/std94) and [CBOR Sequences](https://www.rfc-editor.org/rfc/rfc8742.html). -Features include CBOR tags, duplicate map key detection, float64→32→16, Go struct tags (`toarray`, `keyasint`, `omitempty`), and a standard API. Each release passes hundreds of tests and 250+ million execs of coverage-guided fuzzing. +CBOR is a [trusted alternative](https://www.rfc-editor.org/rfc/rfc8949.html#name-comparison-of-other-binary-) to JSON, MessagePack, Protocol Buffers, etc.  CBOR is an Internet Standard defined by [IETF STD 94 (RFC 8949)](https://www.rfc-editor.org/info/std94) and is designed to be relevant for decades. -[CBOR](CBOR_GOLANG.md) ([RFC 7049](https://tools.ietf.org/html/rfc7049) & [RFC 8949](https://tools.ietf.org/html/rfc8949)) is a binary data format inspired by JSON and MessagePack. CBOR is an [Internet Standard](https://en.wikipedia.org/wiki/Internet_Standard) by [IETF](https://www.ietf.org) used in W3C [WebAuthn](https://en.wikipedia.org/wiki/WebAuthn), COSE ([RFC 8152](https://tools.ietf.org/html/rfc8152)), CWT ([RFC 8392 CBOR Web Token](https://tools.ietf.org/html/rfc8392)), and CDDL [(RFC 8610)](https://datatracker.ietf.org/doc/html/rfc8610). +`fxamacker/cbor` is used in projects by Arm Ltd., Cisco, Dapper Labs, EdgeX Foundry, Fraunhofer‑AISEC, Linux Foundation, Microsoft, Mozilla, Oasis Protocol, Tailscale, Teleport, [and others](https://github.com/fxamacker/cbor#who-uses-fxamackercbor). -[CBOR Library Installation](https://github.com/x448/cbor/edit/patch-11/README.md#cbor-library-installation) shows how to install and begin using this CBOR encoder and decoder. +See [Quick Start](#quick-start). -## CBOR Security +## fxamacker/cbor -__fxamacker/cbor__ is secure. It rejects malformed CBOR data and can detect duplicate map keys. It doesn't crash when decoding bad CBOR data by having extensive tests, coverage-guided fuzzing, data validation, and avoiding Go's `unsafe` package. +[![](https://github.com/fxamacker/cbor/workflows/ci/badge.svg)](https://github.com/fxamacker/cbor/actions?query=workflow%3Aci) +[![](https://github.com/fxamacker/cbor/workflows/cover%20%E2%89%A596%25/badge.svg)](https://github.com/fxamacker/cbor/actions?query=workflow%3A%22cover+%E2%89%A596%25%22) +[![CodeQL](https://github.com/fxamacker/cbor/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/fxamacker/cbor/actions/workflows/codeql-analysis.yml) +[![](https://img.shields.io/badge/fuzzing-passing-44c010)](#fuzzing-and-code-coverage) +[![Go Report Card](https://goreportcard.com/badge/github.com/fxamacker/cbor)](https://goreportcard.com/report/github.com/fxamacker/cbor) +[![](https://img.shields.io/ossf-scorecard/github.com/fxamacker/cbor?label=openssf%20scorecard)](https://github.com/fxamacker/cbor#fuzzing-and-code-coverage) -| | fxamacker/cbor (all versions) | ugorji/go (1.1.0 - 1.1.7) | -| :--- | :------------------ | :--------------- | -| **Malformed CBOR 1** | 87.5 ns/op, 24 B/op, 2 allocs/op | :boom: fatal error: out of memory | -| **Malformed CBOR 2** | 89.5 ns/op, 24 B/op, 2 allocs/op | :boom: runtime: out of memory: cannot allocate | -| | Correctly rejected bad data in all versions.
Benchmark is from latest release. | :warning: Just 1 decode of 9 bytes can exhaust memory. | +`fxamacker/cbor` is a CBOR codec in full conformance with [IETF STD 94 (RFC 8949)](https://www.rfc-editor.org/info/std94). It also supports CBOR Sequences ([RFC 8742](https://www.rfc-editor.org/rfc/rfc8742.html)) and Extended Diagnostic Notation ([Appendix G of RFC 8610](https://www.rfc-editor.org/rfc/rfc8610.html#appendix-G)). -fxamacker/cbor CBOR safety settings include: MaxNestedLevels, MaxArrayElements, MaxMapPairs, and IndefLength. +Features include full support for CBOR tags, [Core Deterministic Encoding](https://www.rfc-editor.org/rfc/rfc8949.html#name-core-deterministic-encoding), duplicate map key detection, etc. -For more info, see: - - [RFC 8949 Section 10 (Security Considerations)](https://tools.ietf.org/html/rfc8949#section-10) or [RFC 7049 Section 8](https://tools.ietf.org/html/rfc7049#section-8). - - [Go warning](https://golang.org/pkg/unsafe/), "Packages that import unsafe may be non-portable and are not protected by the Go 1 compatibility guidelines." +Struct tags (`toarray`, `keyasint`, `omitempty`) reduce encoded size of structs. -## CBOR Performance +![alt text](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/cbor_struct_tags_api.svg?sanitize=1 "CBOR API and Go Struct Tags") -__fxamacker/cbor__ is fast without sacrificing security. It can be faster than libraries relying on `unsafe` package. +API is mostly same as `encoding/json`, plus interfaces that simplify concurrency for CBOR options. -![alt text](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/cbor_speed_comparison.svg?sanitize=1 "CBOR speed comparison chart") +#### CBOR Security -__Click to expand:__ +Configurable limits help defend against malicious inputs. -
- 👉 CBOR Program Size Comparison

+Decoding 10 bytes of malicious data directly into `[]byte` is efficiently rejected. -__fxamacker/cbor__ produces smaller programs without sacrificing features. - -![alt text](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/cbor_size_comparison.svg?sanitize=1 "CBOR program size comparison chart") +| Codec | Speed (ns/op) | Memory | Allocs | +| :---- | ------------: | -----: | -----: | +| fxamacker/cbor 2.5.0 | 43.95n ± 5% | 32 B/op | 2 allocs/op | +| ugorji/go 1.2.11 | 5353261.00n ± 4% | 67111321 B/op | 13 allocs/op | -

+
More Details and Prior Comparions

-

👉 fxamacker/cbor 2.3.0 (safe) vs ugorji/go 1.2.6 (unsafe)

+Latest comparison used: +- Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}` +- go1.19.10, linux/amd64, i5-13600K (disabled all e-cores, DDR4 @2933) +- go test -bench=. -benchmem -count=20 -fxamacker/cbor 2.3.0 (not using `unsafe`) is faster than ugorji/go 1.2.6 (using `unsafe`). +#### Prior comparisons -``` -name old time/op new time/op delta -DecodeCWTClaims-4 2.06µs ± 1% 1.25µs ± 0% -39.57% (p=0.000 n=10+9) -DecodeCOSE/128-Bit_Symmetric_Key-4 1.47µs ± 1% 0.86µs ± 0% -41.25% (p=0.000 n=9+9) -DecodeCOSE/256-Bit_Symmetric_Key-4 1.50µs ± 2% 0.88µs ± 0% -41.63% (p=0.000 n=10+10) -DecodeCOSE/ECDSA_P256_256-Bit_Key-4 2.22µs ± 2% 1.45µs ± 0% -34.65% (p=0.000 n=10+10) -DecodeWebAuthn-4 1.55µs ± 0% 1.32µs ± 0% -14.97% (p=0.000 n=9+10) -EncodeCWTClaims-4 1.46µs ± 0% 0.78µs ± 0% -46.52% (p=0.000 n=10+10) -EncodeCOSE/128-Bit_Symmetric_Key-4 1.79µs ± 1% 0.91µs ± 0% -49.38% (p=0.000 n=9+10) -EncodeCOSE/256-Bit_Symmetric_Key-4 1.79µs ± 1% 0.91µs ± 0% -49.15% (p=0.000 n=10+10) -EncodeCOSE/ECDSA_P256_256-Bit_Key-4 2.09µs ± 1% 1.14µs ± 0% -45.41% (p=0.000 n=10+10) -EncodeWebAuthn-4 981ns ± 0% 823ns ± 1% -16.05% (p=0.000 n=10+10) - -name old alloc/op new alloc/op delta -DecodeCWTClaims-4 760B ± 0% 176B ± 0% -76.84% (p=0.000 n=10+10) -DecodeCOSE/128-Bit_Symmetric_Key-4 800B ± 0% 240B ± 0% -70.00% (p=0.000 n=10+10) -DecodeCOSE/256-Bit_Symmetric_Key-4 816B ± 0% 256B ± 0% -68.63% (p=0.000 n=10+10) -DecodeCOSE/ECDSA_P256_256-Bit_Key-4 913B ± 0% 352B ± 0% -61.45% (p=0.000 n=10+10) -DecodeWebAuthn-4 1.56kB ± 0% 0.99kB ± 0% -36.41% (p=0.000 n=10+10) -EncodeCWTClaims-4 1.36kB ± 0% 0.18kB ± 0% -87.06% (p=0.000 n=10+10) -EncodeCOSE/128-Bit_Symmetric_Key-4 1.97kB ± 0% 0.22kB ± 0% -88.62% (p=0.000 n=10+10) -EncodeCOSE/256-Bit_Symmetric_Key-4 1.97kB ± 0% 0.24kB ± 0% -87.80% (p=0.000 n=10+10) -EncodeCOSE/ECDSA_P256_256-Bit_Key-4 1.97kB ± 0% 0.32kB ± 0% -83.74% (p=0.000 n=10+10) -EncodeWebAuthn-4 1.31kB ± 0% 1.09kB ± 0% -17.07% (p=0.000 n=10+10) - -name old allocs/op new allocs/op delta -DecodeCWTClaims-4 6.00 ± 0% 6.00 ± 0% ~ (all equal) -DecodeCOSE/128-Bit_Symmetric_Key-4 4.00 ± 0% 4.00 ± 0% ~ (all equal) -DecodeCOSE/256-Bit_Symmetric_Key-4 4.00 ± 0% 4.00 ± 0% ~ (all equal) -DecodeCOSE/ECDSA_P256_256-Bit_Key-4 7.00 ± 0% 7.00 ± 0% ~ (all equal) -DecodeWebAuthn-4 5.00 ± 0% 5.00 ± 0% ~ (all equal) -EncodeCWTClaims-4 4.00 ± 0% 2.00 ± 0% -50.00% (p=0.000 n=10+10) -EncodeCOSE/128-Bit_Symmetric_Key-4 6.00 ± 0% 2.00 ± 0% -66.67% (p=0.000 n=10+10) -EncodeCOSE/256-Bit_Symmetric_Key-4 6.00 ± 0% 2.00 ± 0% -66.67% (p=0.000 n=10+10) -EncodeCOSE/ECDSA_P256_256-Bit_Key-4 6.00 ± 0% 2.00 ± 0% -66.67% (p=0.000 n=10+10) -EncodeWebAuthn-4 4.00 ± 0% 2.00 ± 0% -50.00% (p=0.000 n=10+10) -``` -

+| Codec | Speed (ns/op) | Memory | Allocs | +| :---- | ------------: | -----: | -----: | +| fxamacker/cbor 2.5.0-beta2 | 44.33 ± 2% | 32 B/op | 2 allocs/op | +| fxamacker/cbor 0.1.0 - 2.4.0 | ~44.68 ± 6% | 32 B/op | 2 allocs/op | +| ugorji/go 1.2.10 | 5524792.50 ± 3% | 67110491 B/op | 12 allocs/op | +| ugorji/go 1.1.0 - 1.2.6 | 💥 runtime: | out of memory: | cannot allocate | -Benchmarks used Go 1.15.12, linux_amd64 with data from [RFC 8392 Appendix A.1](https://tools.ietf.org/html/rfc8392#appendix-A.1). Default build options were used for all CBOR libraries. Library init code was put outside the benchmark loop for all libraries compared. +- Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}` +- go1.19.6, linux/amd64, i5-13600K (DDR4) +- go test -bench=. -benchmem -count=20 -## CBOR Library API +
-__fxamacker/cbor__ is easy to use. It provides standard API and interfaces. +#### Design and Feature Highlights -__Standard API__. Function signatures identical to [`encoding/json`](https://golang.org/pkg/encoding/json/) include: -`Marshal`, `Unmarshal`, `NewEncoder`, `NewDecoder`, `(*Encoder).Encode`, and `(*Decoder).Decode`. +Design balances tradeoffs between speed, security, memory, encoded data size, usability, etc. -__Standard Interfaces__. Custom encoding and decoding is handled by implementing: -`BinaryMarshaler`, `BinaryUnmarshaler`, `Marshaler`, and `Unmarshaler`. +
Highlights

-__Predefined Encoding Options__. Encoding options are easy to use and are customizable. +__🚀  Speed__ -```go -func CanonicalEncOptions() EncOptions {} // RFC 7049 Canonical CBOR -func CTAP2EncOptions() EncOptions {} // FIDO2 CTAP2 Canonical CBOR -func CoreDetEncOptions() EncOptions {} // RFC 8949 Core Deterministic Encoding -func PreferredUnsortedEncOptions() EncOptions {} // RFC 8949 Preferred Serialization -``` - -fxamacker/cbor designed to simplify concurrency. CBOR options can be used without creating unintended runtime side-effects. +Encoding and decoding is fast without using Go's `unsafe` package. Slower settings are opt-in. Default limits allow very fast and memory efficient rejection of malformed CBOR data. -## Go Struct Tags +__🔒  Security__ -__fxamacker/cbor__ provides Go struct tags like __`toarray`__ and __`keyasint`__ to save time and reduce encoded size of data. +Decoder has configurable limits that defend against malicious inputs. Duplicate map key detection is supported. By contrast, `encoding/gob` is [not designed to be hardened against adversarial inputs](https://pkg.go.dev/encoding/gob#hdr-Security). -
+Codec passed multiple confidential security assessments in 2022. No vulnerabilities found in subset of codec in a [nonconfidential security assessment](https://github.com/veraison/go-cose/blob/v1.0.0-rc.1/reports/NCC_Microsoft-go-cose-Report_2022-05-26_v1.0.pdf) prepared by NCC Group for Microsoft Corporation. -![alt text](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/cbor_struct_tags_api.svg?sanitize=1 "CBOR API and Go Struct Tags") +__🗜️  Data Size__ -## CBOR Features +Struct tags (`toarray`, `keyasint`, `omitempty`) automatically reduce size of encoded structs. Encoding optionally shrinks float64→32→16 when values fit. -__fxamacker/cbor__ is a full-featured CBOR encoder and decoder. +__:jigsaw:  Usability__ -| | CBOR Feature | Description | -| :--- | :--- | :--- | -| ☑️ | CBOR tags | API supports built-in and user-defined tags. | -| ☑️ | Preferred serialization | Integers encode to fewest bytes. Optional float64 → float32 → float16. | -| ☑️ | Map key sorting | Unsorted, length-first (Canonical CBOR), and bytewise-lexicographic (CTAP2). | -| ☑️ | Duplicate map keys | Always forbid for encoding and option to allow/forbid for decoding. | -| ☑️ | Indefinite length data | Option to allow/forbid for encoding and decoding. | -| ☑️ | Well-formedness | Always checked and enforced. | -| ☑️ | Basic validity checks | Check UTF-8 validity and optionally check duplicate map keys. | -| ☑️ | Security considerations | Prevent integer overflow and resource exhaustion (RFC 8949 Section 10). | +API is mostly same as `encoding/json` plus interfaces that simplify concurrency for CBOR options. Encoding and decoding modes can be created at startup and reused by any goroutines. -## CBOR Library Installation +Presets include Core Deterministic Encoding, Preferred Serialization, CTAP2 Canonical CBOR, etc. -fxamacker/cbor supports Go 1.12 and newer versions. Init the Go module, go get v2, and begin coding. +__📆  Extensibility__ -``` -go mod init github.com/my_name/my_repo -go get github.com/fxamacker/cbor/v2 -``` +Features include CBOR [extension points](https://www.rfc-editor.org/rfc/rfc8949.html#section-7.1) (e.g. CBOR tags) and extensive settings. API has interfaces that allow users to create custom encoding and decoding without modifying this library. -```go -import "github.com/fxamacker/cbor/v2" // imports as cbor -``` +

## Quick Start -🛡️ Use Go's `io.LimitReader` to limit size when decoding very large or indefinite size data. -Import using "/v2" like this: `import "github.com/fxamacker/cbor/v2"`, and -it will import version 2.x as package "cbor" (when using Go modules). +__Install__: `go get github.com/fxamacker/cbor/v2` and `import "github.com/fxamacker/cbor/v2"`. + +### Key Points -Functions with identical signatures to encoding/json include: -`Marshal`, `Unmarshal`, `NewEncoder`, `NewDecoder`, `(*Encoder).Encode`, `(*Decoder).Decode`. +- Encoding and decoding modes are created from options (settings). +- Modes can be created at startup and reused. +- Modes are safe for concurrent use. -__Default Mode__ +### Default Mode -If default options are acceptable, package level functions can be used for encoding and decoding. +Package level functions only use default settings. +They provide the "default mode" of encoding and decoding. ```go +// API matches encoding/json. b, err := cbor.Marshal(v) // encode v to []byte b err := cbor.Unmarshal(b, &v) // decode []byte b to v encoder := cbor.NewEncoder(w) // create encoder with io.Writer w decoder := cbor.NewDecoder(r) // create decoder with io.Reader r ``` -__Modes__ - -If you need to use options or CBOR tags, then you'll want to create a mode. - -"Mode" means defined way of encoding or decoding -- it links the standard API to your CBOR options and CBOR tags. This way, you don't pass around options and the API remains identical to `encoding/json`. - -EncMode and DecMode are interfaces created from EncOptions or DecOptions structs. -For example, `em, err := cbor.EncOptions{...}.EncMode()` or `em, err := cbor.CanonicalEncOptions().EncMode()`. - -EncMode and DecMode use immutable options so their behavior won't accidentally change at runtime. Modes are reusable, safe for concurrent use, and allow fast parallelism. - -__Creating and Using Encoding Modes__ - -💡 Avoid using init(). For best performance, reuse EncMode and DecMode after creating them. - -Most apps will probably create one EncMode and DecMode before init(). There's no limit and each can use different options. - -```go -// Create EncOptions using either struct literal or a function. -opts := cbor.CanonicalEncOptions() - -// If needed, modify opts. For example: opts.Time = cbor.TimeUnix - -// Create reusable EncMode interface with immutable options, safe for concurrent use. -em, err := opts.EncMode() - -// Use EncMode like encoding/json, with same function signatures. -b, err := em.Marshal(v) // encode v to []byte b - -encoder := em.NewEncoder(w) // create encoder with io.Writer w -err := encoder.Encode(v) // encode v to io.Writer w -``` - -Both `em.Marshal(v)` and `encoder.Encode(v)` use encoding options specified during creation of encoding mode `em`. - -__Creating Modes With CBOR Tags__ +Some CBOR-based formats or protocols may require non-default settings. -A TagSet is used to specify CBOR tags. - -```go -em, err := opts.EncMode() // no tags -em, err := opts.EncModeWithTags(ts) // immutable tags -em, err := opts.EncModeWithSharedTags(ts) // mutable shared tags -``` +For example, WebAuthn uses "CTAP2 Canonical CBOR" settings. It is available as a preset. -TagSet and all modes using it are safe for concurrent use. Equivalent API is available for DecMode. +### Presets -__Predefined Encoding Options__ +Presets can be used as-is or as a starting point for custom settings. ```go -func CanonicalEncOptions() EncOptions {} // RFC 7049 Canonical CBOR -func CTAP2EncOptions() EncOptions {} // FIDO2 CTAP2 Canonical CBOR -func CoreDetEncOptions() EncOptions {} // RFC 8949 Core Deterministic Encoding -func PreferredUnsortedEncOptions() EncOptions {} // RFC 8949 Preferred Serialization +// EncOptions is a struct of encoder settings. +func CoreDetEncOptions() EncOptions // RFC 8949 Core Deterministic Encoding +func PreferredUnsortedEncOptions() EncOptions // RFC 8949 Preferred Serialization +func CanonicalEncOptions() EncOptions // RFC 7049 Canonical CBOR +func CTAP2EncOptions() EncOptions // FIDO2 CTAP2 Canonical CBOR ``` -The empty curly braces prevent a syntax highlighting bug on GitHub, please ignore them. - -__Struct Tags (keyasint, toarray, omitempty)__ - -The `keyasint`, `toarray`, and `omitempty` struct tags make it easy to use compact CBOR message formats. Internet standards often use CBOR arrays and CBOR maps with int keys to save space. - -The following sections provide more info: - -* [Struct Tags](#struct-tags-1) -* [Decoding Options](#decoding-options) -* [Encoding Options](#encoding-options) -* [API](#api) -* [Usage](#usage) - -
- -⚓ [Quick Start](#quick-start) • [Status](#current-status) • [Design Goals](#design-goals) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) - -## Current Status -Latest version is v2.3 (May 30, 2021), which has: - -* __Stable API__ – Six codec function signatures will never change. No breaking API changes for other funcs in same major version. -* __Passed all tests__ – v2.x passed all 375+ tests on amd64, arm64, ppc64le and s390x with linux. -* __Passed fuzzing__ – v2.2 passed 459+ million execs in coverage-guided fuzzing on Feb 24, 2020 (release date) -and 3.2+ billion execs on March 7, 2020. v2.3 passed 357+ million execs on May 30, 2021 (and is continuing to fuzz). - -
- -⚓ [Quick Start](#quick-start) • [Status](#current-status) • [Design Goals](#design-goals) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) +Presets are used to create custom modes. -## Design Goals -This library is designed to be a generic CBOR encoder and decoder. It was initially created for a [WebAuthn (FIDO2) server library](https://github.com/fxamacker/webauthn), because existing CBOR libraries (in Go) didn't meet certain criteria in 2019. +### Custom Modes -This library is designed to be: +Modes are created from settings. Once created, modes have immutable settings. -* __Easy__ – API is like `encoding/json` plus `keyasint` and `toarray` struct tags. -* __Small__ – Programs in cisco/senml are 4 MB smaller by switching to this library. In extreme cases programs can be smaller by 9+ MB. No code gen and the only imported pkg is x448/float16 which is maintained by the same team. -* __Safe and reliable__ – No `unsafe` pkg, coverage >95%, coverage-guided fuzzing, and data validation to avoid crashes on malformed or malicious data. Decoder settings include: `MaxNestedLevels`, `MaxArrayElements`, `MaxMapPairs`, and `IndefLength`. +💡 Create the mode at startup and reuse it. It is safe for concurrent use. -Avoiding `unsafe` package has benefits. The `unsafe` package [warns](https://golang.org/pkg/unsafe/): +```Go +// Create encoding mode. +opts := cbor.CoreDetEncOptions() // use preset options as a starting point +opts.Time = cbor.TimeUnix // change any settings if needed +em, err := opts.EncMode() // create an immutable encoding mode -> Packages that import unsafe may be non-portable and are not protected by the Go 1 compatibility guidelines. +// Reuse the encoding mode. It is safe for concurrent use. -All releases prioritize reliability to avoid crashes on decoding malformed CBOR data. See [Fuzzing and Coverage](#fuzzing-and-code-coverage). +// API matches encoding/json. +b, err := em.Marshal(v) // encode v to []byte b +encoder := em.NewEncoder(w) // create encoder with io.Writer w +err := encoder.Encode(v) // encode v to io.Writer w +``` -Competing factors are balanced: +Default mode and custom modes automatically apply struct tags. -* __Speed__ vs __safety__ vs __size__ – to keep size small, avoid code generation. For safety, validate data and avoid Go's `unsafe` pkg. For speed, use safe optimizations such as caching struct metadata. This library is faster than a well-known library that uses `unsafe` and code gen. -* __Standards compliance__ vs __size__ – Supports CBOR RFC 7049 with minor [limitations](#limitations). To limit bloat, CBOR tags are supported but not all tags are built-in. The API allows users to add tags that aren't built-in. The API also allows custom encoding and decoding of user-defined Go types. +### Struct Tags -__Click to expand topic:__ +Struct tags (`toarray`, `keyasint`, `omitempty`) reduce encoded size of structs. -
- Supported CBOR Features (Highlights)

- -| | CBOR Feature | Description | -| :--- | :--- | :--- | -| ☑️ | CBOR tags | API supports built-in and user-defined tags. | -| ☑️ | Preferred serialization | Integers encode to fewest bytes. Optional float64 → float32 → float16. | -| ☑️ | Map key sorting | Unsorted, length-first (Canonical CBOR), and bytewise-lexicographic (CTAP2). | -| ☑️ | Duplicate map keys | Always forbid for encoding and option to allow/forbid for decoding. | -| ☑️ | Indefinite length data | Option to allow/forbid for encoding and decoding. | -| ☑️ | Well-formedness | Always checked and enforced. | -| ☑️ | Basic validity checks | Check UTF-8 validity and optionally check duplicate map keys. | -| ☑️ | Security considerations | Prevent integer overflow and resource exhaustion (RFC 8949 Section 10). | +

Example using struct tags

+ +![alt text](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/cbor_struct_tags_api.svg?sanitize=1 "CBOR API and Go Struct Tags")

-
- v2.0 API Design

- -v2.0 decoupled options from CBOR encoding & decoding functions: - -* More encoding/decoding function signatures are identical to encoding/json. -* More function signatures can remain stable forever. -* More flexibility for evolving internal data types, optimizations, and concurrency. -* Features like CBOR tags can be added without more breaking API changes. -* Options to handle duplicate map keys can be added without more breaking API changes. +Struct tags simplify use of CBOR-based protocols that require CBOR arrays or maps with integer keys. -

+### CBOR Tags -Features not in Go's standard library are usually not added. However, the __`toarray`__ struct tag in __ugorji/go__ was too useful to ignore. It was added in v1.3 when a project mentioned they were using it with CBOR to save disk space. +CBOR tags are specified in a `TagSet`. -
+Custom modes can be created with a `TagSet` to handle CBOR tags. + +```go +em, err := opts.EncMode() // no CBOR tags +em, err := opts.EncModeWithTags(ts) // immutable CBOR tags +em, err := opts.EncModeWithSharedTags(ts) // mutable shared CBOR tags +``` -⚓ [Quick Start](#quick-start) • [Status](#current-status) • [Design Goals](#design-goals) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) +`TagSet` and modes using it are safe for concurrent use. Equivalent API is available for `DecMode`. -## Features +
Example using TagSet and TagOptions

-### Standard API +```go +// Use signedCWT struct defined in "Decoding CWT" example. -Many function signatures are identical to encoding/json, including: -`Marshal`, `Unmarshal`, `NewEncoder`, `NewDecoder`, `(*Encoder).Encode`, `(*Decoder).Decode`. +// Create TagSet (safe for concurrency). +tags := cbor.NewTagSet() +// Register tag COSE_Sign1 18 with signedCWT type. +tags.Add( + cbor.TagOptions{EncTag: cbor.EncTagRequired, DecTag: cbor.DecTagRequired}, + reflect.TypeOf(signedCWT{}), + 18) -`RawMessage` can be used to delay CBOR decoding or precompute CBOR encoding, like `encoding/json`. +// Create DecMode with immutable tags. +dm, _ := cbor.DecOptions{}.DecModeWithTags(tags) -Standard interfaces allow user-defined types to have custom CBOR encoding and decoding. They include: -`BinaryMarshaler`, `BinaryUnmarshaler`, `Marshaler`, and `Unmarshaler`. +// Unmarshal to signedCWT with tag support. +var v signedCWT +if err := dm.Unmarshal(data, &v); err != nil { + return err +} -`Marshaler` and `Unmarshaler` interfaces are satisfied by `MarshalCBOR` and `UnmarshalCBOR` functions using same params and return types as Go's MarshalJSON and UnmarshalJSON. +// Create EncMode with immutable tags. +em, _ := cbor.EncOptions{}.EncModeWithTags(tags) -### Struct Tags +// Marshal signedCWT with tag number. +if data, err := cbor.Marshal(v); err != nil { + return err +} +``` -Support "cbor" and "json" keys in Go's struct tags. If both are specified for the same field, then "cbor" is used. +

-* a different field name can be specified, like encoding/json. -* `omitempty` omits (ignores) field if value is empty, like encoding/json. -* `-` always omits (ignores) field, like encoding/json. -* `keyasint` treats fields as elements of CBOR maps with specified int key. -* `toarray` treats fields as elements of CBOR arrays. +### Functions and Interfaces -See [Struct Tags](#struct-tags-1) for more info. +
Functions and interfaces at a glance

-### CBOR Tags (New in v2.1) +Common functions with same API as `encoding/json`: +- `Marshal`, `Unmarshal` +- `NewEncoder`, `(*Encoder).Encode` +- `NewDecoder`, `(*Decoder).Decode` -There are three broad categories of CBOR tags: +NOTE: `Unmarshal` will return `ExtraneousDataError` if there are remaining bytes +because RFC 8949 treats CBOR data item with remaining bytes as malformed. +- 💡 Use `UnmarshalFirst` to decode first CBOR data item and return any remaining bytes. -* __Default built-in CBOR tags__ currently include tag numbers 0 and 1 (Time). Additional default built-in tags in future releases may include tag numbers 2 and 3 (Bignum). +Other useful functions: +- `Diagnose`, `DiagnoseFirst` produce human-readable [Extended Diagnostic Notation](https://www.rfc-editor.org/rfc/rfc8610.html#appendix-G) from CBOR data. +- `UnmarshalFirst` decodes first CBOR data item and return any remaining bytes. +- `Wellformed` returns true if the the CBOR data item is well-formed. -* __Optional built-in CBOR tags__ may be provided in the future via build flags or optional package(s) to help reduce bloat. +Interfaces identical or comparable to Go `encoding` packages include: +`Marshaler`, `Unmarshaler`, `BinaryMarshaler`, and `BinaryUnmarshaler`. -* __User-defined CBOR tags__ are easy by using TagSet to associate tag numbers to user-defined Go types. +The `RawMessage` type can be used to delay CBOR decoding or precompute CBOR encoding. -### Preferred Serialization +

-Preferred serialization encodes integers and floating-point values using the fewest bytes possible. +### Security Tips -* Integers are always encoded using the fewest bytes possible. -* Floating-point values can optionally encode from float64->float32->float16 when values fit. +🔒 Use Go's `io.LimitReader` to limit size when decoding very large or indefinite size data. -### Compact Data Size +Default limits may need to be increased for systems handling very large data (e.g. blockchains). -The combination of preferred serialization and struct tags (toarray, keyasint, omitempty) allows very compact data size. +`DecOptions` can be used to modify default limits for `MaxArrayElements`, `MaxMapPairs`, and `MaxNestedLevels`. -### Predefined Encoding Options +## Status -Easy-to-use functions (no params) return preset EncOptions struct: -`CanonicalEncOptions`, `CTAP2EncOptions`, `CoreDetEncOptions`, `PreferredUnsortedEncOptions` +v2.5.0 was released on Sunday, August 13, 2023. It is fuzz tested and production quality. -### Encoding Options +__IMPORTANT__: Before upgrading from prior release, please read the notable changes highlighted in the release notes. -Integers always encode to the shortest form that preserves value. By default, time values are encoded without tags. +See latest [releases](https://github.com/fxamacker/cbor/releases) and [v2.5.0 release notes](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) for list of new features and improvements. -Encoding of other data types and map key sort order are determined by encoder options. + -### Additional Features +## Who uses fxamacker/cbor -* Decoder always checks for invalid UTF-8 string errors. -* Decoder always decodes in-place to slices, maps, and structs. -* Decoder tries case-sensitive first and falls back to case-insensitive field name match when decoding to structs. -* Both encoder and decoder support indefinite length CBOR data (["streaming"](https://tools.ietf.org/html/rfc7049#section-2.2)). -* Both encoder and decoder correctly handles nil slice, map, pointer, and interface values. +`fxamacker/cbor` is used in projects by Arm Ltd., Berlin Institute of Health at Charité, Chainlink, Cisco, Confidential Computing Consortium, ConsenSys, Dapper Labs, EdgeX Foundry, F5, Fraunhofer‑AISEC, Linux Foundation, Microsoft, Mozilla, National Cybersecurity Agency of France (govt), Netherlands (govt), Oasis Protocol, Smallstep, Tailscale, Taurus SA, Teleport, TIBCO, and others. -
+Although GitHub only reports around 200 repos depend on this library, that is for v1 (old version). For v2 (current version), GitHub reports [2000+ repositories](https://github.com/fxamacker/cbor/network/dependents?package_id=UGFja2FnZS0yMjcwNDY1OTQ4) depend on fxamacker/cbor. -⚓ [Quick Start](#quick-start) • [Status](#current-status) • [Design Goals](#design-goals) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) +`fxamacker/cbor` passed multiple confidential security assessments. A [nonconfidential security assessment](https://github.com/veraison/go-cose/blob/v1.0.0-rc.1/reports/NCC_Microsoft-go-cose-Report_2022-05-26_v1.0.pdf) (prepared by NCC Group for Microsoft Corporation) includes a subset of fxamacker/cbor v2.4.0 in its scope. ## Standards -This library is a full-featured generic CBOR [(RFC 7049)](https://tools.ietf.org/html/rfc7049) encoder and decoder. Notable CBOR features include: - -| | CBOR Feature | Description | -| :--- | :--- | :--- | -| ☑️ | CBOR tags | API supports built-in and user-defined tags. | -| ☑️ | Preferred serialization | Integers encode to fewest bytes. Optional float64 → float32 → float16. | -| ☑️ | Map key sorting | Unsorted, length-first (Canonical CBOR), and bytewise-lexicographic (CTAP2). | -| ☑️ | Duplicate map keys | Always forbid for encoding and option to allow/forbid for decoding. | -| ☑️ | Indefinite length data | Option to allow/forbid for encoding and decoding. | -| ☑️ | Well-formedness | Always checked and enforced. | -| ☑️ | Basic validity checks | Check UTF-8 validity and optionally check duplicate map keys. | -| ☑️ | Security considerations | Prevent integer overflow and resource exhaustion (RFC 8949 Section 10). | +This library is a full-featured generic CBOR [(RFC 8949)](https://tools.ietf.org/html/rfc8949) encoder and decoder. Notable CBOR features include: -See the Features section for list of [Encoding Options](#encoding-options) and [Decoding Options](#decoding-options). +| CBOR Feature | Description | +| :--- | :--- | +| CBOR tags | API supports built-in and user-defined tags. | +| Preferred serialization | Integers encode to fewest bytes. Optional float64 → float32 → float16. | +| Map key sorting | Unsorted, length-first (Canonical CBOR), and bytewise-lexicographic (CTAP2). | +| Duplicate map keys | Always forbid for encoding and option to allow/forbid for decoding. | +| Indefinite length data | Option to allow/forbid for encoding and decoding. | +| Well-formedness | Always checked and enforced. | +| Basic validity checks | Optionally check UTF-8 validity and duplicate map keys. | +| Security considerations | Prevent integer overflow and resource exhaustion (RFC 8949 Section 10). | Known limitations are noted in the [Limitations section](#limitations). @@ -434,15 +346,13 @@ Decoder checks for all required well-formedness errors, including all "subkinds" After well-formedness is verified, basic validity errors are handled as follows: -* Invalid UTF-8 string: Decoder always checks and returns invalid UTF-8 string error. +* Invalid UTF-8 string: Decoder has option to check and return invalid UTF-8 string error. This check is enabled by default. * Duplicate keys in a map: Decoder has options to ignore or enforce rejection of duplicate map keys. When decoding well-formed CBOR arrays and maps, decoder saves the first error it encounters and continues with the next item. Options to handle this differently may be added in the future. By default, decoder treats time values of floating-point NaN and Infinity as if they are CBOR Null or CBOR Undefined. -See [Options](#options) section for detailed settings or [Features](#features) section for a summary of options. - __Click to expand topic:__
@@ -461,12 +371,12 @@ APF suffix means "Allow Partial Fill" so the destination map or struct can conta
Tag Validity

-This library checks tag validity for built-in tags (currently tag numbers 0 and 1): +This library checks tag validity for built-in tags (currently tag numbers 0, 1, 2, 3, and 55799): * Inadmissible type for tag content * Inadmissible value for tag content -Unknown tag data items (not tag number 0 or 1) are handled in two ways: +Unknown tag data items (not tag number 0, 1, 2, 3, or 55799) are handled in two ways: * When decoding into an empty interface, unknown tag data item will be decoded into `cbor.Tag` data type, which contains tag number and tag content. The tag content will be decoded into the default Go data type for the CBOR data type. * When decoding into other Go types, unknown tag data item is decoded into the specified Go type. If Go type is registered with a tag number, the tag number can optionally be verified. @@ -483,623 +393,60 @@ If any of these limitations prevent you from using this library, please open an * CBOR `Undefined` (0xf7) value decodes to Go's `nil` value. CBOR `Null` (0xf6) more closely matches Go's `nil`. * CBOR map keys with data types not supported by Go for map keys are ignored and an error is returned after continuing to decode remaining items. -* When using io.Reader interface to read very large or indefinite length CBOR data, Go's `io.LimitReader` should be used to limit size. - -


- -⚓ [Quick Start](#quick-start) • [Status](#current-status) • [Design Goals](#design-goals) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) - -## API -Many function signatures are identical to Go's encoding/json, such as: -`Marshal`, `Unmarshal`, `NewEncoder`, `NewDecoder`, `(*Encoder).Encode`, and `(*Decoder).Decode`. - -Interfaces identical or comparable to Go's encoding, encoding/json, or encoding/gob include: -`Marshaler`, `Unmarshaler`, `BinaryMarshaler`, and `BinaryUnmarshaler`. - -Like `encoding/json`, `RawMessage` can be used to delay CBOR decoding or precompute CBOR encoding. - -"Mode" in this API means defined way of encoding or decoding -- it links the standard API to CBOR options and CBOR tags. - -EncMode and DecMode are interfaces created from EncOptions or DecOptions structs. -For example, `em, err := cbor.EncOptions{...}.EncMode()` or `em, err := cbor.CanonicalEncOptions().EncMode()`. - -EncMode and DecMode use immutable options so their behavior won't accidentally change at runtime. Modes are intended to be reused and are safe for concurrent use. - -__API for Default Mode__ - -If default options are acceptable, then you don't need to create EncMode or DecMode. - -```go -Marshal(v interface{}) ([]byte, error) -NewEncoder(w io.Writer) *Encoder - -Unmarshal(data []byte, v interface{}) error -NewDecoder(r io.Reader) *Decoder -``` - -__API for Creating & Using Encoding Modes__ - -```go -// EncMode interface uses immutable options and is safe for concurrent use. -type EncMode interface { - Marshal(v interface{}) ([]byte, error) - NewEncoder(w io.Writer) *Encoder - EncOptions() EncOptions // returns copy of options -} - -// EncOptions specifies encoding options. -type EncOptions struct { -... -} - -// EncMode returns an EncMode interface created from EncOptions. -func (opts EncOptions) EncMode() (EncMode, error) {} - -// EncModeWithTags returns EncMode with options and tags that are both immutable. -func (opts EncOptions) EncModeWithTags(tags TagSet) (EncMode, error) {} - -// EncModeWithSharedTags returns EncMode with immutable options and mutable shared tags. -func (opts EncOptions) EncModeWithSharedTags(tags TagSet) (EncMode, error) {} -``` - -The empty curly braces prevent a syntax highlighting bug, please ignore them. - -__API for Predefined Encoding Options__ - -```go -func CanonicalEncOptions() EncOptions {} // RFC 7049 Canonical CBOR -func CTAP2EncOptions() EncOptions {} // FIDO2 CTAP2 Canonical CBOR -func CoreDetEncOptions() EncOptions {} // RFC 8949 Core Deterministic Encoding -func PreferredUnsortedEncOptions() EncOptions {} // RFC 8949 Preferred Serialization -``` - -__API for Creating & Using Decoding Modes__ - -```go -// DecMode interface uses immutable options and is safe for concurrent use. -type DecMode interface { - Unmarshal(data []byte, v interface{}) error - NewDecoder(r io.Reader) *Decoder - DecOptions() DecOptions // returns copy of options -} - -// DecOptions specifies decoding options. -type DecOptions struct { -... -} - -// DecMode returns a DecMode interface created from DecOptions. -func (opts DecOptions) DecMode() (DecMode, error) {} - -// DecModeWithTags returns DecMode with options and tags that are both immutable. -func (opts DecOptions) DecModeWithTags(tags TagSet) (DecMode, error) {} - -// DecModeWithSharedTags returns DecMode with immutable options and mutable shared tags. -func (opts DecOptions) DecModeWithSharedTags(tags TagSet) (DecMode, error) {} -``` - -The empty curly braces prevent a syntax highlighting bug, please ignore them. - -__API for Using CBOR Tags__ - -`TagSet` can be used to associate user-defined Go type(s) to tag number(s). It's also used to create EncMode or DecMode. For example, `em := EncOptions{...}.EncModeWithTags(ts)` or `em := EncOptions{...}.EncModeWithSharedTags(ts)`. This allows every standard API exported by em (like `Marshal` and `NewEncoder`) to use the specified tags automatically. - -`Tag` and `RawTag` can be used to encode/decode a tag number with a Go value, but `TagSet` is generally recommended. - -```go -type TagSet interface { - // Add adds given tag number(s), content type, and tag options to TagSet. - Add(opts TagOptions, contentType reflect.Type, num uint64, nestedNum ...uint64) error - - // Remove removes given tag content type from TagSet. - Remove(contentType reflect.Type) -} -``` - -`Tag` and `RawTag` types can also be used to encode/decode tag number with Go value. - -```go -type Tag struct { - Number uint64 - Content interface{} -} - -type RawTag struct { - Number uint64 - Content RawMessage -} -``` - -See [API docs (godoc.org)](https://godoc.org/github.com/fxamacker/cbor) for more details and more functions. See [Usage section](#usage) for usage and code examples. - -
- -⚓ [Quick Start](#quick-start) • [Status](#current-status) • [Design Goals](#design-goals) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) - -## Options - -Struct tags, decoding options, and encoding options. - -### Struct Tags - -This library supports both "cbor" and "json" key for some (not all) struct tags. If "cbor" and "json" keys are both present for the same field, then "cbor" key will be used. - -| Key | Format Str | Scope | Description | -| --- | ---------- | ----- | ------------| -| cbor or json | "myName" | field | Name of field to use such as "myName", etc. like encoding/json. | -| cbor or json | ",omitempty" | field | Omit (ignore) this field if value is empty, like encoding/json. | -| cbor or json | "-" | field | Omit (ignore) this field always, like encoding/json. | -| cbor | ",keyasint" | field | Treat field as an element of CBOR map with specified int as key. | -| cbor | ",toarray" | struct | Treat each field as an element of CBOR array. This automatically disables "omitempty" and "keyasint" for all fields in the struct. | - -The "keyasint" struct tag requires an integer key to be specified: - -``` -type myStruct struct { - MyField int64 `cbor:-1,keyasint,omitempty` - OurField string `cbor:0,keyasint,omitempty` - FooField Foo `cbor:5,keyasint,omitempty` - BarField Bar `cbor:hello,omitempty` - ... -} -``` - -The "toarray" struct tag requires a special field "_" (underscore) to indicate "toarray" applies to the entire struct: - -``` -type myStruct struct { - _ struct{} `cbor:",toarray"` - MyField int64 - OurField string - ... -} -``` - -__Click to expand:__ - -
- Example Using CBOR Web Tokens

- -![alt text](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/cbor_struct_tags_api.svg?sanitize=1 "CBOR API and Go Struct Tags") - -

- -### Decoding Options - -| DecOptions.TimeTag | Description | -| ------------------ | ----------- | -| DecTagIgnored (default) | Tag numbers are ignored (if present) for time values. | -| DecTagOptional | Tag numbers are only checked for validity if present for time values. | -| DecTagRequired | Tag numbers must be provided for time values except for CBOR Null and CBOR Undefined. | - -The following CBOR time values are decoded as Go's "zero time instant": - -* CBOR Null -* CBOR Undefined -* CBOR floating-point NaN -* CBOR floating-point Infinity - -Go's `time` package provides `IsZero` function, which reports whether t represents "zero time instant" -(January 1, year 1, 00:00:00 UTC). - -
- -| DecOptions.DupMapKey | Description | -| -------------------- | ----------- | -| DupMapKeyQuiet (default) | turns off detection of duplicate map keys. It uses a "keep fastest" method by choosing either "keep first" or "keep last" depending on the Go data type. | -| DupMapKeyEnforcedAPF | enforces detection and rejection of duplidate map keys. Decoding stops immediately and returns `DupMapKeyError` when the first duplicate key is detected. The error includes the duplicate map key and the index number. | - -`DupMapKeyEnforcedAPF` uses "Allow Partial Fill" so the destination map or struct can contain some decoded values at the time of error. Users can respond to the `DupMapKeyError` by discarding the partially filled result if that's required by their protocol. - -
- -| DecOptions.IndefLength | Description | -| ---------------------- | ----------- | -|IndefLengthAllowed (default) | allow indefinite length data | -|IndefLengthForbidden | forbid indefinite length data | - -
- -| DecOptions.TagsMd | Description | -| ----------------- | ----------- | -|TagsAllowed (default) | allow CBOR tags (major type 6) | -|TagsForbidden | forbid CBOR tags (major type 6) | - -
- -| DecOptions.MaxNestedLevels | Description | -| -------------------------- | ----------- | -| 32 (default) | allowed setting is [4, 256] | - -
- -| DecOptions.MaxArrayElements | Description | -| --------------------------- | ----------- | -| 131072 (default) | allowed setting is [16, 2147483647] | - -
- -| DecOptions.MaxMapPairs | Description | -| ---------------------- | ----------- | -| 131072 (default) | allowed setting is [16, 2147483647] | - -### Encoding Options - -__Integers always encode to the shortest form that preserves value__. Encoding of other data types and map key sort order are determined by encoding options. - -These functions are provided to create and return a modifiable EncOptions struct with predefined settings. - -| Predefined EncOptions | Description | -| --------------------- | ----------- | -| CanonicalEncOptions() |[Canonical CBOR (RFC 7049 Section 3.9)](https://tools.ietf.org/html/rfc7049#section-3.9). | -| CTAP2EncOptions() |[CTAP2 Canonical CBOR (FIDO2 CTAP2)](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#ctap2-canonical-cbor-encoding-form). | -| PreferredUnsortedEncOptions() |Unsorted, encode float64->float32->float16 when values fit, NaN values encoded as float16 0x7e00. | -| CoreDetEncOptions() |PreferredUnsortedEncOptions() + map keys are sorted bytewise lexicographic. | - -
- -| EncOptions.Sort | Description | -| --------------- | ----------- | -| SortNone (default) |No sorting for map keys. | -| SortLengthFirst |Length-first map key ordering. | -| SortBytewiseLexical |Bytewise lexicographic map key ordering | -| SortCanonical |(alias) Same as SortLengthFirst [(RFC 7049 Section 3.9)](https://tools.ietf.org/html/rfc7049#section-3.9) | -| SortCTAP2 |(alias) Same as SortBytewiseLexical [(CTAP2 Canonical CBOR)](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#ctap2-canonical-cbor-encoding-form). | -| SortCoreDeterministic |(alias) Same as SortBytewiseLexical. | - -
- -| EncOptions.Time | Description | -| --------------- | ----------- | -| TimeUnix (default) | (seconds) Encode as integer. | -| TimeUnixMicro | (microseconds) Encode as floating-point. ShortestFloat option determines size. | -| TimeUnixDynamic | (seconds or microseconds) Encode as integer if time doesn't have fractional seconds, otherwise encode as floating-point rounded to microseconds. | -| TimeRFC3339 | (seconds) Encode as RFC 3339 formatted string. | -| TimeRFC3339Nano | (nanoseconds) Encode as RFC3339 formatted string. | - -
- -| EncOptions.TimeTag | Description | -| ------------------ | ----------- | -| EncTagNone (default) | Tag number will not be encoded for time values. | -| EncTagRequired | Tag number (0 or 1) will be encoded unless time value is undefined/zero-instant. | - -__Undefined Time Values__ - -By default, undefined (zero instant) time values will encode as CBOR Null without tag number for both EncTagNone and EncTagRequired. Although CBOR Undefined might be technically more correct for EncTagRequired, CBOR Undefined might not be supported by other generic decoders and it isn't supported by JSON. - -Go's `time` package provides `IsZero` function, which reports whether t represents the zero time instant, January 1, year 1, 00:00:00 UTC. - -
- -__Floating-Point Options__ - -Encoder has 3 types of options for floating-point data: ShortestFloatMode, InfConvertMode, and NaNConvertMode. - -| EncOptions.ShortestFloat | Description | -| ------------------------ | ----------- | -| ShortestFloatNone (default) | No size conversion. Encode float32 and float64 to CBOR floating-point of same bit-size. | -| ShortestFloat16 | Encode float64 -> float32 -> float16 ([IEEE 754 binary16](https://en.wikipedia.org/wiki/Half-precision_floating-point_format)) when values fit. | - -Conversions for infinity and NaN use InfConvert and NaNConvert settings. - -| EncOptions.InfConvert | Description | -| --------------------- | ----------- | -| InfConvertFloat16 (default) | Convert +- infinity to float16 since they always preserve value (recommended) | -| InfConvertNone |Don't convert +- infinity to other representations -- used by CTAP2 Canonical CBOR | - -
- -| EncOptions.NaNConvert | Description | -| --------------------- | ----------- | -| NaNConvert7e00 (default) | Encode to 0xf97e00 (CBOR float16 = 0x7e00) -- used by RFC 7049 Canonical CBOR. | -| NaNConvertNone | Don't convert NaN to other representations -- used by CTAP2 Canonical CBOR. | -| NaNConvertQuiet | Force quiet bit = 1 and use shortest form that preserves NaN payload. | -| NaNConvertPreserveSignal | Convert to smallest form that preserves value (quit bit unmodified and NaN payload preserved). | - -
- -| EncOptions.IndefLength | Description | -| ---------------------- | ----------- | -|IndefLengthAllowed (default) | allow indefinite length data | -|IndefLengthForbidden | forbid indefinite length data | - -
- -| EncOptions.TagsMd | Description | -| ----------------- | ----------- | -|TagsAllowed (default) | allow CBOR tags (major type 6) | -|TagsForbidden | forbid CBOR tags (major type 6) | - - -### Tag Options - -TagOptions specifies how encoder and decoder handle tag number registered with TagSet. - -| TagOptions.DecTag | Description | -| ------------------ | ----------- | -| DecTagIgnored (default) | Tag numbers are ignored (if present). | -| DecTagOptional | Tag numbers are only checked for validity if present. | -| DecTagRequired | Tag numbers must be provided except for CBOR Null and CBOR Undefined. | - -
- -| TagOptions.EncTag | Description | -| ------------------ | ----------- | -| EncTagNone (default) | Tag number will not be encoded. | -| EncTagRequired | Tag number will be encoded. | - -
- -⚓ [Quick Start](#quick-start) • [Status](#current-status) • [Design Goals](#design-goals) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) - -## Usage -🛡️ Use Go's `io.LimitReader` to limit size when decoding very large or indefinite size data. - -Functions with identical signatures to encoding/json include: -`Marshal`, `Unmarshal`, `NewEncoder`, `NewDecoder`, `(*Encoder).Encode`, `(*Decoder).Decode`. - -__Default Mode__ - -If default options are acceptable, package level functions can be used for encoding and decoding. - -```go -b, err := cbor.Marshal(v) // encode v to []byte b - -err := cbor.Unmarshal(b, &v) // decode []byte b to v - -encoder := cbor.NewEncoder(w) // create encoder with io.Writer w - -decoder := cbor.NewDecoder(r) // create decoder with io.Reader r -``` - -__Modes__ - -If you need to use options or CBOR tags, then you'll want to create a mode. - -"Mode" means defined way of encoding or decoding -- it links the standard API to your CBOR options and CBOR tags. This way, you don't pass around options and the API remains identical to `encoding/json`. - -EncMode and DecMode are interfaces created from EncOptions or DecOptions structs. -For example, `em, err := cbor.EncOptions{...}.EncMode()` or `em, err := cbor.CanonicalEncOptions().EncMode()`. - -EncMode and DecMode use immutable options so their behavior won't accidentally change at runtime. Modes are reusable, safe for concurrent use, and allow fast parallelism. - -__Creating and Using Encoding Modes__ - -EncMode is an interface ([API](#api)) created from EncOptions struct. EncMode uses immutable options after being created and is safe for concurrent use. For best performance, EncMode should be reused. - -```go -// Create EncOptions using either struct literal or a function. -opts := cbor.CanonicalEncOptions() - -// If needed, modify opts. For example: opts.Time = cbor.TimeUnix - -// Create reusable EncMode interface with immutable options, safe for concurrent use. -em, err := opts.EncMode() - -// Use EncMode like encoding/json, with same function signatures. -b, err := em.Marshal(v) // encode v to []byte b - -encoder := em.NewEncoder(w) // create encoder with io.Writer w -err := encoder.Encode(v) // encode v to io.Writer w -``` - -__Struct Tags (keyasint, toarray, omitempty)__ - -The `keyasint`, `toarray`, and `omitempty` struct tags make it easy to use compact CBOR message formats. Internet standards often use CBOR arrays and CBOR maps with int keys to save space. - -
- -![alt text](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/cbor_struct_tags_api.svg?sanitize=1 "CBOR API and Struct Tags") - -
- -__Decoding CWT (CBOR Web Token)__ using `keyasint` and `toarray` struct tags: - -```go -// Signed CWT is defined in RFC 8392 -type signedCWT struct { - _ struct{} `cbor:",toarray"` - Protected []byte - Unprotected coseHeader - Payload []byte - Signature []byte -} - -// Part of COSE header definition -type coseHeader struct { - Alg int `cbor:"1,keyasint,omitempty"` - Kid []byte `cbor:"4,keyasint,omitempty"` - IV []byte `cbor:"5,keyasint,omitempty"` -} - -// data is []byte containing signed CWT - -var v signedCWT -if err := cbor.Unmarshal(data, &v); err != nil { - return err -} -``` - -__Encoding CWT (CBOR Web Token)__ using `keyasint` and `toarray` struct tags: - -```go -// Use signedCWT struct defined in "Decoding CWT" example. - -var v signedCWT -... -if data, err := cbor.Marshal(v); err != nil { - return err -} -``` - -__Encoding and Decoding CWT (CBOR Web Token) with CBOR Tags__ - -```go -// Use signedCWT struct defined in "Decoding CWT" example. - -// Create TagSet (safe for concurrency). -tags := cbor.NewTagSet() -// Register tag COSE_Sign1 18 with signedCWT type. -tags.Add( - cbor.TagOptions{EncTag: cbor.EncTagRequired, DecTag: cbor.DecTagRequired}, - reflect.TypeOf(signedCWT{}), - 18) - -// Create DecMode with immutable tags. -dm, _ := cbor.DecOptions{}.DecModeWithTags(tags) - -// Unmarshal to signedCWT with tag support. -var v signedCWT -if err := dm.Unmarshal(data, &v); err != nil { - return err -} - -// Create EncMode with immutable tags. -em, _ := cbor.EncOptions{}.EncModeWithTags(tags) - -// Marshal signedCWT with tag number. -if data, err := cbor.Marshal(v); err != nil { - return err -} -``` - -For more examples, see [examples_test.go](example_test.go). - -
- -⚓ [Quick Start](#quick-start) • [Status](#current-status) • [Design Goals](#design-goals) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) - -## Comparisons - -Comparisons are between this newer library and a well-known library that had 1,000+ stars before this library was created. Default build settings for each library were used for all comparisons. - -__This library is safer__. Small malicious CBOR messages are rejected quickly before they exhaust system resources. - -| | **fxamacker/cbor (1.0 - 2.x)** | **ugorji/go (1.1.0 - 1.1.7)** | -| :--- | :------------------ | :--------------- | -| **Malformed CBOR 1** | 59.8 ns/op, 32 B/op, 1 allocs/op | :boom: fatal error: out of memory | -| **Malformed CBOR 2** | 149 ns/op, 128 B/op, 3 allocs/op | :boom: runtime: out of memory: cannot allocate | -| | Correctly rejected bad data. | :warning: Only 1 decode < 10 bytes produces fatal error. | - -__This library is smaller__. Programs like senmlCat can be 4 MB smaller by switching to this library. Programs using more complex CBOR data types can be 9.2 MB smaller. - -![alt text](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/cbor_size_comparison.svg?sanitize=1 "CBOR speed comparison chart") - - -__This library is faster__ for encoding and decoding CBOR Web Token (CWT). However, speed is only one factor and it can vary depending on data types and sizes. Unlike the other library, this one doesn't use Go's ```unsafe``` package or code gen. - -![alt text](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/cbor_speed_comparison.svg?sanitize=1 "CBOR speed comparison chart") - -__This library uses less memory__ for encoding and decoding CBOR Web Token (CWT) using test data from RFC 8392 A.1. - -| | fxamacker/cbor 2.2 | ugorji/go 1.1.7 | -| :--- | :--- | :--- | -| Encode CWT | 176 bytes/op     2 allocs/op | 1424 bytes/op     4 allocs/op | -| Decode CWT | 176 bytes/op     6 allocs/op |   568 bytes/op     6 allocs/op | - -Doing your own comparisons is highly recommended. Use your most common message sizes and data types. - -
- -⚓ [Quick Start](#quick-start) • [Status](#current-status) • [Design Goals](#design-goals) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) - -## Benchmarks - -Go structs are faster than maps with string keys: - -* decoding into struct is >28% faster than decoding into map. -* encoding struct is >35% faster than encoding map. - -Go structs with `keyasint` struct tag are faster than maps with integer keys: - -* decoding into struct is >28% faster than decoding into map. -* encoding struct is >34% faster than encoding map. - -Go structs with `toarray` struct tag are faster than slice: - -* decoding into struct is >15% faster than decoding into slice. -* encoding struct is >12% faster than encoding slice. - -Doing your own benchmarks is highly recommended. Use your most common message sizes and data types. - -See [Benchmarks for fxamacker/cbor](CBOR_BENCHMARKS.md). +* When decoding registered CBOR tag data to interface type, decoder creates a pointer to registered Go type matching CBOR tag number. Requiring a pointer for this is a Go limitation. ## Fuzzing and Code Coverage -__Over 375 tests__ must pass on 4 architectures before tagging a release. They include all RFC 7049 examples, bugs found by fuzzing, maliciously crafted CBOR data, and over 87 tests with malformed data. - -__Code coverage__ must not fall below 95% when tagging a release. Code coverage is 98.6% (`go test -cover`) for cbor v2.2 which is among the highest for libraries (in Go) of this type. - -__Coverage-guided fuzzing__ must pass 250+ million execs before tagging a release. Fuzzing uses [fxamacker/cbor-fuzz](https://github.com/fxamacker/cbor-fuzz). Default corpus has: - -* 2 files related to WebAuthn (FIDO U2F key). -* 3 files with custom struct. -* 9 files with [CWT examples (RFC 8392 Appendix A)](https://tools.ietf.org/html/rfc8392#appendix-A). -* 17 files with [COSE examples (RFC 8152 Appendix B & C)](https://github.com/cose-wg/Examples/tree/master/RFC8152). -* 81 files with [CBOR examples (RFC 7049 Appendix A) ](https://tools.ietf.org/html/rfc7049#appendix-A). It excludes 1 errata first reported in [issue #46](https://github.com/fxamacker/cbor/issues/46). +__Code coverage__ must not fall below 95% when tagging a release. Code coverage is above 96% (`go test -cover`) for fxamacker/cbor v2.5. -Over 1,100 files (corpus) are used for fuzzing because it includes fuzz-generated corpus. - -To prevent excessive delays, fuzzing is not restarted for a release if changes are limited to docs and comments. +__Coverage-guided fuzzing__ must pass billions of execs using before tagging a release. Fuzzing is done using nonpublic code which may eventually get merged into this project. Until then, reports like OpenSSF Scorecard can't detect fuzz tests being used by this project.
-⚓ [Quick Start](#quick-start) • [Status](#current-status) • [Design Goals](#design-goals) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) - ## Versions and API Changes This project uses [Semantic Versioning](https://semver.org), so the API is always backwards compatible unless the major version number changes. These functions have signatures identical to encoding/json and they will likely never change even after major new releases: `Marshal`, `Unmarshal`, `NewEncoder`, `NewDecoder`, `(*Encoder).Encode`, and `(*Decoder).Decode`. -Newly added API documented as "subject to change" are excluded from SemVer. - -Newly added API in the master branch that has never been release tagged are excluded from SemVer. +Exclusions from SemVer: +- Newly added API documented as "subject to change". +- Newly added API in the master branch that has never been release tagged. +- Bug fixes that change behavior (e.g. return error that was missed in prior version) if function parameters are unchanged. We try to highlight these in the release notes. ## Code of Conduct + This project has adopted the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). Contact [faye.github@gmail.com](mailto:faye.github@gmail.com) with any questions or comments. ## Contributing -Please refer to [How to Contribute](CONTRIBUTING.md). -## Security Policy -Security fixes are provided for the latest released version of fxamacker/cbor. +Please open an issue before beginning work on a PR. The improvement may have already been considered, etc. -For the full text of the Security Policy, see [SECURITY.md](SECURITY.md). +For more info, see [How to Contribute](CONTRIBUTING.md). -## Disclaimers -Phrases like "no crashes", "doesn't crash", and "is secure" mean there are no known crash bugs in the latest version based on results of unit tests and coverage-guided fuzzing. They don't imply the software is 100% bug-free or 100% invulnerable to all known and unknown attacks. +## Security Policy -Please read the license for additional disclaimers and terms. +Security fixes are provided for the latest released version of fxamacker/cbor. -## Special Thanks +For the full text of the Security Policy, see [SECURITY.md](SECURITY.md). -__Making this library better__ +## Acknowledgements -* Stefan Tatschner for using this library in [sep](https://git.sr.ht/~rumpelsepp/sep), being the 1st to discover my CBOR library, requesting time.Time in issue #1, and submitting this library in a [PR to cbor.io](https://github.com/cbor/cbor.github.io/pull/56) on Aug 12, 2019. -* Yawning Angel for using this library to [oasis-core](https://github.com/oasislabs/oasis-core), and requesting BinaryMarshaler in issue #5. -* Jernej Kos for requesting RawMessage in issue #11 and offering feedback on v2.1 API for CBOR tags. -* ZenGround0 for using this library in [go-filecoin](https://github.com/filecoin-project/go-filecoin), filing "toarray" bug in issue #129, and requesting -CBOR BSTR <--> Go array in #133. -* Keith Randall for [fixing Go bugs and providing workarounds](https://github.com/golang/go/issues/36400) so we don't have to wait for new versions of Go. +Many thanks to all the contributors on this project! -__Help clarifying CBOR RFC 7049 or 7049bis (7049bis is the draft of RFC 8949)__ +I'm especially grateful to Bastian Müller and Dieter Shirley for suggesting and collaborating on CBOR stream mode, and much more. -* Carsten Bormann for RFC 7049 (CBOR), adding this library to cbor.io, his fast confirmation to my RFC 7049 errata, approving my pull request to 7049bis, and his patience when I misread a line in 7049bis. -* Laurence Lundblade for his help on the IETF mailing list for 7049bis and for pointing out on a CBORbis issue that CBOR Undefined might be problematic translating to JSON. -* Jeffrey Yasskin for his help on the IETF mailing list for 7049bis. +I'm very grateful to Stefan Tatschner, Yawning Angel, Jernej Kos, x448, ZenGround0, and Jakob Borg for their contributions or support in the very early days. -__Words of encouragement and support__ +This library clearly wouldn't be possible without Carsten Bormann authoring CBOR RFCs. -* Jakob Borg for his words of encouragement about this library at Go Forum. This is especially appreciated in the early stages when there's a lot of rough edges. +Special thanks to Laurence Lundblade and Jeffrey Yasskin for their help on IETF mailing list or at [7049bis](https://github.com/cbor-wg/CBORbis). +This library uses `x448/float16` which used to be included. Now as a standalone package, `x448/float16` is useful to other projects as well. ## License -Copyright © 2019-2021 [Faye Amacker](https://github.com/fxamacker). +Copyright © 2019-2023 [Faye Amacker](https://github.com/fxamacker). fxamacker/cbor is licensed under the MIT License. See [LICENSE](LICENSE) for the full license text.
- -⚓ [Quick Start](#quick-start) • [Status](#current-status) • [Design Goals](#design-goals) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) diff --git a/vendor/github.com/fxamacker/cbor/v2/bench_test.go b/vendor/github.com/fxamacker/cbor/v2/bench_test.go index cd36ee4c..5445db23 100644 --- a/vendor/github.com/fxamacker/cbor/v2/bench_test.go +++ b/vendor/github.com/fxamacker/cbor/v2/bench_test.go @@ -5,7 +5,7 @@ package cbor import ( "bytes" - "io/ioutil" + "io" "reflect" "testing" ) @@ -211,6 +211,54 @@ func BenchmarkUnmarshal(b *testing.B) { } } +func BenchmarkUnmarshalFirst(b *testing.B) { + // Random trailing data + trailingData := hexDecode("4a6b0f4718c73f391091ea1c") + for _, bm := range decodeBenchmarks { + for _, t := range bm.decodeToTypes { + name := "CBOR " + bm.name + " to Go " + t.String() + if t.Kind() == reflect.Struct { + name = "CBOR " + bm.name + " to Go " + t.Kind().String() + } + data := make([]byte, 0, len(bm.cborData)+len(trailingData)) + data = append(data, bm.cborData...) + data = append(data, trailingData...) + b.Run(name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + vPtr := reflect.New(t).Interface() + if _, err := UnmarshalFirst(data, vPtr); err != nil { + b.Fatal("UnmarshalFirst:", err) + } + } + }) + } + } +} + +func BenchmarkUnmarshalFirstViaDecoder(b *testing.B) { + // Random trailing data + trailingData := hexDecode("4a6b0f4718c73f391091ea1c") + for _, bm := range decodeBenchmarks { + for _, t := range bm.decodeToTypes { + name := "CBOR " + bm.name + " to Go " + t.String() + if t.Kind() == reflect.Struct { + name = "CBOR " + bm.name + " to Go " + t.Kind().String() + } + data := make([]byte, 0, len(bm.cborData)+len(trailingData)) + data = append(data, bm.cborData...) + data = append(data, trailingData...) + b.Run(name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + vPtr := reflect.New(t).Interface() + if err := NewDecoder(bytes.NewReader(data)).Decode(vPtr); err != nil { + b.Fatal("UnmarshalDecoder:", err) + } + } + }) + } + } +} + func BenchmarkDecode(b *testing.B) { for _, bm := range decodeBenchmarks { for _, t := range bm.decodeToTypes { @@ -415,6 +463,29 @@ func BenchmarkMarshalCanonical(b *testing.B) { } } +// BenchmarkNewEncoderEncode benchmarks NewEncoder() and Encode(). +func BenchmarkNewEncoderEncode(b *testing.B) { + for _, bm := range encodeBenchmarks { + for _, v := range bm.values { + name := "Go " + reflect.TypeOf(v).String() + " to CBOR " + bm.name + if reflect.TypeOf(v).Kind() == reflect.Struct { + name = "Go " + reflect.TypeOf(v).Kind().String() + " to CBOR " + bm.name + } + b.Run(name, func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + encoder := NewEncoder(io.Discard) + if err := encoder.Encode(v); err != nil { + b.Fatal("Encode:", err) + } + } + }) + } + } +} + +// BenchmarkEncode benchmarks Encode(). It reuses same Encoder to exclude NewEncoder() +// from the benchmark. func BenchmarkEncode(b *testing.B) { for _, bm := range encodeBenchmarks { for _, v := range bm.values { @@ -423,7 +494,7 @@ func BenchmarkEncode(b *testing.B) { name = "Go " + reflect.TypeOf(v).Kind().String() + " to CBOR " + bm.name } b.Run(name, func(b *testing.B) { - encoder := NewEncoder(ioutil.Discard) + encoder := NewEncoder(io.Discard) b.ResetTimer() for i := 0; i < b.N; i++ { if err := encoder.Encode(v); err != nil { @@ -437,7 +508,7 @@ func BenchmarkEncode(b *testing.B) { func BenchmarkEncodeStream(b *testing.B) { for i := 0; i < b.N; i++ { - encoder := NewEncoder(ioutil.Discard) + encoder := NewEncoder(io.Discard) for i := 0; i < rounds; i++ { for _, bm := range encodeBenchmarks { for _, v := range bm.values { diff --git a/vendor/github.com/fxamacker/cbor/v2/bytestring.go b/vendor/github.com/fxamacker/cbor/v2/bytestring.go new file mode 100644 index 00000000..26f5ef91 --- /dev/null +++ b/vendor/github.com/fxamacker/cbor/v2/bytestring.go @@ -0,0 +1,62 @@ +// Copyright (c) Faye Amacker. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +package cbor + +import ( + "errors" +) + +// ByteString represents CBOR byte string (major type 2). ByteString can be used +// when using a Go []byte is not possible or convenient. For example, Go doesn't +// allow []byte as map key, so ByteString can be used to support data formats +// having CBOR map with byte string keys. ByteString can also be used to +// encode invalid UTF-8 string as CBOR byte string. +// See DecOption.MapKeyByteStringMode for more details. +type ByteString string + +// Bytes returns bytes representing ByteString. +func (bs ByteString) Bytes() []byte { + return []byte(bs) +} + +// MarshalCBOR encodes ByteString as CBOR byte string (major type 2). +func (bs ByteString) MarshalCBOR() ([]byte, error) { + e := getEncoderBuffer() + defer putEncoderBuffer(e) + + // Encode length + encodeHead(e, byte(cborTypeByteString), uint64(len(bs))) + + // Encode data + buf := make([]byte, e.Len()+len(bs)) + n := copy(buf, e.Bytes()) + copy(buf[n:], bs) + + return buf, nil +} + +// UnmarshalCBOR decodes CBOR byte string (major type 2) to ByteString. +// Decoding CBOR null and CBOR undefined sets ByteString to be empty. +func (bs *ByteString) UnmarshalCBOR(data []byte) error { + if bs == nil { + return errors.New("cbor.ByteString: UnmarshalCBOR on nil pointer") + } + + // Decoding CBOR null and CBOR undefined to ByteString resets data. + // This behavior is similar to decoding CBOR null and CBOR undefined to []byte. + if len(data) == 1 && (data[0] == 0xf6 || data[0] == 0xf7) { + *bs = "" + return nil + } + + d := decoder{data: data, dm: defaultDecMode} + + // Check if CBOR data type is byte string + if typ := d.nextCBORType(); typ != cborTypeByteString { + return &UnmarshalTypeError{CBORType: typ.String(), GoType: typeByteString.String()} + } + + *bs = ByteString(d.parseByteString()) + return nil +} diff --git a/vendor/github.com/fxamacker/cbor/v2/bytestring_test.go b/vendor/github.com/fxamacker/cbor/v2/bytestring_test.go new file mode 100644 index 00000000..2f4920ef --- /dev/null +++ b/vendor/github.com/fxamacker/cbor/v2/bytestring_test.go @@ -0,0 +1,101 @@ +// Copyright (c) Faye Amacker. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +package cbor + +import "testing" + +func TestByteString(t *testing.T) { + type s1 struct { + A ByteString `cbor:"a"` + } + type s2 struct { + A *ByteString `cbor:"a"` + } + type s3 struct { + A ByteString `cbor:"a,omitempty"` + } + type s4 struct { + A *ByteString `cbor:"a,omitempty"` + } + + emptybs := ByteString("") + bs := ByteString("\x01\x02\x03\x04") + + testCases := []roundTripTest{ + { + name: "empty", + obj: emptybs, + wantCborData: hexDecode("40"), + }, + { + name: "not empty", + obj: bs, + wantCborData: hexDecode("4401020304"), + }, + { + name: "array", + obj: []ByteString{bs}, + wantCborData: hexDecode("814401020304"), + }, + { + name: "map with ByteString key", + obj: map[ByteString]bool{bs: true}, + wantCborData: hexDecode("a14401020304f5"), + }, + { + name: "empty ByteString field", + obj: s1{}, + wantCborData: hexDecode("a1616140"), + }, + { + name: "not empty ByteString field", + obj: s1{A: bs}, + wantCborData: hexDecode("a161614401020304"), + }, + { + name: "nil *ByteString field", + obj: s2{}, + wantCborData: hexDecode("a16161f6"), + }, + { + name: "empty *ByteString field", + obj: s2{A: &emptybs}, + wantCborData: hexDecode("a1616140"), + }, + { + name: "not empty *ByteString field", + obj: s2{A: &bs}, + wantCborData: hexDecode("a161614401020304"), + }, + { + name: "empty ByteString field with omitempty option", + obj: s3{}, + wantCborData: hexDecode("a0"), + }, + { + name: "not empty ByteString field with omitempty option", + obj: s3{A: bs}, + wantCborData: hexDecode("a161614401020304"), + }, + { + name: "nil *ByteString field with omitempty option", + obj: s4{}, + wantCborData: hexDecode("a0"), + }, + { + name: "empty *ByteString field with omitempty option", + obj: s4{A: &emptybs}, + wantCborData: hexDecode("a1616140"), + }, + { + name: "not empty *ByteString field with omitempty option", + obj: s4{A: &bs}, + wantCborData: hexDecode("a161614401020304"), + }, + } + + em, _ := EncOptions{}.EncMode() + dm, _ := DecOptions{}.DecMode() + testRoundTrip(t, testCases, em, dm) +} diff --git a/vendor/github.com/fxamacker/cbor/v2/decode.go b/vendor/github.com/fxamacker/cbor/v2/decode.go index 6b6179f5..c0ee14ab 100644 --- a/vendor/github.com/fxamacker/cbor/v2/decode.go +++ b/vendor/github.com/fxamacker/cbor/v2/decode.go @@ -40,17 +40,17 @@ import ( // To unmarshal CBOR into an empty interface value, Unmarshal uses the // following rules: // -// CBOR booleans decode to bool. -// CBOR positive integers decode to uint64. -// CBOR negative integers decode to int64 (big.Int if value overflows). -// CBOR floating points decode to float64. -// CBOR byte strings decode to []byte. -// CBOR text strings decode to string. -// CBOR arrays decode to []interface{}. -// CBOR maps decode to map[interface{}]interface{}. -// CBOR null and undefined values decode to nil. -// CBOR times (tag 0 and 1) decode to time.Time. -// CBOR bignums (tag 2 and 3) decode to big.Int. +// CBOR booleans decode to bool. +// CBOR positive integers decode to uint64. +// CBOR negative integers decode to int64 (big.Int if value overflows). +// CBOR floating points decode to float64. +// CBOR byte strings decode to []byte. +// CBOR text strings decode to string. +// CBOR arrays decode to []interface{}. +// CBOR maps decode to map[interface{}]interface{}. +// CBOR null and undefined values decode to nil. +// CBOR times (tag 0 and 1) decode to time.Time. +// CBOR bignums (tag 2 and 3) decode to big.Int. // // To unmarshal a CBOR array into a slice, Unmarshal allocates a new slice // if the CBOR array is empty or slice capacity is less than CBOR array length. @@ -75,9 +75,9 @@ import ( // To unmarshal a CBOR map into a struct, Unmarshal matches CBOR map keys to the // keys in the following priority: // -// 1. "cbor" key in struct field tag, -// 2. "json" key in struct field tag, -// 3. struct field name. +// 1. "cbor" key in struct field tag, +// 2. "json" key in struct field tag, +// 3. struct field name. // // Unmarshal tries an exact match for field name, then a case-insensitive match. // Map key-value pairs without corresponding struct fields are ignored. See @@ -95,15 +95,51 @@ import ( // // Unmarshal supports CBOR tag 55799 (self-describe CBOR), tag 0 and 1 (time), // and tag 2 and 3 (bignum). +// +// Unmarshal returns ExtraneousDataError error (without decoding into v) +// if there are any remaining bytes following the first valid CBOR data item. +// See UnmarshalFirst, if you want to unmarshal only the first +// CBOR data item without ExtraneousDataError caused by remaining bytes. func Unmarshal(data []byte, v interface{}) error { return defaultDecMode.Unmarshal(data, v) } -// Valid checks whether the CBOR data is complete and well-formed. +// UnmarshalFirst parses the first CBOR data item into the value pointed to by v +// using default decoding options. Any remaining bytes are returned in rest. +// +// If v is nil, not a pointer, or a nil pointer, UnmarshalFirst returns an error. +// +// See the documentation for Unmarshal for details. +func UnmarshalFirst(data []byte, v interface{}) (rest []byte, err error) { + return defaultDecMode.UnmarshalFirst(data, v) +} + +// Valid checks whether data is a well-formed encoded CBOR data item and +// that it complies with default restrictions such as MaxNestedLevels, +// MaxArrayElements, MaxMapPairs, etc. +// +// If there are any remaining bytes after the CBOR data item, +// an ExtraneousDataError is returned. +// +// WARNING: Valid doesn't check if encoded CBOR data item is valid (i.e. validity) +// and RFC 8949 distinctly defines what is "Valid" and what is "Well-formed". +// +// Deprecated: Valid is kept for compatibility and should not be used. +// Use Wellformed instead because it has a more appropriate name. func Valid(data []byte) error { return defaultDecMode.Valid(data) } +// Wellformed checks whether data is a well-formed encoded CBOR data item and +// that it complies with default restrictions such as MaxNestedLevels, +// MaxArrayElements, MaxMapPairs, etc. +// +// If there are any remaining bytes after the CBOR data item, +// an ExtraneousDataError is returned. +func Wellformed(data []byte) error { + return defaultDecMode.Wellformed(data) +} + // Unmarshaler is the interface implemented by types that wish to unmarshal // CBOR data themselves. The input is a valid CBOR value. UnmarshalCBOR // must copy the CBOR data if it needs to use it after returning. @@ -141,6 +177,16 @@ func (e *UnmarshalTypeError) Error() string { return s } +// InvalidMapKeyTypeError describes invalid Go map key type when decoding CBOR map. +// For example, Go doesn't allow slice as map key. +type InvalidMapKeyTypeError struct { + GoType string +} + +func (e *InvalidMapKeyTypeError) Error() string { + return "cbor: invalid map key type: " + e.GoType +} + // DupMapKeyError describes detected duplicate map key in CBOR map. type DupMapKeyError struct { Key interface{} @@ -239,6 +285,36 @@ func (idm IntDecMode) valid() bool { return idm < maxIntDec } +// MapKeyByteStringMode specifies how to decode CBOR byte string (major type 2) +// as Go map key when decoding CBOR map key into an empty Go interface value. +// Specifically, this option applies when decoding CBOR map into +// - Go empty interface, or +// - Go map with empty interface as key type. +// The CBOR map key types handled by this option are +// - byte string +// - tagged byte string +// - nested tagged byte string +type MapKeyByteStringMode int + +const ( + // MapKeyByteStringAllowed allows CBOR byte string to be decoded as Go map key. + // Since Go doesn't allow []byte as map key, CBOR byte string is decoded to + // ByteString which has underlying string type. + // This is the default setting. + MapKeyByteStringAllowed MapKeyByteStringMode = iota + + // MapKeyByteStringForbidden forbids CBOR byte string being decoded as Go map key. + // Attempting to decode CBOR byte string as map key into empty interface value + // returns a decoding error. + MapKeyByteStringForbidden + + maxMapKeyByteStringMode +) + +func (mkbsm MapKeyByteStringMode) valid() bool { + return mkbsm < maxMapKeyByteStringMode +} + // ExtraDecErrorCond specifies extra conditions that should be treated as errors. type ExtraDecErrorCond uint @@ -257,6 +333,26 @@ func (ec ExtraDecErrorCond) valid() bool { return ec < maxExtraDecError } +// UTF8Mode option specifies if decoder should +// decode CBOR Text containing invalid UTF-8 string. +type UTF8Mode int + +const ( + // UTF8RejectInvalid rejects CBOR Text containing + // invalid UTF-8 string. + UTF8RejectInvalid UTF8Mode = iota + + // UTF8DecodeInvalid allows decoding CBOR Text containing + // invalid UTF-8 string. + UTF8DecodeInvalid + + maxUTF8Mode +) + +func (um UTF8Mode) valid() bool { + return um < maxUTF8Mode +} + // DecOptions specifies decoding options. type DecOptions struct { // DupMapKey specifies whether to enforce duplicate map key. @@ -267,7 +363,8 @@ type DecOptions struct { TimeTag DecTagMode // MaxNestedLevels specifies the max nested levels allowed for any combination of CBOR array, maps, and tags. - // Default is 32 levels and it can be set to [4, 256]. + // Default is 32 levels and it can be set to [4, 65535]. Note that higher maximum levels of nesting can + // require larger amounts of stack to deserialize. Don't increase this higher than you require. MaxNestedLevels int // MaxArrayElements specifies the max number of elements for CBOR arrays. @@ -288,8 +385,23 @@ type DecOptions struct { // when decoding CBOR int (major type 0 and 1) to Go interface{}. IntDec IntDecMode + // MapKeyByteString specifies how to decode CBOR byte string as map key + // when decoding CBOR map with byte string key into an empty interface value. + // By default, an error is returned when attempting to decode CBOR byte string + // as map key because Go doesn't allow []byte as map key. + MapKeyByteString MapKeyByteStringMode + // ExtraReturnErrors specifies extra conditions that should be treated as errors. ExtraReturnErrors ExtraDecErrorCond + + // DefaultMapType specifies Go map type to create and decode to + // when unmarshalling CBOR into an empty interface value. + // By default, unmarshal uses map[interface{}]interface{}. + DefaultMapType reflect.Type + + // UTF8 specifies if decoder should decode CBOR Text containing invalid UTF-8. + // By default, unmarshal rejects CBOR text containing invalid UTF-8. + UTF8 UTF8Mode } // DecMode returns DecMode with immutable options and no tags (safe for concurrency). @@ -371,10 +483,13 @@ func (opts DecOptions) decMode() (*decMode, error) { if !opts.IntDec.valid() { return nil, errors.New("cbor: invalid IntDec " + strconv.Itoa(int(opts.IntDec))) } + if !opts.MapKeyByteString.valid() { + return nil, errors.New("cbor: invalid MapKeyByteString " + strconv.Itoa(int(opts.MapKeyByteString))) + } if opts.MaxNestedLevels == 0 { opts.MaxNestedLevels = 32 - } else if opts.MaxNestedLevels < 4 || opts.MaxNestedLevels > 256 { - return nil, errors.New("cbor: invalid MaxNestedLevels " + strconv.Itoa(opts.MaxNestedLevels) + " (range is [4, 256])") + } else if opts.MaxNestedLevels < 4 || opts.MaxNestedLevels > 65535 { + return nil, errors.New("cbor: invalid MaxNestedLevels " + strconv.Itoa(opts.MaxNestedLevels) + " (range is [4, 65535])") } if opts.MaxArrayElements == 0 { opts.MaxArrayElements = defaultMaxArrayElements @@ -389,6 +504,12 @@ func (opts DecOptions) decMode() (*decMode, error) { if !opts.ExtraReturnErrors.valid() { return nil, errors.New("cbor: invalid ExtraReturnErrors " + strconv.Itoa(int(opts.ExtraReturnErrors))) } + if opts.DefaultMapType != nil && opts.DefaultMapType.Kind() != reflect.Map { + return nil, fmt.Errorf("cbor: invalid DefaultMapType %s", opts.DefaultMapType) + } + if !opts.UTF8.valid() { + return nil, errors.New("cbor: invalid UTF8 " + strconv.Itoa(int(opts.UTF8))) + } dm := decMode{ dupMapKey: opts.DupMapKey, timeTag: opts.TimeTag, @@ -398,7 +519,10 @@ func (opts DecOptions) decMode() (*decMode, error) { indefLength: opts.IndefLength, tagsMd: opts.TagsMd, intDec: opts.IntDec, + mapKeyByteString: opts.MapKeyByteString, extraReturnErrors: opts.ExtraReturnErrors, + defaultMapType: opts.DefaultMapType, + utf8: opts.UTF8, } return &dm, nil } @@ -411,10 +535,40 @@ type DecMode interface { // // See the documentation for Unmarshal for details. Unmarshal(data []byte, v interface{}) error - // Valid checks whether the CBOR data is complete and well-formed. + + // UnmarshalFirst parses the first CBOR data item into the value pointed to by v + // using the decoding mode. Any remaining bytes are returned in rest. + // + // If v is nil, not a pointer, or a nil pointer, UnmarshalFirst returns an error. + // + // See the documentation for Unmarshal for details. + UnmarshalFirst(data []byte, v interface{}) (rest []byte, err error) + + // Valid checks whether data is a well-formed encoded CBOR data item and + // that it complies with configurable restrictions such as MaxNestedLevels, + // MaxArrayElements, MaxMapPairs, etc. + // + // If there are any remaining bytes after the CBOR data item, + // an ExtraneousDataError is returned. + // + // WARNING: Valid doesn't check if encoded CBOR data item is valid (i.e. validity) + // and RFC 8949 distinctly defines what is "Valid" and what is "Well-formed". + // + // Deprecated: Valid is kept for compatibility and should not be used. + // Use Wellformed instead because it has a more appropriate name. Valid(data []byte) error + + // Wellformed checks whether data is a well-formed encoded CBOR data item and + // that it complies with configurable restrictions such as MaxNestedLevels, + // MaxArrayElements, MaxMapPairs, etc. + // + // If there are any remaining bytes after the CBOR data item, + // an ExtraneousDataError is returned. + Wellformed(data []byte) error + // NewDecoder returns a new decoder that reads from r using dm DecMode. NewDecoder(r io.Reader) *Decoder + // DecOptions returns user specified options used to create this DecMode. DecOptions() DecOptions } @@ -429,7 +583,10 @@ type decMode struct { indefLength IndefLengthMode tagsMd TagsMode intDec IntDecMode + mapKeyByteString MapKeyByteStringMode extraReturnErrors ExtraDecErrorCond + defaultMapType reflect.Type + utf8 UTF8Mode } var defaultDecMode, _ = DecOptions{}.decMode() @@ -445,7 +602,9 @@ func (dm *decMode) DecOptions() DecOptions { IndefLength: dm.indefLength, TagsMd: dm.tagsMd, IntDec: dm.intDec, + MapKeyByteString: dm.mapKeyByteString, ExtraReturnErrors: dm.extraReturnErrors, + UTF8: dm.utf8, } } @@ -456,13 +615,72 @@ func (dm *decMode) DecOptions() DecOptions { // See the documentation for Unmarshal for details. func (dm *decMode) Unmarshal(data []byte, v interface{}) error { d := decoder{data: data, dm: dm} + + // Check well-formedness. + off := d.off // Save offset before data validation + err := d.wellformed(false) // don't allow any extra data after valid data item. + d.off = off // Restore offset + if err != nil { + return err + } + return d.value(v) } -// Valid checks whether the CBOR data is complete and well-formed. +// UnmarshalFirst parses the first CBOR data item into the value pointed to by v +// using dm decoding mode. Any remaining bytes are returned in rest. +// +// If v is nil, not a pointer, or a nil pointer, UnmarshalFirst returns an error. +// +// See the documentation for Unmarshal for details. +func (dm *decMode) UnmarshalFirst(data []byte, v interface{}) (rest []byte, err error) { + d := decoder{data: data, dm: dm} + + // check well-formedness. + off := d.off // Save offset before data validation + err = d.wellformed(true) // allow extra data after well-formed data item + d.off = off // Restore offset + + // If it is well-formed, parse the value. This is structured like this to allow + // better test coverage + if err == nil { + err = d.value(v) + } + + // If either wellformed or value returned an error, do not return rest bytes + if err != nil { + return nil, err + } + + // Return the rest of the data slice (which might be len 0) + return d.data[d.off:], nil +} + +// Valid checks whether data is a well-formed encoded CBOR data item and +// that it complies with configurable restrictions such as MaxNestedLevels, +// MaxArrayElements, MaxMapPairs, etc. +// +// If there are any remaining bytes after the CBOR data item, +// an ExtraneousDataError is returned. +// +// WARNING: Valid doesn't check if encoded CBOR data item is valid (i.e. validity) +// and RFC 8949 distinctly defines what is "Valid" and what is "Well-formed". +// +// Deprecated: Valid is kept for compatibility and should not be used. +// Use Wellformed instead because it has a more appropriate name. func (dm *decMode) Valid(data []byte) error { + return dm.Wellformed(data) +} + +// Wellformed checks whether data is a well-formed encoded CBOR data item and +// that it complies with configurable restrictions such as MaxNestedLevels, +// MaxArrayElements, MaxMapPairs, etc. +// +// If there are any remaining bytes after the CBOR data item, +// an ExtraneousDataError is returned. +func (dm *decMode) Wellformed(data []byte) error { d := decoder{data: data, dm: dm} - return d.valid() + return d.wellformed(false) } // NewDecoder returns a new decoder that reads from r using dm DecMode. @@ -476,6 +694,10 @@ type decoder struct { dm *decMode } +// value decodes CBOR data item into the value pointed to by v. +// If CBOR data item fails to be decoded into v, +// error is returned and offset is moved to the next CBOR data item. +// Precondition: d.data contains at least one well-formed CBOR data item. func (d *decoder) value(v interface{}) error { // v can't be nil, non-pointer, or nil pointer value. if v == nil { @@ -487,14 +709,6 @@ func (d *decoder) value(v interface{}) error { } else if rv.IsNil() { return &InvalidUnmarshalError{"cbor: Unmarshal(nil " + rv.Type().String() + ")"} } - - off := d.off // Save offset before data validation - err := d.valid() - d.off = off // Restore offset - if err != nil { - return err - } - rv = rv.Elem() return d.parseToValue(rv, getTypeInfo(rv.Type())) } @@ -543,9 +757,34 @@ const ( // and does not perform bounds checking. func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo - if tInfo.spclType == specialTypeIface && !v.IsNil() { - v = v.Elem() - tInfo = getTypeInfo(v.Type()) + if tInfo.spclType == specialTypeIface { + if !v.IsNil() { + // Use value type + v = v.Elem() + tInfo = getTypeInfo(v.Type()) + } else { + // Create and use registered type if CBOR data is registered tag + if d.dm.tags != nil && d.nextCBORType() == cborTypeTag { + + off := d.off + var tagNums []uint64 + for d.nextCBORType() == cborTypeTag { + _, _, tagNum := d.getHead() + tagNums = append(tagNums, tagNum) + } + d.off = off + + registeredType := d.dm.tags.getTypeFromTagNum(tagNums) + if registeredType != nil { + if registeredType.Implements(tInfo.nonPtrType) || + reflect.PtrTo(registeredType).Implements(tInfo.nonPtrType) { + v.Set(reflect.New(registeredType)) + v = v.Elem() + tInfo = getTypeInfo(registeredType) + } + } + } + } } // Create new value for the pointer v to point to if CBOR value is not nil/undefined. @@ -665,14 +904,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin return fillTextString(t, b, v) case cborTypePrimitives: _, ai, val := d.getHead() - if ai < 20 || ai == 24 { - return fillPositiveInt(t, val, v) - } switch ai { - case 20, 21: - return fillBool(t, ai == 21, v) - case 22, 23: - return fillNil(t, v) case 25: f := float64(float16.Frombits(uint16(val)).Float32()) return fillFloat(t, f, v) @@ -682,7 +914,22 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin case 27: f := math.Float64frombits(val) return fillFloat(t, f, v) + default: // ai <= 24 + // Decode simple values (including false, true, null, and undefined) + if tInfo.nonPtrType == typeSimpleValue { + v.SetUint(val) + return nil + } + switch ai { + case 20, 21: + return fillBool(t, ai == 21, v) + case 22, 23: + return fillNil(t, v) + default: + return fillPositiveInt(t, val, v) + } } + case cborTypeTag: _, _, tagNum := d.getHead() switch tagNum { @@ -968,7 +1215,7 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli case cborTypePrimitives: _, ai, val := d.getHead() if ai < 20 || ai == 24 { - return val, nil + return SimpleValue(val), nil } switch ai { case 20, 21: @@ -988,6 +1235,14 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli case cborTypeArray: return d.parseArray() case cborTypeMap: + if d.dm.defaultMapType != nil { + m := reflect.New(d.dm.defaultMapType) + err := d.parseToValue(m, getTypeInfo(m.Elem().Type())) + if err != nil { + return nil, err + } + return m.Elem().Interface(), nil + } return d.parseMap() } return nil, nil @@ -1021,7 +1276,7 @@ func (d *decoder) parseTextString() ([]byte, error) { if ai != 31 { b := d.data[d.off : d.off+int(val)] d.off += int(val) - if !utf8.Valid(b) { + if d.dm.utf8 == UTF8RejectInvalid && !utf8.Valid(b) { return nil, &SemanticError{"cbor: invalid UTF-8 string"} } return b, nil @@ -1032,7 +1287,7 @@ func (d *decoder) parseTextString() ([]byte, error) { _, _, val = d.getHead() x := d.data[d.off : d.off+int(val)] d.off += int(val) - if !utf8.Valid(x) { + if d.dm.utf8 == UTF8RejectInvalid && !utf8.Valid(x) { for !d.foundBreak() { d.skip() // Skip remaining chunk on error } @@ -1117,7 +1372,7 @@ func (d *decoder) parseArrayToArray(v reflect.Value, tInfo *typeInfo) error { return err } -func (d *decoder) parseMap() (map[interface{}]interface{}, error) { +func (d *decoder) parseMap() (interface{}, error) { _, ai, val := d.getHead() hasSize := (ai != 31) count := int(val) @@ -1138,11 +1393,17 @@ func (d *decoder) parseMap() (map[interface{}]interface{}, error) { // Detect if CBOR map key can be used as Go map key. rv := reflect.ValueOf(k) if !isHashableValue(rv) { - if err == nil { - err = errors.New("cbor: invalid map key type: " + rv.Type().String()) + var converted bool + if d.dm.mapKeyByteString == MapKeyByteStringAllowed { + k, converted = convertByteSliceToByteString(k) + } + if !converted { + if err == nil { + err = &InvalidMapKeyTypeError{rv.Type().String()} + } + d.skip() + continue } - d.skip() - continue } // Parse CBOR map value. @@ -1224,11 +1485,21 @@ func (d *decoder) parseMapToMap(v reflect.Value, tInfo *typeInfo) error { //noli // Detect if CBOR map key can be used as Go map key. if keyIsInterfaceType && keyValue.Elem().IsValid() { if !isHashableValue(keyValue.Elem()) { - if err == nil { - err = errors.New("cbor: invalid map key type: " + keyValue.Elem().Type().String()) + var converted bool + if d.dm.mapKeyByteString == MapKeyByteStringAllowed { + var k interface{} + k, converted = convertByteSliceToByteString(keyValue.Elem().Interface()) + if converted { + keyValue.Set(reflect.ValueOf(k)) + } + } + if !converted { + if err == nil { + err = &InvalidMapKeyTypeError{keyValue.Elem().Type().String()} + } + d.skip() + continue } - d.skip() - continue } } @@ -1854,6 +2125,25 @@ func isHashableValue(rv reflect.Value) bool { return true } +// convertByteSliceToByteString converts []byte to ByteString if +// - v is []byte type, or +// - v is Tag type and tag content type is []byte +// This function also handles nested tags. +// CBOR data is already verified to be well-formed before this function is used, +// so the recursion won't exceed max nested levels. +func convertByteSliceToByteString(v interface{}) (interface{}, bool) { + switch v := v.(type) { + case []byte: + return ByteString(v), true + case Tag: + content, converted := convertByteSliceToByteString(v.Content) + if converted { + return Tag{Number: v.Number, Content: content}, true + } + } + return v, false +} + // validBuiltinTag checks that supported built-in tag numbers are followed by expected content types. func validBuiltinTag(tagNum uint64, contentHead byte) error { t := cborType(contentHead & 0xe0) diff --git a/vendor/github.com/fxamacker/cbor/v2/decode_test.go b/vendor/github.com/fxamacker/cbor/v2/decode_test.go index 92f96462..ea3d84d4 100644 --- a/vendor/github.com/fxamacker/cbor/v2/decode_test.go +++ b/vendor/github.com/fxamacker/cbor/v2/decode_test.go @@ -35,6 +35,7 @@ var ( typeByteArray = reflect.TypeOf([5]byte{}) typeIntSlice = reflect.TypeOf([]int{}) typeStringSlice = reflect.TypeOf([]string{}) + typeMapIntfIntf = reflect.TypeOf(map[interface{}]interface{}{}) typeMapStringInt = reflect.TypeOf(map[string]int{}) typeMapStringString = reflect.TypeOf(map[string]string{}) typeMapStringIntf = reflect.TypeOf(map[string]interface{}{}) @@ -54,116 +55,116 @@ var unmarshalTests = []unmarshalTest{ hexDecode("00"), uint64(0), []interface{}{uint8(0), uint16(0), uint32(0), uint64(0), uint(0), int8(0), int16(0), int32(0), int64(0), int(0), float32(0), float64(0), bigIntOrPanic("0")}, - []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("01"), uint64(1), []interface{}{uint8(1), uint16(1), uint32(1), uint64(1), uint(1), int8(1), int16(1), int32(1), int64(1), int(1), float32(1), float64(1), bigIntOrPanic("1")}, - []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("0a"), uint64(10), []interface{}{uint8(10), uint16(10), uint32(10), uint64(10), uint(10), int8(10), int16(10), int32(10), int64(10), int(10), float32(10), float64(10), bigIntOrPanic("10")}, - []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("17"), uint64(23), []interface{}{uint8(23), uint16(23), uint32(23), uint64(23), uint(23), int8(23), int16(23), int32(23), int64(23), int(23), float32(23), float64(23), bigIntOrPanic("23")}, - []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("1818"), uint64(24), []interface{}{uint8(24), uint16(24), uint32(24), uint64(24), uint(24), int8(24), int16(24), int32(24), int64(24), int(24), float32(24), float64(24), bigIntOrPanic("24")}, - []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("1819"), uint64(25), []interface{}{uint8(25), uint16(25), uint32(25), uint64(25), uint(25), int8(25), int16(25), int32(25), int64(25), int(25), float32(25), float64(25), bigIntOrPanic("25")}, - []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("1864"), uint64(100), []interface{}{uint8(100), uint16(100), uint32(100), uint64(100), uint(100), int8(100), int16(100), int32(100), int64(100), int(100), float32(100), float64(100), bigIntOrPanic("100")}, - []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("1903e8"), uint64(1000), []interface{}{uint16(1000), uint32(1000), uint64(1000), uint(1000), int16(1000), int32(1000), int64(1000), int(1000), float32(1000), float64(1000), bigIntOrPanic("1000")}, - []reflect.Type{typeUint8, typeInt8, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeInt8, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("1a000f4240"), uint64(1000000), []interface{}{uint32(1000000), uint64(1000000), uint(1000000), int32(1000000), int64(1000000), int(1000000), float32(1000000), float64(1000000), bigIntOrPanic("1000000")}, - []reflect.Type{typeUint8, typeUint16, typeInt8, typeInt16, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeInt8, typeInt16, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("1b000000e8d4a51000"), uint64(1000000000000), []interface{}{uint64(1000000000000), uint(1000000000000), int64(1000000000000), int(1000000000000), float32(1000000000000), float64(1000000000000), bigIntOrPanic("1000000000000")}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeInt8, typeInt16, typeInt32, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeInt8, typeInt16, typeInt32, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("1bffffffffffffffff"), uint64(18446744073709551615), []interface{}{uint64(18446744073709551615), uint(18446744073709551615), float32(18446744073709551615), float64(18446744073709551615), bigIntOrPanic("18446744073709551615")}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeInt8, typeInt16, typeInt32, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeInt8, typeInt16, typeInt32, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, // negative integer { hexDecode("20"), int64(-1), []interface{}{int8(-1), int16(-1), int32(-1), int64(-1), int(-1), float32(-1), float64(-1), bigIntOrPanic("-1")}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("29"), int64(-10), []interface{}{int8(-10), int16(-10), int32(-10), int64(-10), int(-10), float32(-10), float64(-10), bigIntOrPanic("-10")}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("3863"), int64(-100), []interface{}{int8(-100), int16(-100), int32(-100), int64(-100), int(-100), float32(-100), float64(-100), bigIntOrPanic("-100")}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("3903e7"), int64(-1000), []interface{}{int16(-1000), int32(-1000), int64(-1000), int(-1000), float32(-1000), float64(-1000), bigIntOrPanic("-1000")}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("3bffffffffffffffff"), bigIntOrPanic("-18446744073709551616"), []interface{}{bigIntOrPanic("-18446744073709551616")}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, // CBOR value -18446744073709551616 overflows Go's int64, see TestNegIntOverflow // byte string { hexDecode("40"), []byte{}, - []interface{}{[]byte{}, [0]byte{}, [1]byte{0}, [5]byte{0, 0, 0, 0, 0}}, + []interface{}{[]byte{}, [0]byte{}, [1]byte{0}, [5]byte{0, 0, 0, 0, 0}, ByteString("")}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, { hexDecode("4401020304"), []byte{1, 2, 3, 4}, - []interface{}{[]byte{1, 2, 3, 4}, [0]byte{}, [1]byte{1}, [5]byte{1, 2, 3, 4, 0}}, + []interface{}{[]byte{1, 2, 3, 4}, [0]byte{}, [1]byte{1}, [5]byte{1, 2, 3, 4, 0}, ByteString("\x01\x02\x03\x04")}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, { hexDecode("5f42010243030405ff"), []byte{1, 2, 3, 4, 5}, - []interface{}{[]byte{1, 2, 3, 4, 5}, [0]byte{}, [1]byte{1}, [5]byte{1, 2, 3, 4, 5}, [6]byte{1, 2, 3, 4, 5, 0}}, + []interface{}{[]byte{1, 2, 3, 4, 5}, [0]byte{}, [1]byte{1}, [5]byte{1, 2, 3, 4, 5}, [6]byte{1, 2, 3, 4, 5, 0}, ByteString("\x01\x02\x03\x04\x05")}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, // text string @@ -171,80 +172,80 @@ var unmarshalTests = []unmarshalTest{ hexDecode("60"), "", []interface{}{""}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("6161"), "a", []interface{}{"a"}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("6449455446"), "IETF", []interface{}{"IETF"}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("62225c"), "\"\\", []interface{}{"\"\\"}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("62c3bc"), "ü", []interface{}{"ü"}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("63e6b0b4"), "水", []interface{}{"水"}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("64f0908591"), "𐅑", []interface{}{"𐅑"}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("7f657374726561646d696e67ff"), "streaming", []interface{}{"streaming"}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, // array { hexDecode("80"), []interface{}{}, []interface{}{[]interface{}{}, []byte{}, []string{}, []int{}, [0]int{}, [1]int{0}, [5]int{0}, []float32{}, []float64{}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("83010203"), []interface{}{uint64(1), uint64(2), uint64(3)}, []interface{}{[]interface{}{uint64(1), uint64(2), uint64(3)}, []byte{1, 2, 3}, []int{1, 2, 3}, []uint{1, 2, 3}, [0]int{}, [1]int{1}, [3]int{1, 2, 3}, [5]int{1, 2, 3, 0, 0}, []float32{1, 2, 3}, []float64{1, 2, 3}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("8301820203820405"), []interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, []interface{}{[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, [...]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("83018202039f0405ff"), []interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, []interface{}{[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, [...]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("83019f0203ff820405"), []interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, []interface{}{[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, [...]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("98190102030405060708090a0b0c0d0e0f101112131415161718181819"), @@ -260,25 +261,25 @@ var unmarshalTests = []unmarshalTest{ [30]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0}, []float32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("9fff"), []interface{}{}, []interface{}{[]interface{}{}, []byte{}, []string{}, []int{}, [0]int{}, [1]int{0}, [5]int{0, 0, 0, 0, 0}, []float32{}, []float64{}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("9f018202039f0405ffff"), []interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, []interface{}{[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, [...]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("9f01820203820405ff"), []interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, []interface{}{[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, [...]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff"), @@ -294,39 +295,39 @@ var unmarshalTests = []unmarshalTest{ [30]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0}, []float32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("826161a161626163"), []interface{}{"a", map[interface{}]interface{}{"b": "c"}}, []interface{}{[]interface{}{"a", map[interface{}]interface{}{"b": "c"}}, [...]interface{}{"a", map[interface{}]interface{}{"b": "c"}}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeByteArray, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeByteArray, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("826161bf61626163ff"), []interface{}{"a", map[interface{}]interface{}{"b": "c"}}, []interface{}{[]interface{}{"a", map[interface{}]interface{}{"b": "c"}}, [...]interface{}{"a", map[interface{}]interface{}{"b": "c"}}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeByteArray, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeByteArray, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag, typeBigInt, typeByteString}, }, // map { hexDecode("a0"), map[interface{}]interface{}{}, []interface{}{map[interface{}]interface{}{}, map[string]bool{}, map[string]int{}, map[int]string{}, map[int]bool{}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeTag, typeRawTag, typeByteString}, }, { hexDecode("a201020304"), map[interface{}]interface{}{uint64(1): uint64(2), uint64(3): uint64(4)}, []interface{}{map[interface{}]interface{}{uint64(1): uint64(2), uint64(3): uint64(4)}, map[uint]int{1: 2, 3: 4}, map[int]uint{1: 2, 3: 4}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("a26161016162820203"), map[interface{}]interface{}{"a": uint64(1), "b": []interface{}{uint64(2), uint64(3)}}, []interface{}{map[interface{}]interface{}{"a": uint64(1), "b": []interface{}{uint64(2), uint64(3)}}, map[string]interface{}{"a": uint64(1), "b": []interface{}{uint64(2), uint64(3)}}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("a56161614161626142616361436164614461656145"), @@ -334,34 +335,34 @@ var unmarshalTests = []unmarshalTest{ []interface{}{map[interface{}]interface{}{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}, map[string]interface{}{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}, map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("bf61610161629f0203ffff"), map[interface{}]interface{}{"a": uint64(1), "b": []interface{}{uint64(2), uint64(3)}}, []interface{}{map[interface{}]interface{}{"a": uint64(1), "b": []interface{}{uint64(2), uint64(3)}}, map[string]interface{}{"a": uint64(1), "b": []interface{}{uint64(2), uint64(3)}}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("bf6346756ef563416d7421ff"), map[interface{}]interface{}{"Fun": true, "Amt": int64(-2)}, []interface{}{map[interface{}]interface{}{"Fun": true, "Amt": int64(-2)}, map[string]interface{}{"Fun": true, "Amt": int64(-2)}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, // tag { hexDecode("c074323031332d30332d32315432303a30343a30305a"), time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC), // 2013-03-21 20:04:00 +0000 UTC []interface{}{"2013-03-21T20:04:00Z", time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC), Tag{0, "2013-03-21T20:04:00Z"}, RawTag{0, hexDecode("74323031332d30332d32315432303a30343a30305a")}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeBigInt, typeByteString}, }, // 0: standard date/time { hexDecode("c11a514b67b0"), time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC), // 2013-03-21 20:04:00 +0000 UTC []interface{}{uint32(1363896240), uint64(1363896240), int32(1363896240), int64(1363896240), float32(1363896240), float64(1363896240), time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC), Tag{1, uint64(1363896240)}, RawTag{1, hexDecode("1a514b67b0")}}, - []reflect.Type{typeUint8, typeUint16, typeInt8, typeInt16, typeByteSlice, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt}, + []reflect.Type{typeUint8, typeUint16, typeInt8, typeInt16, typeByteSlice, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeByteString}, }, // 1: epoch-based date/time { hexDecode("c249010000000000000000"), @@ -381,7 +382,7 @@ var unmarshalTests = []unmarshalTest{ // Decode to big.Int bigIntOrPanic("18446744073709551616"), }, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeByteString}, }, // 2: positive bignum: 18446744073709551616 { hexDecode("c349010000000000000000"), @@ -401,62 +402,62 @@ var unmarshalTests = []unmarshalTest{ // Decode to big.Int bigIntOrPanic("-18446744073709551617"), }, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeByteString}, }, // 3: negative bignum: -18446744073709551617 { hexDecode("c1fb41d452d9ec200000"), time.Date(2013, 3, 21, 20, 4, 0, 500000000, time.UTC), // 2013-03-21 20:04:00.5 +0000 UTC []interface{}{float32(1363896240.5), float64(1363896240.5), time.Date(2013, 3, 21, 20, 4, 0, 500000000, time.UTC), Tag{1, float64(1363896240.5)}, RawTag{1, hexDecode("fb41d452d9ec200000")}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeBigInt, typeByteString}, }, // 1: epoch-based date/time { hexDecode("d74401020304"), Tag{23, []byte{0x01, 0x02, 0x03, 0x04}}, []interface{}{[]byte{0x01, 0x02, 0x03, 0x04}, [0]byte{}, [1]byte{0x01}, [3]byte{0x01, 0x02, 0x03}, [...]byte{0x01, 0x02, 0x03, 0x04}, [5]byte{0x01, 0x02, 0x03, 0x04, 0x00}, Tag{23, []byte{0x01, 0x02, 0x03, 0x04}}, RawTag{23, hexDecode("4401020304")}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeBigInt, typeByteString}, }, // 23: expected conversion to base16 encoding { hexDecode("d818456449455446"), Tag{24, []byte{0x64, 0x49, 0x45, 0x54, 0x46}}, []interface{}{[]byte{0x64, 0x49, 0x45, 0x54, 0x46}, [0]byte{}, [1]byte{0x64}, [3]byte{0x64, 0x49, 0x45}, [...]byte{0x64, 0x49, 0x45, 0x54, 0x46}, [6]byte{0x64, 0x49, 0x45, 0x54, 0x46, 0x00}, Tag{24, []byte{0x64, 0x49, 0x45, 0x54, 0x46}}, RawTag{24, hexDecode("456449455446")}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeBigInt, typeByteString}, }, // 24: encoded cborBytes data item { hexDecode("d82076687474703a2f2f7777772e6578616d706c652e636f6d"), Tag{32, "http://www.example.com"}, []interface{}{"http://www.example.com", Tag{32, "http://www.example.com"}, RawTag{32, hexDecode("76687474703a2f2f7777772e6578616d706c652e636f6d")}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeBigInt, typeByteString}, }, // 32: URI // primitives { hexDecode("f4"), false, - []interface{}{false}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteArray, typeByteSlice, typeString, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []interface{}{false, SimpleValue(20)}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteArray, typeByteSlice, typeString, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("f5"), true, - []interface{}{true}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteArray, typeByteSlice, typeString, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []interface{}{true, SimpleValue(21)}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteArray, typeByteSlice, typeString, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("f6"), nil, - []interface{}{false, uint(0), uint8(0), uint16(0), uint32(0), uint64(0), int(0), int8(0), int16(0), int32(0), int64(0), float32(0.0), float64(0.0), "", []byte(nil), []int(nil), []string(nil), map[string]int(nil), time.Time{}, bigIntOrPanic("0"), Tag{}, RawTag{}}, + []interface{}{SimpleValue(22), false, uint(0), uint8(0), uint16(0), uint32(0), uint64(0), int(0), int8(0), int16(0), int32(0), int64(0), float32(0.0), float64(0.0), "", []byte(nil), []int(nil), []string(nil), map[string]int(nil), time.Time{}, bigIntOrPanic("0"), Tag{}, RawTag{}}, nil, }, { hexDecode("f7"), nil, - []interface{}{false, uint(0), uint8(0), uint16(0), uint32(0), uint64(0), int(0), int8(0), int16(0), int32(0), int64(0), float32(0.0), float64(0.0), "", []byte(nil), []int(nil), []string(nil), map[string]int(nil), time.Time{}, bigIntOrPanic("0"), Tag{}, RawTag{}}, + []interface{}{SimpleValue(23), false, uint(0), uint8(0), uint16(0), uint32(0), uint64(0), int(0), int8(0), int16(0), int32(0), int64(0), float32(0.0), float64(0.0), "", []byte(nil), []int(nil), []string(nil), map[string]int(nil), time.Time{}, bigIntOrPanic("0"), Tag{}, RawTag{}}, nil, }, { hexDecode("f0"), - uint64(16), - []interface{}{uint8(16), uint16(16), uint32(16), uint64(16), uint(16), int8(16), int16(16), int32(16), int64(16), int(16), float32(16), float64(16), bigIntOrPanic("16")}, - []reflect.Type{typeByteSlice, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + SimpleValue(16), + []interface{}{SimpleValue(16), uint8(16), uint16(16), uint32(16), uint64(16), uint(16), int8(16), int16(16), int32(16), int64(16), int(16), float32(16), float64(16), bigIntOrPanic("16")}, + []reflect.Type{typeByteSlice, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, // This example is not well-formed because Simple value (with 5-bit value 24) must be >= 32. // See RFC 7049 section 2.3 for details, instead of the incorrect example in RFC 7049 Appendex A. @@ -471,47 +472,47 @@ var unmarshalTests = []unmarshalTest{ */ { hexDecode("f820"), - uint64(32), - []interface{}{uint8(32), uint16(32), uint32(32), uint64(32), uint(32), int8(32), int16(32), int32(32), int64(32), int(32), float32(32), float64(32), bigIntOrPanic("32")}, - []reflect.Type{typeByteSlice, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + SimpleValue(32), + []interface{}{SimpleValue(32), uint8(32), uint16(32), uint32(32), uint64(32), uint(32), int8(32), int16(32), int32(32), int64(32), int(32), float32(32), float64(32), bigIntOrPanic("32")}, + []reflect.Type{typeByteSlice, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, { hexDecode("f8ff"), - uint64(255), - []interface{}{uint8(255), uint16(255), uint32(255), uint64(255), uint(255), int16(255), int32(255), int64(255), int(255), float32(255), float64(255), bigIntOrPanic("255")}, - []reflect.Type{typeByteSlice, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, + SimpleValue(255), + []interface{}{SimpleValue(255), uint8(255), uint16(255), uint32(255), uint64(255), uint(255), int16(255), int32(255), int64(255), int(255), float32(255), float64(255), bigIntOrPanic("255")}, + []reflect.Type{typeByteSlice, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeByteString}, }, // More testcases not covered by https://tools.ietf.org/html/rfc7049#appendix-A. { hexDecode("5fff"), // empty indefinite length byte string []byte{}, - []interface{}{[]byte{}, [0]byte{}, [1]byte{0x00}}, + []interface{}{[]byte{}, [0]byte{}, [1]byte{0x00}, ByteString("")}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, { hexDecode("7fff"), // empty indefinite length text string "", []interface{}{""}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, }, { hexDecode("bfff"), // empty indefinite length map map[interface{}]interface{}{}, []interface{}{map[interface{}]interface{}{}, map[string]bool{}, map[string]int{}, map[int]string{}, map[int]bool{}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeTag, typeRawTag}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeTag, typeRawTag, typeByteString}, }, // More test data with tags { hexDecode("c13a0177f2cf"), // 1969-03-21T20:04:00Z, tag 1 with negative integer as epoch time time.Date(1969, 3, 21, 20, 4, 0, 0, time.UTC), []interface{}{int32(-24638160), int64(-24638160), int32(-24638160), int64(-24638160), float32(-24638160), float64(-24638160), time.Date(1969, 3, 21, 20, 4, 0, 0, time.UTC), Tag{1, int64(-24638160)}, RawTag{1, hexDecode("3a0177f2cf")}, bigIntOrPanic("-24638160")}, - []reflect.Type{typeUint8, typeUint16, typeInt8, typeInt16, typeByteSlice, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt}, + []reflect.Type{typeUint8, typeUint16, typeInt8, typeInt16, typeByteSlice, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeByteString}, }, { hexDecode("d83dd183010203"), // 61(17([1, 2, 3])), nested tags 61 and 17 Tag{61, Tag{17, []interface{}{uint64(1), uint64(2), uint64(3)}}}, []interface{}{[]interface{}{uint64(1), uint64(2), uint64(3)}, []byte{1, 2, 3}, [0]byte{}, [1]byte{1}, [3]byte{1, 2, 3}, [5]byte{1, 2, 3, 0, 0}, []int{1, 2, 3}, []uint{1, 2, 3}, [...]int{1, 2, 3}, []float32{1, 2, 3}, []float64{1, 2, 3}, Tag{61, Tag{17, []interface{}{uint64(1), uint64(2), uint64(3)}}}, RawTag{61, hexDecode("d183010203")}}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{})}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeByteString}, }, } @@ -533,77 +534,77 @@ var unmarshalFloatTests = []unmarshalFloatTest{ hexDecode("f90000"), float64(0.0), []interface{}{float32(0.0), float64(0.0)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("f98000"), - float64(-0.0), - []interface{}{float32(-0.0), float64(-0.0)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + float64(-0.0), //nolint:staticcheck // we know -0.0 is 0.0 in Go + []interface{}{float32(-0.0), float64(-0.0)}, //nolint:staticcheck // we know -0.0 is 0.0 in Go + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("f93c00"), float64(1.0), []interface{}{float32(1.0), float64(1.0)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("f93e00"), float64(1.5), []interface{}{float32(1.5), float64(1.5)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("f97bff"), float64(65504.0), []interface{}{float32(65504.0), float64(65504.0)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("f90001"), // float16 subnormal value float64(5.960464477539063e-08), []interface{}{float32(5.960464477539063e-08), float64(5.960464477539063e-08)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 1e-16, }, { hexDecode("f90400"), float64(6.103515625e-05), []interface{}{float32(6.103515625e-05), float64(6.103515625e-05)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 1e-16, }, { hexDecode("f9c400"), float64(-4.0), []interface{}{float32(-4.0), float64(-4.0)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("f97c00"), math.Inf(1), []interface{}{math.Float32frombits(0x7f800000), math.Inf(1)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("f97e00"), math.NaN(), []interface{}{math.Float32frombits(0x7fc00000), math.NaN()}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("f9fc00"), math.Inf(-1), []interface{}{math.Float32frombits(0xff800000), math.Inf(-1)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, // float32 @@ -611,35 +612,35 @@ var unmarshalFloatTests = []unmarshalFloatTest{ hexDecode("fa47c35000"), float64(100000.0), []interface{}{float32(100000.0), float64(100000.0)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("fa7f7fffff"), float64(3.4028234663852886e+38), []interface{}{float32(3.4028234663852886e+38), float64(3.4028234663852886e+38)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 1e-9, }, { hexDecode("fa7f800000"), math.Inf(1), []interface{}{math.Float32frombits(0x7f800000), math.Inf(1)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("fa7fc00000"), math.NaN(), []interface{}{math.Float32frombits(0x7fc00000), math.NaN()}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("faff800000"), math.Inf(-1), []interface{}{math.Float32frombits(0xff800000), math.Inf(-1)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, // float64 @@ -647,42 +648,42 @@ var unmarshalFloatTests = []unmarshalFloatTest{ hexDecode("fb3ff199999999999a"), float64(1.1), []interface{}{float32(1.1), float64(1.1)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 1e-9, }, { hexDecode("fb7e37e43c8800759c"), float64(1.0e+300), []interface{}{float64(1.0e+300)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 1e-9, }, { hexDecode("fbc010666666666666"), float64(-4.1), []interface{}{float32(-4.1), float64(-4.1)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 1e-9, }, { hexDecode("fb7ff0000000000000"), math.Inf(1), []interface{}{math.Float32frombits(0x7f800000), math.Inf(1)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("fb7ff8000000000000"), math.NaN(), []interface{}{math.Float32frombits(0x7fc00000), math.NaN()}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("fbfff0000000000000"), math.Inf(-1), []interface{}{math.Float32frombits(0xff800000), math.Inf(-1)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, @@ -691,28 +692,28 @@ var unmarshalFloatTests = []unmarshalFloatTest{ hexDecode("f903ff"), float64(0.000060976), []interface{}{float32(0.000060976), float64(0.000060976)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 1e-9, }, { hexDecode("f93bff"), float64(0.999511719), []interface{}{float32(0.999511719), float64(0.999511719)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 1e-9, }, { hexDecode("f93c01"), float64(1.000976563), []interface{}{float32(1.000976563), float64(1.000976563)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 1e-9, }, { hexDecode("f93555"), float64(0.333251953125), []interface{}{float32(0.333251953125), float64(0.333251953125)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 1e-9, }, // CBOR test data "canonNums" are from https://github.com/cbor-wg/cbor-test-vectors @@ -720,70 +721,70 @@ var unmarshalFloatTests = []unmarshalFloatTest{ hexDecode("f9bd00"), float64(-1.25), []interface{}{float32(-1.25), float64(-1.25)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("f93e00"), float64(1.5), []interface{}{float32(1.5), float64(1.5)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("fb4024333333333333"), float64(10.1), []interface{}{float32(10.1), float64(10.1)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("f90001"), float64(5.960464477539063e-8), []interface{}{float32(5.960464477539063e-8), float64(5.960464477539063e-8)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("fa7f7fffff"), float64(3.4028234663852886e+38), []interface{}{float32(3.4028234663852886e+38), float64(3.4028234663852886e+38)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("f90400"), float64(0.00006103515625), []interface{}{float32(0.00006103515625), float64(0.00006103515625)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("f933ff"), float64(0.2498779296875), []interface{}{float32(0.2498779296875), float64(0.2498779296875)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("fa33000000"), float64(2.9802322387695312e-8), []interface{}{float32(2.9802322387695312e-8), float64(2.9802322387695312e-8)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("fa33333866"), float64(4.1727979294137185e-8), []interface{}{float32(4.1727979294137185e-8), float64(4.1727979294137185e-8)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, { hexDecode("fa37002000"), float64(0.000007636845111846924), []interface{}{float32(0.000007636845111846924), float64(0.000007636845111846924)}, - []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, + []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteArray, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt, typeByteString}, 0.0, }, } @@ -1194,6 +1195,9 @@ func TestUnmarshalNil(t *testing.T) { {"slice", []string{"hello", "world"}, []string(nil)}, {"map", map[string]bool{"hello": true, "goodbye": false}, map[string]bool(nil)}, + // Unmarshalling CBOR null to ByteString (string wrapper for []byte) resets ByteString to empty string. + {"cbor.ByteString", ByteString("\x01\x02\x03"), ByteString("")}, + // Unmarshalling CBOR null to time.Time is a no-op. {"time.Time", time.Date(2020, time.January, 2, 3, 4, 5, 6, time.UTC), time.Date(2020, time.January, 2, 3, 4, 5, 6, time.UTC)}, @@ -1211,11 +1215,9 @@ func TestUnmarshalNil(t *testing.T) { // It's tested in TestUnmarshal(). // Unmarshalling to types implementing cbor.BinaryUnmarshaler is a no-op. - //{"cbor.BinaryUnmarshaler", nilBinaryUnmarshaler("hello world"), nilBinaryUnmarshaler("hello world")}, {"cbor.BinaryUnmarshaler", number(456), number(456)}, // When unmarshalling to types implementing cbor.Unmarshaler, - // UnmarshalCBOR function receives raw CBOR bytes (0xf6 or 0xf7). {"cbor.Unmarshaler", nilUnmarshaler("hello world"), nilUnmarshaler("null")}, } @@ -1367,6 +1369,10 @@ var invalidCBORUnmarshalTests = []struct { {"Major type 0 with additional information 31", hexDecode("1f"), "cbor: invalid additional information 31 for type positive integer", true}, {"Major type 1 with additional information 31", hexDecode("3f"), "cbor: invalid additional information 31 for type negative integer", true}, {"Major type 6 with additional information 31", hexDecode("df"), "cbor: invalid additional information 31 for type tag", true}, + // Extraneous data + {"two ints", hexDecode("0001"), "cbor: 1 bytes of extraneous data starting at index 1", false}, + {"two arrays", hexDecode("830102038104"), "cbor: 2 bytes of extraneous data starting at index 4", false}, + {"int and partial array", hexDecode("00830102"), "cbor: 3 bytes of extraneous data starting at index 1", false}, } func TestInvalidCBORUnmarshal(t *testing.T) { @@ -1385,33 +1391,157 @@ func TestInvalidCBORUnmarshal(t *testing.T) { } } -func TestInvalidUTF8TextString(t *testing.T) { - invalidUTF8TextStringTests := []struct { +func TestValidUTF8String(t *testing.T) { + dmRejectInvalidUTF8, err := DecOptions{UTF8: UTF8RejectInvalid}.DecMode() + if err != nil { + t.Errorf("DecMode() returned an error %+v", err) + } + dmDecodeInvalidUTF8, err := DecOptions{UTF8: UTF8DecodeInvalid}.DecMode() + if err != nil { + t.Errorf("DecMode() returned an error %+v", err) + } + + testCases := []struct { + name string + cborData []byte + dm DecMode + wantObj interface{} + }{ + { + name: "with UTF8RejectInvalid", + cborData: hexDecode("6973747265616d696e67"), + dm: dmRejectInvalidUTF8, + wantObj: "streaming", + }, + { + name: "with UTF8DecodeInvalid", + cborData: hexDecode("6973747265616d696e67"), + dm: dmDecodeInvalidUTF8, + wantObj: "streaming", + }, + { + name: "indef length with UTF8RejectInvalid", + cborData: hexDecode("7f657374726561646d696e67ff"), + dm: dmRejectInvalidUTF8, + wantObj: "streaming", + }, + { + name: "indef length with UTF8DecodeInvalid", + cborData: hexDecode("7f657374726561646d696e67ff"), + dm: dmDecodeInvalidUTF8, + wantObj: "streaming", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Decode to empty interface + var i interface{} + err = tc.dm.Unmarshal(tc.cborData, &i) + if err != nil { + t.Errorf("Unmarshal(0x%x) returned error %q", tc.cborData, err) + } + if !reflect.DeepEqual(i, tc.wantObj) { + t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", tc.cborData, i, i, tc.wantObj, tc.wantObj) + } + + // Decode to string + var v string + err = tc.dm.Unmarshal(tc.cborData, &v) + if err != nil { + t.Errorf("Unmarshal(0x%x) returned error %q", tc.cborData, err) + } + if !reflect.DeepEqual(v, tc.wantObj) { + t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", tc.cborData, v, v, tc.wantObj, tc.wantObj) + } + }) + } +} + +func TestInvalidUTF8String(t *testing.T) { + dmRejectInvalidUTF8, err := DecOptions{UTF8: UTF8RejectInvalid}.DecMode() + if err != nil { + t.Errorf("DecMode() returned an error %+v", err) + } + dmDecodeInvalidUTF8, err := DecOptions{UTF8: UTF8DecodeInvalid}.DecMode() + if err != nil { + t.Errorf("DecMode() returned an error %+v", err) + } + + testCases := []struct { name string cborData []byte + dm DecMode + wantObj interface{} wantErrorMsg string }{ - {"definite length text string", hexDecode("61fe"), invalidUTF8ErrorMsg}, - {"indefinite length text string", hexDecode("7f62e6b061b4ff"), invalidUTF8ErrorMsg}, + { + name: "with UTF8RejectInvalid", + cborData: hexDecode("61fe"), + dm: dmRejectInvalidUTF8, + wantErrorMsg: invalidUTF8ErrorMsg, + }, + { + name: "with UTF8DecodeInvalid", + cborData: hexDecode("61fe"), + dm: dmDecodeInvalidUTF8, + wantObj: string([]byte{0xfe}), + }, + { + name: "indef length with UTF8RejectInvalid", + cborData: hexDecode("7f62e6b061b4ff"), + dm: dmRejectInvalidUTF8, + wantErrorMsg: invalidUTF8ErrorMsg, + }, + { + name: "indef length with UTF8DecodeInvalid", + cborData: hexDecode("7f62e6b061b4ff"), + dm: dmDecodeInvalidUTF8, + wantObj: string([]byte{0xe6, 0xb0, 0xb4}), + }, } - for _, tc := range invalidUTF8TextStringTests { + + for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - var i interface{} - if err := Unmarshal(tc.cborData, &i); err == nil { - t.Errorf("Unmarshal(0x%x) didn't return an error", tc.cborData) - } else if err.Error() != tc.wantErrorMsg { - t.Errorf("Unmarshal(0x%x) error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) + // Decode to empty interface + var v interface{} + err = tc.dm.Unmarshal(tc.cborData, &v) + if tc.wantErrorMsg != "" { + if err == nil { + t.Errorf("Unmarshal(0x%x) didn't return error", tc.cborData) + } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { + t.Errorf("Unmarshal(0x%x) error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) + } + } else { + if err != nil { + t.Errorf("Unmarshal(0x%x) returned error %q", tc.cborData, err) + } + if !reflect.DeepEqual(v, tc.wantObj) { + t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", tc.cborData, v, v, tc.wantObj, tc.wantObj) + } } + // Decode to string var s string - if err := Unmarshal(tc.cborData, &s); err == nil { - t.Errorf("Unmarshal(0x%x) didn't return an error", tc.cborData) - } else if err.Error() != tc.wantErrorMsg { - t.Errorf("Unmarshal(0x%x) error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) + err = tc.dm.Unmarshal(tc.cborData, &s) + if tc.wantErrorMsg != "" { + if err == nil { + t.Errorf("Unmarshal(0x%x) didn't return error", tc.cborData) + } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { + t.Errorf("Unmarshal(0x%x) error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) + } + } else { + if err != nil { + t.Errorf("Unmarshal(0x%x) returned error %q", tc.cborData, err) + } + if !reflect.DeepEqual(s, tc.wantObj) { + t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", tc.cborData, s, s, tc.wantObj, tc.wantObj) + } } }) } + // Test decoding of mixed invalid text string and valid text string + // with UTF8RejectInvalid option (default) cborData := hexDecode("7f62e6b061b4ff7f657374726561646d696e67ff") dec := NewDecoder(bytes.NewReader(cborData)) var s string @@ -1425,6 +1555,20 @@ func TestInvalidUTF8TextString(t *testing.T) { } else if s != "streaming" { t.Errorf("Decode() returned %q, want %q", s, "streaming") } + + // Test decoding of mixed invalid text string and valid text string + // with UTF8DecodeInvalid option + dec = dmDecodeInvalidUTF8.NewDecoder(bytes.NewReader(cborData)) + if err := dec.Decode(&s); err != nil { + t.Errorf("Decode() returned error %q", err) + } else if s != string([]byte{0xe6, 0xb0, 0xb4}) { + t.Errorf("Decode() returned %q, want %q", s, string([]byte{0xe6, 0xb0, 0xb4})) + } + if err := dec.Decode(&s); err != nil { + t.Errorf("Decode() returned error %v", err) + } else if s != "streaming" { + t.Errorf("Decode() returned %q, want %q", s, "streaming") + } } func TestUnmarshalStruct(t *testing.T) { @@ -1700,17 +1844,14 @@ func TestMapKeyUnhashable(t *testing.T) { cborData []byte wantErrorMsg string }{ - {"slice as map key", hexDecode("bf8030ff"), "cbor: invalid map key type: []interface {}"}, // {[]: -17} - {"slice as map key", hexDecode("a1813030"), "cbor: invalid map key type: []interface {}"}, // {[-17]: -17} - {"slice as map key", hexDecode("bfd1a388f730303030303030303030303030ff"), "cbor: invalid map key type: []interface {}"}, // {17({[undefined, -17, -17, -17, -17, -17, -17, -17]: -17, -17: -17}): -17}} - {"byte slice as map key", hexDecode("8f3030a730304430303030303030303030303030303030303030303030303030303030"), "cbor: invalid map key type: []uint8"}, // [-17, -17, {-17: -17, h'30303030': -17}, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17] - {"map as map key", hexDecode("bf30a1a030ff"), "cbor: invalid map key type: map"}, // {-17: {{}: -17}}, empty map as map key - {"map as map key", hexDecode("bfb0303030303030303030303030303030303030303030303030303030303030303030ff"), "cbor: invalid map key type: map"}, // {{-17: -17}: -17}, map as key - {"tagged slice as map key", hexDecode("a1c84c30303030303030303030303030"), "cbor: invalid map key type: cbor.Tag"}, // {8(h'303030303030303030303030'): -17} - {"nested-tagged slice as map key", hexDecode("a33030306430303030d1cb4030"), "cbor: invalid map key type: cbor.Tag"}, // {-17: "0000", 17(11(h'')): -17} - {"big.Int as map key", hexDecode("a13bbd3030303030303030"), "cbor: invalid map key type: big.Int"}, // {-13632449055575519281: -17} - {"tagged big.Int as map key", hexDecode("a1c24901000000000000000030"), "cbor: invalid map key type: big.Int"}, // {18446744073709551616: -17} - {"tagged big.Int as map key", hexDecode("a1c34901000000000000000030"), "cbor: invalid map key type: big.Int"}, //{-18446744073709551617: -17} + {"slice as map key", hexDecode("bf8030ff"), "cbor: invalid map key type: []interface {}"}, // {[]: -17} + {"slice as map key", hexDecode("a1813030"), "cbor: invalid map key type: []interface {}"}, // {[-17]: -17} + {"slice as map key", hexDecode("bfd1a388f730303030303030303030303030ff"), "cbor: invalid map key type: []interface {}"}, // {17({[undefined, -17, -17, -17, -17, -17, -17, -17]: -17, -17: -17}): -17}} + {"map as map key", hexDecode("bf30a1a030ff"), "cbor: invalid map key type: map"}, // {-17: {{}: -17}}, empty map as map key + {"map as map key", hexDecode("bfb0303030303030303030303030303030303030303030303030303030303030303030ff"), "cbor: invalid map key type: map"}, // {{-17: -17}: -17}, map as key + {"big.Int as map key", hexDecode("a13bbd3030303030303030"), "cbor: invalid map key type: big.Int"}, // {-13632449055575519281: -17} + {"tagged big.Int as map key", hexDecode("a1c24901000000000000000030"), "cbor: invalid map key type: big.Int"}, // {18446744073709551616: -17} + {"tagged big.Int as map key", hexDecode("a1c34901000000000000000030"), "cbor: invalid map key type: big.Int"}, // {-18446744073709551617: -17} } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { @@ -2995,6 +3136,44 @@ func TestUnmarshalIntoMapError(t *testing.T) { } } +func TestUnmarshalDeepNesting(t *testing.T) { + // Construct this object rather than embed such a large constant in the code + type TestNode struct { + Value int + Child *TestNode + } + n := &TestNode{Value: 0} + root := n + for i := 0; i < 65534; i++ { + child := &TestNode{Value: i} + n.Child = child + n = child + } + em, err := EncOptions{}.EncMode() + if err != nil { + t.Errorf("EncMode() returned error %v", err) + } + cborData, err := em.Marshal(root) + if err != nil { + t.Errorf("Marshal() deeply nested object returned error %v", err) + } + + // Try unmarshal it + dm, err := DecOptions{MaxNestedLevels: 65535}.DecMode() + if err != nil { + t.Errorf("DecMode() returned error %v", err) + } + var readback TestNode + err = dm.Unmarshal(cborData, &readback) + if err != nil { + t.Errorf("Unmarshal() of deeply nested object returned error: %v", err) + } + if !reflect.DeepEqual(root, &readback) { + t.Errorf("Unmarshal() of deeply nested object did not match\nGot: %#v\n Want: %#v\n", + &readback, root) + } +} + func TestStructToArrayError(t *testing.T) { type coseHeader struct { Alg int `cbor:"1,keyasint,omitempty"` @@ -3052,7 +3231,7 @@ func TestStructKeyAsIntError(t *testing.T) { func TestUnmarshalToNotNilInterface(t *testing.T) { cborData := hexDecode("83010203") // []uint64{1, 2, 3} s := "hello" //nolint:goconst - var v interface{} = s // Unmarshal() sees v as type inteface{} and sets CBOR data as default Go type. s is unmodified. Same behavior as encoding/json. + var v interface{} = s // Unmarshal() sees v as type interface{} and sets CBOR data as default Go type. s is unmodified. Same behavior as encoding/json. wantV := []interface{}{uint64(1), uint64(2), uint64(3)} if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) @@ -3065,15 +3244,16 @@ func TestUnmarshalToNotNilInterface(t *testing.T) { func TestDecOptions(t *testing.T) { opts1 := DecOptions{ - TimeTag: DecTagRequired, DupMapKey: DupMapKeyEnforcedAPF, - IndefLength: IndefLengthForbidden, + TimeTag: DecTagRequired, MaxNestedLevels: 100, - MaxMapPairs: 101, MaxArrayElements: 102, + MaxMapPairs: 101, + IndefLength: IndefLengthForbidden, TagsMd: TagsForbidden, IntDec: IntDecConvertSigned, ExtraReturnErrors: ExtraDecErrorUnknownField, + UTF8: UTF8DecodeInvalid, } dm, err := opts1.DecMode() if err != nil { @@ -3154,12 +3334,12 @@ func TestDecModeInvalidMaxNestedLevel(t *testing.T) { { name: "MaxNestedLevels < 4", opts: DecOptions{MaxNestedLevels: 1}, - wantErrorMsg: "cbor: invalid MaxNestedLevels 1 (range is [4, 256])", + wantErrorMsg: "cbor: invalid MaxNestedLevels 1 (range is [4, 65535])", }, { - name: "MaxNestedLevels > 256", - opts: DecOptions{MaxNestedLevels: 257}, - wantErrorMsg: "cbor: invalid MaxNestedLevels 257 (range is [4, 256])", + name: "MaxNestedLevels > 65535", + opts: DecOptions{MaxNestedLevels: 65536}, + wantErrorMsg: "cbor: invalid MaxNestedLevels 65536 (range is [4, 65535])", }, } for _, tc := range testCases { @@ -4483,6 +4663,101 @@ func TestIntDec(t *testing.T) { } } +func TestDecModeInvalidMapKeyByteString(t *testing.T) { + wantErrorMsg := "cbor: invalid MapKeyByteString 101" + _, err := DecOptions{MapKeyByteString: 101}.DecMode() + if err == nil { + t.Errorf("DecMode() didn't return an error") + } else if err.Error() != wantErrorMsg { + t.Errorf("DecMode() returned error %q, want %q", err.Error(), wantErrorMsg) + } +} + +func TestMapKeyByteString(t *testing.T) { + bsForbiddenMode, err := DecOptions{MapKeyByteString: MapKeyByteStringForbidden}.DecMode() + if err != nil { + t.Errorf("DecMode() returned an error %+v", err) + } + + bsAllowedMode, err := DecOptions{MapKeyByteString: MapKeyByteStringAllowed}.DecMode() + if err != nil { + t.Errorf("DecMode() returned an error %+v", err) + } + + testCases := []struct { + name string + cborData []byte + wantObj interface{} + wantErrorMsg string + dm DecMode + }{ + { + name: "byte string map key with MapKeyByteStringForbidden", + cborData: hexDecode("a143abcdef187b"), + wantErrorMsg: "cbor: invalid map key type: []uint8", + dm: bsForbiddenMode, + }, + { + name: "tagged byte string map key with MapKeyByteStringForbidden", + cborData: hexDecode("a1d86443abcdef187b"), + wantErrorMsg: "cbor: invalid map key type: cbor.Tag", + dm: bsForbiddenMode, + }, + { + name: "nested tagged byte string map key with MapKeyByteStringForbidden", + cborData: hexDecode("a1d865d86443abcdef187b"), + wantErrorMsg: "cbor: invalid map key type: cbor.Tag", + dm: bsForbiddenMode, + }, + { + name: "byte string map key with MapKeyByteStringAllowed", + cborData: hexDecode("a143abcdef187b"), + wantObj: map[interface{}]interface{}{ + ByteString("\xab\xcd\xef"): uint64(123), + }, + dm: bsAllowedMode, + }, + { + name: "tagged byte string map key with MapKeyByteStringAllowed", + cborData: hexDecode("a1d86443abcdef187b"), + wantObj: map[interface{}]interface{}{ + Tag{Number: 100, Content: ByteString("\xab\xcd\xef")}: uint64(123), + }, + dm: bsAllowedMode, + }, + { + name: "nested tagged byte string map key with MapKeyByteStringAllowed", + cborData: hexDecode("a1d865d86443abcdef187b"), + wantObj: map[interface{}]interface{}{ + Tag{Number: 101, Content: Tag{Number: 100, Content: ByteString("\xab\xcd\xef")}}: uint64(123), + }, + dm: bsAllowedMode, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + for _, typ := range []reflect.Type{typeIntf, typeMapIntfIntf} { + v := reflect.New(typ) + vPtr := v.Interface() + err = tc.dm.Unmarshal(tc.cborData, vPtr) + if err == nil { + if tc.wantErrorMsg != "" { + t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", tc.cborData, tc.wantErrorMsg) + } else if !reflect.DeepEqual(v.Elem().Interface(), tc.wantObj) { + t.Errorf("Unmarshal(0x%x) return %v (%T), want %v (%T)", tc.cborData, v.Elem().Interface(), v.Elem().Interface(), tc.wantObj, tc.wantObj) + } + } else { + if tc.wantErrorMsg == "" { + t.Errorf("Unmarshal(0x%x) returned error %q", tc.cborData, err) + } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { + t.Errorf("Unmarshal(0x%x) returned error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) + } + } + } + }) + } +} + func TestDecModeInvalidExtraError(t *testing.T) { wantErrorMsg := "cbor: invalid ExtraReturnErrors 3" _, err := DecOptions{ExtraReturnErrors: 3}.DecMode() @@ -4493,7 +4768,7 @@ func TestDecModeInvalidExtraError(t *testing.T) { } } -func TestExtraErrorCondUnknowField(t *testing.T) { +func TestExtraErrorCondUnknownField(t *testing.T) { type s struct { A string B string @@ -4568,7 +4843,17 @@ func TestExtraErrorCondUnknowField(t *testing.T) { } } -func TestStreamExtraErrorCondUnknowField(t *testing.T) { +func TestInvalidUTF8Mode(t *testing.T) { + wantErrorMsg := "cbor: invalid UTF8 2" + _, err := DecOptions{UTF8: 2}.DecMode() + if err == nil { + t.Errorf("DecMode() didn't return an error") + } else if err.Error() != wantErrorMsg { + t.Errorf("DecMode() returned error %q, want %q", err.Error(), wantErrorMsg) + } +} + +func TestStreamExtraErrorCondUnknownField(t *testing.T) { type s struct { A string B string @@ -5251,63 +5536,63 @@ func TestUnmarshalToInterface(t *testing.T) { }{ { name: "uint", - data: hexDecode("a2016b736f6d65206d657373676502187b"), // {1: "some messge", 2: 123} + data: hexDecode("a2016c736f6d65206d65737361676502187b"), // {1: "some message", 2: 123} v: &TestExample{ - Message: "some messge", + Message: "some message", Foo: &uintFoo123, }, unmarshalToObj: &TestExample{Foo: &uintFoo}, }, { name: "int", - data: hexDecode("a2016b736f6d65206d65737367650220"), // {1: "some messge", 2: -1} + data: hexDecode("a2016c736f6d65206d6573736167650220"), // {1: "some message", 2: -1} v: &TestExample{ - Message: "some messge", + Message: "some message", Foo: &intFooNeg1, }, unmarshalToObj: &TestExample{Foo: &intFoo}, }, { name: "bytes", - data: hexDecode("a2016b736f6d65206d65737367650243010203"), // {1: "some messge", 2: [1,2,3]} + data: hexDecode("a2016c736f6d65206d6573736167650243010203"), // {1: "some message", 2: [1,2,3]} v: &TestExample{ - Message: "some messge", + Message: "some message", Foo: &byteFoo123, }, unmarshalToObj: &TestExample{Foo: &byteFoo}, }, { name: "string", - data: hexDecode("a2016b736f6d65206d65737367650263313233"), // {1: "some messge", 2: "123"} + data: hexDecode("a2016c736f6d65206d6573736167650263313233"), // {1: "some message", 2: "123"} v: &TestExample{ - Message: "some messge", + Message: "some message", Foo: &stringFoo123, }, unmarshalToObj: &TestExample{Foo: &stringFoo}, }, { name: "array", - data: hexDecode("a2016b736f6d65206d65737367650283010203"), // {1: "some messge", 2: []int{1,2,3}} + data: hexDecode("a2016c736f6d65206d6573736167650283010203"), // {1: "some message", 2: []int{1,2,3}} v: &TestExample{ - Message: "some messge", + Message: "some message", Foo: &arrayFoo123, }, unmarshalToObj: &TestExample{Foo: &arrayFoo}, }, { name: "map", - data: hexDecode("a2016b736f6d65206d657373676502a3010102020303"), // {1: "some messge", 2: map[int]int{1:1,2:2,3:3}} + data: hexDecode("a2016c736f6d65206d65737361676502a3010102020303"), // {1: "some message", 2: map[int]int{1:1,2:2,3:3}} v: &TestExample{ - Message: "some messge", + Message: "some message", Foo: &mapFoo123, }, unmarshalToObj: &TestExample{Foo: &mapFoo}, }, { name: "struct", - data: hexDecode("a2016b736f6d65206d657373676502a1011901c8"), // {1: "some messge", 2: {1: 456}} + data: hexDecode("a2016c736f6d65206d65737361676502a1011901c8"), // {1: "some message", 2: {1: 456}} v: &TestExample{ - Message: "some messge", + Message: "some message", Foo: &StructFoo{Value: 456}, }, unmarshalToObj: &TestExample{Foo: &StructFoo{}}, @@ -5402,3 +5687,354 @@ func TestUnmarshalTaggedDataToInterface(t *testing.T) { t.Errorf("Unmarshal(0x%x) = %v, want %v", data, v2, v) } } + +type B interface { + Foo() +} + +type C struct { + Field int +} + +func (c *C) Foo() {} + +type D struct { + Field string +} + +func (d *D) Foo() {} + +type A1 struct { + Field B +} + +type A2 struct { + Fields []B +} + +func TestUnmarshalRegisteredTagToInterface(t *testing.T) { + var err error + tags := NewTagSet() + err = tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, reflect.TypeOf(C{}), 279) + if err != nil { + t.Error(err) + } + err = tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, reflect.TypeOf(D{}), 280) + if err != nil { + t.Error(err) + } + + encMode, _ := PreferredUnsortedEncOptions().EncModeWithTags(tags) + decMode, _ := DecOptions{}.DecModeWithTags(tags) + + v1 := A1{Field: &C{Field: 5}} + data1, err := encMode.Marshal(v1) + if err != nil { + t.Fatalf("Marshal(%+v) returned error %v", v1, err) + } + + v2 := A2{Fields: []B{&C{Field: 5}, &D{Field: "a"}}} + data2, err := encMode.Marshal(v2) + if err != nil { + t.Fatalf("Marshal(%+v) returned error %v", v2, err) + } + + testCases := []struct { + name string + data []byte + unmarshalToObj interface{} + wantValue interface{} + }{ + { + name: "interface type", + data: data1, + unmarshalToObj: &A1{}, + wantValue: &v1, + }, + { + name: "concrete type", + data: data1, + unmarshalToObj: &A1{Field: &C{}}, + wantValue: &v1, + }, + { + name: "slice of interface type", + data: data2, + unmarshalToObj: &A2{}, + wantValue: &v2, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err = decMode.Unmarshal(tc.data, tc.unmarshalToObj) + if err != nil { + t.Errorf("Unmarshal(0x%x) returned error %v", tc.data, err) + } + if !reflect.DeepEqual(tc.unmarshalToObj, tc.wantValue) { + t.Errorf("Unmarshal(0x%x) = %v, want %v", tc.data, tc.unmarshalToObj, tc.wantValue) + } + }) + } +} + +func TestDecModeInvalidDefaultMapType(t *testing.T) { + testCases := []struct { + name string + opts DecOptions + wantErrorMsg string + }{ + { + name: "byte slice", + opts: DecOptions{DefaultMapType: reflect.TypeOf([]byte(nil))}, + wantErrorMsg: "cbor: invalid DefaultMapType []uint8", + }, + { + name: "int slice", + opts: DecOptions{DefaultMapType: reflect.TypeOf([]int(nil))}, + wantErrorMsg: "cbor: invalid DefaultMapType []int", + }, + { + name: "string", + opts: DecOptions{DefaultMapType: reflect.TypeOf("")}, + wantErrorMsg: "cbor: invalid DefaultMapType string", + }, + { + name: "unnamed struct type", + opts: DecOptions{DefaultMapType: reflect.TypeOf(struct{}{})}, + wantErrorMsg: "cbor: invalid DefaultMapType struct {}", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.DecMode() + if err == nil { + t.Errorf("DecMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("DecMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) + } +} + +func TestUnmarshalToDefaultMapType(t *testing.T) { + + cborDataMapIntInt := hexDecode("a201020304") // {1: 2, 3: 4} + cborDataMapStringInt := hexDecode("a2616101616202") // {"a": 1, "b": 2} + cborDataArrayOfMapStringint := hexDecode("82a2616101616202a2616303616404") // [{"a": 1, "b": 2}, {"c": 3, "d": 4}] + cborDataNestedMap := hexDecode("a268496e744669656c6401684d61704669656c64a2616101616202") // {"IntField": 1, "MapField": {"a": 1, "b": 2}} + + decOptionsDefault := DecOptions{} + decOptionsMapIntfIntfType := DecOptions{DefaultMapType: reflect.TypeOf(map[interface{}]interface{}(nil))} + decOptionsMapStringIntType := DecOptions{DefaultMapType: reflect.TypeOf(map[string]int(nil))} + decOptionsMapStringIntfType := DecOptions{DefaultMapType: reflect.TypeOf(map[string]interface{}(nil))} + + testCases := []struct { + name string + opts DecOptions + cborData []byte + wantValue interface{} + wantErrorMsg string + }{ + // Decode CBOR map to map[interface{}]interface{} using default options + { + name: "decode CBOR map[int]int to Go map[interface{}]interface{} (default)", + opts: decOptionsDefault, + cborData: cborDataMapIntInt, + wantValue: map[interface{}]interface{}{uint64(1): uint64(2), uint64(3): uint64(4)}, + }, + { + name: "decode CBOR map[string]int to Go map[interface{}]interface{} (default)", + opts: decOptionsDefault, + cborData: cborDataMapStringInt, + wantValue: map[interface{}]interface{}{"a": uint64(1), "b": uint64(2)}, + }, + { + name: "decode CBOR array of map[string]int to Go []map[interface{}]interface{} (default)", + opts: decOptionsDefault, + cborData: cborDataArrayOfMapStringint, + wantValue: []interface{}{ + map[interface{}]interface{}{"a": uint64(1), "b": uint64(2)}, + map[interface{}]interface{}{"c": uint64(3), "d": uint64(4)}, + }, + }, + { + name: "decode CBOR nested map to Go map[interface{}]interface{} (default)", + opts: decOptionsDefault, + cborData: cborDataNestedMap, + wantValue: map[interface{}]interface{}{ + "IntField": uint64(1), + "MapField": map[interface{}]interface{}{"a": uint64(1), "b": uint64(2)}, + }, + }, + // Decode CBOR map to map[interface{}]interface{} using default map type option + { + name: "decode CBOR map[int]int to Go map[interface{}]interface{}", + opts: decOptionsMapIntfIntfType, + cborData: cborDataMapIntInt, + wantValue: map[interface{}]interface{}{uint64(1): uint64(2), uint64(3): uint64(4)}, + }, + { + name: "decode CBOR map[string]int to Go map[interface{}]interface{}", + opts: decOptionsMapIntfIntfType, + cborData: cborDataMapStringInt, + wantValue: map[interface{}]interface{}{"a": uint64(1), "b": uint64(2)}, + }, + { + name: "decode CBOR array of map[string]int to Go []map[interface{}]interface{}", + opts: decOptionsMapIntfIntfType, + cborData: cborDataArrayOfMapStringint, + wantValue: []interface{}{ + map[interface{}]interface{}{"a": uint64(1), "b": uint64(2)}, + map[interface{}]interface{}{"c": uint64(3), "d": uint64(4)}, + }, + }, + { + name: "decode CBOR nested map to Go map[interface{}]interface{}", + opts: decOptionsMapIntfIntfType, + cborData: cborDataNestedMap, + wantValue: map[interface{}]interface{}{ + "IntField": uint64(1), + "MapField": map[interface{}]interface{}{"a": uint64(1), "b": uint64(2)}, + }, + }, + // Decode CBOR map to map[string]interface{} using default map type option + { + name: "decode CBOR map[int]int to Go map[string]interface{}", + opts: decOptionsMapStringIntfType, + cborData: cborDataMapIntInt, + wantErrorMsg: "cbor: cannot unmarshal positive integer into Go value of type string", + }, + { + name: "decode CBOR map[string]int to Go map[string]interface{}", + opts: decOptionsMapStringIntfType, + cborData: cborDataMapStringInt, + wantValue: map[string]interface{}{"a": uint64(1), "b": uint64(2)}, + }, + { + name: "decode CBOR array of map[string]int to Go []map[string]interface{}", + opts: decOptionsMapStringIntfType, + cborData: cborDataArrayOfMapStringint, + wantValue: []interface{}{ + map[string]interface{}{"a": uint64(1), "b": uint64(2)}, + map[string]interface{}{"c": uint64(3), "d": uint64(4)}, + }, + }, + { + name: "decode CBOR nested map to Go map[string]interface{}", + opts: decOptionsMapStringIntfType, + cborData: cborDataNestedMap, + wantValue: map[string]interface{}{ + "IntField": uint64(1), + "MapField": map[string]interface{}{"a": uint64(1), "b": uint64(2)}, + }, + }, + // Decode CBOR map to map[string]int using default map type option + { + name: "decode CBOR map[int]int to Go map[string]int", + opts: decOptionsMapStringIntType, + cborData: cborDataMapIntInt, + wantErrorMsg: "cbor: cannot unmarshal positive integer into Go value of type string", + }, + { + name: "decode CBOR map[string]int to Go map[string]int", + opts: decOptionsMapStringIntType, + cborData: cborDataMapStringInt, + wantValue: map[string]int{"a": 1, "b": 2}, + }, + { + name: "decode CBOR array of map[string]int to Go []map[string]int", + opts: decOptionsMapStringIntType, + cborData: cborDataArrayOfMapStringint, + wantValue: []interface{}{ + map[string]int{"a": 1, "b": 2}, + map[string]int{"c": 3, "d": 4}, + }, + }, + { + name: "decode CBOR nested map to Go map[string]int", + opts: decOptionsMapStringIntType, + cborData: cborDataNestedMap, + wantErrorMsg: "cbor: cannot unmarshal map into Go value of type int", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + decMode, _ := tc.opts.DecMode() + + var v interface{} + err := decMode.Unmarshal(tc.cborData, &v) + if err != nil { + if tc.wantErrorMsg == "" { + t.Errorf("Unmarshal(0x%x) to empty interface returned error %v", tc.cborData, err) + } else if tc.wantErrorMsg != err.Error() { + t.Errorf("Unmarshal(0x%x) error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) + } + } else { + if tc.wantValue == nil { + t.Errorf("Unmarshal(0x%x) = %v (%T), want error %q", tc.cborData, v, v, tc.wantErrorMsg) + } else if !reflect.DeepEqual(v, tc.wantValue) { + t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v, v, tc.wantValue, tc.wantValue) + } + } + }) + } +} + +func TestUnmarshalFirstNoTrailing(t *testing.T) { + for _, tc := range unmarshalTests { + var v interface{} + if rest, err := UnmarshalFirst(tc.cborData, &v); err != nil { + t.Errorf("UnmarshalFirst(0x%x) returned error %v", tc.cborData, err) + } else { + if len(rest) != 0 { + t.Errorf("UnmarshalFirst(0x%x) returned rest %x (want [])", tc.cborData, rest) + } + // Check the value as well, although this is covered by other tests + if tm, ok := tc.emptyInterfaceValue.(time.Time); ok { + if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) { + t.Errorf("UnmarshalFirst(0x%x) = %v (%T), want %v (%T)", tc.cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + } + } else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) { + t.Errorf("UnmarshalFirst(0x%x) = %v (%T), want %v (%T)", tc.cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + } + } + } +} + +func TestUnmarshalfirstTrailing(t *testing.T) { + // Random trailing data + trailingData := hexDecode("4a6b0f4718c73f391091ea1c") + for _, tc := range unmarshalTests { + data := make([]byte, 0, len(tc.cborData)+len(trailingData)) + data = append(data, tc.cborData...) + data = append(data, trailingData...) + var v interface{} + if rest, err := UnmarshalFirst(data, &v); err != nil { + t.Errorf("UnmarshalFirst(0x%x) returned error %v", data, err) + } else { + if !bytes.Equal(trailingData, rest) { + t.Errorf("UnmarshalFirst(0x%x) returned rest %x (want %x)", data, rest, trailingData) + } + // Check the value as well, although this is covered by other tests + if tm, ok := tc.emptyInterfaceValue.(time.Time); ok { + if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) { + t.Errorf("UnmarshalFirst(0x%x) = %v (%T), want %v (%T)", data, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + } + } else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) { + t.Errorf("UnmarshalFirst(0x%x) = %v (%T), want %v (%T)", data, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + } + } + } +} + +func TestUnmarshalFirstInvalidItem(t *testing.T) { + // UnmarshalFirst should not return "rest" if the item was not well-formed + invalidCBOR := hexDecode("83FF20030102") + var v interface{} + rest, err := UnmarshalFirst(invalidCBOR, &v) + if rest != nil || err == nil { + t.Errorf("UnmarshalFirst(0x%x) = (%x, %v), want (nil, err)", invalidCBOR, rest, err) + } +} diff --git a/vendor/github.com/fxamacker/cbor/v2/diagnose.go b/vendor/github.com/fxamacker/cbor/v2/diagnose.go new file mode 100644 index 00000000..dcb03002 --- /dev/null +++ b/vendor/github.com/fxamacker/cbor/v2/diagnose.go @@ -0,0 +1,741 @@ +// Copyright (c) Faye Amacker. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +package cbor + +import ( + "bytes" + "encoding/base32" + "encoding/base64" + "encoding/hex" + "errors" + "io" + "math" + "math/big" + "strconv" + "unicode/utf16" + "unicode/utf8" + + "github.com/x448/float16" +) + +// DiagMode is the main interface for CBOR diagnostic notation. +type DiagMode interface { + // Diagnose returns extended diagnostic notation (EDN) of CBOR data items using this DiagMode. + Diagnose([]byte) (string, error) + + // DiagnoseFirst returns extended diagnostic notation (EDN) of the first CBOR data item using the DiagMode. Any remaining bytes are returned in rest. + DiagnoseFirst([]byte) (string, []byte, error) + + // DiagOptions returns user specified options used to create this DiagMode. + DiagOptions() DiagOptions +} + +// ByteStringEncoding specifies the base encoding that byte strings are notated. +type ByteStringEncoding uint8 + +const ( + // ByteStringBase16Encoding encodes byte strings in base16, without padding. + ByteStringBase16Encoding ByteStringEncoding = iota + + // ByteStringBase32Encoding encodes byte strings in base32, without padding. + ByteStringBase32Encoding + + // ByteStringBase32HexEncoding encodes byte strings in base32hex, without padding. + ByteStringBase32HexEncoding + + // ByteStringBase64Encoding encodes byte strings in base64url, without padding. + ByteStringBase64Encoding + + maxByteStringEncoding +) + +func (bse ByteStringEncoding) valid() error { + if bse >= maxByteStringEncoding { + return errors.New("cbor: invalid ByteStringEncoding " + strconv.Itoa(int(bse))) + } + return nil +} + +// DiagOptions specifies Diag options. +type DiagOptions struct { + // ByteStringEncoding specifies the base encoding that byte strings are notated. + // Default is ByteStringBase16Encoding. + ByteStringEncoding ByteStringEncoding + + // ByteStringHexWhitespace specifies notating with whitespace in byte string + // when ByteStringEncoding is ByteStringBase16Encoding. + ByteStringHexWhitespace bool + + // ByteStringText specifies notating with text in byte string + // if it is a valid UTF-8 text. + ByteStringText bool + + // ByteStringEmbeddedCBOR specifies notating embedded CBOR in byte string + // if it is a valid CBOR bytes. + ByteStringEmbeddedCBOR bool + + // CBORSequence specifies notating CBOR sequences. + // otherwise, it returns an error if there are more bytes after the first CBOR. + CBORSequence bool + + // FloatPrecisionIndicator specifies appending a suffix to indicate float precision. + // Refer to https://www.rfc-editor.org/rfc/rfc8949.html#name-encoding-indicators. + FloatPrecisionIndicator bool + + // MaxNestedLevels specifies the max nested levels allowed for any combination of CBOR array, maps, and tags. + // Default is 32 levels and it can be set to [4, 65535]. Note that higher maximum levels of nesting can + // require larger amounts of stack to deserialize. Don't increase this higher than you require. + MaxNestedLevels int + + // MaxArrayElements specifies the max number of elements for CBOR arrays. + // Default is 128*1024=131072 and it can be set to [16, 2147483647] + MaxArrayElements int + + // MaxMapPairs specifies the max number of key-value pairs for CBOR maps. + // Default is 128*1024=131072 and it can be set to [16, 2147483647] + MaxMapPairs int +} + +// DiagMode returns a DiagMode with immutable options. +func (opts DiagOptions) DiagMode() (DiagMode, error) { + return opts.diagMode() +} + +func (opts DiagOptions) diagMode() (*diagMode, error) { + if err := opts.ByteStringEncoding.valid(); err != nil { + return nil, err + } + + decMode, err := DecOptions{ + MaxNestedLevels: opts.MaxNestedLevels, + MaxArrayElements: opts.MaxArrayElements, + MaxMapPairs: opts.MaxMapPairs, + }.decMode() + if err != nil { + return nil, err + } + + return &diagMode{ + byteStringEncoding: opts.ByteStringEncoding, + byteStringHexWhitespace: opts.ByteStringHexWhitespace, + byteStringText: opts.ByteStringText, + byteStringEmbeddedCBOR: opts.ByteStringEmbeddedCBOR, + cborSequence: opts.CBORSequence, + floatPrecisionIndicator: opts.FloatPrecisionIndicator, + decMode: decMode, + }, nil +} + +type diagMode struct { + byteStringEncoding ByteStringEncoding + byteStringHexWhitespace bool + byteStringText bool + byteStringEmbeddedCBOR bool + cborSequence bool + floatPrecisionIndicator bool + decMode *decMode +} + +// DiagOptions returns user specified options used to create this DiagMode. +func (dm *diagMode) DiagOptions() DiagOptions { + return DiagOptions{ + ByteStringEncoding: dm.byteStringEncoding, + ByteStringHexWhitespace: dm.byteStringHexWhitespace, + ByteStringText: dm.byteStringText, + ByteStringEmbeddedCBOR: dm.byteStringEmbeddedCBOR, + CBORSequence: dm.cborSequence, + FloatPrecisionIndicator: dm.floatPrecisionIndicator, + MaxNestedLevels: dm.decMode.maxNestedLevels, + MaxArrayElements: dm.decMode.maxArrayElements, + MaxMapPairs: dm.decMode.maxMapPairs, + } +} + +// Diagnose returns extended diagnostic notation (EDN) of CBOR data items using the DiagMode. +func (dm *diagMode) Diagnose(data []byte) (string, error) { + return newDiagnose(data, dm.decMode, dm).diag(dm.cborSequence) +} + +// DiagnoseFirst returns extended diagnostic notation (EDN) of the first CBOR data item using the DiagMode. Any remaining bytes are returned in rest. +func (dm *diagMode) DiagnoseFirst(data []byte) (string, []byte, error) { + return newDiagnose(data, dm.decMode, dm).diagFirst() +} + +var defaultDiagMode, _ = DiagOptions{}.diagMode() + +// Diagnose returns extended diagnostic notation (EDN) of CBOR data items +// using the default diagnostic mode. +// +// Refer to https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation. +func Diagnose(data []byte) (string, error) { + return defaultDiagMode.Diagnose(data) +} + +// Diagnose returns extended diagnostic notation (EDN) of the first CBOR data item using the DiagMode. Any remaining bytes are returned in rest. +func DiagnoseFirst(data []byte) (string, []byte, error) { + return defaultDiagMode.DiagnoseFirst(data) +} + +type diagnose struct { + dm *diagMode + d *decoder + w *bytes.Buffer +} + +func newDiagnose(data []byte, decm *decMode, diagm *diagMode) *diagnose { + return &diagnose{ + dm: diagm, + d: &decoder{data: data, dm: decm}, + w: &bytes.Buffer{}, + } +} + +func (di *diagnose) diag(cborSequence bool) (string, error) { + // CBOR Sequence + firstItem := true + for { + switch err := di.wellformed(cborSequence); err { + case nil: + if !firstItem { + if err = di.writeString(", "); err != nil { + return di.w.String(), err + } + } + firstItem = false + if err = di.item(); err != nil { + return di.w.String(), err + } + + case io.EOF: + if firstItem { + return di.w.String(), err + } + return di.w.String(), nil + + default: + return di.w.String(), err + } + } +} + +func (di *diagnose) diagFirst() (string, []byte, error) { + err := di.wellformed(true) + if err == nil { + err = di.item() + } + + if err == nil { + // Return EDN and the rest of the data slice (which might be len 0) + return di.w.String(), di.d.data[di.d.off:], nil + } + + return di.w.String(), nil, err +} + +func (di *diagnose) wellformed(allowExtraData bool) error { + off := di.d.off + err := di.d.wellformed(allowExtraData) + di.d.off = off + return err +} + +func (di *diagnose) item() error { //nolint:gocyclo + initialByte := di.d.data[di.d.off] + switch initialByte { + case 0x5f, 0x7f: // indefinite-length byte/text string + di.d.off++ + if di.d.data[di.d.off] == 0xff { + di.d.off++ + switch initialByte { + case 0x5f: + // indefinite-length bytes with no chunks. + return di.writeString(`''_`) + case 0x7f: + // indefinite-length text with no chunks. + return di.writeString(`""_`) + } + } + + if err := di.writeString("(_ "); err != nil { + return err + } + + i := 0 + for !di.d.foundBreak() { + if i > 0 { + if err := di.writeString(", "); err != nil { + return err + } + } + + i++ + // wellformedIndefiniteString() already checked that the next item is a byte/text string. + if err := di.item(); err != nil { + return err + } + } + + return di.writeByte(')') + + case 0x9f: // indefinite-length array + di.d.off++ + if err := di.writeString("[_ "); err != nil { + return err + } + + i := 0 + for !di.d.foundBreak() { + if i > 0 { + if err := di.writeString(", "); err != nil { + return err + } + } + + i++ + if err := di.item(); err != nil { + return err + } + } + + return di.writeByte(']') + + case 0xbf: // indefinite-length map + di.d.off++ + if err := di.writeString("{_ "); err != nil { + return err + } + + i := 0 + for !di.d.foundBreak() { + if i > 0 { + if err := di.writeString(", "); err != nil { + return err + } + } + + i++ + // key + if err := di.item(); err != nil { + return err + } + + if err := di.writeString(": "); err != nil { + return err + } + + // value + if err := di.item(); err != nil { + return err + } + } + + return di.writeByte('}') + } + + t := di.d.nextCBORType() + switch t { + case cborTypePositiveInt: + _, _, val := di.d.getHead() + return di.writeString(strconv.FormatUint(val, 10)) + + case cborTypeNegativeInt: + _, _, val := di.d.getHead() + if val > math.MaxInt64 { + // CBOR negative integer overflows int64, use big.Int to store value. + bi := new(big.Int) + bi.SetUint64(val) + bi.Add(bi, big.NewInt(1)) + bi.Neg(bi) + return di.writeString(bi.String()) + } + + nValue := int64(-1) ^ int64(val) + return di.writeString(strconv.FormatInt(nValue, 10)) + + case cborTypeByteString: + b := di.d.parseByteString() + return di.encodeByteString(b) + + case cborTypeTextString: + b, err := di.d.parseTextString() + if err != nil { + return err + } + return di.encodeTextString(string(b), '"') + + case cborTypeArray: + _, _, val := di.d.getHead() + count := int(val) + if err := di.writeByte('['); err != nil { + return err + } + + for i := 0; i < count; i++ { + if i > 0 { + if err := di.writeString(", "); err != nil { + return err + } + } + if err := di.item(); err != nil { + return err + } + } + return di.writeByte(']') + + case cborTypeMap: + _, _, val := di.d.getHead() + count := int(val) + if err := di.writeByte('{'); err != nil { + return err + } + + for i := 0; i < count; i++ { + if i > 0 { + if err := di.writeString(", "); err != nil { + return err + } + } + // key + if err := di.item(); err != nil { + return err + } + if err := di.writeString(": "); err != nil { + return err + } + // value + if err := di.item(); err != nil { + return err + } + } + return di.writeByte('}') + + case cborTypeTag: + _, _, tagNum := di.d.getHead() + switch tagNum { + case 2: + if nt := di.d.nextCBORType(); nt != cborTypeByteString { + return errors.New("cbor: tag number 2 must be followed by byte string, got " + nt.String()) + } + + b := di.d.parseByteString() + bi := new(big.Int).SetBytes(b) + return di.writeString(bi.String()) + + case 3: + if nt := di.d.nextCBORType(); nt != cborTypeByteString { + return errors.New("cbor: tag number 3 must be followed by byte string, got " + nt.String()) + } + + b := di.d.parseByteString() + bi := new(big.Int).SetBytes(b) + bi.Add(bi, big.NewInt(1)) + bi.Neg(bi) + return di.writeString(bi.String()) + + default: + if err := di.writeString(strconv.FormatUint(tagNum, 10)); err != nil { + return err + } + if err := di.writeByte('('); err != nil { + return err + } + if err := di.item(); err != nil { + return err + } + return di.writeByte(')') + } + + case cborTypePrimitives: + _, ai, val := di.d.getHead() + switch ai { + case 20: + return di.writeString("false") + + case 21: + return di.writeString("true") + + case 22: + return di.writeString("null") + + case 23: + return di.writeString("undefined") + + case 25, 26, 27: + return di.encodeFloat(ai, val) + + default: + if err := di.writeString("simple("); err != nil { + return err + } + if err := di.writeString(strconv.FormatUint(val, 10)); err != nil { + return err + } + return di.writeByte(')') + } + } + + return nil +} + +func (di *diagnose) writeByte(val byte) error { + return di.w.WriteByte(val) +} + +func (di *diagnose) writeString(val string) error { + _, err := di.w.WriteString(val) + return err +} + +// writeU16 format a rune as "\uxxxx" +func (di *diagnose) writeU16(val rune) error { + if err := di.writeString("\\u"); err != nil { + return err + } + b := make([]byte, 2) + b[0] = byte(val >> 8) + b[1] = byte(val) + return di.writeString(hex.EncodeToString(b)) +} + +var rawBase32Encoding = base32.StdEncoding.WithPadding(base32.NoPadding) +var rawBase32HexEncoding = base32.HexEncoding.WithPadding(base32.NoPadding) + +func (di *diagnose) encodeByteString(val []byte) error { + if len(val) > 0 { + if di.dm.byteStringText && utf8.Valid(val) { + return di.encodeTextString(string(val), '\'') + } + + if di.dm.byteStringEmbeddedCBOR { + di2 := newDiagnose(val, di.dm.decMode, di.dm) + // should always notating embedded CBOR sequence. + if str, err := di2.diag(true); err == nil { + if err := di.writeString("<<"); err != nil { + return err + } + if err := di.writeString(str); err != nil { + return err + } + return di.writeString(">>") + } + } + } + + switch di.dm.byteStringEncoding { + case ByteStringBase16Encoding: + if err := di.writeString("h'"); err != nil { + return err + } + + encoder := hex.NewEncoder(di.w) + if di.dm.byteStringHexWhitespace { + for i, b := range val { + if i > 0 { + if err := di.writeByte(' '); err != nil { + return err + } + } + if _, err := encoder.Write([]byte{b}); err != nil { + return err + } + } + } else { + if _, err := encoder.Write(val); err != nil { + return err + } + } + return di.writeByte('\'') + + case ByteStringBase32Encoding: + if err := di.writeString("b32'"); err != nil { + return err + } + encoder := base32.NewEncoder(rawBase32Encoding, di.w) + if _, err := encoder.Write(val); err != nil { + return err + } + encoder.Close() + return di.writeByte('\'') + + case ByteStringBase32HexEncoding: + if err := di.writeString("h32'"); err != nil { + return err + } + encoder := base32.NewEncoder(rawBase32HexEncoding, di.w) + if _, err := encoder.Write(val); err != nil { + return err + } + encoder.Close() + return di.writeByte('\'') + + case ByteStringBase64Encoding: + if err := di.writeString("b64'"); err != nil { + return err + } + encoder := base64.NewEncoder(base64.RawURLEncoding, di.w) + if _, err := encoder.Write(val); err != nil { + return err + } + encoder.Close() + return di.writeByte('\'') + + default: + return di.dm.byteStringEncoding.valid() + } +} + +var utf16SurrSelf = rune(0x10000) + +// quote should be either `'` or `"` +func (di *diagnose) encodeTextString(val string, quote byte) error { + if err := di.writeByte(quote); err != nil { + return err + } + + for i := 0; i < len(val); { + if b := val[i]; b < utf8.RuneSelf { + switch { + case b == '\t', b == '\n', b == '\r', b == '\\', b == quote: + if err := di.writeByte('\\'); err != nil { + return err + } + + switch b { + case '\t': + b = 't' + case '\n': + b = 'n' + case '\r': + b = 'r' + } + if err := di.writeByte(b); err != nil { + return err + } + + case b >= ' ' && b <= '~': + if err := di.writeByte(b); err != nil { + return err + } + + default: + if err := di.writeU16(rune(b)); err != nil { + return err + } + } + + i++ + continue + } + + c, size := utf8.DecodeRuneInString(val[i:]) + switch { + case c == utf8.RuneError: + // if err := di.writeU16(rune(val[i])); err != nil { + // return err + // } + return &SemanticError{"cbor: invalid UTF-8 string"} + + case c < utf16SurrSelf: + if err := di.writeU16(c); err != nil { + return err + } + + default: + c1, c2 := utf16.EncodeRune(c) + if err := di.writeU16(c1); err != nil { + return err + } + if err := di.writeU16(c2); err != nil { + return err + } + } + + i += size + } + + return di.writeByte(quote) +} + +func (di *diagnose) encodeFloat(ai byte, val uint64) error { + f64 := float64(0) + switch ai { + case 25: + f16 := float16.Frombits(uint16(val)) + switch { + case f16.IsNaN(): + return di.writeString("NaN") + case f16.IsInf(1): + return di.writeString("Infinity") + case f16.IsInf(-1): + return di.writeString("-Infinity") + default: + f64 = float64(f16.Float32()) + } + + case 26: + f32 := math.Float32frombits(uint32(val)) + switch { + case f32 != f32: + return di.writeString("NaN") + case f32 > math.MaxFloat32: + return di.writeString("Infinity") + case f32 < -math.MaxFloat32: + return di.writeString("-Infinity") + default: + f64 = float64(f32) + } + + case 27: + f64 = math.Float64frombits(val) + switch { + case f64 != f64: + return di.writeString("NaN") + case f64 > math.MaxFloat64: + return di.writeString("Infinity") + case f64 < -math.MaxFloat64: + return di.writeString("-Infinity") + } + } + // Use ES6 number to string conversion which should match most JSON generators. + // Inspired by https://github.com/golang/go/blob/4df10fba1687a6d4f51d7238a403f8f2298f6a16/src/encoding/json/encode.go#L585 + b := make([]byte, 0, 32) + if abs := math.Abs(f64); abs != 0 && (abs < 1e-6 || abs >= 1e21) { + b = strconv.AppendFloat(b, f64, 'e', -1, 64) + // clean up e-09 to e-9 + n := len(b) + if n >= 4 && string(b[n-4:n-1]) == "e-0" { + b = append(b[:n-2], b[n-1]) + } + } else { + b = strconv.AppendFloat(b, f64, 'f', -1, 64) + } + + // add decimal point and trailing zero if needed + if bytes.IndexByte(b, '.') < 0 { + if i := bytes.IndexByte(b, 'e'); i < 0 { + b = append(b, '.', '0') + } else { + b = append(b[:i+2], b[i:]...) + b[i] = '.' + b[i+1] = '0' + } + } + + if err := di.writeString(string(b)); err != nil { + return err + } + + if di.dm.floatPrecisionIndicator { + switch ai { + case 25: + return di.writeString("_1") + case 26: + return di.writeString("_2") + case 27: + return di.writeString("_3") + } + } + + return nil +} diff --git a/vendor/github.com/fxamacker/cbor/v2/diagnose_test.go b/vendor/github.com/fxamacker/cbor/v2/diagnose_test.go new file mode 100644 index 00000000..742e8efa --- /dev/null +++ b/vendor/github.com/fxamacker/cbor/v2/diagnose_test.go @@ -0,0 +1,1104 @@ +// Copyright (c) Faye Amacker. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +package cbor + +import ( + "bytes" + "fmt" + "io" + "reflect" + "strings" + "testing" +) + +func TestDiagnosticNotationExamples(t *testing.T) { + // https://www.rfc-editor.org/rfc/rfc8949.html#name-examples-of-encoded-cbor-da + testCases := []struct { + cbor []byte + diag string + }{ + { + hexDecode("00"), + `0`, + }, + { + hexDecode("01"), + `1`, + }, + { + hexDecode("0a"), + `10`, + }, + { + hexDecode("17"), + `23`, + }, + { + hexDecode("1818"), + `24`, + }, + { + hexDecode("1819"), + `25`, + }, + { + hexDecode("1864"), + `100`, + }, + { + hexDecode("1903e8"), + `1000`, + }, + { + hexDecode("1a000f4240"), + `1000000`, + }, + { + hexDecode("1b000000e8d4a51000"), + `1000000000000`, + }, + { + hexDecode("1bffffffffffffffff"), + `18446744073709551615`, + }, + { + hexDecode("c249010000000000000000"), + `18446744073709551616`, + }, + { + hexDecode("3bffffffffffffffff"), + `-18446744073709551616`, + }, + { + hexDecode("c349010000000000000000"), + `-18446744073709551617`, + }, + { + hexDecode("20"), + `-1`, + }, + { + hexDecode("29"), + `-10`, + }, + { + hexDecode("3863"), + `-100`, + }, + { + hexDecode("3903e7"), + `-1000`, + }, + { + hexDecode("f90000"), + `0.0`, + }, + { + hexDecode("f98000"), + `-0.0`, + }, + { + hexDecode("f93c00"), + `1.0`, + }, + { + hexDecode("fb3ff199999999999a"), + `1.1`, + }, + { + hexDecode("f93e00"), + `1.5`, + }, + { + hexDecode("f97bff"), + `65504.0`, + }, + { + hexDecode("fa47c35000"), + `100000.0`, + }, + { + hexDecode("fa7f7fffff"), + `3.4028234663852886e+38`, + }, + { + hexDecode("fb7e37e43c8800759c"), + `1.0e+300`, + }, + { + hexDecode("f90001"), + `5.960464477539063e-8`, + }, + { + hexDecode("f90400"), + `0.00006103515625`, + }, + { + hexDecode("f9c400"), + `-4.0`, + }, + { + hexDecode("fbc010666666666666"), + `-4.1`, + }, + { + hexDecode("f97c00"), + `Infinity`, + }, + { + hexDecode("f97e00"), + `NaN`, + }, + { + hexDecode("f9fc00"), + `-Infinity`, + }, + { + hexDecode("fa7f800000"), + `Infinity`, + }, + { + hexDecode("fa7fc00000"), + `NaN`, + }, + { + hexDecode("faff800000"), + `-Infinity`, + }, + { + hexDecode("fb7ff0000000000000"), + `Infinity`, + }, + { + hexDecode("fb7ff8000000000000"), + `NaN`, + }, + { + hexDecode("fbfff0000000000000"), + `-Infinity`, + }, + { + hexDecode("f4"), + `false`, + }, + { + hexDecode("f5"), + `true`, + }, + { + hexDecode("f6"), + `null`, + }, + { + hexDecode("f7"), + `undefined`, + }, + { + hexDecode("f0"), + `simple(16)`, + }, + { + hexDecode("f8ff"), + `simple(255)`, + }, + { + hexDecode("c074323031332d30332d32315432303a30343a30305a"), + `0("2013-03-21T20:04:00Z")`, + }, + { + hexDecode("c11a514b67b0"), + `1(1363896240)`, + }, + { + hexDecode("c1fb41d452d9ec200000"), + `1(1363896240.5)`, + }, + { + hexDecode("d74401020304"), + `23(h'01020304')`, + }, + { + hexDecode("d818456449455446"), + `24(h'6449455446')`, + }, + { + hexDecode("d82076687474703a2f2f7777772e6578616d706c652e636f6d"), + `32("http://www.example.com")`, + }, + { + hexDecode("40"), + `h''`, + }, + { + hexDecode("4401020304"), + `h'01020304'`, + }, + { + hexDecode("60"), + `""`, + }, + { + hexDecode("6161"), + `"a"`, + }, + { + hexDecode("6449455446"), + `"IETF"`, + }, + { + hexDecode("62225c"), + `"\"\\"`, + }, + { + hexDecode("62c3bc"), + `"\u00fc"`, + }, + { + hexDecode("63e6b0b4"), + `"\u6c34"`, + }, + { + hexDecode("64f0908591"), + `"\ud800\udd51"`, + }, + { + hexDecode("80"), + `[]`, + }, + { + hexDecode("83010203"), + `[1, 2, 3]`, + }, + { + hexDecode("8301820203820405"), + `[1, [2, 3], [4, 5]]`, + }, + { + hexDecode("98190102030405060708090a0b0c0d0e0f101112131415161718181819"), + `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]`, + }, + { + hexDecode("a0"), + `{}`, + }, + { + hexDecode("a201020304"), + `{1: 2, 3: 4}`, + }, + { + hexDecode("a26161016162820203"), + `{"a": 1, "b": [2, 3]}`, + }, + { + hexDecode("826161a161626163"), + `["a", {"b": "c"}]`, + }, + { + hexDecode("a56161614161626142616361436164614461656145"), + `{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}`, + }, + { + hexDecode("5f42010243030405ff"), + `(_ h'0102', h'030405')`, + }, + { + hexDecode("7f657374726561646d696e67ff"), + `(_ "strea", "ming")`, + }, + { + hexDecode("9fff"), + `[_ ]`, + }, + { + hexDecode("9f018202039f0405ffff"), + `[_ 1, [2, 3], [_ 4, 5]]`, + }, + { + hexDecode("9f01820203820405ff"), + `[_ 1, [2, 3], [4, 5]]`, + }, + { + hexDecode("83018202039f0405ff"), + `[1, [2, 3], [_ 4, 5]]`, + }, + { + hexDecode("83019f0203ff820405"), + `[1, [_ 2, 3], [4, 5]]`, + }, + { + hexDecode("9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff"), + `[_ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]`, + }, + { + hexDecode("bf61610161629f0203ffff"), + `{_ "a": 1, "b": [_ 2, 3]}`, + }, + { + hexDecode("826161bf61626163ff"), + `["a", {_ "b": "c"}]`, + }, + { + hexDecode("bf6346756ef563416d7421ff"), + `{_ "Fun": true, "Amt": -2}`, + }, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("Diagnostic %d", i), func(t *testing.T) { + str, err := Diagnose(tc.cbor) + if err != nil { + t.Errorf("Diagnostic(0x%x) returned error %q", tc.cbor, err) + } else if str != tc.diag { + t.Errorf("Diagnostic(0x%x) returned `%s`, want `%s`", tc.cbor, str, tc.diag) + } + + str, rest, err := DiagnoseFirst(tc.cbor) + if err != nil { + t.Errorf("Diagnostic(0x%x) returned error %q", tc.cbor, err) + } else if str != tc.diag { + t.Errorf("Diagnostic(0x%x) returned `%s`, want `%s`", tc.cbor, str, tc.diag) + } + + if rest == nil { + t.Errorf("Diagnostic(0x%x) returned nil rest", tc.cbor) + } else if len(rest) != 0 { + t.Errorf("Diagnostic(0x%x) returned non-empty rest '%x'", tc.cbor, rest) + } + }) + } +} + +func TestDiagnoseByteString(t *testing.T) { + testCases := []struct { + title string + cbor []byte + diag string + opts *DiagOptions + }{ + { + "base16", + hexDecode("4412345678"), + `h'12345678'`, + &DiagOptions{ + ByteStringEncoding: ByteStringBase16Encoding, + }, + }, + { + "base32", + hexDecode("4412345678"), + `b32'CI2FM6A'`, + &DiagOptions{ + ByteStringEncoding: ByteStringBase32Encoding, + }, + }, + { + "base32hex", + hexDecode("4412345678"), + `h32'28Q5CU0'`, + &DiagOptions{ + ByteStringEncoding: ByteStringBase32HexEncoding, + }, + }, + { + "base64", + hexDecode("4412345678"), + `b64'EjRWeA'`, + &DiagOptions{ + ByteStringEncoding: ByteStringBase64Encoding, + }, + }, + { + "without ByteStringHexWhitespace option", + hexDecode("4b48656c6c6f20776f726c64"), + `h'48656c6c6f20776f726c64'`, + &DiagOptions{ + ByteStringHexWhitespace: false, + }, + }, + { + "with ByteStringHexWhitespace option", + hexDecode("4b48656c6c6f20776f726c64"), + `h'48 65 6c 6c 6f 20 77 6f 72 6c 64'`, + &DiagOptions{ + ByteStringHexWhitespace: true, + }, + }, + { + "without ByteStringText option", + hexDecode("4b68656c6c6f20776f726c64"), + `h'68656c6c6f20776f726c64'`, + &DiagOptions{ + ByteStringText: false, + }, + }, + { + "with ByteStringText option", + hexDecode("4b68656c6c6f20776f726c64"), + `'hello world'`, + &DiagOptions{ + ByteStringText: true, + }, + }, + { + "without ByteStringText option and with ByteStringHexWhitespace option", + hexDecode("4b68656c6c6f20776f726c64"), + `h'68 65 6c 6c 6f 20 77 6f 72 6c 64'`, + &DiagOptions{ + ByteStringText: false, + ByteStringHexWhitespace: true, + }, + }, + { + "without ByteStringEmbeddedCBOR", + hexDecode("4101"), + `h'01'`, + &DiagOptions{ + ByteStringEmbeddedCBOR: false, + }, + }, + { + "with ByteStringEmbeddedCBOR", + hexDecode("4101"), + `<<1>>`, + &DiagOptions{ + ByteStringEmbeddedCBOR: true, + }, + }, + { + "multi CBOR items without ByteStringEmbeddedCBOR", + hexDecode("420102"), + `h'0102'`, + &DiagOptions{ + ByteStringEmbeddedCBOR: false, + }, + }, + { + "multi CBOR items with ByteStringEmbeddedCBOR", + hexDecode("420102"), + `<<1, 2>>`, + &DiagOptions{ + ByteStringEmbeddedCBOR: true, + }, + }, + { + "multi CBOR items with ByteStringEmbeddedCBOR", + hexDecode("4563666F6FF6"), + `h'63666f6ff6'`, + &DiagOptions{ + ByteStringEmbeddedCBOR: false, + }, + }, + { + "multi CBOR items with ByteStringEmbeddedCBOR", + hexDecode("4563666F6FF6"), + `<<"foo", null>>`, + &DiagOptions{ + ByteStringEmbeddedCBOR: true, + }, + }, + { + "indefinite length byte string with no chunks", + hexDecode("5fff"), + `''_`, + &DiagOptions{}, + }, + { + "indefinite length byte string with a empty byte string", + hexDecode("5f40ff"), + `(_ h'')`, // RFC 8949, Section 8.1 says `(_ '')` but it looks wrong and conflicts with Appendix A. + &DiagOptions{}, + }, + { + "indefinite length byte string with two empty byte string", + hexDecode("5f4040ff"), + `(_ h'', h'')`, + &DiagOptions{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.title, func(t *testing.T) { + dm, err := tc.opts.DiagMode() + if err != nil { + t.Errorf("DiagMode() for 0x%x returned error %q", tc.cbor, err) + } + + str, err := dm.Diagnose(tc.cbor) + if err != nil { + t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err) + } else if str != tc.diag { + t.Errorf("Diagnose(0x%x) returned `%s`, want %s", tc.cbor, str, tc.diag) + } + }) + } +} + +func TestDiagnoseTextString(t *testing.T) { + testCases := []struct { + title string + cbor []byte + diag string + opts *DiagOptions + }{ + { + "\t", + hexDecode("6109"), + `"\t"`, + &DiagOptions{}, + }, + { + "\r", + hexDecode("610d"), + `"\r"`, + &DiagOptions{}, + }, + { + "other ascii", + hexDecode("611b"), + `"\u001b"`, + &DiagOptions{}, + }, + { + "valid UTF-8 text in byte string", + hexDecode("4d68656c6c6f2c20e4bda0e5a5bd"), + `'hello, \u4f60\u597d'`, + &DiagOptions{ + ByteStringText: true, + }, + }, + { + "valid UTF-8 text in text string", + hexDecode("6d68656c6c6f2c20e4bda0e5a5bd"), + `"hello, \u4f60\u597d"`, // "hello, 你好" + &DiagOptions{ + ByteStringText: true, + }, + }, + { + "invalid UTF-8 text in byte string", + hexDecode("4d68656c6c6fffeee4bda0e5a5bd"), + `h'68656c6c6fffeee4bda0e5a5bd'`, + &DiagOptions{ + ByteStringText: true, + }, + }, + { + "valid grapheme cluster text in byte string", + hexDecode("583448656c6c6f2c2027e29da4efb88fe2808df09f94a5270ae4bda0e5a5bdefbc8c22f09fa791e2808df09fa49de2808df09fa79122"), + `'Hello, \'\u2764\ufe0f\u200d\ud83d\udd25\'\n\u4f60\u597d\uff0c"\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1"'`, + &DiagOptions{ + ByteStringText: true, + }, + }, + { + "valid grapheme cluster text in text string", + hexDecode("783448656c6c6f2c2027e29da4efb88fe2808df09f94a5270ae4bda0e5a5bdefbc8c22f09fa791e2808df09fa49de2808df09fa79122"), + `"Hello, '\u2764\ufe0f\u200d\ud83d\udd25'\n\u4f60\u597d\uff0c\"\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1\""`, // "Hello, '❤️‍🔥'\n你好,\"🧑‍🤝‍🧑\"" + &DiagOptions{ + ByteStringText: true, + }, + }, + { + "invalid grapheme cluster text in byte string", + hexDecode("583448656c6c6feeff27e29da4efb88fe2808df09f94a5270de4bda0e5a5bdefbc8c22f09fa791e2808df09fa49de2808df09fa79122"), + `h'48656c6c6feeff27e29da4efb88fe2808df09f94a5270de4bda0e5a5bdefbc8c22f09fa791e2808df09fa49de2808df09fa79122'`, + &DiagOptions{ + ByteStringText: true, + }, + }, + { + "indefinite length text string with no chunks", + hexDecode("7fff"), + `""_`, + &DiagOptions{}, + }, + { + "indefinite length text string with a empty text string", + hexDecode("7f60ff"), + `(_ "")`, + &DiagOptions{}, + }, + { + "indefinite length text string with two empty text string", + hexDecode("7f6060ff"), + `(_ "", "")`, + &DiagOptions{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.title, func(t *testing.T) { + dm, err := tc.opts.DiagMode() + if err != nil { + t.Errorf("DiagMode() for 0x%x returned error %q", tc.cbor, err) + } + + str, err := dm.Diagnose(tc.cbor) + if err != nil { + t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err) + } else if str != tc.diag { + t.Errorf("Diagnose(0x%x) returned `%s`, want %s", tc.cbor, str, tc.diag) + } + }) + } +} + +func TestDiagnoseInvalidTextString(t *testing.T) { + testCases := []struct { + title string + cbor []byte + wantErrorMsg string + opts *DiagOptions + }{ + { + "invalid UTF-8 text in text string", + hexDecode("6d68656c6c6fffeee4bda0e5a5bd"), + "invalid UTF-8 string", + &DiagOptions{ + ByteStringText: true, + }, + }, + { + "invalid grapheme cluster text in text string", + hexDecode("783448656c6c6feeff27e29da4efb88fe2808df09f94a5270de4bda0e5a5bdefbc8c22f09fa791e2808df09fa49de2808df09fa79122"), + "invalid UTF-8 string", + &DiagOptions{ + ByteStringText: true, + }, + }, + { + "invalid indefinite length text string", + hexDecode("7f6040ff"), + `wrong element type`, + &DiagOptions{ + ByteStringText: true, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.title, func(t *testing.T) { + dm, err := tc.opts.DiagMode() + if err != nil { + t.Errorf("DiagMode() for 0x%x returned error %q", tc.cbor, err) + } + + _, err = dm.Diagnose(tc.cbor) + if err == nil { + t.Errorf("Diagnose(0x%x) didn't return error", tc.cbor) + } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { + t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err) + } + }) + } +} + +func TestDiagnoseFloatingPointNumber(t *testing.T) { + testCases := []struct { + title string + cbor []byte + diag string + opts *DiagOptions + }{ + { + "float16 without FloatPrecisionIndicator option", + hexDecode("f93e00"), + `1.5`, + &DiagOptions{ + FloatPrecisionIndicator: false, + }, + }, + { + "float16 with FloatPrecisionIndicator option", + hexDecode("f93e00"), + `1.5_1`, + &DiagOptions{ + FloatPrecisionIndicator: true, + }, + }, + { + "float32 without FloatPrecisionIndicator option", + hexDecode("fa47c35000"), + `100000.0`, + &DiagOptions{ + FloatPrecisionIndicator: false, + }, + }, + { + "float32 with FloatPrecisionIndicator option", + hexDecode("fa47c35000"), + `100000.0_2`, + &DiagOptions{ + FloatPrecisionIndicator: true, + }, + }, + { + "float64 without FloatPrecisionIndicator option", + hexDecode("fbc010666666666666"), + `-4.1`, + &DiagOptions{ + FloatPrecisionIndicator: false, + }, + }, + { + "float64 with FloatPrecisionIndicator option", + hexDecode("fbc010666666666666"), + `-4.1_3`, + &DiagOptions{ + FloatPrecisionIndicator: true, + }, + }, + { + "with FloatPrecisionIndicator option", + hexDecode("c1fb41d452d9ec200000"), + `1(1363896240.5_3)`, + &DiagOptions{ + FloatPrecisionIndicator: true, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.title, func(t *testing.T) { + dm, err := tc.opts.DiagMode() + if err != nil { + t.Errorf("DiagMode() for 0x%x returned error %q", tc.cbor, err) + } + + str, err := dm.Diagnose(tc.cbor) + if err != nil { + t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err) + } else if str != tc.diag { + t.Errorf("Diagnose(0x%x) returned `%s`, want %s", tc.cbor, str, tc.diag) + } + }) + } +} + +func TestDiagnoseFirst(t *testing.T) { + testCases := []struct { + title string + cbor []byte + diag string + wantRest []byte + wantErrorMsg string + }{ + { + "with no trailing data", + hexDecode("f93e00"), + `1.5`, + []byte{}, + "", + }, + { + "with CBOR Sequences", + hexDecode("f93e0064494554464401020304"), + `1.5`, + hexDecode("64494554464401020304"), + "", + }, + { + "with invalid CBOR trailing data", + hexDecode("f93e00ff494554464401020304"), + `1.5`, + hexDecode("ff494554464401020304"), + "", + }, + { + "with invalid CBOR data", + hexDecode("f93e"), + ``, + nil, + "unexpected EOF", + }, + } + + for _, tc := range testCases { + t.Run(tc.title, func(t *testing.T) { + str, rest, err := DiagnoseFirst(tc.cbor) + if str != tc.diag { + t.Errorf("DiagnoseFirst(0x%x) returned `%s`, want %s", tc.cbor, str, tc.diag) + } + + if bytes.Equal(rest, tc.wantRest) == false { + if str != tc.diag { + t.Errorf("DiagnoseFirst(0x%x) returned rest `%x`, want rest %x", tc.cbor, rest, tc.wantRest) + } + } + + switch { + case tc.wantErrorMsg == "" && err != nil: + t.Errorf("DiagnoseFirst(0x%x) returned error %q", tc.cbor, err) + case tc.wantErrorMsg != "" && err == nil: + t.Errorf("DiagnoseFirst(0x%x) returned nil error, want error %q", tc.cbor, err) + case tc.wantErrorMsg != "" && !strings.Contains(err.Error(), tc.wantErrorMsg): + t.Errorf("DiagnoseFirst(0x%x) returned error %q, want error %q", tc.cbor, err, tc.wantErrorMsg) + } + }) + } +} + +func TestDiagnoseCBORSequences(t *testing.T) { + testCases := []struct { + title string + cbor []byte + diag string + opts *DiagOptions + returnError bool + }{ + { + "CBOR Sequences without CBORSequence option", + hexDecode("f93e0064494554464401020304"), + ``, + &DiagOptions{ + CBORSequence: false, + }, + true, + }, + { + "CBOR Sequences with CBORSequence option", + hexDecode("f93e0064494554464401020304"), + `1.5, "IETF", h'01020304'`, + &DiagOptions{ + CBORSequence: true, + }, + false, + }, + { + "CBOR Sequences with CBORSequence option", + hexDecode("0102"), + `1, 2`, + &DiagOptions{ + CBORSequence: true, + }, + false, + }, + { + "CBOR Sequences with CBORSequence option", + hexDecode("63666F6FF6"), + `"foo", null`, + &DiagOptions{ + CBORSequence: true, + }, + false, + }, + { + "partial/incomplete CBOR Sequences", + hexDecode("f93e00644945544644010203"), + `1.5, "IETF"`, + &DiagOptions{ + CBORSequence: true, + }, + true, + }, + } + + for _, tc := range testCases { + t.Run(tc.title, func(t *testing.T) { + dm, err := tc.opts.DiagMode() + if err != nil { + t.Errorf("DiagMode() for 0x%x returned error %q", tc.cbor, err) + } + + str, err := dm.Diagnose(tc.cbor) + if tc.returnError && err == nil { + t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err) + } else if !tc.returnError && err != nil { + t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err) + } + + if str != tc.diag { + t.Errorf("Diagnose(0x%x) returned `%s`, want %s", tc.cbor, str, tc.diag) + } + }) + } +} + +func TestDiagnoseTag(t *testing.T) { + testCases := []struct { + title string + cbor []byte + diag string + opts *DiagOptions + returnError bool + }{ + { + "CBOR tag number 2 with not well-formed encoded CBOR data item", + hexDecode("c201"), + ``, + &DiagOptions{}, + true, + }, + { + "CBOR tag number 3 with not well-formed encoded CBOR data item", + hexDecode("c301"), + ``, + &DiagOptions{}, + true, + }, + { + "CBOR tag number 2 with well-formed encoded CBOR data item", + hexDecode("c240"), + `0`, + &DiagOptions{}, + false, + }, + { + "CBOR tag number 3 with well-formed encoded CBOR data item", + hexDecode("c340"), + `-1`, // -1 - n + &DiagOptions{}, + false, + }, + { + "CBOR tag number 2 with well-formed encoded CBOR data item", + hexDecode("c249010000000000000000"), + `18446744073709551616`, + &DiagOptions{}, + false, + }, + { + "CBOR tag number 3 with well-formed encoded CBOR data item", + hexDecode("c349010000000000000000"), + `-18446744073709551617`, // -1 - n + &DiagOptions{}, + false, + }, + } + + for _, tc := range testCases { + t.Run(tc.title, func(t *testing.T) { + dm, err := tc.opts.DiagMode() + if err != nil { + t.Errorf("DiagMode() for 0x%x returned error %q", tc.cbor, err) + } + + str, err := dm.Diagnose(tc.cbor) + if tc.returnError && err == nil { + t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err) + } else if !tc.returnError && err != nil { + t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err) + } + + if str != tc.diag { + t.Errorf("Diagnose(0x%x) returned `%s`, want %s", tc.cbor, str, tc.diag) + } + }) + } +} + +func TestDiagnoseOptions(t *testing.T) { + opts := DiagOptions{ + ByteStringEncoding: ByteStringBase32Encoding, + ByteStringHexWhitespace: true, + ByteStringText: false, + ByteStringEmbeddedCBOR: true, + CBORSequence: false, + FloatPrecisionIndicator: true, + MaxNestedLevels: 100, + MaxArrayElements: 101, + MaxMapPairs: 102, + } + dm, err := opts.DiagMode() + if err != nil { + t.Errorf("DiagMode() returned an error %v", err) + } + opts2 := dm.DiagOptions() + if !reflect.DeepEqual(opts, opts2) { + t.Errorf("DiagOptions() returned wrong options %v, want %v", opts2, opts) + } + + opts = DiagOptions{ + ByteStringEncoding: ByteStringBase64Encoding, + ByteStringHexWhitespace: false, + ByteStringText: true, + ByteStringEmbeddedCBOR: false, + CBORSequence: true, + FloatPrecisionIndicator: false, + MaxNestedLevels: 100, + MaxArrayElements: 101, + MaxMapPairs: 102, + } + dm, err = opts.DiagMode() + if err != nil { + t.Errorf("DiagMode() returned an error %v", err) + } + opts2 = dm.DiagOptions() + if !reflect.DeepEqual(opts, opts2) { + t.Errorf("DiagOptions() returned wrong options %v, want %v", opts2, opts) + } +} + +func TestInvalidDiagnoseOptions(t *testing.T) { + opts := &DiagOptions{ + ByteStringEncoding: ByteStringBase64Encoding + 1, + } + _, err := opts.DiagMode() + if err == nil { + t.Errorf("DiagMode() with invalid ByteStringEncoding option didn't return error") + } +} + +func TestDiagnoseExtraneousData(t *testing.T) { + cborData := hexDecode("63666F6FF6") + _, err := Diagnose(cborData) + if err == nil { + t.Errorf("Diagnose(0x%x) didn't return error", cborData) + } else if !strings.Contains(err.Error(), `extraneous data`) { + t.Errorf("Diagnose(0x%x) returned error %q", cborData, err) + } + + _, _, err = DiagnoseFirst(cborData) + if err != nil { + t.Errorf("DiagnoseFirst(0x%x) returned error %v", cborData, err) + } +} + +func TestDiagnoseNotwellformedData(t *testing.T) { + cborData := hexDecode("5f4060ff") + _, err := Diagnose(cborData) + if err == nil { + t.Errorf("Diagnose(0x%x) didn't return error", cborData) + } else if !strings.Contains(err.Error(), `wrong element type`) { + t.Errorf("Diagnose(0x%x) returned error %q", cborData, err) + } +} + +func TestDiagnoseEmptyData(t *testing.T) { + var emptyData []byte + + defaultMode, _ := DiagOptions{}.DiagMode() + sequenceMode, _ := DiagOptions{CBORSequence: true}.DiagMode() + + testCases := []struct { + name string + dm DiagMode + }{ + {"default", defaultMode}, + {"sequence", sequenceMode}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + s, err := tc.dm.Diagnose(emptyData) + if len(s) != 0 { + t.Errorf("Diagnose() didn't return empty notation for empty data") + } + if err != io.EOF { + t.Errorf("Diagnose() didn't return io.EOF for empty data") + } + + s, rest, err := tc.dm.DiagnoseFirst(emptyData) + if len(s) != 0 { + t.Errorf("DiagnoseFirst() didn't return empty notation for empty data") + } + if len(rest) != 0 { + t.Errorf("DiagnoseFirst() didn't return empty rest for empty data") + } + if err != io.EOF { + t.Errorf("DiagnoseFirst() didn't return io.EOF for empty data") + } + }) + } +} diff --git a/vendor/github.com/fxamacker/cbor/v2/doc.go b/vendor/github.com/fxamacker/cbor/v2/doc.go index 51db0e03..b7e377f3 100644 --- a/vendor/github.com/fxamacker/cbor/v2/doc.go +++ b/vendor/github.com/fxamacker/cbor/v2/doc.go @@ -2,10 +2,9 @@ // Licensed under the MIT License. See LICENSE in the project root for license information. /* -Package cbor is a fast & safe CBOR encoder & decoder (RFC 7049) with a -standard API + toarray & keyasint struct tags, CBOR tags, float64->32->16, -CTAP2 & Canonical CBOR, duplicate map key options, and is customizable via -simple API. +Package cbor is a modern CBOR codec (RFC 8949 & RFC 7049) with CBOR tags, +Go struct tags (toarray/keyasint/omitempty), Core Deterministic Encoding, +CTAP2, Canonical CBOR, float64->32->16, and duplicate map key detection. Encoding options allow "preferred serialization" by encoding integers and floats to their smallest forms (e.g. float16) when values fit. @@ -40,9 +39,9 @@ creating modes from options at runtime. EncMode and DecMode interfaces are created from EncOptions or DecOptions structs. - em := cbor.EncOptions{...}.EncMode() - em := cbor.CanonicalEncOptions().EncMode() - em := cbor.CTAP2EncOptions().EncMode() + em, err := cbor.EncOptions{...}.EncMode() + em, err := cbor.CanonicalEncOptions().EncMode() + em, err := cbor.CTAP2EncOptions().EncMode() Modes use immutable options to avoid side-effects and simplify concurrency. Behavior of modes won't accidentally change at runtime after they're created. diff --git a/vendor/github.com/fxamacker/cbor/v2/encode.go b/vendor/github.com/fxamacker/cbor/v2/encode.go index 95d2c23f..10255a60 100644 --- a/vendor/github.com/fxamacker/cbor/v2/encode.go +++ b/vendor/github.com/fxamacker/cbor/v2/encode.go @@ -250,7 +250,7 @@ type BigIntConvertMode int const ( // BigIntConvertShortest makes big.Int encode to CBOR integer if value fits. // E.g. if big.Int value can be converted to CBOR integer while preserving - // value, encoder will encode it to CBOR interger (major type 0 or 1). + // value, encoder will encode it to CBOR integer (major type 0 or 1). BigIntConvertShortest BigIntConvertMode = iota // BigIntConvertNone makes big.Int encode to CBOR bignum (tag 2 or 3) without @@ -264,6 +264,25 @@ func (bim BigIntConvertMode) valid() bool { return bim < maxBigIntConvert } +// NilContainersMode specifies how to encode nil slices and maps. +type NilContainersMode int + +const ( + // NilContainerAsNull encodes nil slices and maps as CBOR null. + // This is the default. + NilContainerAsNull NilContainersMode = iota + + // NilContainerAsEmpty encodes nil slices and maps as + // empty container (CBOR bytestring, array, or map). + NilContainerAsEmpty + + maxNilContainersMode +) + +func (m NilContainersMode) valid() bool { + return m < maxNilContainersMode +} + // EncOptions specifies encoding options. type EncOptions struct { // Sort specifies sorting order. @@ -292,6 +311,9 @@ type EncOptions struct { // IndefLength specifies whether to allow indefinite length CBOR items. IndefLength IndefLengthMode + // NilContainers specifies how to encode nil slices and maps. + NilContainers NilContainersMode + // TagsMd specifies whether to allow CBOR tags (major type 6). TagsMd TagsMode } @@ -464,6 +486,9 @@ func (opts EncOptions) encMode() (*encMode, error) { if !opts.IndefLength.valid() { return nil, errors.New("cbor: invalid IndefLength " + strconv.Itoa(int(opts.IndefLength))) } + if !opts.NilContainers.valid() { + return nil, errors.New("cbor: invalid NilContainers " + strconv.Itoa(int(opts.NilContainers))) + } if !opts.TagsMd.valid() { return nil, errors.New("cbor: invalid TagsMd " + strconv.Itoa(int(opts.TagsMd))) } @@ -479,6 +504,7 @@ func (opts EncOptions) encMode() (*encMode, error) { time: opts.Time, timeTag: opts.TimeTag, indefLength: opts.IndefLength, + nilContainers: opts.NilContainers, tagsMd: opts.TagsMd, } return &em, nil @@ -501,6 +527,7 @@ type encMode struct { time TimeMode timeTag EncTagMode indefLength IndefLengthMode + nilContainers NilContainersMode tagsMd TagsMode } @@ -550,7 +577,7 @@ func (em *encMode) Marshal(v interface{}) ([]byte, error) { // NewEncoder returns a new encoder that writes to w using em EncMode. func (em *encMode) NewEncoder(w io.Writer) *Encoder { - return &Encoder{w: w, em: em, e: getEncoderBuffer()} + return &Encoder{w: w, em: em} } type encoderBuffer struct { @@ -787,7 +814,7 @@ func encodeFloat64(e *encoderBuffer, f64 float64) error { func encodeByteString(e *encoderBuffer, em *encMode, v reflect.Value) error { vk := v.Kind() - if vk == reflect.Slice && v.IsNil() { + if vk == reflect.Slice && v.IsNil() && em.nilContainers == NilContainerAsNull { e.Write(cborNil) return nil } @@ -824,7 +851,7 @@ type arrayEncodeFunc struct { } func (ae arrayEncodeFunc) encode(e *encoderBuffer, em *encMode, v reflect.Value) error { - if v.Kind() == reflect.Slice && v.IsNil() { + if v.Kind() == reflect.Slice && v.IsNil() && em.nilContainers == NilContainerAsNull { e.Write(cborNil) return nil } @@ -849,7 +876,7 @@ type mapEncodeFunc struct { } func (me mapEncodeFunc) encode(e *encoderBuffer, em *encMode, v reflect.Value) error { - if v.IsNil() { + if v.IsNil() && em.nilContainers == NilContainerAsNull { e.Write(cborNil) return nil } @@ -1250,6 +1277,14 @@ func encodeTag(e *encoderBuffer, em *encMode, v reflect.Value) error { return nil } +func encodeSimpleValue(e *encoderBuffer, em *encMode, v reflect.Value) error { + if b := em.encTagBytes(v.Type()); b != nil { + e.Write(b) + } + encodeHead(e, byte(cborTypePrimitives), v.Uint()) + return nil +} + func encodeHead(e *encoderBuffer, t byte, n uint64) { if n <= 23 { e.WriteByte(t | byte(n)) @@ -1282,6 +1317,7 @@ var ( typeMarshaler = reflect.TypeOf((*Marshaler)(nil)).Elem() typeBinaryMarshaler = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem() typeRawMessage = reflect.TypeOf(RawMessage(nil)) + typeByteString = reflect.TypeOf(ByteString("")) ) func getEncodeFuncInternal(t reflect.Type) (encodeFunc, isEmptyFunc) { @@ -1290,6 +1326,8 @@ func getEncodeFuncInternal(t reflect.Type) (encodeFunc, isEmptyFunc) { return getEncodeIndirectValueFunc(t), isEmptyPtr } switch t { + case typeSimpleValue: + return encodeSimpleValue, isEmptyUint case typeTag: return encodeTag, alwaysNotEmpty case typeTime: @@ -1298,6 +1336,8 @@ func getEncodeFuncInternal(t reflect.Type) (encodeFunc, isEmptyFunc) { return encodeBigInt, alwaysNotEmpty case typeRawMessage: return encodeMarshalerType, isEmptySlice + case typeByteString: + return encodeMarshalerType, isEmptyString } if reflect.PtrTo(t).Implements(typeMarshaler) { return encodeMarshalerType, alwaysNotEmpty diff --git a/vendor/github.com/fxamacker/cbor/v2/encode_test.go b/vendor/github.com/fxamacker/cbor/v2/encode_test.go index 7d2f2787..15b4c6fb 100644 --- a/vendor/github.com/fxamacker/cbor/v2/encode_test.go +++ b/vendor/github.com/fxamacker/cbor/v2/encode_test.go @@ -260,6 +260,11 @@ var marshalTests = []marshalTest{ {hexDecode("f4"), []interface{}{false}}, {hexDecode("f5"), []interface{}{true}}, {hexDecode("f6"), []interface{}{nil, []byte(nil), []int(nil), map[uint]bool(nil), (*int)(nil), io.Reader(nil)}}, + // simple values + {hexDecode("e0"), []interface{}{SimpleValue(0)}}, + {hexDecode("f0"), []interface{}{SimpleValue(16)}}, + {hexDecode("f820"), []interface{}{SimpleValue(32)}}, + {hexDecode("f8ff"), []interface{}{SimpleValue(255)}}, // nan, positive and negative inf {hexDecode("f97c00"), []interface{}{math.Inf(1)}}, {hexDecode("f97e00"), []interface{}{math.NaN()}}, @@ -2852,6 +2857,51 @@ func TestInvalidInfConvert(t *testing.T) { } } +func TestNilContainers(t *testing.T) { + nilContainersNull := EncOptions{NilContainers: NilContainerAsNull} + nilContainersEmpty := EncOptions{NilContainers: NilContainerAsEmpty} + + testCases := []struct { + name string + v interface{} + opts EncOptions + wantCborData []byte + }{ + {"map(nil) as CBOR null", map[string]string(nil), nilContainersNull, hexDecode("f6")}, + {"map(nil) as CBOR empty map", map[string]string(nil), nilContainersEmpty, hexDecode("a0")}, + + {"slice(nil) as CBOR null", []int(nil), nilContainersNull, hexDecode("f6")}, + {"slice(nil) as CBOR empty array", []int(nil), nilContainersEmpty, hexDecode("80")}, + + {"[]byte(nil) as CBOR null", []byte(nil), nilContainersNull, hexDecode("f6")}, + {"[]byte(nil) as CBOR empty bytestring", []byte(nil), nilContainersEmpty, hexDecode("40")}, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + em, err := tc.opts.EncMode() + if err != nil { + t.Errorf("EncMode() returned an error %v", err) + } + b, err := em.Marshal(tc.v) + if err != nil { + t.Errorf("Marshal(%v) returned error %v", tc.v, err) + } else if !bytes.Equal(b, tc.wantCborData) { + t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.v, b, tc.wantCborData) + } + }) + } +} + +func TestInvalidNilContainers(t *testing.T) { + wantErrorMsg := "cbor: invalid NilContainers 100" + _, err := EncOptions{NilContainers: NilContainersMode(100)}.EncMode() + if err == nil { + t.Errorf("EncMode() didn't return an error") + } else if err.Error() != wantErrorMsg { + t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg) + } +} + // Keith Randall's workaround for constant propagation issue https://github.com/golang/go/issues/36400 const ( // qnan 32 bits constants @@ -2882,29 +2932,29 @@ const ( var ( // qnan 32 bits variables - qnanVar0xffc00001 uint32 = qnanConst0xffc00001 - qnanVar0x7fc00001 uint32 = qnanConst0x7fc00001 - qnanVar0xffc02000 uint32 = qnanConst0xffc02000 - qnanVar0x7fc02000 uint32 = qnanConst0x7fc02000 + qnanVar0xffc00001 = qnanConst0xffc00001 + qnanVar0x7fc00001 = qnanConst0x7fc00001 + qnanVar0xffc02000 = qnanConst0xffc02000 + qnanVar0x7fc02000 = qnanConst0x7fc02000 // snan 32 bits variables - snanVar0xff800001 uint32 = snanConst0xff800001 - snanVar0x7f800001 uint32 = snanConst0x7f800001 - snanVar0xff802000 uint32 = snanConst0xff802000 - snanVar0x7f802000 uint32 = snanConst0x7f802000 + snanVar0xff800001 = snanConst0xff800001 + snanVar0x7f800001 = snanConst0x7f800001 + snanVar0xff802000 = snanConst0xff802000 + snanVar0x7f802000 = snanConst0x7f802000 // qnan 64 bits variables - qnanVar0xfff8000000000001 uint64 = qnanConst0xfff8000000000001 - qnanVar0x7ff8000000000001 uint64 = qnanConst0x7ff8000000000001 - qnanVar0xfff8000020000000 uint64 = qnanConst0xfff8000020000000 - qnanVar0x7ff8000020000000 uint64 = qnanConst0x7ff8000020000000 - qnanVar0xfffc000000000000 uint64 = qnanConst0xfffc000000000000 - qnanVar0x7ffc000000000000 uint64 = qnanConst0x7ffc000000000000 + qnanVar0xfff8000000000001 = qnanConst0xfff8000000000001 + qnanVar0x7ff8000000000001 = qnanConst0x7ff8000000000001 + qnanVar0xfff8000020000000 = qnanConst0xfff8000020000000 + qnanVar0x7ff8000020000000 = qnanConst0x7ff8000020000000 + qnanVar0xfffc000000000000 = qnanConst0xfffc000000000000 + qnanVar0x7ffc000000000000 = qnanConst0x7ffc000000000000 // snan 64 bits variables - snanVar0xfff0000000000001 uint64 = snanConst0xfff0000000000001 - snanVar0x7ff0000000000001 uint64 = snanConst0x7ff0000000000001 - snanVar0xfff0000020000000 uint64 = snanConst0xfff0000020000000 - snanVar0x7ff0000020000000 uint64 = snanConst0x7ff0000020000000 - snanVar0xfff4000000000000 uint64 = snanConst0xfff4000000000000 - snanVar0x7ff4000000000000 uint64 = snanConst0x7ff4000000000000 + snanVar0xfff0000000000001 = snanConst0xfff0000000000001 + snanVar0x7ff0000000000001 = snanConst0x7ff0000000000001 + snanVar0xfff0000020000000 = snanConst0xfff0000020000000 + snanVar0x7ff0000020000000 = snanConst0x7ff0000020000000 + snanVar0xfff4000000000000 = snanConst0xfff4000000000000 + snanVar0x7ff4000000000000 = snanConst0x7ff4000000000000 ) func TestNaNConvert(t *testing.T) { @@ -3306,6 +3356,7 @@ func TestEncOptions(t *testing.T) { Time: TimeRFC3339Nano, TimeTag: EncTagRequired, IndefLength: IndefLengthForbidden, + NilContainers: NilContainerAsNull, TagsMd: TagsAllowed, } em, err := opts1.EncMode() @@ -3590,3 +3641,61 @@ func TestMarshalNegBigInt(t *testing.T) { }) } } + +func TestStructWithSimpleValueFields(t *testing.T) { + type T struct { + SV1 SimpleValue `cbor:",omitempty"` // omit empty + SV2 SimpleValue + } + + v1 := T{} + want1 := []byte{0xa1, 0x63, 0x53, 0x56, 0x32, 0xe0} + + v2 := T{SV1: SimpleValue(1), SV2: SimpleValue(255)} + want2 := []byte{ + 0xa2, + 0x63, 0x53, 0x56, 0x31, 0xe1, + 0x63, 0x53, 0x56, 0x32, 0xf8, 0xff, + } + + em, _ := EncOptions{}.EncMode() + dm, _ := DecOptions{}.DecMode() + tests := []roundTripTest{ + {"default values", v1, want1}, + {"non-default values", v2, want2}} + testRoundTrip(t, tests, em, dm) +} + +func TestMapWithSimpleValueKey(t *testing.T) { + data := []byte{0xa2, 0x00, 0x00, 0xe0, 0x00} // {0: 0, simple(0): 0} + + // Decode CBOR map with positive integer 0 and simple value 0 as keys. + // No map key duplication is detected because keys are of different CBOR types. + // RFC 8949 Section 5.6.1 says "a simple value 2 is not equivalent to an integer 2". + decOpts := DecOptions{ + DupMapKey: DupMapKeyEnforcedAPF, // duplicated key not allowed + } + decMode, _ := decOpts.DecMode() + + var v map[interface{}]interface{} + err := decMode.Unmarshal(data, &v) + if err != nil { + t.Errorf("Unmarshal(0x%x) returned error %v", data, err) + } + + // Encode decoded Go map. + encOpts := EncOptions{ + Sort: SortBytewiseLexical, + } + encMode, _ := encOpts.EncMode() + + encodedData, err := encMode.Marshal(v) + if err != nil { + t.Errorf("Marshal(%v) returned error %v", v, err) + } + + // Test roundtrip produces identical CBOR data. + if !bytes.Equal(data, encodedData) { + t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, encodedData, data) + } +} diff --git a/vendor/github.com/fxamacker/cbor/v2/simplevalue.go b/vendor/github.com/fxamacker/cbor/v2/simplevalue.go new file mode 100644 index 00000000..ec77a946 --- /dev/null +++ b/vendor/github.com/fxamacker/cbor/v2/simplevalue.go @@ -0,0 +1,17 @@ +package cbor + +import "reflect" + +// SimpleValue represents CBOR simple value. +// CBOR simple value is: +// * an extension point like CBOR tag. +// * a subset of CBOR major type 7 that isn't floating-point. +// * "identified by a number between 0 and 255, but distinct from that number itself". +// For example, "a simple value 2 is not equivalent to an integer 2" as a CBOR map key. +// CBOR simple values identified by 20..23 are: "false", "true" , "null", and "undefined". +// Other CBOR simple values are currently unassigned/reserved by IANA. +type SimpleValue uint8 + +var ( + typeSimpleValue = reflect.TypeOf(SimpleValue(0)) +) diff --git a/vendor/github.com/fxamacker/cbor/v2/stream.go b/vendor/github.com/fxamacker/cbor/v2/stream.go index 29172b92..02fea43c 100644 --- a/vendor/github.com/fxamacker/cbor/v2/stream.go +++ b/vendor/github.com/fxamacker/cbor/v2/stream.go @@ -4,6 +4,7 @@ package cbor import ( + "bytes" "errors" "io" "reflect" @@ -26,26 +27,37 @@ func NewDecoder(r io.Reader) *Decoder { // Decode reads CBOR value and decodes it into the value pointed to by v. func (dec *Decoder) Decode(v interface{}) error { - if len(dec.buf) == dec.off { - if n, err := dec.read(); n == 0 { - return err - } + _, err := dec.readNext() + if err != nil { + // Return validation error or read error. + return err } dec.d.reset(dec.buf[dec.off:]) - err := dec.d.value(v) + err = dec.d.value(v) + + // Increment dec.off even if decoding err is not nil because + // dec.d.off points to the next CBOR data item if current + // CBOR data item is valid but failed to be decoded into v. + // This allows next CBOR data item to be decoded in next + // call to this function. dec.off += dec.d.off dec.bytesRead += dec.d.off + + return err +} + +// Skip skips to the next CBOR data item (if there is any), +// otherwise it returns error such as io.EOF, io.UnexpectedEOF, etc. +func (dec *Decoder) Skip() error { + n, err := dec.readNext() if err != nil { - if err != io.ErrUnexpectedEOF { - return err - } - // Need to read more data. - if n, e := dec.read(); n == 0 { - return e - } - return dec.Decode(v) + // Return validation error or read error. + return err } + + dec.off += n + dec.bytesRead += n return nil } @@ -54,6 +66,71 @@ func (dec *Decoder) NumBytesRead() int { return dec.bytesRead } +// Buffered returns a reader for data remaining in Decoder's buffer. +// Returned reader is valid until the next call to Decode or Skip. +func (dec *Decoder) Buffered() io.Reader { + return bytes.NewReader(dec.buf[dec.off:]) +} + +// readNext() reads next CBOR data item from Reader to buffer. +// It returns the size of next CBOR data item. +// It also returns validation error or read error if any. +func (dec *Decoder) readNext() (int, error) { + var readErr error + var validErr error + + for { + // Process any unread data in dec.buf. + if dec.off < len(dec.buf) { + dec.d.reset(dec.buf[dec.off:]) + off := dec.off // Save offset before data validation + validErr = dec.d.wellformed(true) + dec.off = off // Restore offset + + if validErr == nil { + return dec.d.off, nil + } + + if validErr != io.ErrUnexpectedEOF { + return 0, validErr + } + + // Process last read error on io.ErrUnexpectedEOF. + if readErr != nil { + if readErr == io.EOF { + // current CBOR data item is incomplete. + return 0, io.ErrUnexpectedEOF + } + return 0, readErr + } + } + + // More data is needed and there was no read error. + var n int + for n == 0 { + n, readErr = dec.read() + if n == 0 && readErr != nil { + // No more data can be read and read error is encountered. + // At this point, validErr is either nil or io.ErrUnexpectedEOF. + if readErr == io.EOF { + if validErr == io.ErrUnexpectedEOF { + // current CBOR data item is incomplete. + return 0, io.ErrUnexpectedEOF + } + } + return 0, readErr + } + } + + // At this point, dec.buf contains new data from last read (n > 0). + } +} + +// read() reads data from Reader to buffer. +// It returns number of bytes read and any read error encountered. +// Postconditions: +// - dec.buf contains previously unread data and new data. +// - dec.off is 0. func (dec *Decoder) read() (int, error) { // Grow buf if needed. const minRead = 512 @@ -84,7 +161,6 @@ func (dec *Decoder) overwriteBuf(newBuf []byte) { type Encoder struct { w io.Writer em *encMode - e *encoderBuffer indefTypes []cborType } @@ -111,24 +187,27 @@ func (enc *Encoder) Encode(v interface{}) error { } } - err := encode(enc.e, enc.em, reflect.ValueOf(v)) + buf := getEncoderBuffer() + + err := encode(buf, enc.em, reflect.ValueOf(v)) if err == nil { - _, err = enc.e.WriteTo(enc.w) + _, err = enc.w.Write(buf.Bytes()) } - enc.e.Reset() + + putEncoderBuffer(buf) return err } // StartIndefiniteByteString starts byte string encoding of indefinite length. // Subsequent calls of (*Encoder).Encode() encodes definite length byte strings -// ("chunks") as one continguous string until EndIndefinite is called. +// ("chunks") as one contiguous string until EndIndefinite is called. func (enc *Encoder) StartIndefiniteByteString() error { return enc.startIndefinite(cborTypeByteString) } // StartIndefiniteTextString starts text string encoding of indefinite length. // Subsequent calls of (*Encoder).Encode() encodes definite length text strings -// ("chunks") as one continguous string until EndIndefinite is called. +// ("chunks") as one contiguous string until EndIndefinite is called. func (enc *Encoder) StartIndefiniteTextString() error { return enc.startIndefinite(cborTypeTextString) } @@ -193,7 +272,6 @@ func (m *RawMessage) UnmarshalCBOR(data []byte) error { if m == nil { return errors.New("cbor.RawMessage: UnmarshalCBOR on nil pointer") } - *m = make([]byte, len(data)) - copy(*m, data) + *m = append((*m)[0:0], data...) return nil } diff --git a/vendor/github.com/fxamacker/cbor/v2/stream_test.go b/vendor/github.com/fxamacker/cbor/v2/stream_test.go index 6469628e..095effdd 100644 --- a/vendor/github.com/fxamacker/cbor/v2/stream_test.go +++ b/vendor/github.com/fxamacker/cbor/v2/stream_test.go @@ -5,8 +5,11 @@ package cbor import ( "bytes" + "errors" + "fmt" "io" "reflect" + "strings" "testing" "time" ) @@ -18,30 +21,290 @@ func TestDecoder(t *testing.T) { buf.Write(tc.cborData) } } - decoder := NewDecoder(&buf) - bytesRead := 0 + + testCases := []struct { + name string + reader io.Reader + }{ + {"bytes.Buffer", &buf}, + {"1 byte reader", newNBytesReader(buf.Bytes(), 1)}, + {"toggled reader", newToggledReader(buf.Bytes(), 1)}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + decoder := NewDecoder(tc.reader) + bytesRead := 0 + for i := 0; i < 5; i++ { + for _, tc := range unmarshalTests { + var v interface{} + if err := decoder.Decode(&v); err != nil { + t.Fatalf("Decode() returned error %v", err) + } + if tm, ok := tc.emptyInterfaceValue.(time.Time); ok { + if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) { + t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + } + } else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) { + t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + } + bytesRead += len(tc.cborData) + if decoder.NumBytesRead() != bytesRead { + t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead) + } + } + } + for i := 0; i < 2; i++ { + // no more data + var v interface{} + err := decoder.Decode(&v) + if v != nil { + t.Errorf("Decode() = %v (%T), want nil (no more data)", v, v) + } + if err != io.EOF { + t.Errorf("Decode() returned error %v, want io.EOF (no more data)", err) + } + } + }) + } +} + +func TestDecoderUnmarshalTypeError(t *testing.T) { + var buf bytes.Buffer for i := 0; i < 5; i++ { for _, tc := range unmarshalTests { - var v interface{} - if err := decoder.Decode(&v); err != nil { - t.Fatalf("Decode() returned error %v", err) + for j := 0; j < len(tc.wrongTypes)*2; j++ { + buf.Write(tc.cborData) } - if tm, ok := tc.emptyInterfaceValue.(time.Time); ok { - if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) { + } + } + + testCases := []struct { + name string + reader io.Reader + }{ + {"bytes.Buffer", &buf}, + {"1 byte reader", newNBytesReader(buf.Bytes(), 1)}, + {"toggled reader", newToggledReader(buf.Bytes(), 1)}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + decoder := NewDecoder(tc.reader) + bytesRead := 0 + for i := 0; i < 5; i++ { + for _, tc := range unmarshalTests { + for _, typ := range tc.wrongTypes { + v := reflect.New(typ) + if err := decoder.Decode(v.Interface()); err == nil { + t.Errorf("Decode(0x%x) didn't return an error, want UnmarshalTypeError", tc.cborData) + } else if _, ok := err.(*UnmarshalTypeError); !ok { + t.Errorf("Decode(0x%x) returned wrong error type %T, want UnmarshalTypeError", tc.cborData, err) + } + bytesRead += len(tc.cborData) + if decoder.NumBytesRead() != bytesRead { + t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead) + } + + var vi interface{} + if err := decoder.Decode(&vi); err != nil { + t.Errorf("Decode() returned error %v", err) + } + if tm, ok := tc.emptyInterfaceValue.(time.Time); ok { + if vt, ok := vi.(time.Time); !ok || !tm.Equal(vt) { + t.Errorf("Decode() = %v (%T), want %v (%T)", vi, vi, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + } + } else if !reflect.DeepEqual(vi, tc.emptyInterfaceValue) { + t.Errorf("Decode() = %v (%T), want %v (%T)", vi, vi, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + } + bytesRead += len(tc.cborData) + if decoder.NumBytesRead() != bytesRead { + t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead) + } + } + } + } + for i := 0; i < 2; i++ { + // no more data + var v interface{} + err := decoder.Decode(&v) + if v != nil { + t.Errorf("Decode() = %v (%T), want nil (no more data)", v, v) + } + if err != io.EOF { + t.Errorf("Decode() returned error %v, want io.EOF (no more data)", err) + } + } + }) + } +} + +func TestDecoderUnexpectedEOFError(t *testing.T) { + var buf bytes.Buffer + for _, tc := range unmarshalTests { + buf.Write(tc.cborData) + } + buf.Truncate(buf.Len() - 1) + + testCases := []struct { + name string + reader io.Reader + }{ + {"bytes.Buffer", &buf}, + {"1 byte reader", newNBytesReader(buf.Bytes(), 1)}, + {"toggled reader", newToggledReader(buf.Bytes(), 1)}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + decoder := NewDecoder(tc.reader) + bytesRead := 0 + for i := 0; i < len(unmarshalTests)-1; i++ { + tc := unmarshalTests[i] + var v interface{} + if err := decoder.Decode(&v); err != nil { + t.Fatalf("Decode() returned error %v", err) + } + if tm, ok := tc.emptyInterfaceValue.(time.Time); ok { + if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) { + t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + } + } else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) { t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) } - } else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) { - t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + bytesRead += len(tc.cborData) + if decoder.NumBytesRead() != bytesRead { + t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead) + } } - bytesRead += len(tc.cborData) - if decoder.NumBytesRead() != bytesRead { - t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead) + for i := 0; i < 2; i++ { + // truncated data + var v interface{} + err := decoder.Decode(&v) + if v != nil { + t.Errorf("Decode() = %v (%T), want nil (no more data)", v, v) + } + if err != io.ErrUnexpectedEOF { + t.Errorf("Decode() returned error %v, want io.UnexpectedEOF (truncated data)", err) + } } - } + }) } - // no more data +} + +func TestDecoderReadError(t *testing.T) { + var buf bytes.Buffer + for _, tc := range unmarshalTests { + buf.Write(tc.cborData) + } + buf.Truncate(buf.Len() - 1) + + readerErr := errors.New("reader error") + + testCases := []struct { + name string + reader io.Reader + }{ + {"byte reader", newNBytesReaderWithError(buf.Bytes(), 512, readerErr)}, + {"1 byte reader", newNBytesReaderWithError(buf.Bytes(), 1, readerErr)}, + {"toggled reader", newToggledReaderWithError(buf.Bytes(), 1, readerErr)}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + decoder := NewDecoder(tc.reader) + bytesRead := 0 + for i := 0; i < len(unmarshalTests)-1; i++ { + tc := unmarshalTests[i] + var v interface{} + if err := decoder.Decode(&v); err != nil { + t.Fatalf("Decode() returned error %v", err) + } + if tm, ok := tc.emptyInterfaceValue.(time.Time); ok { + if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) { + t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + } + } else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) { + t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + } + bytesRead += len(tc.cborData) + if decoder.NumBytesRead() != bytesRead { + t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead) + } + } + for i := 0; i < 2; i++ { + // truncated data because Reader returned error + var v interface{} + err := decoder.Decode(&v) + if v != nil { + t.Errorf("Decode() = %v (%T), want nil (no more data)", v, v) + } + if err != readerErr { + t.Errorf("Decode() returned error %v, want reader error", err) + } + } + }) + } +} + +func TestDecoderNoData(t *testing.T) { + readerErr := errors.New("reader error") + + testCases := []struct { + name string + reader io.Reader + wantErr error + }{ + {"byte.Buffer", new(bytes.Buffer), io.EOF}, + {"1 byte reader", newNBytesReaderWithError(nil, 0, readerErr), readerErr}, + {"toggled reader", newToggledReaderWithError(nil, 0, readerErr), readerErr}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + decoder := NewDecoder(tc.reader) + for i := 0; i < 2; i++ { + var v interface{} + err := decoder.Decode(&v) + if v != nil { + t.Errorf("Decode() = %v (%T), want nil", v, v) + } + if err != tc.wantErr { + t.Errorf("Decode() returned error %v, want error %v", err, tc.wantErr) + } + } + }) + } +} + +func TestDecoderRecoverableReadError(t *testing.T) { + cborData := hexDecode("83010203") // [1,2,3] + wantValue := []interface{}{uint64(1), uint64(2), uint64(3)} + recoverableReaderErr := errors.New("recoverable reader error") + + decoder := NewDecoder(newRecoverableReader(cborData, 1, recoverableReaderErr)) + var v interface{} err := decoder.Decode(&v) + if err != recoverableReaderErr { + t.Fatalf("Decode() returned error %v, want error %v", err, recoverableReaderErr) + } + + err = decoder.Decode(&v) + if err != nil { + t.Fatalf("Decode() returned error %v", err) + } + if !reflect.DeepEqual(v, wantValue) { + t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, wantValue, wantValue) + } + if decoder.NumBytesRead() != len(cborData) { + t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), len(cborData)) + } + + // no more data + v = interface{}(nil) + err = decoder.Decode(&v) if v != nil { t.Errorf("Decode() = %v (%T), want nil (no more data)", v, v) } @@ -50,57 +313,244 @@ func TestDecoder(t *testing.T) { } } -func TestDecoderUnmarshalTypeError(t *testing.T) { +func TestDecoderInvalidData(t *testing.T) { + data := []byte{0x01, 0x3e} + decoder := NewDecoder(bytes.NewReader(data)) + + var v1 interface{} + err := decoder.Decode(&v1) + if err != nil { + t.Errorf("Decode() returned error %v when decoding valid data item", err) + } + + var v2 interface{} + err = decoder.Decode(&v2) + if err == nil { + t.Errorf("Decode() didn't return error when decoding invalid data item") + } else if !strings.Contains(err.Error(), "cbor: invalid additional information") { + t.Errorf("Decode() error %q, want \"cbor: invalid additional information\"", err) + } +} + +func TestDecoderSkip(t *testing.T) { var buf bytes.Buffer for i := 0; i < 5; i++ { for _, tc := range unmarshalTests { - for j := 0; j < len(tc.wrongTypes)*2; j++ { - buf.Write(tc.cborData) - } + buf.Write(tc.cborData) } } - decoder := NewDecoder(&buf) - bytesRead := 0 - for i := 0; i < 5; i++ { - for _, tc := range unmarshalTests { - for _, typ := range tc.wrongTypes { - v := reflect.New(typ) - if err := decoder.Decode(v.Interface()); err == nil { - t.Errorf("Decode(0x%x) didn't return an error, want UnmarshalTypeError", tc.cborData) - } else if _, ok := err.(*UnmarshalTypeError); !ok { - t.Errorf("Decode(0x%x) returned wrong error type %T, want UnmarshalTypeError", tc.cborData, err) + + testCases := []struct { + name string + reader io.Reader + }{ + {"bytes.Buffer", &buf}, + {"1 byte reader", newNBytesReader(buf.Bytes(), 1)}, + {"toggled reader", newToggledReader(buf.Bytes(), 1)}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + decoder := NewDecoder(tc.reader) + bytesRead := 0 + for i := 0; i < 5; i++ { + for _, tc := range unmarshalTests { + if err := decoder.Skip(); err != nil { + t.Fatalf("Skip() returned error %v", err) + } + bytesRead += len(tc.cborData) + if decoder.NumBytesRead() != bytesRead { + t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead) + } + } + } + for i := 0; i < 2; i++ { + // no more data + err := decoder.Skip() + if err != io.EOF { + t.Errorf("Skip() returned error %v, want io.EOF (no more data)", err) + } + } + }) + } +} + +func TestDecoderSkipInvalidDataError(t *testing.T) { + var buf bytes.Buffer + for _, tc := range unmarshalTests { + buf.Write(tc.cborData) + } + buf.WriteByte(0x3e) + + testCases := []struct { + name string + reader io.Reader + }{ + {"bytes.Buffer", &buf}, + {"1 byte reader", newNBytesReader(buf.Bytes(), 1)}, + {"toggled reader", newToggledReader(buf.Bytes(), 1)}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + decoder := NewDecoder(tc.reader) + bytesRead := 0 + for i := 0; i < len(unmarshalTests); i++ { + tc := unmarshalTests[i] + if err := decoder.Skip(); err != nil { + t.Fatalf("Skip() returned error %v", err) } bytesRead += len(tc.cborData) if decoder.NumBytesRead() != bytesRead { t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead) } + } + for i := 0; i < 2; i++ { + // last data item is invalid + err := decoder.Skip() + if err == nil { + t.Fatalf("Skip() didn't return error") + } else if !strings.Contains(err.Error(), "cbor: invalid additional information") { + t.Errorf("Skip() error %q, want \"cbor: invalid additional information\"", err) + } + } + }) + } +} + +func TestDecoderSkipUnexpectedEOFError(t *testing.T) { + var buf bytes.Buffer + for _, tc := range unmarshalTests { + buf.Write(tc.cborData) + } + buf.Truncate(buf.Len() - 1) - var vi interface{} - if err := decoder.Decode(&vi); err != nil { - t.Errorf("Decode() returned error %v", err) + testCases := []struct { + name string + reader io.Reader + }{ + {"bytes.Buffer", &buf}, + {"1 byte reader", newNBytesReader(buf.Bytes(), 1)}, + {"toggled reader", newToggledReader(buf.Bytes(), 1)}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + decoder := NewDecoder(tc.reader) + bytesRead := 0 + for i := 0; i < len(unmarshalTests)-1; i++ { + tc := unmarshalTests[i] + if err := decoder.Skip(); err != nil { + t.Fatalf("Skip() returned error %v", err) } - if tm, ok := tc.emptyInterfaceValue.(time.Time); ok { - if vt, ok := vi.(time.Time); !ok || !tm.Equal(vt) { - t.Errorf("Decode() = %v (%T), want %v (%T)", vi, vi, tc.emptyInterfaceValue, tc.emptyInterfaceValue) - } - } else if !reflect.DeepEqual(vi, tc.emptyInterfaceValue) { - t.Errorf("Decode() = %v (%T), want %v (%T)", vi, vi, tc.emptyInterfaceValue, tc.emptyInterfaceValue) + bytesRead += len(tc.cborData) + if decoder.NumBytesRead() != bytesRead { + t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead) + } + } + for i := 0; i < 2; i++ { + // last data item is invalid + err := decoder.Skip() + if err != io.ErrUnexpectedEOF { + t.Errorf("Skip() returned error %v, want io.ErrUnexpectedEOF (truncated data)", err) + } + } + }) + } +} + +func TestDecoderSkipReadError(t *testing.T) { + var buf bytes.Buffer + for _, tc := range unmarshalTests { + buf.Write(tc.cborData) + } + buf.Truncate(buf.Len() - 1) + + readerErr := errors.New("reader error") + + testCases := []struct { + name string + reader io.Reader + }{ + {"byte reader", newNBytesReaderWithError(buf.Bytes(), 512, readerErr)}, + {"1 byte reader", newNBytesReaderWithError(buf.Bytes(), 1, readerErr)}, + {"toggled reader", newToggledReaderWithError(buf.Bytes(), 1, readerErr)}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + decoder := NewDecoder(tc.reader) + bytesRead := 0 + for i := 0; i < len(unmarshalTests)-1; i++ { + tc := unmarshalTests[i] + if err := decoder.Skip(); err != nil { + t.Fatalf("Skip() returned error %v", err) } bytesRead += len(tc.cborData) if decoder.NumBytesRead() != bytesRead { t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead) } } - } + for i := 0; i < 2; i++ { + // truncated data because Reader returned error + err := decoder.Skip() + if err != readerErr { + t.Errorf("Skip() returned error %v, want reader error", err) + } + } + }) } - // no more data - var v interface{} - err := decoder.Decode(&v) - if v != nil { - t.Errorf("Decode() = %v (%T), want nil (no more data)", v, v) +} + +func TestDecoderSkipNoData(t *testing.T) { + readerErr := errors.New("reader error") + + testCases := []struct { + name string + reader io.Reader + wantErr error + }{ + {"byte.Buffer", new(bytes.Buffer), io.EOF}, + {"1 byte reader", newNBytesReaderWithError(nil, 0, readerErr), readerErr}, + {"toggled reader", newToggledReaderWithError(nil, 0, readerErr), readerErr}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + decoder := NewDecoder(tc.reader) + for i := 0; i < 2; i++ { + err := decoder.Skip() + if err != tc.wantErr { + t.Errorf("Decode() returned error %v, want error %v", err, tc.wantErr) + } + } + }) + } +} + +func TestDecoderSkipRecoverableReadError(t *testing.T) { + cborData := hexDecode("83010203") // [1,2,3] + recoverableReaderErr := errors.New("recoverable reader error") + + decoder := NewDecoder(newRecoverableReader(cborData, 1, recoverableReaderErr)) + + err := decoder.Skip() + if err != recoverableReaderErr { + t.Fatalf("Skip() returned error %v, want error %v", err, recoverableReaderErr) } + + err = decoder.Skip() + if err != nil { + t.Fatalf("Skip() returned error %v", err) + } + if decoder.NumBytesRead() != len(cborData) { + t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), len(cborData)) + } + + // no more data + err = decoder.Skip() if err != io.EOF { - t.Errorf("Decode() returned error %v, want io.EOF (no more data)", err) + t.Errorf("Skip() returned error %v, want io.EOF (no more data)", err) } } @@ -127,6 +577,92 @@ func TestDecoderStructTag(t *testing.T) { } } +func TestDecoderBuffered(t *testing.T) { + testCases := []struct { + name string + cborData []byte + buffered []byte + decodeErr error + }{ + { + name: "empty", + cborData: []byte{}, + buffered: []byte{}, + decodeErr: io.EOF, + }, + { + name: "malformed CBOR data item", + cborData: []byte{0xc0}, + buffered: []byte{0xc0}, + decodeErr: io.ErrUnexpectedEOF, + }, + { + name: "1 CBOR data item", + cborData: []byte{0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + buffered: []byte{}, + }, + { + name: "2 CBOR data items", + cborData: []byte{ + // First CBOR data item + 0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Second CBOR data item + 0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + buffered: []byte{ + // Second CBOR data item + 0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }, + { + name: "1 CBOR data item followed by non-CBOR data", + cborData: []byte{ + // CBOR data item + 0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Extraneous non-CBOR data ("abc") + 0x61, 0x62, 0x63, + }, + buffered: []byte{ + // non-CBOR data ("abc") + 0x61, 0x62, 0x63, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + r := bytes.NewReader(tc.cborData) + + decoder := NewDecoder(r) + + // Decoder's buffer has no data yet. + br := decoder.Buffered() + buffered, err := io.ReadAll(br) + if err != nil { + t.Errorf("failed to read from reader returned by Buffered(): %v", err) + } + if len(buffered) > 0 { + t.Errorf("Buffered() = 0x%x (%d bytes), want 0 bytes", buffered, len(buffered)) + } + + var v interface{} + err = decoder.Decode(&v) + if err != tc.decodeErr { + t.Errorf("Decode() returned error %v, want %v", err, tc.decodeErr) + } + + br = decoder.Buffered() + buffered, err = io.ReadAll(br) + if err != nil { + t.Errorf("failed to read from reader returned by Buffered(): %v", err) + } + if !bytes.Equal(tc.buffered, buffered) { + t.Errorf("Buffered() = 0x%x (%d bytes), want 0x%x (%d bytes)", buffered, len(buffered), tc.buffered, len(tc.buffered)) + } + }) + } +} + func TestEncoder(t *testing.T) { var want bytes.Buffer var w bytes.Buffer @@ -387,6 +923,20 @@ func TestRawMessage(t *testing.T) { if !bytes.Equal(b, cborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, cborData) } + + address := fmt.Sprintf("%p", *v.B) + if err := Unmarshal(v.A, v.B); err != nil { + t.Fatalf("Unmarshal(0x%x) returned error %v", v.A, err) + } + if address != fmt.Sprintf("%p", *v.B) { + t.Fatalf("Unmarshal RawMessage should reuse underlying array if it has sufficient capacity") + } + if err := Unmarshal(cborData, v.B); err != nil { + t.Fatalf("Unmarshal(0x%x) returned error %v", cborData, err) + } + if address == fmt.Sprintf("%p", *v.B) { + t.Fatalf("Unmarshal RawMessage should allocate a new underlying array if it does not have sufficient capacity") + } } func TestNullRawMessage(t *testing.T) { @@ -423,3 +973,108 @@ func TestNilRawMessageUnmarshalCBORError(t *testing.T) { t.Errorf("UnmarshalCBOR() returned error %q, want %q", err.Error(), wantErrorMsg) } } + +// nBytesReader reads at most maxBytesPerRead into b. It also returns error at the last read. +type nBytesReader struct { + data []byte + maxBytesPerRead int + off int + err error +} + +func newNBytesReader(data []byte, maxBytesPerRead int) *nBytesReader { + return &nBytesReader{ + data: append([]byte{}, data...), + maxBytesPerRead: maxBytesPerRead, + err: io.EOF, + } +} + +func newNBytesReaderWithError(data []byte, maxBytesPerRead int, err error) *nBytesReader { + return &nBytesReader{ + data: append([]byte{}, data...), + maxBytesPerRead: maxBytesPerRead, + err: err, + } +} + +func (r *nBytesReader) Read(b []byte) (int, error) { + var n int + if r.off < len(r.data) { + numOfBytesToRead := len(r.data) - r.off + if numOfBytesToRead > r.maxBytesPerRead { + numOfBytesToRead = r.maxBytesPerRead + } + n = copy(b, r.data[r.off:r.off+numOfBytesToRead]) + r.off += n + } + if r.off == len(r.data) { + return n, r.err + } + return n, nil +} + +// toggledReader returns (0, nil) for every other read to mimic non-blocking read for stream reader. +type toggledReader struct { + nBytesReader + toggle bool +} + +func newToggledReader(data []byte, maxBytesPerRead int) *toggledReader { + return &toggledReader{ + nBytesReader: nBytesReader{ + data: append([]byte{}, data...), + maxBytesPerRead: maxBytesPerRead, + err: io.EOF, + }, + toggle: true, // first read returns (0, nil) + } +} + +func newToggledReaderWithError(data []byte, maxBytesPerRead int, err error) *toggledReader { + return &toggledReader{ + nBytesReader: nBytesReader{ + data: append([]byte{}, data...), + maxBytesPerRead: maxBytesPerRead, + err: err, + }, + toggle: true, // first read returns (0, nil) + } +} + +func (r *toggledReader) Read(b []byte) (int, error) { + defer func() { + r.toggle = !r.toggle + }() + if r.toggle { + return 0, nil + } + return r.nBytesReader.Read(b) +} + +// recoverableReader returns a recoverable error at first read operation. +type recoverableReader struct { + nBytesReader + recoverableErr error + first bool +} + +func newRecoverableReader(data []byte, maxBytesPerRead int, err error) *recoverableReader { + return &recoverableReader{ + nBytesReader: nBytesReader{ + data: append([]byte{}, data...), + maxBytesPerRead: maxBytesPerRead, + err: io.EOF, + }, + recoverableErr: err, + first: true, + } +} + +func (r *recoverableReader) Read(b []byte) (int, error) { + if r.first { + r.first = false + return 0, r.recoverableErr + } + return r.nBytesReader.Read(b) +} diff --git a/vendor/github.com/fxamacker/cbor/v2/tag.go b/vendor/github.com/fxamacker/cbor/v2/tag.go index db29149f..5205aa66 100644 --- a/vendor/github.com/fxamacker/cbor/v2/tag.go +++ b/vendor/github.com/fxamacker/cbor/v2/tag.go @@ -264,12 +264,6 @@ func newTagItem(opts TagOptions, contentType reflect.Type, num uint64, nestedNum if num == selfDescribedCBORTagNum { return nil, errors.New("cbor: cannot add tag number 55799 to TagSet, it's built-in and ignored automatically") } - //if reflect.PtrTo(contentType).Implements(typeMarshaler) && opts.EncTag != EncTagNone { - //return nil, errors.New("cbor: cannot add cbor.Marshaler to TagSet with EncTag != EncTagNone") - //} - //if reflect.PtrTo(contentType).Implements(typeUnmarshaler) && opts.DecTag != DecTagIgnored { - //return nil, errors.New("cbor: cannot add cbor.Unmarshaler to TagSet with DecTag != DecTagIgnored") - //} te := tagItem{num: []uint64{num}, opts: opts, contentType: contentType} te.num = append(te.num, nestedNum...) diff --git a/vendor/github.com/fxamacker/cbor/v2/valid.go b/vendor/github.com/fxamacker/cbor/v2/valid.go index f775afb0..a5213d06 100644 --- a/vendor/github.com/fxamacker/cbor/v2/valid.go +++ b/vendor/github.com/fxamacker/cbor/v2/valid.go @@ -68,18 +68,36 @@ func (e *TagsMdError) Error() string { return "cbor: CBOR tag isn't allowed" } -// valid checks whether the CBOR data is complete and well-formed. -func (d *decoder) valid() error { +// ExtraneousDataError indicates found extraneous data following well-formed CBOR data item. +type ExtraneousDataError struct { + numOfBytes int // number of bytes of extraneous data + index int // location of extraneous data +} + +func (e *ExtraneousDataError) Error() string { + return "cbor: " + strconv.Itoa(e.numOfBytes) + " bytes of extraneous data starting at index " + strconv.Itoa(e.index) +} + +// wellformed checks whether the CBOR data item is well-formed. +// allowExtraData indicates if extraneous data is allowed after the CBOR data item. +// - use allowExtraData = true when using Decoder.Decode() +// - use allowExtraData = false when using Unmarshal() +func (d *decoder) wellformed(allowExtraData bool) error { if len(d.data) == d.off { return io.EOF } - _, err := d.validInternal(0) + _, err := d.wellformedInternal(0) + if err == nil { + if !allowExtraData && d.off != len(d.data) { + err = &ExtraneousDataError{len(d.data) - d.off, d.off} + } + } return err } -// validInternal checks data's well-formedness and returns max depth and error. -func (d *decoder) validInternal(depth int) (int, error) { - t, ai, val, err := d.validHead() +// wellformedInternal checks data's well-formedness and returns max depth and error. +func (d *decoder) wellformedInternal(depth int) (int, error) { + t, ai, val, err := d.wellformedHead() if err != nil { return 0, err } @@ -90,7 +108,7 @@ func (d *decoder) validInternal(depth int) (int, error) { if d.dm.indefLength == IndefLengthForbidden { return 0, &IndefiniteLengthError{t} } - return d.validIndefiniteString(t, depth) + return d.wellformedIndefiniteString(t, depth) } valInt := int(val) if valInt < 0 { @@ -111,7 +129,7 @@ func (d *decoder) validInternal(depth int) (int, error) { if d.dm.indefLength == IndefLengthForbidden { return 0, &IndefiniteLengthError{t} } - return d.validIndefiniteArrayOrMap(t, depth) + return d.wellformedIndefiniteArrayOrMap(t, depth) } valInt := int(val) @@ -138,7 +156,7 @@ func (d *decoder) validInternal(depth int) (int, error) { for j := 0; j < count; j++ { for i := 0; i < valInt; i++ { var dpt int - if dpt, err = d.validInternal(depth); err != nil { + if dpt, err = d.wellformedInternal(depth); err != nil { return 0, err } if dpt > maxDepth { @@ -160,7 +178,7 @@ func (d *decoder) validInternal(depth int) (int, error) { if cborType(d.data[d.off]&0xe0) != cborTypeTag { break } - if _, _, _, err = d.validHead(); err != nil { + if _, _, _, err = d.wellformedHead(); err != nil { return 0, err } depth++ @@ -169,13 +187,13 @@ func (d *decoder) validInternal(depth int) (int, error) { } } // Check tag content. - return d.validInternal(depth) + return d.wellformedInternal(depth) } return depth, nil } -// validIndefiniteString checks indefinite length byte/text string's well-formedness and returns max depth and error. -func (d *decoder) validIndefiniteString(t cborType, depth int) (int, error) { +// wellformedIndefiniteString checks indefinite length byte/text string's well-formedness and returns max depth and error. +func (d *decoder) wellformedIndefiniteString(t cborType, depth int) (int, error) { var err error for { if len(d.data) == d.off { @@ -193,15 +211,15 @@ func (d *decoder) validIndefiniteString(t cborType, depth int) (int, error) { if (d.data[d.off] & 0x1f) == 31 { return 0, &SyntaxError{"cbor: indefinite-length " + t.String() + " chunk is not definite-length"} } - if depth, err = d.validInternal(depth); err != nil { + if depth, err = d.wellformedInternal(depth); err != nil { return 0, err } } return depth, nil } -// validIndefiniteArrayOrMap checks indefinite length array/map's well-formedness and returns max depth and error. -func (d *decoder) validIndefiniteArrayOrMap(t cborType, depth int) (int, error) { +// wellformedIndefiniteArrayOrMap checks indefinite length array/map's well-formedness and returns max depth and error. +func (d *decoder) wellformedIndefiniteArrayOrMap(t cborType, depth int) (int, error) { var err error maxDepth := depth i := 0 @@ -214,7 +232,7 @@ func (d *decoder) validIndefiniteArrayOrMap(t cborType, depth int) (int, error) break } var dpt int - if dpt, err = d.validInternal(depth); err != nil { + if dpt, err = d.wellformedInternal(depth); err != nil { return 0, err } if dpt > maxDepth { @@ -237,7 +255,7 @@ func (d *decoder) validIndefiniteArrayOrMap(t cborType, depth int) (int, error) return maxDepth, nil } -func (d *decoder) validHead() (t cborType, ai byte, val uint64, err error) { +func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error) { dataLen := len(d.data) - d.off if dataLen == 0 { return 0, 0, 0, io.ErrUnexpectedEOF @@ -290,7 +308,7 @@ func (d *decoder) validHead() (t cborType, ai byte, val uint64, err error) { switch t { case cborTypePositiveInt, cborTypeNegativeInt, cborTypeTag: return 0, 0, 0, &SyntaxError{"cbor: invalid additional information " + strconv.Itoa(int(ai)) + " for type " + t.String()} - case cborTypePrimitives: // 0xff (break code) should not be outside validIndefinite(). + case cborTypePrimitives: // 0xff (break code) should not be outside wellformedIndefinite(). return 0, 0, 0, &SyntaxError{"cbor: unexpected \"break\" code"} } return t, ai, val, nil diff --git a/vendor/github.com/fxamacker/cbor/v2/valid_test.go b/vendor/github.com/fxamacker/cbor/v2/valid_test.go index 109ca2cb..39e9e4d6 100644 --- a/vendor/github.com/fxamacker/cbor/v2/valid_test.go +++ b/vendor/github.com/fxamacker/cbor/v2/valid_test.go @@ -10,8 +10,8 @@ import ( func TestValid1(t *testing.T) { for _, mt := range marshalTests { - if err := Valid(mt.cborData); err != nil { - t.Errorf("Valid() returned error %v", err) + if err := Wellformed(mt.cborData); err != nil { + t.Errorf("Wellformed() returned error %v", err) } } } @@ -19,12 +19,43 @@ func TestValid1(t *testing.T) { func TestValid2(t *testing.T) { for _, mt := range marshalTests { dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() - if err := dm.Valid(mt.cborData); err != nil { - t.Errorf("Valid() returned error %v", err) + if err := dm.Wellformed(mt.cborData); err != nil { + t.Errorf("Wellformed() returned error %v", err) } } } +func TestValidExtraneousData(t *testing.T) { + testCases := []struct { + name string + cborData []byte + extraneousDataNumOfBytes int + extraneousDataIndex int + }{ + {"two numbers", []byte{0x00, 0x01}, 1, 1}, // 0, 1 + {"bytestring and int", []byte{0x44, 0x01, 0x02, 0x03, 0x04, 0x00}, 1, 5}, // h'01020304', 0 + {"int and partial array", []byte{0x00, 0x83, 0x01, 0x02}, 3, 1}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := Wellformed(tc.cborData) + if err == nil { + t.Errorf("Wellformed(0x%x) didn't return an error", tc.cborData) + } else { + ederr, ok := err.(*ExtraneousDataError) + if !ok { + t.Errorf("Wellformed(0x%x) error type %T, want *ExtraneousDataError", tc.cborData, err) + } else if ederr.numOfBytes != tc.extraneousDataNumOfBytes { + t.Errorf("Wellformed(0x%x) returned %d bytes of extraneous data, want %d", tc.cborData, ederr.numOfBytes, tc.extraneousDataNumOfBytes) + } else if ederr.index != tc.extraneousDataIndex { + t.Errorf("Wellformed(0x%x) returned extraneous data index %d, want %d", tc.cborData, ederr.index, tc.extraneousDataIndex) + } + } + }) + } +} + func TestValidOnStreamingData(t *testing.T) { var buf bytes.Buffer for _, t := range marshalTests { @@ -32,8 +63,8 @@ func TestValidOnStreamingData(t *testing.T) { } d := decoder{data: buf.Bytes(), dm: defaultDecMode} for i := 0; i < len(marshalTests); i++ { - if err := d.valid(); err != nil { - t.Errorf("valid() returned error %v", err) + if err := d.wellformed(true); err != nil { + t.Errorf("wellformed() returned error %v", err) } } } @@ -80,12 +111,12 @@ func TestDepth(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { d := decoder{data: tc.cborData, dm: defaultDecMode} - depth, err := d.validInternal(0) + depth, err := d.wellformedInternal(0) if err != nil { - t.Errorf("valid(0x%x) returned error %v", tc.cborData, err) + t.Errorf("wellformed(0x%x) returned error %v", tc.cborData, err) } if depth != tc.wantDepth { - t.Errorf("valid(0x%x) returned depth %d, want %d", tc.cborData, depth, tc.wantDepth) + t.Errorf("wellformed(0x%x) returned depth %d, want %d", tc.cborData, depth, tc.wantDepth) } }) } @@ -145,12 +176,12 @@ func TestDepthError(t *testing.T) { t.Run(tc.name, func(t *testing.T) { dm, _ := tc.opts.decMode() d := decoder{data: tc.cborData, dm: dm} - if _, err := d.validInternal(0); err == nil { - t.Errorf("valid(0x%x) didn't return an error", tc.cborData) + if _, err := d.wellformedInternal(0); err == nil { + t.Errorf("wellformed(0x%x) didn't return an error", tc.cborData) } else if _, ok := err.(*MaxNestedLevelError); !ok { - t.Errorf("valid(0x%x) returned wrong error type %T, want (*MaxNestedLevelError)", tc.cborData, err) + t.Errorf("wellformed(0x%x) returned wrong error type %T, want (*MaxNestedLevelError)", tc.cborData, err) } else if err.Error() != tc.wantErrorMsg { - t.Errorf("valid(0x%x) returned error %q, want error %q", tc.cborData, err.Error(), tc.wantErrorMsg) + t.Errorf("wellformed(0x%x) returned error %q, want error %q", tc.cborData, err.Error(), tc.wantErrorMsg) } }) }