pax_global_header00006660000000000000000000000064141661253560014523gustar00rootroot0000000000000052 comment=2749721c5b4f12582a5827f4d76a76c72f033225 golang-github-fxamacker-cbor-2.4.0/000077500000000000000000000000001416612535600171375ustar00rootroot00000000000000golang-github-fxamacker-cbor-2.4.0/.github/000077500000000000000000000000001416612535600204775ustar00rootroot00000000000000golang-github-fxamacker-cbor-2.4.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001416612535600226625ustar00rootroot00000000000000golang-github-fxamacker-cbor-2.4.0/.github/ISSUE_TEMPLATE/1-bug-report.md000066400000000000000000000010451416612535600254300ustar00rootroot00000000000000--- name: "\U0001F41E Bug report" about: Create a report to help us improve title: 'bug: ' labels: '' assignees: '' --- ### What version of fxamacker/cbor are you using? ### Does this issue reproduce with the latest release? ### What OS and CPU architecture are you using (`go env`)?
go env Output
$ go env

### What did you do? ### What did you expect to see? ### What did you see instead? golang-github-fxamacker-cbor-2.4.0/.github/ISSUE_TEMPLATE/2-feature-request.md000066400000000000000000000011511416612535600264620ustar00rootroot00000000000000--- name: "\U0001F4A1 Feature request" about: Suggest an idea for this project title: 'feature: ' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. golang-github-fxamacker-cbor-2.4.0/.github/ISSUE_TEMPLATE/3-docs-wiki-or-website-issue.md000066400000000000000000000004151416612535600304410ustar00rootroot00000000000000--- name: "\U0001F4DA Docs, wiki, or website issue" about: Report an issue regarding documentation, wiki, or website title: 'docs: ' labels: '' assignees: '' --- ### What is the URL of the content? ### Please describe the problem. ### Screenshot (if applicable). golang-github-fxamacker-cbor-2.4.0/.github/ISSUE_TEMPLATE/4-security-issue-disclosure.md000066400000000000000000000004171416612535600305160ustar00rootroot00000000000000--- name: "\U0001F513 Security issue disclosure" about: Report a security issue in fxamacker/cbor title: '' labels: '' assignees: '' --- golang-github-fxamacker-cbor-2.4.0/.github/pull_request_template.md000066400000000000000000000046421416612535600254460ustar00rootroot00000000000000 #### Description (motivation) #### PR Was Proposed and Welcomed in Currently Open Issue This PR was proposed and welcomed by maintainer(s) in issue # Closes # #### Checklist (for code PR only, ignore for docs PR) - [ ] Include unit tests that cover the new code - [ ] Pass all unit tests - [ ] Pass all 18 ci linters (golint, gosec, staticcheck, etc.) - [ ] Sign each commit with your real name and email. Last line of each commit message should be in this format: Signed-off-by: Firstname Lastname - [ ] Certify the Developer's Certificate of Origin 1.1 (see next section). #### Certify the Developer's Certificate of Origin 1.1 - [ ] By marking this item as completed, I certify the Developer Certificate of Origin 1.1. ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` golang-github-fxamacker-cbor-2.4.0/.github/workflows/000077500000000000000000000000001416612535600225345ustar00rootroot00000000000000golang-github-fxamacker-cbor-2.4.0/.github/workflows/ci-go-cover.yml000066400000000000000000000035121416612535600253720ustar00rootroot00000000000000# Copyright 2020-present 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. # 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. # # To help protect your privacy, this workflow avoids external services. # This workflow simply runs `go test -short -cover` --> grep --> python. # The python script is embedded and readable in this file. # # Steps to install and set minimum required coverage: # 0. Copy this file to github.com/OWNER_NAME/REPO_NAME/.github/workflows/ci-go-cover.yml # 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] 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 runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Go Coverage run: | go version go test -short -cover | grep "^.*coverage:.*of statements$" | python -c "import os,re,sys; cover_rpt = sys.stdin.read(); print(cover_rpt) if len(cover_rpt) != 0 and len(cover_rpt.splitlines()) == 1 else sys.exit(1); min_cover = float(re.findall(r'\d*\.\d+|\d+', os.environ['GITHUB_WORKFLOW'])[0]); cover = float(re.findall(r'\d*\.\d+|\d+', cover_rpt)[0]); sys.exit(1) if (cover > 100) or (cover < min_cover) else sys.exit(0)" shell: bash golang-github-fxamacker-cbor-2.4.0/.github/workflows/ci.yml000066400000000000000000000016511416612535600236550ustar00rootroot00000000000000# GitHub Actions - CI for Go to build & test. See ci-go-cover.yml and safer-golangci-lint.yml for more. # https://github.com/fxamacker/cbor/workflows/ci.yml name: ci on: [push] jobs: # Test on various OS with default Go version. tests: name: Test on ${{matrix.os}} runs-on: ${{ matrix.os }} strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] go-version: [1.15.x, 1.16.x, 1.17.x] steps: - name: Install Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v2 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 -race -v ./... golang-github-fxamacker-cbor-2.4.0/.github/workflows/codeql-analysis.yml000066400000000000000000000046131416612535600263530ustar00rootroot00000000000000# 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" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '30 5 * * 4' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: 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 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 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 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 golang-github-fxamacker-cbor-2.4.0/.github/workflows/safer-golangci-lint.yml000066400000000000000000000074301416612535600271100ustar00rootroot00000000000000# Copyright © 2021 Montgomery Edwards⁴⁴⁸ (github.com/x448). # This file is licensed under MIT License. # # Safer GitHub Actions Workflow for golangci-lint. # https://github.com/x448/safer-golangci-lint # # safer-golangci-lint.yml # # 100% of the script for downloading, installing, and running golangci-lint # is embedded in this file. The embedded SHA384 digest is used to verify the # downloaded golangci-lint tarball (golangci-lint-1.42.0-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 embedded SHA384 instead of downloading CHECKSUM because CHECKSUM file isn't digitally signed. # 3. Use openssl instead of sha256sum because it's easier to change hash algo to BLAKE2s, SHA3-256, etc. # 4. 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. # # To use: # Step 1. Copy this file into [github_repo]/.github/workflows/ # Step 2. There's no step 2 if you like the default settings. # # You can create and use a config file (.golangci.yml) as described in golangci-lint docs. # # To use a newer version of golangci-lint, change these values: # 1. GOLINTERS_VERSION # 2. GOLINTERS_TGZ_DGST # # Release v1.43.0 (Dec 5, 2021) # - Bump Go to 1.17.x. # - Bump golangci-lint to 1.43.0. # - Checksum for golangci-lint-1.43.0-linux-amd64.tar.gz # - SHA-256 is f3515cebec926257da703ba0a2b169e4a322c11dc31a8b4656b50a43e48877f4 # - SHA-384 is 0a5e9adc3cc93fbc2e3c8f4daee061e77407fe3e702001021ef03c8bdf39a81c36c42d35e8ba970f4f09db2279c881bf # name: linters on: pull_request: types: [opened, synchronize, closed] push: branches: [main, master] env: GOLINTERS_VERSION: 1.43.0 GOLINTERS_ARCH: linux-amd64 GOLINTERS_TGZ_DGST: 0a5e9adc3cc93fbc2e3c8f4daee061e77407fe3e702001021ef03c8bdf39a81c36c42d35e8ba970f4f09db2279c881bf GOLINTERS_TIMEOUT: 5m OPENSSL_DGST_CMD: openssl dgst -sha384 -r CURL_CMD: curl --proto =https --tlsv1.2 --location --silent --show-error --fail jobs: main: name: Lint runs-on: ubuntu-latest steps: - name: Checkout source uses: actions/checkout@v2 with: fetch-depth: 1 - name: Setup Go uses: actions/setup-go@v2 with: go-version: 1.17.x - name: Install golangci-lint run: | GOLINTERS_URL_PREFIX="https://github.com/golangci/golangci-lint/releases/download/v${GOLINTERS_VERSION}/" GOLINTERS_TGZ="golangci-lint-${GOLINTERS_VERSION}-${GOLINTERS_ARCH}.tar.gz" GOLINTERS_EXPECTED_DGST="${GOLINTERS_TGZ_DGST} *${GOLINTERS_TGZ}" DGST_CMD="${OPENSSL_DGST_CMD} ${GOLINTERS_TGZ}" cd $(mktemp -d /tmp/golinters.XXXXX) ${CURL_CMD} "${GOLINTERS_URL_PREFIX}${GOLINTERS_TGZ}" --output ${GOLINTERS_TGZ} GOLINTERS_GOT_DGST=$(${DGST_CMD}) if [ "${GOLINTERS_GOT_DGST}" != "${GOLINTERS_EXPECTED_DGST}" ] then echo "Digest of tarball is not equal to expected digest." echo "Expected digest: " "${GOLINTERS_EXPECTED_DGST}" echo "Got digest: " "${GOLINTERS_GOT_DGST}" exit 1 fi 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 (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 golang-github-fxamacker-cbor-2.4.0/.gitignore000066400000000000000000000003001416612535600211200ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out golang-github-fxamacker-cbor-2.4.0/.golangci.yml000066400000000000000000000030351416612535600215240ustar00rootroot00000000000000# Do not delete linter settings. Linters like gocritic can be enabled on the command line. linters-settings: dupl: threshold: 100 funlen: lines: 100 statements: 50 goconst: min-len: 2 min-occurrences: 3 gocritic: enabled-tags: - diagnostic - experimental - opinionated - performance - style disabled-checks: - dupImport # https://github.com/go-critic/go-critic/issues/845 - ifElseChain - octalLiteral - paramTypeCombine - whyNoLint - wrapperFunc gofmt: simplify: false goimports: local-prefixes: github.com/fxamacker/cbor golint: min-confidence: 0 govet: check-shadowing: true lll: line-length: 140 maligned: suggest-new: true misspell: locale: US linters: disable-all: true enable: - deadcode - errcheck - goconst - gocyclo - gofmt - goimports - gosec - govet - ineffassign - misspell - revive - staticcheck - structcheck - typecheck - unconvert - unused - varcheck issues: # max-issues-per-linter default is 50. Set to 0 to disable limit. max-issues-per-linter: 0 # max-same-issues default is 3. Set to 0 to disable limit. max-same-issues: 0 # Excluding configuration per-path, per-linter, per-text and per-source exclude-rules: - path: _test\.go linters: - goconst - dupl - gomnd - lll - path: doc\.go linters: - goimports - gomnd - lll golang-github-fxamacker-cbor-2.4.0/CBOR_BENCHMARKS.md000066400000000000000000000244461416612535600217150ustar00rootroot00000000000000# 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 golang-github-fxamacker-cbor-2.4.0/CBOR_GOLANG.md000066400000000000000000000066241416612535600212450ustar00rootroot00000000000000👉 [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) golang-github-fxamacker-cbor-2.4.0/CODE_OF_CONDUCT.md000066400000000000000000000064311416612535600217420ustar00rootroot00000000000000# 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. ## Our Standards Examples of behavior that contributes to creating a positive environment 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 Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/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 * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our 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. 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. ## 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. ## 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. 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. ## 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 [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq golang-github-fxamacker-cbor-2.4.0/CONTRIBUTING.md000066400000000000000000000101711416612535600213700ustar00rootroot00000000000000# How to contribute Here are some ways you can contribute: - Give this library a star on GitHub. It doesn't cost anything and it lets maintainers know you appreciate their work. - Use this library in your project. By using this library, you're more likely to open an issue with feature request, etc. - Report security vulnerabilities privately by email after reading this contributing guide and [Security Policy](https://github.com/fxamacker/cbor#security-policy). - Open an issue with a feature request. It can help prioritize issues if you provide a link to your project and mention if a missing feature prevents your project from using this library. - Open an issue with a bug report. It's helpful if the bug report includes a link to a reproducer at [Go Playground](https://go.dev/play/). - Open a PR that would close a specific issue. Ask if it's a good time to open a PR in the issue because a solution might already be in progress. Please also read about the signing requirements before spending time on a PR. If you'd like to contribute code or send CBOR data, please read on (it can save you time!) ## Private reports Usually, all issues are tracked publicly on [GitHub](https://github.com/fxamacker/cbor/issues). 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 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 Pull requests have signing requirements and must not be anonymous. Exceptions can be made for docs and CI scripts. See our [Pull Request Template](https://github.com/fxamacker/cbor/blob/master/.github/pull_request_template.md) for details. 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. 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. ## 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. ## 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 around 98%) * Pull requests with small changes that are well-documented and easily understandable. * Sponsors, donations, bounties, or subscriptions. ## Credits - 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. golang-github-fxamacker-cbor-2.4.0/LICENSE000066400000000000000000000020641416612535600201460ustar00rootroot00000000000000MIT License Copyright (c) 2019-present Faye Amacker Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.golang-github-fxamacker-cbor-2.4.0/README.md000066400000000000000000001523341416612535600204260ustar00rootroot00000000000000# CBOR Codec in Go [![](https://github.com/fxamacker/images/raw/master/cbor/v2.4.0/fxamacker_cbor_banner.png)](#cbor-library-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) [![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-3%2B%20billion%20execs-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/badge/go-%3E%3D%201.12-blue)](#cbor-library-installation) [__fxamacker/cbor__](https://github.com/fxamacker/cbor) is a modern [CBOR](https://tools.ietf.org/html/rfc8949) codec in [Go](https://golang.org). It's like `encoding/json` for CBOR with time-saving features. It balances [security](https://github.com/fxamacker/cbor/#cbor-security), usability, [speed](https://github.com/fxamacker/cbor/#cbor-performance), data size, program size, and other competing factors. Features include CBOR tags, duplicate map key detection, float64→32→16, and Go struct tags (`toarray`, `keyasint`, `omitempty`). API is close to `encoding/json` plus predefined CBOR options like Core Deterministic Encoding, Preferred Serialization, CTAP2, etc. Using CBOR [Preferred Serialization](https://www.rfc-editor.org/rfc/rfc8949.html#name-preferred-serialization) with Go struct tags (`toarray`, `keyasint`, `omitempty`) reduces programming effort and creates smaller encoded data size. fxamacker/cbor has 98% coverage and is fuzz tested. It won't exhaust RAM decoding 9 bytes of bad CBOR data. It's used by Arm Ltd., Berlin Institute of Health at Charité, Chainlink, ConsenSys, Dapper Labs, Duo Labs (cisco), EdgeX Foundry, Mozilla, Netherlands (govt), Oasis Labs, Taurus SA, Teleport, and others. Install with `go get github.com/fxamacker/cbor/v2` and `import "github.com/fxamacker/cbor/v2"`. See [Quick Start](#quick-start) to save time. ## What is CBOR? [CBOR](https://tools.ietf.org/html/rfc8949) is a concise binary data format inspired by [JSON](https://www.json.org) and [MessagePack](https://msgpack.org). CBOR is defined in [RFC 8949](https://tools.ietf.org/html/rfc8949) (December 2020) which obsoletes [RFC 7049](https://tools.ietf.org/html/rfc7049) (October 2013). CBOR is an [Internet Standard](https://en.wikipedia.org/wiki/Internet_Standard) by [IETF](https://www.ietf.org). It's used in other standards like [WebAuthn](https://en.wikipedia.org/wiki/WebAuthn) by [W3C](https://www.w3.org), [COSE (RFC 8152)](https://tools.ietf.org/html/rfc8152), [CWT (RFC 8392)](https://tools.ietf.org/html/rfc8392), [CDDL (RFC 8610)](https://datatracker.ietf.org/doc/html/rfc8610) and [more](CBOR_GOLANG.md). [Reasons for choosing CBOR](https://github.com/fxamacker/cbor/wiki/Why-CBOR) vary by project. Some projects replaced protobuf, encoding/json, encoding/gob, etc. with CBOR. For example, by replacing protobuf with CBOR in gRPC. ## Why fxamacker/cbor? fxamacker/cbor balances competing factors such as speed, size, safety, usability, maintainability, and etc. - Killer features include Go struct tags like `toarray`, `keyasint`, etc. They reduce encoded data size, improve speed, and reduce programming effort. For example, `toarray` automatically translates a Go struct to/from a CBOR array. - Modern CBOR features include Core Deterministic Encoding and Preferred Encoding. Other features include CBOR tags, big.Int, float64→32→16, an API like `encoding/json`, and more. - Security features include the option to detect duplicate map keys and options to set various max limits. And it's designed to make concurrent use of CBOR options easy and free from side-effects. - To prevent crashes, it has been fuzz-tested since before release 1.0 and code coverage is kept above 98%. - For portability and safety, it avoids using `unsafe`, which makes it portable and protected by Go1's compatibility guidelines. - For performance, it uses safe optimizations. When used properly, fxamacker/cbor can be faster than CBOR codecs that rely on `unsafe`. However, speed is only one factor and should be considered together with other competing factors. ## CBOR Security __fxamacker/cbor__ is secure. It rejects malformed CBOR data and has an option to detect duplicate map keys. It doesn't crash when decoding bad CBOR data. It has extensive tests, coverage-guided fuzzing, data validation, and avoids Go's `unsafe` package. Decoding 9 or 10 bytes of malformed CBOR data shouldn't exhaust memory. For example, `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}` | | Decode bad 10 bytes to interface{} | Decode bad 10 bytes to []byte | | :--- | :------------------ | :--------------- | | fxamacker/cbor
1.0-2.3 | 49.44 ns/op, 24 B/op, 2 allocs/op* | 51.93 ns/op, 32 B/op, 2 allocs/op* | | ugorji/go 1.2.6 | ⚠️ 45021 ns/op, 262852 B/op, 7 allocs/op | 💥 runtime: out of memory: cannot allocate | | ugorji/go 1.1-1.1.7 | 💥 runtime: out of memory: cannot allocate | 💥 runtime: out of memory: cannot allocate| *Speed and memory are for latest codec version listed in the row (compiled with Go 1.17.5). fxamacker/cbor CBOR safety settings include: MaxNestedLevels, MaxArrayElements, MaxMapPairs, and IndefLength. 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." ## CBOR Performance __fxamacker/cbor__ is fast without sacrificing security. It can be faster than libraries relying on `unsafe` package. ![alt text](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/cbor_speed_comparison.svg?sanitize=1 "CBOR speed comparison chart") __Click to expand:__
👉 CBOR Program Size Comparison

__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")

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

fxamacker/cbor 2.3.0 (not using `unsafe`) is faster than ugorji/go 1.2.6 (using `unsafe`). ``` benchstat results/bench-ugorji-go-count20.txt results/bench-fxamacker-cbor-count20.txt name old time/op new time/op delta DecodeCWTClaims-8 1.08µs ± 0% 0.67µs ± 0% -38.10% (p=0.000 n=16+20) DecodeCOSE/128-Bit_Symmetric_Key-8 715ns ± 0% 501ns ± 0% -29.97% (p=0.000 n=20+19) DecodeCOSE/256-Bit_Symmetric_Key-8 722ns ± 0% 507ns ± 0% -29.72% (p=0.000 n=19+18) DecodeCOSE/ECDSA_P256_256-Bit_Key-8 1.11µs ± 0% 0.83µs ± 0% -25.27% (p=0.000 n=19+20) DecodeWebAuthn-8 880ns ± 0% 727ns ± 0% -17.31% (p=0.000 n=18+20) EncodeCWTClaims-8 785ns ± 0% 388ns ± 0% -50.51% (p=0.000 n=20+20) EncodeCOSE/128-Bit_Symmetric_Key-8 973ns ± 0% 433ns ± 0% -55.45% (p=0.000 n=20+19) EncodeCOSE/256-Bit_Symmetric_Key-8 974ns ± 0% 435ns ± 0% -55.37% (p=0.000 n=20+19) EncodeCOSE/ECDSA_P256_256-Bit_Key-8 1.14µs ± 0% 0.55µs ± 0% -52.10% (p=0.000 n=19+19) EncodeWebAuthn-8 564ns ± 0% 450ns ± 1% -20.18% (p=0.000 n=18+20) name old alloc/op new alloc/op delta DecodeCWTClaims-8 744B ± 0% 160B ± 0% -78.49% (p=0.000 n=20+20) DecodeCOSE/128-Bit_Symmetric_Key-8 792B ± 0% 232B ± 0% -70.71% (p=0.000 n=20+20) DecodeCOSE/256-Bit_Symmetric_Key-8 816B ± 0% 256B ± 0% -68.63% (p=0.000 n=20+20) DecodeCOSE/ECDSA_P256_256-Bit_Key-8 905B ± 0% 344B ± 0% -61.99% (p=0.000 n=20+20) DecodeWebAuthn-8 1.56kB ± 0% 0.99kB ± 0% -36.41% (p=0.000 n=20+20) EncodeCWTClaims-8 1.35kB ± 0% 0.18kB ± 0% -86.98% (p=0.000 n=20+20) EncodeCOSE/128-Bit_Symmetric_Key-8 1.95kB ± 0% 0.22kB ± 0% -88.52% (p=0.000 n=20+20) EncodeCOSE/256-Bit_Symmetric_Key-8 1.95kB ± 0% 0.24kB ± 0% -87.70% (p=0.000 n=20+20) EncodeCOSE/ECDSA_P256_256-Bit_Key-8 1.95kB ± 0% 0.32kB ± 0% -83.61% (p=0.000 n=20+20) EncodeWebAuthn-8 1.30kB ± 0% 1.09kB ± 0% -16.56% (p=0.000 n=20+20) name old allocs/op new allocs/op delta DecodeCWTClaims-8 6.00 ± 0% 6.00 ± 0% ~ (all equal) DecodeCOSE/128-Bit_Symmetric_Key-8 4.00 ± 0% 4.00 ± 0% ~ (all equal) DecodeCOSE/256-Bit_Symmetric_Key-8 4.00 ± 0% 4.00 ± 0% ~ (all equal) DecodeCOSE/ECDSA_P256_256-Bit_Key-8 7.00 ± 0% 7.00 ± 0% ~ (all equal) DecodeWebAuthn-8 5.00 ± 0% 5.00 ± 0% ~ (all equal) EncodeCWTClaims-8 4.00 ± 0% 2.00 ± 0% -50.00% (p=0.000 n=20+20) EncodeCOSE/128-Bit_Symmetric_Key-8 6.00 ± 0% 2.00 ± 0% -66.67% (p=0.000 n=20+20) EncodeCOSE/256-Bit_Symmetric_Key-8 6.00 ± 0% 2.00 ± 0% -66.67% (p=0.000 n=20+20) EncodeCOSE/ECDSA_P256_256-Bit_Key-8 6.00 ± 0% 2.00 ± 0% -66.67% (p=0.000 n=20+20) EncodeWebAuthn-8 4.00 ± 0% 2.00 ± 0% -50.00% (p=0.000 n=20+20) ```

Benchmarks used Go 1.17.5, linux_amd64, and 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. ## CBOR API __fxamacker/cbor__ is easy to use. It provides standard API and interfaces. __Standard API__. Function signatures identical to [`encoding/json`](https://golang.org/pkg/encoding/json/) include: `Marshal`, `Unmarshal`, `NewEncoder`, `NewDecoder`, `(*Encoder).Encode`, and `(*Decoder).Decode`. __Standard Interfaces__. Custom encoding and decoding is handled by implementing: `BinaryMarshaler`, `BinaryUnmarshaler`, `Marshaler`, and `Unmarshaler`. __Predefined Encoding Options__. Encoding options are easy to use and are customizable. ```go 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 ``` fxamacker/cbor designed to simplify concurrency. CBOR options can be used without creating unintended runtime side-effects. ## Go Struct Tags __fxamacker/cbor__ provides Go struct tags like __`toarray`__ and __`keyasint`__ to save time and reduce encoded size of data.
![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") ## CBOR Features __fxamacker/cbor__ is a full-featured CBOR encoder and decoder. | | 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). | ## CBOR Library Installation fxamacker/cbor supports Go 1.12 and newer versions. Init the Go module, go get v2, and begin coding. ``` go mod init github.com/my_name/my_repo go get github.com/fxamacker/cbor/v2 ``` ```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). 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__ 💡 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__ 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 ``` TagSet and all modes using it are safe for concurrent use. Equivalent API is available for DecMode. __Predefined Encoding Options__ ```go 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) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) ## Features ### Standard API Many function signatures are identical to encoding/json, including: `Marshal`, `Unmarshal`, `NewEncoder`, `NewDecoder`, `(*Encoder).Encode`, `(*Decoder).Decode`. `RawMessage` can be used to delay CBOR decoding or precompute CBOR encoding, like `encoding/json`. Standard interfaces allow user-defined types to have custom CBOR encoding and decoding. They include: `BinaryMarshaler`, `BinaryUnmarshaler`, `Marshaler`, and `Unmarshaler`. `Marshaler` and `Unmarshaler` interfaces are satisfied by `MarshalCBOR` and `UnmarshalCBOR` functions using same params and return types as Go's MarshalJSON and UnmarshalJSON. ### Struct Tags 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. See [Struct Tags](#struct-tags-1) for more info. ### CBOR Tags (New in v2.1) There are three categories of CBOR tags: * __Default built-in CBOR tags__ currently include tag numbers 0 (Standard Date/Time), 1 (Epoch Date/Time), 2 (Unsigned Bignum), 3 (Negative Bignum), 55799 (Self-Described CBOR). * __Optional built-in CBOR tags__ may be provided in the future via build flags or optional package(s) to help reduce bloat. * __User-defined CBOR tags__ are easy by using TagSet to associate tag numbers to user-defined Go types. ### Preferred Serialization Preferred serialization encodes integers and floating-point values using the fewest bytes possible. * Integers are always encoded using the fewest bytes possible. * Floating-point values can optionally encode from float64->float32->float16 when values fit. ### Compact Data Size The combination of preferred serialization and struct tags (toarray, keyasint, omitempty) allows very compact data size. ### Predefined Encoding Options Easy-to-use functions (no params) return preset EncOptions struct: `CanonicalEncOptions`, `CTAP2EncOptions`, `CoreDetEncOptions`, `PreferredUnsortedEncOptions` ### Encoding Options Integers always encode to the shortest form that preserves value. By default, time values are encoded without tags. Encoding of other data types and map key sort order are determined by encoder options. | EncOptions | Available Settings (defaults listed first) | :--- | :--- | | Sort | **SortNone**, SortLengthFirst, SortBytewiseLexical
Aliases: SortCanonical, SortCTAP2, SortCoreDeterministic | | Time | **TimeUnix**, TimeUnixMicro, TimeUnixDynamic, TimeRFC3339, TimeRFC3339Nano | | TimeTag | **EncTagNone**, EncTagRequired | | ShortestFloat | **ShortestFloatNone**, ShortestFloat16 | | BigIntConvert | **BigIntConvertShortest**, BigIntConvertNone | | InfConvert | **InfConvertFloat16**, InfConvertNone | | NaNConvert | **NaNConvert7e00**, NaNConvertNone, NaNConvertQuiet, NaNConvertPreserveSignal | | IndefLength | **IndefLengthAllowed**, IndefLengthForbidden | | TagsMd | **TagsAllowed**, TagsForbidden | See [Options](#options) section for details about each setting. ### Decoding Options | DecOptions | Available Settings (defaults listed first) | | :--- | :--- | | TimeTag | **DecTagIgnored**, DecTagOptional, DecTagRequired | | DupMapKey | **DupMapKeyQuiet**, DupMapKeyEnforcedAPF | | IntDec | **IntDecConvertNone**, IntDecConvertSigned | | IndefLength | **IndefLengthAllowed**, IndefLengthForbidden | | TagsMd | **TagsAllowed**, TagsForbidden | | ExtraReturnErrors | **ExtraDecErrorNone**, ExtraDecErrorUnknownField | | MaxNestedLevels | **32**, can be set to [4, 256] | | MaxArrayElements | **131072**, can be set to [16, 2147483647] | | MaxMapPairs | **131072**, can be set to [16, 2147483647] | See [Options](#options) section for details about each setting. ### Additional Features * 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. * Decoder supports decoding registered CBOR tag data to interface types. * 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.
⚓ [Quick Start](#quick-start) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) ## Standards This library is a full-featured generic CBOR [(RFC 8949)](https://tools.ietf.org/html/rfc8949) 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). | See the Features section for list of [Encoding Options](#encoding-options) and [Decoding Options](#decoding-options). Known limitations are noted in the [Limitations section](#limitations). Go nil values for slices, maps, pointers, etc. are encoded as CBOR null. Empty slices, maps, etc. are encoded as empty CBOR arrays and maps. Decoder checks for all required well-formedness errors, including all "subkinds" of syntax errors and too little data. 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. * 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:__
Duplicate Map Keys

This library provides options for fast detection and rejection of duplicate map keys based on applying a Go-specific data model to CBOR's extended generic data model in order to determine duplicate vs distinct map keys. Detection relies on whether the CBOR map key would be a duplicate "key" when decoded and applied to the user-provided Go map or struct. `DupMapKeyQuiet` turns off detection of duplicate map keys. It tries to use 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. APF suffix means "Allow Partial Fill" so the destination map or struct can contain some decoded values at the time of error. It is the caller's responsibility to respond to the `DupMapKeyError` by discarding the partially filled result if that's required by their protocol.

Tag Validity

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, 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. Decoder also has an option to forbid tag data items (treat any tag data item as error) which is specified by protocols such as CTAP2 Canonical CBOR. For more information, see [decoding options](#decoding-options-1) and [tag options](#tag-options).

## Limitations If any of these limitations prevent you from using this library, please open an issue along with a link to your project. * 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. * 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.
⚓ [Quick Start](#quick-start) • [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 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 ``` __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) • [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.IntDec | Description | | ------------------ | ----------- | | IntDecConvertNone (default) | When decoding to Go interface{}, CBOR positive int (major type 0) decode to uint64 value, and CBOR negative int (major type 1) decode to int64 value. | | IntDecConvertSigned | When decoding to Go interface{}, CBOR positive/negative int (major type 0 and 1) decode to int64 value. | If `IntDecConvertedSigned` is used and value overflows int64, UnmarshalTypeError is returned.
| 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.ExtraReturnErrors | Description | | ----------------- | ----------- | |ExtraDecErrorNone (default) | no extra decoding errors. E.g. ignore unknown fields if encountered. | |ExtraDecErrorUnknownField | return error if unknown field is encountered |
| 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 [(RFC 8949 Section 4.2.1)](https://datatracker.ietf.org/doc/html/rfc8949#section-4.2.1).| | 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 [(RFC 8949 Section 4.2.1)](https://datatracker.ietf.org/doc/html/rfc8949#section-4.2.1). |
| 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. | 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.
| EncOptions.BigIntConvert | Description | | ------------------------ | ----------- | | BigIntConvertShortest (default) | Encode big.Int as CBOR integer if value fits. | | BigIntConvertNone | Encode big.Int as CBOR bignum (tag 2 or 3). |
__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 8949 Preferred Encoding, etc. | | 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) • [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) • [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. Decoding 9 or 10 bytes of malformed CBOR data shouldn't exhaust memory. For example, `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}` | | Decode bad 10 bytes to interface{} | Decode bad 10 bytes to []byte | | :--- | :------------------ | :--------------- | | fxamacker/cbor
1.0-2.3 | 49.44 ns/op, 24 B/op, 2 allocs/op* | 51.93 ns/op, 32 B/op, 2 allocs/op* | | ugorji/go 1.2.6 | ⚠️ 45021 ns/op, 262852 B/op, 7 allocs/op | 💥 runtime: out of memory: cannot allocate | | ugorji/go 1.1.0-1.1.7 | 💥 runtime: out of memory: cannot allocate | 💥 runtime: out of memory: cannot allocate| *Speed and memory are for latest codec version listed in the row (compiled with Go 1.17.5). fxamacker/cbor CBOR safety settings include: MaxNestedLevels, MaxArrayElements, MaxMapPairs, and IndefLength. __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.3 | ugorji/go 1.2.6 | | :--- | :--- | :--- | | Encode CWT | 0.18 kB/op         2 allocs/op | 1.35 kB/op         4 allocs/op | | Decode CWT | 160 bytes/op     6 allocs/op | 744 bytes/op     6 allocs/op | Running your own benchmarks is highly recommended. Use your most common data structures and data sizes.
⚓ [Quick Start](#quick-start) • [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). ## Fuzzing and Code Coverage __Over 375 tests__ must pass on 4 architectures before tagging a release. They include all RFC 7049 and RFC 8949 examples, bugs found by fuzzing, maliciously crafted CBOR data, and over 87 tests with malformed data. There's some overlap in the tests but it isn't a high priority to trim tests. __Code coverage__ must not fall below 95% when tagging a release. Code coverage is above 98% (`go test -cover`) for cbor v2.3 which is among the highest for libraries (in Go) of this type. __Coverage-guided fuzzing__ must pass 1+ billion execs using a large corpus before tagging a release. Fuzzing is usually continued after the release is tagged and is manually stopped after reaching 1-3 billion execs. Fuzzing uses a customized version of [dvyukov/go-fuzz](https://github.com/dvyukov/go-fuzz). To prevent delays to release schedules, fuzzing is not restarted for a release if changes are limited to ci, docs, and comments.
⚓ [Quick Start](#quick-start) • [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. ## 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. For the full text of the Security Policy, see [SECURITY.md](SECURITY.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. Please read the license for additional disclaimers and terms. ## Special Thanks __Making this library better__ * Stefan Tatschner for using this library in [sep](https://rumpelsepp.org/projects/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. __Help clarifying CBOR RFC 7049 or 7049bis (7049bis is the draft of RFC 8949)__ * 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. __Words of encouragement and support__ * 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. ## License Copyright © 2019-2022 [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) • [Features](#features) • [Standards](#standards) • [API](#api) • [Options](#options) • [Usage](#usage) • [Fuzzing](#fuzzing-and-code-coverage) • [License](#license) golang-github-fxamacker-cbor-2.4.0/SECURITY.md000066400000000000000000000006021416612535600207260ustar00rootroot00000000000000# Security Policy Security fixes are provided for the latest released version of fxamacker/cbor. If the security vulnerability is already known to the public, then you can open an issue as a bug report. To report security vulnerabilities not yet known to the public, please email faye.github@gmail.com and allow time for the problem to be resolved before reporting it to the public. golang-github-fxamacker-cbor-2.4.0/bench_test.go000066400000000000000000001017761416612535600216200ustar00rootroot00000000000000// 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" "io/ioutil" "reflect" "testing" ) const rounds = 100 type claims struct { Iss string `cbor:"1,keyasint"` Sub string `cbor:"2,keyasint"` Aud string `cbor:"3,keyasint"` Exp int `cbor:"4,keyasint"` Nbf int `cbor:"5,keyasint"` Iat int `cbor:"6,keyasint"` Cti []byte `cbor:"7,keyasint"` } type coseHeader struct { Alg int `cbor:"1,keyasint,omitempty"` Kid []byte `cbor:"4,keyasint,omitempty"` IV []byte `cbor:"5,keyasint,omitempty"` } type macedCOSE struct { _ struct{} `cbor:",toarray"` Protected []byte Unprotected coseHeader Payload []byte Tag []byte } type coseKey struct { Kty int `cbor:"1,keyasint,omitempty"` Kid []byte `cbor:"2,keyasint,omitempty"` Alg int `cbor:"3,keyasint,omitempty"` KeyOpts int `cbor:"4,keyasint,omitempty"` IV []byte `cbor:"5,keyasint,omitempty"` CrvOrNOrK RawMessage `cbor:"-1,keyasint,omitempty"` // K for symmetric keys, Crv for elliptic curve keys, N for RSA modulus XOrE RawMessage `cbor:"-2,keyasint,omitempty"` // X for curve x-coordinate, E for RSA public exponent Y RawMessage `cbor:"-3,keyasint,omitempty"` // Y for curve y-cooridate D []byte `cbor:"-4,keyasint,omitempty"` } type attestationObject struct { AuthnData []byte `cbor:"authData"` Fmt string `cbor:"fmt"` AttStmt RawMessage `cbor:"attStmt"` } type SenMLRecord struct { BaseName string `cbor:"-2,keyasint,omitempty"` BaseTime float64 `cbor:"-3,keyasint,omitempty"` BaseUnit string `cbor:"-4,keyasint,omitempty"` BaseValue float64 `cbor:"-5,keyasint,omitempty"` BaseSum float64 `cbor:"-6,keyasint,omitempty"` BaseVersion int `cbor:"-1,keyasint,omitempty"` Name string `cbor:"0,keyasint,omitempty"` Unit string `cbor:"1,keyasint,omitempty"` Time float64 `cbor:"6,keyasint,omitempty"` UpdateTime float64 `cbor:"7,keyasint,omitempty"` Value float64 `cbor:"2,keyasint,omitempty"` ValueS string `cbor:"3,keyasint,omitempty"` ValueB bool `cbor:"4,keyasint,omitempty"` ValueD string `cbor:"8,keyasint,omitempty"` Sum float64 `cbor:"5,keyasint,omitempty"` } type T1 struct { T bool UI uint I int F float64 B []byte S string Slci []int Mss map[string]string } type T2 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"` } type T3 struct { _ struct{} `cbor:",toarray"` T bool UI uint I int F float64 B []byte S string Slci []int Mss map[string]string } var decodeBenchmarks = []struct { name string cborData []byte decodeToTypes []reflect.Type }{ {"bool", hexDecode("f5"), []reflect.Type{typeIntf, typeBool}}, // true {"positive int", hexDecode("1bffffffffffffffff"), []reflect.Type{typeIntf, typeUint64}}, // uint64(18446744073709551615) {"negative int", hexDecode("3903e7"), []reflect.Type{typeIntf, typeInt64}}, // int64(-1000) {"float", hexDecode("fbc010666666666666"), []reflect.Type{typeIntf, typeFloat64}}, // float64(-4.1) {"bytes", hexDecode("581a0102030405060708090a0b0c0d0e0f101112131415161718191a"), []reflect.Type{typeIntf, typeByteSlice}}, // []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} {"bytes indef len", hexDecode("5f410141024103410441054106410741084109410a410b410c410d410e410f4110411141124113411441154116411741184119411aff"), []reflect.Type{typeIntf, typeByteSlice}}, // []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} {"text", hexDecode("782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67"), []reflect.Type{typeIntf, typeString}}, // "The quick brown fox jumps over the lazy dog" {"text indef len", hexDecode("7f61546168616561206171617561696163616b612061626172616f6177616e61206166616f61786120616a6175616d617061736120616f61766165617261206174616861656120616c6161617a617961206164616f6167ff"), []reflect.Type{typeIntf, typeString}}, // "The quick brown fox jumps over the lazy dog" {"array", hexDecode("981a0102030405060708090a0b0c0d0e0f101112131415161718181819181a"), []reflect.Type{typeIntf, typeIntSlice}}, // []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} {"array indef len", hexDecode("9f0102030405060708090a0b0c0d0e0f101112131415161718181819181aff"), []reflect.Type{typeIntf, typeIntSlice}}, // []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", hexDecode("ad616161416162614261636143616461446165614561666146616761476168614861696149616a614a616c614c616d614d616e614e"), []reflect.Type{typeIntf, typeMapStringIntf, typeMapStringString}}, // 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"}} {"map indef len", hexDecode("bf616161416162614261636143616461446165614561666146616761476168614861696149616a614a616b614b616c614c616d614d616e614eff"), []reflect.Type{typeIntf, typeMapStringIntf, typeMapStringString}}, // 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"}} } var encodeBenchmarks = []struct { name string cborData []byte values []interface{} }{ {"bool", hexDecode("f5"), []interface{}{true}}, {"positive int", hexDecode("1bffffffffffffffff"), []interface{}{uint64(18446744073709551615)}}, {"negative int", hexDecode("3903e7"), []interface{}{int64(-1000)}}, {"float", hexDecode("fbc010666666666666"), []interface{}{float64(-4.1)}}, {"bytes", hexDecode("581a0102030405060708090a0b0c0d0e0f101112131415161718191a"), []interface{}{[]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}}}, {"text", hexDecode("782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67"), []interface{}{"The quick brown fox jumps over the lazy dog"}}, {"array", hexDecode("981a0102030405060708090a0b0c0d0e0f101112131415161718181819181a"), []interface{}{[]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", hexDecode("ad616161416162614261636143616461446165614561666146616761476168614861696149616a614a616c614c616d614d616e614e"), []interface{}{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"}}}, } func BenchmarkUnmarshal(b *testing.B) { 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() } b.Run(name, func(b *testing.B) { for i := 0; i < b.N; i++ { vPtr := reflect.New(t).Interface() if err := Unmarshal(bm.cborData, vPtr); err != nil { b.Fatal("Unmarshal:", err) } } }) } } var moreBenchmarks = []struct { name string cborData []byte decodeToType reflect.Type }{ // Unmarshal CBOR map with string key to map[string]interface{}. { "CBOR map to Go map[string]interface{}", hexDecode("a86154f56255691bffffffffffffffff61493903e76146fbc0106666666666666142581a0102030405060708090a0b0c0d0e0f101112131415161718191a6153782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f6764536c6369981a0102030405060708090a0b0c0d0e0f101112131415161718181819181a634d7373ad6163614361656145616661466167614761686148616e614e616d614d61616141616261426164614461696149616a614a616c614c"), reflect.TypeOf(map[string]interface{}{}), }, // Unmarshal CBOR map with string key to struct. { "CBOR map to Go struct", hexDecode("a86154f56255491bffffffffffffffff61493903e76146fbc0106666666666666142581a0102030405060708090a0b0c0d0e0f101112131415161718191a6153782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f6764536c6369981a0102030405060708090a0b0c0d0e0f101112131415161718181819181a634d7373ad6163614361656145616661466167614761686148616e614e616d614d61616141616261426164614461696149616a614a616c614c"), reflect.TypeOf(T1{}), }, // Unmarshal CBOR map with integer key, such as COSE Key and SenML, to map[int]interface{}. { "CBOR map to Go map[int]interface{}", hexDecode("a801f5021bffffffffffffffff033903e704fbc01066666666666605581a0102030405060708090a0b0c0d0e0f101112131415161718191a06782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f6707981a0102030405060708090a0b0c0d0e0f101112131415161718181819181a08ad61646144616661466167614761686148616d614d616e614e6161614161626142616361436165614561696149616a614a616c614c"), reflect.TypeOf(map[int]interface{}{}), }, // Unmarshal CBOR map with integer key, such as COSE Key and SenML, to struct. { "CBOR map to Go struct keyasint", hexDecode("a801f5021bffffffffffffffff033903e704fbc01066666666666605581a0102030405060708090a0b0c0d0e0f101112131415161718191a06782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f6707981a0102030405060708090a0b0c0d0e0f101112131415161718181819181a08ad61646144616661466167614761686148616d614d616e614e6161614161626142616361436165614561696149616a614a616c614c"), reflect.TypeOf(T2{}), }, // Unmarshal CBOR array of known sequence of data types, such as signed/maced/encrypted CWT, to []interface{}. { "CBOR array to Go []interface{}", hexDecode("88f51bffffffffffffffff3903e7fbc010666666666666581a0102030405060708090a0b0c0d0e0f101112131415161718191a782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67981a0102030405060708090a0b0c0d0e0f101112131415161718181819181aad616261426163614361646144616561456166614661696149616e614e616161416167614761686148616a614a616c614c616d614d"), reflect.TypeOf([]interface{}{}), }, // Unmarshal CBOR array of known sequence of data types, such as signed/maced/encrypted CWT, to struct. { "CBOR array to Go struct toarray", hexDecode("88f51bffffffffffffffff3903e7fbc010666666666666581a0102030405060708090a0b0c0d0e0f101112131415161718191a782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67981a0102030405060708090a0b0c0d0e0f101112131415161718181819181aad616261426163614361646144616561456166614661696149616e614e616161416167614761686148616a614a616c614c616d614d"), reflect.TypeOf(T3{}), }, } for _, bm := range moreBenchmarks { b.Run(bm.name, func(b *testing.B) { for i := 0; i < b.N; i++ { vPtr := reflect.New(bm.decodeToType).Interface() if err := Unmarshal(bm.cborData, vPtr); err != nil { b.Fatal("Unmarshal:", err) } } }) } } func BenchmarkDecode(b *testing.B) { 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() } buf := bytes.NewReader(bm.cborData) decoder := NewDecoder(buf) b.Run(name, func(b *testing.B) { for i := 0; i < b.N; i++ { vPtr := reflect.New(t).Interface() if err := decoder.Decode(vPtr); err != nil { b.Fatal("Decode:", err) } buf.Seek(0, 0) //nolint:errcheck } }) } } } func BenchmarkDecodeStream(b *testing.B) { var cborData []byte for _, bm := range decodeBenchmarks { for i := 0; i < len(bm.decodeToTypes); i++ { cborData = append(cborData, bm.cborData...) } } b.ResetTimer() for i := 0; i < b.N; i++ { buf := bytes.NewReader(cborData) decoder := NewDecoder(buf) for j := 0; j < rounds; j++ { for _, bm := range decodeBenchmarks { for _, t := range bm.decodeToTypes { vPtr := reflect.New(t).Interface() if err := decoder.Decode(vPtr); err != nil { b.Fatal("Decode:", err) } } } buf.Seek(0, 0) //nolint:errcheck } } } func BenchmarkMarshal(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) { for i := 0; i < b.N; i++ { if _, err := Marshal(v); err != nil { b.Fatal("Marshal:", err) } } }) } } // Marshal map[string]interface{} to CBOR map m1 := map[string]interface{}{ //nolint:dupl "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"}, } // Marshal struct to CBOR map v1 := T1{ //nolint:dupl T: true, UI: 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"}, } // Marshal map[int]interface{} to CBOR map m2 := map[int]interface{}{ //nolint:dupl 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"}, } // Marshal struct keyasint, such as COSE Key and SenML v2 := T2{ //nolint:dupl T: true, UI: 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"}, } // Marshal []interface to CBOR array. slc := []interface{}{ 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"}, } // Marshal struct toarray to CBOR array, such as signed/maced/encrypted CWT. v3 := T3{ //nolint:dupl T: true, UI: 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"}, } var moreBenchmarks = []struct { name string value interface{} }{ {"Go map[string]interface{} to CBOR map", m1}, {"Go struct to CBOR map", v1}, {"Go map[int]interface{} to CBOR map", m2}, {"Go struct keyasint to CBOR map", v2}, {"Go []interface{} to CBOR map", slc}, {"Go struct toarray to CBOR array", v3}, } for _, bm := range moreBenchmarks { b.Run(bm.name, func(b *testing.B) { for i := 0; i < b.N; i++ { if _, err := Marshal(bm.value); err != nil { b.Fatal("Marshal:", err) } } }) } } func BenchmarkMarshalCanonical(b *testing.B) { type strc struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` D string `cbor:"d"` E string `cbor:"e"` F string `cbor:"f"` G string `cbor:"g"` H string `cbor:"h"` I string `cbor:"i"` J string `cbor:"j"` L string `cbor:"l"` M string `cbor:"m"` N string `cbor:"n"` } for _, bm := range []struct { name string cborData []byte values []interface{} }{ {"map", hexDecode("ad616161416162614261636143616461446165614561666146616761476168614861696149616a614a616c614c616d614d616e614e"), []interface{}{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"}, strc{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"}}}, } { 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) { for i := 0; i < b.N; i++ { if _, err := Marshal(v); err != nil { b.Fatal("Marshal:", err) } } }) // Canonical encoding name = "Go " + reflect.TypeOf(v).String() + " to CBOR " + bm.name + " canonical" if reflect.TypeOf(v).Kind() == reflect.Struct { name = "Go " + reflect.TypeOf(v).Kind().String() + " to CBOR " + bm.name + " canonical" } em, _ := EncOptions{Sort: SortCanonical}.EncMode() b.Run(name, func(b *testing.B) { for i := 0; i < b.N; i++ { if _, err := em.Marshal(v); err != nil { b.Fatal("Marshal:", err) } } }) } } } func BenchmarkEncode(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) { encoder := NewEncoder(ioutil.Discard) b.ResetTimer() for i := 0; i < b.N; i++ { if err := encoder.Encode(v); err != nil { b.Fatal("Encode:", err) } } }) } } } func BenchmarkEncodeStream(b *testing.B) { for i := 0; i < b.N; i++ { encoder := NewEncoder(ioutil.Discard) for i := 0; i < rounds; i++ { for _, bm := range encodeBenchmarks { for _, v := range bm.values { if err := encoder.Encode(v); err != nil { b.Fatal("Encode:", err) } } } } } } func BenchmarkUnmarshalCOSE(b *testing.B) { // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.2 testCases := []struct { name string cborData []byte }{ {"128-Bit Symmetric Key", hexDecode("a42050231f4c4d4d3051fdc2ec0a3851d5b3830104024c53796d6d6574726963313238030a")}, {"256-Bit Symmetric Key", hexDecode("a4205820403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d795693880104024c53796d6d6574726963323536030a")}, {"ECDSA P256 256-Bit Key", hexDecode("a72358206c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c1922582060f7f1a780d8a783bfb7a2dd6b2796e8128dbbcef9d3d168db9529971a36e7b9215820143329cce7868e416927599cf65a34f3ce2ffda55a7eca69ed8919a394d42f0f2001010202524173796d6d657472696345434453413235360326")}, } for _, tc := range testCases { b.Run(tc.name, func(b *testing.B) { for i := 0; i < b.N; i++ { var v coseKey if err := Unmarshal(tc.cborData, &v); err != nil { b.Fatal("Unmarshal:", err) } } }) } } func BenchmarkMarshalCOSE(b *testing.B) { // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.2 testCases := []struct { name string cborData []byte }{ {"128-Bit Symmetric Key", hexDecode("a42050231f4c4d4d3051fdc2ec0a3851d5b3830104024c53796d6d6574726963313238030a")}, {"256-Bit Symmetric Key", hexDecode("a4205820403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d795693880104024c53796d6d6574726963323536030a")}, {"ECDSA P256 256-Bit Key", hexDecode("a72358206c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c1922582060f7f1a780d8a783bfb7a2dd6b2796e8128dbbcef9d3d168db9529971a36e7b9215820143329cce7868e416927599cf65a34f3ce2ffda55a7eca69ed8919a394d42f0f2001010202524173796d6d657472696345434453413235360326")}, } for _, tc := range testCases { var v coseKey if err := Unmarshal(tc.cborData, &v); err != nil { b.Fatal("Unmarshal:", err) } b.Run(tc.name, func(b *testing.B) { for i := 0; i < b.N; i++ { if _, err := Marshal(v); err != nil { b.Fatal("Marshal:", err) } } }) } } func BenchmarkUnmarshalCWTClaims(b *testing.B) { // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1 cborData := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71") for i := 0; i < b.N; i++ { var v claims if err := Unmarshal(cborData, &v); err != nil { b.Fatal("Unmarshal:", err) } } } func BenchmarkUnmarshalCWTClaimsWithDupMapKeyOpt(b *testing.B) { // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1 cborData := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71") dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() for i := 0; i < b.N; i++ { var v claims if err := dm.Unmarshal(cborData, &v); err != nil { b.Fatal("Unmarshal:", err) } } } func BenchmarkMarshalCWTClaims(b *testing.B) { // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1 cborData := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71") var v claims if err := Unmarshal(cborData, &v); err != nil { b.Fatal("Unmarshal:", err) } for i := 0; i < b.N; i++ { if _, err := Marshal(v); err != nil { b.Fatal("Unmarshal:", err) } } } func BenchmarkUnmarshalSenML(b *testing.B) { // Data from https://tools.ietf.org/html/rfc8428#section-6 cborData := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333") for i := 0; i < b.N; i++ { var v []SenMLRecord if err := Unmarshal(cborData, &v); err != nil { b.Fatal("Unmarshal:", err) } } } func BenchmarkMarshalSenML(b *testing.B) { // Data from https://tools.ietf.org/html/rfc8428#section-6 cborData := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333") var v []SenMLRecord if err := Unmarshal(cborData, &v); err != nil { b.Fatal("Unmarshal:", err) } for i := 0; i < b.N; i++ { if _, err := Marshal(v); err != nil { b.Fatal("Unmarshal:", err) } } } func BenchmarkMarshalSenMLShortestFloat16(b *testing.B) { // Data from https://tools.ietf.org/html/rfc8428#section-6 cborData := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333") var v []SenMLRecord if err := Unmarshal(cborData, &v); err != nil { b.Fatal("Unmarshal:", err) } em, _ := EncOptions{ShortestFloat: ShortestFloat16}.EncMode() for i := 0; i < b.N; i++ { if _, err := em.Marshal(v); err != nil { b.Fatal("Unmarshal:", err) } } } func BenchmarkUnmarshalWebAuthn(b *testing.B) { // Data generated from Yubico security key cborData := hexDecode("a363666d74686669646f2d7532666761747453746d74a26373696758483046022100e7ab373cfbd99fcd55fd59b0f6f17fef5b77a20ddec3db7f7e4d55174e366236022100828336b4822125fb56541fb14a8a273876acd339395ec2dad95cf41c1dd2a9ae637835638159024e3082024a30820132a0030201020204124a72fe300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a302c312a302806035504030c2159756269636f205532462045452053657269616c203234393431343937323135383059301306072a8648ce3d020106082a8648ce3d030107034200043d8b1bbd2fcbf6086e107471601468484153c1c6d3b4b68a5e855e6e40757ee22bcd8988bf3befd7cdf21cb0bf5d7a150d844afe98103c6c6607d9faae287c02a33b3039302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e313013060b2b0601040182e51c020101040403020520300d06092a864886f70d01010b05000382010100a14f1eea0076f6b8476a10a2be72e60d0271bb465b2dfbfc7c1bd12d351989917032631d795d097fa30a26a325634e85721bc2d01a86303f6bc075e5997319e122148b0496eec8d1f4f94cf4110de626c289443d1f0f5bbb239ca13e81d1d5aa9df5af8e36126475bfc23af06283157252762ff68879bcf0ef578d55d67f951b4f32b63c8aea5b0f99c67d7d814a7ff5a6f52df83e894a3a5d9c8b82e7f8bc8daf4c80175ff8972fda79333ec465d806eacc948f1bab22045a95558a48c20226dac003d41fbc9e05ea28a6bb5e10a49de060a0a4f6a2676a34d68c4abe8c61874355b9027e828ca9e064b002d62e8d8cf0744921753d35e3c87c5d5779453e7768617574684461746158c449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976341000000000000000000000000000000000000000000408903fd7dfd2c9770e98cae0123b13a2c27828a106349bc6277140e7290b7e9eb7976aa3c04ed347027caf7da3a2fa76304751c02208acfc4e7fc6c7ebbc375c8a5010203262001215820ad7f7992c335b90d882b2802061b97a4fabca7e2ee3e7a51e728b8055e4eb9c7225820e0966ba7005987fece6f0e0e13447aa98cec248e4000a594b01b74c1cb1d40b3") for i := 0; i < b.N; i++ { var v attestationObject if err := Unmarshal(cborData, &v); err != nil { b.Fatal("Unmarshal:", err) } } } func BenchmarkMarshalWebAuthn(b *testing.B) { // Data generated from Yubico security key cborData := hexDecode("a363666d74686669646f2d7532666761747453746d74a26373696758483046022100e7ab373cfbd99fcd55fd59b0f6f17fef5b77a20ddec3db7f7e4d55174e366236022100828336b4822125fb56541fb14a8a273876acd339395ec2dad95cf41c1dd2a9ae637835638159024e3082024a30820132a0030201020204124a72fe300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a302c312a302806035504030c2159756269636f205532462045452053657269616c203234393431343937323135383059301306072a8648ce3d020106082a8648ce3d030107034200043d8b1bbd2fcbf6086e107471601468484153c1c6d3b4b68a5e855e6e40757ee22bcd8988bf3befd7cdf21cb0bf5d7a150d844afe98103c6c6607d9faae287c02a33b3039302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e313013060b2b0601040182e51c020101040403020520300d06092a864886f70d01010b05000382010100a14f1eea0076f6b8476a10a2be72e60d0271bb465b2dfbfc7c1bd12d351989917032631d795d097fa30a26a325634e85721bc2d01a86303f6bc075e5997319e122148b0496eec8d1f4f94cf4110de626c289443d1f0f5bbb239ca13e81d1d5aa9df5af8e36126475bfc23af06283157252762ff68879bcf0ef578d55d67f951b4f32b63c8aea5b0f99c67d7d814a7ff5a6f52df83e894a3a5d9c8b82e7f8bc8daf4c80175ff8972fda79333ec465d806eacc948f1bab22045a95558a48c20226dac003d41fbc9e05ea28a6bb5e10a49de060a0a4f6a2676a34d68c4abe8c61874355b9027e828ca9e064b002d62e8d8cf0744921753d35e3c87c5d5779453e7768617574684461746158c449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976341000000000000000000000000000000000000000000408903fd7dfd2c9770e98cae0123b13a2c27828a106349bc6277140e7290b7e9eb7976aa3c04ed347027caf7da3a2fa76304751c02208acfc4e7fc6c7ebbc375c8a5010203262001215820ad7f7992c335b90d882b2802061b97a4fabca7e2ee3e7a51e728b8055e4eb9c7225820e0966ba7005987fece6f0e0e13447aa98cec248e4000a594b01b74c1cb1d40b3") var v attestationObject if err := Unmarshal(cborData, &v); err != nil { b.Fatal("Unmarshal:", err) } for i := 0; i < b.N; i++ { if _, err := Marshal(v); err != nil { b.Fatal("Marshal:", err) } } } func BenchmarkUnmarshalCOSEMAC(b *testing.B) { // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4 cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200") for i := 0; i < b.N; i++ { var v macedCOSE if err := Unmarshal(cborData, &v); err != nil { b.Fatal("Unmarshal:", err) } } } func BenchmarkUnmarshalCOSEMACWithTag(b *testing.B) { // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4 cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200") // Register tag CBOR Web Token (CWT) 61 and COSE_Mac0 17 with macedCOSE type tags := NewTagSet() if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, reflect.TypeOf(macedCOSE{}), 61, 17); err != nil { b.Fatal("TagSet.Add:", err) } // Create DecMode with tags dm, _ := DecOptions{}.DecModeWithTags(tags) for i := 0; i < b.N; i++ { var v macedCOSE if err := dm.Unmarshal(cborData, &v); err != nil { b.Fatal("Unmarshal:", err) } } } func BenchmarkMarshalCOSEMAC(b *testing.B) { // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4 cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200") var v macedCOSE if err := Unmarshal(cborData, &v); err != nil { b.Fatal("Unmarshal():", err) } for i := 0; i < b.N; i++ { if _, err := Marshal(v); err != nil { b.Fatal("Marshal():", v, err) } } } func BenchmarkMarshalCOSEMACWithTag(b *testing.B) { // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4 cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200") // Register tag CBOR Web Token (CWT) 61 and COSE_Mac0 17 with macedCOSE type tags := NewTagSet() if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, reflect.TypeOf(macedCOSE{}), 61, 17); err != nil { b.Fatal("TagSet.Add:", err) } // Create EncMode with tags. dm, _ := DecOptions{}.DecModeWithTags(tags) em, _ := EncOptions{}.EncModeWithTags(tags) var v macedCOSE if err := dm.Unmarshal(cborData, &v); err != nil { b.Fatal("Unmarshal():", err) } for i := 0; i < b.N; i++ { if _, err := em.Marshal(v); err != nil { b.Fatal("Marshal():", v, err) } } } golang-github-fxamacker-cbor-2.4.0/cache.go000066400000000000000000000170571416612535600205430ustar00rootroot00000000000000// 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" "errors" "reflect" "sort" "strconv" "strings" "sync" ) type encodeFuncs struct { ef encodeFunc ief isEmptyFunc } var ( decodingStructTypeCache sync.Map // map[reflect.Type]*decodingStructType encodingStructTypeCache sync.Map // map[reflect.Type]*encodingStructType encodeFuncCache sync.Map // map[reflect.Type]encodeFuncs typeInfoCache sync.Map // map[reflect.Type]*typeInfo ) type specialType int const ( specialTypeNone specialType = iota specialTypeUnmarshalerIface specialTypeEmptyIface specialTypeIface specialTypeTag specialTypeTime ) type typeInfo struct { elemTypeInfo *typeInfo keyTypeInfo *typeInfo typ reflect.Type kind reflect.Kind nonPtrType reflect.Type nonPtrKind reflect.Kind spclType specialType } func newTypeInfo(t reflect.Type) *typeInfo { tInfo := typeInfo{typ: t, kind: t.Kind()} for t.Kind() == reflect.Ptr { t = t.Elem() } k := t.Kind() tInfo.nonPtrType = t tInfo.nonPtrKind = k if k == reflect.Interface { if t.NumMethod() == 0 { tInfo.spclType = specialTypeEmptyIface } else { tInfo.spclType = specialTypeIface } } else if t == typeTag { tInfo.spclType = specialTypeTag } else if t == typeTime { tInfo.spclType = specialTypeTime } else if reflect.PtrTo(t).Implements(typeUnmarshaler) { tInfo.spclType = specialTypeUnmarshalerIface } switch k { case reflect.Array, reflect.Slice: tInfo.elemTypeInfo = getTypeInfo(t.Elem()) case reflect.Map: tInfo.keyTypeInfo = getTypeInfo(t.Key()) tInfo.elemTypeInfo = getTypeInfo(t.Elem()) } return &tInfo } type decodingStructType struct { fields fields err error toArray bool } func getDecodingStructType(t reflect.Type) *decodingStructType { if v, _ := decodingStructTypeCache.Load(t); v != nil { return v.(*decodingStructType) } flds, structOptions := getFields(t) toArray := hasToArrayOption(structOptions) var err error for i := 0; i < len(flds); i++ { if flds[i].keyAsInt { nameAsInt, numErr := strconv.Atoi(flds[i].name) if numErr != nil { err = errors.New("cbor: failed to parse field name \"" + flds[i].name + "\" to int (" + numErr.Error() + ")") break } flds[i].nameAsInt = int64(nameAsInt) } flds[i].typInfo = getTypeInfo(flds[i].typ) } structType := &decodingStructType{fields: flds, err: err, toArray: toArray} decodingStructTypeCache.Store(t, structType) return structType } type encodingStructType struct { fields fields bytewiseFields fields lengthFirstFields fields omitEmptyFieldsIdx []int err error toArray bool fixedLength bool // Struct type doesn't have any omitempty or anonymous fields. } func (st *encodingStructType) getFields(em *encMode) fields { if em.sort == SortNone { return st.fields } if em.sort == SortLengthFirst { return st.lengthFirstFields } return st.bytewiseFields } type bytewiseFieldSorter struct { fields fields } func (x *bytewiseFieldSorter) Len() int { return len(x.fields) } func (x *bytewiseFieldSorter) Swap(i, j int) { x.fields[i], x.fields[j] = x.fields[j], x.fields[i] } func (x *bytewiseFieldSorter) Less(i, j int) bool { return bytes.Compare(x.fields[i].cborName, x.fields[j].cborName) <= 0 } type lengthFirstFieldSorter struct { fields fields } func (x *lengthFirstFieldSorter) Len() int { return len(x.fields) } func (x *lengthFirstFieldSorter) Swap(i, j int) { x.fields[i], x.fields[j] = x.fields[j], x.fields[i] } func (x *lengthFirstFieldSorter) Less(i, j int) bool { if len(x.fields[i].cborName) != len(x.fields[j].cborName) { return len(x.fields[i].cborName) < len(x.fields[j].cborName) } return bytes.Compare(x.fields[i].cborName, x.fields[j].cborName) <= 0 } func getEncodingStructType(t reflect.Type) (*encodingStructType, error) { if v, _ := encodingStructTypeCache.Load(t); v != nil { structType := v.(*encodingStructType) return structType, structType.err } flds, structOptions := getFields(t) if hasToArrayOption(structOptions) { return getEncodingStructToArrayType(t, flds) } var err error var hasKeyAsInt bool var hasKeyAsStr bool var omitEmptyIdx []int fixedLength := true e := getEncoderBuffer() for i := 0; i < len(flds); i++ { // Get field's encodeFunc flds[i].ef, flds[i].ief = getEncodeFunc(flds[i].typ) if flds[i].ef == nil { err = &UnsupportedTypeError{t} break } // Encode field name if flds[i].keyAsInt { nameAsInt, numErr := strconv.Atoi(flds[i].name) if numErr != nil { err = errors.New("cbor: failed to parse field name \"" + flds[i].name + "\" to int (" + numErr.Error() + ")") break } flds[i].nameAsInt = int64(nameAsInt) if nameAsInt >= 0 { encodeHead(e, byte(cborTypePositiveInt), uint64(nameAsInt)) } else { n := nameAsInt*(-1) - 1 encodeHead(e, byte(cborTypeNegativeInt), uint64(n)) } flds[i].cborName = make([]byte, e.Len()) copy(flds[i].cborName, e.Bytes()) e.Reset() hasKeyAsInt = true } else { encodeHead(e, byte(cborTypeTextString), uint64(len(flds[i].name))) flds[i].cborName = make([]byte, e.Len()+len(flds[i].name)) n := copy(flds[i].cborName, e.Bytes()) copy(flds[i].cborName[n:], flds[i].name) e.Reset() hasKeyAsStr = true } // Check if field is from embedded struct if len(flds[i].idx) > 1 { fixedLength = false } // Check if field can be omitted when empty if flds[i].omitEmpty { fixedLength = false omitEmptyIdx = append(omitEmptyIdx, i) } } putEncoderBuffer(e) if err != nil { structType := &encodingStructType{err: err} encodingStructTypeCache.Store(t, structType) return structType, structType.err } // Sort fields by canonical order bytewiseFields := make(fields, len(flds)) copy(bytewiseFields, flds) sort.Sort(&bytewiseFieldSorter{bytewiseFields}) lengthFirstFields := bytewiseFields if hasKeyAsInt && hasKeyAsStr { lengthFirstFields = make(fields, len(flds)) copy(lengthFirstFields, flds) sort.Sort(&lengthFirstFieldSorter{lengthFirstFields}) } structType := &encodingStructType{ fields: flds, bytewiseFields: bytewiseFields, lengthFirstFields: lengthFirstFields, omitEmptyFieldsIdx: omitEmptyIdx, fixedLength: fixedLength, } encodingStructTypeCache.Store(t, structType) return structType, structType.err } func getEncodingStructToArrayType(t reflect.Type, flds fields) (*encodingStructType, error) { for i := 0; i < len(flds); i++ { // Get field's encodeFunc flds[i].ef, flds[i].ief = getEncodeFunc(flds[i].typ) if flds[i].ef == nil { structType := &encodingStructType{err: &UnsupportedTypeError{t}} encodingStructTypeCache.Store(t, structType) return structType, structType.err } } structType := &encodingStructType{ fields: flds, toArray: true, fixedLength: true, } encodingStructTypeCache.Store(t, structType) return structType, structType.err } func getEncodeFunc(t reflect.Type) (encodeFunc, isEmptyFunc) { if v, _ := encodeFuncCache.Load(t); v != nil { fs := v.(encodeFuncs) return fs.ef, fs.ief } ef, ief := getEncodeFuncInternal(t) encodeFuncCache.Store(t, encodeFuncs{ef, ief}) return ef, ief } func getTypeInfo(t reflect.Type) *typeInfo { if v, _ := typeInfoCache.Load(t); v != nil { return v.(*typeInfo) } tInfo := newTypeInfo(t) typeInfoCache.Store(t, tInfo) return tInfo } func hasToArrayOption(tag string) bool { s := ",toarray" idx := strings.Index(tag, s) return idx >= 0 && (len(tag) == idx+len(s) || tag[idx+len(s)] == ',') } golang-github-fxamacker-cbor-2.4.0/decode.go000066400000000000000000001506501416612535600207200ustar00rootroot00000000000000// Copyright (c) Faye Amacker. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. package cbor import ( "encoding" "encoding/binary" "errors" "fmt" "io" "math" "math/big" "reflect" "strconv" "strings" "time" "unicode/utf8" "github.com/x448/float16" ) // Unmarshal parses the CBOR-encoded data into the value pointed to by v // using default decoding options. If v is nil, not a pointer, or // a nil pointer, Unmarshal returns an error. // // To unmarshal CBOR into a value implementing the Unmarshaler interface, // Unmarshal calls that value's UnmarshalCBOR method with a valid // CBOR value. // // To unmarshal CBOR byte string into a value implementing the // encoding.BinaryUnmarshaler interface, Unmarshal calls that value's // UnmarshalBinary method with decoded CBOR byte string. // // To unmarshal CBOR into a pointer, Unmarshal sets the pointer to nil // if CBOR data is null (0xf6) or undefined (0xf7). Otherwise, Unmarshal // unmarshals CBOR into the value pointed to by the pointer. If the // pointer is nil, Unmarshal creates a new value for it to point to. // // 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. // // 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. // Otherwise Unmarshal overwrites existing elements, and sets slice length // to CBOR array length. // // To unmarshal a CBOR array into a Go array, Unmarshal decodes CBOR array // elements into Go array elements. If the Go array is smaller than the // CBOR array, the extra CBOR array elements are discarded. If the CBOR // array is smaller than the Go array, the extra Go array elements are // set to zero values. // // To unmarshal a CBOR array into a struct, struct must have a special field "_" // with struct tag `cbor:",toarray"`. Go array elements are decoded into struct // fields. Any "omitempty" struct field tag option is ignored in this case. // // To unmarshal a CBOR map into a map, Unmarshal allocates a new map only if the // map is nil. Otherwise Unmarshal reuses the existing map and keeps existing // entries. Unmarshal stores key-value pairs from the CBOR map into Go map. // See DecOptions.DupMapKey to enable duplicate map key detection. // // 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. // // Unmarshal tries an exact match for field name, then a case-insensitive match. // Map key-value pairs without corresponding struct fields are ignored. See // DecOptions.ExtraReturnErrors to return error at unknown field. // // To unmarshal a CBOR text string into a time.Time value, Unmarshal parses text // string formatted in RFC3339. To unmarshal a CBOR integer/float into a // time.Time value, Unmarshal creates an unix time with integer/float as seconds // and fractional seconds since January 1, 1970 UTC. // // To unmarshal CBOR null (0xf6) and undefined (0xf7) values into a // slice/map/pointer, Unmarshal sets Go value to nil. Because null is often // used to mean "not present", unmarshalling CBOR null and undefined value // into any other Go type has no effect and returns no error. // // Unmarshal supports CBOR tag 55799 (self-describe CBOR), tag 0 and 1 (time), // and tag 2 and 3 (bignum). func Unmarshal(data []byte, v interface{}) error { return defaultDecMode.Unmarshal(data, v) } // Valid checks whether the CBOR data is complete and well-formed. func Valid(data []byte) error { return defaultDecMode.Valid(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. type Unmarshaler interface { UnmarshalCBOR([]byte) error } // InvalidUnmarshalError describes an invalid argument passed to Unmarshal. type InvalidUnmarshalError struct { s string } func (e *InvalidUnmarshalError) Error() string { return e.s } // UnmarshalTypeError describes a CBOR value that can't be decoded to a Go type. type UnmarshalTypeError struct { CBORType string // type of CBOR value GoType string // type of Go value it could not be decoded into StructFieldName string // name of the struct field holding the Go value (optional) errorMsg string // additional error message (optional) } func (e *UnmarshalTypeError) Error() string { var s string if e.StructFieldName != "" { s = "cbor: cannot unmarshal " + e.CBORType + " into Go struct field " + e.StructFieldName + " of type " + e.GoType } else { s = "cbor: cannot unmarshal " + e.CBORType + " into Go value of type " + e.GoType } if e.errorMsg != "" { s += " (" + e.errorMsg + ")" } return s } // DupMapKeyError describes detected duplicate map key in CBOR map. type DupMapKeyError struct { Key interface{} Index int } func (e *DupMapKeyError) Error() string { return fmt.Sprintf("cbor: found duplicate map key \"%v\" at map element index %d", e.Key, e.Index) } // UnknownFieldError describes detected unknown field in CBOR map when decoding to Go struct. type UnknownFieldError struct { Index int } func (e *UnknownFieldError) Error() string { return fmt.Sprintf("cbor: found unknown field at map element index %d", e.Index) } // DupMapKeyMode specifies how to enforce duplicate map key. type DupMapKeyMode int const ( // DupMapKeyQuiet doesn't enforce duplicate map key. Decoder quietly (no error) // uses faster of "keep first" or "keep last" depending on Go data type and other factors. DupMapKeyQuiet DupMapKeyMode = iota // DupMapKeyEnforcedAPF enforces detection and rejection of duplicate map keys. // APF means "Allow Partial Fill" and the destination map or struct can be partially filled. // If a duplicate map key is detected, DupMapKeyError is returned without further decoding // of the map. It's the caller's responsibility to respond to DupMapKeyError by // discarding the partially filled result if their protocol requires it. // WARNING: using DupMapKeyEnforcedAPF will decrease performance and increase memory use. DupMapKeyEnforcedAPF maxDupMapKeyMode ) func (dmkm DupMapKeyMode) valid() bool { return dmkm < maxDupMapKeyMode } // IndefLengthMode specifies whether to allow indefinite length items. type IndefLengthMode int const ( // IndefLengthAllowed allows indefinite length items. IndefLengthAllowed IndefLengthMode = iota // IndefLengthForbidden disallows indefinite length items. IndefLengthForbidden maxIndefLengthMode ) func (m IndefLengthMode) valid() bool { return m < maxIndefLengthMode } // TagsMode specifies whether to allow CBOR tags. type TagsMode int const ( // TagsAllowed allows CBOR tags. TagsAllowed TagsMode = iota // TagsForbidden disallows CBOR tags. TagsForbidden maxTagsMode ) func (tm TagsMode) valid() bool { return tm < maxTagsMode } // IntDecMode specifies which Go int type (int64 or uint64) should // be used when decoding CBOR int (major type 0 and 1) to Go interface{}. type IntDecMode int const ( // IntDecConvertNone affects how CBOR int (major type 0 and 1) decodes to Go interface{}. // It makes CBOR positive int (major type 0) decode to uint64 value, and // CBOR negative int (major type 1) decode to int64 value. IntDecConvertNone IntDecMode = iota // IntDecConvertSigned affects how CBOR int (major type 0 and 1) decodes to Go interface{}. // It makes CBOR positive/negative int (major type 0 and 1) decode to int64 value. // If value overflows int64, UnmarshalTypeError is returned. IntDecConvertSigned maxIntDec ) func (idm IntDecMode) valid() bool { return idm < maxIntDec } // ExtraDecErrorCond specifies extra conditions that should be treated as errors. type ExtraDecErrorCond uint // ExtraDecErrorNone indicates no extra error condition. const ExtraDecErrorNone ExtraDecErrorCond = 0 const ( // ExtraDecErrorUnknownField indicates error condition when destination // Go struct doesn't have a field matching a CBOR map key. ExtraDecErrorUnknownField ExtraDecErrorCond = 1 << iota maxExtraDecError ) func (ec ExtraDecErrorCond) valid() bool { return ec < maxExtraDecError } // DecOptions specifies decoding options. type DecOptions struct { // DupMapKey specifies whether to enforce duplicate map key. DupMapKey DupMapKeyMode // TimeTag specifies whether to check validity of time.Time (e.g. valid tag number and tag content type). // For now, valid tag number means 0 or 1 as specified in RFC 7049 if the Go type is time.Time. 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]. 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 // IndefLength specifies whether to allow indefinite length CBOR items. IndefLength IndefLengthMode // TagsMd specifies whether to allow CBOR tags (major type 6). TagsMd TagsMode // IntDec specifies which Go integer type (int64 or uint64) to use // when decoding CBOR int (major type 0 and 1) to Go interface{}. IntDec IntDecMode // 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 } // DecMode returns DecMode with immutable options and no tags (safe for concurrency). func (opts DecOptions) DecMode() (DecMode, error) { return opts.decMode() } // DecModeWithTags returns DecMode with options and tags that are both immutable (safe for concurrency). func (opts DecOptions) DecModeWithTags(tags TagSet) (DecMode, error) { if opts.TagsMd == TagsForbidden { return nil, errors.New("cbor: cannot create DecMode with TagSet when TagsMd is TagsForbidden") } if tags == nil { return nil, errors.New("cbor: cannot create DecMode with nil value as TagSet") } dm, err := opts.decMode() if err != nil { return nil, err } // Copy tags ts := tagSet(make(map[reflect.Type]*tagItem)) syncTags := tags.(*syncTagSet) syncTags.RLock() for contentType, tag := range syncTags.t { if tag.opts.DecTag != DecTagIgnored { ts[contentType] = tag } } syncTags.RUnlock() if len(ts) > 0 { dm.tags = ts } return dm, nil } // DecModeWithSharedTags returns DecMode with immutable options and mutable shared tags (safe for concurrency). func (opts DecOptions) DecModeWithSharedTags(tags TagSet) (DecMode, error) { if opts.TagsMd == TagsForbidden { return nil, errors.New("cbor: cannot create DecMode with TagSet when TagsMd is TagsForbidden") } if tags == nil { return nil, errors.New("cbor: cannot create DecMode with nil value as TagSet") } dm, err := opts.decMode() if err != nil { return nil, err } dm.tags = tags return dm, nil } const ( defaultMaxArrayElements = 131072 minMaxArrayElements = 16 maxMaxArrayElements = 2147483647 defaultMaxMapPairs = 131072 minMaxMapPairs = 16 maxMaxMapPairs = 2147483647 ) func (opts DecOptions) decMode() (*decMode, error) { if !opts.DupMapKey.valid() { return nil, errors.New("cbor: invalid DupMapKey " + strconv.Itoa(int(opts.DupMapKey))) } if !opts.TimeTag.valid() { return nil, errors.New("cbor: invalid TimeTag " + strconv.Itoa(int(opts.TimeTag))) } if !opts.IndefLength.valid() { return nil, errors.New("cbor: invalid IndefLength " + strconv.Itoa(int(opts.IndefLength))) } if !opts.TagsMd.valid() { return nil, errors.New("cbor: invalid TagsMd " + strconv.Itoa(int(opts.TagsMd))) } if !opts.IntDec.valid() { return nil, errors.New("cbor: invalid IntDec " + strconv.Itoa(int(opts.IntDec))) } 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])") } if opts.MaxArrayElements == 0 { opts.MaxArrayElements = defaultMaxArrayElements } else if opts.MaxArrayElements < minMaxArrayElements || opts.MaxArrayElements > maxMaxArrayElements { return nil, errors.New("cbor: invalid MaxArrayElements " + strconv.Itoa(opts.MaxArrayElements) + " (range is [" + strconv.Itoa(minMaxArrayElements) + ", " + strconv.Itoa(maxMaxArrayElements) + "])") } if opts.MaxMapPairs == 0 { opts.MaxMapPairs = defaultMaxMapPairs } else if opts.MaxMapPairs < minMaxMapPairs || opts.MaxMapPairs > maxMaxMapPairs { return nil, errors.New("cbor: invalid MaxMapPairs " + strconv.Itoa(opts.MaxMapPairs) + " (range is [" + strconv.Itoa(minMaxMapPairs) + ", " + strconv.Itoa(maxMaxMapPairs) + "])") } 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) } dm := decMode{ dupMapKey: opts.DupMapKey, timeTag: opts.TimeTag, maxNestedLevels: opts.MaxNestedLevels, maxArrayElements: opts.MaxArrayElements, maxMapPairs: opts.MaxMapPairs, indefLength: opts.IndefLength, tagsMd: opts.TagsMd, intDec: opts.IntDec, extraReturnErrors: opts.ExtraReturnErrors, defaultMapType: opts.DefaultMapType, } return &dm, nil } // DecMode is the main interface for CBOR decoding. type DecMode interface { // Unmarshal parses the CBOR-encoded data into the value pointed to by v // using the decoding mode. If v is nil, not a pointer, or a nil pointer, // Unmarshal returns an error. // // See the documentation for Unmarshal for details. Unmarshal(data []byte, v interface{}) error // Valid checks whether the CBOR data is complete and well-formed. Valid(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 } type decMode struct { tags tagProvider dupMapKey DupMapKeyMode timeTag DecTagMode maxNestedLevels int maxArrayElements int maxMapPairs int indefLength IndefLengthMode tagsMd TagsMode intDec IntDecMode extraReturnErrors ExtraDecErrorCond defaultMapType reflect.Type } var defaultDecMode, _ = DecOptions{}.decMode() // DecOptions returns user specified options used to create this DecMode. func (dm *decMode) DecOptions() DecOptions { return DecOptions{ DupMapKey: dm.dupMapKey, TimeTag: dm.timeTag, MaxNestedLevels: dm.maxNestedLevels, MaxArrayElements: dm.maxArrayElements, MaxMapPairs: dm.maxMapPairs, IndefLength: dm.indefLength, TagsMd: dm.tagsMd, IntDec: dm.intDec, ExtraReturnErrors: dm.extraReturnErrors, } } // Unmarshal parses the CBOR-encoded data into the value pointed to by v // using dm decoding mode. If v is nil, not a pointer, or a nil pointer, // Unmarshal returns an error. // // See the documentation for Unmarshal for details. func (dm *decMode) Unmarshal(data []byte, v interface{}) error { d := decoder{data: data, dm: dm} return d.value(v) } // Valid checks whether the CBOR data is complete and well-formed. func (dm *decMode) Valid(data []byte) error { d := decoder{data: data, dm: dm} return d.valid() } // NewDecoder returns a new decoder that reads from r using dm DecMode. func (dm *decMode) NewDecoder(r io.Reader) *Decoder { return &Decoder{r: r, d: decoder{dm: dm}} } type decoder struct { data []byte off int // next read offset in data dm *decMode } func (d *decoder) value(v interface{}) error { // v can't be nil, non-pointer, or nil pointer value. if v == nil { return &InvalidUnmarshalError{"cbor: Unmarshal(nil)"} } rv := reflect.ValueOf(v) if rv.Kind() != reflect.Ptr { return &InvalidUnmarshalError{"cbor: Unmarshal(non-pointer " + rv.Type().String() + ")"} } 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())) } type cborType uint8 const ( cborTypePositiveInt cborType = 0x00 cborTypeNegativeInt cborType = 0x20 cborTypeByteString cborType = 0x40 cborTypeTextString cborType = 0x60 cborTypeArray cborType = 0x80 cborTypeMap cborType = 0xa0 cborTypeTag cborType = 0xc0 cborTypePrimitives cborType = 0xe0 ) func (t cborType) String() string { switch t { case cborTypePositiveInt: return "positive integer" case cborTypeNegativeInt: return "negative integer" case cborTypeByteString: return "byte string" case cborTypeTextString: return "UTF-8 text string" case cborTypeArray: return "array" case cborTypeMap: return "map" case cborTypeTag: return "tag" case cborTypePrimitives: return "primitives" default: return "Invalid type " + strconv.Itoa(int(t)) } } const ( selfDescribedCBORTagNum = 55799 ) // parseToValue decodes CBOR data to value. It assumes data is well-formed, // and does not perform bounds checking. func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo 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. if !d.nextCBORNil() { for v.Kind() == reflect.Ptr { if v.IsNil() { if !v.CanSet() { d.skip() return errors.New("cbor: cannot set new value for " + v.Type().String()) } v.Set(reflect.New(v.Type().Elem())) } v = v.Elem() } } // Strip self-described CBOR tag number. for d.nextCBORType() == cborTypeTag { off := d.off _, _, tagNum := d.getHead() if tagNum != selfDescribedCBORTagNum { d.off = off break } } // Check validity of supported built-in tags. if d.nextCBORType() == cborTypeTag { off := d.off _, _, tagNum := d.getHead() if err := validBuiltinTag(tagNum, d.data[d.off]); err != nil { d.skip() return err } d.off = off } if tInfo.spclType != specialTypeNone { switch tInfo.spclType { case specialTypeEmptyIface: iv, err := d.parse(false) // Skipped self-described CBOR tag number already. if iv != nil { v.Set(reflect.ValueOf(iv)) } return err case specialTypeTag: return d.parseToTag(v) case specialTypeTime: if d.nextCBORNil() { // Decoding CBOR null and undefined to time.Time is no-op. d.skip() return nil } tm, err := d.parseToTime() if err != nil { return err } v.Set(reflect.ValueOf(tm)) return nil case specialTypeUnmarshalerIface: return d.parseToUnmarshaler(v) } } // Check registered tag number if tagItem := d.getRegisteredTagItem(tInfo.nonPtrType); tagItem != nil { t := d.nextCBORType() if t != cborTypeTag { if tagItem.opts.DecTag == DecTagRequired { d.skip() // Required tag number is absent, skip entire tag return &UnmarshalTypeError{ CBORType: t.String(), GoType: tInfo.typ.String(), errorMsg: "expect CBOR tag value"} } } else if err := d.validRegisteredTagNums(tagItem); err != nil { d.skip() // Skip tag content return err } } t := d.nextCBORType() switch t { case cborTypePositiveInt: _, _, val := d.getHead() return fillPositiveInt(t, val, v) case cborTypeNegativeInt: _, _, val := 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) if tInfo.nonPtrType == typeBigInt { v.Set(reflect.ValueOf(*bi)) return nil } return &UnmarshalTypeError{ CBORType: t.String(), GoType: tInfo.nonPtrType.String(), errorMsg: bi.String() + " overflows Go's int64", } } nValue := int64(-1) ^ int64(val) return fillNegativeInt(t, nValue, v) case cborTypeByteString: b := d.parseByteString() return fillByteString(t, b, v) case cborTypeTextString: b, err := d.parseTextString() if err != nil { return err } 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) case 26: f := float64(math.Float32frombits(uint32(val))) return fillFloat(t, f, v) case 27: f := math.Float64frombits(val) return fillFloat(t, f, v) } case cborTypeTag: _, _, tagNum := d.getHead() switch tagNum { case 2: // Bignum (tag 2) can be decoded to uint, int, float, slice, array, or big.Int. b := d.parseByteString() bi := new(big.Int).SetBytes(b) if tInfo.nonPtrType == typeBigInt { v.Set(reflect.ValueOf(*bi)) return nil } if tInfo.nonPtrKind == reflect.Slice || tInfo.nonPtrKind == reflect.Array { return fillByteString(t, b, v) } if bi.IsUint64() { return fillPositiveInt(t, bi.Uint64(), v) } return &UnmarshalTypeError{ CBORType: t.String(), GoType: tInfo.nonPtrType.String(), errorMsg: bi.String() + " overflows " + v.Type().String(), } case 3: // Bignum (tag 3) can be decoded to int, float, slice, array, or big.Int. b := d.parseByteString() bi := new(big.Int).SetBytes(b) bi.Add(bi, big.NewInt(1)) bi.Neg(bi) if tInfo.nonPtrType == typeBigInt { v.Set(reflect.ValueOf(*bi)) return nil } if tInfo.nonPtrKind == reflect.Slice || tInfo.nonPtrKind == reflect.Array { return fillByteString(t, b, v) } if bi.IsInt64() { return fillNegativeInt(t, bi.Int64(), v) } return &UnmarshalTypeError{ CBORType: t.String(), GoType: tInfo.nonPtrType.String(), errorMsg: bi.String() + " overflows " + v.Type().String(), } } return d.parseToValue(v, tInfo) case cborTypeArray: if tInfo.nonPtrKind == reflect.Slice { return d.parseArrayToSlice(v, tInfo) } else if tInfo.nonPtrKind == reflect.Array { return d.parseArrayToArray(v, tInfo) } else if tInfo.nonPtrKind == reflect.Struct { return d.parseArrayToStruct(v, tInfo) } d.skip() return &UnmarshalTypeError{CBORType: t.String(), GoType: tInfo.nonPtrType.String()} case cborTypeMap: if tInfo.nonPtrKind == reflect.Struct { return d.parseMapToStruct(v, tInfo) } else if tInfo.nonPtrKind == reflect.Map { return d.parseMapToMap(v, tInfo) } d.skip() return &UnmarshalTypeError{CBORType: t.String(), GoType: tInfo.nonPtrType.String()} } return nil } func (d *decoder) parseToTag(v reflect.Value) error { if d.nextCBORNil() { // Decoding CBOR null and undefined to cbor.Tag is no-op. d.skip() return nil } t := d.nextCBORType() if t != cborTypeTag { d.skip() return &UnmarshalTypeError{CBORType: t.String(), GoType: typeTag.String()} } // Unmarshal tag number _, _, num := d.getHead() // Unmarshal tag content content, err := d.parse(false) if err != nil { return err } v.Set(reflect.ValueOf(Tag{num, content})) return nil } func (d *decoder) parseToTime() (tm time.Time, err error) { t := d.nextCBORType() // Verify that tag number or absence of tag number is acceptable to specified timeTag. if t == cborTypeTag { if d.dm.timeTag == DecTagIgnored { // Skip tag number for t == cborTypeTag { d.getHead() t = d.nextCBORType() } } else { // Read tag number _, _, tagNum := d.getHead() if tagNum != 0 && tagNum != 1 { d.skip() err = errors.New("cbor: wrong tag number for time.Time, got " + strconv.Itoa(int(tagNum)) + ", expect 0 or 1") return } } } else { if d.dm.timeTag == DecTagRequired { d.skip() err = &UnmarshalTypeError{CBORType: t.String(), GoType: typeTime.String(), errorMsg: "expect CBOR tag value"} return } } var content interface{} content, err = d.parse(false) if err != nil { return } switch c := content.(type) { case nil: return case uint64: return time.Unix(int64(c), 0), nil case int64: return time.Unix(c, 0), nil case float64: if math.IsNaN(c) || math.IsInf(c, 0) { return } f1, f2 := math.Modf(c) return time.Unix(int64(f1), int64(f2*1e9)), nil case string: tm, err = time.Parse(time.RFC3339, c) if err != nil { tm = time.Time{} err = errors.New("cbor: cannot set " + c + " for time.Time: " + err.Error()) return } return default: err = &UnmarshalTypeError{CBORType: t.String(), GoType: typeTime.String()} return } } // parseToUnmarshaler parses CBOR data to value implementing Unmarshaler interface. // It assumes data is well-formed, and does not perform bounds checking. func (d *decoder) parseToUnmarshaler(v reflect.Value) error { if d.nextCBORNil() && v.Kind() == reflect.Ptr && v.IsNil() { d.skip() return nil } if v.Kind() != reflect.Ptr && v.CanAddr() { v = v.Addr() } if u, ok := v.Interface().(Unmarshaler); ok { start := d.off d.skip() return u.UnmarshalCBOR(d.data[start:d.off]) } d.skip() return errors.New("cbor: failed to assert " + v.Type().String() + " as cbor.Unmarshaler") } // parse parses CBOR data and returns value in default Go type. // It assumes data is well-formed, and does not perform bounds checking. func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //nolint:gocyclo // Strip self-described CBOR tag number. if skipSelfDescribedTag { for d.nextCBORType() == cborTypeTag { off := d.off _, _, tagNum := d.getHead() if tagNum != selfDescribedCBORTagNum { d.off = off break } } } // Check validity of supported built-in tags. if d.nextCBORType() == cborTypeTag { off := d.off _, _, tagNum := d.getHead() if err := validBuiltinTag(tagNum, d.data[d.off]); err != nil { d.skip() return nil, err } d.off = off } t := d.nextCBORType() switch t { case cborTypePositiveInt: _, _, val := d.getHead() if d.dm.intDec == IntDecConvertNone { return val, nil } if val > math.MaxInt64 { return nil, &UnmarshalTypeError{ CBORType: t.String(), GoType: reflect.TypeOf(int64(0)).String(), errorMsg: strconv.FormatUint(val, 10) + " overflows Go's int64", } } return int64(val), nil case cborTypeNegativeInt: _, _, val := d.getHead() if val > math.MaxInt64 { // CBOR negative integer value overflows Go int64, use big.Int instead. bi := new(big.Int).SetUint64(val) bi.Add(bi, big.NewInt(1)) bi.Neg(bi) return *bi, nil } nValue := int64(-1) ^ int64(val) return nValue, nil case cborTypeByteString: return d.parseByteString(), nil case cborTypeTextString: b, err := d.parseTextString() if err != nil { return nil, err } return string(b), nil case cborTypeTag: tagOff := d.off _, _, tagNum := d.getHead() contentOff := d.off switch tagNum { case 0, 1: d.off = tagOff return d.parseToTime() case 2: b := d.parseByteString() bi := new(big.Int).SetBytes(b) return *bi, nil case 3: b := d.parseByteString() bi := new(big.Int).SetBytes(b) bi.Add(bi, big.NewInt(1)) bi.Neg(bi) return *bi, nil } if d.dm.tags != nil { // Parse to specified type if tag number is registered. tagNums := []uint64{tagNum} for d.nextCBORType() == cborTypeTag { _, _, num := d.getHead() tagNums = append(tagNums, num) } registeredType := d.dm.tags.getTypeFromTagNum(tagNums) if registeredType != nil { d.off = tagOff rv := reflect.New(registeredType) if err := d.parseToValue(rv.Elem(), getTypeInfo(registeredType)); err != nil { return nil, err } return rv.Elem().Interface(), nil } } // Parse tag content d.off = contentOff content, err := d.parse(false) if err != nil { return nil, err } return Tag{tagNum, content}, nil case cborTypePrimitives: _, ai, val := d.getHead() if ai < 20 || ai == 24 { return val, nil } switch ai { case 20, 21: return (ai == 21), nil case 22, 23: return nil, nil case 25: f := float64(float16.Frombits(uint16(val)).Float32()) return f, nil case 26: f := float64(math.Float32frombits(uint32(val))) return f, nil case 27: f := math.Float64frombits(val) return f, nil } 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 } // parseByteString parses CBOR encoded byte string. It returns a byte slice // pointing to a copy of parsed data. func (d *decoder) parseByteString() []byte { _, ai, val := d.getHead() if ai != 31 { b := make([]byte, int(val)) copy(b, d.data[d.off:d.off+int(val)]) d.off += int(val) return b } // Process indefinite length string chunks. b := []byte{} for !d.foundBreak() { _, _, val = d.getHead() b = append(b, d.data[d.off:d.off+int(val)]...) d.off += int(val) } return b } // parseTextString parses CBOR encoded text string. It returns a byte slice // to prevent creating an extra copy of string. Caller should wrap returned // byte slice as string when needed. func (d *decoder) parseTextString() ([]byte, error) { _, ai, val := d.getHead() if ai != 31 { b := d.data[d.off : d.off+int(val)] d.off += int(val) if !utf8.Valid(b) { return nil, &SemanticError{"cbor: invalid UTF-8 string"} } return b, nil } // Process indefinite length string chunks. b := []byte{} for !d.foundBreak() { _, _, val = d.getHead() x := d.data[d.off : d.off+int(val)] d.off += int(val) if !utf8.Valid(x) { for !d.foundBreak() { d.skip() // Skip remaining chunk on error } return nil, &SemanticError{"cbor: invalid UTF-8 string"} } b = append(b, x...) } return b, nil } func (d *decoder) parseArray() ([]interface{}, error) { _, ai, val := d.getHead() hasSize := (ai != 31) count := int(val) if !hasSize { count = d.numOfItemsUntilBreak() // peek ahead to get array size to preallocate slice for better performance } v := make([]interface{}, count) var e interface{} var err, lastErr error for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { if e, lastErr = d.parse(true); lastErr != nil { if err == nil { err = lastErr } continue } v[i] = e } return v, err } func (d *decoder) parseArrayToSlice(v reflect.Value, tInfo *typeInfo) error { _, ai, val := d.getHead() hasSize := (ai != 31) count := int(val) if !hasSize { count = d.numOfItemsUntilBreak() // peek ahead to get array size to preallocate slice for better performance } if v.IsNil() || v.Cap() < count || count == 0 { v.Set(reflect.MakeSlice(tInfo.nonPtrType, count, count)) } v.SetLen(count) var err error for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { if lastErr := d.parseToValue(v.Index(i), tInfo.elemTypeInfo); lastErr != nil { if err == nil { err = lastErr } } } return err } func (d *decoder) parseArrayToArray(v reflect.Value, tInfo *typeInfo) error { _, ai, val := d.getHead() hasSize := (ai != 31) count := int(val) gi := 0 vLen := v.Len() var err error for ci := 0; (hasSize && ci < count) || (!hasSize && !d.foundBreak()); ci++ { if gi < vLen { // Read CBOR array element and set array element if lastErr := d.parseToValue(v.Index(gi), tInfo.elemTypeInfo); lastErr != nil { if err == nil { err = lastErr } } gi++ } else { d.skip() // Skip remaining CBOR array element } } // Set remaining Go array elements to zero values. if gi < vLen { zeroV := reflect.Zero(tInfo.elemTypeInfo.typ) for ; gi < vLen; gi++ { v.Index(gi).Set(zeroV) } } return err } func (d *decoder) parseMap() (interface{}, error) { _, ai, val := d.getHead() hasSize := (ai != 31) count := int(val) m := make(map[interface{}]interface{}) var k, e interface{} var err, lastErr error keyCount := 0 for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { // Parse CBOR map key. if k, lastErr = d.parse(true); lastErr != nil { if err == nil { err = lastErr } d.skip() continue } // 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()) } d.skip() continue } // Parse CBOR map value. if e, lastErr = d.parse(true); lastErr != nil { if err == nil { err = lastErr } continue } // Add key-value pair to Go map. m[k] = e // Detect duplicate map key. if d.dm.dupMapKey == DupMapKeyEnforcedAPF { newKeyCount := len(m) if newKeyCount == keyCount { m[k] = nil err = &DupMapKeyError{k, i} i++ // skip the rest of the map for ; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { d.skip() // Skip map key d.skip() // Skip map value } return m, err } keyCount = newKeyCount } } return m, err } func (d *decoder) parseMapToMap(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo _, ai, val := d.getHead() hasSize := (ai != 31) count := int(val) if v.IsNil() { mapsize := count if !hasSize { mapsize = 0 } v.Set(reflect.MakeMapWithSize(tInfo.nonPtrType, mapsize)) } keyType, eleType := tInfo.keyTypeInfo.typ, tInfo.elemTypeInfo.typ reuseKey, reuseEle := isImmutableKind(tInfo.keyTypeInfo.kind), isImmutableKind(tInfo.elemTypeInfo.kind) var keyValue, eleValue, zeroKeyValue, zeroEleValue reflect.Value keyIsInterfaceType := keyType == typeIntf // If key type is interface{}, need to check if key value is hashable. var err, lastErr error keyCount := v.Len() var existingKeys map[interface{}]bool // Store existing map keys, used for detecting duplicate map key. if d.dm.dupMapKey == DupMapKeyEnforcedAPF { existingKeys = make(map[interface{}]bool, keyCount) if keyCount > 0 { vKeys := v.MapKeys() for i := 0; i < len(vKeys); i++ { existingKeys[vKeys[i].Interface()] = true } } } for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { // Parse CBOR map key. if !keyValue.IsValid() { keyValue = reflect.New(keyType).Elem() } else if !reuseKey { if !zeroKeyValue.IsValid() { zeroKeyValue = reflect.Zero(keyType) } keyValue.Set(zeroKeyValue) } if lastErr = d.parseToValue(keyValue, tInfo.keyTypeInfo); lastErr != nil { if err == nil { err = lastErr } d.skip() continue } // 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()) } d.skip() continue } } // Parse CBOR map value. if !eleValue.IsValid() { eleValue = reflect.New(eleType).Elem() } else if !reuseEle { if !zeroEleValue.IsValid() { zeroEleValue = reflect.Zero(eleType) } eleValue.Set(zeroEleValue) } if lastErr := d.parseToValue(eleValue, tInfo.elemTypeInfo); lastErr != nil { if err == nil { err = lastErr } continue } // Add key-value pair to Go map. v.SetMapIndex(keyValue, eleValue) // Detect duplicate map key. if d.dm.dupMapKey == DupMapKeyEnforcedAPF { newKeyCount := v.Len() if newKeyCount == keyCount { kvi := keyValue.Interface() if !existingKeys[kvi] { v.SetMapIndex(keyValue, reflect.New(eleType).Elem()) err = &DupMapKeyError{kvi, i} i++ // skip the rest of the map for ; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { d.skip() // skip map key d.skip() // skip map value } return err } delete(existingKeys, kvi) } keyCount = newKeyCount } } return err } func (d *decoder) parseArrayToStruct(v reflect.Value, tInfo *typeInfo) error { structType := getDecodingStructType(tInfo.nonPtrType) if structType.err != nil { return structType.err } if !structType.toArray { t := d.nextCBORType() d.skip() return &UnmarshalTypeError{ CBORType: t.String(), GoType: tInfo.nonPtrType.String(), errorMsg: "cannot decode CBOR array to struct without toarray option", } } start := d.off t, ai, val := d.getHead() hasSize := (ai != 31) count := int(val) if !hasSize { count = d.numOfItemsUntilBreak() // peek ahead to get array size } if count != len(structType.fields) { d.off = start d.skip() return &UnmarshalTypeError{ CBORType: t.String(), GoType: tInfo.typ.String(), errorMsg: "cannot decode CBOR array to struct with different number of elements", } } var err, lastErr error for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { f := structType.fields[i] // Get field value by index var fv reflect.Value if len(f.idx) == 1 { fv = v.Field(f.idx[0]) } else { fv, lastErr = getFieldValue(v, f.idx, func(v reflect.Value) (reflect.Value, error) { // Return a new value for embedded field null pointer to point to, or return error. if !v.CanSet() { return reflect.Value{}, errors.New("cbor: cannot set embedded pointer to unexported struct: " + v.Type().String()) } v.Set(reflect.New(v.Type().Elem())) return v, nil }) if lastErr != nil && err == nil { err = lastErr } if !fv.IsValid() { d.skip() continue } } if lastErr = d.parseToValue(fv, f.typInfo); lastErr != nil { if err == nil { if typeError, ok := lastErr.(*UnmarshalTypeError); ok { typeError.StructFieldName = tInfo.typ.String() + "." + f.name err = typeError } else { err = lastErr } } } } return err } // parseMapToStruct needs to be fast so gocyclo can be ignored for now. func (d *decoder) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo structType := getDecodingStructType(tInfo.nonPtrType) if structType.err != nil { return structType.err } if structType.toArray { t := d.nextCBORType() d.skip() return &UnmarshalTypeError{ CBORType: t.String(), GoType: tInfo.nonPtrType.String(), errorMsg: "cannot decode CBOR map to struct with toarray option", } } var err, lastErr error // Get CBOR map size _, ai, val := d.getHead() hasSize := (ai != 31) count := int(val) // Keeps track of matched struct fields foundFldIdx := make([]bool, len(structType.fields)) // Keeps track of CBOR map keys to detect duplicate map key keyCount := 0 var mapKeys map[interface{}]struct{} if d.dm.dupMapKey == DupMapKeyEnforcedAPF { mapKeys = make(map[interface{}]struct{}, len(structType.fields)) } errOnUnknownField := (d.dm.extraReturnErrors & ExtraDecErrorUnknownField) > 0 for j := 0; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ { var f *field var k interface{} // Used by duplicate map key detection t := d.nextCBORType() if t == cborTypeTextString { var keyBytes []byte keyBytes, lastErr = d.parseTextString() if lastErr != nil { if err == nil { err = lastErr } d.skip() // skip value continue } keyLen := len(keyBytes) // Find field with exact match for i := 0; i < len(structType.fields); i++ { fld := structType.fields[i] if !foundFldIdx[i] && len(fld.name) == keyLen && fld.name == string(keyBytes) { f = fld foundFldIdx[i] = true break } } // Find field with case-insensitive match if f == nil { keyString := string(keyBytes) for i := 0; i < len(structType.fields); i++ { fld := structType.fields[i] if !foundFldIdx[i] && len(fld.name) == keyLen && strings.EqualFold(fld.name, keyString) { f = fld foundFldIdx[i] = true break } } } if d.dm.dupMapKey == DupMapKeyEnforcedAPF { k = string(keyBytes) } } else if t <= cborTypeNegativeInt { // uint/int var nameAsInt int64 if t == cborTypePositiveInt { _, _, val := d.getHead() nameAsInt = int64(val) } else { _, _, val := d.getHead() if val > math.MaxInt64 { if err == nil { err = &UnmarshalTypeError{ CBORType: t.String(), GoType: reflect.TypeOf(int64(0)).String(), errorMsg: "-1-" + strconv.FormatUint(val, 10) + " overflows Go's int64", } } d.skip() // skip value continue } nameAsInt = int64(-1) ^ int64(val) } // Find field for i := 0; i < len(structType.fields); i++ { fld := structType.fields[i] if !foundFldIdx[i] && fld.keyAsInt && fld.nameAsInt == nameAsInt { f = fld foundFldIdx[i] = true break } } if d.dm.dupMapKey == DupMapKeyEnforcedAPF { k = nameAsInt } } else { if err == nil { err = &UnmarshalTypeError{ CBORType: t.String(), GoType: reflect.TypeOf("").String(), errorMsg: "map key is of type " + t.String() + " and cannot be used to match struct field name", } } if d.dm.dupMapKey == DupMapKeyEnforcedAPF { // parse key k, lastErr = d.parse(true) if lastErr != nil { d.skip() // skip value continue } // Detect if CBOR map key can be used as Go map key. if !isHashableValue(reflect.ValueOf(k)) { d.skip() // skip value continue } } else { d.skip() // skip key } } if d.dm.dupMapKey == DupMapKeyEnforcedAPF { mapKeys[k] = struct{}{} newKeyCount := len(mapKeys) if newKeyCount == keyCount { err = &DupMapKeyError{k, j} d.skip() // skip value j++ // skip the rest of the map for ; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ { d.skip() d.skip() } return err } keyCount = newKeyCount } if f == nil { if errOnUnknownField { err = &UnknownFieldError{j} d.skip() // Skip value j++ // skip the rest of the map for ; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ { d.skip() d.skip() } return err } d.skip() // Skip value continue } // Get field value by index var fv reflect.Value if len(f.idx) == 1 { fv = v.Field(f.idx[0]) } else { fv, lastErr = getFieldValue(v, f.idx, func(v reflect.Value) (reflect.Value, error) { // Return a new value for embedded field null pointer to point to, or return error. if !v.CanSet() { return reflect.Value{}, errors.New("cbor: cannot set embedded pointer to unexported struct: " + v.Type().String()) } v.Set(reflect.New(v.Type().Elem())) return v, nil }) if lastErr != nil && err == nil { err = lastErr } if !fv.IsValid() { d.skip() continue } } if lastErr = d.parseToValue(fv, f.typInfo); lastErr != nil { if err == nil { if typeError, ok := lastErr.(*UnmarshalTypeError); ok { typeError.StructFieldName = tInfo.nonPtrType.String() + "." + f.name err = typeError } else { err = lastErr } } } } return err } // validRegisteredTagNums verifies that tag numbers match registered tag numbers of type t. // validRegisteredTagNums assumes next CBOR data type is tag. It scans all tag numbers, and stops at tag content. func (d *decoder) validRegisteredTagNums(registeredTag *tagItem) error { // Scan until next cbor data is tag content. tagNums := make([]uint64, 0, 1) for d.nextCBORType() == cborTypeTag { _, _, val := d.getHead() tagNums = append(tagNums, val) } if !registeredTag.equalTagNum(tagNums) { return &WrongTagError{registeredTag.contentType, registeredTag.num, tagNums} } return nil } func (d *decoder) getRegisteredTagItem(vt reflect.Type) *tagItem { if d.dm.tags != nil { return d.dm.tags.getTagItemFromType(vt) } return nil } // skip moves data offset to the next item. skip assumes data is well-formed, // and does not perform bounds checking. func (d *decoder) skip() { t, ai, val := d.getHead() if ai == 31 { switch t { case cborTypeByteString, cborTypeTextString, cborTypeArray, cborTypeMap: for { if d.data[d.off] == 0xff { d.off++ return } d.skip() } } } switch t { case cborTypeByteString, cborTypeTextString: d.off += int(val) case cborTypeArray: for i := 0; i < int(val); i++ { d.skip() } case cborTypeMap: for i := 0; i < int(val)*2; i++ { d.skip() } case cborTypeTag: d.skip() } } // getHead assumes data is well-formed, and does not perform bounds checking. func (d *decoder) getHead() (t cborType, ai byte, val uint64) { t = cborType(d.data[d.off] & 0xe0) ai = d.data[d.off] & 0x1f val = uint64(ai) d.off++ if ai < 24 { return } if ai == 24 { val = uint64(d.data[d.off]) d.off++ return } if ai == 25 { val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+2])) d.off += 2 return } if ai == 26 { val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+4])) d.off += 4 return } if ai == 27 { val = binary.BigEndian.Uint64(d.data[d.off : d.off+8]) d.off += 8 return } return } func (d *decoder) numOfItemsUntilBreak() int { savedOff := d.off i := 0 for !d.foundBreak() { d.skip() i++ } d.off = savedOff return i } // foundBreak assumes data is well-formed, and does not perform bounds checking. func (d *decoder) foundBreak() bool { if d.data[d.off] == 0xff { d.off++ return true } return false } func (d *decoder) reset(data []byte) { d.data = data d.off = 0 } func (d *decoder) nextCBORType() cborType { return cborType(d.data[d.off] & 0xe0) } func (d *decoder) nextCBORNil() bool { return d.data[d.off] == 0xf6 || d.data[d.off] == 0xf7 } var ( typeIntf = reflect.TypeOf([]interface{}(nil)).Elem() typeTime = reflect.TypeOf(time.Time{}) typeBigInt = reflect.TypeOf(big.Int{}) typeUnmarshaler = reflect.TypeOf((*Unmarshaler)(nil)).Elem() typeBinaryUnmarshaler = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem() ) func fillNil(t cborType, v reflect.Value) error { switch v.Kind() { case reflect.Slice, reflect.Map, reflect.Interface, reflect.Ptr: v.Set(reflect.Zero(v.Type())) return nil } return nil } func fillPositiveInt(t cborType, val uint64, v reflect.Value) error { switch v.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if val > math.MaxInt64 { return &UnmarshalTypeError{ CBORType: t.String(), GoType: v.Type().String(), errorMsg: strconv.FormatUint(val, 10) + " overflows " + v.Type().String(), } } if v.OverflowInt(int64(val)) { return &UnmarshalTypeError{ CBORType: t.String(), GoType: v.Type().String(), errorMsg: strconv.FormatUint(val, 10) + " overflows " + v.Type().String(), } } v.SetInt(int64(val)) return nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if v.OverflowUint(val) { return &UnmarshalTypeError{ CBORType: t.String(), GoType: v.Type().String(), errorMsg: strconv.FormatUint(val, 10) + " overflows " + v.Type().String(), } } v.SetUint(val) return nil case reflect.Float32, reflect.Float64: f := float64(val) v.SetFloat(f) return nil } if v.Type() == typeBigInt { i := new(big.Int).SetUint64(val) v.Set(reflect.ValueOf(*i)) return nil } return &UnmarshalTypeError{CBORType: t.String(), GoType: v.Type().String()} } func fillNegativeInt(t cborType, val int64, v reflect.Value) error { switch v.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if v.OverflowInt(val) { return &UnmarshalTypeError{ CBORType: t.String(), GoType: v.Type().String(), errorMsg: strconv.FormatInt(val, 10) + " overflows " + v.Type().String(), } } v.SetInt(val) return nil case reflect.Float32, reflect.Float64: f := float64(val) v.SetFloat(f) return nil } if v.Type() == typeBigInt { i := new(big.Int).SetInt64(val) v.Set(reflect.ValueOf(*i)) return nil } return &UnmarshalTypeError{CBORType: t.String(), GoType: v.Type().String()} } func fillBool(t cborType, val bool, v reflect.Value) error { if v.Kind() == reflect.Bool { v.SetBool(val) return nil } return &UnmarshalTypeError{CBORType: t.String(), GoType: v.Type().String()} } func fillFloat(t cborType, val float64, v reflect.Value) error { switch v.Kind() { case reflect.Float32, reflect.Float64: if v.OverflowFloat(val) { return &UnmarshalTypeError{ CBORType: t.String(), GoType: v.Type().String(), errorMsg: strconv.FormatFloat(val, 'E', -1, 64) + " overflows " + v.Type().String(), } } v.SetFloat(val) return nil } return &UnmarshalTypeError{CBORType: t.String(), GoType: v.Type().String()} } func fillByteString(t cborType, val []byte, v reflect.Value) error { if reflect.PtrTo(v.Type()).Implements(typeBinaryUnmarshaler) { if v.CanAddr() { v = v.Addr() if u, ok := v.Interface().(encoding.BinaryUnmarshaler); ok { return u.UnmarshalBinary(val) } } return errors.New("cbor: cannot set new value for " + v.Type().String()) } if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 { v.SetBytes(val) return nil } if v.Kind() == reflect.Array && v.Type().Elem().Kind() == reflect.Uint8 { vLen := v.Len() i := 0 for ; i < vLen && i < len(val); i++ { v.Index(i).SetUint(uint64(val[i])) } // Set remaining Go array elements to zero values. if i < vLen { zeroV := reflect.Zero(reflect.TypeOf(byte(0))) for ; i < vLen; i++ { v.Index(i).Set(zeroV) } } return nil } return &UnmarshalTypeError{CBORType: t.String(), GoType: v.Type().String()} } func fillTextString(t cborType, val []byte, v reflect.Value) error { if v.Kind() == reflect.String { v.SetString(string(val)) return nil } return &UnmarshalTypeError{CBORType: t.String(), GoType: v.Type().String()} } func isImmutableKind(k reflect.Kind) bool { switch k { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String: return true default: return false } } func isHashableValue(rv reflect.Value) bool { switch rv.Kind() { case reflect.Slice, reflect.Map, reflect.Func: return false case reflect.Struct: switch rv.Type() { case typeTag: tag := rv.Interface().(Tag) return isHashableValue(reflect.ValueOf(tag.Content)) case typeBigInt: return false } } return true } // 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) switch tagNum { case 0: // Tag content (date/time text string in RFC 3339 format) must be string type. if t != cborTypeTextString { return errors.New("cbor: tag number 0 must be followed by text string, got " + t.String()) } return nil case 1: // Tag content (epoch date/time) must be uint, int, or float type. if t != cborTypePositiveInt && t != cborTypeNegativeInt && (contentHead < 0xf9 || contentHead > 0xfb) { return errors.New("cbor: tag number 1 must be followed by integer or floating-point number, got " + t.String()) } return nil case 2, 3: // Tag content (bignum) must be byte type. if t != cborTypeByteString { return errors.New("cbor: tag number 2 or 3 must be followed by byte string, got " + t.String()) } return nil } return nil } golang-github-fxamacker-cbor-2.4.0/decode_test.go000066400000000000000000007142301416612535600217570ustar00rootroot00000000000000// 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/binary" "encoding/hex" "errors" "fmt" "io" "math" "math/big" "reflect" "strings" "testing" "time" ) var ( typeBool = reflect.TypeOf(true) typeUint8 = reflect.TypeOf(uint8(0)) typeUint16 = reflect.TypeOf(uint16(0)) typeUint32 = reflect.TypeOf(uint32(0)) typeUint64 = reflect.TypeOf(uint64(0)) typeInt8 = reflect.TypeOf(int8(0)) typeInt16 = reflect.TypeOf(int16(0)) typeInt32 = reflect.TypeOf(int32(0)) typeInt64 = reflect.TypeOf(int64(0)) typeFloat32 = reflect.TypeOf(float32(0)) typeFloat64 = reflect.TypeOf(float64(0)) typeString = reflect.TypeOf("") typeByteSlice = reflect.TypeOf([]byte(nil)) typeByteArray = reflect.TypeOf([5]byte{}) typeIntSlice = reflect.TypeOf([]int{}) typeStringSlice = reflect.TypeOf([]string{}) typeMapStringInt = reflect.TypeOf(map[string]int{}) typeMapStringString = reflect.TypeOf(map[string]string{}) typeMapStringIntf = reflect.TypeOf(map[string]interface{}{}) ) type unmarshalTest struct { cborData []byte emptyInterfaceValue interface{} values []interface{} wrongTypes []reflect.Type } var unmarshalTests = []unmarshalTest{ // CBOR test data are from https://tools.ietf.org/html/rfc7049#appendix-A. // positive integer { 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}, }, { 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}, }, { 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}, }, { 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}, }, { 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}, }, { 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}, }, { 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}, }, { 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}, }, { 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}, }, { 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}, }, { 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}, }, // 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}, }, { 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}, }, { 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}, }, { 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}, }, { hexDecode("3bffffffffffffffff"), bigIntOrPanic("-18446744073709551616"), []interface{}{bigIntOrPanic("-18446744073709551616")}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeString, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag}, }, // 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}}, []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}}, []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}}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, // text string { hexDecode("60"), "", []interface{}{""}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, { hexDecode("6161"), "a", []interface{}{"a"}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, { hexDecode("6449455446"), "IETF", []interface{}{"IETF"}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, { hexDecode("62225c"), "\"\\", []interface{}{"\"\\"}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, { hexDecode("62c3bc"), "ü", []interface{}{"ü"}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, { hexDecode("63e6b0b4"), "水", []interface{}{"水"}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, { hexDecode("64f0908591"), "𐅑", []interface{}{"𐅑"}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, { hexDecode("7f657374726561646d696e67ff"), "streaming", []interface{}{"streaming"}, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeByteArray, typeIntSlice, typeMapStringInt, typeTag, typeRawTag, typeBigInt}, }, // 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}, }, { 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}, }, { 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}, }, { 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}, }, { 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}, }, { hexDecode("98190102030405060708090a0b0c0d0e0f101112131415161718181819"), []interface{}{uint64(1), uint64(2), uint64(3), uint64(4), uint64(5), uint64(6), uint64(7), uint64(8), uint64(9), uint64(10), uint64(11), uint64(12), uint64(13), uint64(14), uint64(15), uint64(16), uint64(17), uint64(18), uint64(19), uint64(20), uint64(21), uint64(22), uint64(23), uint64(24), uint64(25)}, []interface{}{ []interface{}{uint64(1), uint64(2), uint64(3), uint64(4), uint64(5), uint64(6), uint64(7), uint64(8), uint64(9), uint64(10), uint64(11), uint64(12), uint64(13), uint64(14), uint64(15), uint64(16), uint64(17), uint64(18), uint64(19), uint64(20), uint64(21), uint64(22), uint64(23), uint64(24), uint64(25)}, []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}, []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}, []uint{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]int{}, [1]int{1}, [...]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}, [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}, }, { 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}, }, { 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}, }, { 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}, }, { hexDecode("9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff"), []interface{}{uint64(1), uint64(2), uint64(3), uint64(4), uint64(5), uint64(6), uint64(7), uint64(8), uint64(9), uint64(10), uint64(11), uint64(12), uint64(13), uint64(14), uint64(15), uint64(16), uint64(17), uint64(18), uint64(19), uint64(20), uint64(21), uint64(22), uint64(23), uint64(24), uint64(25)}, []interface{}{ []interface{}{uint64(1), uint64(2), uint64(3), uint64(4), uint64(5), uint64(6), uint64(7), uint64(8), uint64(9), uint64(10), uint64(11), uint64(12), uint64(13), uint64(14), uint64(15), uint64(16), uint64(17), uint64(18), uint64(19), uint64(20), uint64(21), uint64(22), uint64(23), uint64(24), uint64(25)}, []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}, []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}, []uint{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]int{}, [1]int{1}, [...]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}, [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}, }, { 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}, }, { 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}, }, // 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}, }, { 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}, }, { 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}, }, { hexDecode("a56161614161626142616361436164614461656145"), map[interface{}]interface{}{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}, []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}, }, { 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}, }, { 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}, }, // 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}, }, // 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}, }, // 1: epoch-based date/time { hexDecode("c249010000000000000000"), bigIntOrPanic("18446744073709551616"), []interface{}{ // Decode to byte slice []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Decode to array of various lengths [0]byte{}, [1]byte{0x01}, [3]byte{0x01, 0x00, 0x00}, [...]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, [10]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Decode to Tag and RawTag Tag{2, []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, RawTag{2, hexDecode("49010000000000000000")}, // Decode to big.Int bigIntOrPanic("18446744073709551616"), }, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt}, }, // 2: positive bignum: 18446744073709551616 { hexDecode("c349010000000000000000"), bigIntOrPanic("-18446744073709551617"), []interface{}{ // Decode to byte slice []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Decode to array of various lengths [0]byte{}, [1]byte{0x01}, [3]byte{0x01, 0x00, 0x00}, [...]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, [10]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Decode to Tag and RawTag Tag{3, []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, RawTag{3, hexDecode("49010000000000000000")}, // Decode to big.Int bigIntOrPanic("-18446744073709551617"), }, []reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt}, }, // 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}, }, // 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}, }, // 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}, }, // 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}, }, // 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}, }, { 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}, }, { 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{}}, 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{}}, 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}, }, // 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. // I reported an errata to RFC 7049 and Carsten Bormann confirmed at https://github.com/fxamacker/cbor/issues/46 /* { hexDecode("f818"), 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)}, []reflect.Type{typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt}, }, */ { 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}, }, { 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}, }, // 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}}, []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}, }, { 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}, }, // 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}, }, { 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{})}, }, } type unmarshalFloatTest struct { cborData []byte emptyInterfaceValue interface{} values []interface{} wrongTypes []reflect.Type equalityThreshold float64 // Not used for +inf, -inf, and NaN. } // unmarshalFloatTests includes test values for float16, float32, and float64. // Note: the function for float16 to float32 conversion was tested with all // 65536 values, which is too many to include here. var unmarshalFloatTests = []unmarshalFloatTest{ // CBOR test data are from https://tools.ietf.org/html/rfc7049#appendix-A. // float16 { 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}, 0.0, }, { hexDecode("f98000"), 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}, 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}, 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}, 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}, 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}, 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}, 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}, 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}, 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}, 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}, 0.0, }, // float32 { 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}, 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}, 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}, 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}, 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}, 0.0, }, // float64 { 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}, 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}, 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}, 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}, 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}, 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}, 0.0, }, // float16 test data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format { 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}, 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}, 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}, 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}, 1e-9, }, // CBOR test data "canonNums" are from https://github.com/cbor-wg/cbor-test-vectors { 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}, 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}, 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}, 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}, 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}, 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}, 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}, 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}, 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}, 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}, 0.0, }, } const invalidUTF8ErrorMsg = "cbor: invalid UTF-8 string" func hexDecode(s string) []byte { data, err := hex.DecodeString(s) if err != nil { panic(err) } return data } func bigIntOrPanic(s string) big.Int { bi, ok := new(big.Int).SetString(s, 10) if !ok { panic("failed to convert " + s + " to big.Int") } return *bi } func TestUnmarshal(t *testing.T) { for _, tc := range unmarshalTests { // Test unmarshalling CBOR into empty interface. var v interface{} if err := Unmarshal(tc.cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } else { if tm, ok := tc.emptyInterfaceValue.(time.Time); ok { if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) } } else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) } } // Test unmarshalling CBOR into RawMessage. var r RawMessage if err := Unmarshal(tc.cborData, &r); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } else if !bytes.Equal(r, tc.cborData) { t.Errorf("Unmarshal(0x%x) returned RawMessage %v, want %v", tc.cborData, r, tc.cborData) } // Test unmarshalling CBOR into compatible data types. for _, value := range tc.values { v := reflect.New(reflect.TypeOf(value)) vPtr := v.Interface() if err := Unmarshal(tc.cborData, vPtr); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } else { if tm, ok := value.(time.Time); ok { if vt, ok := v.Elem().Interface().(time.Time); !ok || !tm.Equal(vt) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v.Elem().Interface(), v.Elem().Interface(), value, value) } } else if !reflect.DeepEqual(v.Elem().Interface(), value) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v.Elem().Interface(), v.Elem().Interface(), value, value) } } } // Test unmarshalling CBOR into incompatible data types. for _, typ := range tc.wrongTypes { v := reflect.New(typ) vPtr := v.Interface() if err := Unmarshal(tc.cborData, vPtr); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", tc.cborData, typ.String()) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", tc.cborData, err) } else if !strings.Contains(err.Error(), "cannot unmarshal") { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.cborData, err.Error(), "cannot unmarshal") } } } } func TestUnmarshalFloat(t *testing.T) { for _, tc := range unmarshalFloatTests { // Test unmarshalling CBOR into empty interface. var v interface{} if err := Unmarshal(tc.cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } else { testFloat(t, tc.cborData, v, tc.emptyInterfaceValue, tc.equalityThreshold) } // Test unmarshalling CBOR into RawMessage. var r RawMessage if err := Unmarshal(tc.cborData, &r); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } else if !bytes.Equal(r, tc.cborData) { t.Errorf("Unmarshal(0x%x) returned RawMessage %v, want %v", tc.cborData, r, tc.cborData) } // Test unmarshalling CBOR into compatible data types. for _, value := range tc.values { v := reflect.New(reflect.TypeOf(value)) vPtr := v.Interface() if err := Unmarshal(tc.cborData, vPtr); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } else { testFloat(t, tc.cborData, v.Elem().Interface(), value, tc.equalityThreshold) } } // Test unmarshalling CBOR into incompatible data types. for _, typ := range tc.wrongTypes { v := reflect.New(typ) vPtr := v.Interface() if err := Unmarshal(tc.cborData, vPtr); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", tc.cborData) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", tc.cborData, err) } else if !strings.Contains(err.Error(), "cannot unmarshal") { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.cborData, err.Error(), "cannot unmarshal") } } } } func testFloat(t *testing.T, cborData []byte, f interface{}, wantf interface{}, equalityThreshold float64) { switch wantf := wantf.(type) { case float32: f, ok := f.(float32) if !ok { t.Errorf("Unmarshal(0x%x) returned value of type %T, want float32", cborData, f) return } if math.IsNaN(float64(wantf)) { if !math.IsNaN(float64(f)) { t.Errorf("Unmarshal(0x%x) = %f, want NaN", cborData, f) } } else if math.IsInf(float64(wantf), 0) { if f != wantf { t.Errorf("Unmarshal(0x%x) = %f, want %f", cborData, f, wantf) } } else if math.Abs(float64(f-wantf)) > equalityThreshold { t.Errorf("Unmarshal(0x%x) = %.18f, want %.18f, diff %.18f > threshold %.18f", cborData, f, wantf, math.Abs(float64(f-wantf)), equalityThreshold) } case float64: f, ok := f.(float64) if !ok { t.Errorf("Unmarshal(0x%x) returned value of type %T, want float64", cborData, f) return } if math.IsNaN(wantf) { if !math.IsNaN(f) { t.Errorf("Unmarshal(0x%x) = %f, want NaN", cborData, f) } } else if math.IsInf(wantf, 0) { if f != wantf { t.Errorf("Unmarshal(0x%x) = %f, want %f", cborData, f, wantf) } } else if math.Abs(f-wantf) > equalityThreshold { t.Errorf("Unmarshal(0x%x) = %.18f, want %.18f, diff %.18f > threshold %.18f", cborData, f, wantf, math.Abs(f-wantf), equalityThreshold) } } } func TestNegIntOverflow(t *testing.T) { cborData := hexDecode("3bffffffffffffffff") // -18446744073709551616 // Decode CBOR neg int that overflows Go int64 to empty interface var v1 interface{} wantObj := bigIntOrPanic("-18446744073709551616") if err := Unmarshal(cborData, &v1); err != nil { t.Errorf("Unmarshal(0x%x) returned error %+v", cborData, err) } else if !reflect.DeepEqual(v1, wantObj) { t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", cborData, v1, v1, wantObj, wantObj) } // Decode CBOR neg int that overflows Go int64 to big.Int var v2 big.Int if err := Unmarshal(cborData, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %+v", cborData, err) } else if !reflect.DeepEqual(v2, wantObj) { t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", cborData, v2, v2, wantObj, wantObj) } // Decode CBOR neg int that overflows Go int64 to int64 var v3 int64 if err := Unmarshal(cborData, &v3); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), "cannot unmarshal") { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), "cannot unmarshal") } } func TestUnmarshalIntoPtrPrimitives(t *testing.T) { cborDataInt := hexDecode("1818") // 24 cborDataString := hexDecode("7f657374726561646d696e67ff") // "streaming" const wantInt = 24 const wantString = "streaming" var p1 *int var p2 *string var p3 *RawMessage var i int pi := &i ppi := &pi var s string ps := &s pps := &ps var r RawMessage pr := &r ppr := &pr // Unmarshal CBOR integer into a non-nil pointer. if err := Unmarshal(cborDataInt, &ppi); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborDataInt, err) } else if i != wantInt { t.Errorf("Unmarshal(0x%x) = %v (%T), want %d", cborDataInt, i, i, wantInt) } // Unmarshal CBOR integer into a nil pointer. if err := Unmarshal(cborDataInt, &p1); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborDataInt, err) } else if *p1 != wantInt { t.Errorf("Unmarshal(0x%x) = %v (%T), want %d", cborDataInt, *pi, pi, wantInt) } // Unmarshal CBOR string into a non-nil pointer. if err := Unmarshal(cborDataString, &pps); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborDataString, err) } else if s != wantString { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborDataString, s, s, wantString) } // Unmarshal CBOR string into a nil pointer. if err := Unmarshal(cborDataString, &p2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborDataString, err) } else if *p2 != wantString { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborDataString, *p2, p2, wantString) } // Unmarshal CBOR string into a non-nil RawMessage. if err := Unmarshal(cborDataString, &ppr); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborDataString, err) } else if !bytes.Equal(r, cborDataString) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborDataString, r, r, cborDataString) } // Unmarshal CBOR string into a nil pointer to RawMessage. if err := Unmarshal(cborDataString, &p3); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborDataString, err) } else if !bytes.Equal(*p3, cborDataString) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborDataString, *p3, p3, cborDataString) } } func TestUnmarshalIntoPtrArrayPtrElem(t *testing.T) { cborData := hexDecode("83010203") // []int{1, 2, 3} n1, n2, n3 := 1, 2, 3 wantArray := []*int{&n1, &n2, &n3} var p *[]*int var slc []*int pslc := &slc ppslc := &pslc // Unmarshal CBOR array into a non-nil pointer. if err := Unmarshal(cborData, &ppslc); err != nil { t.Errorf("Unmarshal(0x%x, %s) returned error %v", cborData, reflect.TypeOf(ppslc), err) } else if !reflect.DeepEqual(slc, wantArray) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborData, slc, slc, wantArray) } // Unmarshal CBOR array into a nil pointer. if err := Unmarshal(cborData, &p); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if !reflect.DeepEqual(*p, wantArray) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborData, *p, p, wantArray) } } func TestUnmarshalIntoPtrMapPtrElem(t *testing.T) { cborData := hexDecode("a201020304") // {1: 2, 3: 4} n1, n2, n3, n4 := 1, 2, 3, 4 wantMap := map[int]*int{n1: &n2, n3: &n4} var p *map[int]*int var m map[int]*int pm := &m ppm := &pm // Unmarshal CBOR map into a non-nil pointer. if err := Unmarshal(cborData, &ppm); err != nil { t.Errorf("Unmarshal(0x%x, %s) returned error %v", cborData, reflect.TypeOf(ppm), err) } else if !reflect.DeepEqual(m, wantMap) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborData, m, m, wantMap) } // Unmarshal CBOR map into a nil pointer. if err := Unmarshal(cborData, &p); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if !reflect.DeepEqual(*p, wantMap) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborData, *p, p, wantMap) } } func TestUnmarshalIntoPtrStructPtrElem(t *testing.T) { type s1 struct { A *string `cbor:"a"` B *string `cbor:"b"` C *string `cbor:"c"` D *string `cbor:"d"` E *string `cbor:"e"` } cborData := hexDecode("a56161614161626142616361436164614461656145") // map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"} a, b, c, d, e := "A", "B", "C", "D", "E" wantObj := s1{A: &a, B: &b, C: &c, D: &d, E: &e} var p *s1 var s s1 ps := &s pps := &ps // Unmarshal CBOR map into a non-nil pointer. if err := Unmarshal(cborData, &pps); err != nil { t.Errorf("Unmarshal(0x%x, %s) returned error %v", cborData, reflect.TypeOf(pps), err) } else if !reflect.DeepEqual(s, wantObj) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborData, s, s, wantObj) } // Unmarshal CBOR map into a nil pointer. if err := Unmarshal(cborData, &p); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if !reflect.DeepEqual(*p, wantObj) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborData, *p, p, wantObj) } } func TestUnmarshalIntoArray(t *testing.T) { cborData := hexDecode("83010203") // []int{1, 2, 3} // Unmarshal CBOR array into Go array. var arr1 [3]int if err := Unmarshal(cborData, &arr1); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if arr1 != [3]int{1, 2, 3} { t.Errorf("Unmarshal(0x%x) = %v (%T), want [3]int{1, 2, 3}", cborData, arr1, arr1) } // Unmarshal CBOR array into Go array with more elements. var arr2 [5]int if err := Unmarshal(cborData, &arr2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if arr2 != [5]int{1, 2, 3, 0, 0} { t.Errorf("Unmarshal(0x%x) = %v (%T), want [5]int{1, 2, 3, 0, 0}", cborData, arr2, arr2) } // Unmarshal CBOR array into Go array with less elements. var arr3 [1]int if err := Unmarshal(cborData, &arr3); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if arr3 != [1]int{1} { t.Errorf("Unmarshal(0x%x) = %v (%T), want [0]int{1}", cborData, arr3, arr3) } } type nilUnmarshaler string func (s *nilUnmarshaler) UnmarshalCBOR(data []byte) error { if len(data) == 1 && (data[0] == 0xf6 || data[0] == 0xf7) { *s = "null" } else { *s = nilUnmarshaler(data) } return nil } func TestUnmarshalNil(t *testing.T) { type T struct { I int } cborData := [][]byte{hexDecode("f6"), hexDecode("f7")} // CBOR null and undefined values testCases := []struct { name string value interface{} wantValue interface{} }{ // Unmarshalling CBOR null to the following types is a no-op. {"bool", true, true}, {"int", int(-1), int(-1)}, {"int8", int8(-2), int8(-2)}, {"int16", int16(-3), int16(-3)}, {"int32", int32(-4), int32(-4)}, {"int64", int64(-5), int64(-5)}, {"uint", uint(1), uint(1)}, {"uint8", uint8(2), uint8(2)}, {"uint16", uint16(3), uint16(3)}, {"uint32", uint32(4), uint32(4)}, {"uint64", uint64(5), uint64(5)}, {"float32", float32(1.23), float32(1.23)}, {"float64", float64(4.56), float64(4.56)}, {"string", "hello", "hello"}, {"array", [3]int{1, 2, 3}, [3]int{1, 2, 3}}, // Unmarshalling CBOR null to slice/map sets Go values to nil. {"[]byte", []byte{1, 2, 3}, []byte(nil)}, {"slice", []string{"hello", "world"}, []string(nil)}, {"map", map[string]bool{"hello": true, "goodbye": false}, map[string]bool(nil)}, // 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)}, // Unmarshalling CBOR null to big.Int is a no-op. {"big.Int", bigIntOrPanic("123"), bigIntOrPanic("123")}, // Unmarshalling CBOR null to user defined struct types is a no-op. {"user defined struct", T{I: 123}, T{I: 123}}, // Unmarshalling CBOR null to cbor.Tag and cbor.RawTag is a no-op. {"cbor.RawTag", RawTag{123, []byte{4, 5, 6}}, RawTag{123, []byte{4, 5, 6}}}, {"cbor.Tag", Tag{123, "hello world"}, Tag{123, "hello world"}}, // Unmarshalling to cbor.RawMessage sets cbor.RawMessage to raw CBOR bytes (0xf6 or 0xf7). // It's tested in TestUnmarshal(). // Unmarshalling to types implementing cbor.BinaryUnmarshaler is a no-op. {"cbor.BinaryUnmarshaler", number(456), number(456)}, // When unmarshalling to types implementing cbor.Unmarshaler, {"cbor.Unmarshaler", nilUnmarshaler("hello world"), nilUnmarshaler("null")}, } // Unmarshalling to values of specified Go types. for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { for _, data := range cborData { v := reflect.New(reflect.TypeOf(tc.value)) v.Elem().Set(reflect.ValueOf(tc.value)) if err := Unmarshal(data, v.Interface()); err != nil { t.Errorf("Unmarshal(0x%x) to %T returned error %v", data, v.Elem().Interface(), err) } else if !reflect.DeepEqual(v.Elem().Interface(), tc.wantValue) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", data, v.Elem().Interface(), v.Elem().Interface(), tc.wantValue, tc.wantValue) } } }) } } var invalidUnmarshalTests = []struct { name string v interface{} wantErrorMsg string }{ {"unmarshal into nil interface{}", nil, "cbor: Unmarshal(nil)"}, {"unmarshal into non-pointer value", 5, "cbor: Unmarshal(non-pointer int)"}, {"unmarshal into nil pointer", (*int)(nil), "cbor: Unmarshal(nil *int)"}, } func TestInvalidUnmarshal(t *testing.T) { cborData := []byte{0x00} for _, tc := range invalidUnmarshalTests { t.Run(tc.name, func(t *testing.T) { err := Unmarshal(cborData, tc.v) if err == nil { t.Errorf("Unmarshal(0x%x, %v) didn't return an error", cborData, tc.v) } else if _, ok := err.(*InvalidUnmarshalError); !ok { t.Errorf("Unmarshal(0x%x, %v) error type %T, want *InvalidUnmarshalError", cborData, tc.v, err) } else if err.Error() != tc.wantErrorMsg { t.Errorf("Unmarshal(0x%x, %v) error %q, want %q", cborData, tc.v, err.Error(), tc.wantErrorMsg) } }) } } var invalidCBORUnmarshalTests = []struct { name string cborData []byte wantErrorMsg string errorMsgPartialMatch bool }{ {"Nil data", []byte(nil), "EOF", false}, {"Empty data", []byte{}, "EOF", false}, {"Tag number not followed by tag content", []byte{0xc0}, "unexpected EOF", false}, {"Definite length strings with tagged chunk", hexDecode("5fc64401020304ff"), "cbor: wrong element type tag for indefinite-length byte string", false}, {"Definite length strings with tagged chunk", hexDecode("7fc06161ff"), "cbor: wrong element type tag for indefinite-length UTF-8 text string", false}, {"Indefinite length strings with invalid head", hexDecode("7f61"), "unexpected EOF", false}, {"Invalid nested tag number", hexDecode("d864dc1a514b67b0"), "cbor: invalid additional information", true}, // Data from 7049bis G.1 // Premature end of the input {"End of input in a head", hexDecode("18"), "unexpected EOF", false}, {"End of input in a head", hexDecode("19"), "unexpected EOF", false}, {"End of input in a head", hexDecode("1a"), "unexpected EOF", false}, {"End of input in a head", hexDecode("1b"), "unexpected EOF", false}, {"End of input in a head", hexDecode("1901"), "unexpected EOF", false}, {"End of input in a head", hexDecode("1a0102"), "unexpected EOF", false}, {"End of input in a head", hexDecode("1b01020304050607"), "unexpected EOF", false}, {"End of input in a head", hexDecode("38"), "unexpected EOF", false}, {"End of input in a head", hexDecode("58"), "unexpected EOF", false}, {"End of input in a head", hexDecode("78"), "unexpected EOF", false}, {"End of input in a head", hexDecode("98"), "unexpected EOF", false}, {"End of input in a head", hexDecode("9a01ff00"), "unexpected EOF", false}, {"End of input in a head", hexDecode("b8"), "unexpected EOF", false}, {"End of input in a head", hexDecode("d8"), "unexpected EOF", false}, {"End of input in a head", hexDecode("f8"), "unexpected EOF", false}, {"End of input in a head", hexDecode("f900"), "unexpected EOF", false}, {"End of input in a head", hexDecode("fa0000"), "unexpected EOF", false}, {"End of input in a head", hexDecode("fb000000"), "unexpected EOF", false}, {"Definite length strings with short data", hexDecode("41"), "unexpected EOF", false}, {"Definite length strings with short data", hexDecode("61"), "unexpected EOF", false}, {"Definite length strings with short data", hexDecode("5affffffff00"), "unexpected EOF", false}, {"Definite length strings with short data", hexDecode("5bffffffffffffffff010203"), "cbor: byte string length 18446744073709551615 is too large, causing integer overflow", false}, {"Definite length strings with short data", hexDecode("7affffffff00"), "unexpected EOF", false}, {"Definite length strings with short data", hexDecode("7b7fffffffffffffff010203"), "unexpected EOF", false}, {"Definite length maps and arrays not closed with enough items", hexDecode("81"), "unexpected EOF", false}, {"Definite length maps and arrays not closed with enough items", hexDecode("818181818181818181"), "unexpected EOF", false}, {"Definite length maps and arrays not closed with enough items", hexDecode("8200"), "unexpected EOF", false}, {"Definite length maps and arrays not closed with enough items", hexDecode("a1"), "unexpected EOF", false}, {"Definite length maps and arrays not closed with enough items", hexDecode("a20102"), "unexpected EOF", false}, {"Definite length maps and arrays not closed with enough items", hexDecode("a100"), "unexpected EOF", false}, {"Definite length maps and arrays not closed with enough items", hexDecode("a2000000"), "unexpected EOF", false}, {"Indefinite length strings not closed by a break stop code", hexDecode("5f4100"), "unexpected EOF", false}, {"Indefinite length strings not closed by a break stop code", hexDecode("7f6100"), "unexpected EOF", false}, {"Indefinite length maps and arrays not closed by a break stop code", hexDecode("9f"), "unexpected EOF", false}, {"Indefinite length maps and arrays not closed by a break stop code", hexDecode("9f0102"), "unexpected EOF", false}, {"Indefinite length maps and arrays not closed by a break stop code", hexDecode("bf"), "unexpected EOF", false}, {"Indefinite length maps and arrays not closed by a break stop code", hexDecode("bf01020102"), "unexpected EOF", false}, {"Indefinite length maps and arrays not closed by a break stop code", hexDecode("819f"), "unexpected EOF", false}, {"Indefinite length maps and arrays not closed by a break stop code", hexDecode("9f8000"), "unexpected EOF", false}, {"Indefinite length maps and arrays not closed by a break stop code", hexDecode("9f9f9f9f9fffffffff"), "unexpected EOF", false}, {"Indefinite length maps and arrays not closed by a break stop code", hexDecode("9f819f819f9fffffff"), "unexpected EOF", false}, // Five subkinds of well-formedness error kind 3 (syntax error) {"Reserved additional information values", hexDecode("3e"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("5c"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("5d"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("5e"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("7c"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("7d"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("7e"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("9c"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("9d"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("9e"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("bc"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("bd"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("be"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("dc"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("dd"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("de"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("fc"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("fd"), "cbor: invalid additional information", true}, {"Reserved additional information values", hexDecode("fe"), "cbor: invalid additional information", true}, {"Reserved two-byte encodings of simple types", hexDecode("f800"), "cbor: invalid simple value 0 for type primitives", true}, {"Reserved two-byte encodings of simple types", hexDecode("f801"), "cbor: invalid simple value 1 for type primitives", true}, {"Reserved two-byte encodings of simple types", hexDecode("f818"), "cbor: invalid simple value 24 for type primitives", true}, {"Reserved two-byte encodings of simple types", hexDecode("f81f"), "cbor: invalid simple value 31 for type primitives", true}, {"Indefinite length string chunks not of the correct type", hexDecode("5f00ff"), "cbor: wrong element type positive integer for indefinite-length byte string", false}, {"Indefinite length string chunks not of the correct type", hexDecode("5f21ff"), "cbor: wrong element type negative integer for indefinite-length byte string", false}, {"Indefinite length string chunks not of the correct type", hexDecode("5f6100ff"), "cbor: wrong element type UTF-8 text string for indefinite-length byte string", false}, {"Indefinite length string chunks not of the correct type", hexDecode("5f80ff"), "cbor: wrong element type array for indefinite-length byte string", false}, {"Indefinite length string chunks not of the correct type", hexDecode("5fa0ff"), "cbor: wrong element type map for indefinite-length byte string", false}, {"Indefinite length string chunks not of the correct type", hexDecode("5fc000ff"), "cbor: wrong element type tag for indefinite-length byte string", false}, {"Indefinite length string chunks not of the correct type", hexDecode("5fe0ff"), "cbor: wrong element type primitives for indefinite-length byte string", false}, {"Indefinite length string chunks not of the correct type", hexDecode("7f4100ff"), "cbor: wrong element type byte string for indefinite-length UTF-8 text string", false}, {"Indefinite length string chunks not definite length", hexDecode("5f5f4100ffff"), "cbor: indefinite-length byte string chunk is not definite-length", false}, {"Indefinite length string chunks not definite length", hexDecode("7f7f6100ffff"), "cbor: indefinite-length UTF-8 text string chunk is not definite-length", false}, {"Break occurring on its own outside of an indefinite length item", hexDecode("ff"), "cbor: unexpected \"break\" code", true}, {"Break occurring in a definite length array or map or a tag", hexDecode("81ff"), "cbor: unexpected \"break\" code", true}, {"Break occurring in a definite length array or map or a tag", hexDecode("8200ff"), "cbor: unexpected \"break\" code", true}, {"Break occurring in a definite length array or map or a tag", hexDecode("a1ff"), "cbor: unexpected \"break\" code", true}, {"Break occurring in a definite length array or map or a tag", hexDecode("a1ff00"), "cbor: unexpected \"break\" code", true}, {"Break occurring in a definite length array or map or a tag", hexDecode("a100ff"), "cbor: unexpected \"break\" code", true}, {"Break occurring in a definite length array or map or a tag", hexDecode("a20000ff"), "cbor: unexpected \"break\" code", true}, {"Break occurring in a definite length array or map or a tag", hexDecode("9f81ff"), "cbor: unexpected \"break\" code", true}, {"Break occurring in a definite length array or map or a tag", hexDecode("9f829f819f9fffffffff"), "cbor: unexpected \"break\" code", true}, {"Break in indefinite length map would lead to odd number of items (break in a value position)", hexDecode("bf00ff"), "cbor: unexpected \"break\" code", true}, {"Break in indefinite length map would lead to odd number of items (break in a value position)", hexDecode("bf000000ff"), "cbor: unexpected \"break\" code", true}, {"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}, } func TestInvalidCBORUnmarshal(t *testing.T) { for _, tc := range invalidCBORUnmarshalTests { t.Run(tc.name, func(t *testing.T) { var i interface{} err := Unmarshal(tc.cborData, &i) if err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", tc.cborData) } else if !tc.errorMsgPartialMatch && err.Error() != tc.wantErrorMsg { t.Errorf("Unmarshal(0x%x) error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) } else if tc.errorMsgPartialMatch && !strings.Contains(err.Error(), tc.wantErrorMsg) { t.Errorf("Unmarshal(0x%x) error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) } }) } } func TestInvalidUTF8TextString(t *testing.T) { invalidUTF8TextStringTests := []struct { name string cborData []byte wantErrorMsg string }{ {"definite length text string", hexDecode("61fe"), invalidUTF8ErrorMsg}, {"indefinite length text string", hexDecode("7f62e6b061b4ff"), invalidUTF8ErrorMsg}, } for _, tc := range invalidUTF8TextStringTests { 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) } 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) } }) } // Test decoding of mixed invalid text string and valid text string cborData := hexDecode("7f62e6b061b4ff7f657374726561646d696e67ff") dec := NewDecoder(bytes.NewReader(cborData)) var s string if err := dec.Decode(&s); err == nil { t.Errorf("Decode() didn't return an error") } else if s != "" { t.Errorf("Decode() returned %q, want %q", s, "") } 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) { want := outer{ IntField: 123, FloatField: 100000.0, BoolField: true, StringField: "test", ByteStringField: []byte{1, 3, 5}, ArrayField: []string{"hello", "world"}, MapField: map[string]bool{"morning": true, "afternoon": false}, NestedStructField: &inner{X: 1000, Y: 1000000}, unexportedField: 0, } tests := []struct { name string cborData []byte want interface{} }{ {"case-insensitive field name match", hexDecode("a868696e746669656c64187b6a666c6f61746669656c64fa47c3500069626f6f6c6669656c64f56b537472696e674669656c6464746573746f42797465537472696e674669656c64430103056a41727261794669656c64826568656c6c6f65776f726c64684d61704669656c64a2676d6f726e696e67f56961667465726e6f6f6ef4714e65737465645374727563744669656c64a261581903e861591a000f4240"), want}, {"exact field name match", hexDecode("a868496e744669656c64187b6a466c6f61744669656c64fa47c3500069426f6f6c4669656c64f56b537472696e674669656c6464746573746f42797465537472696e674669656c64430103056a41727261794669656c64826568656c6c6f65776f726c64684d61704669656c64a2676d6f726e696e67f56961667465726e6f6f6ef4714e65737465645374727563744669656c64a261581903e861591a000f4240"), want}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { var v outer if err := Unmarshal(tc.cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } else if !reflect.DeepEqual(v, want) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v, v, want, want) } }) } } func TestUnmarshalStructError1(t *testing.T) { type outer2 struct { IntField int FloatField float32 BoolField bool StringField string ByteStringField []byte ArrayField []int // wrong type MapField map[string]bool NestedStructField map[int]string // wrong type unexportedField int64 } want := outer2{ IntField: 123, FloatField: 100000.0, BoolField: true, StringField: "test", ByteStringField: []byte{1, 3, 5}, ArrayField: []int{0, 0}, MapField: map[string]bool{"morning": true, "afternoon": false}, NestedStructField: map[int]string{}, unexportedField: 0, } cborData := hexDecode("a868496e744669656c64187b6a466c6f61744669656c64fa47c3500069426f6f6c4669656c64f56b537472696e674669656c6464746573746f42797465537472696e674669656c64430103056a41727261794669656c64826568656c6c6f65776f726c64684d61704669656c64a2676d6f726e696e67f56961667465726e6f6f6ef4714e65737465645374727563744669656c64a261581903e861591a000f4240") wantCBORType := "UTF-8 text string" wantGoType := "int" wantStructFieldName := "cbor.outer2.ArrayField" wantErrorMsg := "cannot unmarshal UTF-8 text string into Go struct field cbor.outer2.ArrayField of type int" var v outer2 if err := Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else { if typeError, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*UnmarshalTypeError)", cborData, err) } else { if typeError.CBORType != wantCBORType { t.Errorf("Unmarshal(0x%x) returned (*UnmarshalTypeError).CBORType %s, want %s", cborData, typeError.CBORType, wantCBORType) } if typeError.GoType != wantGoType { t.Errorf("Unmarshal(0x%x) returned (*UnmarshalTypeError).GoType %s, want %s", cborData, typeError.GoType, wantGoType) } if typeError.StructFieldName != wantStructFieldName { t.Errorf("Unmarshal(0x%x) returned (*UnmarshalTypeError).StructFieldName %s, want %s", cborData, typeError.StructFieldName, wantStructFieldName) } if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } } } if !reflect.DeepEqual(v, want) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, want, want) } } func TestUnmarshalStructError2(t *testing.T) { // Unmarshal integer and invalid UTF8 string as field name into struct type strc struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` } want := strc{ A: "A", } // Unmarshal returns first error encountered, which is *UnmarshalTypeError (failed to unmarshal int into Go string) cborData := hexDecode("a3fa47c35000026161614161fe6142") // {100000.0:2, "a":"A", 0xfe: B} wantCBORType := "primitives" wantGoType := "string" wantErrorMsg := "cannot unmarshal primitives into Go value of type string" v := strc{} if err := Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else { if typeError, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*UnmarshalTypeError)", cborData, err) } else { if typeError.CBORType != wantCBORType { t.Errorf("Unmarshal(0x%x) returned (*UnmarshalTypeError).CBORType %s, want %s", cborData, typeError.CBORType, wantCBORType) } if typeError.GoType != wantGoType { t.Errorf("Unmarshal(0x%x) returned (*UnmarshalTypeError).GoType %s, want %s", cborData, typeError.GoType, wantGoType) } if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } } } if !reflect.DeepEqual(v, want) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, want, want) } // Unmarshal returns first error encountered, which is *cbor.SemanticError (invalid UTF8 string) cborData = hexDecode("a361fe6142010261616141") // {0xfe: B, 1:2, "a":"A"} v = strc{} if err := Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else { if _, ok := err.(*SemanticError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*SemanticError)", cborData, err) } else if err.Error() != invalidUTF8ErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want error %q", cborData, err.Error(), invalidUTF8ErrorMsg) } } if !reflect.DeepEqual(v, want) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, want, want) } // Unmarshal returns first error encountered, which is *cbor.SemanticError (invalid UTF8 string) cborData = hexDecode("a3616261fe010261616141") // {"b": 0xfe, 1:2, "a":"A"} v = strc{} if err := Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else { if _, ok := err.(*SemanticError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*SemanticError)", cborData, err) } else if err.Error() != invalidUTF8ErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want error %q", cborData, err.Error(), invalidUTF8ErrorMsg) } } if !reflect.DeepEqual(v, want) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, want, want) } } func TestUnmarshalPrefilledArray(t *testing.T) { prefilledArr := []int{1, 2, 3, 4, 5} want := []int{10, 11, 3, 4, 5} cborData := hexDecode("820a0b") // []int{10, 11} if err := Unmarshal(cborData, &prefilledArr); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if len(prefilledArr) != 2 || cap(prefilledArr) != 5 { t.Errorf("Unmarshal(0x%x) = %v (len %d, cap %d), want len == 2, cap == 5", cborData, prefilledArr, len(prefilledArr), cap(prefilledArr)) } if !reflect.DeepEqual(prefilledArr[:cap(prefilledArr)], want) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, prefilledArr, prefilledArr, want, want) } cborData = hexDecode("80") // empty array if err := Unmarshal(cborData, &prefilledArr); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if len(prefilledArr) != 0 || cap(prefilledArr) != 0 { t.Errorf("Unmarshal(0x%x) = %v (len %d, cap %d), want len == 0, cap == 0", cborData, prefilledArr, len(prefilledArr), cap(prefilledArr)) } } func TestUnmarshalPrefilledMap(t *testing.T) { prefilledMap := map[string]string{"key": "value", "a": "1"} want := map[string]string{"key": "value", "a": "A", "b": "B", "c": "C", "d": "D", "e": "E"} cborData := hexDecode("a56161614161626142616361436164614461656145") // map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"} if err := Unmarshal(cborData, &prefilledMap); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(prefilledMap, want) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, prefilledMap, prefilledMap, want, want) } prefilledMap = map[string]string{"key": "value"} want = map[string]string{"key": "value"} cborData = hexDecode("a0") // map[string]string{} if err := Unmarshal(cborData, &prefilledMap); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(prefilledMap, want) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, prefilledMap, prefilledMap, want, want) } } func TestUnmarshalPrefilledStruct(t *testing.T) { type s struct { a int B []int C bool } prefilledStruct := s{a: 100, B: []int{200, 300, 400, 500}, C: true} want := s{a: 100, B: []int{2, 3}, C: true} cborData := hexDecode("a26161016162820203") // map[string]interface{} {"a": 1, "b": []int{2, 3}} if err := Unmarshal(cborData, &prefilledStruct); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(prefilledStruct, want) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, prefilledStruct, prefilledStruct, want, want) } if len(prefilledStruct.B) != 2 || cap(prefilledStruct.B) != 4 { t.Errorf("Unmarshal(0x%x) = %v (len %d, cap %d), want len == 2, cap == 5", cborData, prefilledStruct.B, len(prefilledStruct.B), cap(prefilledStruct.B)) } if !reflect.DeepEqual(prefilledStruct.B[:cap(prefilledStruct.B)], []int{2, 3, 400, 500}) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, prefilledStruct.B, prefilledStruct.B, []int{2, 3, 400, 500}, []int{2, 3, 400, 500}) } } func TestStructFieldNil(t *testing.T) { type TestStruct struct { I int PI *int PPI **int } var struc TestStruct cborData, err := Marshal(struc) if err != nil { t.Fatalf("Marshal(%+v) returned error %v", struc, err) } var struc2 TestStruct err = Unmarshal(cborData, &struc2) if err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if !reflect.DeepEqual(struc, struc2) { t.Errorf("Unmarshal(0x%x) returned %+v, want %+v", cborData, struc2, struc) } } func TestLengthOverflowsInt(t *testing.T) { // Data is generating by go-fuzz. // string/slice/map length in uint64 cast to int causes integer overflow. cborData := [][]byte{ hexDecode("bbcf30303030303030cfd697829782"), hexDecode("5bcf30303030303030cfd697829782"), } wantErrorMsg := "is too large" for _, data := range cborData { var intf interface{} if err := Unmarshal(data, &intf); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error containing substring %q", data, wantErrorMsg) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing substring %q", data, err.Error(), wantErrorMsg) } } } func TestMapKeyUnhashable(t *testing.T) { testCases := []struct { name string 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} } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var v interface{} if err := Unmarshal(tc.cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", tc.cborData, tc.wantErrorMsg) } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) } if _, ok := v.(map[interface{}]interface{}); ok { var v map[interface{}]interface{} if err := Unmarshal(tc.cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", tc.cborData, tc.wantErrorMsg) } 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 TestMapKeyNaN(t *testing.T) { // Data is generating by go-fuzz. cborData := hexDecode("b0303030303030303030303030303030303038303030faffff30303030303030303030303030") // {-17: -17, NaN: -17} var intf interface{} if err := Unmarshal(cborData, &intf); err != nil { t.Fatalf("Unmarshal(0x%x) returned error %v", cborData, err) } em, err := EncOptions{Sort: SortCanonical}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } if _, err := em.Marshal(intf); err != nil { t.Errorf("Marshal(%v) returned error %v", intf, err) } } func TestUnmarshalUndefinedElement(t *testing.T) { // Data is generating by go-fuzz. cborData := hexDecode("bfd1a388f730303030303030303030303030ff") // {17({[undefined, -17, -17, -17, -17, -17, -17, -17]: -17, -17: -17}): -17} var intf interface{} wantErrorMsg := "invalid map key type" if err := Unmarshal(cborData, &intf); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error containing substring %q", cborData, wantErrorMsg) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing substring %q", cborData, err.Error(), wantErrorMsg) } } func TestMapKeyNil(t *testing.T) { testData := [][]byte{ hexDecode("a1f630"), // {null: -17} } want := map[interface{}]interface{}{nil: int64(-17)} for _, data := range testData { var intf interface{} if err := Unmarshal(data, &intf); err != nil { t.Fatalf("Unmarshal(0x%x) returned error %v", data, err) } else if !reflect.DeepEqual(intf, want) { t.Errorf("Unmarshal(0x%x) returned %+v, want %+v", data, intf, want) } if _, err := Marshal(intf); err != nil { t.Errorf("Marshal(%v) returned error %v", intf, err) } var v map[interface{}]interface{} if err := Unmarshal(data, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", data, err) } else if !reflect.DeepEqual(v, want) { t.Errorf("Unmarshal(0x%x) returned %+v, want %+v", data, v, want) } if _, err := Marshal(v); err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } } } func TestDecodeTime(t *testing.T) { testCases := []struct { name string cborRFC3339Time []byte cborUnixTime []byte wantTime time.Time }{ // Decoding CBOR null/defined to time.Time is no-op. See TestUnmarshalNil. { name: "NaN", cborRFC3339Time: hexDecode("f97e00"), cborUnixTime: hexDecode("f97e00"), wantTime: time.Time{}, }, { name: "positive infinity", cborRFC3339Time: hexDecode("f97c00"), cborUnixTime: hexDecode("f97c00"), wantTime: time.Time{}, }, { name: "negative infinity", cborRFC3339Time: hexDecode("f9fc00"), cborUnixTime: hexDecode("f9fc00"), wantTime: time.Time{}, }, { name: "time without fractional seconds", // positive integer cborRFC3339Time: hexDecode("74323031332d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("1a514b67b0"), wantTime: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"), }, { name: "time with fractional seconds", // float cborRFC3339Time: hexDecode("7819313937302d30312d30315432313a34363a34302d30363a3030"), cborUnixTime: hexDecode("fa47c35000"), wantTime: parseTime(time.RFC3339Nano, "1970-01-01T21:46:40-06:00"), }, { name: "time with fractional seconds", // float cborRFC3339Time: hexDecode("76323031332d30332d32315432303a30343a30302e355a"), cborUnixTime: hexDecode("fb41d452d9ec200000"), wantTime: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00.5Z"), }, { name: "time before January 1, 1970 UTC without fractional seconds", // negative integer cborRFC3339Time: hexDecode("74313936392d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("3a0177f2cf"), wantTime: parseTime(time.RFC3339Nano, "1969-03-21T20:04:00Z"), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tm := time.Now() if err := Unmarshal(tc.cborRFC3339Time, &tm); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborRFC3339Time, err) } else if !tc.wantTime.Equal(tm) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborRFC3339Time, tm, tm, tc.wantTime, tc.wantTime) } tm = time.Now() if err := Unmarshal(tc.cborUnixTime, &tm); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborUnixTime, err) } else if !tc.wantTime.Equal(tm) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborUnixTime, tm, tm, tc.wantTime, tc.wantTime) } }) } } func TestDecodeTimeWithTag(t *testing.T) { testCases := []struct { name string cborRFC3339Time []byte cborUnixTime []byte wantTime time.Time }{ { name: "time without fractional seconds", // positive integer cborRFC3339Time: hexDecode("c074323031332d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("c11a514b67b0"), wantTime: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"), }, { name: "time with fractional seconds", // float cborRFC3339Time: hexDecode("c076323031332d30332d32315432303a30343a30302e355a"), cborUnixTime: hexDecode("c1fb41d452d9ec200000"), wantTime: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00.5Z"), }, { name: "time before January 1, 1970 UTC without fractional seconds", // negative integer cborRFC3339Time: hexDecode("c074313936392d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("c13a0177f2cf"), wantTime: parseTime(time.RFC3339Nano, "1969-03-21T20:04:00Z"), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tm := time.Now() if err := Unmarshal(tc.cborRFC3339Time, &tm); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborRFC3339Time, err) } else if !tc.wantTime.Equal(tm) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborRFC3339Time, tm, tm, tc.wantTime, tc.wantTime) } tm = time.Now() if err := Unmarshal(tc.cborUnixTime, &tm); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborUnixTime, err) } else if !tc.wantTime.Equal(tm) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborUnixTime, tm, tm, tc.wantTime, tc.wantTime) } var v interface{} if err := Unmarshal(tc.cborRFC3339Time, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborRFC3339Time, err) } else if tm, ok := v.(time.Time); !ok || !tc.wantTime.Equal(tm) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborRFC3339Time, v, v, tc.wantTime, tc.wantTime) } v = nil if err := Unmarshal(tc.cborUnixTime, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborUnixTime, err) } else if tm, ok := v.(time.Time); !ok || !tc.wantTime.Equal(tm) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborUnixTime, v, v, tc.wantTime, tc.wantTime) } }) } } func TestDecodeTimeError(t *testing.T) { testCases := []struct { name string cborData []byte wantErrorMsg string }{ { name: "invalid RFC3339 time string", cborData: hexDecode("7f657374726561646d696e67ff"), wantErrorMsg: "cbor: cannot set streaming for time.Time", }, { name: "byte string data cannot be decoded into time.Time", cborData: hexDecode("4f013030303030303030e03031ed3030"), wantErrorMsg: "cbor: cannot unmarshal byte string into Go value of type time.Time", }, { name: "bool cannot be decoded into time.Time", cborData: hexDecode("f4"), wantErrorMsg: "cbor: cannot unmarshal primitives into Go value of type time.Time", }, { name: "invalid UTF-8 string", cborData: hexDecode("7f62e6b061b4ff"), wantErrorMsg: "cbor: invalid UTF-8 string", }, { name: "negative integer overflow", cborData: hexDecode("3bffffffffffffffff"), wantErrorMsg: "cbor: cannot unmarshal negative integer into Go value of type time.Time", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tm := time.Now() if err := Unmarshal(tc.cborData, &tm); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error msg %q", tc.cborData, tc.wantErrorMsg) } 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 TestDecodeInvalidTagTime(t *testing.T) { typeTimeSlice := reflect.TypeOf([]time.Time{}) testCases := []struct { name string cborData []byte decodeToTypes []reflect.Type wantErrorMsg string }{ { name: "Tag 0 with invalid RFC3339 time string", cborData: hexDecode("c07f657374726561646d696e67ff"), decodeToTypes: []reflect.Type{typeIntf, typeTime}, wantErrorMsg: "cbor: cannot set streaming for time.Time", }, { name: "Tag 0 with invalid UTF-8 string", cborData: hexDecode("c07f62e6b061b4ff"), decodeToTypes: []reflect.Type{typeIntf, typeTime}, wantErrorMsg: "cbor: invalid UTF-8 string", }, { name: "Tag 0 with integer content", cborData: hexDecode("c01a514b67b0"), decodeToTypes: []reflect.Type{typeIntf, typeTime}, wantErrorMsg: "cbor: tag number 0 must be followed by text string, got positive integer", }, { name: "Tag 0 with byte string content", cborData: hexDecode("c04f013030303030303030e03031ed3030"), decodeToTypes: []reflect.Type{typeIntf, typeTime}, wantErrorMsg: "cbor: tag number 0 must be followed by text string, got byte string", }, { name: "Tag 0 with integer content as array element", cborData: hexDecode("81c01a514b67b0"), decodeToTypes: []reflect.Type{typeIntf, typeTimeSlice}, wantErrorMsg: "cbor: tag number 0 must be followed by text string, got positive integer", }, { name: "Tag 1 with negative integer overflow", cborData: hexDecode("c13bffffffffffffffff"), decodeToTypes: []reflect.Type{typeIntf, typeTime}, wantErrorMsg: "cbor: cannot unmarshal tag into Go value of type time.Time", }, { name: "Tag 1 with string content", cborData: hexDecode("c174323031332d30332d32315432303a30343a30305a"), decodeToTypes: []reflect.Type{typeIntf, typeTime}, wantErrorMsg: "cbor: tag number 1 must be followed by integer or floating-point number, got UTF-8 text string", }, { name: "Tag 1 with simple value", cborData: hexDecode("d801f6"), // 1(null) decodeToTypes: []reflect.Type{typeIntf, typeTime}, wantErrorMsg: "cbor: tag number 1 must be followed by integer or floating-point number, got primitive", }, { name: "Tag 1 with string content as array element", cborData: hexDecode("81c174323031332d30332d32315432303a30343a30305a"), decodeToTypes: []reflect.Type{typeIntf, typeTimeSlice}, wantErrorMsg: "cbor: tag number 1 must be followed by integer or floating-point number, got UTF-8 text string", }, } dm, _ := DecOptions{TimeTag: DecTagOptional}.DecMode() for _, tc := range testCases { for _, decodeToType := range tc.decodeToTypes { t.Run(tc.name+" decode to "+decodeToType.String(), func(t *testing.T) { v := reflect.New(decodeToType) if err := dm.Unmarshal(tc.cborData, v.Interface()); err == nil { t.Errorf("Unmarshal(0x%x) didn't return error, want error msg %q", tc.cborData, tc.wantErrorMsg) } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", tc.cborData, err, tc.wantErrorMsg) } }) } } } func TestDecodeTag0Error(t *testing.T) { cborData := hexDecode("c01a514b67b0") // 0(1363896240) wantErrorMsg := "cbor: tag number 0 must be followed by text string, got positive integer" timeTagIgnoredDM, _ := DecOptions{TimeTag: DecTagIgnored}.DecMode() timeTagOptionalDM, _ := DecOptions{TimeTag: DecTagOptional}.DecMode() timeTagRequiredDM, _ := DecOptions{TimeTag: DecTagRequired}.DecMode() testCases := []struct { name string dm DecMode }{ {name: "DecTagIgnored", dm: timeTagIgnoredDM}, {name: "DecTagOptional", dm: timeTagOptionalDM}, {name: "DecTagRequired", dm: timeTagRequiredDM}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Decode to interface{} var v interface{} if err := tc.dm.Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return error, want error msg %q", cborData, wantErrorMsg) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err, wantErrorMsg) } // Decode to time.Time var tm time.Time if err := tc.dm.Unmarshal(cborData, &tm); err == nil { t.Errorf("Unmarshal(0x%x) didn't return error, want error msg %q", cborData, wantErrorMsg) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err, wantErrorMsg) } // Decode to uint64 var ui uint64 if err := tc.dm.Unmarshal(cborData, &ui); err == nil { t.Errorf("Unmarshal(0x%x) didn't return error, want error msg %q", cborData, wantErrorMsg) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err, wantErrorMsg) } }) } } func TestDecodeTag1Error(t *testing.T) { cborData := hexDecode("c174323031332d30332d32315432303a30343a30305a") // 1("2013-03-21T20:04:00Z") wantErrorMsg := "cbor: tag number 1 must be followed by integer or floating-point number, got UTF-8 text string" timeTagIgnoredDM, _ := DecOptions{TimeTag: DecTagIgnored}.DecMode() timeTagOptionalDM, _ := DecOptions{TimeTag: DecTagOptional}.DecMode() timeTagRequiredDM, _ := DecOptions{TimeTag: DecTagRequired}.DecMode() testCases := []struct { name string dm DecMode }{ {name: "DecTagIgnored", dm: timeTagIgnoredDM}, {name: "DecTagOptional", dm: timeTagOptionalDM}, {name: "DecTagRequired", dm: timeTagRequiredDM}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Decode to interface{} var v interface{} if err := tc.dm.Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return error, want error msg %q", cborData, wantErrorMsg) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err, wantErrorMsg) } // Decode to time.Time var tm time.Time if err := tc.dm.Unmarshal(cborData, &tm); err == nil { t.Errorf("Unmarshal(0x%x) didn't return error, want error msg %q", cborData, wantErrorMsg) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err, wantErrorMsg) } // Decode to string var s string if err := tc.dm.Unmarshal(cborData, &s); err == nil { t.Errorf("Unmarshal(0x%x) didn't return error, want error msg %q", cborData, wantErrorMsg) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err, wantErrorMsg) } }) } } func TestDecodeTimeStreaming(t *testing.T) { // Decoder decodes from mixed invalid and valid time. testCases := []struct { cborData []byte wantErrorMsg string wantObj time.Time }{ { cborData: hexDecode("c07f62e6b061b4ff"), wantErrorMsg: "cbor: invalid UTF-8 string", }, { cborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"), wantObj: time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC), }, { cborData: hexDecode("c01a514b67b0"), wantErrorMsg: "cbor: tag number 0 must be followed by text string, got positive integer", }, { cborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"), wantObj: time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC), }, { cborData: hexDecode("c13bffffffffffffffff"), wantErrorMsg: "cbor: cannot unmarshal tag into Go value of type time.Time", }, { cborData: hexDecode("c11a514b67b0"), wantObj: time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC), }, { cborData: hexDecode("c174323031332d30332d32315432303a30343a30305a"), wantErrorMsg: "tag number 1 must be followed by integer or floating-point number, got UTF-8 text string", }, { cborData: hexDecode("c11a514b67b0"), wantObj: time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC), }, } // Data is a mixed stream of valid and invalid time data var cborData []byte for _, tc := range testCases { cborData = append(cborData, tc.cborData...) } dm, _ := DecOptions{TimeTag: DecTagOptional}.DecMode() dec := dm.NewDecoder(bytes.NewReader(cborData)) for _, tc := range testCases { var v interface{} err := dec.Decode(&v) if tc.wantErrorMsg != "" { if err == nil { t.Errorf("Unmarshal(0x%x) didn't return error, want error msg %q", tc.cborData, tc.wantErrorMsg) } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error msg %q, want %q", tc.cborData, err, tc.wantErrorMsg) } } else { tm, ok := v.(time.Time) if !ok { t.Errorf("Unmarshal(0x%x) returned %s (%T), want time.Time", tc.cborData, v, v) } if !tc.wantObj.Equal(tm) { t.Errorf("Unmarshal(0x%x) returned %s, want %s", tc.cborData, tm, tc.wantObj) } } } dec = dm.NewDecoder(bytes.NewReader(cborData)) for _, tc := range testCases { var tm time.Time err := dec.Decode(&tm) if tc.wantErrorMsg != "" { if err == nil { t.Errorf("Unmarshal(0x%x) did't return error, want error msg %q", tc.cborData, tc.wantErrorMsg) } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error msg %q, want %q", tc.cborData, err, tc.wantErrorMsg) } } else { if !tc.wantObj.Equal(tm) { t.Errorf("Unmarshal(0x%x) returned %s, want %s", tc.cborData, tm, tc.wantObj) } } } } func TestDecTimeTagOption(t *testing.T) { timeTagIgnoredDecMode, _ := DecOptions{TimeTag: DecTagIgnored}.DecMode() timeTagOptionalDecMode, _ := DecOptions{TimeTag: DecTagOptional}.DecMode() timeTagRequiredDecMode, _ := DecOptions{TimeTag: DecTagRequired}.DecMode() testCases := []struct { name string cborRFC3339Time []byte cborUnixTime []byte decMode DecMode wantTime time.Time wantErrorMsg string }{ // not-tagged time CBOR data { name: "not-tagged data with DecTagIgnored option", cborRFC3339Time: hexDecode("74323031332d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("1a514b67b0"), decMode: timeTagIgnoredDecMode, wantTime: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"), }, { name: "not-tagged data with timeTagOptionalDecMode option", cborRFC3339Time: hexDecode("74323031332d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("1a514b67b0"), decMode: timeTagOptionalDecMode, wantTime: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"), }, { name: "not-tagged data with timeTagRequiredDecMode option", cborRFC3339Time: hexDecode("74323031332d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("1a514b67b0"), decMode: timeTagRequiredDecMode, wantErrorMsg: "expect CBOR tag value", }, // tagged time CBOR data { name: "tagged data with timeTagIgnoredDecMode option", cborRFC3339Time: hexDecode("c074323031332d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("c11a514b67b0"), decMode: timeTagIgnoredDecMode, wantTime: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"), }, { name: "tagged data with timeTagOptionalDecMode option", cborRFC3339Time: hexDecode("c074323031332d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("c11a514b67b0"), decMode: timeTagOptionalDecMode, wantTime: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"), }, { name: "tagged data with timeTagRequiredDecMode option", cborRFC3339Time: hexDecode("c074323031332d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("c11a514b67b0"), decMode: timeTagRequiredDecMode, wantTime: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"), }, // mis-tagged time CBOR data { name: "mis-tagged data with timeTagIgnoredDecMode option", cborRFC3339Time: hexDecode("c8c974323031332d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("c8c91a514b67b0"), decMode: timeTagIgnoredDecMode, wantTime: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"), }, { name: "mis-tagged data with timeTagOptionalDecMode option", cborRFC3339Time: hexDecode("c8c974323031332d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("c8c91a514b67b0"), decMode: timeTagOptionalDecMode, wantErrorMsg: "cbor: wrong tag number for time.Time, got 8, expect 0 or 1", }, { name: "mis-tagged data with timeTagRequiredDecMode option", cborRFC3339Time: hexDecode("c8c974323031332d30332d32315432303a30343a30305a"), cborUnixTime: hexDecode("c8c91a514b67b0"), decMode: timeTagRequiredDecMode, wantErrorMsg: "cbor: wrong tag number for time.Time, got 8, expect 0 or 1", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tm := time.Now() err := tc.decMode.Unmarshal(tc.cborRFC3339Time, &tm) if tc.wantErrorMsg != "" { if err == nil { t.Errorf("Unmarshal(0x%x) didn't return error", tc.cborRFC3339Time) } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.cborRFC3339Time, err.Error(), tc.wantErrorMsg) } } else { if !tc.wantTime.Equal(tm) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborRFC3339Time, tm, tm, tc.wantTime, tc.wantTime) } } tm = time.Now() err = tc.decMode.Unmarshal(tc.cborUnixTime, &tm) if tc.wantErrorMsg != "" { if err == nil { t.Errorf("Unmarshal(0x%x) didn't return error", tc.cborRFC3339Time) } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.cborRFC3339Time, err.Error(), tc.wantErrorMsg) } } else { if !tc.wantTime.Equal(tm) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborRFC3339Time, tm, tm, tc.wantTime, tc.wantTime) } } }) } } func TestUnmarshalStructTag1(t *testing.T) { type strc struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` } want := strc{ A: "A", B: "B", C: "C", } cborData := hexDecode("a3616161416162614261636143") // {"a":"A", "b":"B", "c":"C"} var v strc if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(v, want) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, want, want) } } func TestUnmarshalStructTag2(t *testing.T) { type strc struct { A string `json:"a"` B string `json:"b"` C string `json:"c"` } want := strc{ A: "A", B: "B", C: "C", } cborData := hexDecode("a3616161416162614261636143") // {"a":"A", "b":"B", "c":"C"} var v strc if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(v, want) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, want, want) } } func TestUnmarshalStructTag3(t *testing.T) { type strc struct { A string `json:"x" cbor:"a"` B string `json:"y" cbor:"b"` C string `json:"z"` } want := strc{ A: "A", B: "B", C: "C", } cborData := hexDecode("a36161614161626142617a6143") // {"a":"A", "b":"B", "z":"C"} var v strc if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(v, want) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, v, v, want, want) } } func TestUnmarshalStructTag4(t *testing.T) { type strc struct { A string `json:"x" cbor:"a"` B string `json:"y" cbor:"b"` C string `json:"-"` } want := strc{ A: "A", B: "B", } cborData := hexDecode("a3616161416162614261636143") // {"a":"A", "b":"B", "c":"C"} var v strc if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(v, want) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, v, v, want, want) } } type number uint64 func (n number) MarshalBinary() (data []byte, err error) { if n == 0 { return []byte{}, nil } data = make([]byte, 8) binary.BigEndian.PutUint64(data, uint64(n)) return } func (n *number) UnmarshalBinary(data []byte) (err error) { if len(data) == 0 { *n = 0 return nil } if len(data) != 8 { return errors.New("number:UnmarshalBinary: invalid length") } *n = number(binary.BigEndian.Uint64(data)) return } type stru struct { a, b, c string } func (s *stru) MarshalBinary() ([]byte, error) { if s.a == "" && s.b == "" && s.c == "" { return []byte{}, nil } return []byte(fmt.Sprintf("%s,%s,%s", s.a, s.b, s.c)), nil } func (s *stru) UnmarshalBinary(data []byte) (err error) { if len(data) == 0 { s.a, s.b, s.c = "", "", "" return nil } ss := strings.Split(string(data), ",") if len(ss) != 3 { return errors.New("stru:UnmarshalBinary: invalid element count") } s.a, s.b, s.c = ss[0], ss[1], ss[2] return } type marshalBinaryError string func (n marshalBinaryError) MarshalBinary() (data []byte, err error) { return nil, errors.New(string(n)) } func TestBinaryMarshalerUnmarshaler(t *testing.T) { testCases := []roundTripTest{ { name: "primitive obj", obj: number(1234567890), wantCborData: hexDecode("4800000000499602d2"), }, { name: "struct obj", obj: stru{a: "a", b: "b", c: "c"}, wantCborData: hexDecode("45612C622C63"), }, } em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, testCases, em, dm) } func TestBinaryUnmarshalerError(t *testing.T) { //nolint:dupl testCases := []struct { name string typ reflect.Type cborData []byte wantErrorMsg string }{ { name: "primitive type", typ: reflect.TypeOf(number(0)), cborData: hexDecode("44499602d2"), wantErrorMsg: "number:UnmarshalBinary: invalid length", }, { name: "struct type", typ: reflect.TypeOf(stru{}), cborData: hexDecode("47612C622C632C64"), wantErrorMsg: "stru:UnmarshalBinary: invalid element count", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v := reflect.New(tc.typ) if err := Unmarshal(tc.cborData, v.Interface()); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error msg %q", tc.cborData, tc.wantErrorMsg) } else if err.Error() != tc.wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) } }) } } func TestBinaryMarshalerError(t *testing.T) { wantErrorMsg := "MarshalBinary: error" v := marshalBinaryError(wantErrorMsg) if _, err := Marshal(v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error msg %q", v, wantErrorMsg) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", v, err.Error(), wantErrorMsg) } } type number2 uint64 func (n number2) MarshalCBOR() (data []byte, err error) { m := map[string]uint64{"num": uint64(n)} return Marshal(m) } func (n *number2) UnmarshalCBOR(data []byte) (err error) { var v map[string]uint64 if err := Unmarshal(data, &v); err != nil { return err } *n = number2(v["num"]) return nil } type stru2 struct { a, b, c string } func (s *stru2) MarshalCBOR() ([]byte, error) { v := []string{s.a, s.b, s.c} return Marshal(v) } func (s *stru2) UnmarshalCBOR(data []byte) (err error) { var v []string if err := Unmarshal(data, &v); err != nil { return err } if len(v) > 0 { s.a = v[0] } if len(v) > 1 { s.b = v[1] } if len(v) > 2 { s.c = v[2] } return nil } type marshalCBORError string func (n marshalCBORError) MarshalCBOR() (data []byte, err error) { return nil, errors.New(string(n)) } func TestMarshalerUnmarshaler(t *testing.T) { testCases := []roundTripTest{ { name: "primitive obj", obj: number2(1), wantCborData: hexDecode("a1636e756d01"), }, { name: "struct obj", obj: stru2{a: "a", b: "b", c: "c"}, wantCborData: hexDecode("83616161626163"), }, } em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, testCases, em, dm) } func TestUnmarshalerError(t *testing.T) { //nolint:dupl testCases := []struct { name string typ reflect.Type cborData []byte wantErrorMsg string }{ { name: "primitive type", typ: reflect.TypeOf(number2(0)), cborData: hexDecode("44499602d2"), wantErrorMsg: "cbor: cannot unmarshal byte string into Go value of type map[string]uint64", }, { name: "struct type", typ: reflect.TypeOf(stru2{}), cborData: hexDecode("47612C622C632C64"), wantErrorMsg: "cbor: cannot unmarshal byte string into Go value of type []string", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v := reflect.New(tc.typ) if err := Unmarshal(tc.cborData, v.Interface()); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error msg %q", tc.cborData, tc.wantErrorMsg) } else if err.Error() != tc.wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) } }) } } func TestMarshalerError(t *testing.T) { wantErrorMsg := "MarshalCBOR: error" v := marshalCBORError(wantErrorMsg) if _, err := Marshal(v); err == nil { t.Errorf("Marshal(%+v) didn't return an error, want error msg %q", v, wantErrorMsg) } else if err.Error() != wantErrorMsg { t.Errorf("Marshal(%+v) returned error %q, want %q", v, err.Error(), wantErrorMsg) } } // Found at https://github.com/oasislabs/oasis-core/blob/master/go/common/cbor/cbor_test.go func TestOutOfMem1(t *testing.T) { cborData := []byte("\x9b\x00\x00000000") var f []byte if err := Unmarshal(cborData, &f); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } } // Found at https://github.com/oasislabs/oasis-core/blob/master/go/common/cbor/cbor_test.go func TestOutOfMem2(t *testing.T) { cborData := []byte("\x9b\x00\x00\x81112233") var f []byte if err := Unmarshal(cborData, &f); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } } // Found at https://github.com/cose-wg/Examples/tree/master/RFC8152 func TestCOSEExamples(t *testing.T) { cborData := [][]byte{ hexDecode("D8608443A10101A1054C02D1F7E6F26C43D4868D87CE582464F84D913BA60A76070A9A48F26E97E863E2852948658F0811139868826E89218A75715B818440A101225818DBD43C4E9D719C27C6275C67D628D493F090593DB8218F11818344A1013818A220A401022001215820B2ADD44368EA6D641F9CA9AF308B4079AEB519F11E9B8A55A600B21233E86E6822F40458246D65726961646F632E6272616E64796275636B406275636B6C616E642E6578616D706C6540"), hexDecode("D8628440A054546869732069732074686520636F6E74656E742E818343A10126A1044231315840E2AEAFD40D69D19DFE6E52077C5D7FF4E408282CBEFB5D06CBF414AF2E19D982AC45AC98B8544C908B4507DE1E90B717C3D34816FE926A2B98F53AFD2FA0F30A"), hexDecode("D8628440A054546869732069732074686520636F6E74656E742E828343A10126A1044231315840E2AEAFD40D69D19DFE6E52077C5D7FF4E408282CBEFB5D06CBF414AF2E19D982AC45AC98B8544C908B4507DE1E90B717C3D34816FE926A2B98F53AFD2FA0F30A8344A1013823A104581E62696C626F2E62616767696E7340686F626269746F6E2E6578616D706C65588400A2D28A7C2BDB1587877420F65ADF7D0B9A06635DD1DE64BB62974C863F0B160DD2163734034E6AC003B01E8705524C5C4CA479A952F0247EE8CB0B4FB7397BA08D009E0C8BF482270CC5771AA143966E5A469A09F613488030C5B07EC6D722E3835ADB5B2D8C44E95FFB13877DD2582866883535DE3BB03D01753F83AB87BB4F7A0297"), hexDecode("D8628440A1078343A10126A10442313158405AC05E289D5D0E1B0A7F048A5D2B643813DED50BC9E49220F4F7278F85F19D4A77D655C9D3B51E805A74B099E1E085AACD97FC29D72F887E8802BB6650CCEB2C54546869732069732074686520636F6E74656E742E818343A10126A1044231315840E2AEAFD40D69D19DFE6E52077C5D7FF4E408282CBEFB5D06CBF414AF2E19D982AC45AC98B8544C908B4507DE1E90B717C3D34816FE926A2B98F53AFD2FA0F30A"), hexDecode("D8628456A2687265736572766564F40281687265736572766564A054546869732069732074686520636F6E74656E742E818343A10126A10442313158403FC54702AA56E1B2CB20284294C9106A63F91BAC658D69351210A031D8FC7C5FF3E4BE39445B1A3E83E1510D1ACA2F2E8A7C081C7645042B18ABA9D1FAD1BD9C"), hexDecode("D28443A10126A10442313154546869732069732074686520636F6E74656E742E58408EB33E4CA31D1C465AB05AAC34CC6B23D58FEF5C083106C4D25A91AEF0B0117E2AF9A291AA32E14AB834DC56ED2A223444547E01F11D3B0916E5A4C345CACB36"), hexDecode("D8608443A10101A1054CC9CF4DF2FE6C632BF788641358247ADBE2709CA818FB415F1E5DF66F4E1A51053BA6D65A1A0C52A357DA7A644B8070A151B0818344A1013818A220A40102200121582098F50A4FF6C05861C8860D13A638EA56C3F5AD7590BBFBF054E1C7B4D91D628022F50458246D65726961646F632E6272616E64796275636B406275636B6C616E642E6578616D706C6540"), hexDecode("D8608443A1010AA1054D89F52F65A1C580933B5261A76C581C753548A19B1307084CA7B2056924ED95F2E3B17006DFE931B687B847818343A10129A2335061616262636364646565666667676868044A6F75722D73656372657440"), hexDecode("D8608443A10101A2054CC9CF4DF2FE6C632BF7886413078344A1013823A104581E62696C626F2E62616767696E7340686F626269746F6E2E6578616D706C65588400929663C8789BB28177AE28467E66377DA12302D7F9594D2999AFA5DFA531294F8896F2B6CDF1740014F4C7F1A358E3A6CF57F4ED6FB02FCF8F7AA989F5DFD07F0700A3A7D8F3C604BA70FA9411BD10C2591B483E1D2C31DE003183E434D8FBA18F17A4C7E3DFA003AC1CF3D30D44D2533C4989D3AC38C38B71481CC3430C9D65E7DDFF58247ADBE2709CA818FB415F1E5DF66F4E1A51053BA6D65A1A0C52A357DA7A644B8070A151B0818344A1013818A220A40102200121582098F50A4FF6C05861C8860D13A638EA56C3F5AD7590BBFBF054E1C7B4D91D628022F50458246D65726961646F632E6272616E64796275636B406275636B6C616E642E6578616D706C6540"), hexDecode("D8608443A10101A1054C02D1F7E6F26C43D4868D87CE582464F84D913BA60A76070A9A48F26E97E863E28529D8F5335E5F0165EEE976B4A5F6C6F09D818344A101381FA3225821706572656772696E2E746F6F6B407475636B626F726F7567682E6578616D706C650458246D65726961646F632E6272616E64796275636B406275636B6C616E642E6578616D706C6535420101581841E0D76F579DBD0D936A662D54D8582037DE2E366FDE1C62"), hexDecode("D08343A1010AA1054D89F52F65A1C580933B5261A78C581C5974E1B99A3A4CC09A659AA2E9E7FFF161D38CE71CB45CE460FFB569"), hexDecode("D08343A1010AA1064261A7581C252A8911D465C125B6764739700F0141ED09192DE139E053BD09ABCA"), hexDecode("D8618543A1010FA054546869732069732074686520636F6E74656E742E489E1226BA1F81B848818340A20125044A6F75722D73656372657440"), hexDecode("D8618543A10105A054546869732069732074686520636F6E74656E742E582081A03448ACD3D305376EAA11FB3FE416A955BE2CBE7EC96F012C994BC3F16A41818344A101381AA3225821706572656772696E2E746F6F6B407475636B626F726F7567682E6578616D706C650458246D65726961646F632E6272616E64796275636B406275636B6C616E642E6578616D706C653558404D8553E7E74F3C6A3A9DD3EF286A8195CBF8A23D19558CCFEC7D34B824F42D92BD06BD2C7F0271F0214E141FB779AE2856ABF585A58368B017E7F2A9E5CE4DB540"), hexDecode("D8618543A1010EA054546869732069732074686520636F6E74656E742E4836F5AFAF0BAB5D43818340A2012404582430313863306165352D346439622D343731622D626664362D6565663331346263373033375818711AB0DC2FC4585DCE27EFFA6781C8093EBA906F227B6EB0"), hexDecode("D8618543A10105A054546869732069732074686520636F6E74656E742E5820BF48235E809B5C42E995F2B7D5FA13620E7ED834E337F6AA43DF161E49E9323E828344A101381CA220A4010220032158420043B12669ACAC3FD27898FFBA0BCD2E6C366D53BC4DB71F909A759304ACFB5E18CDC7BA0B13FF8C7636271A6924B1AC63C02688075B55EF2D613574E7DC242F79C322F504581E62696C626F2E62616767696E7340686F626269746F6E2E6578616D706C655828339BC4F79984CDC6B3E6CE5F315A4C7D2B0AC466FCEA69E8C07DFBCA5BB1F661BC5F8E0DF9E3EFF58340A2012404582430313863306165352D346439622D343731622D626664362D65656633313462633730333758280B2C7CFCE04E98276342D6476A7723C090DFDD15F9A518E7736549E998370695E6D6A83B4AE507BB"), hexDecode("D18443A1010FA054546869732069732074686520636F6E74656E742E48726043745027214F"), } for _, d := range cborData { var v interface{} if err := Unmarshal(d, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", d, err) } } } func TestUnmarshalStructKeyAsIntError(t *testing.T) { type T1 struct { F1 int `cbor:"1,keyasint"` } cborData := hexDecode("a13bffffffffffffffff01") // {1: -18446744073709551616} var v T1 if err := Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), "cannot unmarshal") { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), "cannot unmarshal") } } func TestUnmarshalArrayToStruct(t *testing.T) { type T struct { _ struct{} `cbor:",toarray"` A int B int C int } testCases := []struct { name string cborData []byte }{ {"definite length array", hexDecode("83010203")}, {"indefinite length array", hexDecode("9f010203ff")}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var v T if err := Unmarshal(tc.cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } }) } } func TestUnmarshalArrayToStructNoToArrayOptionError(t *testing.T) { type T struct { A int B int C int } cborData := hexDecode("8301020383010203") var v1 T wantT := T{} dec := NewDecoder(bytes.NewReader(cborData)) if err := dec.Decode(&v1); err == nil { t.Errorf("Decode(%+v) didn't return an error", v1) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Decode(%+v) returned wrong error type %T, want (*UnmarshalTypeError)", v1, err) } else if !strings.Contains(err.Error(), "cannot unmarshal") { t.Errorf("Decode(%+v) returned error %q, want error containing %q", err.Error(), v1, "cannot unmarshal") } if !reflect.DeepEqual(v1, wantT) { t.Errorf("Decode() = %+v (%T), want %+v (%T)", v1, v1, wantT, wantT) } var v2 []int want := []int{1, 2, 3} if err := dec.Decode(&v2); err != nil { t.Errorf("Decode() returned error %v", err) } if !reflect.DeepEqual(v2, want) { t.Errorf("Decode() = %+v (%T), want %+v (%T)", v2, v2, want, want) } } func TestUnmarshalNonArrayDataToStructToArray(t *testing.T) { type T struct { _ struct{} `cbor:",toarray"` A int B int C int } testCases := []struct { name string cborData []byte }{ {"CBOR positive int", hexDecode("00")}, // 0 {"CBOR negative int", hexDecode("20")}, // -1 {"CBOR byte string", hexDecode("4401020304")}, // h`01020304` {"CBOR text string", hexDecode("7f657374726561646d696e67ff")}, // streaming {"CBOR map", hexDecode("a3614101614202614303")}, // {"A": 1, "B": 2, "C": 3} {"CBOR bool", hexDecode("f5")}, // true {"CBOR float", hexDecode("fa7f7fffff")}, // 3.4028234663852886e+38 } wantT := T{} wantErrorMsg := "cannot unmarshal" for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var v T if err := Unmarshal(tc.cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", tc.cborData) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", tc.cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v, wantT) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", tc.cborData, v, v, wantT, wantT) } }) } } func TestUnmarshalArrayToStructWrongSizeError(t *testing.T) { type T struct { _ struct{} `cbor:",toarray"` A int B int } cborData := hexDecode("8301020383010203") var v1 T wantT := T{} dec := NewDecoder(bytes.NewReader(cborData)) if err := dec.Decode(&v1); err == nil { t.Errorf("Decode(%+v) didn't return an error", v1) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Decode(%+v) returned wrong error type %T, want (*UnmarshalTypeError)", v1, err) } else if !strings.Contains(err.Error(), "cannot unmarshal") { t.Errorf("Decode(%+v) returned error %q, want error containing %q", v1, err.Error(), "cannot unmarshal") } if !reflect.DeepEqual(v1, wantT) { t.Errorf("Decode() = %+v (%T), want %+v (%T)", v1, v1, wantT, wantT) } var v2 []int want := []int{1, 2, 3} if err := dec.Decode(&v2); err != nil { t.Errorf("Decode() returned error %v", err) } if !reflect.DeepEqual(v2, want) { t.Errorf("Decode() = %+v (%T), want %+v (%T)", v2, v2, want, want) } } func TestUnmarshalArrayToStructWrongFieldTypeError(t *testing.T) { type T struct { _ struct{} `cbor:",toarray"` A int B string C int } testCases := []struct { name string cborData []byte wantErrorMsg string wantV interface{} }{ // [1, 2, 3] {"wrong field type", hexDecode("83010203"), "cannot unmarshal", T{A: 1, C: 3}}, // [1, 0xfe, 3] {"invalid UTF-8 string", hexDecode("830161fe03"), invalidUTF8ErrorMsg, T{A: 1, C: 3}}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var v T if err := Unmarshal(tc.cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", tc.cborData) } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.cborData, err.Error(), tc.wantErrorMsg) } if !reflect.DeepEqual(v, tc.wantV) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", tc.cborData, v, v, tc.wantV, tc.wantV) } }) } } func TestUnmarshalArrayToStructCannotSetEmbeddedPointerError(t *testing.T) { type ( s1 struct { x int //nolint:unused,structcheck X int } S2 struct { y int //nolint:unused,structcheck Y int } S struct { _ struct{} `cbor:",toarray"` *s1 *S2 } ) cborData := []byte{0x82, 0x02, 0x04} // [2, 4] const wantErrorMsg = "cannot set embedded pointer to unexported struct" wantV := S{S2: &S2{Y: 4}} var v S err := Unmarshal(cborData, &v) if err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error %q", cborData, wantErrorMsg) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v, wantV) { t.Errorf("Decode() = %+v (%T), want %+v (%T)", v, v, wantV, wantV) } } func TestUnmarshalIntoSliceError(t *testing.T) { cborData := []byte{0x83, 0x61, 0x61, 0x61, 0xfe, 0x61, 0x62} // ["a", 0xfe, "b"] wantErrorMsg := invalidUTF8ErrorMsg var want interface{} // Unmarshal CBOR array into Go empty interface. var v1 interface{} want = []interface{}{"a", interface{}(nil), "b"} if err := Unmarshal(cborData, &v1); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", cborData, wantErrorMsg) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v1, want) { t.Errorf("Unmarshal(0x%x) = %v, want %v", cborData, v1, want) } // Unmarshal CBOR array into Go slice. var v2 []string want = []string{"a", "", "b"} if err := Unmarshal(cborData, &v2); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", cborData, wantErrorMsg) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v2, want) { t.Errorf("Unmarshal(0x%x) = %v, want %v", cborData, v2, want) } // Unmarshal CBOR array into Go array. var v3 [3]string want = [3]string{"a", "", "b"} if err := Unmarshal(cborData, &v3); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", cborData, wantErrorMsg) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v3, want) { t.Errorf("Unmarshal(0x%x) = %v, want %v", cborData, v3, want) } // Unmarshal CBOR array into populated Go slice. v4 := []string{"hello", "to", "you"} want = []string{"a", "to", "b"} if err := Unmarshal(cborData, &v4); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", cborData, wantErrorMsg) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v4, want) { t.Errorf("Unmarshal(0x%x) = %v, want %v", cborData, v4, want) } } func TestUnmarshalIntoMapError(t *testing.T) { cborData := [][]byte{ {0xa3, 0x61, 0x61, 0x61, 0x41, 0x61, 0xfe, 0x61, 0x43, 0x61, 0x62, 0x61, 0x42}, // {"a":"A", 0xfe: "C", "b":"B"} {0xa3, 0x61, 0x61, 0x61, 0x41, 0x61, 0x63, 0x61, 0xfe, 0x61, 0x62, 0x61, 0x42}, // {"a":"A", "c": 0xfe, "b":"B"} } wantErrorMsg := invalidUTF8ErrorMsg var want interface{} for _, data := range cborData { // Unmarshal CBOR map into Go empty interface. var v1 interface{} want = map[interface{}]interface{}{"a": "A", "b": "B"} if err := Unmarshal(data, &v1); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", data, wantErrorMsg) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", data, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v1, want) { t.Errorf("Unmarshal(0x%x) = %v, want %v", data, v1, want) } // Unmarshal CBOR map into Go map[interface{}]interface{}. var v2 map[interface{}]interface{} want = map[interface{}]interface{}{"a": "A", "b": "B"} if err := Unmarshal(data, &v2); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", data, wantErrorMsg) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", data, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v2, want) { t.Errorf("Unmarshal(0x%x) = %v, want %v", data, v2, want) } // Unmarshal CBOR array into Go map[string]string. var v3 map[string]string want = map[string]string{"a": "A", "b": "B"} if err := Unmarshal(data, &v3); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", data, wantErrorMsg) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", data, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v3, want) { t.Errorf("Unmarshal(0x%x) = %v, want %v", data, v3, want) } // Unmarshal CBOR array into populated Go map[string]string. v4 := map[string]string{"c": "D"} want = map[string]string{"a": "A", "b": "B", "c": "D"} if err := Unmarshal(data, &v4); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", data, wantErrorMsg) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", data, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v4, want) { t.Errorf("Unmarshal(0x%x) = %v, want %v", data, v4, want) } } } func TestStructToArrayError(t *testing.T) { type coseHeader struct { Alg int `cbor:"1,keyasint,omitempty"` Kid []byte `cbor:"4,keyasint,omitempty"` IV []byte `cbor:"5,keyasint,omitempty"` } type nestedCWT struct { _ struct{} `cbor:",toarray"` Protected []byte Unprotected coseHeader Ciphertext []byte } for _, tc := range []struct { cborData []byte wantErrorMsg string }{ // [-17, [-17, -17], -17] {hexDecode("9f3082303030ff"), "cbor: cannot unmarshal negative integer into Go struct field cbor.nestedCWT.Protected of type []uint8"}, // [[], [], ["\x930000", -17]] {hexDecode("9f9fff9fff9f65933030303030ffff"), "cbor: cannot unmarshal array into Go struct field cbor.nestedCWT.Unprotected of type cbor.coseHeader (cannot decode CBOR array to struct without toarray option)"}, } { var v nestedCWT if err := Unmarshal(tc.cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", tc.cborData, tc.wantErrorMsg) } else if err.Error() != tc.wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) } } } func TestStructKeyAsIntError(t *testing.T) { type claims struct { Iss string `cbor:"1,keyasint"` Sub string `cbor:"2,keyasint"` Aud string `cbor:"3,keyasint"` Exp float64 `cbor:"4,keyasint"` Nbf float64 `cbor:"5,keyasint"` Iat float64 `cbor:"6,keyasint"` Cti []byte `cbor:"7,keyasint"` } cborData := hexDecode("bf0783e662f03030ff") // {7: [simple(6), "\xF00", -17]} wantErrorMsg := invalidUTF8ErrorMsg wantV := claims{Cti: []byte{6, 0, 0}} var v claims if err := Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want %q", cborData, wantErrorMsg) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v, wantV) { t.Errorf("Unmarshal(0x%x) = %v, want %v", cborData, v, wantV) } } 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. 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) } else if !reflect.DeepEqual(v, wantV) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV) } else if s != "hello" { t.Errorf("Unmarshal(0x%x) modified s %q", cborData, s) } } func TestDecOptions(t *testing.T) { opts1 := DecOptions{ TimeTag: DecTagRequired, DupMapKey: DupMapKeyEnforcedAPF, IndefLength: IndefLengthForbidden, MaxNestedLevels: 100, MaxMapPairs: 101, MaxArrayElements: 102, TagsMd: TagsForbidden, IntDec: IntDecConvertSigned, ExtraReturnErrors: ExtraDecErrorUnknownField, } dm, err := opts1.DecMode() if err != nil { t.Errorf("DecMode() returned an error %v", err) } else { opts2 := dm.DecOptions() if !reflect.DeepEqual(opts1, opts2) { t.Errorf("DecOptions->DecMode->DecOptions returned different values: %v, %v", opts1, opts2) } } } type roundTripTest struct { name string obj interface{} wantCborData []byte } func testRoundTrip(t *testing.T, testCases []roundTripTest, em EncMode, dm DecMode) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { b, err := em.Marshal(tc.obj) if err != nil { t.Errorf("Marshal(%+v) returned error %v", tc.obj, err) } if !bytes.Equal(b, tc.wantCborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.wantCborData) } v := reflect.New(reflect.TypeOf(tc.obj)) if err := dm.Unmarshal(b, v.Interface()); err != nil { t.Errorf("Unmarshal() returned error %v", err) } if !reflect.DeepEqual(tc.obj, v.Elem().Interface()) { t.Errorf("Marshal-Unmarshal returned different values: %v, %v", tc.obj, v.Elem().Interface()) } }) } } func TestDecModeInvalidTimeTag(t *testing.T) { wantErrorMsg := "cbor: invalid TimeTag 101" _, err := DecOptions{TimeTag: 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 TestDecModeInvalidDuplicateMapKey(t *testing.T) { wantErrorMsg := "cbor: invalid DupMapKey 101" _, err := DecOptions{DupMapKey: 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 TestDecModeDefaultMaxNestedLevel(t *testing.T) { dm, err := DecOptions{}.DecMode() if err != nil { t.Errorf("DecMode() returned error %v", err) } else { maxNestedLevels := dm.DecOptions().MaxNestedLevels if maxNestedLevels != 32 { t.Errorf("DecOptions().MaxNestedLevels = %d, want %v", maxNestedLevels, 32) } } } func TestDecModeInvalidMaxNestedLevel(t *testing.T) { testCases := []struct { name string opts DecOptions wantErrorMsg string }{ { name: "MaxNestedLevels < 4", opts: DecOptions{MaxNestedLevels: 1}, wantErrorMsg: "cbor: invalid MaxNestedLevels 1 (range is [4, 256])", }, { name: "MaxNestedLevels > 256", opts: DecOptions{MaxNestedLevels: 257}, wantErrorMsg: "cbor: invalid MaxNestedLevels 257 (range is [4, 256])", }, } 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 TestDecModeDefaultMaxMapPairs(t *testing.T) { dm, err := DecOptions{}.DecMode() if err != nil { t.Errorf("DecMode() returned error %v", err) } else { maxMapPairs := dm.DecOptions().MaxMapPairs if maxMapPairs != defaultMaxMapPairs { t.Errorf("DecOptions().MaxMapPairs = %d, want %v", maxMapPairs, defaultMaxMapPairs) } } } func TestDecModeInvalidMaxMapPairs(t *testing.T) { testCases := []struct { name string opts DecOptions wantErrorMsg string }{ { name: "MaxMapPairs < 16", opts: DecOptions{MaxMapPairs: 1}, wantErrorMsg: "cbor: invalid MaxMapPairs 1 (range is [16, 2147483647])", }, { name: "MaxMapPairs > 2147483647", opts: DecOptions{MaxMapPairs: 2147483648}, wantErrorMsg: "cbor: invalid MaxMapPairs 2147483648 (range is [16, 2147483647])", }, } 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 TestDecModeDefaultMaxArrayElements(t *testing.T) { dm, err := DecOptions{}.DecMode() if err != nil { t.Errorf("DecMode() returned error %v", err) } else { maxArrayElements := dm.DecOptions().MaxArrayElements if maxArrayElements != defaultMaxArrayElements { t.Errorf("DecOptions().MaxArrayElementsr = %d, want %v", maxArrayElements, defaultMaxArrayElements) } } } func TestDecModeInvalidMaxArrayElements(t *testing.T) { testCases := []struct { name string opts DecOptions wantErrorMsg string }{ { name: "MaxArrayElements < 16", opts: DecOptions{MaxArrayElements: 1}, wantErrorMsg: "cbor: invalid MaxArrayElements 1 (range is [16, 2147483647])", }, { name: "MaxArrayElements > 2147483647", opts: DecOptions{MaxArrayElements: 2147483648}, wantErrorMsg: "cbor: invalid MaxArrayElements 2147483648 (range is [16, 2147483647])", }, } 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 TestDecModeInvalidIndefiniteLengthMode(t *testing.T) { wantErrorMsg := "cbor: invalid IndefLength 101" _, err := DecOptions{IndefLength: 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 TestDecModeInvalidTagsMode(t *testing.T) { wantErrorMsg := "cbor: invalid TagsMd 101" _, err := DecOptions{TagsMd: 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 TestUnmarshalStructKeyAsIntNumError(t *testing.T) { type T1 struct { F1 int `cbor:"a,keyasint"` } type T2 struct { F1 int `cbor:"-18446744073709551616,keyasint"` } testCases := []struct { name string cborData []byte obj interface{} wantErrorMsg string }{ { name: "string as key", cborData: hexDecode("a1616101"), obj: T1{}, wantErrorMsg: "cbor: failed to parse field name \"a\" to int", }, { name: "out of range int as key", cborData: hexDecode("a13bffffffffffffffff01"), obj: T2{}, wantErrorMsg: "cbor: failed to parse field name \"-18446744073709551616\" to int", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v := reflect.New(reflect.TypeOf(tc.obj)) err := Unmarshal(tc.cborData, v.Interface()) if err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error %q", tc.cborData, tc.wantErrorMsg) } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { t.Errorf("Unmarshal(0x%x) error %v, want %v", tc.cborData, err.Error(), tc.wantErrorMsg) } }) } } func TestUnmarshalEmptyMapWithDupMapKeyOpt(t *testing.T) { testCases := []struct { name string cborData []byte wantV interface{} }{ { name: "empty map", cborData: hexDecode("a0"), wantV: map[interface{}]interface{}{}, }, { name: "indefinite empty map", cborData: hexDecode("bfff"), wantV: map[interface{}]interface{}{}, }, } dm, err := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() if err != nil { t.Errorf("DecMode() returned error %v", err) } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var v interface{} if err := dm.Unmarshal(tc.cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } if !reflect.DeepEqual(v, tc.wantV) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v, v, tc.wantV, tc.wantV) } }) } } func TestUnmarshalDupMapKeyToEmptyInterface(t *testing.T) { cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"} // Duplicate key overwrites previous value (default). wantV := map[interface{}]interface{}{"a": "F", "b": "B", "c": "C", "d": "D", "e": "E"} var v interface{} if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(v, wantV) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV) } // Duplicate key triggers error. wantV = map[interface{}]interface{}{"a": nil, "b": "B", "c": "C"} wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() var v2 interface{} if err := dm.Unmarshal(cborData, &v2); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v2, wantV) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v2, v2, wantV, wantV) } } func TestStreamDupMapKeyToEmptyInterface(t *testing.T) { cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // map with duplicate key "c": {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"} var b []byte for i := 0; i < 3; i++ { b = append(b, cborData...) } // Duplicate key overwrites previous value (default). wantV := map[interface{}]interface{}{"a": "F", "b": "B", "c": "C", "d": "D", "e": "E"} dec := NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var v1 interface{} if err := dec.Decode(&v1); err != nil { t.Errorf("Decode() returned error %v", err) } if !reflect.DeepEqual(v1, wantV) { t.Errorf("Decode() = %v (%T), want %v (%T)", v1, v1, wantV, wantV) } } var v interface{} if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } // Duplicate key triggers error. wantV = map[interface{}]interface{}{"a": nil, "b": "B", "c": "C"} wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() dec = dm.NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var v2 interface{} if err := dec.Decode(&v2); err == nil { t.Errorf("Decode() didn't return an error") } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err) } else if err.Error() != wantErrorMsg { t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v2, wantV) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v2, v2, wantV, wantV) } } if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } } func TestUnmarshalDupMapKeyToEmptyMap(t *testing.T) { cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"} // Duplicate key overwrites previous value (default). wantM := map[string]string{"a": "F", "b": "B", "c": "C", "d": "D", "e": "E"} var m map[string]string if err := Unmarshal(cborData, &m); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(m, wantM) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m, m, wantM, wantM) } // Duplicate key triggers error. wantM = map[string]string{"a": "", "b": "B", "c": "C"} wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() var m2 map[string]string if err := dm.Unmarshal(cborData, &m2); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(m2, wantM) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m2, m2, wantM, wantM) } } func TestStreamDupMapKeyToEmptyMap(t *testing.T) { cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"} var b []byte for i := 0; i < 3; i++ { b = append(b, cborData...) } // Duplicate key overwrites previous value (default). wantM := map[string]string{"a": "F", "b": "B", "c": "C", "d": "D", "e": "E"} dec := NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var m1 map[string]string if err := dec.Decode(&m1); err != nil { t.Errorf("Decode() returned error %v", err) } if !reflect.DeepEqual(m1, wantM) { t.Errorf("Decode() = %v (%T), want %v (%T)", m1, m1, wantM, wantM) } } var v interface{} if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } // Duplicate key triggers error. wantM = map[string]string{"a": "", "b": "B", "c": "C"} wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() dec = dm.NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var m2 map[string]string if err := dec.Decode(&m2); err == nil { t.Errorf("Decode() didn't return an error") } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err) } else if err.Error() != wantErrorMsg { t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg) } if !reflect.DeepEqual(m2, wantM) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m2, m2, wantM, wantM) } } if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } } func TestUnmarshalDupMapKeyToNotEmptyMap(t *testing.T) { cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"} // Duplicate key overwrites previous value (default). m := map[string]string{"a": "Z", "b": "Z", "c": "Z", "d": "Z", "e": "Z", "f": "Z"} wantM := map[string]string{"a": "F", "b": "B", "c": "C", "d": "D", "e": "E", "f": "Z"} if err := Unmarshal(cborData, &m); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(m, wantM) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m, m, wantM, wantM) } // Duplicate key triggers error. m2 := map[string]string{"a": "Z", "b": "Z", "c": "Z", "d": "Z", "e": "Z", "f": "Z"} wantM = map[string]string{"a": "", "b": "B", "c": "C", "d": "Z", "e": "Z", "f": "Z"} wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() if err := dm.Unmarshal(cborData, &m2); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(m2, wantM) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m2, m2, wantM, wantM) } } func TestStreamDupMapKeyToNotEmptyMap(t *testing.T) { cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"} var b []byte for i := 0; i < 3; i++ { b = append(b, cborData...) } // Duplicate key overwrites previous value (default). wantM := map[string]string{"a": "F", "b": "B", "c": "C", "d": "D", "e": "E", "f": "Z"} dec := NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { m1 := map[string]string{"a": "Z", "b": "Z", "c": "Z", "d": "Z", "e": "Z", "f": "Z"} if err := dec.Decode(&m1); err != nil { t.Errorf("Decode() returned error %v", err) } if !reflect.DeepEqual(m1, wantM) { t.Errorf("Decode() = %v (%T), want %v (%T)", m1, m1, wantM, wantM) } } var v interface{} if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } // Duplicate key triggers error. wantM = map[string]string{"a": "", "b": "B", "c": "C", "d": "Z", "e": "Z", "f": "Z"} wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() dec = dm.NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { m2 := map[string]string{"a": "Z", "b": "Z", "c": "Z", "d": "Z", "e": "Z", "f": "Z"} if err := dec.Decode(&m2); err == nil { t.Errorf("Decode() didn't return an error") } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err) } else if err.Error() != wantErrorMsg { t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg) } if !reflect.DeepEqual(m2, wantM) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m2, m2, wantM, wantM) } } if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } } func TestUnmarshalDupMapKeyToStruct(t *testing.T) { type s struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` D string `cbor:"d"` E string `cbor:"e"` } cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"} // Duplicate key doesn't overwrite previous value (default). wantS := s{A: "A", B: "B", C: "C", D: "D", E: "E"} var s1 s if err := Unmarshal(cborData, &s1); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS) } // Duplicate key triggers error. wantS = s{A: "A", B: "B", C: "C"} wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() var s2 s if err := dm.Unmarshal(cborData, &s2); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2)) } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } func TestStreamDupMapKeyToStruct(t *testing.T) { type s struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` D string `cbor:"d"` E string `cbor:"e"` } cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"} var b []byte for i := 0; i < 3; i++ { b = append(b, cborData...) } // Duplicate key overwrites previous value (default). wantS := s{A: "A", B: "B", C: "C", D: "D", E: "E"} dec := NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var s1 s if err := dec.Decode(&s1); err != nil { t.Errorf("Decode() returned error %v", err) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Decode() = %v (%T), want %v (%T)", s1, s1, wantS, wantS) } } var v interface{} if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } // Duplicate key triggers error. wantS = s{A: "A", B: "B", C: "C"} wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() dec = dm.NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var s2 s if err := dec.Decode(&s2); err == nil { t.Errorf("Decode() didn't return an error") } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err) } else if err.Error() != wantErrorMsg { t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } } // dupl map key is a struct field func TestUnmarshalDupMapKeyToStructKeyAsInt(t *testing.T) { type s struct { A int `cbor:"1,keyasint"` B int `cbor:"3,keyasint"` C int `cbor:"5,keyasint"` } cborData := hexDecode("a40102030401030506") // {1:2, 3:4, 1:3, 5:6} // Duplicate key doesn't overwrite previous value (default). wantS := s{A: 2, B: 4, C: 6} var s1 s if err := Unmarshal(cborData, &s1); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS) } // Duplicate key triggers error. wantS = s{A: 2, B: 4} wantErrorMsg := "cbor: found duplicate map key \"1\" at map element index 2" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() var s2 s if err := dm.Unmarshal(cborData, &s2); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2)) } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } func TestStreamDupMapKeyToStructKeyAsInt(t *testing.T) { type s struct { A int `cbor:"1,keyasint"` B int `cbor:"3,keyasint"` C int `cbor:"5,keyasint"` } cborData := hexDecode("a40102030401030506") // {1:2, 3:4, 1:3, 5:6} var b []byte for i := 0; i < 3; i++ { b = append(b, cborData...) } // Duplicate key overwrites previous value (default). wantS := s{A: 2, B: 4, C: 6} dec := NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var s1 s if err := dec.Decode(&s1); err != nil { t.Errorf("Decode() returned error %v", err) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Decode() = %v (%T), want %v (%T)", s1, s1, wantS, wantS) } } var v interface{} if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } // Duplicate key triggers error. wantS = s{A: 2, B: 4} wantErrorMsg := "cbor: found duplicate map key \"1\" at map element index 2" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() dec = dm.NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var s2 s if err := dec.Decode(&s2); err == nil { t.Errorf("Decode() didn't return an error") } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err) } else if err.Error() != wantErrorMsg { t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } } func TestUnmarshalDupMapKeyToStructNoMatchingField(t *testing.T) { type s struct { B string `cbor:"b"` C string `cbor:"c"` D string `cbor:"d"` E string `cbor:"e"` } cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"} wantS := s{B: "B", C: "C", D: "D", E: "E"} var s1 s if err := Unmarshal(cborData, &s1); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS) } // Duplicate key triggers error even though map key "a" doesn't have a corresponding struct field. wantS = s{B: "B", C: "C"} wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() var s2 s if err := dm.Unmarshal(cborData, &s2); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2)) } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } func TestStreamDupMapKeyToStructNoMatchingField(t *testing.T) { type s struct { B string `cbor:"b"` C string `cbor:"c"` D string `cbor:"d"` E string `cbor:"e"` } cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"} var b []byte for i := 0; i < 3; i++ { b = append(b, cborData...) } // Duplicate key overwrites previous value (default). wantS := s{B: "B", C: "C", D: "D", E: "E"} dec := NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var s1 s if err := dec.Decode(&s1); err != nil { t.Errorf("Decode() returned error %v", err) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Decode() = %v (%T), want %v (%T)", s1, s1, wantS, wantS) } } var v interface{} if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } // Duplicate key triggers error. wantS = s{B: "B", C: "C"} wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() dec = dm.NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var s2 s if err := dec.Decode(&s2); err == nil { t.Errorf("Decode() didn't return an error") } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err) } else if err.Error() != wantErrorMsg { t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Decode() = %v (%T), want %v (%T)", s2, s2, wantS, wantS) } } if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } } func TestUnmarshalDupMapKeyToStructKeyAsIntNoMatchingField(t *testing.T) { type s struct { B int `cbor:"3,keyasint"` C int `cbor:"5,keyasint"` } cborData := hexDecode("a40102030401030506") // {1:2, 3:4, 1:3, 5:6} wantS := s{B: 4, C: 6} var s1 s if err := Unmarshal(cborData, &s1); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS) } // Duplicate key triggers error even though map key "a" doesn't have a corresponding struct field. wantS = s{B: 4} wantErrorMsg := "cbor: found duplicate map key \"1\" at map element index 2" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() var s2 s if err := dm.Unmarshal(cborData, &s2); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2)) } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } func TestStreamDupMapKeyToStructKeyAsIntNoMatchingField(t *testing.T) { type s struct { B int `cbor:"3,keyasint"` C int `cbor:"5,keyasint"` } cborData := hexDecode("a40102030401030506") // {1:2, 3:4, 1:3, 5:6} var b []byte for i := 0; i < 3; i++ { b = append(b, cborData...) } // Duplicate key overwrites previous value (default). wantS := s{B: 4, C: 6} dec := NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var s1 s if err := dec.Decode(&s1); err != nil { t.Errorf("Decode() returned error %v", err) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Decode() = %v (%T), want %v (%T)", s1, s1, wantS, wantS) } } var v interface{} if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } // Duplicate key triggers error. wantS = s{B: 4} wantErrorMsg := "cbor: found duplicate map key \"1\" at map element index 2" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() dec = dm.NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var s2 s if err := dec.Decode(&s2); err == nil { t.Errorf("Decode() didn't return an error") } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err) } else if err.Error() != wantErrorMsg { t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Decode() = %v (%T), want %v (%T)", s2, s2, wantS, wantS) } } if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } } func TestUnmarshalDupMapKeyToStructWrongType(t *testing.T) { type s struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` D string `cbor:"d"` E string `cbor:"e"` } cborData := hexDecode("a861616141fa47c35000026162614261636143fa47c3500003616161466164614461656145") // {"a": "A", 100000.0:2, "b": "B", "c": "C", 100000.0:3, "a": "F", "d": "D", "e": "E"} var s1 s wantS := s{A: "A", B: "B", C: "C", D: "D", E: "E"} wantErrorMsg := "cbor: cannot unmarshal" if err := Unmarshal(cborData, &s1); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS) } wantS = s{A: "A", B: "B", C: "C"} wantErrorMsg = "cbor: found duplicate map key \"100000\" at map element index 4" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() var s2 s if err := dm.Unmarshal(cborData, &s2); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2)) } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } func TestStreamDupMapKeyToStructWrongType(t *testing.T) { type s struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` D string `cbor:"d"` E string `cbor:"e"` } cborData := hexDecode("a861616141fa47c35000026162614261636143fa47c3500003616161466164614461656145") // {"a": "A", 100000.0:2, "b": "B", "c": "C", 100000.0:3, "a": "F", "d": "D", "e": "E"} var b []byte for i := 0; i < 3; i++ { b = append(b, cborData...) } wantS := s{A: "A", B: "B", C: "C", D: "D", E: "E"} wantErrorMsg := "cbor: cannot unmarshal" dec := NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var s1 s if err := dec.Decode(&s1); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS) } } var v interface{} if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } // Duplicate key triggers error. wantS = s{A: "A", B: "B", C: "C"} wantErrorMsg = "cbor: found duplicate map key \"100000\" at map element index 4" dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() dec = dm.NewDecoder(bytes.NewReader(b)) for i := 0; i < 3; i++ { var s2 s if err := dec.Decode(&s2); err == nil { t.Errorf("Decode() didn't return an error") } else if _, ok := err.(*DupMapKeyError); !ok { t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err) } else if err.Error() != wantErrorMsg { t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } if err := dec.Decode(&v); err != io.EOF { t.Errorf("Decode() returned error %v, want %v", err, io.EOF) } } func TestUnmarshalDupMapKeyToStructStringParseError(t *testing.T) { type s struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` D string `cbor:"d"` E string `cbor:"e"` } cborData := hexDecode("a661fe6141616261426163614361fe61466164614461656145") // {"\xFE": "A", "b": "B", "c": "C", "\xFE": "F", "d": "D", "e": "E"} wantS := s{A: "", B: "B", C: "C", D: "D", E: "E"} wantErrorMsg := "cbor: invalid UTF-8 string" // Duplicate key doesn't overwrite previous value (default). var s1 s if err := Unmarshal(cborData, &s1); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*SemanticError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*SemanticError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS) } // Duplicate key triggers error. dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() var s2 s if err := dm.Unmarshal(cborData, &s2); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2)) } else if _, ok := err.(*SemanticError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*SemanticError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } func TestUnmarshalDupMapKeyToStructIntParseError(t *testing.T) { type s struct { A int `cbor:"1,keyasint"` B int `cbor:"3,keyasint"` C int `cbor:"5,keyasint"` } cborData := hexDecode("a43bffffffffffffffff0203043bffffffffffffffff030506") // {-18446744073709551616:2, 3:4, -18446744073709551616:3, 5:6} // Duplicate key doesn't overwrite previous value (default). wantS := s{B: 4, C: 6} wantErrorMsg := "cbor: cannot unmarshal" var s1 s if err := Unmarshal(cborData, &s1); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS) } // Duplicate key triggers error. dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() var s2 s if err := dm.Unmarshal(cborData, &s2); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2)) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } func TestUnmarshalDupMapKeyToStructWrongTypeParseError(t *testing.T) { type s struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` D string `cbor:"d"` E string `cbor:"e"` } cborData := hexDecode("a68161fe614161626142616361438161fe61466164614461656145") // {["\xFE"]: "A", "b": "B", "c": "C", ["\xFE"]: "F", "d": "D", "e": "E"} // Duplicate key doesn't overwrite previous value (default). wantS := s{A: "", B: "B", C: "C", D: "D", E: "E"} wantErrorMsg := "cbor: cannot unmarshal" var s1 s if err := Unmarshal(cborData, &s1); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS) } // Duplicate key triggers error. dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() var s2 s if err := dm.Unmarshal(cborData, &s2); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2)) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } func TestUnmarshalDupMapKeyToStructWrongTypeUnhashableError(t *testing.T) { type s struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` D string `cbor:"d"` E string `cbor:"e"` } cborData := hexDecode("a6810061416162614261636143810061466164614461656145") // {[0]: "A", "b": "B", "c": "C", [0]: "F", "d": "D", "e": "E"} wantS := s{A: "", B: "B", C: "C", D: "D", E: "E"} // Duplicate key doesn't overwrite previous value (default). wantErrorMsg := "cbor: cannot unmarshal" var s1 s if err := Unmarshal(cborData, &s1); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS) } // Duplicate key triggers error. dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() var s2 s if err := dm.Unmarshal(cborData, &s2); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2)) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } func TestUnmarshalDupMapKeyToStructTagTypeError(t *testing.T) { type s struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` D string `cbor:"d"` E string `cbor:"e"` } cborData := hexDecode("a6c24901000000000000000061416162614261636143c24901000000000000000061466164614461656145") // {bignum(18446744073709551616): "A", "b": "B", "c": "C", bignum(18446744073709551616): "F", "d": "D", "e": "E"} wantS := s{A: "", B: "B", C: "C", D: "D", E: "E"} // Duplicate key doesn't overwrite previous value (default). wantErrorMsg := "cbor: cannot unmarshal" var s1 s if err := Unmarshal(cborData, &s1); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s1, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS) } // Duplicate key triggers error. dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode() var s2 s if err := dm.Unmarshal(cborData, &s2); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2)) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(s2, wantS) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS) } } func TestIndefiniteLengthArrayToArray(t *testing.T) { testCases := []struct { name string cborData []byte wantV interface{} }{ { name: "CBOR empty array to Go 5 elem array", cborData: hexDecode("9fff"), wantV: [5]byte{}, }, { name: "CBOR 3 elem array to Go 5 elem array", cborData: hexDecode("9f010203ff"), wantV: [5]byte{1, 2, 3, 0, 0}, }, { name: "CBOR 10 elem array to Go 5 elem array", cborData: hexDecode("9f0102030405060708090aff"), wantV: [5]byte{1, 2, 3, 4, 5}, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v := reflect.New(reflect.TypeOf(tc.wantV)) if err := Unmarshal(tc.cborData, v.Interface()); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } if !reflect.DeepEqual(v.Elem().Interface(), tc.wantV) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v.Elem().Interface(), v.Elem().Interface(), tc.wantV, tc.wantV) } }) } } func TestExceedMaxArrayElements(t *testing.T) { testCases := []struct { name string opts DecOptions cborData []byte wantErrorMsg string }{ { name: "array", opts: DecOptions{MaxArrayElements: 16}, cborData: hexDecode("910101010101010101010101010101010101"), wantErrorMsg: "cbor: exceeded max number of elements 16 for CBOR array", }, { name: "indefinite length array", opts: DecOptions{MaxArrayElements: 16}, cborData: hexDecode("9f0101010101010101010101010101010101ff"), wantErrorMsg: "cbor: exceeded max number of elements 16 for CBOR array", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { dm, _ := tc.opts.DecMode() var v interface{} if err := dm.Unmarshal(tc.cborData, &v); 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) returned error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) } }) } } func TestExceedMaxMapPairs(t *testing.T) { testCases := []struct { name string opts DecOptions cborData []byte wantErrorMsg string }{ { name: "array", opts: DecOptions{MaxMapPairs: 16}, cborData: hexDecode("b101010101010101010101010101010101010101010101010101010101010101010101"), wantErrorMsg: "cbor: exceeded max number of key-value pairs 16 for CBOR map", }, { name: "indefinite length array", opts: DecOptions{MaxMapPairs: 16}, cborData: hexDecode("bf01010101010101010101010101010101010101010101010101010101010101010101ff"), wantErrorMsg: "cbor: exceeded max number of key-value pairs 16 for CBOR map", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { dm, _ := tc.opts.DecMode() var v interface{} if err := dm.Unmarshal(tc.cborData, &v); 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) returned error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) } }) } } func TestDecIndefiniteLengthOption(t *testing.T) { testCases := []struct { name string opts DecOptions cborData []byte wantErrorMsg string }{ { name: "byte string", opts: DecOptions{IndefLength: IndefLengthForbidden}, cborData: hexDecode("5fff"), wantErrorMsg: "cbor: indefinite-length byte string isn't allowed", }, { name: "text string", opts: DecOptions{IndefLength: IndefLengthForbidden}, cborData: hexDecode("7fff"), wantErrorMsg: "cbor: indefinite-length UTF-8 text string isn't allowed", }, { name: "array", opts: DecOptions{IndefLength: IndefLengthForbidden}, cborData: hexDecode("9fff"), wantErrorMsg: "cbor: indefinite-length array isn't allowed", }, { name: "indefinite length array", opts: DecOptions{IndefLength: IndefLengthForbidden}, cborData: hexDecode("bfff"), wantErrorMsg: "cbor: indefinite-length map isn't allowed", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Default option allows indefinite length items var v interface{} if err := Unmarshal(tc.cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned an error %v", tc.cborData, err) } dm, _ := tc.opts.DecMode() if err := dm.Unmarshal(tc.cborData, &v); 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) returned error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg) } }) } } func TestDecTagsMdOption(t *testing.T) { cborData := hexDecode("c074323031332d30332d32315432303a30343a30305a") wantErrorMsg := "cbor: CBOR tag isn't allowed" // Default option allows CBOR tags var v interface{} if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned an error %v", cborData, err) } // Decoding CBOR tags with TagsForbidden option returns error dm, _ := DecOptions{TagsMd: TagsForbidden}.DecMode() if err := dm.Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if err.Error() != wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err.Error(), wantErrorMsg) } // Create DecMode with TagSet and TagsForbidden option returns error wantErrorMsg = "cbor: cannot create DecMode with TagSet when TagsMd is TagsForbidden" tags := NewTagSet() _, err := DecOptions{TagsMd: TagsForbidden}.DecModeWithTags(tags) if err == nil { t.Errorf("DecModeWithTags() didn't return an error") } else if err.Error() != wantErrorMsg { t.Errorf("DecModeWithTags() returned error %q, want %q", err.Error(), wantErrorMsg) } _, err = DecOptions{TagsMd: TagsForbidden}.DecModeWithSharedTags(tags) if err == nil { t.Errorf("DecModeWithSharedTags() didn't return an error") } else if err.Error() != wantErrorMsg { t.Errorf("DecModeWithSharedTags() returned error %q, want %q", err.Error(), wantErrorMsg) } } func TestDecModeInvalidIntDec(t *testing.T) { wantErrorMsg := "cbor: invalid IntDec 101" _, err := DecOptions{IntDec: 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 TestIntDec(t *testing.T) { dm, err := DecOptions{IntDec: IntDecConvertSigned}.DecMode() if err != nil { t.Errorf("DecMode() returned an error %+v", err) } testCases := []struct { name string cborData []byte wantObj interface{} wantErrorMsg string }{ { name: "CBOR pos int", cborData: hexDecode("1a000f4240"), wantObj: int64(1000000), }, { name: "CBOR pos int overflows int64", cborData: hexDecode("1bffffffffffffffff"), wantErrorMsg: "18446744073709551615 overflows Go's int64", }, { name: "CBOR neg int", cborData: hexDecode("3903e7"), wantObj: int64(-1000), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var v interface{} err := dm.Unmarshal(tc.cborData, &v) 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, tc.wantObj) { t.Errorf("Unmarshal(0x%x) return %v (%T), want %v (%T)", tc.cborData, v, v, 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() 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 TestExtraErrorCondUnknowField(t *testing.T) { type s struct { A string B string C string } dm, _ := DecOptions{}.DecMode() dmUnknownFieldError, _ := DecOptions{ExtraReturnErrors: ExtraDecErrorUnknownField}.DecMode() testCases := []struct { name string cborData []byte dm DecMode wantObj interface{} wantErrorMsg string }{ { name: "field by field match", cborData: hexDecode("a3614161616142616261436163"), // map[string]string{"A": "a", "B": "b", "C": "c"} dm: dm, wantObj: s{A: "a", B: "b", C: "c"}, }, { name: "field by field match with ExtraDecErrorUnknownField", cborData: hexDecode("a3614161616142616261436163"), // map[string]string{"A": "a", "B": "b", "C": "c"} dm: dmUnknownFieldError, wantObj: s{A: "a", B: "b", C: "c"}, }, { name: "CBOR map less field", cborData: hexDecode("a26141616161426162"), // map[string]string{"A": "a", "B": "b"} dm: dm, wantObj: s{A: "a", B: "b", C: ""}, }, { name: "CBOR map less field with ExtraDecErrorUnknownField", cborData: hexDecode("a26141616161426162"), // map[string]string{"A": "a", "B": "b"} dm: dmUnknownFieldError, wantObj: s{A: "a", B: "b", C: ""}, }, { name: "CBOR map unknown field", cborData: hexDecode("a461416161614261626143616361446164"), // map[string]string{"A": "a", "B": "b", "C": "c", "D": "d"} dm: dm, wantObj: s{A: "a", B: "b", C: "c"}, }, { name: "CBOR map unknown field with ExtraDecErrorUnknownField", cborData: hexDecode("a461416161614261626143616361446164"), // map[string]string{"A": "a", "B": "b", "C": "c", "D": "d"} dm: dmUnknownFieldError, wantErrorMsg: "cbor: found unknown field at map element index 3", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var v s err := tc.dm.Unmarshal(tc.cborData, &v) 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, tc.wantObj) { t.Errorf("Unmarshal(0x%x) return %v (%T), want %v (%T)", tc.cborData, v, v, 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 TestStreamExtraErrorCondUnknowField(t *testing.T) { type s struct { A string B string C string } cborData := hexDecode("a461416161614461646142616261436163a3614161616142616261436163") // map[string]string{"A": "a", "D": "d", "B": "b", "C": "c"}, map[string]string{"A": "a", "B": "b", "C": "c"} wantErrorMsg := "cbor: found unknown field at map element index 1" wantObj := s{A: "a", B: "b", C: "c"} dmUnknownFieldError, _ := DecOptions{ExtraReturnErrors: ExtraDecErrorUnknownField}.DecMode() dec := dmUnknownFieldError.NewDecoder(bytes.NewReader(cborData)) var v1 s err := dec.Decode(&v1) if err == nil { t.Errorf("Decode() didn't return an error, want %q", wantErrorMsg) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Decode() returned error %q, want %q", err.Error(), wantErrorMsg) } var v2 s err = dec.Decode(&v2) if err != nil { t.Errorf("Decode() returned an error %v", err) } else if !reflect.DeepEqual(v2, wantObj) { t.Errorf("Decode() return %v (%T), want %v (%T)", v2, v2, wantObj, wantObj) } } // TestUnmarshalTagNum55799 is identical to TestUnmarshal, // except that CBOR test data is prefixed with tag number 55799 (0xd9d9f7). func TestUnmarshalTagNum55799(t *testing.T) { tagNum55799 := hexDecode("d9d9f7") for _, tc := range unmarshalTests { // Prefix tag number 55799 to CBOR test data cborData := make([]byte, len(tc.cborData)+6) copy(cborData, tagNum55799) copy(cborData[3:], tagNum55799) copy(cborData[6:], tc.cborData) // Test unmarshalling CBOR into empty interface. var v interface{} if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else { if tm, ok := tc.emptyInterfaceValue.(time.Time); ok { if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) } } else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) } } // Test unmarshalling CBOR into RawMessage. var r RawMessage if err := Unmarshal(cborData, &r); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if !bytes.Equal(r, tc.cborData) { t.Errorf("Unmarshal(0x%x) returned RawMessage %v, want %v", cborData, r, tc.cborData) } // Test unmarshalling CBOR into compatible data types. for _, value := range tc.values { v := reflect.New(reflect.TypeOf(value)) vPtr := v.Interface() if err := Unmarshal(cborData, vPtr); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else { if tm, ok := value.(time.Time); ok { if vt, ok := v.Elem().Interface().(time.Time); !ok || !tm.Equal(vt) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v.Elem().Interface(), v.Elem().Interface(), value, value) } } else if !reflect.DeepEqual(v.Elem().Interface(), value) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v.Elem().Interface(), v.Elem().Interface(), value, value) } } } // Test unmarshalling CBOR into incompatible data types. for _, typ := range tc.wrongTypes { v := reflect.New(typ) vPtr := v.Interface() if err := Unmarshal(cborData, vPtr); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, typ.String()) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), "cannot unmarshal") { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), "cannot unmarshal") } } } } // TestUnmarshalFloatWithTagNum55799 is identical to TestUnmarshalFloat, // except that CBOR test data is prefixed with tag number 55799 (0xd9d9f7). func TestUnmarshalFloatWithTagNum55799(t *testing.T) { tagNum55799 := hexDecode("d9d9f7") for _, tc := range unmarshalFloatTests { // Prefix tag number 55799 to CBOR test data cborData := make([]byte, len(tc.cborData)+3) copy(cborData, tagNum55799) copy(cborData[3:], tc.cborData) // Test unmarshalling CBOR into empty interface. var v interface{} if err := Unmarshal(tc.cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } else { testFloat(t, tc.cborData, v, tc.emptyInterfaceValue, tc.equalityThreshold) } // Test unmarshalling CBOR into RawMessage. var r RawMessage if err := Unmarshal(tc.cborData, &r); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } else if !bytes.Equal(r, tc.cborData) { t.Errorf("Unmarshal(0x%x) returned RawMessage %v, want %v", tc.cborData, r, tc.cborData) } // Test unmarshalling CBOR into compatible data types. for _, value := range tc.values { v := reflect.New(reflect.TypeOf(value)) vPtr := v.Interface() if err := Unmarshal(tc.cborData, vPtr); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } else { testFloat(t, tc.cborData, v.Elem().Interface(), value, tc.equalityThreshold) } } // Test unmarshalling CBOR into incompatible data types. for _, typ := range tc.wrongTypes { v := reflect.New(typ) vPtr := v.Interface() if err := Unmarshal(tc.cborData, vPtr); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", tc.cborData) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", tc.cborData, err) } else if !strings.Contains(err.Error(), "cannot unmarshal") { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.cborData, err.Error(), "cannot unmarshal") } } } } func TestUnmarshalTagNum55799AsElement(t *testing.T) { testCases := []struct { name string cborData []byte emptyInterfaceValue interface{} values []interface{} wrongTypes []reflect.Type }{ { "array", hexDecode("d9d9f783d9d9f701d9d9f702d9d9f703"), // 55799([55799(1), 55799(2), 55799(3)]) []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}, }, { "map", hexDecode("d9d9f7a2d9d9f701d9d9f702d9d9f703d9d9f704"), // 55799({55799(1): 55799(2), 55799(3): 55799(4)}) 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}, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Test unmarshalling CBOR into empty interface. var v interface{} if err := Unmarshal(tc.cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } else { if tm, ok := tc.emptyInterfaceValue.(time.Time); ok { if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) } } else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue) } } // Test unmarshalling CBOR into compatible data types. for _, value := range tc.values { v := reflect.New(reflect.TypeOf(value)) vPtr := v.Interface() if err := Unmarshal(tc.cborData, vPtr); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err) } else { if tm, ok := value.(time.Time); ok { if vt, ok := v.Elem().Interface().(time.Time); !ok || !tm.Equal(vt) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v.Elem().Interface(), v.Elem().Interface(), value, value) } } else if !reflect.DeepEqual(v.Elem().Interface(), value) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v.Elem().Interface(), v.Elem().Interface(), value, value) } } } // Test unmarshalling CBOR into incompatible data types. for _, typ := range tc.wrongTypes { v := reflect.New(typ) vPtr := v.Interface() if err := Unmarshal(tc.cborData, vPtr); err == nil { t.Errorf("Unmarshal(0x%x, %s) didn't return an error", tc.cborData, typ.String()) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", tc.cborData, err) } else if !strings.Contains(err.Error(), "cannot unmarshal") { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.cborData, err.Error(), "cannot unmarshal") } } }) } } func TestUnmarshalTagNum55799ToBinaryUnmarshaler(t *testing.T) { cborData := hexDecode("d9d9f74800000000499602d2") // 55799(h'00000000499602D2') wantObj := number(1234567890) var v number if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if !reflect.DeepEqual(v, wantObj) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj) } } func TestUnmarshalTagNum55799ToUnmarshaler(t *testing.T) { cborData := hexDecode("d9d9f7d864a1636e756d01") // 55799(100({"num": 1})) wantObj := number3(1) var v number3 if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if !reflect.DeepEqual(v, wantObj) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj) } } func TestUnmarshalTagNum55799ToRegisteredGoType(t *testing.T) { type myInt int typ := reflect.TypeOf(myInt(0)) tags := NewTagSet() if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, typ, 125); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", typ, 125, err) } dm, _ := DecOptions{}.DecModeWithTags(tags) cborData := hexDecode("d9d9f7d87d01") // 55799(125(1)) wantObj := myInt(1) var v myInt if err := dm.Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if !reflect.DeepEqual(v, wantObj) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj) } } // TODO: wait for clarification from 7049bis https://github.com/cbor-wg/CBORbis/issues/183 // Nested tag number 55799 may be stripeed as well depending on 7049bis clarification. func TestUnmarshalNestedTagNum55799ToEmptyInterface(t *testing.T) { cborData := hexDecode("d864d9d9f701") // 100(55799(1)) wantObj := Tag{100, Tag{55799, uint64(1)}} var v interface{} if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if !reflect.DeepEqual(v, wantObj) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj) } } func TestUnmarshalNestedTagNum55799ToValue(t *testing.T) { cborData := hexDecode("d864d9d9f701") // 100(55799(1)) wantObj := 1 var v int if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if !reflect.DeepEqual(v, wantObj) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj) } } func TestUnmarshalNestedTagNum55799ToTag(t *testing.T) { cborData := hexDecode("d864d9d9f701") // 100(55799(1)) wantObj := Tag{100, Tag{55799, uint64(1)}} var v Tag if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if !reflect.DeepEqual(v, wantObj) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj) } } func TestUnmarshalNestedTagNum55799ToTime(t *testing.T) { cborData := hexDecode("c0d9d9f774323031332d30332d32315432303a30343a30305a") // 0(55799("2013-03-21T20:04:00Z")) wantErrorMsg := "tag number 0 must be followed by text string, got tag" var v time.Time if err := Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return error", cborData) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %s, want %s", cborData, err.Error(), wantErrorMsg) } } func TestUnmarshalNestedTagNum55799ToBinaryUnmarshaler(t *testing.T) { cborData := hexDecode("d864d9d9f74800000000499602d2") // 100(55799(h'00000000499602D2')) wantObj := number(1234567890) var v number if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } else if !reflect.DeepEqual(v, wantObj) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj) } } func TestUnmarshalNestedTagNum55799ToUnmarshaler(t *testing.T) { cborData := hexDecode("d864d9d9f7a1636e756d01") // 100(55799({"num": 1})) wantErrorMsg := "wrong tag content type" var v number3 if err := Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return error", cborData) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %s, want %s", cborData, err.Error(), wantErrorMsg) } } func TestUnmarshalNestedTagNum55799ToRegisteredGoType(t *testing.T) { type myInt int typ := reflect.TypeOf(myInt(0)) tags := NewTagSet() if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, typ, 125); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", typ, 125, err) } dm, _ := DecOptions{}.DecModeWithTags(tags) cborData := hexDecode("d87dd9d9f701") // 125(55799(1)) wantErrorMsg := "cbor: wrong tag number for cbor.myInt, got [125 55799], expected [125]" var v myInt if err := dm.Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal() didn't return error") } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %s, want %s", cborData, err.Error(), wantErrorMsg) } } func TestUnmarshalPosIntToBigInt(t *testing.T) { cborData := hexDecode("1bffffffffffffffff") // 18446744073709551615 wantEmptyInterfaceValue := uint64(18446744073709551615) wantBigIntValue := bigIntOrPanic("18446744073709551615") var v1 interface{} if err := Unmarshal(cborData, &v1); err != nil { t.Errorf("Unmarshal(0x%x) returned error %+v", cborData, err) } else if !reflect.DeepEqual(v1, wantEmptyInterfaceValue) { t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", cborData, v1, v1, wantEmptyInterfaceValue, wantEmptyInterfaceValue) } var v2 big.Int if err := Unmarshal(cborData, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %+v", cborData, err) } else if !reflect.DeepEqual(v2, wantBigIntValue) { t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", cborData, v2, v2, wantBigIntValue, wantBigIntValue) } } func TestUnmarshalNegIntToBigInt(t *testing.T) { testCases := []struct { name string cborData []byte wantEmptyInterfaceValue interface{} wantBigIntValue big.Int }{ { name: "fit Go int64", cborData: hexDecode("3b7fffffffffffffff"), // -9223372036854775808 wantEmptyInterfaceValue: int64(-9223372036854775808), wantBigIntValue: bigIntOrPanic("-9223372036854775808"), }, { name: "overflow Go int64", cborData: hexDecode("3b8000000000000000"), // -9223372036854775809 wantEmptyInterfaceValue: bigIntOrPanic("-9223372036854775809"), wantBigIntValue: bigIntOrPanic("-9223372036854775809"), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var v1 interface{} if err := Unmarshal(tc.cborData, &v1); err != nil { t.Errorf("Unmarshal(0x%x) returned error %+v", tc.cborData, err) } else if !reflect.DeepEqual(v1, tc.wantEmptyInterfaceValue) { t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", tc.cborData, v1, v1, tc.wantEmptyInterfaceValue, tc.wantEmptyInterfaceValue) } var v2 big.Int if err := Unmarshal(tc.cborData, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %+v", tc.cborData, err) } else if !reflect.DeepEqual(v2, tc.wantBigIntValue) { t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", tc.cborData, v2, v2, tc.wantBigIntValue, tc.wantBigIntValue) } }) } } func TestUnmarshalTag2(t *testing.T) { testCases := []struct { name string cborData []byte wantEmptyInterfaceValue interface{} wantValues []interface{} }{ { name: "fit Go int64", cborData: hexDecode("c2430f4240"), // 2(1000000) wantEmptyInterfaceValue: bigIntOrPanic("1000000"), wantValues: []interface{}{ int64(1000000), uint64(1000000), float32(1000000), float64(1000000), bigIntOrPanic("1000000"), }, }, { name: "fit Go uint64", cborData: hexDecode("c248ffffffffffffffff"), // 2(18446744073709551615) wantEmptyInterfaceValue: bigIntOrPanic("18446744073709551615"), wantValues: []interface{}{ uint64(18446744073709551615), float32(18446744073709551615), float64(18446744073709551615), bigIntOrPanic("18446744073709551615"), }, }, { name: "fit Go uint64 with leading zeros", cborData: hexDecode("c24900ffffffffffffffff"), // 2(18446744073709551615) wantEmptyInterfaceValue: bigIntOrPanic("18446744073709551615"), wantValues: []interface{}{ uint64(18446744073709551615), float32(18446744073709551615), float64(18446744073709551615), bigIntOrPanic("18446744073709551615"), }, }, { name: "overflow Go uint64", cborData: hexDecode("c249010000000000000000"), // 2(18446744073709551616) wantEmptyInterfaceValue: bigIntOrPanic("18446744073709551616"), wantValues: []interface{}{ bigIntOrPanic("18446744073709551616"), }, }, { name: "overflow Go uint64 with leading zeros", cborData: hexDecode("c24b0000010000000000000000"), // 2(18446744073709551616) wantEmptyInterfaceValue: bigIntOrPanic("18446744073709551616"), wantValues: []interface{}{ bigIntOrPanic("18446744073709551616"), }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var v1 interface{} if err := Unmarshal(tc.cborData, &v1); err != nil { t.Errorf("Unmarshal(0x%x) returned error %+v", tc.cborData, err) } else if !reflect.DeepEqual(v1, tc.wantEmptyInterfaceValue) { t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", tc.cborData, v1, v1, tc.wantEmptyInterfaceValue, tc.wantEmptyInterfaceValue) } for _, wantValue := range tc.wantValues { v := reflect.New(reflect.TypeOf(wantValue)) if err := Unmarshal(tc.cborData, v.Interface()); err != nil { t.Errorf("Unmarshal(0x%x) returned error %+v", tc.cborData, err) } else if !reflect.DeepEqual(v.Elem().Interface(), wantValue) { t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", tc.cborData, v.Elem().Interface(), v.Elem().Interface(), wantValue, wantValue) } } }) } } func TestUnmarshalTag3(t *testing.T) { testCases := []struct { name string cborData []byte wantEmptyInterfaceValue interface{} wantValues []interface{} }{ { name: "fit Go int64", cborData: hexDecode("c3487fffffffffffffff"), // 3(-9223372036854775808) wantEmptyInterfaceValue: bigIntOrPanic("-9223372036854775808"), wantValues: []interface{}{ int64(-9223372036854775808), float32(-9223372036854775808), float64(-9223372036854775808), bigIntOrPanic("-9223372036854775808"), }, }, { name: "fit Go int64 with leading zeros", cborData: hexDecode("c349007fffffffffffffff"), // 3(-9223372036854775808) wantEmptyInterfaceValue: bigIntOrPanic("-9223372036854775808"), wantValues: []interface{}{ int64(-9223372036854775808), float32(-9223372036854775808), float64(-9223372036854775808), bigIntOrPanic("-9223372036854775808"), }, }, { name: "overflow Go int64", cborData: hexDecode("c349010000000000000000"), // 3(-18446744073709551617) wantEmptyInterfaceValue: bigIntOrPanic("-18446744073709551617"), wantValues: []interface{}{ bigIntOrPanic("-18446744073709551617"), }, }, { name: "overflow Go int64 with leading zeros", cborData: hexDecode("c34b0000010000000000000000"), // 3(-18446744073709551617) wantEmptyInterfaceValue: bigIntOrPanic("-18446744073709551617"), wantValues: []interface{}{ bigIntOrPanic("-18446744073709551617"), }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var v1 interface{} if err := Unmarshal(tc.cborData, &v1); err != nil { t.Errorf("Unmarshal(0x%x) returned error %+v", tc.cborData, err) } else if !reflect.DeepEqual(v1, tc.wantEmptyInterfaceValue) { t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", tc.cborData, v1, v1, tc.wantEmptyInterfaceValue, tc.wantEmptyInterfaceValue) } for _, wantValue := range tc.wantValues { v := reflect.New(reflect.TypeOf(wantValue)) if err := Unmarshal(tc.cborData, v.Interface()); err != nil { t.Errorf("Unmarshal(0x%x) returned error %+v", tc.cborData, err) } else if !reflect.DeepEqual(v.Elem().Interface(), wantValue) { t.Errorf("Unmarshal(0x%x) returned %v (%T), want %v (%T)", tc.cborData, v.Elem().Interface(), v.Elem().Interface(), wantValue, wantValue) } } }) } } func TestUnmarshalInvalidTagBignum(t *testing.T) { typeBigIntSlice := reflect.TypeOf([]big.Int{}) testCases := []struct { name string cborData []byte decodeToTypes []reflect.Type wantErrorMsg string }{ { name: "Tag 2 with string", cborData: hexDecode("c27f657374726561646d696e67ff"), decodeToTypes: []reflect.Type{typeIntf, typeBigInt}, wantErrorMsg: "cbor: tag number 2 or 3 must be followed by byte string, got UTF-8 text string", }, { name: "Tag 3 with string", cborData: hexDecode("c37f657374726561646d696e67ff"), decodeToTypes: []reflect.Type{typeIntf, typeBigInt}, wantErrorMsg: "cbor: tag number 2 or 3 must be followed by byte string, got UTF-8 text string", }, { name: "Tag 3 with negavtive int", cborData: hexDecode("81C330"), // [3(-17)] decodeToTypes: []reflect.Type{typeIntf, typeBigIntSlice}, wantErrorMsg: "cbor: tag number 2 or 3 must be followed by byte string, got negative integer", }, } for _, tc := range testCases { for _, decodeToType := range tc.decodeToTypes { t.Run(tc.name+" decode to "+decodeToType.String(), func(t *testing.T) { v := reflect.New(decodeToType) if err := Unmarshal(tc.cborData, v.Interface()); err == nil { t.Errorf("Unmarshal(0x%x) didn't return error, want error msg %q", tc.cborData, tc.wantErrorMsg) } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", tc.cborData, err, tc.wantErrorMsg) } }) } } } type Foo interface { Foo() string } type UintFoo uint func (f *UintFoo) Foo() string { return fmt.Sprint(f) } type IntFoo int func (f *IntFoo) Foo() string { return fmt.Sprint(*f) } type ByteFoo []byte func (f *ByteFoo) Foo() string { return fmt.Sprint(*f) } type StringFoo string func (f *StringFoo) Foo() string { return string(*f) } type ArrayFoo []int func (f *ArrayFoo) Foo() string { return fmt.Sprint(*f) } type MapFoo map[int]int func (f *MapFoo) Foo() string { return fmt.Sprint(*f) } type StructFoo struct { Value int `cbor:"1,keyasint"` } func (f *StructFoo) Foo() string { return fmt.Sprint(*f) } type TestExample struct { Message string `cbor:"1,keyasint"` Foo Foo `cbor:"2,keyasint"` } func TestUnmarshalToInterface(t *testing.T) { uintFoo, uintFoo123 := UintFoo(0), UintFoo(123) intFoo, intFooNeg1 := IntFoo(0), IntFoo(-1) byteFoo, byteFoo123 := ByteFoo(nil), ByteFoo([]byte{1, 2, 3}) stringFoo, stringFoo123 := StringFoo(""), StringFoo("123") arrayFoo, arrayFoo123 := ArrayFoo(nil), ArrayFoo([]int{1, 2, 3}) mapFoo, mapFoo123 := MapFoo(nil), MapFoo(map[int]int{1: 1, 2: 2, 3: 3}) em, _ := EncOptions{Sort: SortCanonical}.EncMode() testCases := []struct { name string data []byte v *TestExample unmarshalToObj *TestExample }{ { name: "uint", data: hexDecode("a2016b736f6d65206d657373676502187b"), // {1: "some messge", 2: 123} v: &TestExample{ Message: "some messge", Foo: &uintFoo123, }, unmarshalToObj: &TestExample{Foo: &uintFoo}, }, { name: "int", data: hexDecode("a2016b736f6d65206d65737367650220"), // {1: "some messge", 2: -1} v: &TestExample{ Message: "some messge", Foo: &intFooNeg1, }, unmarshalToObj: &TestExample{Foo: &intFoo}, }, { name: "bytes", data: hexDecode("a2016b736f6d65206d65737367650243010203"), // {1: "some messge", 2: [1,2,3]} v: &TestExample{ Message: "some messge", Foo: &byteFoo123, }, unmarshalToObj: &TestExample{Foo: &byteFoo}, }, { name: "string", data: hexDecode("a2016b736f6d65206d65737367650263313233"), // {1: "some messge", 2: "123"} v: &TestExample{ Message: "some messge", Foo: &stringFoo123, }, unmarshalToObj: &TestExample{Foo: &stringFoo}, }, { name: "array", data: hexDecode("a2016b736f6d65206d65737367650283010203"), // {1: "some messge", 2: []int{1,2,3}} v: &TestExample{ Message: "some messge", Foo: &arrayFoo123, }, unmarshalToObj: &TestExample{Foo: &arrayFoo}, }, { name: "map", data: hexDecode("a2016b736f6d65206d657373676502a3010102020303"), // {1: "some messge", 2: map[int]int{1:1,2:2,3:3}} v: &TestExample{ Message: "some messge", Foo: &mapFoo123, }, unmarshalToObj: &TestExample{Foo: &mapFoo}, }, { name: "struct", data: hexDecode("a2016b736f6d65206d657373676502a1011901c8"), // {1: "some messge", 2: {1: 456}} v: &TestExample{ Message: "some messge", Foo: &StructFoo{Value: 456}, }, unmarshalToObj: &TestExample{Foo: &StructFoo{}}, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { data, err := em.Marshal(tc.v) if err != nil { t.Errorf("Marshal(%+v) returned error %v", tc.v, err) } else if !bytes.Equal(data, tc.data) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.v, data, tc.v) } // Unmarshal to empty interface var einterface TestExample if err = Unmarshal(data, &einterface); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error (*UnmarshalTypeError)", data) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*UnmarshalTypeError)", data, err) } // Unmarshal to interface value err = Unmarshal(data, tc.unmarshalToObj) if err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", data, err) } else if !reflect.DeepEqual(tc.unmarshalToObj, tc.v) { t.Errorf("Unmarshal(0x%x) = %v, want %v", data, tc.unmarshalToObj, tc.v) } }) } } type Bar struct { I int } func (b *Bar) Foo() string { return fmt.Sprint(*b) } type FooStruct struct { Foos []Foo } func TestUnmarshalTaggedDataToInterface(t *testing.T) { var tags = NewTagSet() err := tags.Add( TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, reflect.TypeOf(&Bar{}), 4, ) if err != nil { t.Error(err) } v := &FooStruct{ Foos: []Foo{&Bar{1}}, } want := hexDecode("a164466f6f7381c4a1614901") // {"Foos": [4({"I": 1})]} em, _ := EncOptions{}.EncModeWithTags(tags) data, err := em.Marshal(v) if err != nil { t.Errorf("Marshal(%+v) returned error %v", v, err) } else if !bytes.Equal(data, want) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, data, want) } dm, _ := DecOptions{}.DecModeWithTags(tags) // Unmarshal to empty interface var v1 Bar if err = dm.Unmarshal(data, &v1); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error (*UnmarshalTypeError)", data) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*UnmarshalTypeError)", data, err) } // Unmarshal to interface value v2 := &FooStruct{ Foos: []Foo{&Bar{}}, } err = dm.Unmarshal(data, v2) if err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", data, err) } else if !reflect.DeepEqual(v2, v) { 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) } } }) } } golang-github-fxamacker-cbor-2.4.0/doc.go000066400000000000000000000104001416612535600202260ustar00rootroot00000000000000// Copyright (c) Faye Amacker. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. /* 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. Struct tags like "keyasint", "toarray" and "omitempty" make CBOR data smaller and easier to use with structs. For example, "toarray" tag makes struct fields encode to CBOR array elements. And "keyasint" makes a field encode to an element of CBOR map with specified int key. Latest docs can be viewed at https://github.com/fxamacker/cbor#cbor-library-in-go Basics The Quick Start guide is at https://github.com/fxamacker/cbor#quick-start Function signatures identical to encoding/json include: Marshal, Unmarshal, NewEncoder, NewDecoder, (*Encoder).Encode, (*Decoder).Decode. Standard interfaces include: BinaryMarshaler, BinaryUnmarshaler, Marshaler, and Unmarshaler. Custom encoding and decoding is possible by implementing standard interfaces for user-defined Go types. Codec functions are available at package-level (using defaults options) or by creating modes from options at runtime. "Mode" in this API means definite way of encoding (EncMode) or decoding (DecMode). EncMode and DecMode interfaces are created from EncOptions or DecOptions structs. em := cbor.EncOptions{...}.EncMode() em := cbor.CanonicalEncOptions().EncMode() em := 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. Modes are intended to be reused and are safe for concurrent use. EncMode and DecMode Interfaces // 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 } // 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 } Using Default Encoding Mode b, err := cbor.Marshal(v) encoder := cbor.NewEncoder(w) err = encoder.Encode(v) Using Default Decoding Mode err := cbor.Unmarshal(b, &v) decoder := cbor.NewDecoder(r) err = decoder.Decode(&v) Creating and Using Encoding Modes // Create EncOptions using either struct literal or a function. opts := cbor.CanonicalEncOptions() // If needed, modify encoding options 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) // or encoder := em.NewEncoder(w) err := encoder.Encode(v) // NOTE: Both em.Marshal(v) and encoder.Encode(v) use encoding options // specified during creation of em (encoding mode). CBOR Options Predefined Encoding Options: https://github.com/fxamacker/cbor#predefined-encoding-options Encoding Options: https://github.com/fxamacker/cbor#encoding-options Decoding Options: https://github.com/fxamacker/cbor#decoding-options Struct Tags Struct tags like `cbor:"name,omitempty"` and `json:"name,omitempty"` work as expected. If both struct tags are specified then `cbor` is used. Struct tags like "keyasint", "toarray", and "omitempty" make it easy to use very compact formats like COSE and CWT (CBOR Web Tokens) with structs. For example, "toarray" makes struct fields encode to array elements. And "keyasint" makes struct fields encode to elements of CBOR map with int keys. https://raw.githubusercontent.com/fxamacker/images/master/cbor/v2.0.0/cbor_easy_api.png Struct tags are listed at https://github.com/fxamacker/cbor#struct-tags-1 Tests and Fuzzing Over 375 tests are included in this package. Cover-guided fuzzing is handled by fxamacker/cbor-fuzz. */ package cbor golang-github-fxamacker-cbor-2.4.0/encode.go000066400000000000000000001216441416612535600207330ustar00rootroot00000000000000// 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" "encoding/binary" "errors" "io" "math" "math/big" "reflect" "sort" "strconv" "sync" "time" "github.com/x448/float16" ) // Marshal returns the CBOR encoding of v using default encoding options. // See EncOptions for encoding options. // // Marshal uses the following encoding rules: // // If value implements the Marshaler interface, Marshal calls its // MarshalCBOR method. // // If value implements encoding.BinaryMarshaler, Marhsal calls its // MarshalBinary method and encode it as CBOR byte string. // // Boolean values encode as CBOR booleans (type 7). // // Positive integer values encode as CBOR positive integers (type 0). // // Negative integer values encode as CBOR negative integers (type 1). // // Floating point values encode as CBOR floating points (type 7). // // String values encode as CBOR text strings (type 3). // // []byte values encode as CBOR byte strings (type 2). // // Array and slice values encode as CBOR arrays (type 4). // // Map values encode as CBOR maps (type 5). // // Struct values encode as CBOR maps (type 5). Each exported struct field // becomes a pair with field name encoded as CBOR text string (type 3) and // field value encoded based on its type. See struct tag option "keyasint" // to encode field name as CBOR integer (type 0 and 1). Also see struct // tag option "toarray" for special field "_" to encode struct values as // CBOR array (type 4). // // Marshal supports format string stored under the "cbor" key in the struct // field's tag. CBOR format string can specify the name of the field, // "omitempty" and "keyasint" options, and special case "-" for field omission. // If "cbor" key is absent, Marshal uses "json" key. // // Struct field name is treated as integer if it has "keyasint" option in // its format string. The format string must specify an integer as its // field name. // // Special struct field "_" is used to specify struct level options, such as // "toarray". "toarray" option enables Go struct to be encoded as CBOR array. // "omitempty" is disabled by "toarray" to ensure that the same number // of elements are encoded every time. // // Anonymous struct fields are marshaled as if their exported fields // were fields in the outer struct. Marshal follows the same struct fields // visibility rules used by JSON encoding package. // // time.Time values encode as text strings specified in RFC3339 or numerical // representation of seconds since January 1, 1970 UTC depending on // EncOptions.Time setting. Also See EncOptions.TimeTag to encode // time.Time as CBOR tag with tag number 0 or 1. // // big.Int values encode as CBOR integers (type 0 and 1) if values fit. // Otherwise, big.Int values encode as CBOR bignums (tag 2 and 3). See // EncOptions.BigIntConvert to always encode big.Int values as CBOR // bignums. // // Pointer values encode as the value pointed to. // // Interface values encode as the value stored in the interface. // // Nil slice/map/pointer/interface values encode as CBOR nulls (type 7). // // Values of other types cannot be encoded in CBOR. Attempting // to encode such a value causes Marshal to return an UnsupportedTypeError. func Marshal(v interface{}) ([]byte, error) { return defaultEncMode.Marshal(v) } // Marshaler is the interface implemented by types that can marshal themselves // into valid CBOR. type Marshaler interface { MarshalCBOR() ([]byte, error) } // UnsupportedTypeError is returned by Marshal when attempting to encode value // of an unsupported type. type UnsupportedTypeError struct { Type reflect.Type } func (e *UnsupportedTypeError) Error() string { return "cbor: unsupported type: " + e.Type.String() } // SortMode identifies supported sorting order. type SortMode int const ( // SortNone means no sorting. SortNone SortMode = 0 // SortLengthFirst causes map keys or struct fields to be sorted such that: // - If two keys have different lengths, the shorter one sorts earlier; // - If two keys have the same length, the one with the lower value in // (byte-wise) lexical order sorts earlier. // It is used in "Canonical CBOR" encoding in RFC 7049 3.9. SortLengthFirst SortMode = 1 // SortBytewiseLexical causes map keys or struct fields to be sorted in the // bytewise lexicographic order of their deterministic CBOR encodings. // It is used in "CTAP2 Canonical CBOR" and "Core Deterministic Encoding" // in RFC 7049bis. SortBytewiseLexical SortMode = 2 // SortCanonical is used in "Canonical CBOR" encoding in RFC 7049 3.9. SortCanonical SortMode = SortLengthFirst // SortCTAP2 is used in "CTAP2 Canonical CBOR". SortCTAP2 SortMode = SortBytewiseLexical // SortCoreDeterministic is used in "Core Deterministic Encoding" in RFC 7049bis. SortCoreDeterministic SortMode = SortBytewiseLexical maxSortMode SortMode = 3 ) func (sm SortMode) valid() bool { return sm < maxSortMode } // ShortestFloatMode specifies which floating-point format should // be used as the shortest possible format for CBOR encoding. // It is not used for encoding Infinity and NaN values. type ShortestFloatMode int const ( // ShortestFloatNone makes float values encode without any conversion. // This is the default for ShortestFloatMode in v1. // E.g. a float32 in Go will encode to CBOR float32. And // a float64 in Go will encode to CBOR float64. ShortestFloatNone ShortestFloatMode = iota // ShortestFloat16 specifies float16 as the shortest form that preserves value. // E.g. if float64 can convert to float32 while preserving value, then // encoding will also try to convert float32 to float16. So a float64 might // encode as CBOR float64, float32 or float16 depending on the value. ShortestFloat16 maxShortestFloat ) func (sfm ShortestFloatMode) valid() bool { return sfm < maxShortestFloat } // NaNConvertMode specifies how to encode NaN and overrides ShortestFloatMode. // ShortestFloatMode is not used for encoding Infinity and NaN values. type NaNConvertMode int const ( // NaNConvert7e00 always encodes NaN to 0xf97e00 (CBOR float16 = 0x7e00). NaNConvert7e00 NaNConvertMode = iota // NaNConvertNone never modifies or converts NaN to other representations // (float64 NaN stays float64, etc. even if it can use float16 without losing // any bits). NaNConvertNone // NaNConvertPreserveSignal converts NaN to the smallest form that preserves // value (quiet bit + payload) as described in RFC 7049bis Draft 12. NaNConvertPreserveSignal // NaNConvertQuiet always forces quiet bit = 1 and shortest form that preserves // NaN payload. NaNConvertQuiet maxNaNConvert ) func (ncm NaNConvertMode) valid() bool { return ncm < maxNaNConvert } // InfConvertMode specifies how to encode Infinity and overrides ShortestFloatMode. // ShortestFloatMode is not used for encoding Infinity and NaN values. type InfConvertMode int const ( // InfConvertFloat16 always converts Inf to lossless IEEE binary16 (float16). InfConvertFloat16 InfConvertMode = iota // InfConvertNone never converts (used by CTAP2 Canonical CBOR). InfConvertNone maxInfConvert ) func (icm InfConvertMode) valid() bool { return icm < maxInfConvert } // TimeMode specifies how to encode time.Time values. type TimeMode int const ( // TimeUnix causes time.Time to be encoded as epoch time in integer with second precision. TimeUnix TimeMode = iota // TimeUnixMicro causes time.Time to be encoded as epoch time in float-point rounded to microsecond precision. TimeUnixMicro // TimeUnixDynamic causes time.Time to be encoded as integer if time.Time doesn't have fractional seconds, // otherwise float-point rounded to microsecond precision. TimeUnixDynamic // TimeRFC3339 causes time.Time to be encoded as RFC3339 formatted string with second precision. TimeRFC3339 // TimeRFC3339Nano causes time.Time to be encoded as RFC3339 formatted string with nanosecond precision. TimeRFC3339Nano maxTimeMode ) func (tm TimeMode) valid() bool { return tm < maxTimeMode } // BigIntConvertMode specifies how to encode big.Int values. 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). BigIntConvertShortest BigIntConvertMode = iota // BigIntConvertNone makes big.Int encode to CBOR bignum (tag 2 or 3) without // converting it to another CBOR type. BigIntConvertNone maxBigIntConvert ) func (bim BigIntConvertMode) valid() bool { return bim < maxBigIntConvert } // EncOptions specifies encoding options. type EncOptions struct { // Sort specifies sorting order. Sort SortMode // ShortestFloat specifies the shortest floating-point encoding that preserves // the value being encoded. ShortestFloat ShortestFloatMode // NaNConvert specifies how to encode NaN and it overrides ShortestFloatMode. NaNConvert NaNConvertMode // InfConvert specifies how to encode Inf and it overrides ShortestFloatMode. InfConvert InfConvertMode // BigIntConvert specifies how to encode big.Int values. BigIntConvert BigIntConvertMode // Time specifies how to encode time.Time. Time TimeMode // TimeTag allows time.Time to be encoded with a tag number. // RFC3339 format gets tag number 0, and numeric epoch time tag number 1. TimeTag EncTagMode // IndefLength specifies whether to allow indefinite length CBOR items. IndefLength IndefLengthMode // TagsMd specifies whether to allow CBOR tags (major type 6). TagsMd TagsMode } // CanonicalEncOptions returns EncOptions for "Canonical CBOR" encoding, // defined in RFC 7049 Section 3.9 with the following rules: // // 1. "Integers must be as small as possible." // 2. "The expression of lengths in major types 2 through 5 must be as short as possible." // 3. The keys in every map must be sorted in length-first sorting order. // See SortLengthFirst for details. // 4. "Indefinite-length items must be made into definite-length items." // 5. "If a protocol allows for IEEE floats, then additional canonicalization rules might // need to be added. One example rule might be to have all floats start as a 64-bit // float, then do a test conversion to a 32-bit float; if the result is the same numeric // value, use the shorter value and repeat the process with a test conversion to a // 16-bit float. (This rule selects 16-bit float for positive and negative Infinity // as well.) Also, there are many representations for NaN. If NaN is an allowed value, // it must always be represented as 0xf97e00." // func CanonicalEncOptions() EncOptions { return EncOptions{ Sort: SortCanonical, ShortestFloat: ShortestFloat16, NaNConvert: NaNConvert7e00, InfConvert: InfConvertFloat16, IndefLength: IndefLengthForbidden, } } // CTAP2EncOptions returns EncOptions for "CTAP2 Canonical CBOR" encoding, // defined in CTAP specification, with the following rules: // // 1. "Integers must be encoded as small as possible." // 2. "The representations of any floating-point values are not changed." // 3. "The expression of lengths in major types 2 through 5 must be as short as possible." // 4. "Indefinite-length items must be made into definite-length items."" // 5. The keys in every map must be sorted in bytewise lexicographic order. // See SortBytewiseLexical for details. // 6. "Tags as defined in Section 2.4 in [RFC7049] MUST NOT be present." // func CTAP2EncOptions() EncOptions { return EncOptions{ Sort: SortCTAP2, ShortestFloat: ShortestFloatNone, NaNConvert: NaNConvertNone, InfConvert: InfConvertNone, IndefLength: IndefLengthForbidden, TagsMd: TagsForbidden, } } // CoreDetEncOptions returns EncOptions for "Core Deterministic" encoding, // defined in RFC 7049bis with the following rules: // // 1. "Preferred serialization MUST be used. In particular, this means that arguments // (see Section 3) for integers, lengths in major types 2 through 5, and tags MUST // be as short as possible" // "Floating point values also MUST use the shortest form that preserves the value" // 2. "Indefinite-length items MUST NOT appear." // 3. "The keys in every map MUST be sorted in the bytewise lexicographic order of // their deterministic encodings." // func CoreDetEncOptions() EncOptions { return EncOptions{ Sort: SortCoreDeterministic, ShortestFloat: ShortestFloat16, NaNConvert: NaNConvert7e00, InfConvert: InfConvertFloat16, IndefLength: IndefLengthForbidden, } } // PreferredUnsortedEncOptions returns EncOptions for "Preferred Serialization" encoding, // defined in RFC 7049bis with the following rules: // // 1. "The preferred serialization always uses the shortest form of representing the argument // (Section 3);" // 2. "it also uses the shortest floating-point encoding that preserves the value being // encoded (see Section 5.5)." // "The preferred encoding for a floating-point value is the shortest floating-point encoding // that preserves its value, e.g., 0xf94580 for the number 5.5, and 0xfa45ad9c00 for the // number 5555.5, unless the CBOR-based protocol specifically excludes the use of the shorter // floating-point encodings. For NaN values, a shorter encoding is preferred if zero-padding // the shorter significand towards the right reconstitutes the original NaN value (for many // applications, the single NaN encoding 0xf97e00 will suffice)." // 3. "Definite length encoding is preferred whenever the length is known at the time the // serialization of the item starts." // func PreferredUnsortedEncOptions() EncOptions { return EncOptions{ Sort: SortNone, ShortestFloat: ShortestFloat16, NaNConvert: NaNConvert7e00, InfConvert: InfConvertFloat16, } } // EncMode returns EncMode with immutable options and no tags (safe for concurrency). func (opts EncOptions) EncMode() (EncMode, error) { return opts.encMode() } // EncModeWithTags returns EncMode with options and tags that are both immutable (safe for concurrency). func (opts EncOptions) EncModeWithTags(tags TagSet) (EncMode, error) { if opts.TagsMd == TagsForbidden { return nil, errors.New("cbor: cannot create EncMode with TagSet when TagsMd is TagsForbidden") } if tags == nil { return nil, errors.New("cbor: cannot create EncMode with nil value as TagSet") } em, err := opts.encMode() if err != nil { return nil, err } // Copy tags ts := tagSet(make(map[reflect.Type]*tagItem)) syncTags := tags.(*syncTagSet) syncTags.RLock() for contentType, tag := range syncTags.t { if tag.opts.EncTag != EncTagNone { ts[contentType] = tag } } syncTags.RUnlock() if len(ts) > 0 { em.tags = ts } return em, nil } // EncModeWithSharedTags returns EncMode with immutable options and mutable shared tags (safe for concurrency). func (opts EncOptions) EncModeWithSharedTags(tags TagSet) (EncMode, error) { if opts.TagsMd == TagsForbidden { return nil, errors.New("cbor: cannot create EncMode with TagSet when TagsMd is TagsForbidden") } if tags == nil { return nil, errors.New("cbor: cannot create EncMode with nil value as TagSet") } em, err := opts.encMode() if err != nil { return nil, err } em.tags = tags return em, nil } func (opts EncOptions) encMode() (*encMode, error) { if !opts.Sort.valid() { return nil, errors.New("cbor: invalid SortMode " + strconv.Itoa(int(opts.Sort))) } if !opts.ShortestFloat.valid() { return nil, errors.New("cbor: invalid ShortestFloatMode " + strconv.Itoa(int(opts.ShortestFloat))) } if !opts.NaNConvert.valid() { return nil, errors.New("cbor: invalid NaNConvertMode " + strconv.Itoa(int(opts.NaNConvert))) } if !opts.InfConvert.valid() { return nil, errors.New("cbor: invalid InfConvertMode " + strconv.Itoa(int(opts.InfConvert))) } if !opts.BigIntConvert.valid() { return nil, errors.New("cbor: invalid BigIntConvertMode " + strconv.Itoa(int(opts.BigIntConvert))) } if !opts.Time.valid() { return nil, errors.New("cbor: invalid TimeMode " + strconv.Itoa(int(opts.Time))) } if !opts.TimeTag.valid() { return nil, errors.New("cbor: invalid TimeTag " + strconv.Itoa(int(opts.TimeTag))) } if !opts.IndefLength.valid() { return nil, errors.New("cbor: invalid IndefLength " + strconv.Itoa(int(opts.IndefLength))) } if !opts.TagsMd.valid() { return nil, errors.New("cbor: invalid TagsMd " + strconv.Itoa(int(opts.TagsMd))) } if opts.TagsMd == TagsForbidden && opts.TimeTag == EncTagRequired { return nil, errors.New("cbor: cannot set TagsMd to TagsForbidden when TimeTag is EncTagRequired") } em := encMode{ sort: opts.Sort, shortestFloat: opts.ShortestFloat, nanConvert: opts.NaNConvert, infConvert: opts.InfConvert, bigIntConvert: opts.BigIntConvert, time: opts.Time, timeTag: opts.TimeTag, indefLength: opts.IndefLength, tagsMd: opts.TagsMd, } return &em, nil } // EncMode is the main interface for CBOR encoding. type EncMode interface { Marshal(v interface{}) ([]byte, error) NewEncoder(w io.Writer) *Encoder EncOptions() EncOptions } type encMode struct { tags tagProvider sort SortMode shortestFloat ShortestFloatMode nanConvert NaNConvertMode infConvert InfConvertMode bigIntConvert BigIntConvertMode time TimeMode timeTag EncTagMode indefLength IndefLengthMode tagsMd TagsMode } var defaultEncMode = &encMode{} // EncOptions returns user specified options used to create this EncMode. func (em *encMode) EncOptions() EncOptions { return EncOptions{ Sort: em.sort, ShortestFloat: em.shortestFloat, NaNConvert: em.nanConvert, InfConvert: em.infConvert, BigIntConvert: em.bigIntConvert, Time: em.time, TimeTag: em.timeTag, IndefLength: em.indefLength, TagsMd: em.tagsMd, } } func (em *encMode) encTagBytes(t reflect.Type) []byte { if em.tags != nil { if tagItem := em.tags.getTagItemFromType(t); tagItem != nil { return tagItem.cborTagNum } } return nil } // Marshal returns the CBOR encoding of v using em encoding mode. // // See the documentation for Marshal for details. func (em *encMode) Marshal(v interface{}) ([]byte, error) { e := getEncoderBuffer() if err := encode(e, em, reflect.ValueOf(v)); err != nil { putEncoderBuffer(e) return nil, err } buf := make([]byte, e.Len()) copy(buf, e.Bytes()) putEncoderBuffer(e) return buf, nil } // 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()} } type encoderBuffer struct { bytes.Buffer scratch [16]byte } // encoderBufferPool caches unused encoderBuffer objects for later reuse. var encoderBufferPool = sync.Pool{ New: func() interface{} { e := new(encoderBuffer) e.Grow(32) // TODO: make this configurable return e }, } func getEncoderBuffer() *encoderBuffer { return encoderBufferPool.Get().(*encoderBuffer) } func putEncoderBuffer(e *encoderBuffer) { e.Reset() encoderBufferPool.Put(e) } type encodeFunc func(e *encoderBuffer, em *encMode, v reflect.Value) error type isEmptyFunc func(v reflect.Value) (empty bool, err error) var ( cborFalse = []byte{0xf4} cborTrue = []byte{0xf5} cborNil = []byte{0xf6} cborNaN = []byte{0xf9, 0x7e, 0x00} cborPositiveInfinity = []byte{0xf9, 0x7c, 0x00} cborNegativeInfinity = []byte{0xf9, 0xfc, 0x00} ) func encode(e *encoderBuffer, em *encMode, v reflect.Value) error { if !v.IsValid() { // v is zero value e.Write(cborNil) return nil } vt := v.Type() f, _ := getEncodeFunc(vt) if f == nil { return &UnsupportedTypeError{vt} } return f(e, em, v) } func encodeBool(e *encoderBuffer, em *encMode, v reflect.Value) error { if b := em.encTagBytes(v.Type()); b != nil { e.Write(b) } b := cborFalse if v.Bool() { b = cborTrue } e.Write(b) return nil } func encodeInt(e *encoderBuffer, em *encMode, v reflect.Value) error { if b := em.encTagBytes(v.Type()); b != nil { e.Write(b) } i := v.Int() if i >= 0 { encodeHead(e, byte(cborTypePositiveInt), uint64(i)) return nil } i = i*(-1) - 1 encodeHead(e, byte(cborTypeNegativeInt), uint64(i)) return nil } func encodeUint(e *encoderBuffer, em *encMode, v reflect.Value) error { if b := em.encTagBytes(v.Type()); b != nil { e.Write(b) } encodeHead(e, byte(cborTypePositiveInt), v.Uint()) return nil } func encodeFloat(e *encoderBuffer, em *encMode, v reflect.Value) error { if b := em.encTagBytes(v.Type()); b != nil { e.Write(b) } f64 := v.Float() if math.IsNaN(f64) { return encodeNaN(e, em, v) } if math.IsInf(f64, 0) { return encodeInf(e, em, v) } fopt := em.shortestFloat if v.Kind() == reflect.Float64 && (fopt == ShortestFloatNone || cannotFitFloat32(f64)) { // Encode float64 // Don't use encodeFloat64() because it cannot be inlined. e.scratch[0] = byte(cborTypePrimitives) | byte(27) binary.BigEndian.PutUint64(e.scratch[1:], math.Float64bits(f64)) e.Write(e.scratch[:9]) return nil } f32 := float32(f64) if fopt == ShortestFloat16 { var f16 float16.Float16 p := float16.PrecisionFromfloat32(f32) if p == float16.PrecisionExact { // Roundtrip float32->float16->float32 test isn't needed. f16 = float16.Fromfloat32(f32) } else if p == float16.PrecisionUnknown { // Try roundtrip float32->float16->float32 to determine if float32 can fit into float16. f16 = float16.Fromfloat32(f32) if f16.Float32() == f32 { p = float16.PrecisionExact } } if p == float16.PrecisionExact { // Encode float16 // Don't use encodeFloat16() because it cannot be inlined. e.scratch[0] = byte(cborTypePrimitives) | byte(25) binary.BigEndian.PutUint16(e.scratch[1:], uint16(f16)) e.Write(e.scratch[:3]) return nil } } // Encode float32 // Don't use encodeFloat32() because it cannot be inlined. e.scratch[0] = byte(cborTypePrimitives) | byte(26) binary.BigEndian.PutUint32(e.scratch[1:], math.Float32bits(f32)) e.Write(e.scratch[:5]) return nil } func encodeInf(e *encoderBuffer, em *encMode, v reflect.Value) error { f64 := v.Float() if em.infConvert == InfConvertFloat16 { if f64 > 0 { e.Write(cborPositiveInfinity) } else { e.Write(cborNegativeInfinity) } return nil } if v.Kind() == reflect.Float64 { return encodeFloat64(e, f64) } return encodeFloat32(e, float32(f64)) } func encodeNaN(e *encoderBuffer, em *encMode, v reflect.Value) error { switch em.nanConvert { case NaNConvert7e00: e.Write(cborNaN) return nil case NaNConvertNone: if v.Kind() == reflect.Float64 { return encodeFloat64(e, v.Float()) } f32 := float32NaNFromReflectValue(v) return encodeFloat32(e, f32) default: // NaNConvertPreserveSignal, NaNConvertQuiet if v.Kind() == reflect.Float64 { f64 := v.Float() f64bits := math.Float64bits(f64) if em.nanConvert == NaNConvertQuiet && f64bits&(1<<51) == 0 { f64bits |= 1 << 51 // Set quiet bit = 1 f64 = math.Float64frombits(f64bits) } // The lower 29 bits are dropped when converting from float64 to float32. if f64bits&0x1fffffff != 0 { // Encode NaN as float64 because dropped coef bits from float64 to float32 are not all 0s. return encodeFloat64(e, f64) } // Create float32 from float64 manually because float32(f64) always turns on NaN's quiet bits. sign := uint32(f64bits>>32) & (1 << 31) exp := uint32(0x7f800000) coef := uint32((f64bits & 0xfffffffffffff) >> 29) f32bits := sign | exp | coef f32 := math.Float32frombits(f32bits) // The lower 13 bits are dropped when converting from float32 to float16. if f32bits&0x1fff != 0 { // Encode NaN as float32 because dropped coef bits from float32 to float16 are not all 0s. return encodeFloat32(e, f32) } // Encode NaN as float16 f16, _ := float16.FromNaN32ps(f32) // Ignore err because it only returns error when f32 is not a NaN. return encodeFloat16(e, f16) } f32 := float32NaNFromReflectValue(v) f32bits := math.Float32bits(f32) if em.nanConvert == NaNConvertQuiet && f32bits&(1<<22) == 0 { f32bits |= 1 << 22 // Set quiet bit = 1 f32 = math.Float32frombits(f32bits) } // The lower 13 bits are dropped coef bits when converting from float32 to float16. if f32bits&0x1fff != 0 { // Encode NaN as float32 because dropped coef bits from float32 to float16 are not all 0s. return encodeFloat32(e, f32) } f16, _ := float16.FromNaN32ps(f32) // Ignore err because it only returns error when f32 is not a NaN. return encodeFloat16(e, f16) } } func encodeFloat16(e *encoderBuffer, f16 float16.Float16) error { e.scratch[0] = byte(cborTypePrimitives) | byte(25) binary.BigEndian.PutUint16(e.scratch[1:], uint16(f16)) e.Write(e.scratch[:3]) return nil } func encodeFloat32(e *encoderBuffer, f32 float32) error { e.scratch[0] = byte(cborTypePrimitives) | byte(26) binary.BigEndian.PutUint32(e.scratch[1:], math.Float32bits(f32)) e.Write(e.scratch[:5]) return nil } func encodeFloat64(e *encoderBuffer, f64 float64) error { e.scratch[0] = byte(cborTypePrimitives) | byte(27) binary.BigEndian.PutUint64(e.scratch[1:], math.Float64bits(f64)) e.Write(e.scratch[:9]) return nil } func encodeByteString(e *encoderBuffer, em *encMode, v reflect.Value) error { vk := v.Kind() if vk == reflect.Slice && v.IsNil() { e.Write(cborNil) return nil } if b := em.encTagBytes(v.Type()); b != nil { e.Write(b) } slen := v.Len() if slen == 0 { return e.WriteByte(byte(cborTypeByteString)) } encodeHead(e, byte(cborTypeByteString), uint64(slen)) if vk == reflect.Array { for i := 0; i < slen; i++ { e.WriteByte(byte(v.Index(i).Uint())) } return nil } e.Write(v.Bytes()) return nil } func encodeString(e *encoderBuffer, em *encMode, v reflect.Value) error { if b := em.encTagBytes(v.Type()); b != nil { e.Write(b) } s := v.String() encodeHead(e, byte(cborTypeTextString), uint64(len(s))) e.WriteString(s) return nil } type arrayEncodeFunc struct { f encodeFunc } func (ae arrayEncodeFunc) encode(e *encoderBuffer, em *encMode, v reflect.Value) error { if v.Kind() == reflect.Slice && v.IsNil() { e.Write(cborNil) return nil } if b := em.encTagBytes(v.Type()); b != nil { e.Write(b) } alen := v.Len() if alen == 0 { return e.WriteByte(byte(cborTypeArray)) } encodeHead(e, byte(cborTypeArray), uint64(alen)) for i := 0; i < alen; i++ { if err := ae.f(e, em, v.Index(i)); err != nil { return err } } return nil } type mapEncodeFunc struct { kf, ef encodeFunc } func (me mapEncodeFunc) encode(e *encoderBuffer, em *encMode, v reflect.Value) error { if v.IsNil() { e.Write(cborNil) return nil } if b := em.encTagBytes(v.Type()); b != nil { e.Write(b) } mlen := v.Len() if mlen == 0 { return e.WriteByte(byte(cborTypeMap)) } if em.sort != SortNone { return me.encodeCanonical(e, em, v) } encodeHead(e, byte(cborTypeMap), uint64(mlen)) iter := v.MapRange() for iter.Next() { if err := me.kf(e, em, iter.Key()); err != nil { return err } if err := me.ef(e, em, iter.Value()); err != nil { return err } } return nil } type keyValue struct { keyCBORData, keyValueCBORData []byte keyLen, keyValueLen int } type bytewiseKeyValueSorter struct { kvs []keyValue } func (x *bytewiseKeyValueSorter) Len() int { return len(x.kvs) } func (x *bytewiseKeyValueSorter) Swap(i, j int) { x.kvs[i], x.kvs[j] = x.kvs[j], x.kvs[i] } func (x *bytewiseKeyValueSorter) Less(i, j int) bool { return bytes.Compare(x.kvs[i].keyCBORData, x.kvs[j].keyCBORData) <= 0 } type lengthFirstKeyValueSorter struct { kvs []keyValue } func (x *lengthFirstKeyValueSorter) Len() int { return len(x.kvs) } func (x *lengthFirstKeyValueSorter) Swap(i, j int) { x.kvs[i], x.kvs[j] = x.kvs[j], x.kvs[i] } func (x *lengthFirstKeyValueSorter) Less(i, j int) bool { if len(x.kvs[i].keyCBORData) != len(x.kvs[j].keyCBORData) { return len(x.kvs[i].keyCBORData) < len(x.kvs[j].keyCBORData) } return bytes.Compare(x.kvs[i].keyCBORData, x.kvs[j].keyCBORData) <= 0 } var keyValuePool = sync.Pool{} func getKeyValues(length int) *[]keyValue { v := keyValuePool.Get() if v == nil { y := make([]keyValue, length) return &y } x := v.(*[]keyValue) if cap(*x) >= length { *x = (*x)[:length] return x } // []keyValue from the pool does not have enough capacity. // Return it back to the pool and create a new one. keyValuePool.Put(x) y := make([]keyValue, length) return &y } func putKeyValues(x *[]keyValue) { *x = (*x)[:0] keyValuePool.Put(x) } func (me mapEncodeFunc) encodeCanonical(e *encoderBuffer, em *encMode, v reflect.Value) error { kve := getEncoderBuffer() // accumulated cbor encoded key-values kvsp := getKeyValues(v.Len()) // for sorting keys kvs := *kvsp iter := v.MapRange() for i := 0; iter.Next(); i++ { off := kve.Len() if err := me.kf(kve, em, iter.Key()); err != nil { putEncoderBuffer(kve) putKeyValues(kvsp) return err } n1 := kve.Len() - off if err := me.ef(kve, em, iter.Value()); err != nil { putEncoderBuffer(kve) putKeyValues(kvsp) return err } n2 := kve.Len() - off // Save key and keyvalue length to create slice later. kvs[i] = keyValue{keyLen: n1, keyValueLen: n2} } b := kve.Bytes() for i, off := 0, 0; i < len(kvs); i++ { kvs[i].keyCBORData = b[off : off+kvs[i].keyLen] kvs[i].keyValueCBORData = b[off : off+kvs[i].keyValueLen] off += kvs[i].keyValueLen } if em.sort == SortBytewiseLexical { sort.Sort(&bytewiseKeyValueSorter{kvs}) } else { sort.Sort(&lengthFirstKeyValueSorter{kvs}) } encodeHead(e, byte(cborTypeMap), uint64(len(kvs))) for i := 0; i < len(kvs); i++ { e.Write(kvs[i].keyValueCBORData) } putEncoderBuffer(kve) putKeyValues(kvsp) return nil } func encodeStructToArray(e *encoderBuffer, em *encMode, v reflect.Value) (err error) { structType, err := getEncodingStructType(v.Type()) if err != nil { return err } if b := em.encTagBytes(v.Type()); b != nil { e.Write(b) } flds := structType.fields encodeHead(e, byte(cborTypeArray), uint64(len(flds))) for i := 0; i < len(flds); i++ { f := flds[i] var fv reflect.Value if len(f.idx) == 1 { fv = v.Field(f.idx[0]) } else { // Get embedded field value. No error is expected. fv, _ = getFieldValue(v, f.idx, func(v reflect.Value) (reflect.Value, error) { // Write CBOR nil for null pointer to embedded struct e.Write(cborNil) return reflect.Value{}, nil }) if !fv.IsValid() { continue } } if err := f.ef(e, em, fv); err != nil { return err } } return nil } func encodeFixedLengthStruct(e *encoderBuffer, em *encMode, v reflect.Value, flds fields) error { if b := em.encTagBytes(v.Type()); b != nil { e.Write(b) } encodeHead(e, byte(cborTypeMap), uint64(len(flds))) for i := 0; i < len(flds); i++ { f := flds[i] e.Write(f.cborName) fv := v.Field(f.idx[0]) if err := f.ef(e, em, fv); err != nil { return err } } return nil } func encodeStruct(e *encoderBuffer, em *encMode, v reflect.Value) (err error) { structType, err := getEncodingStructType(v.Type()) if err != nil { return err } flds := structType.getFields(em) if structType.fixedLength { return encodeFixedLengthStruct(e, em, v, flds) } kve := getEncoderBuffer() // encode key-value pairs based on struct field tag options kvcount := 0 for i := 0; i < len(flds); i++ { f := flds[i] var fv reflect.Value if len(f.idx) == 1 { fv = v.Field(f.idx[0]) } else { // Get embedded field value. No error is expected. fv, _ = getFieldValue(v, f.idx, func(v reflect.Value) (reflect.Value, error) { // Skip null pointer to embedded struct return reflect.Value{}, nil }) if !fv.IsValid() { continue } } if f.omitEmpty { empty, err := f.ief(fv) if err != nil { putEncoderBuffer(kve) return err } if empty { continue } } kve.Write(f.cborName) if err := f.ef(kve, em, fv); err != nil { putEncoderBuffer(kve) return err } kvcount++ } if b := em.encTagBytes(v.Type()); b != nil { e.Write(b) } encodeHead(e, byte(cborTypeMap), uint64(kvcount)) e.Write(kve.Bytes()) putEncoderBuffer(kve) return nil } func encodeIntf(e *encoderBuffer, em *encMode, v reflect.Value) error { if v.IsNil() { e.Write(cborNil) return nil } return encode(e, em, v.Elem()) } func encodeTime(e *encoderBuffer, em *encMode, v reflect.Value) error { t := v.Interface().(time.Time) if t.IsZero() { e.Write(cborNil) // Even if tag is required, encode as CBOR null. return nil } if em.timeTag == EncTagRequired { tagNumber := 1 if em.time == TimeRFC3339 || em.time == TimeRFC3339Nano { tagNumber = 0 } encodeHead(e, byte(cborTypeTag), uint64(tagNumber)) } switch em.time { case TimeUnix: secs := t.Unix() return encodeInt(e, em, reflect.ValueOf(secs)) case TimeUnixMicro: t = t.UTC().Round(time.Microsecond) f := float64(t.UnixNano()) / 1e9 return encodeFloat(e, em, reflect.ValueOf(f)) case TimeUnixDynamic: t = t.UTC().Round(time.Microsecond) secs, nsecs := t.Unix(), uint64(t.Nanosecond()) if nsecs == 0 { return encodeInt(e, em, reflect.ValueOf(secs)) } f := float64(secs) + float64(nsecs)/1e9 return encodeFloat(e, em, reflect.ValueOf(f)) case TimeRFC3339: s := t.Format(time.RFC3339) return encodeString(e, em, reflect.ValueOf(s)) default: // TimeRFC3339Nano s := t.Format(time.RFC3339Nano) return encodeString(e, em, reflect.ValueOf(s)) } } func encodeBigInt(e *encoderBuffer, em *encMode, v reflect.Value) error { vbi := v.Interface().(big.Int) sign := vbi.Sign() bi := new(big.Int).SetBytes(vbi.Bytes()) // bi is absolute value of v if sign < 0 { // For negative number, convert to CBOR encoded number (-v-1). bi.Sub(bi, big.NewInt(1)) } if em.bigIntConvert == BigIntConvertShortest { if bi.IsUint64() { if sign >= 0 { // Encode as CBOR pos int (major type 0) encodeHead(e, byte(cborTypePositiveInt), bi.Uint64()) return nil } // Encode as CBOR neg int (major type 1) encodeHead(e, byte(cborTypeNegativeInt), bi.Uint64()) return nil } } tagNum := 2 if sign < 0 { tagNum = 3 } // Write tag number encodeHead(e, byte(cborTypeTag), uint64(tagNum)) // Write bignum byte string b := bi.Bytes() encodeHead(e, byte(cborTypeByteString), uint64(len(b))) e.Write(b) return nil } func encodeBinaryMarshalerType(e *encoderBuffer, em *encMode, v reflect.Value) error { vt := v.Type() m, ok := v.Interface().(encoding.BinaryMarshaler) if !ok { pv := reflect.New(vt) pv.Elem().Set(v) m = pv.Interface().(encoding.BinaryMarshaler) } data, err := m.MarshalBinary() if err != nil { return err } if b := em.encTagBytes(vt); b != nil { e.Write(b) } encodeHead(e, byte(cborTypeByteString), uint64(len(data))) e.Write(data) return nil } func encodeMarshalerType(e *encoderBuffer, em *encMode, v reflect.Value) error { if em.tagsMd == TagsForbidden && v.Type() == typeRawTag { return errors.New("cbor: cannot encode cbor.RawTag when TagsMd is TagsForbidden") } m, ok := v.Interface().(Marshaler) if !ok { pv := reflect.New(v.Type()) pv.Elem().Set(v) m = pv.Interface().(Marshaler) } data, err := m.MarshalCBOR() if err != nil { return err } e.Write(data) return nil } func encodeTag(e *encoderBuffer, em *encMode, v reflect.Value) error { if em.tagsMd == TagsForbidden { return errors.New("cbor: cannot encode cbor.Tag when TagsMd is TagsForbidden") } t := v.Interface().(Tag) if t.Number == 0 && t.Content == nil { // Marshal uninitialized cbor.Tag e.Write(cborNil) return nil } // Marshal tag number encodeHead(e, byte(cborTypeTag), t.Number) // Marshal tag content if err := encode(e, em, reflect.ValueOf(t.Content)); err != nil { return err } return nil } func encodeHead(e *encoderBuffer, t byte, n uint64) { if n <= 23 { e.WriteByte(t | byte(n)) return } if n <= math.MaxUint8 { e.scratch[0] = t | byte(24) e.scratch[1] = byte(n) e.Write(e.scratch[:2]) return } if n <= math.MaxUint16 { e.scratch[0] = t | byte(25) binary.BigEndian.PutUint16(e.scratch[1:], uint16(n)) e.Write(e.scratch[:3]) return } if n <= math.MaxUint32 { e.scratch[0] = t | byte(26) binary.BigEndian.PutUint32(e.scratch[1:], uint32(n)) e.Write(e.scratch[:5]) return } e.scratch[0] = t | byte(27) binary.BigEndian.PutUint64(e.scratch[1:], n) e.Write(e.scratch[:9]) } var ( typeMarshaler = reflect.TypeOf((*Marshaler)(nil)).Elem() typeBinaryMarshaler = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem() typeRawMessage = reflect.TypeOf(RawMessage(nil)) ) func getEncodeFuncInternal(t reflect.Type) (encodeFunc, isEmptyFunc) { k := t.Kind() if k == reflect.Ptr { return getEncodeIndirectValueFunc(t), isEmptyPtr } switch t { case typeTag: return encodeTag, alwaysNotEmpty case typeTime: return encodeTime, alwaysNotEmpty case typeBigInt: return encodeBigInt, alwaysNotEmpty case typeRawMessage: return encodeMarshalerType, isEmptySlice } if reflect.PtrTo(t).Implements(typeMarshaler) { return encodeMarshalerType, alwaysNotEmpty } if reflect.PtrTo(t).Implements(typeBinaryMarshaler) { return encodeBinaryMarshalerType, isEmptyBinaryMarshaler } switch k { case reflect.Bool: return encodeBool, isEmptyBool case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return encodeInt, isEmptyInt case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return encodeUint, isEmptyUint case reflect.Float32, reflect.Float64: return encodeFloat, isEmptyFloat case reflect.String: return encodeString, isEmptyString case reflect.Slice, reflect.Array: if t.Elem().Kind() == reflect.Uint8 { return encodeByteString, isEmptySlice } f, _ := getEncodeFunc(t.Elem()) if f == nil { return nil, nil } return arrayEncodeFunc{f: f}.encode, isEmptySlice case reflect.Map: kf, _ := getEncodeFunc(t.Key()) ef, _ := getEncodeFunc(t.Elem()) if kf == nil || ef == nil { return nil, nil } return mapEncodeFunc{kf: kf, ef: ef}.encode, isEmptyMap case reflect.Struct: // Get struct's special field "_" tag options if f, ok := t.FieldByName("_"); ok { tag := f.Tag.Get("cbor") if tag != "-" { if hasToArrayOption(tag) { return encodeStructToArray, isEmptyStruct } } } return encodeStruct, isEmptyStruct case reflect.Interface: return encodeIntf, isEmptyIntf } return nil, nil } func getEncodeIndirectValueFunc(t reflect.Type) encodeFunc { for t.Kind() == reflect.Ptr { t = t.Elem() } f, _ := getEncodeFunc(t) if f == nil { return nil } return func(e *encoderBuffer, em *encMode, v reflect.Value) error { for v.Kind() == reflect.Ptr && !v.IsNil() { v = v.Elem() } if v.Kind() == reflect.Ptr && v.IsNil() { e.Write(cborNil) return nil } return f(e, em, v) } } func alwaysNotEmpty(v reflect.Value) (empty bool, err error) { return false, nil } func isEmptyBool(v reflect.Value) (bool, error) { return !v.Bool(), nil } func isEmptyInt(v reflect.Value) (bool, error) { return v.Int() == 0, nil } func isEmptyUint(v reflect.Value) (bool, error) { return v.Uint() == 0, nil } func isEmptyFloat(v reflect.Value) (bool, error) { return v.Float() == 0.0, nil } func isEmptyString(v reflect.Value) (bool, error) { return v.Len() == 0, nil } func isEmptySlice(v reflect.Value) (bool, error) { return v.Len() == 0, nil } func isEmptyMap(v reflect.Value) (bool, error) { return v.Len() == 0, nil } func isEmptyPtr(v reflect.Value) (bool, error) { return v.IsNil(), nil } func isEmptyIntf(v reflect.Value) (bool, error) { return v.IsNil(), nil } func isEmptyStruct(v reflect.Value) (bool, error) { structType, err := getEncodingStructType(v.Type()) if err != nil { return false, err } if structType.toArray { return len(structType.fields) == 0, nil } if len(structType.fields) > len(structType.omitEmptyFieldsIdx) { return false, nil } for _, i := range structType.omitEmptyFieldsIdx { f := structType.fields[i] // Get field value var fv reflect.Value if len(f.idx) == 1 { fv = v.Field(f.idx[0]) } else { // Get embedded field value. No error is expected. fv, _ = getFieldValue(v, f.idx, func(v reflect.Value) (reflect.Value, error) { // Skip null pointer to embedded struct return reflect.Value{}, nil }) if !fv.IsValid() { continue } } empty, err := f.ief(fv) if err != nil { return false, err } if !empty { return false, nil } } return true, nil } func isEmptyBinaryMarshaler(v reflect.Value) (bool, error) { m, ok := v.Interface().(encoding.BinaryMarshaler) if !ok { pv := reflect.New(v.Type()) pv.Elem().Set(v) m = pv.Interface().(encoding.BinaryMarshaler) } data, err := m.MarshalBinary() if err != nil { return false, err } return len(data) == 0, nil } func cannotFitFloat32(f64 float64) bool { f32 := float32(f64) return float64(f32) != f64 } // float32NaNFromReflectValue extracts float32 NaN from reflect.Value while preserving NaN's quiet bit. func float32NaNFromReflectValue(v reflect.Value) float32 { // Keith Randall's workaround for issue https://github.com/golang/go/issues/36400 p := reflect.New(v.Type()) p.Elem().Set(v) f32 := p.Convert(reflect.TypeOf((*float32)(nil))).Elem().Interface().(float32) return f32 } golang-github-fxamacker-cbor-2.4.0/encode_test.go000066400000000000000000003443151416612535600217740ustar00rootroot00000000000000// 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/binary" "fmt" "io" "io/ioutil" "math" "math/big" "reflect" "strings" "testing" "time" ) type marshalTest struct { cborData []byte values []interface{} } type marshalErrorTest struct { name string value interface{} wantErrorMsg string } type inner struct { X, Y, z int64 } type outer struct { IntField int FloatField float32 BoolField bool StringField string ByteStringField []byte ArrayField []string MapField map[string]bool NestedStructField *inner unexportedField int64 } // CBOR test data are from https://tools.ietf.org/html/rfc7049#appendix-A. var marshalTests = []marshalTest{ // positive integer {hexDecode("00"), []interface{}{uint(0), uint8(0), uint16(0), uint32(0), uint64(0), int(0), int8(0), int16(0), int32(0), int64(0)}}, {hexDecode("01"), []interface{}{uint(1), uint8(1), uint16(1), uint32(1), uint64(1), int(1), int8(1), int16(1), int32(1), int64(1)}}, {hexDecode("0a"), []interface{}{uint(10), uint8(10), uint16(10), uint32(10), uint64(10), int(10), int8(10), int16(10), int32(10), int64(10)}}, {hexDecode("17"), []interface{}{uint(23), uint8(23), uint16(23), uint32(23), uint64(23), int(23), int8(23), int16(23), int32(23), int64(23)}}, {hexDecode("1818"), []interface{}{uint(24), uint8(24), uint16(24), uint32(24), uint64(24), int(24), int8(24), int16(24), int32(24), int64(24)}}, {hexDecode("1819"), []interface{}{uint(25), uint8(25), uint16(25), uint32(25), uint64(25), int(25), int8(25), int16(25), int32(25), int64(25)}}, {hexDecode("1864"), []interface{}{uint(100), uint8(100), uint16(100), uint32(100), uint64(100), int(100), int8(100), int16(100), int32(100), int64(100)}}, {hexDecode("18ff"), []interface{}{uint(255), uint8(255), uint16(255), uint32(255), uint64(255), int(255), int16(255), int32(255), int64(255)}}, {hexDecode("190100"), []interface{}{uint(256), uint16(256), uint32(256), uint64(256), int(256), int16(256), int32(256), int64(256)}}, {hexDecode("1903e8"), []interface{}{uint(1000), uint16(1000), uint32(1000), uint64(1000), int(1000), int16(1000), int32(1000), int64(1000)}}, {hexDecode("19ffff"), []interface{}{uint(65535), uint16(65535), uint32(65535), uint64(65535), int(65535), int32(65535), int64(65535)}}, {hexDecode("1a00010000"), []interface{}{uint(65536), uint32(65536), uint64(65536), int(65536), int32(65536), int64(65536)}}, {hexDecode("1a000f4240"), []interface{}{uint(1000000), uint32(1000000), uint64(1000000), int(1000000), int32(1000000), int64(1000000)}}, {hexDecode("1affffffff"), []interface{}{uint(4294967295), uint32(4294967295), uint64(4294967295), int64(4294967295)}}, {hexDecode("1b000000e8d4a51000"), []interface{}{uint64(1000000000000), int64(1000000000000)}}, {hexDecode("1bffffffffffffffff"), []interface{}{uint64(18446744073709551615)}}, // negative integer {hexDecode("20"), []interface{}{int(-1), int8(-1), int16(-1), int32(-1), int64(-1)}}, {hexDecode("29"), []interface{}{int(-10), int8(-10), int16(-10), int32(-10), int64(-10)}}, {hexDecode("37"), []interface{}{int(-24), int8(-24), int16(-24), int32(-24), int64(-24)}}, {hexDecode("3818"), []interface{}{int(-25), int8(-25), int16(-25), int32(-25), int64(-25)}}, {hexDecode("3863"), []interface{}{int(-100), int8(-100), int16(-100), int32(-100), int64(-100)}}, {hexDecode("38ff"), []interface{}{int(-256), int16(-256), int32(-256), int64(-256)}}, {hexDecode("390100"), []interface{}{int(-257), int16(-257), int32(-257), int64(-257)}}, {hexDecode("3903e7"), []interface{}{int(-1000), int16(-1000), int32(-1000), int64(-1000)}}, {hexDecode("39ffff"), []interface{}{int(-65536), int32(-65536), int64(-65536)}}, {hexDecode("3a00010000"), []interface{}{int(-65537), int32(-65537), int64(-65537)}}, {hexDecode("3affffffff"), []interface{}{int64(-4294967296)}}, // byte string {hexDecode("40"), []interface{}{[]byte{}}}, {hexDecode("4401020304"), []interface{}{[]byte{1, 2, 3, 4}, [...]byte{1, 2, 3, 4}}}, // text string {hexDecode("60"), []interface{}{""}}, {hexDecode("6161"), []interface{}{"a"}}, {hexDecode("6449455446"), []interface{}{"IETF"}}, {hexDecode("62225c"), []interface{}{"\"\\"}}, {hexDecode("62c3bc"), []interface{}{"ü"}}, {hexDecode("63e6b0b4"), []interface{}{"水"}}, {hexDecode("64f0908591"), []interface{}{"𐅑"}}, // array { hexDecode("80"), []interface{}{ [0]int{}, []uint{}, // []uint8{}, []uint16{}, []uint32{}, []uint64{}, []int{}, []int8{}, []int16{}, []int32{}, []int64{}, []string{}, []bool{}, []float32{}, []float64{}, []interface{}{}, }, }, { hexDecode("83010203"), []interface{}{ [...]int{1, 2, 3}, []uint{1, 2, 3}, // []uint8{1, 2, 3}, []uint16{1, 2, 3}, []uint32{1, 2, 3}, []uint64{1, 2, 3}, []int{1, 2, 3}, []int8{1, 2, 3}, []int16{1, 2, 3}, []int32{1, 2, 3}, []int64{1, 2, 3}, []interface{}{1, 2, 3}, }, }, { hexDecode("8301820203820405"), []interface{}{ [...]interface{}{1, [...]int{2, 3}, [...]int{4, 5}}, []interface{}{1, []uint{2, 3}, []uint{4, 5}}, // []interface{}{1, []uint8{2, 3}, []uint8{4, 5}}, []interface{}{1, []uint16{2, 3}, []uint16{4, 5}}, []interface{}{1, []uint32{2, 3}, []uint32{4, 5}}, []interface{}{1, []uint64{2, 3}, []uint64{4, 5}}, []interface{}{1, []int{2, 3}, []int{4, 5}}, []interface{}{1, []int8{2, 3}, []int8{4, 5}}, []interface{}{1, []int16{2, 3}, []int16{4, 5}}, []interface{}{1, []int32{2, 3}, []int32{4, 5}}, []interface{}{1, []int64{2, 3}, []int64{4, 5}}, []interface{}{1, []interface{}{2, 3}, []interface{}{4, 5}}, }, }, { hexDecode("98190102030405060708090a0b0c0d0e0f101112131415161718181819"), []interface{}{ [...]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}, []uint{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}, // []uint8{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}, []uint16{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}, []uint32{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}, []uint64{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}, []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}, []int8{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}, []int16{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}, []int32{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}, []int64{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}, []interface{}{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("826161a161626163"), []interface{}{ [...]interface{}{"a", map[string]string{"b": "c"}}, []interface{}{"a", map[string]string{"b": "c"}}, []interface{}{"a", map[interface{}]interface{}{"b": "c"}}, }, }, // map { hexDecode("a0"), []interface{}{ map[uint]bool{}, map[uint8]bool{}, map[uint16]bool{}, map[uint32]bool{}, map[uint64]bool{}, map[int]bool{}, map[int8]bool{}, map[int16]bool{}, map[int32]bool{}, map[int64]bool{}, map[float32]bool{}, map[float64]bool{}, map[bool]bool{}, map[string]bool{}, map[interface{}]interface{}{}, }, }, { hexDecode("a201020304"), []interface{}{ map[uint]uint{3: 4, 1: 2}, map[uint8]uint8{3: 4, 1: 2}, map[uint16]uint16{3: 4, 1: 2}, map[uint32]uint32{3: 4, 1: 2}, map[uint64]uint64{3: 4, 1: 2}, map[int]int{3: 4, 1: 2}, map[int8]int8{3: 4, 1: 2}, map[int16]int16{3: 4, 1: 2}, map[int32]int32{3: 4, 1: 2}, map[int64]int64{3: 4, 1: 2}, map[interface{}]interface{}{3: 4, 1: 2}, }, }, { hexDecode("a26161016162820203"), []interface{}{ map[string]interface{}{"a": 1, "b": []interface{}{2, 3}}, map[interface{}]interface{}{"b": []interface{}{2, 3}, "a": 1}, }, }, { hexDecode("a56161614161626142616361436164614461656145"), []interface{}{ map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}, map[interface{}]interface{}{"b": "B", "a": "A", "c": "C", "e": "E", "d": "D"}, }, }, // tag { hexDecode("c074323031332d30332d32315432303a30343a30305a"), []interface{}{Tag{0, "2013-03-21T20:04:00Z"}, RawTag{0, hexDecode("74323031332d30332d32315432303a30343a30305a")}}, }, // 0: standard date/time { hexDecode("c11a514b67b0"), []interface{}{Tag{1, uint64(1363896240)}, RawTag{1, hexDecode("1a514b67b0")}}, }, // 1: epoch-based date/time { hexDecode("c249010000000000000000"), []interface{}{ bigIntOrPanic("18446744073709551616"), Tag{2, []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, RawTag{2, hexDecode("49010000000000000000")}, }, }, // 2: positive bignum: 18446744073709551616 { hexDecode("c349010000000000000000"), []interface{}{ bigIntOrPanic("-18446744073709551617"), Tag{3, []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, RawTag{3, hexDecode("49010000000000000000")}, }, }, // 3: negative bignum: -18446744073709551617 { hexDecode("c1fb41d452d9ec200000"), []interface{}{Tag{1, float64(1363896240.5)}, RawTag{1, hexDecode("fb41d452d9ec200000")}}, }, // 1: epoch-based date/time { hexDecode("d74401020304"), []interface{}{Tag{23, []byte{0x01, 0x02, 0x03, 0x04}}, RawTag{23, hexDecode("4401020304")}}, }, // 23: expected conversion to base16 encoding { hexDecode("d818456449455446"), []interface{}{Tag{24, []byte{0x64, 0x49, 0x45, 0x54, 0x46}}, RawTag{24, hexDecode("456449455446")}}, }, // 24: encoded cborBytes data item { hexDecode("d82076687474703a2f2f7777772e6578616d706c652e636f6d"), []interface{}{Tag{32, "http://www.example.com"}, RawTag{32, hexDecode("76687474703a2f2f7777772e6578616d706c652e636f6d")}}, }, // 32: URI // primitives {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)}}, // nan, positive and negative inf {hexDecode("f97c00"), []interface{}{math.Inf(1)}}, {hexDecode("f97e00"), []interface{}{math.NaN()}}, {hexDecode("f9fc00"), []interface{}{math.Inf(-1)}}, // float32 {hexDecode("fa47c35000"), []interface{}{float32(100000.0)}}, {hexDecode("fa7f7fffff"), []interface{}{float32(3.4028234663852886e+38)}}, // float64 {hexDecode("fb3ff199999999999a"), []interface{}{float64(1.1)}}, {hexDecode("fb7e37e43c8800759c"), []interface{}{float64(1.0e+300)}}, {hexDecode("fbc010666666666666"), []interface{}{float64(-4.1)}}, // More testcases not covered by https://tools.ietf.org/html/rfc7049#appendix-A. { hexDecode("d83dd183010203"), // 61(17([1, 2, 3])), nested tags 61 and 17 []interface{}{Tag{61, Tag{17, []interface{}{uint64(1), uint64(2), uint64(3)}}}, RawTag{61, hexDecode("d183010203")}}, }, } var exMarshalTests = []marshalTest{ { // array of nils hexDecode("83f6f6f6"), []interface{}{ []interface{}{nil, nil, nil}, }, }, } func TestMarshal(t *testing.T) { testMarshal(t, marshalTests) testMarshal(t, exMarshalTests) } func TestInvalidTypeMarshal(t *testing.T) { type s1 struct { Chan chan bool } type s2 struct { _ struct{} `cbor:",toarray"` Chan chan bool } var marshalErrorTests = []marshalErrorTest{ {"channel cannot be marshaled", make(chan bool), "cbor: unsupported type: chan bool"}, {"slice of channel cannot be marshaled", make([]chan bool, 10), "cbor: unsupported type: []chan bool"}, {"slice of pointer to channel cannot be marshaled", make([]*chan bool, 10), "cbor: unsupported type: []*chan bool"}, {"map of channel cannot be marshaled", make(map[string]chan bool), "cbor: unsupported type: map[string]chan bool"}, {"struct of channel cannot be marshaled", s1{}, "cbor: unsupported type: cbor.s1"}, {"struct of channel cannot be marshaled", s2{}, "cbor: unsupported type: cbor.s2"}, {"function cannot be marshaled", func(i int) int { return i * i }, "cbor: unsupported type: func(int) int"}, {"complex cannot be marshaled", complex(100, 8), "cbor: unsupported type: complex128"}, } em, err := EncOptions{Sort: SortCanonical}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } for _, tc := range marshalErrorTests { t.Run(tc.name, func(t *testing.T) { b, err := Marshal(&tc.value) if err == nil { t.Errorf("Marshal(%v) didn't return an error, want error %q", tc.value, tc.wantErrorMsg) } else if _, ok := err.(*UnsupportedTypeError); !ok { t.Errorf("Marshal(%v) error type %T, want *UnsupportedTypeError", tc.value, err) } else if err.Error() != tc.wantErrorMsg { t.Errorf("Marshal(%v) error %q, want %q", tc.value, err.Error(), tc.wantErrorMsg) } else if b != nil { t.Errorf("Marshal(%v) = 0x%x, want nil", tc.value, b) } b, err = em.Marshal(&tc.value) if err == nil { t.Errorf("Marshal(%v) didn't return an error, want error %q", tc.value, tc.wantErrorMsg) } else if _, ok := err.(*UnsupportedTypeError); !ok { t.Errorf("Marshal(%v) error type %T, want *UnsupportedTypeError", tc.value, err) } else if err.Error() != tc.wantErrorMsg { t.Errorf("Marshal(%v) error %q, want %q", tc.value, err.Error(), tc.wantErrorMsg) } else if b != nil { t.Errorf("Marshal(%v) = 0x%x, want nil", tc.value, b) } }) } } func TestMarshalLargeByteString(t *testing.T) { // []byte{100, 100, 100, ...} lengths := []int{0, 1, 2, 22, 23, 24, 254, 255, 256, 65534, 65535, 65536, 10000000} tests := make([]marshalTest, len(lengths)) for i, length := range lengths { cborData := bytes.NewBuffer(encodeCborHeader(cborTypeByteString, uint64(length))) value := make([]byte, length) for j := 0; j < length; j++ { cborData.WriteByte(100) value[j] = 100 } tests[i] = marshalTest{cborData.Bytes(), []interface{}{value}} } testMarshal(t, tests) } func TestMarshalLargeTextString(t *testing.T) { // "ddd..." lengths := []int{0, 1, 2, 22, 23, 24, 254, 255, 256, 65534, 65535, 65536, 10000000} tests := make([]marshalTest, len(lengths)) for i, length := range lengths { cborData := bytes.NewBuffer(encodeCborHeader(cborTypeTextString, uint64(length))) value := make([]byte, length) for j := 0; j < length; j++ { cborData.WriteByte(100) value[j] = 100 } tests[i] = marshalTest{cborData.Bytes(), []interface{}{string(value)}} } testMarshal(t, tests) } func TestMarshalLargeArray(t *testing.T) { // []string{"水", "水", "水", ...} lengths := []int{0, 1, 2, 22, 23, 24, 254, 255, 256, 65534, 65535, 65536, 131072} tests := make([]marshalTest, len(lengths)) for i, length := range lengths { cborData := bytes.NewBuffer(encodeCborHeader(cborTypeArray, uint64(length))) value := make([]string, length) for j := 0; j < length; j++ { cborData.Write([]byte{0x63, 0xe6, 0xb0, 0xb4}) value[j] = "水" } tests[i] = marshalTest{cborData.Bytes(), []interface{}{value}} } testMarshal(t, tests) } func TestMarshalLargeMapCanonical(t *testing.T) { // map[int]int {0:0, 1:1, 2:2, ...} lengths := []int{0, 1, 2, 22, 23, 24, 254, 255, 256, 65534, 65535, 65536, 131072} tests := make([]marshalTest, len(lengths)) for i, length := range lengths { cborData := bytes.NewBuffer(encodeCborHeader(cborTypeMap, uint64(length))) value := make(map[int]int, length) for j := 0; j < length; j++ { d := encodeCborHeader(cborTypePositiveInt, uint64(j)) cborData.Write(d) cborData.Write(d) value[j] = j } tests[i] = marshalTest{cborData.Bytes(), []interface{}{value}} } testMarshal(t, tests) } func TestMarshalLargeMap(t *testing.T) { // map[int]int {0:0, 1:1, 2:2, ...} lengths := []int{0, 1, 2, 22, 23, 24, 254, 255, 256, 65534, 65535, 65536, 131072} for _, length := range lengths { m1 := make(map[int]int, length) for i := 0; i < length; i++ { m1[i] = i } cborData, err := Marshal(m1) if err != nil { t.Fatalf("Marshal(%v) returned error %v", m1, err) } m2 := make(map[int]int) if err = Unmarshal(cborData, &m2); err != nil { t.Fatalf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(m1, m2) { t.Errorf("Unmarshal() = %v, want %v", m2, m1) } } } func encodeCborHeader(t cborType, n uint64) []byte { b := make([]byte, 9) if n <= 23 { b[0] = byte(t) | byte(n) return b[:1] } else if n <= math.MaxUint8 { b[0] = byte(t) | byte(24) b[1] = byte(n) return b[:2] } else if n <= math.MaxUint16 { b[0] = byte(t) | byte(25) binary.BigEndian.PutUint16(b[1:], uint16(n)) return b[:3] } else if n <= math.MaxUint32 { b[0] = byte(t) | byte(26) binary.BigEndian.PutUint32(b[1:], uint32(n)) return b[:5] } else { b[0] = byte(t) | byte(27) binary.BigEndian.PutUint64(b[1:], n) return b[:9] } } func testMarshal(t *testing.T, testCases []marshalTest) { em, err := EncOptions{Sort: SortCanonical}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } for _, tc := range testCases { for _, value := range tc.values { if _, err := Marshal(value); err != nil { t.Errorf("Marshal(%v) returned error %v", value, err) } if b, err := em.Marshal(value); err != nil { t.Errorf("Marshal(%v) returned error %v", value, err) } else if !bytes.Equal(b, tc.cborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", value, b, tc.cborData) } } r := RawMessage(tc.cborData) if b, err := Marshal(r); err != nil { t.Errorf("Marshal(%v) returned error %v", r, err) } else if !bytes.Equal(b, r) { t.Errorf("Marshal(%v) returned %v, want %v", r, b, r) } } } func TestMarshalStruct(t *testing.T) { v1 := outer{ IntField: 123, FloatField: 100000.0, BoolField: true, StringField: "test", ByteStringField: []byte{1, 3, 5}, ArrayField: []string{"hello", "world"}, MapField: map[string]bool{"afternoon": false, "morning": true}, NestedStructField: &inner{X: 1000, Y: 1000000, z: 10000000}, unexportedField: 6, } unmarshalWant := outer{ IntField: 123, FloatField: 100000.0, BoolField: true, StringField: "test", ByteStringField: []byte{1, 3, 5}, ArrayField: []string{"hello", "world"}, MapField: map[string]bool{"afternoon": false, "morning": true}, NestedStructField: &inner{X: 1000, Y: 1000000}, } cborData, err := Marshal(v1) if err != nil { t.Fatalf("Marshal(%v) returned error %v", v1, err) } var v2 outer if err = Unmarshal(cborData, &v2); err != nil { t.Fatalf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(unmarshalWant, v2) { t.Errorf("Unmarshal() = %v, want %v", v2, unmarshalWant) } } func TestMarshalStructCanonical(t *testing.T) { v := outer{ IntField: 123, FloatField: 100000.0, BoolField: true, StringField: "test", ByteStringField: []byte{1, 3, 5}, ArrayField: []string{"hello", "world"}, MapField: map[string]bool{"afternoon": false, "morning": true}, NestedStructField: &inner{X: 1000, Y: 1000000, z: 10000000}, unexportedField: 6, } var cborData bytes.Buffer cborData.WriteByte(byte(cborTypeMap) | 8) // CBOR header: map type with 8 items (exported fields) cborData.WriteByte(byte(cborTypeTextString) | 8) // "IntField" cborData.WriteString("IntField") cborData.WriteByte(byte(cborTypePositiveInt) | 24) cborData.WriteByte(123) cborData.WriteByte(byte(cborTypeTextString) | 8) // "MapField" cborData.WriteString("MapField") cborData.WriteByte(byte(cborTypeMap) | 2) cborData.WriteByte(byte(cborTypeTextString) | 7) cborData.WriteString("morning") cborData.WriteByte(byte(cborTypePrimitives) | 21) cborData.WriteByte(byte(cborTypeTextString) | 9) cborData.WriteString("afternoon") cborData.WriteByte(byte(cborTypePrimitives) | 20) cborData.WriteByte(byte(cborTypeTextString) | 9) // "BoolField" cborData.WriteString("BoolField") cborData.WriteByte(byte(cborTypePrimitives) | 21) cborData.WriteByte(byte(cborTypeTextString) | 10) // "ArrayField" cborData.WriteString("ArrayField") cborData.WriteByte(byte(cborTypeArray) | 2) cborData.WriteByte(byte(cborTypeTextString) | 5) cborData.WriteString("hello") cborData.WriteByte(byte(cborTypeTextString) | 5) cborData.WriteString("world") cborData.WriteByte(byte(cborTypeTextString) | 10) // "FloatField" cborData.WriteString("FloatField") cborData.Write([]byte{0xfa, 0x47, 0xc3, 0x50, 0x00}) cborData.WriteByte(byte(cborTypeTextString) | 11) // "StringField" cborData.WriteString("StringField") cborData.WriteByte(byte(cborTypeTextString) | 4) cborData.WriteString("test") cborData.WriteByte(byte(cborTypeTextString) | 15) // "ByteStringField" cborData.WriteString("ByteStringField") cborData.WriteByte(byte(cborTypeByteString) | 3) cborData.Write([]byte{1, 3, 5}) cborData.WriteByte(byte(cborTypeTextString) | 17) // "NestedStructField" cborData.WriteString("NestedStructField") cborData.WriteByte(byte(cborTypeMap) | 2) cborData.WriteByte(byte(cborTypeTextString) | 1) cborData.WriteString("X") cborData.WriteByte(byte(cborTypePositiveInt) | 25) b := make([]byte, 2) binary.BigEndian.PutUint16(b, uint16(1000)) cborData.Write(b) cborData.WriteByte(byte(cborTypeTextString) | 1) cborData.WriteString("Y") cborData.WriteByte(byte(cborTypePositiveInt) | 26) b = make([]byte, 4) binary.BigEndian.PutUint32(b, uint32(1000000)) cborData.Write(b) em, err := EncOptions{Sort: SortCanonical}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %q", err) } if b, err := em.Marshal(v); err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, cborData.Bytes()) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, cborData.Bytes()) } } func TestMarshalNullPointerToEmbeddedStruct(t *testing.T) { type ( T1 struct { X int } T2 struct { *T1 } ) v := T2{} wantCborData := []byte{0xa0} // {} cborData, err := Marshal(v) if err != nil { t.Fatalf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(wantCborData, cborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, cborData, wantCborData) } } func TestMarshalNullPointerToStruct(t *testing.T) { type ( T1 struct { X int } T2 struct { T *T1 } ) v := T2{} wantCborData := []byte{0xa1, 0x61, 0x54, 0xf6} // {X: nil} cborData, err := Marshal(v) if err != nil { t.Fatalf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(wantCborData, cborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, cborData, wantCborData) } } // Struct fields encoding follows the same struct fields visibility // rules used by JSON encoding package. Some struct types are from // tests in JSON encoding package to ensure that the same rules are // followed. func TestAnonymousFields1(t *testing.T) { // Fields (T1.X, T2.X) with the same name at the same level are ignored type ( T1 struct{ x, X int } T2 struct{ x, X int } T struct { T1 T2 } ) v := T{T1{1, 2}, T2{3, 4}} want := []byte{0xa0} // {} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } } func TestAnonymousFields2(t *testing.T) { // Field (T.X) with the same name at a less nested level is serialized type ( T1 struct{ x, X int } T2 struct{ x, X int } T struct { T1 T2 x, X int } ) v := T{T1{1, 2}, T2{3, 4}, 5, 6} want := []byte{0xa1, 0x61, 0x58, 0x06} // {X:6} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } var v2 T unmarshalWant := T{X: 6} if err := Unmarshal(b, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if !reflect.DeepEqual(v2, unmarshalWant) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant) } } func TestAnonymousFields3(t *testing.T) { // Unexported embedded field (myInt) of non-struct type is ignored type ( myInt int T struct { myInt } ) v := T{5} want := []byte{0xa0} // {} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } } func TestAnonymousFields4(t *testing.T) { // Exported embedded field (MyInt) of non-struct type is serialized type ( MyInt int T struct { MyInt } ) v := T{5} want := []byte{0xa1, 0x65, 0x4d, 0x79, 0x49, 0x6e, 0x74, 0x05} // {MyInt: 5} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } var v2 T if err = Unmarshal(b, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if !reflect.DeepEqual(v, v2) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v, v, v2, v2) } } func TestAnonymousFields5(t *testing.T) { // Unexported embedded field (*myInt) of pointer to non-struct type is ignored type ( myInt int T struct { *myInt } ) v := T{new(myInt)} *v.myInt = 5 want := []byte{0xa0} // {} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } } func TestAnonymousFields6(t *testing.T) { // Exported embedded field (*MyInt) of pointer to non-struct type should be serialized type ( MyInt int T struct { *MyInt } ) v := T{new(MyInt)} *v.MyInt = 5 want := []byte{0xa1, 0x65, 0x4d, 0x79, 0x49, 0x6e, 0x74, 0x05} // {MyInt: 5} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } var v2 T if err = Unmarshal(b, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if !reflect.DeepEqual(v, v2) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v, v, v2, v2) } } func TestAnonymousFields7(t *testing.T) { // Exported fields (t1.X, T2.Y) of embedded structs should have their exported fields be serialized type ( t1 struct{ x, X int } T2 struct{ y, Y int } T struct { t1 T2 } ) v := T{t1{1, 2}, T2{3, 4}} want := []byte{0xa2, 0x61, 0x58, 0x02, 0x61, 0x59, 0x04} // {X:2, Y:4} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } var v2 T unmarshalWant := T{t1{X: 2}, T2{Y: 4}} if err = Unmarshal(b, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if !reflect.DeepEqual(v2, unmarshalWant) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant) } } func TestAnonymousFields8(t *testing.T) { // Exported fields of pointers (t1.X, T2.Y) type ( t1 struct{ x, X int } T2 struct{ y, Y int } T struct { *t1 *T2 } ) v := T{&t1{1, 2}, &T2{3, 4}} want := []byte{0xa2, 0x61, 0x58, 0x02, 0x61, 0x59, 0x04} // {X:2, Y:4} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } // v1 cannot be unmarshaled to because reflect cannot allocate unexported field s1. var v1 T wantErrorMsg := "cannot set embedded pointer to unexported struct" wantV := T{T2: &T2{Y: 4}} err = Unmarshal(b, &v1) if err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error %q", b, wantErrorMsg) } else if !strings.Contains(err.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error %q", b, err.Error(), wantErrorMsg) } if !reflect.DeepEqual(v1, wantV) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", b, v1, v1, wantV, wantV) } // v2 can be unmarshaled to because unexported field t1 is already allocated. var v2 T v2.t1 = &t1{} unmarshalWant := T{&t1{X: 2}, &T2{Y: 4}} if err = Unmarshal(b, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if !reflect.DeepEqual(v2, unmarshalWant) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant) } } func TestAnonymousFields9(t *testing.T) { // Multiple levels of nested anonymous fields type ( MyInt1 int MyInt2 int myInt int t2 struct { MyInt2 myInt } t1 struct { MyInt1 myInt t2 } T struct { t1 myInt } ) v := T{t1{1, 2, t2{3, 4}}, 6} want := []byte{0xa2, 0x66, 0x4d, 0x79, 0x49, 0x6e, 0x74, 0x31, 0x01, 0x66, 0x4d, 0x79, 0x49, 0x6e, 0x74, 0x32, 0x03} // {MyInt1: 1, MyInt2: 3} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } var v2 T unmarshalWant := T{t1: t1{MyInt1: 1, t2: t2{MyInt2: 3}}} if err = Unmarshal(b, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if !reflect.DeepEqual(v2, unmarshalWant) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant) } } func TestAnonymousFields10(t *testing.T) { // Fields of the same struct type at the same level type ( t3 struct { Z int } t1 struct { X int t3 } t2 struct { Y int t3 } T struct { t1 t2 } ) v := T{t1{1, t3{2}}, t2{3, t3{4}}} want := []byte{0xa2, 0x61, 0x58, 0x01, 0x61, 0x59, 0x03} // {X: 1, Y: 3} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } var v2 T unmarshalWant := T{t1: t1{X: 1}, t2: t2{Y: 3}} if err = Unmarshal(b, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if !reflect.DeepEqual(v2, unmarshalWant) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant) } } func TestAnonymousFields11(t *testing.T) { // Fields (T.t2.X, T.t1.t2.X) of the same struct type at different levels type ( t2 struct { X int } t1 struct { Y int t2 } T struct { t1 t2 } ) v := T{t1{1, t2{2}}, t2{3}} want := []byte{0xa2, 0x61, 0x59, 0x01, 0x61, 0x58, 0x03} // {Y: 1, X: 3} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } var v2 T unmarshalWant := T{t1: t1{Y: 1}, t2: t2{X: 3}} if err = Unmarshal(b, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if !reflect.DeepEqual(v2, unmarshalWant) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant) } } func TestOmitAndRenameStructField(t *testing.T) { type T struct { I int // never omit Io int `cbor:",omitempty"` // omit empty Iao int `cbor:"-"` // always omit R int `cbor:"omitempty"` // renamed to omitempty } v1 := T{} // {"I": 0, "omitempty": 0} want1 := []byte{0xa2, 0x61, 0x49, 0x00, 0x69, 0x6f, 0x6d, 0x69, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x00} v2 := T{I: 1, Io: 2, Iao: 0, R: 3} // {"I": 1, "Io": 2, "omitempty": 3} want2 := []byte{0xa3, 0x61, 0x49, 0x01, 0x62, 0x49, 0x6f, 0x02, 0x69, 0x6f, 0x6d, 0x69, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x03} em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() tests := []roundTripTest{ {"default values", v1, want1}, {"non-default values", v2, want2}} testRoundTrip(t, tests, em, dm) } func TestOmitEmptyForBuiltinType(t *testing.T) { type T struct { B bool `cbor:"b"` Bo bool `cbor:"bo,omitempty"` UI uint `cbor:"ui"` UIo uint `cbor:"uio,omitempty"` I int `cbor:"i"` Io int `cbor:"io,omitempty"` F float64 `cbor:"f"` Fo float64 `cbor:"fo,omitempty"` S string `cbor:"s"` So string `cbor:"so,omitempty"` Slc []string `cbor:"slc"` Slco []string `cbor:"slco,omitempty"` M map[int]string `cbor:"m"` Mo map[int]string `cbor:"mo,omitempty"` P *int `cbor:"p"` Po *int `cbor:"po,omitempty"` Intf interface{} `cbor:"intf"` Intfo interface{} `cbor:"intfo,omitempty"` } v := T{} // {"b": false, "ui": 0, "i":0, "f": 0, "s": "", "slc": null, "m": {}, "p": nil, "intf": nil } want := []byte{0xa9, 0x61, 0x62, 0xf4, 0x62, 0x75, 0x69, 0x00, 0x61, 0x69, 0x00, 0x61, 0x66, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x73, 0x60, 0x63, 0x73, 0x6c, 0x63, 0xf6, 0x61, 0x6d, 0xf6, 0x61, 0x70, 0xf6, 0x64, 0x69, 0x6e, 0x74, 0x66, 0xf6, } em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm) } func TestOmitEmptyForAnonymousStruct(t *testing.T) { type T struct { Str struct{} `cbor:"str"` Stro struct{} `cbor:"stro,omitempty"` } v := T{} want := []byte{0xa1, 0x63, 0x73, 0x74, 0x72, 0xa0} // {"str": {}} em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm) } func TestOmitEmptyForStruct1(t *testing.T) { type T1 struct { Bo bool `cbor:"bo,omitempty"` UIo uint `cbor:"uio,omitempty"` Io int `cbor:"io,omitempty"` Fo float64 `cbor:"fo,omitempty"` So string `cbor:"so,omitempty"` Slco []string `cbor:"slco,omitempty"` Mo map[int]string `cbor:"mo,omitempty"` Po *int `cbor:"po,omitempty"` Intfo interface{} `cbor:"intfo,omitempty"` } type T struct { Str T1 `cbor:"str"` Stro T1 `cbor:"stro,omitempty"` } v := T{} want := []byte{0xa1, 0x63, 0x73, 0x74, 0x72, 0xa0} // {"str": {}} em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm) } func TestOmitEmptyForStruct2(t *testing.T) { type T1 struct { Bo bool `cbor:"bo,omitempty"` UIo uint `cbor:"uio,omitempty"` Io int `cbor:"io,omitempty"` Fo float64 `cbor:"fo,omitempty"` So string `cbor:"so,omitempty"` Slco []string `cbor:"slco,omitempty"` Mo map[int]string `cbor:"mo,omitempty"` Po *int `cbor:"po,omitempty"` Intfo interface{} `cbor:"intfo"` } type T struct { Stro T1 `cbor:"stro,omitempty"` } v := T{} want := []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa1, 0x65, 0x69, 0x6e, 0x74, 0x66, 0x6f, 0xf6} // {"stro": {intfo: nil}} em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, []roundTripTest{{"non-default values", v, want}}, em, dm) } func TestOmitEmptyForNestedStruct(t *testing.T) { type T1 struct { Bo bool `cbor:"bo,omitempty"` UIo uint `cbor:"uio,omitempty"` Io int `cbor:"io,omitempty"` Fo float64 `cbor:"fo,omitempty"` So string `cbor:"so,omitempty"` Slco []string `cbor:"slco,omitempty"` Mo map[int]string `cbor:"mo,omitempty"` Po *int `cbor:"po,omitempty"` Intfo interface{} `cbor:"intfo,omitempty"` } type T2 struct { Stro T1 `cbor:"stro,omitempty"` } type T struct { Str T2 `cbor:"str"` Stro T2 `cbor:"stro,omitempty"` } v := T{} want := []byte{0xa1, 0x63, 0x73, 0x74, 0x72, 0xa0} // {"str": {}} em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm) } func TestOmitEmptyForToArrayStruct1(t *testing.T) { type T1 struct { _ struct{} `cbor:",toarray"` b bool ui uint i int f float64 s string slc []string m map[int]string p *int intf interface{} } type T struct { Str T1 `cbor:"str"` Stro T1 `cbor:"stro,omitempty"` } v := T{ Str: T1{b: false, ui: 0, i: 0, f: 0.0, s: "", slc: nil, m: nil, p: nil, intf: nil}, Stro: T1{b: false, ui: 0, i: 0, f: 0.0, s: "", slc: nil, m: nil, p: nil, intf: nil}, } want := []byte{0xa1, 0x63, 0x73, 0x74, 0x72, 0x80} // {"str": []} em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, []roundTripTest{{"no exportable fields", v, want}}, em, dm) } func TestOmitEmptyForToArrayStruct2(t *testing.T) { type T1 struct { _ struct{} `cbor:",toarray"` Bo bool `cbor:"bo"` UIo uint `cbor:"uio"` Io int `cbor:"io"` Fo float64 `cbor:"fo"` So string `cbor:"so"` Slco []string `cbor:"slco"` Mo map[int]string `cbor:"mo"` Po *int `cbor:"po"` Intfo interface{} `cbor:"intfo"` } type T struct { Stro T1 `cbor:"stro,omitempty"` } v := T{} // {"stro": [false, 0, 0, 0.0, "", [], {}, nil, nil]} want := []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0x89, 0xf4, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xf6, 0xf6, 0xf6, 0xf6} em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, []roundTripTest{{"has exportable fields", v, want}}, em, dm) } func TestOmitEmptyForStructWithPtrToAnonymousField(t *testing.T) { type ( T1 struct { X int `cbor:"x,omitempty"` Y int `cbor:"y,omitempty"` } T2 struct { *T1 } T struct { Stro T2 `cbor:"stro,omitempty"` } ) testCases := []struct { name string obj interface{} wantCborData []byte }{ { name: "null pointer to anonymous field", obj: T{}, wantCborData: []byte{0xa0}, // {} }, { name: "not-null pointer to anonymous field", obj: T{T2{&T1{}}}, wantCborData: []byte{0xa0}, // {} }, { name: "not empty value in field 1", obj: T{T2{&T1{X: 1}}}, wantCborData: []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa1, 0x61, 0x78, 0x01}, // {stro:{x:1}} }, { name: "not empty value in field 2", obj: T{T2{&T1{Y: 2}}}, wantCborData: []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa1, 0x61, 0x79, 0x02}, // {stro:{y:2}} }, { name: "not empty value in all fields", obj: T{T2{&T1{X: 1, Y: 2}}}, wantCborData: []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa2, 0x61, 0x78, 0x01, 0x61, 0x79, 0x02}, // {stro:{x:1, y:2}} }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { b, err := Marshal(tc.obj) if err != nil { t.Errorf("Marshal(%+v) returned error %v", tc.obj, err) } if !bytes.Equal(b, tc.wantCborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.wantCborData) } }) } } func TestOmitEmptyForStructWithAnonymousField(t *testing.T) { type ( T1 struct { X int `cbor:"x,omitempty"` Y int `cbor:"y,omitempty"` } T2 struct { T1 } T struct { Stro T2 `cbor:"stro,omitempty"` } ) testCases := []struct { name string obj interface{} wantCborData []byte }{ { name: "default values", obj: T{}, wantCborData: []byte{0xa0}, // {} }, { name: "default values", obj: T{T2{T1{}}}, wantCborData: []byte{0xa0}, // {} }, { name: "not empty value in field 1", obj: T{T2{T1{X: 1}}}, wantCborData: []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa1, 0x61, 0x78, 0x01}, // {stro:{x:1}} }, { name: "not empty value in field 2", obj: T{T2{T1{Y: 2}}}, wantCborData: []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa1, 0x61, 0x79, 0x02}, // {stro:{y:2}} }, { name: "not empty value in all fields", obj: T{T2{T1{X: 1, Y: 2}}}, wantCborData: []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa2, 0x61, 0x78, 0x01, 0x61, 0x79, 0x02}, // {stro:{x:1, y:2}} }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { b, err := Marshal(tc.obj) if err != nil { t.Errorf("Marshal(%+v) returned error %v", tc.obj, err) } if !bytes.Equal(b, tc.wantCborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.wantCborData) } }) } } func TestOmitEmptyForBinaryMarshaler1(t *testing.T) { type T1 struct { No number `cbor:"no,omitempty"` } type T struct { Str T1 `cbor:"str"` Stro T1 `cbor:"stro,omitempty"` } testCases := []roundTripTest{ { "empty BinaryMarshaler", T1{}, []byte{0xa0}, // {} }, { "empty struct containing empty BinaryMarshaler", T{}, []byte{0xa1, 0x63, 0x73, 0x74, 0x72, 0xa0}, // {str: {}} }, } em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, testCases, em, dm) } func TestOmitEmptyForBinaryMarshaler2(t *testing.T) { type T1 struct { So stru `cbor:"so,omitempty"` } type T struct { Str T1 `cbor:"str"` Stro T1 `cbor:"stro,omitempty"` } testCases := []roundTripTest{ { "empty BinaryMarshaler", T1{}, []byte{0xa0}, // {} }, { "empty struct containing empty BinaryMarshaler", T{}, []byte{0xa1, 0x63, 0x73, 0x74, 0x72, 0xa0}, // {str: {}} }, } em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, testCases, em, dm) } // omitempty is a no-op for time.Time. func TestOmitEmptyForTime(t *testing.T) { type T struct { Tm time.Time `cbor:"t,omitempty"` } v := T{} want := []byte{0xa1, 0x61, 0x74, 0xf6} // {"t": nil} em, _ := EncOptions{}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm) } // omitempty is a no-op for big.Int. func TestOmitEmptyForBigInt(t *testing.T) { type T struct { I big.Int `cbor:"bi,omitempty"` } v := T{} want := []byte{0xa1, 0x62, 0x62, 0x69, 0xc2, 0x40} // {"bi": 2([])} em, _ := EncOptions{BigIntConvert: BigIntConvertNone}.EncMode() dm, _ := DecOptions{}.DecMode() testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm) } func TestTaggedField(t *testing.T) { // A field (T2.X) with a tag dominates untagged field. type ( T1 struct { S string } T2 struct { X string `cbor:"S"` } T struct { T1 T2 } ) v := T{T1{"T1"}, T2{"T2"}} want := []byte{0xa1, 0x61, 0x53, 0x62, 0x54, 0x32} // {"S":"T2"} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } var v2 T unmarshalWant := T{T2: T2{"T2"}} if err = Unmarshal(b, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if !reflect.DeepEqual(v2, unmarshalWant) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant) } } func TestDuplicatedFields(t *testing.T) { // Duplicate fields (T.T1.S, T.T2.S) are ignored. type ( T1 struct { S string } T2 struct { S string } T3 struct { X string `cbor:"S"` } T4 struct { T1 T3 } T struct { T1 T2 T4 // Contains a tagged S field through T3; should not dominate. } ) v := T{ T1{"T1"}, T2{"T2"}, T4{ T1{"nested T1"}, T3{"nested T3"}, }, } want := []byte{0xa0} // {} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } } type TReader struct { X int } func (s TReader) Read(p []byte) (n int, err error) { return 0, nil } func TestTaggedAnonymousField(t *testing.T) { // Anonymous field with a name given in its CBOR tag is treated as having that name, rather than being anonymous. type ( T1 struct { X int } T struct { X int T1 `cbor:"T1"` } ) v := T{X: 1, T1: T1{X: 2}} want := []byte{0xa2, 0x61, 0x58, 0x01, 0x62, 0x54, 0x31, 0xa1, 0x61, 0x58, 0x02} // {X: 1, T1: {X:2}} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%+v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, want) } var v2 T unmarshalWant := T{X: 1, T1: T1{X: 2}} if err = Unmarshal(b, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if !reflect.DeepEqual(v2, unmarshalWant) { t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", b, v2, v2, unmarshalWant, unmarshalWant) } } func TestAnonymousInterfaceField(t *testing.T) { // Anonymous field of interface type is treated the same as having that type as its name, rather than being anonymous. type ( T struct { X int io.Reader } ) v := T{X: 1, Reader: TReader{X: 2}} want := []byte{0xa2, 0x61, 0x58, 0x01, 0x66, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0xa1, 0x61, 0x58, 0x02} // {X: 1, Reader: {X:2}} b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%+v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, want) } var v2 T const wantErrorMsg = "cannot unmarshal map into Go struct field cbor.T.Reader of type io.Reader" if err = Unmarshal(b, &v2); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error (*UnmarshalTypeError)", b) } else { if typeError, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*UnmarshalTypeError)", b, err) } else if !strings.Contains(typeError.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", b, err.Error(), wantErrorMsg) } } } func TestEncodeInterface(t *testing.T) { var r io.Reader = TReader{X: 2} want := []byte{0xa1, 0x61, 0x58, 0x02} // {X:2} b, err := Marshal(r) if err != nil { t.Errorf("Marshal(%+v) returned error %v", r, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", r, b, want) } var v io.Reader const wantErrorMsg = "cannot unmarshal map into Go value of type io.Reader" if err = Unmarshal(b, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error, want error (*UnmarshalTypeError)", b) } else { if typeError, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*UnmarshalTypeError)", b, err) } else if !strings.Contains(typeError.Error(), wantErrorMsg) { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", b, err.Error(), wantErrorMsg) } } } func TestEncodeTime(t *testing.T) { timeUnixOpt := EncOptions{Time: TimeUnix} timeUnixMicroOpt := EncOptions{Time: TimeUnixMicro} timeUnixDynamicOpt := EncOptions{Time: TimeUnixDynamic} timeRFC3339Opt := EncOptions{Time: TimeRFC3339} timeRFC3339NanoOpt := EncOptions{Time: TimeRFC3339Nano} type timeConvert struct { opt EncOptions wantCborData []byte } testCases := []struct { name string tm time.Time convert []timeConvert }{ { name: "zero time", tm: time.Time{}, convert: []timeConvert{ { opt: timeUnixOpt, wantCborData: hexDecode("f6"), // encode as CBOR null }, { opt: timeUnixMicroOpt, wantCborData: hexDecode("f6"), // encode as CBOR null }, { opt: timeUnixDynamicOpt, wantCborData: hexDecode("f6"), // encode as CBOR null }, { opt: timeRFC3339Opt, wantCborData: hexDecode("f6"), // encode as CBOR null }, { opt: timeRFC3339NanoOpt, wantCborData: hexDecode("f6"), // encode as CBOR null }, }, }, { name: "time without fractional seconds", tm: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"), convert: []timeConvert{ { opt: timeUnixOpt, wantCborData: hexDecode("1a514b67b0"), // 1363896240 }, { opt: timeUnixMicroOpt, wantCborData: hexDecode("fb41d452d9ec000000"), // 1363896240.0 }, { opt: timeUnixDynamicOpt, wantCborData: hexDecode("1a514b67b0"), // 1363896240 }, { opt: timeRFC3339Opt, wantCborData: hexDecode("74323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z" }, { opt: timeRFC3339NanoOpt, wantCborData: hexDecode("74323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z" }, }, }, { name: "time with fractional seconds", tm: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00.5Z"), convert: []timeConvert{ { opt: timeUnixOpt, wantCborData: hexDecode("1a514b67b0"), // 1363896240 }, { opt: timeUnixMicroOpt, wantCborData: hexDecode("fb41d452d9ec200000"), // 1363896240.5 }, { opt: timeUnixDynamicOpt, wantCborData: hexDecode("fb41d452d9ec200000"), // 1363896240.5 }, { opt: timeRFC3339Opt, wantCborData: hexDecode("74323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z" }, { opt: timeRFC3339NanoOpt, wantCborData: hexDecode("76323031332d30332d32315432303a30343a30302e355a"), // "2013-03-21T20:04:00.5Z" }, }, }, { name: "time before January 1, 1970 UTC without fractional seconds", tm: parseTime(time.RFC3339Nano, "1969-03-21T20:04:00Z"), convert: []timeConvert{ { opt: timeUnixOpt, wantCborData: hexDecode("3a0177f2cf"), // -24638160 }, { opt: timeUnixMicroOpt, wantCborData: hexDecode("fbc1777f2d00000000"), // -24638160.0 }, { opt: timeUnixDynamicOpt, wantCborData: hexDecode("3a0177f2cf"), // -24638160 }, { opt: timeRFC3339Opt, wantCborData: hexDecode("74313936392d30332d32315432303a30343a30305a"), // "1969-03-21T20:04:00Z" }, { opt: timeRFC3339NanoOpt, wantCborData: hexDecode("74313936392d30332d32315432303a30343a30305a"), // "1969-03-21T20:04:00Z" }, }, }, } for _, tc := range testCases { for _, convert := range tc.convert { var convertName string switch convert.opt.Time { case TimeUnix: convertName = "TimeUnix" case TimeUnixMicro: convertName = "TimeUnixMicro" case TimeUnixDynamic: convertName = "TimeUnixDynamic" case TimeRFC3339: convertName = "TimeRFC3339" case TimeRFC3339Nano: convertName = "TimeRFC3339Nano" } name := tc.name + " with " + convertName + " option" t.Run(name, func(t *testing.T) { em, err := convert.opt.EncMode() if err != nil { t.Errorf("EncMode() returned error %v", err) } b, err := em.Marshal(tc.tm) if err != nil { t.Errorf("Marshal(%+v) returned error %v", tc.tm, err) } else if !bytes.Equal(b, convert.wantCborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.tm, b, convert.wantCborData) } }) } } } func TestEncodeTimeWithTag(t *testing.T) { timeUnixOpt := EncOptions{Time: TimeUnix, TimeTag: EncTagRequired} timeUnixMicroOpt := EncOptions{Time: TimeUnixMicro, TimeTag: EncTagRequired} timeUnixDynamicOpt := EncOptions{Time: TimeUnixDynamic, TimeTag: EncTagRequired} timeRFC3339Opt := EncOptions{Time: TimeRFC3339, TimeTag: EncTagRequired} timeRFC3339NanoOpt := EncOptions{Time: TimeRFC3339Nano, TimeTag: EncTagRequired} type timeConvert struct { opt EncOptions wantCborData []byte } testCases := []struct { name string tm time.Time convert []timeConvert }{ { name: "zero time", tm: time.Time{}, convert: []timeConvert{ { opt: timeUnixOpt, wantCborData: hexDecode("f6"), // encode as CBOR null }, { opt: timeUnixMicroOpt, wantCborData: hexDecode("f6"), // encode as CBOR null }, { opt: timeUnixDynamicOpt, wantCborData: hexDecode("f6"), // encode as CBOR null }, { opt: timeRFC3339Opt, wantCborData: hexDecode("f6"), // encode as CBOR null }, { opt: timeRFC3339NanoOpt, wantCborData: hexDecode("f6"), // encode as CBOR null }, }, }, { name: "time without fractional seconds", tm: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"), convert: []timeConvert{ { opt: timeUnixOpt, wantCborData: hexDecode("c11a514b67b0"), // 1363896240 }, { opt: timeUnixMicroOpt, wantCborData: hexDecode("c1fb41d452d9ec000000"), // 1363896240.0 }, { opt: timeUnixDynamicOpt, wantCborData: hexDecode("c11a514b67b0"), // 1363896240 }, { opt: timeRFC3339Opt, wantCborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z" }, { opt: timeRFC3339NanoOpt, wantCborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z" }, }, }, { name: "time with fractional seconds", tm: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00.5Z"), convert: []timeConvert{ { opt: timeUnixOpt, wantCborData: hexDecode("c11a514b67b0"), // 1363896240 }, { opt: timeUnixMicroOpt, wantCborData: hexDecode("c1fb41d452d9ec200000"), // 1363896240.5 }, { opt: timeUnixDynamicOpt, wantCborData: hexDecode("c1fb41d452d9ec200000"), // 1363896240.5 }, { opt: timeRFC3339Opt, wantCborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z" }, { opt: timeRFC3339NanoOpt, wantCborData: hexDecode("c076323031332d30332d32315432303a30343a30302e355a"), // "2013-03-21T20:04:00.5Z" }, }, }, { name: "time before January 1, 1970 UTC without fractional seconds", tm: parseTime(time.RFC3339Nano, "1969-03-21T20:04:00Z"), convert: []timeConvert{ { opt: timeUnixOpt, wantCborData: hexDecode("c13a0177f2cf"), // -24638160 }, { opt: timeUnixMicroOpt, wantCborData: hexDecode("c1fbc1777f2d00000000"), // -24638160.0 }, { opt: timeUnixDynamicOpt, wantCborData: hexDecode("c13a0177f2cf"), // -24638160 }, { opt: timeRFC3339Opt, wantCborData: hexDecode("c074313936392d30332d32315432303a30343a30305a"), // "1969-03-21T20:04:00Z" }, { opt: timeRFC3339NanoOpt, wantCborData: hexDecode("c074313936392d30332d32315432303a30343a30305a"), // "1969-03-21T20:04:00Z" }, }, }, } for _, tc := range testCases { for _, convert := range tc.convert { var convertName string switch convert.opt.Time { case TimeUnix: convertName = "TimeUnix" case TimeUnixMicro: convertName = "TimeUnixMicro" case TimeUnixDynamic: convertName = "TimeUnixDynamic" case TimeRFC3339: convertName = "TimeRFC3339" case TimeRFC3339Nano: convertName = "TimeRFC3339Nano" } name := tc.name + " with " + convertName + " option" t.Run(name, func(t *testing.T) { em, err := convert.opt.EncMode() if err != nil { t.Errorf("EncMode() returned error %v", err) } b, err := em.Marshal(tc.tm) if err != nil { t.Errorf("Marshal(%+v) returned error %v", tc.tm, err) } else if !bytes.Equal(b, convert.wantCborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.tm, b, convert.wantCborData) } }) } } } func parseTime(layout string, value string) time.Time { tm, err := time.Parse(layout, value) if err != nil { panic(err) } return tm } func TestInvalidTimeMode(t *testing.T) { wantErrorMsg := "cbor: invalid TimeMode 100" _, err := EncOptions{Time: TimeMode(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) } } func TestMarshalStructTag1(t *testing.T) { type strc struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` } v := strc{ A: "A", B: "B", C: "C", } want := hexDecode("a3616161416162614261636143") // {"a":"A", "b":"B", "c":"C"} if b, err := Marshal(v); err != nil { t.Errorf("Marshal(%+v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%+v) = %v, want %v", v, b, want) } } func TestMarshalStructTag2(t *testing.T) { type strc struct { A string `json:"a"` B string `json:"b"` C string `json:"c"` } v := strc{ A: "A", B: "B", C: "C", } want := hexDecode("a3616161416162614261636143") // {"a":"A", "b":"B", "c":"C"} if b, err := Marshal(v); err != nil { t.Errorf("Marshal(%+v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%+v) = %v, want %v", v, b, want) } } func TestMarshalStructTag3(t *testing.T) { type strc struct { A string `json:"x" cbor:"a"` B string `json:"y" cbor:"b"` C string `json:"z"` } v := strc{ A: "A", B: "B", C: "C", } want := hexDecode("a36161614161626142617a6143") // {"a":"A", "b":"B", "z":"C"} if b, err := Marshal(v); err != nil { t.Errorf("Marshal(%+v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%+v) = %v, want %v", v, b, want) } } func TestMarshalStructTag4(t *testing.T) { type strc struct { A string `json:"x" cbor:"a"` B string `json:"y" cbor:"b"` C string `json:"-"` } v := strc{ A: "A", B: "B", C: "C", } want := hexDecode("a26161614161626142") // {"a":"A", "b":"B"} if b, err := Marshal(v); err != nil { t.Errorf("Marshal(%+v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%+v) = %v, want %v", v, b, want) } } func TestMarshalStructLongFieldName(t *testing.T) { type strc struct { A string `cbor:"a"` B string `cbor:"abcdefghijklmnopqrstuvwxyz"` C string `cbor:"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmn"` } v := strc{ A: "A", B: "B", C: "C", } want := hexDecode("a361616141781a6162636465666768696a6b6c6d6e6f707172737475767778797a614278426162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6143") // {"a":"A", "abcdefghijklmnopqrstuvwxyz":"B", "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmn":"C"} if b, err := Marshal(v); err != nil { t.Errorf("Marshal(%+v) returned error %v", v, err) } else if !bytes.Equal(b, want) { t.Errorf("Marshal(%+v) = %v, want %v", v, b, want) } } func TestMarshalRawMessageValue(t *testing.T) { type ( T1 struct { M RawMessage `cbor:",omitempty"` } T2 struct { M *RawMessage `cbor:",omitempty"` } ) var ( rawNil = RawMessage(nil) rawEmpty = RawMessage([]byte{}) raw = RawMessage([]byte{0x01}) ) tests := []struct { obj interface{} want []byte }{ // Test with nil RawMessage. {rawNil, []byte{0xf6}}, {&rawNil, []byte{0xf6}}, {[]interface{}{rawNil}, []byte{0x81, 0xf6}}, {&[]interface{}{rawNil}, []byte{0x81, 0xf6}}, {[]interface{}{&rawNil}, []byte{0x81, 0xf6}}, {&[]interface{}{&rawNil}, []byte{0x81, 0xf6}}, {struct{ M RawMessage }{rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {&struct{ M RawMessage }{rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {struct{ M *RawMessage }{&rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {&struct{ M *RawMessage }{&rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {map[string]interface{}{"M": rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {&map[string]interface{}{"M": rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {map[string]interface{}{"M": &rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {&map[string]interface{}{"M": &rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {T1{rawNil}, []byte{0xa0}}, {T2{&rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {&T1{rawNil}, []byte{0xa0}}, {&T2{&rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, // Test with empty, but non-nil, RawMessage. {rawEmpty, []byte{0xf6}}, {&rawEmpty, []byte{0xf6}}, {[]interface{}{rawEmpty}, []byte{0x81, 0xf6}}, {&[]interface{}{rawEmpty}, []byte{0x81, 0xf6}}, {[]interface{}{&rawEmpty}, []byte{0x81, 0xf6}}, {&[]interface{}{&rawEmpty}, []byte{0x81, 0xf6}}, {struct{ M RawMessage }{rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {&struct{ M RawMessage }{rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {struct{ M *RawMessage }{&rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {&struct{ M *RawMessage }{&rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {map[string]interface{}{"M": rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {&map[string]interface{}{"M": rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {map[string]interface{}{"M": &rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {&map[string]interface{}{"M": &rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {T1{rawEmpty}, []byte{0xa0}}, {T2{&rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, {&T1{rawEmpty}, []byte{0xa0}}, {&T2{&rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}}, // Test with RawMessage with some data. {raw, []byte{0x01}}, {&raw, []byte{0x01}}, {[]interface{}{raw}, []byte{0x81, 0x01}}, {&[]interface{}{raw}, []byte{0x81, 0x01}}, {[]interface{}{&raw}, []byte{0x81, 0x01}}, {&[]interface{}{&raw}, []byte{0x81, 0x01}}, {struct{ M RawMessage }{raw}, []byte{0xa1, 0x61, 0x4d, 0x01}}, {&struct{ M RawMessage }{raw}, []byte{0xa1, 0x61, 0x4d, 0x01}}, {struct{ M *RawMessage }{&raw}, []byte{0xa1, 0x61, 0x4d, 0x01}}, {&struct{ M *RawMessage }{&raw}, []byte{0xa1, 0x61, 0x4d, 0x01}}, {map[string]interface{}{"M": raw}, []byte{0xa1, 0x61, 0x4d, 0x01}}, {&map[string]interface{}{"M": raw}, []byte{0xa1, 0x61, 0x4d, 0x01}}, {map[string]interface{}{"M": &raw}, []byte{0xa1, 0x61, 0x4d, 0x01}}, {&map[string]interface{}{"M": &raw}, []byte{0xa1, 0x61, 0x4d, 0x01}}, {T1{raw}, []byte{0xa1, 0x61, 0x4d, 0x01}}, {T2{&raw}, []byte{0xa1, 0x61, 0x4d, 0x01}}, {&T1{raw}, []byte{0xa1, 0x61, 0x4d, 0x01}}, {&T2{&raw}, []byte{0xa1, 0x61, 0x4d, 0x01}}, } for _, tc := range tests { b, err := Marshal(tc.obj) if err != nil { t.Errorf("Marshal(%+v) returned error %v", tc.obj, err) } if !bytes.Equal(b, tc.want) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.want) } } } func TestCyclicDataStructure(t *testing.T) { type Node struct { V int `cbor:"v"` N *Node `cbor:"n,omitempty"` } v := Node{1, &Node{2, &Node{3, nil}}} // linked list: 1, 2, 3 wantCborData := []byte{0xa2, 0x61, 0x76, 0x01, 0x61, 0x6e, 0xa2, 0x61, 0x76, 0x02, 0x61, 0x6e, 0xa1, 0x61, 0x76, 0x03} // {v: 1, n: {v: 2, n: {v: 3}}} cborData, err := Marshal(v) if err != nil { t.Fatalf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(wantCborData, cborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, cborData, wantCborData) } var v1 Node if err = Unmarshal(cborData, &v1); err != nil { t.Fatalf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(v, v1) { t.Errorf("Unmarshal(0x%x) returned %+v, want %+v", cborData, v1, v) } } func TestMarshalUnmarshalStructKeyAsInt(t *testing.T) { type T struct { F1 int `cbor:"1,omitempty,keyasint"` F2 int `cbor:"2,omitempty"` F3 int `cbor:"-3,omitempty,keyasint"` } testCases := []struct { name string obj interface{} wantCborData []byte }{ { "Zero value struct", T{}, hexDecode("a0"), // {} }, { "Initialized value struct", T{F1: 1, F2: 2, F3: 3}, hexDecode("a301012203613202"), // {1: 1, -3: 3, "2": 2} }, } em, err := EncOptions{Sort: SortCanonical}.EncMode() if err != nil { t.Errorf("EncMode() returned error %v", err) } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { b, err := em.Marshal(tc.obj) if err != nil { t.Errorf("Marshal(%+v) returned error %v", tc.obj, err) } if !bytes.Equal(b, tc.wantCborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.wantCborData) } var v2 T if err := Unmarshal(b, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } if !reflect.DeepEqual(tc.obj, v2) { t.Errorf("Unmarshal(0x%x) returned %+v, want %+v", b, v2, tc.obj) } }) } } func TestMarshalStructKeyAsIntNumError(t *testing.T) { type T1 struct { F1 int `cbor:"2.0,keyasint"` } type T2 struct { F1 int `cbor:"-18446744073709551616,keyasint"` } testCases := []struct { name string obj interface{} wantErrorMsg string }{ { name: "float as key", obj: T1{}, wantErrorMsg: "cbor: failed to parse field name \"2.0\" to int", }, { name: "out of range int as key", obj: T2{}, wantErrorMsg: "cbor: failed to parse field name \"-18446744073709551616\" to int", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { b, err := Marshal(tc.obj) if err == nil { t.Errorf("Marshal(%+v) didn't return an error, want error %q", tc.obj, tc.wantErrorMsg) } else if !strings.Contains(err.Error(), tc.wantErrorMsg) { t.Errorf("Marshal(%v) error %v, want %v", tc.obj, err.Error(), tc.wantErrorMsg) } else if b != nil { t.Errorf("Marshal(%v) = 0x%x, want nil", tc.obj, b) } }) } } func TestMarshalUnmarshalStructToArray(t *testing.T) { type T1 struct { M int `cbor:",omitempty"` } type T2 struct { N int `cbor:",omitempty"` O int `cbor:",omitempty"` } type T struct { _ struct{} `cbor:",toarray"` A int `cbor:",omitempty"` B T1 // nested struct T1 // embedded struct *T2 // embedded struct } testCases := []struct { name string obj T wantCborData []byte }{ { "Zero value struct (test omitempty)", T{}, hexDecode("8500a000f6f6"), // [0, {}, 0, nil, nil] }, { "Initialized struct", T{A: 24, B: T1{M: 1}, T1: T1{M: 2}, T2: &T2{N: 3, O: 4}}, hexDecode("851818a1614d01020304"), // [24, {M: 1}, 2, 3, 4] }, { "Null pointer to embedded struct", T{A: 24, B: T1{M: 1}, T1: T1{M: 2}}, hexDecode("851818a1614d0102f6f6"), // [24, {M: 1}, 2, nil, nil] }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { b, err := Marshal(tc.obj) if err != nil { t.Errorf("Marshal(%+v) returned error %v", tc.obj, err) } if !bytes.Equal(b, tc.wantCborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.wantCborData) } // SortMode should be ignored for struct to array encoding em, err := EncOptions{Sort: SortCanonical}.EncMode() if err != nil { t.Errorf("EncMode() returned error %v", err) } b, err = em.Marshal(tc.obj) if err != nil { t.Errorf("Marshal(%+v) returned error %v", tc.obj, err) } if !bytes.Equal(b, tc.wantCborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.wantCborData) } var v2 T if err := Unmarshal(b, &v2); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } if tc.obj.T2 == nil { if !reflect.DeepEqual(*(v2.T2), T2{}) { t.Errorf("Unmarshal(0x%x) returned %+v, want %+v", b, v2, tc.obj) } v2.T2 = nil } if !reflect.DeepEqual(tc.obj, v2) { t.Errorf("Unmarshal(0x%x) returned %+v, want %+v", b, v2, tc.obj) } }) } } func TestMapSort(t *testing.T) { m := make(map[interface{}]bool) m[10] = true m[100] = true m[-1] = true m["z"] = true m["aa"] = true m[[1]int{100}] = true m[[1]int{-1}] = true m[false] = true lenFirstSortedCborData := hexDecode("a80af520f5f4f51864f5617af58120f5626161f5811864f5") // sorted keys: 10, -1, false, 100, "z", [-1], "aa", [100] bytewiseSortedCborData := hexDecode("a80af51864f520f5617af5626161f5811864f58120f5f4f5") // sorted keys: 10, 100, -1, "z", "aa", [100], [-1], false testCases := []struct { name string opts EncOptions wantCborData []byte }{ {"Length first sort", EncOptions{Sort: SortLengthFirst}, lenFirstSortedCborData}, {"Bytewise sort", EncOptions{Sort: SortBytewiseLexical}, bytewiseSortedCborData}, {"CBOR canonical sort", EncOptions{Sort: SortCanonical}, lenFirstSortedCborData}, {"CTAP2 canonical sort", EncOptions{Sort: SortCTAP2}, bytewiseSortedCborData}, {"Core deterministic sort", EncOptions{Sort: SortCoreDeterministic}, bytewiseSortedCborData}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { em, err := tc.opts.EncMode() if err != nil { t.Errorf("EncMode() returned error %v", err) } b, err := em.Marshal(m) if err != nil { t.Errorf("Marshal(%v) returned error %v", m, err) } if !bytes.Equal(b, tc.wantCborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", m, b, tc.wantCborData) } }) } } func TestStructSort(t *testing.T) { type T struct { A bool `cbor:"aa"` B bool `cbor:"z"` C bool `cbor:"-1,keyasint"` D bool `cbor:"100,keyasint"` E bool `cbor:"10,keyasint"` } var v T unsortedCborData := hexDecode("a5626161f4617af420f41864f40af4") // unsorted fields: "aa", "z", -1, 100, 10 lenFirstSortedCborData := hexDecode("a50af420f41864f4617af4626161f4") // sorted fields: 10, -1, 100, "z", "aa", bytewiseSortedCborData := hexDecode("a50af41864f420f4617af4626161f4") // sorted fields: 10, 100, -1, "z", "aa" testCases := []struct { name string opts EncOptions wantCborData []byte }{ {"No sort", EncOptions{}, unsortedCborData}, {"No sort", EncOptions{Sort: SortNone}, unsortedCborData}, {"Length first sort", EncOptions{Sort: SortLengthFirst}, lenFirstSortedCborData}, {"Bytewise sort", EncOptions{Sort: SortBytewiseLexical}, bytewiseSortedCborData}, {"CBOR canonical sort", EncOptions{Sort: SortCanonical}, lenFirstSortedCborData}, {"CTAP2 canonical sort", EncOptions{Sort: SortCTAP2}, bytewiseSortedCborData}, {"Core deterministic sort", EncOptions{Sort: SortCoreDeterministic}, bytewiseSortedCborData}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { em, err := tc.opts.EncMode() if err != nil { t.Errorf("EncMode() returned error %v", err) } b, err := em.Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(b, tc.wantCborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, tc.wantCborData) } }) } } func TestInvalidSort(t *testing.T) { wantErrorMsg := "cbor: invalid SortMode 100" _, err := EncOptions{Sort: SortMode(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) } } func TestTypeAlias(t *testing.T) { //nolint:dupl,unconvert type myBool = bool type myUint = uint type myUint8 = uint8 type myUint16 = uint16 type myUint32 = uint32 type myUint64 = uint64 type myInt = int type myInt8 = int8 type myInt16 = int16 type myInt32 = int32 type myInt64 = int64 type myFloat32 = float32 type myFloat64 = float64 type myString = string type myByteSlice = []byte type myIntSlice = []int type myIntArray = [4]int type myMapIntInt = map[int]int testCases := []roundTripTest{ { name: "bool alias", obj: myBool(true), wantCborData: hexDecode("f5"), }, { name: "uint alias", obj: myUint(0), wantCborData: hexDecode("00"), }, { name: "uint8 alias", obj: myUint8(0), wantCborData: hexDecode("00"), }, { name: "uint16 alias", obj: myUint16(1000), wantCborData: hexDecode("1903e8"), }, { name: "uint32 alias", obj: myUint32(1000000), wantCborData: hexDecode("1a000f4240"), }, { name: "uint64 alias", obj: myUint64(1000000000000), wantCborData: hexDecode("1b000000e8d4a51000"), }, { name: "int alias", obj: myInt(-1), wantCborData: hexDecode("20"), }, { name: "int8 alias", obj: myInt8(-1), wantCborData: hexDecode("20"), }, { name: "int16 alias", obj: myInt16(-1000), wantCborData: hexDecode("3903e7"), }, { name: "int32 alias", obj: myInt32(-1000), wantCborData: hexDecode("3903e7"), }, { name: "int64 alias", obj: myInt64(-1000), wantCborData: hexDecode("3903e7"), }, { name: "float32 alias", obj: myFloat32(100000.0), wantCborData: hexDecode("fa47c35000"), }, { name: "float64 alias", obj: myFloat64(1.1), wantCborData: hexDecode("fb3ff199999999999a"), }, { name: "string alias", obj: myString("a"), wantCborData: hexDecode("6161"), }, { name: "[]byte alias", obj: myByteSlice([]byte{1, 2, 3, 4}), //nolint:unconvert wantCborData: hexDecode("4401020304"), }, { name: "[]int alias", obj: myIntSlice([]int{1, 2, 3, 4}), //nolint:unconvert wantCborData: hexDecode("8401020304"), }, { name: "[4]int alias", obj: myIntArray([...]int{1, 2, 3, 4}), //nolint:unconvert wantCborData: hexDecode("8401020304"), }, { name: "map[int]int alias", obj: myMapIntInt(map[int]int{1: 2, 3: 4}), //nolint:unconvert wantCborData: hexDecode("a201020304"), }, } em, err := EncOptions{Sort: SortCanonical}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } dm, err := DecOptions{}.DecMode() if err != nil { t.Errorf("DecMode() returned an error %v", err) } testRoundTrip(t, testCases, em, dm) } func TestNewTypeWithBuiltinUnderlyingType(t *testing.T) { //nolint:dupl type myBool bool type myUint uint type myUint8 uint8 type myUint16 uint16 type myUint32 uint32 type myUint64 uint64 type myInt int type myInt8 int8 type myInt16 int16 type myInt32 int32 type myInt64 int64 type myFloat32 float32 type myFloat64 float64 type myString string type myByteSlice []byte type myIntSlice []int type myIntArray [4]int type myMapIntInt map[int]int testCases := []roundTripTest{ { name: "bool alias", obj: myBool(true), wantCborData: hexDecode("f5"), }, { name: "uint alias", obj: myUint(0), wantCborData: hexDecode("00"), }, { name: "uint8 alias", obj: myUint8(0), wantCborData: hexDecode("00"), }, { name: "uint16 alias", obj: myUint16(1000), wantCborData: hexDecode("1903e8"), }, { name: "uint32 alias", obj: myUint32(1000000), wantCborData: hexDecode("1a000f4240"), }, { name: "uint64 alias", obj: myUint64(1000000000000), wantCborData: hexDecode("1b000000e8d4a51000"), }, { name: "int alias", obj: myInt(-1), wantCborData: hexDecode("20"), }, { name: "int8 alias", obj: myInt8(-1), wantCborData: hexDecode("20"), }, { name: "int16 alias", obj: myInt16(-1000), wantCborData: hexDecode("3903e7"), }, { name: "int32 alias", obj: myInt32(-1000), wantCborData: hexDecode("3903e7"), }, { name: "int64 alias", obj: myInt64(-1000), wantCborData: hexDecode("3903e7"), }, { name: "float32 alias", obj: myFloat32(100000.0), wantCborData: hexDecode("fa47c35000"), }, { name: "float64 alias", obj: myFloat64(1.1), wantCborData: hexDecode("fb3ff199999999999a"), }, { name: "string alias", obj: myString("a"), wantCborData: hexDecode("6161"), }, { name: "[]byte alias", obj: myByteSlice([]byte{1, 2, 3, 4}), wantCborData: hexDecode("4401020304"), }, { name: "[]int alias", obj: myIntSlice([]int{1, 2, 3, 4}), wantCborData: hexDecode("8401020304"), }, { name: "[4]int alias", obj: myIntArray([...]int{1, 2, 3, 4}), wantCborData: hexDecode("8401020304"), }, { name: "map[int]int alias", obj: myMapIntInt(map[int]int{1: 2, 3: 4}), wantCborData: hexDecode("a201020304"), }, } em, err := EncOptions{Sort: SortCanonical}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } dm, err := DecOptions{}.DecMode() if err != nil { t.Errorf("DecMode() returned an error %v", err) } testRoundTrip(t, testCases, em, dm) } func TestShortestFloat16(t *testing.T) { testCases := []struct { name string f64 float64 wantCborData []byte }{ // Data from RFC 7049 appendix A {"Shrink to float16", 0.0, hexDecode("f90000")}, {"Shrink to float16", 1.0, hexDecode("f93c00")}, {"Shrink to float16", 1.5, hexDecode("f93e00")}, {"Shrink to float16", 65504.0, hexDecode("f97bff")}, {"Shrink to float16", 5.960464477539063e-08, hexDecode("f90001")}, {"Shrink to float16", 6.103515625e-05, hexDecode("f90400")}, {"Shrink to float16", -4.0, hexDecode("f9c400")}, // Data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format {"Shrink to float16", 0.333251953125, hexDecode("f93555")}, // Data from 7049bis 4.2.1 and 5.5 {"Shrink to float16", 5.5, hexDecode("f94580")}, // Data from RFC 7049 appendix A {"Shrink to float32", 100000.0, hexDecode("fa47c35000")}, {"Shrink to float32", 3.4028234663852886e+38, hexDecode("fa7f7fffff")}, // Data from 7049bis 4.2.1 and 5.5 {"Shrink to float32", 5555.5, hexDecode("fa45ad9c00")}, {"Shrink to float32", 1000000.5, hexDecode("fa49742408")}, // Data from RFC 7049 appendix A {"Shrink to float64", 1.0e+300, hexDecode("fb7e37e43c8800759c")}, } em, err := EncOptions{ShortestFloat: ShortestFloat16}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { b, err := em.Marshal(tc.f64) if err != nil { t.Errorf("Marshal(%v) returned error %v", tc.f64, err) } else if !bytes.Equal(b, tc.wantCborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.f64, b, tc.wantCborData) } var f64 float64 if err = Unmarshal(b, &f64); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if f64 != tc.f64 { t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f64, tc.f64) } }) } } /* func TestShortestFloat32(t *testing.T) { testCases := []struct { name string f64 float64 wantCborData []byte }{ // Data from RFC 7049 appendix A {"Shrink to float32", 0.0, hexDecode("fa00000000")}, {"Shrink to float32", 1.0, hexDecode("fa3f800000")}, {"Shrink to float32", 1.5, hexDecode("fa3fc00000")}, {"Shrink to float32", 65504.0, hexDecode("fa477fe000")}, {"Shrink to float32", 5.960464477539063e-08, hexDecode("fa33800000")}, {"Shrink to float32", 6.103515625e-05, hexDecode("fa38800000")}, {"Shrink to float32", -4.0, hexDecode("fac0800000")}, // Data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format {"Shrink to float32", 0.333251953125, hexDecode("fa3eaaa000")}, // Data from 7049bis 4.2.1 and 5.5 {"Shrink to float32", 5.5, hexDecode("fa40b00000")}, // Data from RFC 7049 appendix A {"Shrink to float32", 100000.0, hexDecode("fa47c35000")}, {"Shrink to float32", 3.4028234663852886e+38, hexDecode("fa7f7fffff")}, // Data from 7049bis 4.2.1 and 5.5 {"Shrink to float32", 5555.5, hexDecode("fa45ad9c00")}, {"Shrink to float32", 1000000.5, hexDecode("fa49742408")}, // Data from RFC 7049 appendix A {"Shrink to float64", 1.0e+300, hexDecode("fb7e37e43c8800759c")}, } em, err := EncOptions{ShortestFloat: ShortestFloat32}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { b, err := em.Marshal(tc.f64) if err != nil { t.Errorf("Marshal(%v) returned error %v", tc.f64, err) } else if !bytes.Equal(b, tc.wantCborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.f64, b, tc.wantCborData) } var f64 float64 if err = Unmarshal(b, &f64); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if f64 != tc.f64 { t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f64, tc.f64) } }) } } func TestShortestFloat64(t *testing.T) { testCases := []struct { name string f64 float64 wantCborData []byte }{ // Data from RFC 7049 appendix A {"Shrink to float64", 0.0, hexDecode("fb0000000000000000")}, {"Shrink to float64", 1.0, hexDecode("fb3ff0000000000000")}, {"Shrink to float64", 1.5, hexDecode("fb3ff8000000000000")}, {"Shrink to float64", 65504.0, hexDecode("fb40effc0000000000")}, {"Shrink to float64", 5.960464477539063e-08, hexDecode("fb3e70000000000000")}, {"Shrink to float64", 6.103515625e-05, hexDecode("fb3f10000000000000")}, {"Shrink to float64", -4.0, hexDecode("fbc010000000000000")}, // Data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format {"Shrink to float64", 0.333251953125, hexDecode("fb3fd5540000000000")}, // Data from 7049bis 4.2.1 and 5.5 {"Shrink to float64", 5.5, hexDecode("fb4016000000000000")}, // Data from RFC 7049 appendix A {"Shrink to float64", 100000.0, hexDecode("fb40f86a0000000000")}, {"Shrink to float64", 3.4028234663852886e+38, hexDecode("fb47efffffe0000000")}, // Data from 7049bis 4.2.1 and 5.5 {"Shrink to float64", 5555.5, hexDecode("fb40b5b38000000000")}, {"Shrink to float64", 1000000.5, hexDecode("fb412e848100000000")}, // Data from RFC 7049 appendix A {"Shrink to float64", 1.0e+300, hexDecode("fb7e37e43c8800759c")}, } em, err := EncOptions{ShortestFloat: ShortestFloat64}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { b, err := em.Marshal(tc.f64) if err != nil { t.Errorf("Marshal(%v) returned error %v", tc.f64, err) } else if !bytes.Equal(b, tc.wantCborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.f64, b, tc.wantCborData) } var f64 float64 if err = Unmarshal(b, &f64); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if f64 != tc.f64 { t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f64, tc.f64) } }) } } */ func TestShortestFloatNone(t *testing.T) { testCases := []struct { name string f interface{} wantCborData []byte }{ // Data from RFC 7049 appendix A {"float32", float32(0.0), hexDecode("fa00000000")}, {"float64", float64(0.0), hexDecode("fb0000000000000000")}, {"float32", float32(1.0), hexDecode("fa3f800000")}, {"float64", float64(1.0), hexDecode("fb3ff0000000000000")}, {"float32", float32(1.5), hexDecode("fa3fc00000")}, {"float64", float64(1.5), hexDecode("fb3ff8000000000000")}, {"float32", float32(65504.0), hexDecode("fa477fe000")}, {"float64", float64(65504.0), hexDecode("fb40effc0000000000")}, {"float32", float32(5.960464477539063e-08), hexDecode("fa33800000")}, {"float64", float64(5.960464477539063e-08), hexDecode("fb3e70000000000000")}, {"float32", float32(6.103515625e-05), hexDecode("fa38800000")}, {"float64", float64(6.103515625e-05), hexDecode("fb3f10000000000000")}, {"float32", float32(-4.0), hexDecode("fac0800000")}, {"float64", float64(-4.0), hexDecode("fbc010000000000000")}, // Data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format {"float32", float32(0.333251953125), hexDecode("fa3eaaa000")}, {"float64", float64(0.333251953125), hexDecode("fb3fd5540000000000")}, // Data from 7049bis 4.2.1 and 5.5 {"float32", float32(5.5), hexDecode("fa40b00000")}, {"float64", float64(5.5), hexDecode("fb4016000000000000")}, // Data from RFC 7049 appendix A {"float32", float32(100000.0), hexDecode("fa47c35000")}, {"float64", float64(100000.0), hexDecode("fb40f86a0000000000")}, {"float32", float32(3.4028234663852886e+38), hexDecode("fa7f7fffff")}, {"float64", float64(3.4028234663852886e+38), hexDecode("fb47efffffe0000000")}, // Data from 7049bis 4.2.1 and 5.5 {"float32", float32(5555.5), hexDecode("fa45ad9c00")}, {"float64", float64(5555.5), hexDecode("fb40b5b38000000000")}, {"float32", float32(1000000.5), hexDecode("fa49742408")}, {"float64", float64(1000000.5), hexDecode("fb412e848100000000")}, {"float64", float64(1.0e+300), hexDecode("fb7e37e43c8800759c")}, } em, err := EncOptions{ShortestFloat: ShortestFloatNone}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { b, err := em.Marshal(tc.f) if err != nil { t.Errorf("Marshal(%v) returned error %v", tc.f, err) } else if !bytes.Equal(b, tc.wantCborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.f, b, tc.wantCborData) } if reflect.ValueOf(tc.f).Kind() == reflect.Float32 { var f32 float32 if err = Unmarshal(b, &f32); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if f32 != tc.f { t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f32, tc.f) } } else { var f64 float64 if err = Unmarshal(b, &f64); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", b, err) } else if f64 != tc.f { t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f64, tc.f) } } }) } } func TestInvalidShortestFloat(t *testing.T) { wantErrorMsg := "cbor: invalid ShortestFloatMode 100" _, err := EncOptions{ShortestFloat: ShortestFloatMode(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) } } func TestInfConvert(t *testing.T) { infConvertNoneOpt := EncOptions{InfConvert: InfConvertNone} infConvertFloat16Opt := EncOptions{InfConvert: InfConvertFloat16} testCases := []struct { name string v interface{} opts EncOptions wantCborData []byte }{ {"float32 -inf no conversion", float32(math.Inf(-1)), infConvertNoneOpt, hexDecode("faff800000")}, {"float32 +inf no conversion", float32(math.Inf(1)), infConvertNoneOpt, hexDecode("fa7f800000")}, {"float64 -inf no conversion", math.Inf(-1), infConvertNoneOpt, hexDecode("fbfff0000000000000")}, {"float64 +inf no conversion", math.Inf(1), infConvertNoneOpt, hexDecode("fb7ff0000000000000")}, {"float32 -inf to float16", float32(math.Inf(-1)), infConvertFloat16Opt, hexDecode("f9fc00")}, {"float32 +inf to float16", float32(math.Inf(1)), infConvertFloat16Opt, hexDecode("f97c00")}, {"float64 -inf to float16", math.Inf(-1), infConvertFloat16Opt, hexDecode("f9fc00")}, {"float64 +inf to float16", math.Inf(1), infConvertFloat16Opt, hexDecode("f97c00")}, } 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 TestInvalidInfConvert(t *testing.T) { wantErrorMsg := "cbor: invalid InfConvertMode 100" _, err := EncOptions{InfConvert: InfConvertMode(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 qnanConst0xffc00001 uint32 = 0xffc00001 qnanConst0x7fc00001 uint32 = 0x7fc00001 qnanConst0xffc02000 uint32 = 0xffc02000 qnanConst0x7fc02000 uint32 = 0x7fc02000 // snan 32 bits constants snanConst0xff800001 uint32 = 0xff800001 snanConst0x7f800001 uint32 = 0x7f800001 snanConst0xff802000 uint32 = 0xff802000 snanConst0x7f802000 uint32 = 0x7f802000 // qnan 64 bits constants qnanConst0xfff8000000000001 uint64 = 0xfff8000000000001 qnanConst0x7ff8000000000001 uint64 = 0x7ff8000000000001 qnanConst0xfff8000020000000 uint64 = 0xfff8000020000000 qnanConst0x7ff8000020000000 uint64 = 0x7ff8000020000000 qnanConst0xfffc000000000000 uint64 = 0xfffc000000000000 qnanConst0x7ffc000000000000 uint64 = 0x7ffc000000000000 // snan 64 bits constants snanConst0xfff0000000000001 uint64 = 0xfff0000000000001 snanConst0x7ff0000000000001 uint64 = 0x7ff0000000000001 snanConst0xfff0000020000000 uint64 = 0xfff0000020000000 snanConst0x7ff0000020000000 uint64 = 0x7ff0000020000000 snanConst0xfff4000000000000 uint64 = 0xfff4000000000000 snanConst0x7ff4000000000000 uint64 = 0x7ff4000000000000 ) var ( // qnan 32 bits variables qnanVar0xffc00001 = qnanConst0xffc00001 qnanVar0x7fc00001 = qnanConst0x7fc00001 qnanVar0xffc02000 = qnanConst0xffc02000 qnanVar0x7fc02000 = qnanConst0x7fc02000 // snan 32 bits variables snanVar0xff800001 = snanConst0xff800001 snanVar0x7f800001 = snanConst0x7f800001 snanVar0xff802000 = snanConst0xff802000 snanVar0x7f802000 = snanConst0x7f802000 // qnan 64 bits variables qnanVar0xfff8000000000001 = qnanConst0xfff8000000000001 qnanVar0x7ff8000000000001 = qnanConst0x7ff8000000000001 qnanVar0xfff8000020000000 = qnanConst0xfff8000020000000 qnanVar0x7ff8000020000000 = qnanConst0x7ff8000020000000 qnanVar0xfffc000000000000 = qnanConst0xfffc000000000000 qnanVar0x7ffc000000000000 = qnanConst0x7ffc000000000000 // snan 64 bits variables snanVar0xfff0000000000001 = snanConst0xfff0000000000001 snanVar0x7ff0000000000001 = snanConst0x7ff0000000000001 snanVar0xfff0000020000000 = snanConst0xfff0000020000000 snanVar0x7ff0000020000000 = snanConst0x7ff0000020000000 snanVar0xfff4000000000000 = snanConst0xfff4000000000000 snanVar0x7ff4000000000000 = snanConst0x7ff4000000000000 ) func TestNaNConvert(t *testing.T) { nanConvert7e00Opt := EncOptions{NaNConvert: NaNConvert7e00} nanConvertNoneOpt := EncOptions{NaNConvert: NaNConvertNone} nanConvertPreserveSignalOpt := EncOptions{NaNConvert: NaNConvertPreserveSignal} nanConvertQuietOpt := EncOptions{NaNConvert: NaNConvertQuiet} type nanConvert struct { opt EncOptions wantCborData []byte } testCases := []struct { v interface{} convert []nanConvert }{ // float32 qNaN dropped payload not zero {math.Float32frombits(qnanVar0xffc00001), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("faffc00001")}, {nanConvertPreserveSignalOpt, hexDecode("faffc00001")}, {nanConvertQuietOpt, hexDecode("faffc00001")}, }}, // float32 qNaN dropped payload not zero {math.Float32frombits(qnanVar0x7fc00001), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fa7fc00001")}, {nanConvertPreserveSignalOpt, hexDecode("fa7fc00001")}, {nanConvertQuietOpt, hexDecode("fa7fc00001")}, }}, // float32 -qNaN dropped payload zero {math.Float32frombits(qnanVar0xffc02000), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("faffc02000")}, {nanConvertPreserveSignalOpt, hexDecode("f9fe01")}, {nanConvertQuietOpt, hexDecode("f9fe01")}, }}, // float32 qNaN dropped payload zero {math.Float32frombits(qnanVar0x7fc02000), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fa7fc02000")}, {nanConvertPreserveSignalOpt, hexDecode("f97e01")}, {nanConvertQuietOpt, hexDecode("f97e01")}, }}, // float32 -sNaN dropped payload not zero {math.Float32frombits(snanVar0xff800001), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("faff800001")}, {nanConvertPreserveSignalOpt, hexDecode("faff800001")}, {nanConvertQuietOpt, hexDecode("faffc00001")}, }}, // float32 sNaN dropped payload not zero {math.Float32frombits(snanVar0x7f800001), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fa7f800001")}, {nanConvertPreserveSignalOpt, hexDecode("fa7f800001")}, {nanConvertQuietOpt, hexDecode("fa7fc00001")}, }}, // float32 -sNaN dropped payload zero {math.Float32frombits(snanVar0xff802000), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("faff802000")}, {nanConvertPreserveSignalOpt, hexDecode("f9fc01")}, {nanConvertQuietOpt, hexDecode("f9fe01")}, }}, // float32 sNaN dropped payload zero {math.Float32frombits(snanVar0x7f802000), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fa7f802000")}, {nanConvertPreserveSignalOpt, hexDecode("f97c01")}, {nanConvertQuietOpt, hexDecode("f97e01")}, }}, // float64 -qNaN dropped payload not zero {math.Float64frombits(qnanVar0xfff8000000000001), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fbfff8000000000001")}, {nanConvertPreserveSignalOpt, hexDecode("fbfff8000000000001")}, {nanConvertQuietOpt, hexDecode("fbfff8000000000001")}, }}, // float64 qNaN dropped payload not zero {math.Float64frombits(qnanVar0x7ff8000000000001), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fb7ff8000000000001")}, {nanConvertPreserveSignalOpt, hexDecode("fb7ff8000000000001")}, {nanConvertQuietOpt, hexDecode("fb7ff8000000000001")}, }}, // float64 -qNaN dropped payload zero {math.Float64frombits(qnanVar0xfff8000020000000), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fbfff8000020000000")}, {nanConvertPreserveSignalOpt, hexDecode("faffc00001")}, {nanConvertQuietOpt, hexDecode("faffc00001")}, }}, // float64 qNaN dropped payload zero {math.Float64frombits(qnanVar0x7ff8000020000000), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fb7ff8000020000000")}, {nanConvertPreserveSignalOpt, hexDecode("fa7fc00001")}, {nanConvertQuietOpt, hexDecode("fa7fc00001")}, }}, // float64 -qNaN dropped payload zero {math.Float64frombits(qnanVar0xfffc000000000000), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fbfffc000000000000")}, {nanConvertPreserveSignalOpt, hexDecode("f9ff00")}, {nanConvertQuietOpt, hexDecode("f9ff00")}, }}, // float64 qNaN dropped payload zero {math.Float64frombits(qnanVar0x7ffc000000000000), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fb7ffc000000000000")}, {nanConvertPreserveSignalOpt, hexDecode("f97f00")}, {nanConvertQuietOpt, hexDecode("f97f00")}, }}, // float64 -sNaN dropped payload not zero {math.Float64frombits(snanVar0xfff0000000000001), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fbfff0000000000001")}, {nanConvertPreserveSignalOpt, hexDecode("fbfff0000000000001")}, {nanConvertQuietOpt, hexDecode("fbfff8000000000001")}, }}, // float64 sNaN dropped payload not zero {math.Float64frombits(snanVar0x7ff0000000000001), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fb7ff0000000000001")}, {nanConvertPreserveSignalOpt, hexDecode("fb7ff0000000000001")}, {nanConvertQuietOpt, hexDecode("fb7ff8000000000001")}, }}, // float64 -sNaN dropped payload zero {math.Float64frombits(snanVar0xfff0000020000000), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fbfff0000020000000")}, {nanConvertPreserveSignalOpt, hexDecode("faff800001")}, {nanConvertQuietOpt, hexDecode("faffc00001")}, }}, // float64 sNaN dropped payload zero {math.Float64frombits(snanVar0x7ff0000020000000), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fb7ff0000020000000")}, {nanConvertPreserveSignalOpt, hexDecode("fa7f800001")}, {nanConvertQuietOpt, hexDecode("fa7fc00001")}, }}, // float64 -sNaN dropped payload zero {math.Float64frombits(snanVar0xfff4000000000000), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fbfff4000000000000")}, {nanConvertPreserveSignalOpt, hexDecode("f9fd00")}, {nanConvertQuietOpt, hexDecode("f9ff00")}, }}, // float64 sNaN dropped payload zero {math.Float64frombits(snanVar0x7ff4000000000000), []nanConvert{ {nanConvert7e00Opt, hexDecode("f97e00")}, {nanConvertNoneOpt, hexDecode("fb7ff4000000000000")}, {nanConvertPreserveSignalOpt, hexDecode("f97d00")}, {nanConvertQuietOpt, hexDecode("f97f00")}, }}, } for _, tc := range testCases { for _, convert := range tc.convert { var convertName string switch convert.opt.NaNConvert { case NaNConvert7e00: convertName = "Convert7e00" case NaNConvertNone: convertName = "ConvertNone" case NaNConvertPreserveSignal: convertName = "ConvertPreserveSignal" case NaNConvertQuiet: convertName = "ConvertQuiet" } var vName string switch v := tc.v.(type) { case float32: vName = fmt.Sprintf("0x%x", math.Float32bits(v)) case float64: vName = fmt.Sprintf("0x%x", math.Float64bits(v)) } name := convertName + "_" + vName t.Run(name, func(t *testing.T) { em, err := convert.opt.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, convert.wantCborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.v, b, convert.wantCborData) } }) } } } func TestInvalidNaNConvert(t *testing.T) { wantErrorMsg := "cbor: invalid NaNConvertMode 100" _, err := EncOptions{NaNConvert: NaNConvertMode(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) } } func TestMarshalSenML(t *testing.T) { // Data from https://tools.ietf.org/html/rfc8428#section-6 // Data contains 13 floating-point numbers. cborData := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333") testCases := []struct { name string opts EncOptions }{ {"EncOptions ShortestFloatNone", EncOptions{}}, {"EncOptions ShortestFloat16", EncOptions{ShortestFloat: ShortestFloat16}}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var v []SenMLRecord if err := Unmarshal(cborData, &v); err != nil { t.Errorf("Marshal() returned error %v", err) } em, err := tc.opts.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } b, err := em.Marshal(v) if err != nil { t.Errorf("Unmarshal() returned error %v ", err) } var v2 []SenMLRecord if err := Unmarshal(b, &v2); err != nil { t.Errorf("Marshal() returned error %v", err) } if !reflect.DeepEqual(v, v2) { t.Errorf("SenML round-trip failed: v1 %+v, v2 %+v", v, v2) } }) } } func TestCanonicalEncOptions(t *testing.T) { //nolint:dupl wantSortMode := SortCanonical wantShortestFloat := ShortestFloat16 wantNaNConvert := NaNConvert7e00 wantInfConvert := InfConvertFloat16 wantErrorMsg := "cbor: indefinite-length array isn't allowed" em, err := CanonicalEncOptions().EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } opts := em.EncOptions() if opts.Sort != wantSortMode { t.Errorf("CanonicalEncOptions() returned EncOptions with Sort %d, want %d", opts.Sort, wantSortMode) } if opts.ShortestFloat != wantShortestFloat { t.Errorf("CanonicalEncOptions() returned EncOptions with ShortestFloat %d, want %d", opts.ShortestFloat, wantShortestFloat) } if opts.NaNConvert != wantNaNConvert { t.Errorf("CanonicalEncOptions() returned EncOptions with NaNConvert %d, want %d", opts.NaNConvert, wantNaNConvert) } if opts.InfConvert != wantInfConvert { t.Errorf("CanonicalEncOptions() returned EncOptions with InfConvert %d, want %d", opts.InfConvert, wantInfConvert) } enc := em.NewEncoder(ioutil.Discard) if err := enc.StartIndefiniteArray(); err == nil { t.Errorf("StartIndefiniteArray() didn't return an error") } else if err.Error() != wantErrorMsg { t.Errorf("StartIndefiniteArray() returned error %q, want %q", err.Error(), wantErrorMsg) } } func TestCTAP2EncOptions(t *testing.T) { //nolint:dupl wantSortMode := SortCTAP2 wantShortestFloat := ShortestFloatNone wantNaNConvert := NaNConvertNone wantInfConvert := InfConvertNone wantErrorMsg := "cbor: indefinite-length array isn't allowed" em, err := CTAP2EncOptions().EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } opts := em.EncOptions() if opts.Sort != wantSortMode { t.Errorf("CTAP2EncOptions() returned EncOptions with Sort %d, want %d", opts.Sort, wantSortMode) } if opts.ShortestFloat != wantShortestFloat { t.Errorf("CTAP2EncOptions() returned EncOptions with ShortestFloat %d, want %d", opts.ShortestFloat, wantShortestFloat) } if opts.NaNConvert != wantNaNConvert { t.Errorf("CTAP2EncOptions() returned EncOptions with NaNConvert %d, want %d", opts.NaNConvert, wantNaNConvert) } if opts.InfConvert != wantInfConvert { t.Errorf("CTAP2EncOptions() returned EncOptions with InfConvert %d, want %d", opts.InfConvert, wantInfConvert) } enc := em.NewEncoder(ioutil.Discard) if err := enc.StartIndefiniteArray(); err == nil { t.Errorf("StartIndefiniteArray() didn't return an error") } else if err.Error() != wantErrorMsg { t.Errorf("StartIndefiniteArray() returned error %q, want %q", err.Error(), wantErrorMsg) } } func TestCoreDetEncOptions(t *testing.T) { //nolint:dupl wantSortMode := SortCoreDeterministic wantShortestFloat := ShortestFloat16 wantNaNConvert := NaNConvert7e00 wantInfConvert := InfConvertFloat16 wantErrorMsg := "cbor: indefinite-length array isn't allowed" em, err := CoreDetEncOptions().EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } opts := em.EncOptions() if opts.Sort != wantSortMode { t.Errorf("CoreDetEncOptions() returned EncOptions with Sort %d, want %d", opts.Sort, wantSortMode) } if opts.ShortestFloat != wantShortestFloat { t.Errorf("CoreDetEncOptions() returned EncOptions with ShortestFloat %d, want %d", opts.ShortestFloat, wantShortestFloat) } if opts.NaNConvert != wantNaNConvert { t.Errorf("CoreDetEncOptions() returned EncOptions with NaNConvert %d, want %d", opts.NaNConvert, wantNaNConvert) } if opts.InfConvert != wantInfConvert { t.Errorf("CoreDetEncOptions() returned EncOptions with InfConvert %d, want %d", opts.InfConvert, wantInfConvert) } enc := em.NewEncoder(ioutil.Discard) if err := enc.StartIndefiniteArray(); err == nil { t.Errorf("StartIndefiniteArray() didn't return an error") } else if err.Error() != wantErrorMsg { t.Errorf("StartIndefiniteArray() returned error %q, want %q", err.Error(), wantErrorMsg) } } func TestPreferredUnsortedEncOptions(t *testing.T) { wantSortMode := SortNone wantShortestFloat := ShortestFloat16 wantNaNConvert := NaNConvert7e00 wantInfConvert := InfConvertFloat16 em, err := PreferredUnsortedEncOptions().EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } opts := em.EncOptions() if opts.Sort != wantSortMode { t.Errorf("PreferredUnsortedEncOptions() returned EncOptions with Sort %d, want %d", opts.Sort, wantSortMode) } if opts.ShortestFloat != wantShortestFloat { t.Errorf("PreferredUnsortedEncOptions() returned EncOptions with ShortestFloat %d, want %d", opts.ShortestFloat, wantShortestFloat) } if opts.NaNConvert != wantNaNConvert { t.Errorf("PreferredUnsortedEncOptions() returned EncOptions with NaNConvert %d, want %d", opts.NaNConvert, wantNaNConvert) } if opts.InfConvert != wantInfConvert { t.Errorf("PreferredUnsortedEncOptions() returned EncOptions with InfConvert %d, want %d", opts.InfConvert, wantInfConvert) } enc := em.NewEncoder(ioutil.Discard) if err := enc.StartIndefiniteArray(); err != nil { t.Errorf("StartIndefiniteArray() returned error %v", err) } } func TestEncModeInvalidIndefiniteLengthMode(t *testing.T) { wantErrorMsg := "cbor: invalid IndefLength 101" _, err := EncOptions{IndefLength: 101}.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) } } func TestEncModeInvalidTagsMode(t *testing.T) { wantErrorMsg := "cbor: invalid TagsMd 101" _, err := EncOptions{TagsMd: 101}.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) } } func TestEncModeInvalidBigIntConvertMode(t *testing.T) { wantErrorMsg := "cbor: invalid BigIntConvertMode 101" _, err := EncOptions{BigIntConvert: 101}.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) } } func TestEncOptions(t *testing.T) { opts1 := EncOptions{ Sort: SortBytewiseLexical, ShortestFloat: ShortestFloat16, NaNConvert: NaNConvertPreserveSignal, InfConvert: InfConvertNone, BigIntConvert: BigIntConvertNone, Time: TimeRFC3339Nano, TimeTag: EncTagRequired, IndefLength: IndefLengthForbidden, TagsMd: TagsAllowed, } em, err := opts1.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } else { opts2 := em.EncOptions() if !reflect.DeepEqual(opts1, opts2) { t.Errorf("EncOptions->EncMode->EncOptions returned different values: %v, %v", opts1, opts2) } } } func TestEncModeInvalidTimeTag(t *testing.T) { wantErrorMsg := "cbor: invalid TimeTag 100" _, err := EncOptions{TimeTag: 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) } } func TestEncIndefiniteLengthOption(t *testing.T) { // Default option allows indefinite length items var buf bytes.Buffer enc := NewEncoder(&buf) if err := enc.StartIndefiniteByteString(); err != nil { t.Errorf("StartIndefiniteByteString() returned an error %v", err) } if err := enc.StartIndefiniteTextString(); err != nil { t.Errorf("StartIndefiniteTextString() returned an error %v", err) } if err := enc.StartIndefiniteArray(); err != nil { t.Errorf("StartIndefiniteArray() returned an error %v", err) } if err := enc.StartIndefiniteMap(); err != nil { t.Errorf("StartIndefiniteMap() returned an error %v", err) } // StartIndefiniteXXX returns error when IndefLength = IndefLengthForbidden em, _ := EncOptions{IndefLength: IndefLengthForbidden}.EncMode() enc = em.NewEncoder(&buf) wantErrorMsg := "cbor: indefinite-length byte string isn't allowed" if err := enc.StartIndefiniteByteString(); err == nil { t.Errorf("StartIndefiniteByteString() didn't return an error") } else if _, ok := err.(*IndefiniteLengthError); !ok { t.Errorf("StartIndefiniteByteString() error type %T, want *IndefiniteLengthError", err) } else if err.Error() != wantErrorMsg { t.Errorf("StartIndefiniteByteString() returned error %q, want %q", err.Error(), wantErrorMsg) } wantErrorMsg = "cbor: indefinite-length UTF-8 text string isn't allowed" if err := enc.StartIndefiniteTextString(); err == nil { t.Errorf("StartIndefiniteTextString() didn't return an error") } else if _, ok := err.(*IndefiniteLengthError); !ok { t.Errorf("StartIndefiniteTextString() error type %T, want *IndefiniteLengthError", err) } else if err.Error() != wantErrorMsg { t.Errorf("StartIndefiniteTextString() returned error %q, want %q", err.Error(), wantErrorMsg) } wantErrorMsg = "cbor: indefinite-length array isn't allowed" if err := enc.StartIndefiniteArray(); err == nil { t.Errorf("StartIndefiniteArray() didn't return an error") } else if _, ok := err.(*IndefiniteLengthError); !ok { t.Errorf("StartIndefiniteArray() error type %T, want *IndefiniteLengthError", err) } else if err.Error() != wantErrorMsg { t.Errorf("StartIndefiniteArray() returned error %q, want %q", err.Error(), wantErrorMsg) } wantErrorMsg = "cbor: indefinite-length map isn't allowed" if err := enc.StartIndefiniteMap(); err == nil { t.Errorf("StartIndefiniteMap() didn't return an error") } else if _, ok := err.(*IndefiniteLengthError); !ok { t.Errorf("StartIndefiniteMap() error type %T, want *IndefiniteLengthError", err) } else if err.Error() != wantErrorMsg { t.Errorf("StartIndefiniteMap() returned error %q, want %q", err.Error(), wantErrorMsg) } } func TestEncTagsMdOption(t *testing.T) { // Default option allows encoding CBOR tags tag := Tag{123, "hello"} if _, err := Marshal(tag); err != nil { t.Errorf("Marshal() returned an error %v", err) } // Create EncMode with TimeTag = EncTagRequired and TagsForbidden option returns error wantErrorMsg := "cbor: cannot set TagsMd to TagsForbidden when TimeTag is EncTagRequired" _, err := EncOptions{TimeTag: EncTagRequired, TagsMd: TagsForbidden}.EncMode() if err == nil { t.Errorf("EncModeWithTags() didn't return an error") } else if err.Error() != wantErrorMsg { t.Errorf("EncModeWithTags() returned error %q, want %q", err.Error(), wantErrorMsg) } // Create EncMode with TagSet and TagsForbidden option returns error wantErrorMsg = "cbor: cannot create EncMode with TagSet when TagsMd is TagsForbidden" tags := NewTagSet() _, err = EncOptions{TagsMd: TagsForbidden}.EncModeWithTags(tags) if err == nil { t.Errorf("EncModeWithTags() didn't return an error") } else if err.Error() != wantErrorMsg { t.Errorf("EncModeWithTags() returned error %q, want %q", err.Error(), wantErrorMsg) } _, err = EncOptions{TagsMd: TagsForbidden}.EncModeWithSharedTags(tags) if err == nil { t.Errorf("EncModeWithSharedTags() didn't return an error") } else if err.Error() != wantErrorMsg { t.Errorf("EncModeWithSharedTags() returned error %q, want %q", err.Error(), wantErrorMsg) } // Encoding Tag and TagsForbidden option returns error wantErrorMsg = "cbor: cannot encode cbor.Tag when TagsMd is TagsForbidden" em, _ := EncOptions{TagsMd: TagsForbidden}.EncMode() if _, err := em.Marshal(&tag); err == nil { t.Errorf("Marshal() didn't return an error") } else if err.Error() != wantErrorMsg { t.Errorf("Marshal() returned error %q, want %q", err.Error(), wantErrorMsg) } // Encoding RawTag and TagsForbidden option returns error wantErrorMsg = "cbor: cannot encode cbor.RawTag when TagsMd is TagsForbidden" rawTag := RawTag{123, []byte{01}} if _, err := em.Marshal(&rawTag); err == nil { t.Errorf("Marshal() didn't return an error") } else if err.Error() != wantErrorMsg { t.Errorf("Marshal() returned error %q, want %q", err.Error(), wantErrorMsg) } } func TestMarshalPosBigInt(t *testing.T) { testCases := []struct { name string cborDataShortest []byte cborDataBigInt []byte value big.Int }{ { name: "fit uint8", cborDataShortest: hexDecode("00"), cborDataBigInt: hexDecode("c240"), value: bigIntOrPanic("0"), }, { name: "fit uint16", cborDataShortest: hexDecode("1903e8"), cborDataBigInt: hexDecode("c24203e8"), value: bigIntOrPanic("1000"), }, { name: "fit uint32", cborDataShortest: hexDecode("1a000f4240"), cborDataBigInt: hexDecode("c2430f4240"), value: bigIntOrPanic("1000000"), }, { name: "fit uint64", cborDataShortest: hexDecode("1b000000e8d4a51000"), cborDataBigInt: hexDecode("c245e8d4a51000"), value: bigIntOrPanic("1000000000000"), }, { name: "max uint64", cborDataShortest: hexDecode("1bffffffffffffffff"), cborDataBigInt: hexDecode("c248ffffffffffffffff"), value: bigIntOrPanic("18446744073709551615"), }, { name: "overflow uint64", cborDataShortest: hexDecode("c249010000000000000000"), cborDataBigInt: hexDecode("c249010000000000000000"), value: bigIntOrPanic("18446744073709551616"), }, } dmShortest, err := EncOptions{}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } dmBigInt, err := EncOptions{BigIntConvert: BigIntConvertNone}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if b, err := dmShortest.Marshal(tc.value); err != nil { t.Errorf("Marshal(%v) returned error %v", tc.value, err) } else if !bytes.Equal(b, tc.cborDataShortest) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.value, b, tc.cborDataShortest) } if b, err := dmBigInt.Marshal(tc.value); err != nil { t.Errorf("Marshal(%v) returned error %v", tc.value, err) } else if !bytes.Equal(b, tc.cborDataBigInt) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.value, b, tc.cborDataBigInt) } }) } } func TestMarshalNegBigInt(t *testing.T) { testCases := []struct { name string cborDataShortest []byte cborDataBigInt []byte value big.Int }{ { name: "fit int8", cborDataShortest: hexDecode("20"), cborDataBigInt: hexDecode("c340"), value: bigIntOrPanic("-1"), }, { name: "fit int16", cborDataShortest: hexDecode("3903e7"), cborDataBigInt: hexDecode("c34203e7"), value: bigIntOrPanic("-1000"), }, { name: "fit int32", cborDataShortest: hexDecode("3a000f423f"), cborDataBigInt: hexDecode("c3430f423f"), value: bigIntOrPanic("-1000000"), }, { name: "fit int64", cborDataShortest: hexDecode("3b000000e8d4a50fff"), cborDataBigInt: hexDecode("c345e8d4a50fff"), value: bigIntOrPanic("-1000000000000"), }, { name: "min int64", cborDataShortest: hexDecode("3b7fffffffffffffff"), cborDataBigInt: hexDecode("c3487fffffffffffffff"), value: bigIntOrPanic("-9223372036854775808"), }, { name: "overflow Go int64 fit CBOR neg int", cborDataShortest: hexDecode("3b8000000000000000"), cborDataBigInt: hexDecode("c3488000000000000000"), value: bigIntOrPanic("-9223372036854775809"), }, { name: "min CBOR neg int", cborDataShortest: hexDecode("3bffffffffffffffff"), cborDataBigInt: hexDecode("c348ffffffffffffffff"), value: bigIntOrPanic("-18446744073709551616"), }, { name: "overflow CBOR neg int", cborDataShortest: hexDecode("c349010000000000000000"), cborDataBigInt: hexDecode("c349010000000000000000"), value: bigIntOrPanic("-18446744073709551617"), }, } dmShortest, err := EncOptions{}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } dmBigInt, err := EncOptions{BigIntConvert: BigIntConvertNone}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if b, err := dmShortest.Marshal(tc.value); err != nil { t.Errorf("Marshal(%v) returned error %v", tc.value, err) } else if !bytes.Equal(b, tc.cborDataShortest) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.value, b, tc.cborDataShortest) } if b, err := dmBigInt.Marshal(tc.value); err != nil { t.Errorf("Marshal(%v) returned error %v", tc.value, err) } else if !bytes.Equal(b, tc.cborDataBigInt) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.value, b, tc.cborDataBigInt) } }) } } golang-github-fxamacker-cbor-2.4.0/example_test.go000066400000000000000000000545331416612535600221720ustar00rootroot00000000000000// Copyright (c) Faye Amacker. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. package cbor_test import ( "bytes" "encoding/hex" "fmt" "io" "reflect" "time" "github.com/fxamacker/cbor/v2" // remove "/v2" suffix if you're not using Go modules (see README.md) ) func ExampleMarshal() { type Animal struct { Age int Name string Owners []string Male bool } animal := Animal{Age: 4, Name: "Candy", Owners: []string{"Mary", "Joe"}} b, err := cbor.Marshal(animal) if err != nil { fmt.Println("error:", err) } fmt.Printf("%x\n", b) // Output: // a46341676504644e616d656543616e6479664f776e65727382644d617279634a6f65644d616c65f4 } func ExampleMarshal_time() { tm, _ := time.Parse(time.RFC3339, "2013-03-21T20:04:00Z") // Encode time as string in RFC3339 format with second precision. em, err := cbor.EncOptions{Time: cbor.TimeRFC3339}.EncMode() if err != nil { fmt.Println("error:", err) } b, err := em.Marshal(tm) if err != nil { fmt.Println("error:", err) } fmt.Printf("%x\n", b) // Encode time as numerical representation of seconds since January 1, 1970 UTC. em, err = cbor.EncOptions{Time: cbor.TimeUnix}.EncMode() if err != nil { fmt.Println("error:", err) } b, err = em.Marshal(tm) if err != nil { fmt.Println("error:", err) } fmt.Printf("%x\n", b) // Output: // 74323031332d30332d32315432303a30343a30305a // 1a514b67b0 } // This example uses Marshal to encode struct and map in canonical form. func ExampleMarshal_canonical() { type Animal struct { Age int Name string Contacts map[string]string Male bool } animal := Animal{Age: 4, Name: "Candy", Contacts: map[string]string{"Mary": "111-111-1111", "Joe": "222-222-2222"}} em, err := cbor.CanonicalEncOptions().EncMode() if err != nil { fmt.Println("error:", err) } b, err := em.Marshal(animal) if err != nil { fmt.Println("error:", err) } fmt.Printf("%x\n", b) // Output: // a46341676504644d616c65f4644e616d656543616e647968436f6e7461637473a2634a6f656c3232322d3232322d32323232644d6172796c3131312d3131312d31313131 } // This example uses "toarray" struct tag to encode struct as CBOR array. func ExampleMarshal_toarray() { type Record struct { _ struct{} `cbor:",toarray"` Name string Unit string Measurement int } rec := Record{Name: "current", Unit: "V", Measurement: 1} b, err := cbor.Marshal(rec) if err != nil { fmt.Println("error:", err) } fmt.Printf("%x\n", b) // Output: // 836763757272656e74615601 } // This example uses "keyasint" struct tag to encode struct's fiele names as integer. // This feautre is very useful in handling COSE, CWT, SenML data. func ExampleMarshal_keyasint() { type Record struct { Name string `cbor:"1,keyasint"` Unit string `cbor:"2,keyasint"` Measurement int `cbor:"3,keyasint"` } rec := Record{Name: "current", Unit: "V", Measurement: 1} b, err := cbor.Marshal(rec) if err != nil { fmt.Println("error:", err) } fmt.Printf("%x\n", b) // Output: // a3016763757272656e740261560301 } func ExampleUnmarshal() { type Animal struct { Age int Name string Owners []string Male bool } cborData, _ := hex.DecodeString("a46341676504644e616d656543616e6479664f776e65727382644d617279634a6f65644d616c65f4") var animal Animal err := cbor.Unmarshal(cborData, &animal) if err != nil { fmt.Println("error:", err) } fmt.Printf("%+v", animal) // Output: // {Age:4 Name:Candy Owners:[Mary Joe] Male:false} } func ExampleUnmarshal_time() { cborRFC3339Time, _ := hex.DecodeString("74323031332d30332d32315432303a30343a30305a") tm := time.Time{} if err := cbor.Unmarshal(cborRFC3339Time, &tm); err != nil { fmt.Println("error:", err) } fmt.Printf("%+v\n", tm.UTC().Format(time.RFC3339Nano)) cborUnixTime, _ := hex.DecodeString("1a514b67b0") tm = time.Time{} if err := cbor.Unmarshal(cborUnixTime, &tm); err != nil { fmt.Println("error:", err) } fmt.Printf("%+v\n", tm.UTC().Format(time.RFC3339Nano)) // Output: // 2013-03-21T20:04:00Z // 2013-03-21T20:04:00Z } func ExampleEncoder() { type Animal struct { Age int Name string Owners []string Male bool } animals := []Animal{ {Age: 4, Name: "Candy", Owners: []string{"Mary", "Joe"}, Male: false}, {Age: 6, Name: "Rudy", Owners: []string{"Cindy"}, Male: true}, {Age: 2, Name: "Duke", Owners: []string{"Norton"}, Male: true}, } var buf bytes.Buffer em, err := cbor.CanonicalEncOptions().EncMode() if err != nil { fmt.Println("error:", err) } enc := em.NewEncoder(&buf) for _, animal := range animals { err := enc.Encode(animal) if err != nil { fmt.Println("error:", err) } } fmt.Printf("%x\n", buf.Bytes()) // Output: // a46341676504644d616c65f4644e616d656543616e6479664f776e65727382644d617279634a6f65a46341676506644d616c65f5644e616d656452756479664f776e657273816543696e6479a46341676502644d616c65f5644e616d656444756b65664f776e65727381664e6f72746f6e } // ExampleEncoder_indefiniteLengthByteString encodes a stream of definite // length byte string ("chunks") as an indefinite length byte string. func ExampleEncoder_indefiniteLengthByteString() { var buf bytes.Buffer encoder := cbor.NewEncoder(&buf) // Start indefinite length byte string encoding. if err := encoder.StartIndefiniteByteString(); err != nil { fmt.Println("error:", err) } // Encode definite length byte string. if err := encoder.Encode([]byte{1, 2}); err != nil { fmt.Println("error:", err) } // Encode definite length byte string. if err := encoder.Encode([3]byte{3, 4, 5}); err != nil { fmt.Println("error:", err) } // Close indefinite length byte string. if err := encoder.EndIndefinite(); err != nil { fmt.Println("error:", err) } fmt.Printf("%x\n", buf.Bytes()) // Output: // 5f42010243030405ff } // ExampleEncoder_indefiniteLengthTextString encodes a stream of definite // length text string ("chunks") as an indefinite length text string. func ExampleEncoder_indefiniteLengthTextString() { var buf bytes.Buffer encoder := cbor.NewEncoder(&buf) // Start indefinite length text string encoding. if err := encoder.StartIndefiniteTextString(); err != nil { fmt.Println("error:", err) } // Encode definite length text string. if err := encoder.Encode("strea"); err != nil { fmt.Println("error:", err) } // Encode definite length text string. if err := encoder.Encode("ming"); err != nil { fmt.Println("error:", err) } // Close indefinite length text string. if err := encoder.EndIndefinite(); err != nil { fmt.Println("error:", err) } fmt.Printf("%x\n", buf.Bytes()) // Output: // 7f657374726561646d696e67ff } // ExampleEncoder_indefiniteLengthArray encodes a stream of elements as an // indefinite length array. Encoder supports nested indefinite length values. func ExampleEncoder_indefiniteLengthArray() { var buf bytes.Buffer enc := cbor.NewEncoder(&buf) // Start indefinite length array encoding. if err := enc.StartIndefiniteArray(); err != nil { fmt.Println("error:", err) } // Encode array element. if err := enc.Encode(1); err != nil { fmt.Println("error:", err) } // Encode array element. if err := enc.Encode([]int{2, 3}); err != nil { fmt.Println("error:", err) } // Start a nested indefinite length array as array element. if err := enc.StartIndefiniteArray(); err != nil { fmt.Println("error:", err) } // Encode nested array element. if err := enc.Encode(4); err != nil { fmt.Println("error:", err) } // Encode nested array element. if err := enc.Encode(5); err != nil { fmt.Println("error:", err) } // Close nested indefinite length array. if err := enc.EndIndefinite(); err != nil { fmt.Println("error:", err) } // Close outer indefinite length array. if err := enc.EndIndefinite(); err != nil { fmt.Println("error:", err) } fmt.Printf("%x\n", buf.Bytes()) // Output: // 9f018202039f0405ffff } // ExampleEncoder_indefiniteLengthMap encodes a stream of elements as an // indefinite length map. Encoder supports nested indefinite length values. func ExampleEncoder_indefiniteLengthMap() { var buf bytes.Buffer em, err := cbor.EncOptions{Sort: cbor.SortCanonical}.EncMode() if err != nil { fmt.Println("error:", err) } enc := em.NewEncoder(&buf) // Start indefinite length map encoding. if err := enc.StartIndefiniteMap(); err != nil { fmt.Println("error:", err) } // Encode map key. if err := enc.Encode("a"); err != nil { fmt.Println("error:", err) } // Encode map value. if err := enc.Encode(1); err != nil { fmt.Println("error:", err) } // Encode map key. if err := enc.Encode("b"); err != nil { fmt.Println("error:", err) } // Start an indefinite length array as map value. if err := enc.StartIndefiniteArray(); err != nil { fmt.Println("error:", err) } // Encoded array element. if err := enc.Encode(2); err != nil { fmt.Println("error:", err) } // Encoded array element. if err := enc.Encode(3); err != nil { fmt.Println("error:", err) } // Close indefinite length array. if err := enc.EndIndefinite(); err != nil { fmt.Println("error:", err) } // Close indefinite length map. if err := enc.EndIndefinite(); err != nil { fmt.Println("error:", err) } fmt.Printf("%x\n", buf.Bytes()) // Output: // bf61610161629f0203ffff } func ExampleDecoder() { type Animal struct { Age int Name string Owners []string Male bool } cborData, _ := hex.DecodeString("a46341676504644d616c65f4644e616d656543616e6479664f776e65727382644d617279634a6f65a46341676506644d616c65f5644e616d656452756479664f776e657273816543696e6479a46341676502644d616c65f5644e616d656444756b65664f776e65727381664e6f72746f6e") dec := cbor.NewDecoder(bytes.NewReader(cborData)) for { var animal Animal if err := dec.Decode(&animal); err != nil { if err != io.EOF { fmt.Println("error:", err) } break } fmt.Printf("%+v\n", animal) } // Output: // {Age:4 Name:Candy Owners:[Mary Joe] Male:false} // {Age:6 Name:Rudy Owners:[Cindy] Male:true} // {Age:2 Name:Duke Owners:[Norton] Male:true} } func Example_cWT() { // Use "keyasint" struct tag to encode/decode struct to/from CBOR map. type claims struct { Iss string `cbor:"1,keyasint"` Sub string `cbor:"2,keyasint"` Aud string `cbor:"3,keyasint"` Exp int `cbor:"4,keyasint"` Nbf int `cbor:"5,keyasint"` Iat int `cbor:"6,keyasint"` Cti []byte `cbor:"7,keyasint"` } // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1 cborData, _ := hex.DecodeString("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71") var v claims if err := cbor.Unmarshal(cborData, &v); err != nil { fmt.Println("error:", err) } if _, err := cbor.Marshal(v); err != nil { fmt.Println("error:", err) } fmt.Printf("%+v", v) // Output: // {Iss:coap://as.example.com Sub:erikw Aud:coap://light.example.com Exp:1444064944 Nbf:1443944944 Iat:1443944944 Cti:[11 113]} } func Example_cWTWithDupMapKeyOption() { type claims struct { Iss string `cbor:"1,keyasint"` Sub string `cbor:"2,keyasint"` Aud string `cbor:"3,keyasint"` Exp int `cbor:"4,keyasint"` Nbf int `cbor:"5,keyasint"` Iat int `cbor:"6,keyasint"` Cti []byte `cbor:"7,keyasint"` } // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1 cborData, _ := hex.DecodeString("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71") dm, _ := cbor.DecOptions{DupMapKey: cbor.DupMapKeyEnforcedAPF}.DecMode() var v claims if err := dm.Unmarshal(cborData, &v); err != nil { fmt.Println("error:", err) } fmt.Printf("%+v", v) // Output: // {Iss:coap://as.example.com Sub:erikw Aud:coap://light.example.com Exp:1444064944 Nbf:1443944944 Iat:1443944944 Cti:[11 113]} } func Example_signedCWT() { // Use "keyasint" struct tag to encode/decode struct to/from CBOR map. // Partial COSE header definition type coseHeader struct { Alg int `cbor:"1,keyasint,omitempty"` Kid []byte `cbor:"4,keyasint,omitempty"` IV []byte `cbor:"5,keyasint,omitempty"` } // Use "toarray" struct tag to encode/decode struct to/from CBOR array. type signedCWT struct { _ struct{} `cbor:",toarray"` Protected []byte Unprotected coseHeader Payload []byte Signature []byte } // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.3 cborData, _ := hex.DecodeString("d28443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7158405427c1ff28d23fbad1f29c4c7c6a555e601d6fa29f9179bc3d7438bacaca5acd08c8d4d4f96131680c429a01f85951ecee743a52b9b63632c57209120e1c9e30") var v signedCWT if err := cbor.Unmarshal(cborData, &v); err != nil { fmt.Println("error:", err) } if _, err := cbor.Marshal(v); err != nil { fmt.Println("error:", err) } fmt.Printf("%+v", v) // Output: // {_:{} Protected:[161 1 38] Unprotected:{Alg:0 Kid:[65 115 121 109 109 101 116 114 105 99 69 67 68 83 65 50 53 54] IV:[]} Payload:[167 1 117 99 111 97 112 58 47 47 97 115 46 101 120 97 109 112 108 101 46 99 111 109 2 101 101 114 105 107 119 3 120 24 99 111 97 112 58 47 47 108 105 103 104 116 46 101 120 97 109 112 108 101 46 99 111 109 4 26 86 18 174 176 5 26 86 16 217 240 6 26 86 16 217 240 7 66 11 113] Signature:[84 39 193 255 40 210 63 186 209 242 156 76 124 106 85 94 96 29 111 162 159 145 121 188 61 116 56 186 202 202 90 205 8 200 212 212 249 97 49 104 12 66 154 1 248 89 81 236 238 116 58 82 185 182 54 50 197 114 9 18 14 28 158 48]} } func Example_signedCWTWithTag() { // Use "keyasint" struct tag to encode/decode struct to/from CBOR map. // Partial COSE header definition type coseHeader struct { Alg int `cbor:"1,keyasint,omitempty"` Kid []byte `cbor:"4,keyasint,omitempty"` IV []byte `cbor:"5,keyasint,omitempty"` } // Use "toarray" struct tag to encode/decode struct to/from CBOR array. type signedCWT struct { _ struct{} `cbor:",toarray"` Protected []byte Unprotected coseHeader Payload []byte Signature []byte } // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.3 cborData, _ := hex.DecodeString("d28443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7158405427c1ff28d23fbad1f29c4c7c6a555e601d6fa29f9179bc3d7438bacaca5acd08c8d4d4f96131680c429a01f85951ecee743a52b9b63632c57209120e1c9e30") // Register tag COSE_Sign1 18 with signedCWT type. tags := cbor.NewTagSet() if err := tags.Add( cbor.TagOptions{EncTag: cbor.EncTagRequired, DecTag: cbor.DecTagRequired}, reflect.TypeOf(signedCWT{}), 18); err != nil { fmt.Println("error:", err) } dm, _ := cbor.DecOptions{}.DecModeWithTags(tags) em, _ := cbor.EncOptions{}.EncModeWithTags(tags) var v signedCWT if err := dm.Unmarshal(cborData, &v); err != nil { fmt.Println("error:", err) } if _, err := em.Marshal(v); err != nil { fmt.Println("error:", err) } fmt.Printf("%+v", v) // Output: // {_:{} Protected:[161 1 38] Unprotected:{Alg:0 Kid:[65 115 121 109 109 101 116 114 105 99 69 67 68 83 65 50 53 54] IV:[]} Payload:[167 1 117 99 111 97 112 58 47 47 97 115 46 101 120 97 109 112 108 101 46 99 111 109 2 101 101 114 105 107 119 3 120 24 99 111 97 112 58 47 47 108 105 103 104 116 46 101 120 97 109 112 108 101 46 99 111 109 4 26 86 18 174 176 5 26 86 16 217 240 6 26 86 16 217 240 7 66 11 113] Signature:[84 39 193 255 40 210 63 186 209 242 156 76 124 106 85 94 96 29 111 162 159 145 121 188 61 116 56 186 202 202 90 205 8 200 212 212 249 97 49 104 12 66 154 1 248 89 81 236 238 116 58 82 185 182 54 50 197 114 9 18 14 28 158 48]} } func Example_cOSE() { // Use "keyasint" struct tag to encode/decode struct to/from CBOR map. // Use cbor.RawMessage to delay unmarshaling (CrvOrNOrK's data type depends on Kty's value). type coseKey struct { Kty int `cbor:"1,keyasint,omitempty"` Kid []byte `cbor:"2,keyasint,omitempty"` Alg int `cbor:"3,keyasint,omitempty"` KeyOpts int `cbor:"4,keyasint,omitempty"` IV []byte `cbor:"5,keyasint,omitempty"` CrvOrNOrK cbor.RawMessage `cbor:"-1,keyasint,omitempty"` // K for symmetric keys, Crv for elliptic curve keys, N for RSA modulus XOrE cbor.RawMessage `cbor:"-2,keyasint,omitempty"` // X for curve x-coordinate, E for RSA public exponent Y cbor.RawMessage `cbor:"-3,keyasint,omitempty"` // Y for curve y-cooridate D []byte `cbor:"-4,keyasint,omitempty"` } // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.2 // 128-Bit Symmetric Key cborData, _ := hex.DecodeString("a42050231f4c4d4d3051fdc2ec0a3851d5b3830104024c53796d6d6574726963313238030a") var v coseKey if err := cbor.Unmarshal(cborData, &v); err != nil { fmt.Println("error:", err) } if _, err := cbor.Marshal(v); err != nil { fmt.Println("error:", err) } fmt.Printf("%+v", v) // Output: // {Kty:4 Kid:[83 121 109 109 101 116 114 105 99 49 50 56] Alg:10 KeyOpts:0 IV:[] CrvOrNOrK:[80 35 31 76 77 77 48 81 253 194 236 10 56 81 213 179 131] XOrE:[] Y:[] D:[]} } func Example_senML() { // Use "keyasint" struct tag to encode/decode struct to/from CBOR map. type SenMLRecord struct { BaseName string `cbor:"-2,keyasint,omitempty"` BaseTime float64 `cbor:"-3,keyasint,omitempty"` BaseUnit string `cbor:"-4,keyasint,omitempty"` BaseValue float64 `cbor:"-5,keyasint,omitempty"` BaseSum float64 `cbor:"-6,keyasint,omitempty"` BaseVersion int `cbor:"-1,keyasint,omitempty"` Name string `cbor:"0,keyasint,omitempty"` Unit string `cbor:"1,keyasint,omitempty"` Time float64 `cbor:"6,keyasint,omitempty"` UpdateTime float64 `cbor:"7,keyasint,omitempty"` Value float64 `cbor:"2,keyasint,omitempty"` ValueS string `cbor:"3,keyasint,omitempty"` ValueB bool `cbor:"4,keyasint,omitempty"` ValueD string `cbor:"8,keyasint,omitempty"` Sum float64 `cbor:"5,keyasint,omitempty"` } // Data from https://tools.ietf.org/html/rfc8428#section-6 cborData, _ := hex.DecodeString("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333") var v []*SenMLRecord if err := cbor.Unmarshal(cborData, &v); err != nil { fmt.Println("error:", err) } // Encoder uses ShortestFloat16 option to use float16 as the shortest form that preserves floating-point value. em, err := cbor.EncOptions{ShortestFloat: cbor.ShortestFloat16}.EncMode() if err != nil { fmt.Println("error:", err) } if _, err := em.Marshal(v); err != nil { fmt.Println("error:", err) } for _, rec := range v { fmt.Printf("%+v\n", *rec) } // Output: // {BaseName:urn:dev:ow:10e2073a0108006: BaseTime:1.276020076001e+09 BaseUnit:A BaseValue:0 BaseSum:0 BaseVersion:5 Name:voltage Unit:V Time:0 UpdateTime:0 Value:120.1 ValueS: ValueB:false ValueD: Sum:0} // {BaseName: BaseTime:0 BaseUnit: BaseValue:0 BaseSum:0 BaseVersion:0 Name:current Unit: Time:-5 UpdateTime:0 Value:1.2 ValueS: ValueB:false ValueD: Sum:0} // {BaseName: BaseTime:0 BaseUnit: BaseValue:0 BaseSum:0 BaseVersion:0 Name:current Unit: Time:-4 UpdateTime:0 Value:1.3 ValueS: ValueB:false ValueD: Sum:0} // {BaseName: BaseTime:0 BaseUnit: BaseValue:0 BaseSum:0 BaseVersion:0 Name:current Unit: Time:-3 UpdateTime:0 Value:1.4 ValueS: ValueB:false ValueD: Sum:0} // {BaseName: BaseTime:0 BaseUnit: BaseValue:0 BaseSum:0 BaseVersion:0 Name:current Unit: Time:-2 UpdateTime:0 Value:1.5 ValueS: ValueB:false ValueD: Sum:0} // {BaseName: BaseTime:0 BaseUnit: BaseValue:0 BaseSum:0 BaseVersion:0 Name:current Unit: Time:-1 UpdateTime:0 Value:1.6 ValueS: ValueB:false ValueD: Sum:0} // {BaseName: BaseTime:0 BaseUnit: BaseValue:0 BaseSum:0 BaseVersion:0 Name:current Unit: Time:0 UpdateTime:0 Value:1.7 ValueS: ValueB:false ValueD: Sum:0} } func Example_webAuthn() { // Use cbor.RawMessage to delay unmarshaling (AttStmt's data type depends on Fmt's value). type attestationObject struct { AuthnData []byte `cbor:"authData"` Fmt string `cbor:"fmt"` AttStmt cbor.RawMessage `cbor:"attStmt"` } cborData, _ := hex.DecodeString("a363666d74686669646f2d7532666761747453746d74a26373696758483046022100e7ab373cfbd99fcd55fd59b0f6f17fef5b77a20ddec3db7f7e4d55174e366236022100828336b4822125fb56541fb14a8a273876acd339395ec2dad95cf41c1dd2a9ae637835638159024e3082024a30820132a0030201020204124a72fe300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a302c312a302806035504030c2159756269636f205532462045452053657269616c203234393431343937323135383059301306072a8648ce3d020106082a8648ce3d030107034200043d8b1bbd2fcbf6086e107471601468484153c1c6d3b4b68a5e855e6e40757ee22bcd8988bf3befd7cdf21cb0bf5d7a150d844afe98103c6c6607d9faae287c02a33b3039302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e313013060b2b0601040182e51c020101040403020520300d06092a864886f70d01010b05000382010100a14f1eea0076f6b8476a10a2be72e60d0271bb465b2dfbfc7c1bd12d351989917032631d795d097fa30a26a325634e85721bc2d01a86303f6bc075e5997319e122148b0496eec8d1f4f94cf4110de626c289443d1f0f5bbb239ca13e81d1d5aa9df5af8e36126475bfc23af06283157252762ff68879bcf0ef578d55d67f951b4f32b63c8aea5b0f99c67d7d814a7ff5a6f52df83e894a3a5d9c8b82e7f8bc8daf4c80175ff8972fda79333ec465d806eacc948f1bab22045a95558a48c20226dac003d41fbc9e05ea28a6bb5e10a49de060a0a4f6a2676a34d68c4abe8c61874355b9027e828ca9e064b002d62e8d8cf0744921753d35e3c87c5d5779453e7768617574684461746158c449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976341000000000000000000000000000000000000000000408903fd7dfd2c9770e98cae0123b13a2c27828a106349bc6277140e7290b7e9eb7976aa3c04ed347027caf7da3a2fa76304751c02208acfc4e7fc6c7ebbc375c8a5010203262001215820ad7f7992c335b90d882b2802061b97a4fabca7e2ee3e7a51e728b8055e4eb9c7225820e0966ba7005987fece6f0e0e13447aa98cec248e4000a594b01b74c1cb1d40b3") var v attestationObject if err := cbor.Unmarshal(cborData, &v); err != nil { fmt.Println("error:", err) } if _, err := cbor.Marshal(v); err != nil { fmt.Println("error:", err) } fmt.Printf("%+v", v) } golang-github-fxamacker-cbor-2.4.0/go.mod000066400000000000000000000001251416612535600202430ustar00rootroot00000000000000module github.com/fxamacker/cbor/v2 go 1.12 require github.com/x448/float16 v0.8.4 golang-github-fxamacker-cbor-2.4.0/go.sum000066400000000000000000000002451416612535600202730ustar00rootroot00000000000000github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= golang-github-fxamacker-cbor-2.4.0/stream.go000066400000000000000000000126241416612535600207660ustar00rootroot00000000000000// 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" "io" "reflect" ) // Decoder reads and decodes CBOR values from io.Reader. type Decoder struct { r io.Reader d decoder buf []byte off int // next read offset in buf bytesRead int } // NewDecoder returns a new decoder that reads and decodes from r using // the default decoding options. func NewDecoder(r io.Reader) *Decoder { return defaultDecMode.NewDecoder(r) } // 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 } } dec.d.reset(dec.buf[dec.off:]) err := dec.d.value(v) dec.off += dec.d.off dec.bytesRead += dec.d.off 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 nil } // NumBytesRead returns the number of bytes read. func (dec *Decoder) NumBytesRead() int { return dec.bytesRead } func (dec *Decoder) read() (int, error) { // Grow buf if needed. const minRead = 512 if cap(dec.buf)-len(dec.buf)+dec.off < minRead { oldUnreadBuf := dec.buf[dec.off:] dec.buf = make([]byte, len(dec.buf)-dec.off, 2*cap(dec.buf)+minRead) dec.overwriteBuf(oldUnreadBuf) } // Copy unread data over read data and reset off to 0. if dec.off > 0 { dec.overwriteBuf(dec.buf[dec.off:]) } // Read from reader and reslice buf. n, err := dec.r.Read(dec.buf[len(dec.buf):cap(dec.buf)]) dec.buf = dec.buf[0 : len(dec.buf)+n] return n, err } func (dec *Decoder) overwriteBuf(newBuf []byte) { n := copy(dec.buf, newBuf) dec.buf = dec.buf[:n] dec.off = 0 } // Encoder writes CBOR values to io.Writer. type Encoder struct { w io.Writer em *encMode e *encoderBuffer indefTypes []cborType } // NewEncoder returns a new encoder that writes to w using the default encoding options. func NewEncoder(w io.Writer) *Encoder { return defaultEncMode.NewEncoder(w) } // Encode writes the CBOR encoding of v. func (enc *Encoder) Encode(v interface{}) error { if len(enc.indefTypes) > 0 && v != nil { indefType := enc.indefTypes[len(enc.indefTypes)-1] if indefType == cborTypeTextString { k := reflect.TypeOf(v).Kind() if k != reflect.String { return errors.New("cbor: cannot encode item type " + k.String() + " for indefinite-length text string") } } else if indefType == cborTypeByteString { t := reflect.TypeOf(v) k := t.Kind() if (k != reflect.Array && k != reflect.Slice) || t.Elem().Kind() != reflect.Uint8 { return errors.New("cbor: cannot encode item type " + k.String() + " for indefinite-length byte string") } } } err := encode(enc.e, enc.em, reflect.ValueOf(v)) if err == nil { _, err = enc.e.WriteTo(enc.w) } enc.e.Reset() 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. 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. func (enc *Encoder) StartIndefiniteTextString() error { return enc.startIndefinite(cborTypeTextString) } // StartIndefiniteArray starts array encoding of indefinite length. // Subsequent calls of (*Encoder).Encode() encodes elements of the array // until EndIndefinite is called. func (enc *Encoder) StartIndefiniteArray() error { return enc.startIndefinite(cborTypeArray) } // StartIndefiniteMap starts array encoding of indefinite length. // Subsequent calls of (*Encoder).Encode() encodes elements of the map // until EndIndefinite is called. func (enc *Encoder) StartIndefiniteMap() error { return enc.startIndefinite(cborTypeMap) } // EndIndefinite closes last opened indefinite length value. func (enc *Encoder) EndIndefinite() error { if len(enc.indefTypes) == 0 { return errors.New("cbor: cannot encode \"break\" code outside indefinite length values") } _, err := enc.w.Write([]byte{0xff}) if err == nil { enc.indefTypes = enc.indefTypes[:len(enc.indefTypes)-1] } return err } var cborIndefHeader = map[cborType][]byte{ cborTypeByteString: {0x5f}, cborTypeTextString: {0x7f}, cborTypeArray: {0x9f}, cborTypeMap: {0xbf}, } func (enc *Encoder) startIndefinite(typ cborType) error { if enc.em.indefLength == IndefLengthForbidden { return &IndefiniteLengthError{typ} } _, err := enc.w.Write(cborIndefHeader[typ]) if err == nil { enc.indefTypes = append(enc.indefTypes, typ) } return err } // RawMessage is a raw encoded CBOR value. type RawMessage []byte // MarshalCBOR returns m or CBOR nil if m is nil. func (m RawMessage) MarshalCBOR() ([]byte, error) { if len(m) == 0 { return cborNil, nil } return m, nil } // UnmarshalCBOR creates a copy of data and saves to *m. 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) return nil } golang-github-fxamacker-cbor-2.4.0/stream_test.go000066400000000000000000000317431416612535600220300ustar00rootroot00000000000000// 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" "io" "reflect" "testing" "time" ) func TestDecoder(t *testing.T) { var buf bytes.Buffer for i := 0; i < 5; i++ { for _, tc := range unmarshalTests { buf.Write(tc.cborData) } } decoder := NewDecoder(&buf) 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) } } } // 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 { for j := 0; j < len(tc.wrongTypes)*2; j++ { 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) } 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) } } } } // 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 TestDecoderStructTag(t *testing.T) { type strc struct { A string `json:"x" cbor:"a"` B string `json:"y" cbor:"b"` C string `json:"z"` } want := strc{ A: "A", B: "B", C: "C", } cborData := hexDecode("a36161614161626142617a6143") // {"a":"A", "b":"B", "z":"C"} var v strc dec := NewDecoder(bytes.NewReader(cborData)) if err := dec.Decode(&v); err != nil { t.Errorf("Decode() returned error %v", err) } if !reflect.DeepEqual(v, want) { t.Errorf("Decode() = %+v (%T), want %+v (%T)", v, v, want, want) } } func TestEncoder(t *testing.T) { var want bytes.Buffer var w bytes.Buffer em, err := CanonicalEncOptions().EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } encoder := em.NewEncoder(&w) for _, tc := range marshalTests { for _, value := range tc.values { want.Write(tc.cborData) if err := encoder.Encode(value); err != nil { t.Fatalf("Encode() returned error %v", err) } } } if !bytes.Equal(w.Bytes(), want.Bytes()) { t.Errorf("Encoding mismatch: got %v, want %v", w.Bytes(), want.Bytes()) } } func TestEncoderError(t *testing.T) { testcases := []struct { name string value interface{} wantErrorMsg string }{ {"channel cannot be marshaled", make(chan bool), "cbor: unsupported type: chan bool"}, {"function cannot be marshaled", func(i int) int { return i * i }, "cbor: unsupported type: func(int) int"}, {"complex cannot be marshaled", complex(100, 8), "cbor: unsupported type: complex128"}, } var w bytes.Buffer encoder := NewEncoder(&w) for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { err := encoder.Encode(&tc.value) if err == nil { t.Errorf("Encode(%v) didn't return an error, want error %q", tc.value, tc.wantErrorMsg) } else if _, ok := err.(*UnsupportedTypeError); !ok { t.Errorf("Encode(%v) error type %T, want *UnsupportedTypeError", tc.value, err) } else if err.Error() != tc.wantErrorMsg { t.Errorf("Encode(%v) error %q, want %q", tc.value, err.Error(), tc.wantErrorMsg) } }) } if w.Len() != 0 { t.Errorf("Encoder's writer has %d bytes of data, want empty data", w.Len()) } } func TestIndefiniteByteString(t *testing.T) { want := hexDecode("5f42010243030405ff") var w bytes.Buffer encoder := NewEncoder(&w) if err := encoder.StartIndefiniteByteString(); err != nil { t.Fatalf("StartIndefiniteByteString() returned error %v", err) } if err := encoder.Encode([]byte{1, 2}); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.Encode([3]byte{3, 4, 5}); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.EndIndefinite(); err != nil { t.Fatalf("EndIndefinite() returned error %v", err) } if !bytes.Equal(w.Bytes(), want) { t.Errorf("Encoding mismatch: got %v, want %v", w.Bytes(), want) } } func TestIndefiniteByteStringError(t *testing.T) { var w bytes.Buffer encoder := NewEncoder(&w) if err := encoder.StartIndefiniteByteString(); err != nil { t.Fatalf("StartIndefiniteByteString() returned error %v", err) } if err := encoder.Encode([]int{1, 2}); err == nil { t.Errorf("Encode() didn't return an error") } else if err.Error() != "cbor: cannot encode item type slice for indefinite-length byte string" { t.Errorf("Encode() returned error %q, want %q", err.Error(), "cbor: cannot encode item type slice for indefinite-length byte string") } if err := encoder.Encode("hello"); err == nil { t.Errorf("Encode() didn't return an error") } else if err.Error() != "cbor: cannot encode item type string for indefinite-length byte string" { t.Errorf("Encode() returned error %q, want %q", err.Error(), "cbor: cannot encode item type string for indefinite-length byte string") } } func TestIndefiniteTextString(t *testing.T) { want := hexDecode("7f657374726561646d696e67ff") var w bytes.Buffer encoder := NewEncoder(&w) if err := encoder.StartIndefiniteTextString(); err != nil { t.Fatalf("StartIndefiniteTextString() returned error %v", err) } if err := encoder.Encode("strea"); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.Encode("ming"); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.EndIndefinite(); err != nil { t.Fatalf("EndIndefinite() returned error %v", err) } if !bytes.Equal(w.Bytes(), want) { t.Errorf("Encoding mismatch: got %v, want %v", w.Bytes(), want) } } func TestIndefiniteTextStringError(t *testing.T) { var w bytes.Buffer encoder := NewEncoder(&w) if err := encoder.StartIndefiniteTextString(); err != nil { t.Fatalf("StartIndefiniteTextString() returned error %v", err) } if err := encoder.Encode([]byte{1, 2}); err == nil { t.Errorf("Encode() didn't return an error") } else if err.Error() != "cbor: cannot encode item type slice for indefinite-length text string" { t.Errorf("Encode() returned error %q, want %q", err.Error(), "cbor: cannot encode item type slice for indefinite-length text string") } } func TestIndefiniteArray(t *testing.T) { want := hexDecode("9f018202039f0405ffff") var w bytes.Buffer encoder := NewEncoder(&w) if err := encoder.StartIndefiniteArray(); err != nil { t.Fatalf("StartIndefiniteArray() returned error %v", err) } if err := encoder.Encode(1); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.Encode([]int{2, 3}); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.StartIndefiniteArray(); err != nil { t.Fatalf("StartIndefiniteArray() returned error %v", err) } if err := encoder.Encode(4); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.Encode(5); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.EndIndefinite(); err != nil { t.Fatalf("EndIndefinite() returned error %v", err) } if err := encoder.EndIndefinite(); err != nil { t.Fatalf("EndIndefinite() returned error %v", err) } if !bytes.Equal(w.Bytes(), want) { t.Errorf("Encoding mismatch: got %v, want %v", w.Bytes(), want) } } func TestIndefiniteMap(t *testing.T) { want := hexDecode("bf61610161629f0203ffff") var w bytes.Buffer em, err := EncOptions{Sort: SortCanonical}.EncMode() if err != nil { t.Errorf("EncMode() returned an error %v", err) } encoder := em.NewEncoder(&w) if err := encoder.StartIndefiniteMap(); err != nil { t.Fatalf("StartIndefiniteMap() returned error %v", err) } if err := encoder.Encode("a"); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.Encode(1); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.Encode("b"); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.StartIndefiniteArray(); err != nil { t.Fatalf("StartIndefiniteArray() returned error %v", err) } if err := encoder.Encode(2); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.Encode(3); err != nil { t.Fatalf("Encode() returned error %v", err) } if err := encoder.EndIndefinite(); err != nil { t.Fatalf("EndIndefinite() returned error %v", err) } if err := encoder.EndIndefinite(); err != nil { t.Fatalf("EndIndefinite() returned error %v", err) } if !bytes.Equal(w.Bytes(), want) { t.Errorf("Encoding mismatch: got %v, want %v", w.Bytes(), want) } } func TestIndefiniteLengthError(t *testing.T) { var w bytes.Buffer encoder := NewEncoder(&w) if err := encoder.StartIndefiniteByteString(); err != nil { t.Fatalf("StartIndefiniteByteString() returned error %v", err) } if err := encoder.EndIndefinite(); err != nil { t.Fatalf("EndIndefinite() returned error %v", err) } if err := encoder.EndIndefinite(); err == nil { t.Fatalf("EndIndefinite() didn't return an error") } } func TestEncoderStructTag(t *testing.T) { type strc struct { A string `json:"x" cbor:"a"` B string `json:"y" cbor:"b"` C string `json:"z"` } v := strc{ A: "A", B: "B", C: "C", } want := hexDecode("a36161614161626142617a6143") // {"a":"A", "b":"B", "z":"C"} var w bytes.Buffer encoder := NewEncoder(&w) if err := encoder.Encode(v); err != nil { t.Errorf("Encode(%+v) returned error %v", v, err) } if !bytes.Equal(w.Bytes(), want) { t.Errorf("Encoding mismatch: got %v, want %v", w.Bytes(), want) } } func TestRawMessage(t *testing.T) { type strc struct { A RawMessage `cbor:"a"` B *RawMessage `cbor:"b"` C *RawMessage `cbor:"c"` } cborData := hexDecode("a361610161628202036163f6") // {"a": 1, "b": [2, 3], "c": nil}, r := RawMessage(hexDecode("820203")) want := strc{ A: RawMessage([]byte{0x01}), B: &r, } var v strc if err := Unmarshal(cborData, &v); err != nil { t.Fatalf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(v, want) { t.Errorf("Unmarshal(0x%x) returned v %v, want %v", cborData, v, want) } b, err := Marshal(v) if err != nil { t.Fatalf("Marshal(%+v) returned error %v", v, err) } if !bytes.Equal(b, cborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, cborData) } } func TestNullRawMessage(t *testing.T) { r := RawMessage(nil) wantCborData := []byte{0xf6} b, err := Marshal(r) if err != nil { t.Errorf("Marshal(%+v) returned error %v", r, err) } if !bytes.Equal(b, wantCborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", r, b, wantCborData) } } func TestEmptyRawMessage(t *testing.T) { var r RawMessage wantCborData := []byte{0xf6} b, err := Marshal(r) if err != nil { t.Errorf("Marshal(%+v) returned error %v", r, err) } if !bytes.Equal(b, wantCborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", r, b, wantCborData) } } func TestNilRawMessageUnmarshalCBORError(t *testing.T) { wantErrorMsg := "cbor.RawMessage: UnmarshalCBOR on nil pointer" var r *RawMessage cborData := hexDecode("01") if err := r.UnmarshalCBOR(cborData); err == nil { t.Errorf("UnmarshalCBOR() didn't return error") } else if err.Error() != wantErrorMsg { t.Errorf("UnmarshalCBOR() returned error %q, want %q", err.Error(), wantErrorMsg) } } golang-github-fxamacker-cbor-2.4.0/structfields.go000066400000000000000000000146031416612535600222050ustar00rootroot00000000000000// Copyright (c) Faye Amacker. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. package cbor import ( "reflect" "sort" "strings" ) type field struct { name string nameAsInt int64 // used to decoder to match field name with CBOR int cborName []byte idx []int typ reflect.Type ef encodeFunc ief isEmptyFunc typInfo *typeInfo // used to decoder to reuse type info tagged bool // used to choose dominant field (at the same level tagged fields dominate untagged fields) omitEmpty bool // used to skip empty field keyAsInt bool // used to encode/decode field name as int } type fields []*field // indexFieldSorter sorts fields by field idx at each level, breaking ties with idx depth. type indexFieldSorter struct { fields fields } func (x *indexFieldSorter) Len() int { return len(x.fields) } func (x *indexFieldSorter) Swap(i, j int) { x.fields[i], x.fields[j] = x.fields[j], x.fields[i] } func (x *indexFieldSorter) Less(i, j int) bool { iIdx, jIdx := x.fields[i].idx, x.fields[j].idx for k := 0; k < len(iIdx) && k < len(jIdx); k++ { if iIdx[k] != jIdx[k] { return iIdx[k] < jIdx[k] } } return len(iIdx) <= len(jIdx) } // nameLevelAndTagFieldSorter sorts fields by field name, idx depth, and presence of tag. type nameLevelAndTagFieldSorter struct { fields fields } func (x *nameLevelAndTagFieldSorter) Len() int { return len(x.fields) } func (x *nameLevelAndTagFieldSorter) Swap(i, j int) { x.fields[i], x.fields[j] = x.fields[j], x.fields[i] } func (x *nameLevelAndTagFieldSorter) Less(i, j int) bool { fi, fj := x.fields[i], x.fields[j] if fi.name != fj.name { return fi.name < fj.name } if len(fi.idx) != len(fj.idx) { return len(fi.idx) < len(fj.idx) } if fi.tagged != fj.tagged { return fi.tagged } return i < j // Field i and j have the same name, depth, and tagged status. Nothing else matters. } // getFields returns visible fields of struct type t following visibility rules for JSON encoding. func getFields(t reflect.Type) (flds fields, structOptions string) { // Get special field "_" tag options if f, ok := t.FieldByName("_"); ok { tag := f.Tag.Get("cbor") if tag != "-" { structOptions = tag } } // nTypes contains next level anonymous fields' types and indexes // (there can be multiple fields of the same type at the same level) flds, nTypes := appendFields(t, nil, nil, nil) if len(nTypes) > 0 { var cTypes map[reflect.Type][][]int // current level anonymous fields' types and indexes vTypes := map[reflect.Type]bool{t: true} // visited field types at less nested levels for len(nTypes) > 0 { cTypes, nTypes = nTypes, nil for t, idx := range cTypes { // If there are multiple anonymous fields of the same struct type at the same level, all are ignored. if len(idx) > 1 { continue } // Anonymous field of the same type at deeper nested level is ignored. if vTypes[t] { continue } vTypes[t] = true flds, nTypes = appendFields(t, idx[0], flds, nTypes) } } } sort.Sort(&nameLevelAndTagFieldSorter{flds}) // Keep visible fields. j := 0 // index of next unique field for i := 0; i < len(flds); { name := flds[i].name if i == len(flds)-1 || // last field name != flds[i+1].name || // field i has unique field name len(flds[i].idx) < len(flds[i+1].idx) || // field i is at a less nested level than field i+1 (flds[i].tagged && !flds[i+1].tagged) { // field i is tagged while field i+1 is not flds[j] = flds[i] j++ } // Skip fields with the same field name. for i++; i < len(flds) && name == flds[i].name; i++ { } } if j != len(flds) { flds = flds[:j] } // Sort fields by field index sort.Sort(&indexFieldSorter{flds}) return flds, structOptions } // appendFields appends type t's exportable fields to flds and anonymous struct fields to nTypes . func appendFields(t reflect.Type, idx []int, flds fields, nTypes map[reflect.Type][][]int) (fields, map[reflect.Type][][]int) { for i := 0; i < t.NumField(); i++ { f := t.Field(i) ft := f.Type for ft.Kind() == reflect.Ptr { ft = ft.Elem() } if !isFieldExportable(f, ft.Kind()) { continue } tag := f.Tag.Get("cbor") if tag == "" { tag = f.Tag.Get("json") } if tag == "-" { continue } tagged := len(tag) > 0 // Parse field tag options var tagFieldName string var omitempty, keyasint bool for j := 0; len(tag) > 0; j++ { var token string idx := strings.IndexByte(tag, ',') if idx == -1 { token, tag = tag, "" } else { token, tag = tag[:idx], tag[idx+1:] } if j == 0 { tagFieldName = token } else { switch token { case "omitempty": omitempty = true case "keyasint": keyasint = true } } } fieldName := tagFieldName if tagFieldName == "" { fieldName = f.Name } fIdx := make([]int, len(idx)+1) copy(fIdx, idx) fIdx[len(fIdx)-1] = i if !f.Anonymous || ft.Kind() != reflect.Struct || len(tagFieldName) > 0 { flds = append(flds, &field{ name: fieldName, idx: fIdx, typ: f.Type, omitEmpty: omitempty, keyAsInt: keyasint, tagged: tagged}) } else { if nTypes == nil { nTypes = make(map[reflect.Type][][]int) } nTypes[ft] = append(nTypes[ft], fIdx) } } return flds, nTypes } // isFieldExportable returns true if f is an exportable (regular or anonymous) field or // a nonexportable anonymous field of struct type. // Nonexportable anonymous field of struct type can contain exportable fields. func isFieldExportable(f reflect.StructField, fk reflect.Kind) bool { exportable := f.PkgPath == "" return exportable || (f.Anonymous && fk == reflect.Struct) } type embeddedFieldNullPtrFunc func(reflect.Value) (reflect.Value, error) // getFieldValue returns field value of struct v by index. When encountering null pointer // to anonymous (embedded) struct field, f is called with the last traversed field value. func getFieldValue(v reflect.Value, idx []int, f embeddedFieldNullPtrFunc) (fv reflect.Value, err error) { fv = v for i, n := range idx { fv = fv.Field(n) if i < len(idx)-1 { if fv.Kind() == reflect.Ptr && fv.Type().Elem().Kind() == reflect.Struct { if fv.IsNil() { // Null pointer to embedded struct field fv, err = f(fv) if err != nil || !fv.IsValid() { return fv, err } } fv = fv.Elem() } } } return fv, nil } golang-github-fxamacker-cbor-2.4.0/tag.go000066400000000000000000000164731416612535600202540ustar00rootroot00000000000000package cbor import ( "errors" "fmt" "reflect" "sync" ) // Tag represents CBOR tag data, including tag number and unmarshaled tag content. type Tag struct { Number uint64 Content interface{} } // RawTag represents CBOR tag data, including tag number and raw tag content. // RawTag implements Unmarshaler and Marshaler interfaces. type RawTag struct { Number uint64 Content RawMessage } // UnmarshalCBOR sets *t with tag number and raw tag content copied from data. func (t *RawTag) UnmarshalCBOR(data []byte) error { if t == nil { return errors.New("cbor.RawTag: UnmarshalCBOR on nil pointer") } // Decoding CBOR null and undefined to cbor.RawTag is no-op. if len(data) == 1 && (data[0] == 0xf6 || data[0] == 0xf7) { return nil } d := decoder{data: data, dm: defaultDecMode} // Unmarshal tag number. typ, _, num := d.getHead() if typ != cborTypeTag { return &UnmarshalTypeError{CBORType: typ.String(), GoType: typeRawTag.String()} } t.Number = num // Unmarshal tag content. c := d.data[d.off:] t.Content = make([]byte, len(c)) copy(t.Content, c) return nil } // MarshalCBOR returns CBOR encoding of t. func (t RawTag) MarshalCBOR() ([]byte, error) { if t.Number == 0 && len(t.Content) == 0 { // Marshal uninitialized cbor.RawTag b := make([]byte, len(cborNil)) copy(b, cborNil) return b, nil } e := getEncoderBuffer() encodeHead(e, byte(cborTypeTag), t.Number) content := t.Content if len(content) == 0 { content = cborNil } buf := make([]byte, len(e.Bytes())+len(content)) n := copy(buf, e.Bytes()) copy(buf[n:], content) putEncoderBuffer(e) return buf, nil } // DecTagMode specifies how decoder handles tag number. type DecTagMode int const ( // DecTagIgnored makes decoder ignore tag number (skips if present). DecTagIgnored DecTagMode = iota // DecTagOptional makes decoder verify tag number if it's present. DecTagOptional // DecTagRequired makes decoder verify tag number and tag number must be present. DecTagRequired maxDecTagMode ) func (dtm DecTagMode) valid() bool { return dtm < maxDecTagMode } // EncTagMode specifies how encoder handles tag number. type EncTagMode int const ( // EncTagNone makes encoder not encode tag number. EncTagNone EncTagMode = iota // EncTagRequired makes encoder encode tag number. EncTagRequired maxEncTagMode ) func (etm EncTagMode) valid() bool { return etm < maxEncTagMode } // TagOptions specifies how encoder and decoder handle tag number. type TagOptions struct { DecTag DecTagMode EncTag EncTagMode } // TagSet is an interface to add and remove tag info. It is used by EncMode and DecMode // to provide CBOR tag support. 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) tagProvider } type tagProvider interface { getTagItemFromType(t reflect.Type) *tagItem getTypeFromTagNum(num []uint64) reflect.Type } type tagItem struct { num []uint64 cborTagNum []byte contentType reflect.Type opts TagOptions } func (t *tagItem) equalTagNum(num []uint64) bool { // Fast path to compare 1 tag number if len(t.num) == 1 && len(num) == 1 && t.num[0] == num[0] { return true } if len(t.num) != len(num) { return false } for i := 0; i < len(t.num); i++ { if t.num[i] != num[i] { return false } } return true } type ( tagSet map[reflect.Type]*tagItem syncTagSet struct { sync.RWMutex t tagSet } ) func (t tagSet) getTagItemFromType(typ reflect.Type) *tagItem { return t[typ] } func (t tagSet) getTypeFromTagNum(num []uint64) reflect.Type { for typ, tag := range t { if tag.equalTagNum(num) { return typ } } return nil } // NewTagSet returns TagSet (safe for concurrency). func NewTagSet() TagSet { return &syncTagSet{t: make(map[reflect.Type]*tagItem)} } // Add adds given tag number(s), content type, and tag options to TagSet. func (t *syncTagSet) Add(opts TagOptions, contentType reflect.Type, num uint64, nestedNum ...uint64) error { if contentType == nil { return errors.New("cbor: cannot add nil content type to TagSet") } for contentType.Kind() == reflect.Ptr { contentType = contentType.Elem() } tag, err := newTagItem(opts, contentType, num, nestedNum...) if err != nil { return err } t.Lock() defer t.Unlock() for typ, ti := range t.t { if typ == contentType { return errors.New("cbor: content type " + contentType.String() + " already exists in TagSet") } if ti.equalTagNum(tag.num) { return fmt.Errorf("cbor: tag number %v already exists in TagSet", tag.num) } } t.t[contentType] = tag return nil } // Remove removes given tag content type from TagSet. func (t *syncTagSet) Remove(contentType reflect.Type) { for contentType.Kind() == reflect.Ptr { contentType = contentType.Elem() } t.Lock() delete(t.t, contentType) t.Unlock() } func (t *syncTagSet) getTagItemFromType(typ reflect.Type) *tagItem { t.RLock() ti := t.t[typ] t.RUnlock() return ti } func (t *syncTagSet) getTypeFromTagNum(num []uint64) reflect.Type { t.RLock() rt := t.t.getTypeFromTagNum(num) t.RUnlock() return rt } func newTagItem(opts TagOptions, contentType reflect.Type, num uint64, nestedNum ...uint64) (*tagItem, error) { if opts.DecTag == DecTagIgnored && opts.EncTag == EncTagNone { return nil, errors.New("cbor: cannot add tag with DecTagIgnored and EncTagNone options to TagSet") } if contentType.PkgPath() == "" || contentType.Kind() == reflect.Interface { return nil, errors.New("cbor: can only add named types to TagSet, got " + contentType.String()) } if contentType == typeTime { return nil, errors.New("cbor: cannot add time.Time to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead") } if contentType == typeBigInt { return nil, errors.New("cbor: cannot add big.Int to TagSet, it's built-in and supported automatically") } if contentType == typeTag { return nil, errors.New("cbor: cannot add cbor.Tag to TagSet") } if contentType == typeRawTag { return nil, errors.New("cbor: cannot add cbor.RawTag to TagSet") } if num == 0 || num == 1 { return nil, errors.New("cbor: cannot add tag number 0 or 1 to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead") } if num == 2 || num == 3 { return nil, errors.New("cbor: cannot add tag number 2 or 3 to TagSet, it's built-in and supported automatically") } if num == selfDescribedCBORTagNum { return nil, errors.New("cbor: cannot add tag number 55799 to TagSet, it's built-in and ignored automatically") } te := tagItem{num: []uint64{num}, opts: opts, contentType: contentType} te.num = append(te.num, nestedNum...) // Cache encoded tag numbers e := getEncoderBuffer() for _, n := range te.num { encodeHead(e, byte(cborTypeTag), n) } te.cborTagNum = make([]byte, e.Len()) copy(te.cborTagNum, e.Bytes()) putEncoderBuffer(e) return &te, nil } var ( typeTag = reflect.TypeOf(Tag{}) typeRawTag = reflect.TypeOf(RawTag{}) ) // WrongTagError describes mismatch between CBOR tag and registered tag. type WrongTagError struct { RegisteredType reflect.Type RegisteredTagNum []uint64 TagNum []uint64 } func (e *WrongTagError) Error() string { return fmt.Sprintf("cbor: wrong tag number for %s, got %v, expected %v", e.RegisteredType.String(), e.TagNum, e.RegisteredTagNum) } golang-github-fxamacker-cbor-2.4.0/tag_test.go000066400000000000000000001321571416612535600213110ustar00rootroot00000000000000package cbor import ( "bytes" "fmt" "io" "math/big" "reflect" "strings" "testing" "time" ) func TestTagNewTypeWithBuiltinUnderlyingType(t *testing.T) { type myBool bool type myUint uint type myUint8 uint8 type myUint16 uint16 type myUint32 uint32 type myUint64 uint64 type myInt int type myInt8 int8 type myInt16 int16 type myInt32 int32 type myInt64 int64 type myFloat32 float32 type myFloat64 float64 type myString string type myByteSlice []byte type myIntSlice []int type myIntArray [4]int type myMapIntInt map[int]int types := []reflect.Type{ reflect.TypeOf(myBool(false)), reflect.TypeOf(myUint(0)), reflect.TypeOf(myUint8(0)), reflect.TypeOf(myUint16(0)), reflect.TypeOf(myUint32(0)), reflect.TypeOf(myUint64(0)), reflect.TypeOf(myInt(0)), reflect.TypeOf(myInt8(0)), reflect.TypeOf(myInt16(0)), reflect.TypeOf(myInt32(0)), reflect.TypeOf(myInt64(0)), reflect.TypeOf(myFloat32(0)), reflect.TypeOf(myFloat64(0)), reflect.TypeOf(myString("")), reflect.TypeOf(myByteSlice([]byte{})), reflect.TypeOf(myIntSlice([]int{})), reflect.TypeOf(myIntArray([4]int{})), reflect.TypeOf(myMapIntInt(map[int]int{})), } tags := NewTagSet() for i, typ := range types { tagNum := uint64(100 + i) if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, typ, tagNum); err != nil { t.Fatalf("TagSet.Add(%s, %d) returned error %v", typ, tagNum, err) } } em, _ := EncOptions{Sort: SortCanonical}.EncModeWithTags(tags) dm, _ := DecOptions{}.DecModeWithTags(tags) testCases := []roundTripTest{ { name: "bool", obj: myBool(true), wantCborData: hexDecode("d864f5"), }, { name: "uint", obj: myUint(0), wantCborData: hexDecode("d86500"), }, { name: "uint8", obj: myUint8(0), wantCborData: hexDecode("d86600"), }, { name: "uint16", obj: myUint16(1000), wantCborData: hexDecode("d8671903e8"), }, { name: "uint32", obj: myUint32(1000000), wantCborData: hexDecode("d8681a000f4240"), }, { name: "uint64", obj: myUint64(1000000000000), wantCborData: hexDecode("d8691b000000e8d4a51000"), }, { name: "int", obj: myInt(-1), wantCborData: hexDecode("d86a20"), }, { name: "int8", obj: myInt8(-1), wantCborData: hexDecode("d86b20"), }, { name: "int16", obj: myInt16(-1000), wantCborData: hexDecode("d86c3903e7"), }, { name: "int32", obj: myInt32(-1000), wantCborData: hexDecode("d86d3903e7"), }, { name: "int64", obj: myInt64(-1000), wantCborData: hexDecode("d86e3903e7"), }, { name: "float32", obj: myFloat32(100000.0), wantCborData: hexDecode("d86ffa47c35000"), }, { name: "float64", obj: myFloat64(1.1), wantCborData: hexDecode("d870fb3ff199999999999a"), }, { name: "string", obj: myString("a"), wantCborData: hexDecode("d8716161"), }, { name: "[]byte", obj: myByteSlice([]byte{1, 2, 3, 4}), wantCborData: hexDecode("d8724401020304"), }, { name: "[]int", obj: myIntSlice([]int{1, 2, 3, 4}), wantCborData: hexDecode("d8738401020304"), }, { name: "[4]int", obj: myIntArray([...]int{1, 2, 3, 4}), wantCborData: hexDecode("d8748401020304"), }, { name: "map[int]int", obj: myMapIntInt(map[int]int{1: 2, 3: 4}), wantCborData: hexDecode("d875a201020304"), }, } testRoundTrip(t, testCases, em, dm) } func TestTagBinaryMarshalerUnmarshaler(t *testing.T) { t1 := reflect.TypeOf((*number)(nil)) // Use *number for testing purpose t2 := reflect.TypeOf(stru{}) tags := NewTagSet() if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, t1, 123); err != nil { t.Fatalf("TagSet.Add(%s, %d) returned error %v", t1, 123, err) } if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, t2, 124); err != nil { t.Fatalf("TagSet.Add(%s, %d) returned error %v", t2, 124, err) } em, _ := EncOptions{}.EncModeWithTags(tags) dm, _ := DecOptions{}.DecModeWithTags(tags) testCases := []roundTripTest{ { name: "primitive obj", obj: number(1234567890), wantCborData: hexDecode("d87b4800000000499602d2"), }, { name: "struct obj", obj: stru{a: "a", b: "b", c: "c"}, wantCborData: hexDecode("d87c45612C622C63"), }, } testRoundTrip(t, testCases, em, dm) } func TestTagStruct(t *testing.T) { type T struct { S string `cbor:"s,omitempty"` } t1 := reflect.TypeOf(T{}) tags := NewTagSet() if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, t1, 100); err != nil { t.Fatalf("TagSet.Add(%s, %d) returned error %v", t1, 100, err) } em, _ := EncOptions{}.EncModeWithTags(tags) dm, _ := DecOptions{}.DecModeWithTags(tags) cborData := hexDecode("d864a0") // {} var v T if err := dm.Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal() returned error %v", err) } b, err := em.Marshal(v) if err != nil { t.Errorf("Marshal(%+v) returned error %v", v, err) } if !bytes.Equal(b, cborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, cborData) } } func TestTagFixedLengthStruct(t *testing.T) { type T struct { S string `cbor:"s"` } t1 := reflect.TypeOf(T{}) tags := NewTagSet() if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, t1, 100); err != nil { t.Fatalf("TagSet.Add(%s, %d) returned error %v", t1, 100, err) } em, _ := EncOptions{}.EncModeWithTags(tags) dm, _ := DecOptions{}.DecModeWithTags(tags) cborData := hexDecode("d864a1617360") // {"s":""} var v T if err := dm.Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal() returned error %v", err) } b, err := em.Marshal(v) if err != nil { t.Errorf("Marshal(%+v) returned error %v", v, err) } if !bytes.Equal(b, cborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, cborData) } } func TestTagToArrayStruct(t *testing.T) { type coseHeader struct { Alg int `cbor:"1,keyasint,omitempty"` Kid []byte `cbor:"4,keyasint,omitempty"` IV []byte `cbor:"5,keyasint,omitempty"` } type signedCWT struct { _ struct{} `cbor:",toarray"` Protected []byte Unprotected coseHeader Payload []byte Signature []byte } t1 := reflect.TypeOf(signedCWT{}) tags := NewTagSet() if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, t1, 18); err != nil { t.Fatalf("TagSet.Add(%s, %d) returned error %v", t1, 18, err) } em, _ := EncOptions{}.EncModeWithTags(tags) dm, _ := DecOptions{}.DecModeWithTags(tags) // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.3 cborData := hexDecode("d28443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7158405427c1ff28d23fbad1f29c4c7c6a555e601d6fa29f9179bc3d7438bacaca5acd08c8d4d4f96131680c429a01f85951ecee743a52b9b63632c57209120e1c9e30") var v signedCWT if err := dm.Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal() returned error %v", err) } b, err := em.Marshal(v) if err != nil { t.Errorf("Marshal(%+v) returned error %v", v, err) } if !bytes.Equal(b, cborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, cborData) } } func TestNestedTagStruct(t *testing.T) { type coseHeader struct { Alg int `cbor:"1,keyasint,omitempty"` Kid []byte `cbor:"4,keyasint,omitempty"` IV []byte `cbor:"5,keyasint,omitempty"` } type macedCOSE struct { _ struct{} `cbor:",toarray"` Protected []byte Unprotected coseHeader Payload []byte Tag []byte } t1 := reflect.TypeOf(macedCOSE{}) // Register tag CBOR Web Token (CWT) 61 and COSE_Mac0 17 with macedCOSE type tags := NewTagSet() if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, t1, 61, 17); err != nil { t.Fatalf("TagSet.Add(%s, %d, %v) returned error %v", t1, 61, 17, err) } em, _ := EncOptions{}.EncModeWithTags(tags) dm, _ := DecOptions{}.DecModeWithTags(tags) // Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4 cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200") var v macedCOSE if err := dm.Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal() returned error %v", err) } b, err := em.Marshal(v) if err != nil { t.Errorf("Marshal(%+v) returned error %v", v, err) } if !bytes.Equal(b, cborData) { t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, cborData) } } func TestAddTagError(t *testing.T) { type myInt int testCases := []struct { name string typ reflect.Type num uint64 opts TagOptions wantErrorMsg string }{ { name: "nil type", typ: nil, num: 100, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: cannot add nil content type to TagSet", }, { name: "DecTag is DecTagIgnored && EncTag is EncTagNone", typ: reflect.TypeOf(myInt(0)), num: 100, opts: TagOptions{DecTag: DecTagIgnored, EncTag: EncTagNone}, wantErrorMsg: "cbor: cannot add tag with DecTagIgnored and EncTagNone options to TagSet", }, { name: "time.Time", typ: reflect.TypeOf(time.Time{}), num: 101, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: cannot add time.Time to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead", }, { name: "builtin type string", typ: reflect.TypeOf(""), num: 102, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: can only add named types to TagSet, got string", }, { name: "unnamed type struct{}", typ: reflect.TypeOf(struct{}{}), num: 103, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: can only add named types to TagSet, got struct {}", }, { name: "interface", typ: reflect.TypeOf((*io.Reader)(nil)).Elem(), num: 104, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: can only add named types to TagSet, got io.Reader", }, { name: "cbor.Tag", typ: reflect.TypeOf(Tag{}), num: 105, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: cannot add cbor.Tag to TagSet", }, { name: "cbor.RawTag", typ: reflect.TypeOf(RawTag{}), num: 106, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: cannot add cbor.RawTag to TagSet", }, { name: "big.Int", typ: reflect.TypeOf(big.Int{}), num: 107, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: cannot add big.Int to TagSet, it's built-in and supported automatically", }, /* { name: "cbor.Unmarshaler", typ: reflect.TypeOf(number2(0)), num: 107, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagNone}, wantErrorMsg: "cbor: cannot add cbor.Unmarshaler to TagSet with DecTag != DecTagIgnored", }, { name: "cbor.Marshaler", typ: reflect.TypeOf(number2(0)), num: 108, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: cannot add cbor.Marshaler to TagSet with EncTag != EncTagNone", }, */ { name: "tag number 0", typ: reflect.TypeOf(myInt(0)), num: 0, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: cannot add tag number 0 or 1 to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead", }, { name: "tag number 1", typ: reflect.TypeOf(myInt(0)), num: 1, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: cannot add tag number 0 or 1 to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead", }, { name: "tag number 2", typ: reflect.TypeOf(myInt(0)), num: 2, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: cannot add tag number 2 or 3 to TagSet, it's built-in and supported automatically", }, { name: "tag number 3", typ: reflect.TypeOf(myInt(0)), num: 3, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: cannot add tag number 2 or 3 to TagSet, it's built-in and supported automatically", }, { name: "tag number 55799", typ: reflect.TypeOf(myInt(0)), num: 55799, opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, wantErrorMsg: "cbor: cannot add tag number 55799 to TagSet, it's built-in and ignored automatically", }, } tags := NewTagSet() for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if err := tags.Add(tc.opts, tc.typ, tc.num); err == nil { t.Errorf("TagSet.Add(%s, %d) didn't return an error", tc.typ.String(), tc.num) } else if err.Error() != tc.wantErrorMsg { var typeString string if tc.typ == nil { typeString = "nil" } else { typeString = tc.typ.String() } t.Errorf("TagSet.Add(%s, %d) returned error msg %q, want %q", typeString, tc.num, err, tc.wantErrorMsg) } }) } } func TestAddDuplicateTagContentTypeError(t *testing.T) { type myInt int myIntType := reflect.TypeOf(myInt(0)) wantErrorMsg := "cbor: content type cbor.myInt already exists in TagSet" tags := NewTagSet() // Add myIntType and 100 to tags if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, myIntType, 100); err != nil { t.Errorf("TagSet.Add(%s, %d) returned error %v", myIntType.String(), 100, err) } // Add myIntType and 101 to tags if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, myIntType, 101); err == nil { t.Errorf("TagSet.Add(%s, %d) didn't return an error", myIntType.String(), 101) } else if err.Error() != wantErrorMsg { t.Errorf("TagSet.Add(%s, %d) returned error msg %q, want %q", myIntType, 101, err, wantErrorMsg) } } func TestAddDuplicateTagNumError(t *testing.T) { type myBool bool type myInt int myBoolType := reflect.TypeOf(myBool(false)) myIntType := reflect.TypeOf(myInt(0)) wantErrorMsg := "cbor: tag number [100] already exists in TagSet" tags := NewTagSet() // Add myIntType and 100 to tags if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, myIntType, 100); err != nil { t.Errorf("TagSet.Add(%s, %d) returned error %v", myIntType.String(), 100, err) } // Add myBoolType and 100 to tags if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, myBoolType, 100); err == nil { t.Errorf("TagSet.Add(%s, %d) didn't return an error", myBoolType.String(), 100) } else if err.Error() != wantErrorMsg { t.Errorf("TagSet.Add(%s, %d) returned error msg %q, want %q", myBoolType, 100, err, wantErrorMsg) } } func TestAddDuplicateTagNumsError(t *testing.T) { type myBool bool type myInt int myBoolType := reflect.TypeOf(myBool(false)) myIntType := reflect.TypeOf(myInt(0)) wantErrorMsg := "cbor: tag number [100 101] already exists in TagSet" tags := NewTagSet() // Add myIntType and [100, 101] to tags if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, myIntType, 100, 101); err != nil { t.Errorf("TagSet.Add(%s, %d, %d) returned error %v", myIntType.String(), 100, 101, err) } // Add myBoolType and [100, 101] to tags if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, myBoolType, 100, 101); err == nil { t.Errorf("TagSet.Add(%s, %d, %d) didn't return an error", myBoolType.String(), 100, 101) } else if err.Error() != wantErrorMsg { t.Errorf("TagSet.Add(%s, %d, %d) returned error msg %q, want %q", myBoolType, 100, 101, err, wantErrorMsg) } } func TestAddRemoveTag(t *testing.T) { type myInt int type myFloat float64 myIntType := reflect.TypeOf(myInt(0)) myFloatType := reflect.TypeOf(myFloat(0.0)) pMyIntType := reflect.TypeOf((*myInt)(nil)) pMyFloatType := reflect.TypeOf((*myFloat)(nil)) tags := NewTagSet() stags := tags.(*syncTagSet) if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, myIntType, 100); err != nil { t.Errorf("TagSet.Add(%s, %d) returned error %v", myIntType.String(), 100, err) } if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, myFloatType, 101); err != nil { t.Errorf("TagSet.Add(%s, %d) returned error %v", myFloatType.String(), 101, err) } if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, pMyIntType, 102); err == nil { t.Errorf("TagSet.Add(%s, %d) didn't return an error", pMyIntType.String(), 102) } if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, pMyFloatType, 103); err == nil { t.Errorf("TagSet.Add(%s, %d) didn't return an error", pMyFloatType.String(), 103) } if len(stags.t) != 2 { t.Errorf("TagSet len is %d, want %d", len(stags.t), 2) } tags.Remove(pMyIntType) if len(stags.t) != 1 { t.Errorf("TagSet len is %d, want %d", len(stags.t), 1) } tags.Remove(pMyFloatType) if len(stags.t) != 0 { t.Errorf("TagSet len is %d, want %d", len(stags.t), 0) } tags.Remove(myIntType) tags.Remove(myFloatType) } func TestAddTagTypeAliasError(t *testing.T) { type myBool = bool type myUint = uint type myUint8 = uint8 type myUint16 = uint16 type myUint32 = uint32 type myUint64 = uint64 type myInt = int type myInt8 = int8 type myInt16 = int16 type myInt32 = int32 type myInt64 = int64 type myFloat32 = float32 type myFloat64 = float64 type myString = string type myByteSlice = []byte type myIntSlice = []int type myIntArray = [4]int type myMapIntInt = map[int]int testCases := []struct { name string typ reflect.Type wantErrorMsg string }{ { name: "bool", typ: reflect.TypeOf(myBool(false)), wantErrorMsg: "cbor: can only add named types to TagSet, got bool", }, { name: "uint", typ: reflect.TypeOf(myUint(0)), wantErrorMsg: "cbor: can only add named types to TagSet, got uint", }, { name: "uint8", typ: reflect.TypeOf(myUint8(0)), wantErrorMsg: "cbor: can only add named types to TagSet, got uint8", }, { name: "uint16", typ: reflect.TypeOf(myUint16(0)), wantErrorMsg: "cbor: can only add named types to TagSet, got uint16", }, { name: "uint32", typ: reflect.TypeOf(myUint32(0)), wantErrorMsg: "cbor: can only add named types to TagSet, got uint32", }, { name: "uint64", typ: reflect.TypeOf(myUint64(0)), wantErrorMsg: "cbor: can only add named types to TagSet, got uint64", }, { name: "int", typ: reflect.TypeOf(myInt(0)), wantErrorMsg: "cbor: can only add named types to TagSet, got int", }, { name: "int8", typ: reflect.TypeOf(myInt8(0)), wantErrorMsg: "cbor: can only add named types to TagSet, got int8", }, { name: "int16", typ: reflect.TypeOf(myInt16(0)), wantErrorMsg: "cbor: can only add named types to TagSet, got int16", }, { name: "int32", typ: reflect.TypeOf(myInt32(0)), wantErrorMsg: "cbor: can only add named types to TagSet, got int32", }, { name: "int64", typ: reflect.TypeOf(myInt64(0)), wantErrorMsg: "cbor: can only add named types to TagSet, got int64", }, { name: "float32", typ: reflect.TypeOf(myFloat32(0.0)), wantErrorMsg: "cbor: can only add named types to TagSet, got float32", }, { name: "float64", typ: reflect.TypeOf(myFloat64(0.0)), wantErrorMsg: "cbor: can only add named types to TagSet, got float64", }, { name: "string", typ: reflect.TypeOf(myString("")), wantErrorMsg: "cbor: can only add named types to TagSet, got string", }, { name: "[]byte", typ: reflect.TypeOf(myByteSlice([]byte{})), //nolint:unconvert wantErrorMsg: "cbor: can only add named types to TagSet, got []uint8", }, { name: "[]int", typ: reflect.TypeOf(myIntSlice([]int{})), //nolint:unconvert wantErrorMsg: "cbor: can only add named types to TagSet, got []int", }, { name: "[4]int", typ: reflect.TypeOf(myIntArray([4]int{})), //nolint:unconvert wantErrorMsg: "cbor: can only add named types to TagSet, got [4]int", }, { name: "map[int]int", typ: reflect.TypeOf(myMapIntInt(map[int]int{})), //nolint:unconvert wantErrorMsg: "cbor: can only add named types to TagSet, got map[int]int", }, } tags := NewTagSet() for i, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, tc.typ, uint64(100+i)); err == nil { t.Errorf("TagSet.Add(%s, %d) didn't return an error", tc.typ.String(), 0) } else if err.Error() != tc.wantErrorMsg { t.Errorf("TagSet.Add(%s, %d) returned error msg %q, want %q", tc.typ.String(), 0, err, tc.wantErrorMsg) } }) } } // TestDecodeTag decodes tag data with DecTagRequired/EncTagOptional/EncTagNone options. func TestDecodeTagData(t *testing.T) { type myInt int type s struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` } type tagInfo struct { t reflect.Type n []uint64 } tagInfos := []tagInfo{ {reflect.TypeOf((*number)(nil)), []uint64{123}}, // BinaryMarshaler *number {reflect.TypeOf(stru{}), []uint64{124}}, // BinaryMarshaler stru {reflect.TypeOf(myInt(0)), []uint64{125}}, // non-struct type {reflect.TypeOf(s{}), []uint64{126}}, // struct type } tagsDecRequired := NewTagSet() tagsDecOptional := NewTagSet() tagsDecIgnored := NewTagSet() for _, tag := range tagInfos { if err := tagsDecRequired.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, tag.t, tag.n[0], tag.n[1:]...); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err) } if err := tagsDecOptional.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagOptional}, tag.t, tag.n[0], tag.n[1:]...); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err) } if err := tagsDecIgnored.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagIgnored}, tag.t, tag.n[0], tag.n[1:]...); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err) } } type tag struct { name string tagSet TagSet } tags := []tag{ {"EncTagRequired_DecTagRequired", tagsDecRequired}, {"EncTagRequired_DecTagOptional", tagsDecOptional}, {"EncTagRequired_DecTagIgnored", tagsDecIgnored}, } testCases := []roundTripTest{ { name: "BinaryMarshaler non-struct", obj: number(1234567890), wantCborData: hexDecode("d87b4800000000499602d2"), }, { name: "BinaryMarshaler struct", obj: stru{a: "a", b: "b", c: "c"}, wantCborData: hexDecode("d87c45612C622C63"), }, { name: "non-struct", obj: myInt(1), wantCborData: hexDecode("d87d01"), }, { name: "struct", obj: s{A: "A", B: "B", C: "C"}, wantCborData: hexDecode("d87ea3616161416162614261636143"), // {"a":"A", "b":"B", "c":"C"} }, } for _, tag := range tags { t.Run(tag.name, func(t *testing.T) { em, _ := EncOptions{}.EncModeWithTags(tag.tagSet) dm, _ := DecOptions{}.DecModeWithTags(tag.tagSet) testRoundTrip(t, testCases, em, dm) }) } } // TestDecodeNoTag decodes no-tag data with DecTagRequired/EncTagOptional/EncTagNone options func TestDecodeNoTagData(t *testing.T) { type myInt int type s struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` } type tagInfo struct { t reflect.Type n []uint64 } tagInfos := []tagInfo{ {reflect.TypeOf((*number)(nil)), []uint64{123}}, // BinaryMarshaler *number {reflect.TypeOf(stru{}), []uint64{124}}, // BinaryMarshaler stru {reflect.TypeOf(myInt(0)), []uint64{125}}, // non-struct type {reflect.TypeOf(s{}), []uint64{126}}, // struct type } tagsDecRequired := NewTagSet() tagsDecOptional := NewTagSet() for _, tag := range tagInfos { if err := tagsDecRequired.Add(TagOptions{EncTag: EncTagNone, DecTag: DecTagRequired}, tag.t, tag.n[0], tag.n[1:]...); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err) } if err := tagsDecOptional.Add(TagOptions{EncTag: EncTagNone, DecTag: DecTagOptional}, tag.t, tag.n[0], tag.n[1:]...); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err) } } type tag struct { name string tagSet TagSet } tags := []tag{ {"EncTagIgnored_DecTagOptional", tagsDecOptional}, } testCases := []roundTripTest{ { name: "BinaryMarshaler non-struct", obj: number(1234567890), wantCborData: hexDecode("4800000000499602d2"), }, { name: "BinaryMarshaler struct", obj: stru{a: "a", b: "b", c: "c"}, wantCborData: hexDecode("45612C622C63"), }, { name: "non-struct", obj: myInt(1), wantCborData: hexDecode("01"), }, { name: "struct", obj: s{A: "A", B: "B", C: "C"}, wantCborData: hexDecode("a3616161416162614261636143"), // {"a":"A", "b":"B", "c":"C"} }, } for _, tag := range tags { t.Run(tag.name, func(t *testing.T) { em, _ := EncOptions{}.EncModeWithTags(tag.tagSet) dm, _ := DecOptions{}.DecModeWithTags(tag.tagSet) testRoundTrip(t, testCases, em, dm) }) } // Decode non-tag data with DecTagRequired option returns UnmarshalTypeError for _, tc := range testCases { name := "EncTagIgnored_DecTagRequired " + tc.name t.Run(name, func(t *testing.T) { dm, _ := DecOptions{}.DecModeWithTags(tagsDecRequired) v := reflect.New(reflect.TypeOf(tc.obj)) if err := dm.Unmarshal(tc.wantCborData, v.Interface()); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", tc.wantCborData) } else { if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*UnmarshalTypeError)", tc.wantCborData, err) } else if !strings.Contains(err.Error(), "expect CBOR tag value") { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.wantCborData, err.Error(), "expect CBOR tag value") } } }) } } // TestDecodeWrongTag decodes wrong tag data with DecTagRequired/EncTagOptional/EncTagNone options func TestDecodeWrongTag(t *testing.T) { type myInt int type s struct { A string `cbor:"a"` B string `cbor:"b"` C string `cbor:"c"` } type tagInfo struct { t reflect.Type n []uint64 } tagInfos := []tagInfo{ {reflect.TypeOf((*number)(nil)), []uint64{123}}, // BinaryMarshaler *number {reflect.TypeOf(stru{}), []uint64{124}}, // BinaryMarshaler stru {reflect.TypeOf(myInt(0)), []uint64{100}}, // non-struct type {reflect.TypeOf(s{}), []uint64{101, 102}}, // struct type } tagsDecRequired := NewTagSet() tagsDecOptional := NewTagSet() tagsDecIgnored := NewTagSet() for _, tag := range tagInfos { if err := tagsDecRequired.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, tag.t, tag.n[0], tag.n[1:]...); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err) } if err := tagsDecOptional.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagOptional}, tag.t, tag.n[0], tag.n[1:]...); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err) } if err := tagsDecIgnored.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagIgnored}, tag.t, tag.n[0], tag.n[1:]...); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err) } } type tag struct { name string tagSet TagSet } tags := []tag{ {"EncTagRequired_DecTagRequired", tagsDecRequired}, {"EncTagRequired_DecTagOptional", tagsDecOptional}, } testCases := []struct { name string obj interface{} cborData []byte wantErrorMsg string }{ { name: "BinaryMarshaler non-struct", obj: number(1234567890), cborData: hexDecode("d87d4800000000499602d2"), wantErrorMsg: "cbor: wrong tag number for cbor.number, got [125], expected [123]", }, { name: "BinaryMarshaler struct", obj: stru{a: "a", b: "b", c: "c"}, cborData: hexDecode("d87d45612C622C63"), wantErrorMsg: "cbor: wrong tag number for cbor.stru, got [125], expected [124]", }, { name: "non-struct", obj: myInt(1), cborData: hexDecode("d87d01"), wantErrorMsg: "cbor: wrong tag number for cbor.myInt, got [125], expected [100]", }, { name: "struct", obj: s{A: "A", B: "B", C: "C"}, cborData: hexDecode("d87ea3616161416162614261636143"), // {"a":"A", "b":"B", "c":"C"} wantErrorMsg: "cbor: wrong tag number for cbor.s, got [126], expected [101 102]", }, } for _, tc := range testCases { for _, tag := range tags { name := tag.name + " " + tc.name t.Run(name, func(t *testing.T) { dm, _ := DecOptions{}.DecModeWithTags(tag.tagSet) v := reflect.New(reflect.TypeOf(tc.obj)) if err := dm.Unmarshal(tc.cborData, v.Interface()); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", tc.cborData) } else { if _, ok := err.(*WrongTagError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*WrongTagError)", tc.cborData, err) } else if err.Error() != tc.wantErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want error %q", tc.cborData, err.Error(), tc.wantErrorMsg) } } }) } } // Decode wrong tag data with DecTagIgnored option returns no error. for _, tc := range testCases { name := "EncTagRequired_DecTagIgnored " + tc.name t.Run(name, func(t *testing.T) { dm, _ := DecOptions{}.DecModeWithTags(tagsDecIgnored) v := reflect.New(reflect.TypeOf(tc.obj)) if err := dm.Unmarshal(tc.cborData, v.Interface()); err != nil { t.Errorf("Unmarshal() returned error %v", err) } if !reflect.DeepEqual(tc.obj, v.Elem().Interface()) { t.Errorf("Marshal-Unmarshal returned different values: %v, %v", tc.obj, v.Elem().Interface()) } }) } } func TestEncodeSharedTag(t *testing.T) { type myInt int myIntType := reflect.TypeOf(myInt(0)) sharedTagSet := NewTagSet() em, err := EncOptions{}.EncModeWithSharedTags(sharedTagSet) if err != nil { t.Errorf("EncModeWithSharedTags() returned error %v", err) } // Register myInt type with tag number 123 if err = sharedTagSet.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, myIntType, 123); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", myIntType, 100, err) } // Encode myInt with tag number 123 v := myInt(1) wantCborData := hexDecode("d87b01") b, err := em.Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(b, wantCborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, wantCborData) } // Unregister myInt type sharedTagSet.Remove(myIntType) // Encode myInt without tag number 123 v = myInt(2) wantCborData = hexDecode("02") b, err = em.Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(b, wantCborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, wantCborData) } // Register myInt type with tag number 234 if err = sharedTagSet.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, myIntType, 234); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", myIntType, 100, err) } // Encode myInt with tag number 234 v = myInt(3) wantCborData = hexDecode("d8ea03") b, err = em.Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(b, wantCborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, wantCborData) } } func TestDecodeSharedTag(t *testing.T) { type myInt int myIntType := reflect.TypeOf(myInt(0)) sharedTagSet := NewTagSet() dm, err := DecOptions{}.DecModeWithSharedTags(sharedTagSet) if err != nil { t.Errorf("DecModeWithSharedTags() returned error %v", err) } // Register myInt type with tag number 123 if err = sharedTagSet.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, myIntType, 123); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", myIntType, 100, err) } // Decode myInt with tag number 123 var v myInt wantV := myInt(1) cborData := hexDecode("d87b01") if err = dm.Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(v, wantV) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV) } // Unregister myInt type sharedTagSet.Remove(myIntType) // Decode myInt without tag number wantV = myInt(2) cborData = hexDecode("02") if err := dm.Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(v, wantV) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV) } // Register myInt type with tag number 234 if err := sharedTagSet.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, myIntType, 234); err != nil { t.Fatalf("TagSet.Add(%s, %v) returned error %v", myIntType, 100, err) } // Decode myInt with tag number 234 wantV = myInt(3) cborData = hexDecode("d8ea03") if err := dm.Unmarshal(cborData, &v); err != nil { t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err) } if !reflect.DeepEqual(v, wantV) { t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV) } } func TestDecModeWithTagsError(t *testing.T) { // Create DecMode with nil as TagSet wantErrorMsg := "cbor: cannot create DecMode with nil value as TagSet" dm, err := DecOptions{}.DecModeWithTags(nil) if dm != nil { t.Errorf("DecModeWithTags(nil) returned %v", dm) } if err.Error() != wantErrorMsg { t.Errorf("DecModeWithTags(nil) returned error %q, want %q", err.Error(), wantErrorMsg) } dm, err = DecOptions{}.DecModeWithSharedTags(nil) if dm != nil { t.Errorf("DecModeWithSharedTags(nil) returned %v", dm) } if err.Error() != wantErrorMsg { t.Errorf("DecModeWithSharedTags(nil) returned error %q, want %q", err.Error(), wantErrorMsg) } // Create DecMode with invalid EncOptions wantErrorMsg = "cbor: invalid TimeTag 100" dm, err = DecOptions{TimeTag: 100}.DecModeWithTags(NewTagSet()) if dm != nil { t.Errorf("DecModeWithTags() returned %v", dm) } if err.Error() != wantErrorMsg { t.Errorf("DecModeWithTags() returned error %q, want %q", err.Error(), wantErrorMsg) } dm, err = DecOptions{TimeTag: 100}.DecModeWithSharedTags(NewTagSet()) if dm != nil { t.Errorf("DecModeWithSharedTags() returned %v", dm) } if err.Error() != wantErrorMsg { t.Errorf("DecModeWithSharedTags() returned error %q, want %q", err.Error(), wantErrorMsg) } } func TestEncModeWithTagsError(t *testing.T) { // Create EncMode with nil as TagSet wantErrorMsg := "cbor: cannot create EncMode with nil value as TagSet" em, err := EncOptions{}.EncModeWithTags(nil) if em != nil { t.Errorf("EncModeWithTags(nil) returned %v", em) } if err.Error() != wantErrorMsg { t.Errorf("EncModeWithTags(nil) returned error %q, want %q", err.Error(), wantErrorMsg) } em, err = EncOptions{}.EncModeWithSharedTags(nil) if em != nil { t.Errorf("EncModeWithSharedTags(nil) returned %v", em) } if err.Error() != wantErrorMsg { t.Errorf("EncModeWithSharedTags(nil) returned error %q, want %q", err.Error(), wantErrorMsg) } // Create EncMode with invalid EncOptions wantErrorMsg = "cbor: invalid TimeTag 100" em, err = EncOptions{TimeTag: 100}.EncModeWithTags(NewTagSet()) if em != nil { t.Errorf("EncModeWithTags() returned %v", em) } if err.Error() != wantErrorMsg { t.Errorf("EncModeWithTags() returned error %q, want %q", err.Error(), wantErrorMsg) } em, err = EncOptions{TimeTag: 100}.EncModeWithSharedTags(NewTagSet()) if em != nil { t.Errorf("EncModeWithSharedTags() returned %v", em) } if err.Error() != wantErrorMsg { t.Errorf("EncModeWithSharedTags() returned error %q, want %q", err.Error(), wantErrorMsg) } } func TestNilRawTagUnmarshalCBORError(t *testing.T) { wantErrorMsg := "cbor.RawTag: UnmarshalCBOR on nil pointer" var tag *RawTag cborData := hexDecode("c249010000000000000000") if err := tag.UnmarshalCBOR(cborData); err == nil { t.Errorf("UnmarshalCBOR() didn't return error") } else if err.Error() != wantErrorMsg { t.Errorf("UnmarshalCBOR() returned error %q, want %q", err.Error(), wantErrorMsg) } } func TestTagUnmarshalError(t *testing.T) { cborData := hexDecode("d87b61fe") // invalid UTF-8 string var tag Tag if err := Unmarshal(cborData, &tag); err == nil { t.Errorf("Unmarshal(0x%x) didn't return error", cborData) } else if err.Error() != invalidUTF8ErrorMsg { t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err.Error(), invalidUTF8ErrorMsg) } } func TestTagMarshalError(t *testing.T) { wantErrorMsg := "cbor: unsupported type: chan bool" tag := Tag{ Number: 123, Content: make(chan bool), } if _, err := Marshal(tag); err == nil { t.Errorf("Marshal() didn't return error") } else if err.Error() != wantErrorMsg { t.Errorf("Marshal() returned error %q, want %q", err.Error(), wantErrorMsg) } } func TestMarshalUninitializedTag(t *testing.T) { var v Tag b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(b, cborNil) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, cborNil) } } func TestMarshalUninitializedRawTag(t *testing.T) { var v RawTag b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(b, cborNil) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, cborNil) } } func TestMarshalTagWithEmptyContent(t *testing.T) { v := Tag{Number: 100} // Tag.Content is empty want := hexDecode("d864f6") // 100(null) b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } } func TestMarshalRawTagWithEmptyContent(t *testing.T) { v := RawTag{Number: 100} // RawTag.Content is empty want := hexDecode("d864f6") // 100(null) b, err := Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(b, want) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want) } } func TestEncodeTag(t *testing.T) { m := make(map[interface{}]bool) m[10] = true m[100] = true m[-1] = true m["z"] = true m["aa"] = true m[[1]int{100}] = true m[[1]int{-1}] = true m[false] = true v := Tag{100, m} lenFirstSortedCborData := hexDecode("d864a80af520f5f4f51864f5617af58120f5626161f5811864f5") // tag number: 100, value: map with sorted keys: 10, -1, false, 100, "z", [-1], "aa", [100] bytewiseSortedCborData := hexDecode("d864a80af51864f520f5617af5626161f5811864f58120f5f4f5") // tag number: 100, value: map with sorted keys: 10, 100, -1, "z", "aa", [100], [-1], false em, _ := EncOptions{Sort: SortLengthFirst}.EncMode() b, err := em.Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(b, lenFirstSortedCborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, lenFirstSortedCborData) } em, _ = EncOptions{Sort: SortBytewiseLexical}.EncMode() b, err = em.Marshal(v) if err != nil { t.Errorf("Marshal(%v) returned error %v", v, err) } if !bytes.Equal(b, bytewiseSortedCborData) { t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, bytewiseSortedCborData) } } func TestDecodeTagToEmptyIface(t *testing.T) { type myBool bool type myUint uint typeMyBool := reflect.TypeOf(myBool(false)) typeMyUint := reflect.TypeOf(myUint(0)) tags := NewTagSet() if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, typeMyBool, 100); err != nil { t.Fatalf("TagSet.Add(%s, %d) returned error %v", typeMyBool, 100, err) } if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, typeMyUint, 101, 102); err != nil { t.Fatalf("TagSet.Add(%s, %d, %d) returned error %v", typeMyUint, 101, 102, err) } dm, _ := DecOptions{}.DecModeWithTags(tags) dmSharedTags, _ := DecOptions{}.DecModeWithSharedTags(tags) testCases := []struct { name string cborData []byte wantObj interface{} }{ { name: "registered myBool", cborData: hexDecode("d864f5"), // 100(true) wantObj: myBool(true), }, { name: "registered myUint", cborData: hexDecode("d865d86600"), // 101(102(0)) wantObj: myUint(0), }, { name: "not registered bool", cborData: hexDecode("d865f5"), // 101(true) wantObj: Tag{101, true}, }, { name: "not registered uint", cborData: hexDecode("d865d86700"), // 101(103(0)) wantObj: Tag{101, Tag{103, uint64(0)}}, }, { name: "not registered uint", cborData: hexDecode("d865d866d86700"), // 101(102(103(0))) wantObj: Tag{101, Tag{102, Tag{103, uint64(0)}}}, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var v1 interface{} if err := dm.Unmarshal(tc.cborData, &v1); err != nil { t.Errorf("Unmarshal() returned error %v", err) } if !reflect.DeepEqual(tc.wantObj, v1) { t.Errorf("Unmarshal to interface{} returned different values: %v, %v", tc.wantObj, v1) } var v2 interface{} if err := dmSharedTags.Unmarshal(tc.cborData, &v2); err != nil { t.Errorf("Unmarshal() returned error %v", err) } if !reflect.DeepEqual(tc.wantObj, v2) { t.Errorf("Unmarshal to interface{} returned different values: %v, %v", tc.wantObj, v2) } }) } } func TestDecodeRegisteredTagToEmptyIfaceError(t *testing.T) { type myInt int typeMyInt := reflect.TypeOf(myInt(0)) tags := NewTagSet() if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, typeMyInt, 101, 102); err != nil { t.Fatalf("TagSet.Add(%s, %d, %d) returned error %v", typeMyInt, 101, 102, err) } dm, _ := DecOptions{}.DecModeWithTags(tags) cborData := hexDecode("d865d8663bffffffffffffffff") // 101(102(-18446744073709551616)) var v interface{} if err := dm.Unmarshal(cborData, &v); err == nil { t.Errorf("Unmarshal(0x%x) didn't return an error", cborData) } else if _, ok := err.(*UnmarshalTypeError); !ok { t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err) } else if !strings.Contains(err.Error(), "cannot unmarshal") { t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), "cannot unmarshal") } } type number3 uint64 // MarshalCBOR marshals number3 to CBOR tagged map (tag number 100) func (n number3) MarshalCBOR() (data []byte, err error) { m := map[string]uint64{"num": uint64(n)} return Marshal(Tag{100, m}) } // UnmarshalCBOR unmarshals CBOR tagged map to number3 func (n *number3) UnmarshalCBOR(data []byte) (err error) { var rawTag RawTag if err := Unmarshal(data, &rawTag); err != nil { return err } if rawTag.Number != 100 { return fmt.Errorf("wrong tag number %d, want %d", rawTag.Number, 100) } if rawTag.Content[0]&0xe0 != 0xa0 { return fmt.Errorf("wrong tag content type, want map") } var v map[string]uint64 if err := Unmarshal(rawTag.Content, &v); err != nil { return err } *n = number3(v["num"]) return nil } func TestDecodeRegisterTagForUnmarshaler(t *testing.T) { typ := reflect.TypeOf(number3(0)) tags := NewTagSet() if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, typ, 100); err != nil { t.Fatalf("TagSet.Add(%s, %d) returned error %v", typ, 100, err) } cborData := hexDecode("d864a1636e756d01") // 100({"num": 1}) wantObj := number3(1) dm, _ := DecOptions{}.DecModeWithTags(tags) em, _ := EncOptions{}.EncModeWithTags(tags) // Decode to empty interface. Unmarshal() should return object of registered type. var v1 interface{} if err := dm.Unmarshal(cborData, &v1); err != nil { t.Errorf("Unmarshal() returned error %v", err) } if !reflect.DeepEqual(wantObj, v1) { t.Errorf("Unmarshal() returned different values: %v (%T), %v (%T)", wantObj, wantObj, v1, v1) } b, err := em.Marshal(v1) if err != nil { t.Errorf("Marshal(%v) returned error %v", v1, err) } else if !bytes.Equal(b, cborData) { t.Errorf("Marshal(%v) returned %v, want %v", v1, b, cborData) } // Decode to registered type. var v2 number3 if err = dm.Unmarshal(cborData, &v2); err != nil { t.Errorf("Unmarshal() returned error %v", err) } if !reflect.DeepEqual(wantObj, v2) { t.Errorf("Unmarshal() returned different values: %v, %v", wantObj, v2) } b, err = em.Marshal(v2) if err != nil { t.Errorf("Marshal(%v) returned error %v", v2, err) } else if !bytes.Equal(b, cborData) { t.Errorf("Marshal(%v) returned %v, want %v", v2, b, cborData) } } golang-github-fxamacker-cbor-2.4.0/valid.go000066400000000000000000000172131416612535600205710ustar00rootroot00000000000000// Copyright (c) Faye Amacker. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. package cbor import ( "encoding/binary" "errors" "io" "strconv" ) // SyntaxError is a description of a CBOR syntax error. type SyntaxError struct { msg string } func (e *SyntaxError) Error() string { return e.msg } // SemanticError is a description of a CBOR semantic error. type SemanticError struct { msg string } func (e *SemanticError) Error() string { return e.msg } // MaxNestedLevelError indicates exceeded max nested level of any combination of CBOR arrays/maps/tags. type MaxNestedLevelError struct { maxNestedLevels int } func (e *MaxNestedLevelError) Error() string { return "cbor: exceeded max nested level " + strconv.Itoa(e.maxNestedLevels) } // MaxArrayElementsError indicates exceeded max number of elements for CBOR arrays. type MaxArrayElementsError struct { maxArrayElements int } func (e *MaxArrayElementsError) Error() string { return "cbor: exceeded max number of elements " + strconv.Itoa(e.maxArrayElements) + " for CBOR array" } // MaxMapPairsError indicates exceeded max number of key-value pairs for CBOR maps. type MaxMapPairsError struct { maxMapPairs int } func (e *MaxMapPairsError) Error() string { return "cbor: exceeded max number of key-value pairs " + strconv.Itoa(e.maxMapPairs) + " for CBOR map" } // IndefiniteLengthError indicates found disallowed indefinite length items. type IndefiniteLengthError struct { t cborType } func (e *IndefiniteLengthError) Error() string { return "cbor: indefinite-length " + e.t.String() + " isn't allowed" } // TagsMdError indicates found disallowed CBOR tags. type TagsMdError struct { } 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 { if len(d.data) == d.off { return io.EOF } _, err := d.validInternal(0) 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() if err != nil { return 0, err } switch t { case cborTypeByteString, cborTypeTextString: if ai == 31 { if d.dm.indefLength == IndefLengthForbidden { return 0, &IndefiniteLengthError{t} } return d.validIndefiniteString(t, depth) } valInt := int(val) if valInt < 0 { // Detect integer overflow return 0, errors.New("cbor: " + t.String() + " length " + strconv.FormatUint(val, 10) + " is too large, causing integer overflow") } if len(d.data)-d.off < valInt { // valInt+off may overflow integer return 0, io.ErrUnexpectedEOF } d.off += valInt case cborTypeArray, cborTypeMap: depth++ if depth > d.dm.maxNestedLevels { return 0, &MaxNestedLevelError{d.dm.maxNestedLevels} } if ai == 31 { if d.dm.indefLength == IndefLengthForbidden { return 0, &IndefiniteLengthError{t} } return d.validIndefiniteArrayOrMap(t, depth) } valInt := int(val) if valInt < 0 { // Detect integer overflow return 0, errors.New("cbor: " + t.String() + " length " + strconv.FormatUint(val, 10) + " is too large, it would cause integer overflow") } if t == cborTypeArray { if valInt > d.dm.maxArrayElements { return 0, &MaxArrayElementsError{d.dm.maxArrayElements} } } else { if valInt > d.dm.maxMapPairs { return 0, &MaxMapPairsError{d.dm.maxMapPairs} } } count := 1 if t == cborTypeMap { count = 2 } maxDepth := depth for j := 0; j < count; j++ { for i := 0; i < valInt; i++ { var dpt int if dpt, err = d.validInternal(depth); err != nil { return 0, err } if dpt > maxDepth { maxDepth = dpt // Save max depth } } } depth = maxDepth case cborTypeTag: if d.dm.tagsMd == TagsForbidden { return 0, &TagsMdError{} } // Scan nested tag numbers to avoid recursion. for { if len(d.data) == d.off { // Tag number must be followed by tag content. return 0, io.ErrUnexpectedEOF } if cborType(d.data[d.off]&0xe0) != cborTypeTag { break } if _, _, _, err = d.validHead(); err != nil { return 0, err } depth++ if depth > d.dm.maxNestedLevels { return 0, &MaxNestedLevelError{d.dm.maxNestedLevels} } } // Check tag content. return d.validInternal(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) { var err error for { if len(d.data) == d.off { return 0, io.ErrUnexpectedEOF } if d.data[d.off] == 0xff { d.off++ break } // Peek ahead to get next type and indefinite length status. nt := cborType(d.data[d.off] & 0xe0) if t != nt { return 0, &SyntaxError{"cbor: wrong element type " + nt.String() + " for indefinite-length " + t.String()} } 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 { 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) { var err error maxDepth := depth i := 0 for { if len(d.data) == d.off { return 0, io.ErrUnexpectedEOF } if d.data[d.off] == 0xff { d.off++ break } var dpt int if dpt, err = d.validInternal(depth); err != nil { return 0, err } if dpt > maxDepth { maxDepth = dpt } i++ if t == cborTypeArray { if i > d.dm.maxArrayElements { return 0, &MaxArrayElementsError{d.dm.maxArrayElements} } } else { if i%2 == 0 && i/2 > d.dm.maxMapPairs { return 0, &MaxMapPairsError{d.dm.maxMapPairs} } } } if t == cborTypeMap && i%2 == 1 { return 0, &SyntaxError{"cbor: unexpected \"break\" code"} } return maxDepth, nil } func (d *decoder) validHead() (t cborType, ai byte, val uint64, err error) { dataLen := len(d.data) - d.off if dataLen == 0 { return 0, 0, 0, io.ErrUnexpectedEOF } t = cborType(d.data[d.off] & 0xe0) ai = d.data[d.off] & 0x1f val = uint64(ai) d.off++ if ai < 24 { return t, ai, val, nil } if ai == 24 { if dataLen < 2 { return 0, 0, 0, io.ErrUnexpectedEOF } val = uint64(d.data[d.off]) d.off++ if t == cborTypePrimitives && val < 32 { return 0, 0, 0, &SyntaxError{"cbor: invalid simple value " + strconv.Itoa(int(val)) + " for type " + t.String()} } return t, ai, val, nil } if ai == 25 { if dataLen < 3 { return 0, 0, 0, io.ErrUnexpectedEOF } val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+2])) d.off += 2 return t, ai, val, nil } if ai == 26 { if dataLen < 5 { return 0, 0, 0, io.ErrUnexpectedEOF } val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+4])) d.off += 4 return t, ai, val, nil } if ai == 27 { if dataLen < 9 { return 0, 0, 0, io.ErrUnexpectedEOF } val = binary.BigEndian.Uint64(d.data[d.off : d.off+8]) d.off += 8 return t, ai, val, nil } if ai == 31 { 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(). return 0, 0, 0, &SyntaxError{"cbor: unexpected \"break\" code"} } return t, ai, val, nil } // ai == 28, 29, 30 return 0, 0, 0, &SyntaxError{"cbor: invalid additional information " + strconv.Itoa(int(ai)) + " for type " + t.String()} } golang-github-fxamacker-cbor-2.4.0/valid_test.go000066400000000000000000000174521416612535600216350ustar00rootroot00000000000000// 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" "testing" ) func TestValid1(t *testing.T) { for _, mt := range marshalTests { if err := Valid(mt.cborData); err != nil { t.Errorf("Valid() returned error %v", err) } } } 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) } } } func TestValidOnStreamingData(t *testing.T) { var buf bytes.Buffer for _, t := range marshalTests { buf.Write(t.cborData) } 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) } } } func TestDepth(t *testing.T) { testCases := []struct { name string cborData []byte wantDepth int }{ {"uint", hexDecode("00"), 0}, // 0 {"int", hexDecode("20"), 0}, // -1 {"bool", hexDecode("f4"), 0}, // false {"nil", hexDecode("f6"), 0}, // nil {"float", hexDecode("fa47c35000"), 0}, // 100000.0 {"byte string", hexDecode("40"), 0}, // []byte{} {"indefinite length byte string", hexDecode("5f42010243030405ff"), 0}, // []byte{1, 2, 3, 4, 5} {"text string", hexDecode("60"), 0}, // "" {"indefinite length text string", hexDecode("7f657374726561646d696e67ff"), 0}, // "streaming" {"empty array", hexDecode("80"), 1}, // [] {"indefinite length empty array", hexDecode("9fff"), 1}, // [] {"array", hexDecode("98190102030405060708090a0b0c0d0e0f101112131415161718181819"), 1}, // [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] {"indefinite length array", hexDecode("9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff"), 1}, // [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] {"nested array", hexDecode("8301820203820405"), 2}, // [1,[2,3],[4,5]] {"indefinite length nested array", hexDecode("83018202039f0405ff"), 2}, // [1,[2,3],[4,5]] {"array and map", hexDecode("826161a161626163"), 2}, // [a", {"b": "c"}] {"indefinite length array and map", hexDecode("826161bf61626163ff"), 2}, // [a", {"b": "c"}] {"empty map", hexDecode("a0"), 1}, // {} {"indefinite length empty map", hexDecode("bfff"), 1}, // {} {"map", hexDecode("a201020304"), 1}, // {1:2, 3:4} {"nested map", hexDecode("a26161016162820203"), 2}, // {"a": 1, "b": [2, 3]} {"indefinite length nested map", hexDecode("bf61610161629f0203ffff"), 2}, // {"a": 1, "b": [2, 3]} {"tag", hexDecode("c074323031332d30332d32315432303a30343a30305a"), 0}, // 0("2013-03-21T20:04:00Z") {"tagged map", hexDecode("d864a26161016162820203"), 2}, // 100({"a": 1, "b": [2, 3]}) {"tagged map and array", hexDecode("d864a26161016162d865820203"), 2}, // 100({"a": 1, "b": 101([2, 3])}) {"tagged map and array", hexDecode("d864a26161016162d865d866820203"), 3}, // 100({"a": 1, "b": 101(102([2, 3]))}) {"nested tag", hexDecode("d864d865d86674323031332d30332d32315432303a30343a30305a"), 2}, // 100(101(102("2013-03-21T20:04:00Z"))) {"32-level array", hexDecode("82018181818181818181818181818181818181818181818181818181818181818101"), 32}, {"32-level indefinite length array", hexDecode("9f018181818181818181818181818181818181818181818181818181818181818101ff"), 32}, {"32-level map", hexDecode("a1018181818181818181818181818181818181818181818181818181818181818101"), 32}, {"32-level indefinite length map", hexDecode("bf018181818181818181818181818181818181818181818181818181818181818101ff"), 32}, {"32-level tag", hexDecode("d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d86474323031332d30332d32315432303a30343a30305a"), 32}, // 100(100(...("2013-03-21T20:04:00Z"))) } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { d := decoder{data: tc.cborData, dm: defaultDecMode} depth, err := d.validInternal(0) if err != nil { t.Errorf("valid(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) } }) } } func TestDepthError(t *testing.T) { testCases := []struct { name string cborData []byte opts DecOptions wantErrorMsg string }{ { name: "33-level array", cborData: hexDecode("82018181818181818181818181818181818181818181818181818181818181818101"), opts: DecOptions{MaxNestedLevels: 4}, wantErrorMsg: "cbor: exceeded max nested level 4", }, { name: "33-level array", cborData: hexDecode("82018181818181818181818181818181818181818181818181818181818181818101"), opts: DecOptions{MaxNestedLevels: 10}, wantErrorMsg: "cbor: exceeded max nested level 10", }, { name: "33-level array", cborData: hexDecode("8201818181818181818181818181818181818181818181818181818181818181818101"), opts: DecOptions{}, wantErrorMsg: "cbor: exceeded max nested level 32", }, { name: "33-level indefinite length array", cborData: hexDecode("9f01818181818181818181818181818181818181818181818181818181818181818101ff"), opts: DecOptions{}, wantErrorMsg: "cbor: exceeded max nested level 32", }, { name: "33-level map", cborData: hexDecode("a101818181818181818181818181818181818181818181818181818181818181818101"), opts: DecOptions{}, wantErrorMsg: "cbor: exceeded max nested level 32", }, { name: "33-level indefinite length map", cborData: hexDecode("bf01818181818181818181818181818181818181818181818181818181818181818101ff"), opts: DecOptions{}, wantErrorMsg: "cbor: exceeded max nested level 32", }, { name: "33-level tag", cborData: hexDecode("d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d86474323031332d30332d32315432303a30343a30305a"), opts: DecOptions{}, wantErrorMsg: "cbor: exceeded max nested level 32", }, } for _, tc := range testCases { 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) } else if _, ok := err.(*MaxNestedLevelError); !ok { t.Errorf("valid(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) } }) } }