Compare commits
2 Commits
feature/cb
...
release-2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49b43cbd39 | ||
|
|
0dd8886f91 |
36
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
36
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
@ -1,36 +0,0 @@
|
||||
---
|
||||
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`)?
|
||||
|
||||
<details><summary><code>go env</code> Output</summary><br><pre>
|
||||
$ go env
|
||||
|
||||
</pre></details>
|
||||
|
||||
### What did you do?
|
||||
|
||||
|
||||
<!--
|
||||
If possible, provide steps and/or code to reproduce the problem.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
|
||||
|
||||
### What did you see instead?
|
||||
20
.github/ISSUE_TEMPLATE/2-feature-request.md
vendored
20
.github/ISSUE_TEMPLATE/2-feature-request.md
vendored
@ -1,20 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@ -1,16 +0,0 @@
|
||||
---
|
||||
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).
|
||||
@ -1,16 +0,0 @@
|
||||
---
|
||||
name: "\U0001F513 Security issue disclosure"
|
||||
about: Report a security issue in fxamacker/cbor
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
🛑 PLEASE DO NOT DISCLOSE THE ISSUE HERE BECAUSE IT IS PUBLIC.
|
||||
|
||||
Email security disclosures to: faye.github@gmail.com
|
||||
|
||||
-->
|
||||
12
.github/dependabot.yml
vendored
12
.github/dependabot.yml
vendored
@ -1,12 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
69
.github/pull_request_template.md
vendored
69
.github/pull_request_template.md
vendored
@ -1,69 +0,0 @@
|
||||
<!--
|
||||
Thank you for your interest in contributing to fxamacker/cbor!
|
||||
-->
|
||||
|
||||
### Description
|
||||
|
||||
|
||||
<!-- For code contributions, please complete all the items below this line. -->
|
||||
<!-- For documentation-only contributions, please delete everything below this line. -->
|
||||
|
||||
#### PR Was Proposed and Welcomed in Currently Open Issue
|
||||
|
||||
- [ ] This PR was proposed and welcomed by maintainer(s) in issue #___
|
||||
- [ ] Closes or Updates Issue #___
|
||||
|
||||
#### Checklist (for code PR only, ignore for docs PR)
|
||||
|
||||
- [ ] Include unit tests that cover the new code
|
||||
- [ ] Pass all unit tests
|
||||
- [ ] Pass all lint checks in CI (goimports, 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 <firstname.lastname@example.com>
|
||||
- [ ] 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.
|
||||
```
|
||||
|
||||
13
.github/workflows/ci-go-cover.yml
vendored
13
.github/workflows/ci-go-cover.yml
vendored
@ -1,7 +1,7 @@
|
||||
# Copyright 2020-2023 Montgomery Edwards⁴⁴⁸ (github.com/x448).
|
||||
# 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 2023.5.14.
|
||||
# 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.
|
||||
#
|
||||
@ -14,7 +14,7 @@
|
||||
# 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 ≥96%
|
||||
name: cover ≥98%
|
||||
|
||||
# Remove default permissions.
|
||||
permissions: {}
|
||||
@ -23,7 +23,6 @@ on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main, master]
|
||||
|
||||
jobs:
|
||||
|
||||
@ -36,11 +35,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.21
|
||||
go-version: 1.19
|
||||
check-latest: true
|
||||
- name: Install x448/float16
|
||||
run: go get github.com/x448/float16@v0.8.4
|
||||
|
||||
42
.github/workflows/ci.yml
vendored
42
.github/workflows/ci.yml
vendored
@ -1,54 +1,26 @@
|
||||
# GitHub Actions - CI for Go to build & test.
|
||||
# GitHub Actions - CI for Go to build & test. See ci-go-cover.yml and linters.yml for code coverage and linters.
|
||||
# https://github.com/fxamacker/cbor/workflows/ci.yml
|
||||
# See ci-go-cover.yml for coverage and safer-golangci-lint.yml for linting.
|
||||
name: ci
|
||||
|
||||
# Revoke default permissions.
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release*'
|
||||
- 'feature/stream-mode'
|
||||
tags:
|
||||
- 'v*'
|
||||
on: [push]
|
||||
jobs:
|
||||
|
||||
# Test on various OS with default Go version.
|
||||
tests:
|
||||
name: test ${{matrix.os}} go-${{ matrix.go-version }}
|
||||
name: Test on ${{matrix.os}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
go-version: [1.17, 1.18, 1.19, '1.20', 1.21, 1.22, 1.23]
|
||||
os: [macos-latest, ubuntu-latest]
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@v1
|
||||
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 ./...
|
||||
go test -short -race -v ./...
|
||||
|
||||
45
.github/workflows/codeql-analysis.yml
vendored
45
.github/workflows/codeql-analysis.yml
vendored
@ -1,45 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
# Remove default permissions
|
||||
permissions: {}
|
||||
|
||||
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' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
# 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@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
|
||||
47
.github/workflows/govulncheck.yml
vendored
47
.github/workflows/govulncheck.yml
vendored
@ -1,47 +0,0 @@
|
||||
# GitHub Actions workflow for govulncheck.
|
||||
# This file is licensed under MIT License.
|
||||
# https://github.com/fxamacker/cbor
|
||||
|
||||
name: govulncheck
|
||||
|
||||
# Revoke default permissions and grant what's needed in each job.
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**'
|
||||
- '!**.md'
|
||||
push:
|
||||
paths:
|
||||
- '**'
|
||||
- '!**.md'
|
||||
branches:
|
||||
- 'main'
|
||||
- 'master'
|
||||
- 'release*'
|
||||
- 'feature/stream-mode'
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
Check:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Grant permission to read content.
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version: 1.21.x
|
||||
check-latest: true
|
||||
- name: Install latest from golang.org
|
||||
run: go install golang.org/x/vuln/cmd/govulncheck@4ea4418106cea3bb2c9aa098527c924e9e1fbbb4 # v1.1.3
|
||||
- name: Run govulncheck
|
||||
run: govulncheck -show=traces ./...
|
||||
21
.github/workflows/linters.yml
vendored
Normal file
21
.github/workflows/linters.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Go Linters - GitHub Actions
|
||||
name: linters
|
||||
on: [push]
|
||||
jobs:
|
||||
|
||||
# Check linters on latest-ubuntu with default version of Go.
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install golangci-lint
|
||||
run: |
|
||||
go version
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.23.1
|
||||
- name: Run required linters in .golangci.yml plus hard-coded ones here
|
||||
run: $(go env GOPATH)/bin/golangci-lint run --timeout=5m -E deadcode -E errcheck -E gofmt -E golint -E gosec -E govet -E ineffassign -E maligned -E staticcheck -E structcheck -E unconvert -E varcheck
|
||||
- name: Run optional linters (not required to pass)
|
||||
run: $(go env GOPATH)/bin/golangci-lint run --timeout=5m --issues-exit-code=0 -E dupl -E gocritic -E gosimple -E lll -E prealloc -E deadcode -E errcheck -E gofmt -E golint -E gosec -E govet -E ineffassign -E maligned -E staticcheck -E structcheck -E unconvert -E varcheck
|
||||
|
||||
71
.github/workflows/safer-golangci-lint.yml
vendored
71
.github/workflows/safer-golangci-lint.yml
vendored
@ -1,71 +0,0 @@
|
||||
# Copyright © 2021-2023 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
|
||||
#
|
||||
name: linters
|
||||
|
||||
# Remove default permissions and grant only what is required in each job.
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main, master]
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.22'
|
||||
GOLINTERS_VERSION: 1.56.2
|
||||
GOLINTERS_ARCH: linux-amd64
|
||||
GOLINTERS_TGZ_DGST: e1c313fb5fc85a33890fdee5dbb1777d1f5829c84d655a47a55688f3aad5e501
|
||||
GOLINTERS_TIMEOUT: 15m
|
||||
OPENSSL_DGST_CMD: openssl dgst -sha256 -r
|
||||
CURL_CMD: curl --proto =https --tlsv1.2 --location --silent --show-error --fail
|
||||
|
||||
jobs:
|
||||
main:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Install golangci-lint
|
||||
run: |
|
||||
GOLINTERS_URL_PREFIX="https://github.com/golangci/golangci-lint/releases/download/v${GOLINTERS_VERSION}/"
|
||||
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
|
||||
@ -1,26 +1,12 @@
|
||||
# Do not delete linter settings. Linters like gocritic can be enabled on the command line.
|
||||
|
||||
linters-settings:
|
||||
depguard:
|
||||
rules:
|
||||
prevent_unmaintained_packages:
|
||||
list-mode: strict
|
||||
files:
|
||||
- $all
|
||||
- "!$test"
|
||||
allow:
|
||||
- $gostd
|
||||
- github.com/x448/float16
|
||||
deny:
|
||||
- pkg: io/ioutil
|
||||
desc: "replaced by io and os packages since Go 1.16: https://tip.golang.org/doc/go1.16#ioutil"
|
||||
dupl:
|
||||
threshold: 100
|
||||
funlen:
|
||||
lines: 100
|
||||
statements: 50
|
||||
goconst:
|
||||
ignore-tests: true
|
||||
min-len: 2
|
||||
min-occurrences: 3
|
||||
gocritic:
|
||||
@ -31,14 +17,14 @@ linters-settings:
|
||||
- performance
|
||||
- style
|
||||
disabled-checks:
|
||||
- commentedOutCode
|
||||
- dupImport # https://github.com/go-critic/go-critic/issues/845
|
||||
- ifElseChain
|
||||
- octalLiteral
|
||||
- paramTypeCombine
|
||||
- whyNoLint
|
||||
- wrapperFunc
|
||||
gofmt:
|
||||
simplify: false
|
||||
simplify: false
|
||||
goimports:
|
||||
local-prefixes: github.com/fxamacker/cbor
|
||||
golint:
|
||||
@ -51,54 +37,50 @@ linters-settings:
|
||||
suggest-new: true
|
||||
misspell:
|
||||
locale: US
|
||||
staticcheck:
|
||||
checks: ["all"]
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- asciicheck
|
||||
- bidichk
|
||||
- depguard
|
||||
- deadcode
|
||||
- errcheck
|
||||
- exportloopref
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- goimports
|
||||
- goprintffuncname
|
||||
- golint
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- maligned
|
||||
- misspell
|
||||
- nilerr
|
||||
- revive
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- 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: decode.go
|
||||
text: "string ` overflows ` has (\\d+) occurrences, make it a constant"
|
||||
- path: decode.go
|
||||
text: "string ` \\(range is \\[` has (\\d+) occurrences, make it a constant"
|
||||
- path: decode.go
|
||||
text: "string `, ` has (\\d+) occurrences, make it a constant"
|
||||
- path: decode.go
|
||||
text: "string ` overflows Go's int64` has (\\d+) occurrences, make it a constant"
|
||||
- path: decode.go
|
||||
text: "string `\\]\\)` has (\\d+) occurrences, make it a constant"
|
||||
- path: valid.go
|
||||
text: "string ` for type ` has (\\d+) occurrences, make it a constant"
|
||||
- path: valid.go
|
||||
text: "string `cbor: ` has (\\d+) occurrences, make it a constant"
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- goconst
|
||||
- dupl
|
||||
- gomnd
|
||||
- lll
|
||||
- path: doc\.go
|
||||
linters:
|
||||
- goimports
|
||||
- gomnd
|
||||
- lll
|
||||
|
||||
# golangci.com configuration
|
||||
# https://github.com/golangci/golangci/wiki/Configuration
|
||||
service:
|
||||
golangci-lint-version: 1.23.x # use the fixed version to not introduce new linters unexpectedly
|
||||
|
||||
264
CBOR_BENCHMARKS.md
Normal file
264
CBOR_BENCHMARKS.md
Normal file
@ -0,0 +1,264 @@
|
||||
# CBOR Benchmarks for fxamacker/cbor
|
||||
|
||||
See [bench_test.go](bench_test.go).
|
||||
|
||||
Benchmarks on Jan. 12, 2020 with cbor v1.5.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 | 114 ns/op | 16 B/op | 1 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_bool_to_Go_bool-2 | 101 ns/op | 1 B/op | 1 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_positive_int_to_Go_interface_{}-2 | 141 ns/op | 24 B/op | 2 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_positive_int_to_Go_uint64-2 | 120 ns/op | 8 B/op | 1 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_negative_int_to_Go_interface_{}-2 | 141 ns/op | 24 B/op | 2 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_negative_int_to_Go_int64-2 | 120 ns/op | 8 B/op | 1 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_float_to_Go_interface_{}-2 | 143 ns/op | 24 B/op | 2 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_float_to_Go_float64-2 | 121 ns/op | 8 B/op | 1 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_bytes_to_Go_interface_{}-2 | 182 ns/op | 80 B/op | 3 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_bytes_to_Go_[]uint8-2 | 201 ns/op | 64 B/op | 2 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_text_to_Go_interface_{}-2 | 213 ns/op | 80 B/op | 3 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_text_to_Go_string-2 | 196 ns/op | 64 B/op | 2 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_array_to_Go_interface_{}-2 |1094 ns/op | 672 B/op | 29 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_array_to_Go_[]int-2 | 1102 ns/op | 272 B/op | 3 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_map_to_Go_interface_{}-2 | 2966 ns/op | 1420 B/op | 30 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_map_to_Go_map[string]interface_{}-2 | 3754 ns/op | 964 B/op | 19 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_map_to_Go_map[string]string-2 | 2621 ns/op | 740 B/op | 5 allocs/op
|
||||
|
||||
Encoding Benchmark | Time | Memory | Allocs
|
||||
--- | ---: | ---: | ---:
|
||||
BenchmarkMarshal/Go_bool_to_CBOR_bool-2 | 88.1 ns/op | 1 B/op | 1 allocs/op
|
||||
BenchmarkMarshal/Go_uint64_to_CBOR_positive_int-2 | 97.3 ns/op | 16 B/op | 1 allocs/op
|
||||
BenchmarkMarshal/Go_int64_to_CBOR_negative_int-2 | 91.2 ns/op | 3 B/op | 1 allocs/op
|
||||
BenchmarkMarshal/Go_float64_to_CBOR_float-2 | 100 ns/op | 16 B/op | 1 allocs/op
|
||||
BenchmarkMarshal/Go_[]uint8_to_CBOR_bytes-2 | 123 ns/op | 32 B/op | 1 allocs/op
|
||||
BenchmarkMarshal/Go_string_to_CBOR_text-2 | 118 ns/op | 48 B/op | 1 allocs/op
|
||||
BenchmarkMarshal/Go_[]int_to_CBOR_array-2 | 528 ns/op | 32 B/op | 1 allocs/op
|
||||
BenchmarkMarshal/Go_map[string]string_to_CBOR_map-2 | 2096 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 | 6260 ns/op | 2621 B/op | 73 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_map_to_Go_struct-2 | 4481 ns/op | 1172 B/op | 10 allocs/op
|
||||
|
||||
Encoding Benchmark | Time | Memory | Allocs
|
||||
--- | ---: | ---: | ---:
|
||||
BenchmarkMarshal/Go_map[string]interface{}_to_CBOR_map-2 | 4462 ns/op | 1072 B/op | 45 allocs/op
|
||||
BenchmarkMarshal/Go_struct_to_CBOR_map-2 | 2891 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| 6070 ns/op | 2517 B/op | 70 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_map_to_Go_struct_keyasint-2 | 4355 ns/op | 1173 B/op | 10 allocs/op
|
||||
|
||||
Encoding Benchmark | Time | Memory | Allocs
|
||||
--- | ---: | ---: | ---:
|
||||
BenchmarkMarshal/Go_map[int]interface{}_to_CBOR_map-2 | 4318 ns/op | 992 B/op | 45 allocs/op
|
||||
BenchmarkMarshal/Go_struct_keyasint_to_CBOR_map-2 | 2879 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 | 4968 ns/op | 2404 B/op | 67 allocs/op
|
||||
BenchmarkUnmarshal/CBOR_array_to_Go_struct_toarray-2 | 4205 ns/op | 1164 B/op | 9 allocs/op
|
||||
|
||||
Encoding Benchmark | Time | Memory | Allocs
|
||||
--- | ---: | ---: | ---:
|
||||
BenchmarkMarshal/Go_[]interface{}_to_CBOR_map-2 | 3272 ns/op | 704 B/op | 28 allocs/op
|
||||
BenchmarkMarshal/Go_struct_toarray_to_CBOR_array-2 | 2840 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 | 578 ns/op | 240 B/op | 4 allocs/op
|
||||
BenchmarkUnmarshalCOSE/256-Bit_Symmetric_Key-2 | 586 ns/op | 256 B/op | 4 allocs/op
|
||||
BenchmarkUnmarshalCOSE/ECDSA_P256_256-Bit_Key-2 | 989 ns/op | 360 B/op | 7 allocs/op
|
||||
|
||||
Encoding Benchmark | Time | Memory | Allocs
|
||||
--- | ---: | ---: | ---:
|
||||
BenchmarkMarshalCOSE/128-Bit_Symmetric_Key-2 | 535 ns/op | 224 B/op | 2 allocs/op
|
||||
BenchmarkMarshalCOSE/256-Bit_Symmetric_Key-2 | 543 ns/op | 240 B/op | 2 allocs/op
|
||||
BenchmarkMarshalCOSE/ECDSA_P256_256-Bit_Key-2 | 681 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 | 796 ns/op | 176 B/op | 6 allocs/op
|
||||
|
||||
Encoding Benchmark | Time | Memory | Allocs
|
||||
--- | ---: | ---: | ---:
|
||||
BenchmarkMarshalCWTClaims-2 | 466 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 | 3146 ns/op | 1544 B/op | 18 allocs/op
|
||||
|
||||
Encoding Benchmark | Time | Memory | Allocs
|
||||
--- | ---: | ---: | ---:
|
||||
BenchmarkMarshalSenML-2 | 2968 ns/op | 272 B/op | 2 allocs/op
|
||||
32
CBOR_GOLANG.md
Normal file
32
CBOR_GOLANG.md
Normal file
@ -0,0 +1,32 @@
|
||||
👉 [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 7049 Concise Binary Object Representation](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.
|
||||
|
||||
<hr>
|
||||
|
||||
👉 [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)
|
||||
@ -1,133 +1,76 @@
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
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 a positive environment for our
|
||||
community include:
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
* 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 include:
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* 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 email address,
|
||||
without their explicit permission
|
||||
* 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
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
## Our Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
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 within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
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 to the community leaders responsible for enforcement at
|
||||
faye.github@gmail.com.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
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.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
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 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
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
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
|
||||
@ -1,41 +1,47 @@
|
||||
# How to contribute
|
||||
|
||||
You can contribute by using the library, opening issues, or opening pull requests.
|
||||
This project started because I needed an easy, small, and crash-proof CBOR library for my [WebAuthn (FIDO2) server library](https://github.com/fxamacker/webauthn). I believe this was the first and still only standalone CBOR library (in Go) that is fuzz tested as of November 10, 2019.
|
||||
|
||||
## Bug reports and security vulnerabilities
|
||||
To my surprise, Stefan Tatschner (rumpelsepp) submitted the first 2 issues when I didn't expect this project to be noticed. So I decided to make it more full-featured for others by announcing releases and asking for feedback. Even this document exists because Montgomery Edwards⁴⁴⁸ (x448) opened [issue #22](https://github.com/fxamacker/cbor/issues/22). In other words, you can contribute by opening an issue that helps the project improve. Especially in the early stages.
|
||||
|
||||
Most issues are tracked publicly on [GitHub](https://github.com/fxamacker/cbor/issues).
|
||||
When I announced v1.2 on Go Forum, Jakob Borg (calmh) responded with a thumbs up and encouragement. Another project of equal priority needed my time and Jakob's kind words tipped the scale for me to work on this one (speedups for [milestone v1.3](https://github.com/fxamacker/cbor/issues?q=is%3Aopen+is%3Aissue+milestone%3Av1.3.0).) So words of appreciation or encouragement is nice way to contribute to open source projects.
|
||||
|
||||
Another way is by using this library in your project. It can lead to features that benefit both projects, which is what happened when oasislabs/oasis-core switched to this CBOR libary -- thanks Yawning Angel (yawning) for requesting BinaryMarshaler/BinaryUnmarshaler and Jernej Kos (kostco) for requesting RawMessage!
|
||||
|
||||
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 signed contract where I'm indemnified, held harmless, and defended by you for any data you send to me.
|
||||
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
|
||||
## Prerequisites to pull requests
|
||||
Please [create an issue](https://github.com/fxamacker/cbor/issues/new/choose), if one doesn't already exist, and describe your concern. You'll need a [GitHub account](https://github.com/signup/free) to do this.
|
||||
|
||||
Please [create an issue](https://github.com/fxamacker/cbor/issues/new/choose) before you begin work on a PR. The improvement may have already been considered, etc.
|
||||
|
||||
Pull requests have signing requirements and must not be anonymous. Exceptions are usually made for docs and CI scripts.
|
||||
|
||||
See the [Pull Request Template](https://github.com/fxamacker/cbor/blob/master/.github/pull_request_template.md) for details.
|
||||
|
||||
Pull requests have a greater chance of being approved if:
|
||||
- it does not reduce speed, increase memory use, reduce security, etc. for people not using the new option or feature.
|
||||
- it has > 97% code coverage.
|
||||
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 1024 bytes by email. If you want to send crash-producing CBOR data > 1024 bytes by email, please get my permission before sending it to me.
|
||||
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 at 97.8% for v1.2.)
|
||||
* Pull requests with small changes that are well-documented and easily understandable.
|
||||
* Sponsors, donations, bounties, subscriptions: I'd like to run uninterrupted fuzzing between releases on a server with dedicated CPUs (after v1.3 or v1.4.)
|
||||
|
||||
## Credits
|
||||
This guide used nlohmann/json contribution guidelines for inspiration as suggested in issue #22.
|
||||
|
||||
- This guide used nlohmann/json contribution guidelines for inspiration as suggested in issue #22.
|
||||
- Special thanks to @lukseven for pointing out the contribution guidelines didn't mention signing requirements.
|
||||
|
||||
4
LICENSE
4
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019-present Faye Amacker
|
||||
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
|
||||
@ -18,4 +18,4 @@ 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.
|
||||
SOFTWARE.
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
# 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.
|
||||
444
bench_test.go
444
bench_test.go
@ -5,8 +5,7 @@ package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
@ -107,68 +106,9 @@ type T3 struct {
|
||||
Mss map[string]string
|
||||
}
|
||||
|
||||
type ManyFieldsOneOmitEmpty struct {
|
||||
F01, F02, F03, F04, F05, F06, F07, F08, F09, F10, F11, F12, F13, F14, F15, F16 int
|
||||
F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31 int
|
||||
|
||||
F32 int `cbor:",omitempty"`
|
||||
}
|
||||
|
||||
type SomeFieldsOneOmitEmpty struct {
|
||||
F01, F02, F03, F04, F05, F06, F07 int
|
||||
|
||||
F08 int `cbor:",omitempty"`
|
||||
}
|
||||
|
||||
type ManyFieldsAllOmitEmpty struct {
|
||||
F01 int `cbor:",omitempty"`
|
||||
F02 int `cbor:",omitempty"`
|
||||
F03 int `cbor:",omitempty"`
|
||||
F04 int `cbor:",omitempty"`
|
||||
F05 int `cbor:",omitempty"`
|
||||
F06 int `cbor:",omitempty"`
|
||||
F07 int `cbor:",omitempty"`
|
||||
F08 int `cbor:",omitempty"`
|
||||
F09 int `cbor:",omitempty"`
|
||||
F10 int `cbor:",omitempty"`
|
||||
F11 int `cbor:",omitempty"`
|
||||
F12 int `cbor:",omitempty"`
|
||||
F13 int `cbor:",omitempty"`
|
||||
F14 int `cbor:",omitempty"`
|
||||
F15 int `cbor:",omitempty"`
|
||||
F16 int `cbor:",omitempty"`
|
||||
F17 int `cbor:",omitempty"`
|
||||
F18 int `cbor:",omitempty"`
|
||||
F19 int `cbor:",omitempty"`
|
||||
F20 int `cbor:",omitempty"`
|
||||
F21 int `cbor:",omitempty"`
|
||||
F22 int `cbor:",omitempty"`
|
||||
F23 int `cbor:",omitempty"`
|
||||
F24 int `cbor:",omitempty"`
|
||||
F25 int `cbor:",omitempty"`
|
||||
F26 int `cbor:",omitempty"`
|
||||
F27 int `cbor:",omitempty"`
|
||||
F28 int `cbor:",omitempty"`
|
||||
F29 int `cbor:",omitempty"`
|
||||
F30 int `cbor:",omitempty"`
|
||||
F31 int `cbor:",omitempty"`
|
||||
F32 int `cbor:",omitempty"`
|
||||
}
|
||||
|
||||
type SomeFieldsAllOmitEmpty struct {
|
||||
F01 int `cbor:",omitempty"`
|
||||
F02 int `cbor:",omitempty"`
|
||||
F03 int `cbor:",omitempty"`
|
||||
F04 int `cbor:",omitempty"`
|
||||
F05 int `cbor:",omitempty"`
|
||||
F06 int `cbor:",omitempty"`
|
||||
F07 int `cbor:",omitempty"`
|
||||
F08 int `cbor:",omitempty"`
|
||||
}
|
||||
|
||||
var decodeBenchmarks = []struct {
|
||||
name string
|
||||
data []byte
|
||||
cborData []byte
|
||||
decodeToTypes []reflect.Type
|
||||
}{
|
||||
{"bool", hexDecode("f5"), []reflect.Type{typeIntf, typeBool}}, // true
|
||||
@ -186,9 +126,9 @@ var decodeBenchmarks = []struct {
|
||||
}
|
||||
|
||||
var encodeBenchmarks = []struct {
|
||||
name string
|
||||
data []byte
|
||||
values []interface{}
|
||||
name string
|
||||
cborData []byte
|
||||
values []interface{}
|
||||
}{
|
||||
{"bool", hexDecode("f5"), []interface{}{true}},
|
||||
{"positive int", hexDecode("1bffffffffffffffff"), []interface{}{uint64(18446744073709551615)}},
|
||||
@ -210,7 +150,7 @@ func BenchmarkUnmarshal(b *testing.B) {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
vPtr := reflect.New(t).Interface()
|
||||
if err := Unmarshal(bm.data, vPtr); err != nil {
|
||||
if err := Unmarshal(bm.cborData, vPtr); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
}
|
||||
@ -219,7 +159,7 @@ func BenchmarkUnmarshal(b *testing.B) {
|
||||
}
|
||||
var moreBenchmarks = []struct {
|
||||
name string
|
||||
data []byte
|
||||
cborData []byte
|
||||
decodeToType reflect.Type
|
||||
}{
|
||||
// Unmarshal CBOR map with string key to map[string]interface{}.
|
||||
@ -263,7 +203,7 @@ func BenchmarkUnmarshal(b *testing.B) {
|
||||
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.data, vPtr); err != nil {
|
||||
if err := Unmarshal(bm.cborData, vPtr); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
}
|
||||
@ -271,54 +211,6 @@ func BenchmarkUnmarshal(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalFirst(b *testing.B) {
|
||||
// Random trailing data
|
||||
trailingData := hexDecode("4a6b0f4718c73f391091ea1c")
|
||||
for _, bm := range decodeBenchmarks {
|
||||
for _, t := range bm.decodeToTypes {
|
||||
name := "CBOR " + bm.name + " to Go " + t.String()
|
||||
if t.Kind() == reflect.Struct {
|
||||
name = "CBOR " + bm.name + " to Go " + t.Kind().String()
|
||||
}
|
||||
data := make([]byte, 0, len(bm.data)+len(trailingData))
|
||||
data = append(data, bm.data...)
|
||||
data = append(data, trailingData...)
|
||||
b.Run(name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
vPtr := reflect.New(t).Interface()
|
||||
if _, err := UnmarshalFirst(data, vPtr); err != nil {
|
||||
b.Fatal("UnmarshalFirst:", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalFirstViaDecoder(b *testing.B) {
|
||||
// Random trailing data
|
||||
trailingData := hexDecode("4a6b0f4718c73f391091ea1c")
|
||||
for _, bm := range decodeBenchmarks {
|
||||
for _, t := range bm.decodeToTypes {
|
||||
name := "CBOR " + bm.name + " to Go " + t.String()
|
||||
if t.Kind() == reflect.Struct {
|
||||
name = "CBOR " + bm.name + " to Go " + t.Kind().String()
|
||||
}
|
||||
data := make([]byte, 0, len(bm.data)+len(trailingData))
|
||||
data = append(data, bm.data...)
|
||||
data = append(data, trailingData...)
|
||||
b.Run(name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
vPtr := reflect.New(t).Interface()
|
||||
if err := NewDecoder(bytes.NewReader(data)).Decode(vPtr); err != nil {
|
||||
b.Fatal("UnmarshalDecoder:", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecode(b *testing.B) {
|
||||
for _, bm := range decodeBenchmarks {
|
||||
for _, t := range bm.decodeToTypes {
|
||||
@ -326,7 +218,7 @@ func BenchmarkDecode(b *testing.B) {
|
||||
if t.Kind() == reflect.Struct {
|
||||
name = "CBOR " + bm.name + " to Go " + t.Kind().String()
|
||||
}
|
||||
buf := bytes.NewReader(bm.data)
|
||||
buf := bytes.NewReader(bm.cborData)
|
||||
decoder := NewDecoder(buf)
|
||||
b.Run(name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
@ -342,16 +234,16 @@ func BenchmarkDecode(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkDecodeStream(b *testing.B) {
|
||||
var data []byte
|
||||
var cborData []byte
|
||||
for _, bm := range decodeBenchmarks {
|
||||
for i := 0; i < len(bm.decodeToTypes); i++ {
|
||||
data = append(data, bm.data...)
|
||||
cborData = append(cborData, bm.cborData...)
|
||||
}
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf := bytes.NewReader(data)
|
||||
buf := bytes.NewReader(cborData)
|
||||
decoder := NewDecoder(buf)
|
||||
for j := 0; j < rounds; j++ {
|
||||
for _, bm := range decodeBenchmarks {
|
||||
@ -455,17 +347,6 @@ func BenchmarkMarshal(b *testing.B) {
|
||||
}{
|
||||
{"Go map[string]interface{} to CBOR map", m1},
|
||||
{"Go struct to CBOR map", v1},
|
||||
{"Go struct many fields all omitempty all empty to CBOR map", ManyFieldsAllOmitEmpty{}},
|
||||
{"Go struct some fields all omitempty all empty to CBOR map", SomeFieldsAllOmitEmpty{}},
|
||||
{"Go struct many fields all omitempty all nonempty to CBOR map", ManyFieldsAllOmitEmpty{
|
||||
F01: 1, F02: 1, F03: 1, F04: 1, F05: 1, F06: 1, F07: 1, F08: 1, F09: 1, F10: 1, F11: 1, F12: 1, F13: 1, F14: 1, F15: 1, F16: 1,
|
||||
F17: 1, F18: 1, F19: 1, F20: 1, F21: 1, F22: 1, F23: 1, F24: 1, F25: 1, F26: 1, F27: 1, F28: 1, F29: 1, F30: 1, F31: 1, F32: 1,
|
||||
}},
|
||||
{"Go struct some fields all omitempty all nonempty to CBOR map", SomeFieldsAllOmitEmpty{
|
||||
F01: 1, F02: 1, F03: 1, F04: 1, F05: 1, F06: 1, F07: 1, F08: 1,
|
||||
}},
|
||||
{"Go struct many fields one omitempty to CBOR map", ManyFieldsOneOmitEmpty{}},
|
||||
{"Go struct some fields one omitempty to CBOR map", SomeFieldsOneOmitEmpty{}},
|
||||
{"Go map[int]interface{} to CBOR map", m2},
|
||||
{"Go struct keyasint to CBOR map", v2},
|
||||
{"Go []interface{} to CBOR map", slc},
|
||||
@ -499,11 +380,11 @@ func BenchmarkMarshalCanonical(b *testing.B) {
|
||||
N string `cbor:"n"`
|
||||
}
|
||||
for _, bm := range []struct {
|
||||
name string
|
||||
data []byte
|
||||
values []interface{}
|
||||
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"}, map[int]int{0: 0} /* single-entry map */}},
|
||||
{"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
|
||||
@ -534,29 +415,6 @@ func BenchmarkMarshalCanonical(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkNewEncoderEncode benchmarks NewEncoder() and Encode().
|
||||
func BenchmarkNewEncoderEncode(b *testing.B) {
|
||||
for _, bm := range encodeBenchmarks {
|
||||
for _, v := range bm.values {
|
||||
name := "Go " + reflect.TypeOf(v).String() + " to CBOR " + bm.name
|
||||
if reflect.TypeOf(v).Kind() == reflect.Struct {
|
||||
name = "Go " + reflect.TypeOf(v).Kind().String() + " to CBOR " + bm.name
|
||||
}
|
||||
b.Run(name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
encoder := NewEncoder(io.Discard)
|
||||
if err := encoder.Encode(v); err != nil {
|
||||
b.Fatal("Encode:", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkEncode benchmarks Encode(). It reuses same Encoder to exclude NewEncoder()
|
||||
// from the benchmark.
|
||||
func BenchmarkEncode(b *testing.B) {
|
||||
for _, bm := range encodeBenchmarks {
|
||||
for _, v := range bm.values {
|
||||
@ -565,7 +423,7 @@ func BenchmarkEncode(b *testing.B) {
|
||||
name = "Go " + reflect.TypeOf(v).Kind().String() + " to CBOR " + bm.name
|
||||
}
|
||||
b.Run(name, func(b *testing.B) {
|
||||
encoder := NewEncoder(io.Discard)
|
||||
encoder := NewEncoder(ioutil.Discard)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := encoder.Encode(v); err != nil {
|
||||
@ -579,7 +437,7 @@ func BenchmarkEncode(b *testing.B) {
|
||||
|
||||
func BenchmarkEncodeStream(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
encoder := NewEncoder(io.Discard)
|
||||
encoder := NewEncoder(ioutil.Discard)
|
||||
for i := 0; i < rounds; i++ {
|
||||
for _, bm := range encodeBenchmarks {
|
||||
for _, v := range bm.values {
|
||||
@ -595,8 +453,8 @@ func BenchmarkEncodeStream(b *testing.B) {
|
||||
func BenchmarkUnmarshalCOSE(b *testing.B) {
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.2
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
name string
|
||||
cborData []byte
|
||||
}{
|
||||
{"128-Bit Symmetric Key", hexDecode("a42050231f4c4d4d3051fdc2ec0a3851d5b3830104024c53796d6d6574726963313238030a")},
|
||||
{"256-Bit Symmetric Key", hexDecode("a4205820403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d795693880104024c53796d6d6574726963323536030a")},
|
||||
@ -606,7 +464,7 @@ func BenchmarkUnmarshalCOSE(b *testing.B) {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v coseKey
|
||||
if err := Unmarshal(tc.data, &v); err != nil {
|
||||
if err := Unmarshal(tc.cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
}
|
||||
@ -617,8 +475,8 @@ func BenchmarkUnmarshalCOSE(b *testing.B) {
|
||||
func BenchmarkMarshalCOSE(b *testing.B) {
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.2
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
name string
|
||||
cborData []byte
|
||||
}{
|
||||
{"128-Bit Symmetric Key", hexDecode("a42050231f4c4d4d3051fdc2ec0a3851d5b3830104024c53796d6d6574726963313238030a")},
|
||||
{"256-Bit Symmetric Key", hexDecode("a4205820403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d795693880104024c53796d6d6574726963323536030a")},
|
||||
@ -626,7 +484,7 @@ func BenchmarkMarshalCOSE(b *testing.B) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
var v coseKey
|
||||
if err := Unmarshal(tc.data, &v); err != nil {
|
||||
if err := Unmarshal(tc.cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
@ -641,10 +499,10 @@ func BenchmarkMarshalCOSE(b *testing.B) {
|
||||
|
||||
func BenchmarkUnmarshalCWTClaims(b *testing.B) {
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1
|
||||
data := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
|
||||
cborData := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v claims
|
||||
if err := Unmarshal(data, &v); err != nil {
|
||||
if err := Unmarshal(cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
}
|
||||
@ -652,11 +510,11 @@ func BenchmarkUnmarshalCWTClaims(b *testing.B) {
|
||||
|
||||
func BenchmarkUnmarshalCWTClaimsWithDupMapKeyOpt(b *testing.B) {
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1
|
||||
data := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
|
||||
cborData := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
|
||||
dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v claims
|
||||
if err := dm.Unmarshal(data, &v); err != nil {
|
||||
if err := dm.Unmarshal(cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
}
|
||||
@ -664,9 +522,9 @@ func BenchmarkUnmarshalCWTClaimsWithDupMapKeyOpt(b *testing.B) {
|
||||
|
||||
func BenchmarkMarshalCWTClaims(b *testing.B) {
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1
|
||||
data := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
|
||||
cborData := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
|
||||
var v claims
|
||||
if err := Unmarshal(data, &v); err != nil {
|
||||
if err := Unmarshal(cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
@ -678,10 +536,10 @@ func BenchmarkMarshalCWTClaims(b *testing.B) {
|
||||
|
||||
func BenchmarkUnmarshalSenML(b *testing.B) {
|
||||
// Data from https://tools.ietf.org/html/rfc8428#section-6
|
||||
data := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333")
|
||||
cborData := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333")
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v []SenMLRecord
|
||||
if err := Unmarshal(data, &v); err != nil {
|
||||
if err := Unmarshal(cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
}
|
||||
@ -689,9 +547,9 @@ func BenchmarkUnmarshalSenML(b *testing.B) {
|
||||
|
||||
func BenchmarkMarshalSenML(b *testing.B) {
|
||||
// Data from https://tools.ietf.org/html/rfc8428#section-6
|
||||
data := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333")
|
||||
cborData := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333")
|
||||
var v []SenMLRecord
|
||||
if err := Unmarshal(data, &v); err != nil {
|
||||
if err := Unmarshal(cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
@ -703,9 +561,9 @@ func BenchmarkMarshalSenML(b *testing.B) {
|
||||
|
||||
func BenchmarkMarshalSenMLShortestFloat16(b *testing.B) {
|
||||
// Data from https://tools.ietf.org/html/rfc8428#section-6
|
||||
data := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333")
|
||||
cborData := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333")
|
||||
var v []SenMLRecord
|
||||
if err := Unmarshal(data, &v); err != nil {
|
||||
if err := Unmarshal(cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
em, _ := EncOptions{ShortestFloat: ShortestFloat16}.EncMode()
|
||||
@ -718,10 +576,10 @@ func BenchmarkMarshalSenMLShortestFloat16(b *testing.B) {
|
||||
|
||||
func BenchmarkUnmarshalWebAuthn(b *testing.B) {
|
||||
// Data generated from Yubico security key
|
||||
data := hexDecode("a363666d74686669646f2d7532666761747453746d74a26373696758483046022100e7ab373cfbd99fcd55fd59b0f6f17fef5b77a20ddec3db7f7e4d55174e366236022100828336b4822125fb56541fb14a8a273876acd339395ec2dad95cf41c1dd2a9ae637835638159024e3082024a30820132a0030201020204124a72fe300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a302c312a302806035504030c2159756269636f205532462045452053657269616c203234393431343937323135383059301306072a8648ce3d020106082a8648ce3d030107034200043d8b1bbd2fcbf6086e107471601468484153c1c6d3b4b68a5e855e6e40757ee22bcd8988bf3befd7cdf21cb0bf5d7a150d844afe98103c6c6607d9faae287c02a33b3039302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e313013060b2b0601040182e51c020101040403020520300d06092a864886f70d01010b05000382010100a14f1eea0076f6b8476a10a2be72e60d0271bb465b2dfbfc7c1bd12d351989917032631d795d097fa30a26a325634e85721bc2d01a86303f6bc075e5997319e122148b0496eec8d1f4f94cf4110de626c289443d1f0f5bbb239ca13e81d1d5aa9df5af8e36126475bfc23af06283157252762ff68879bcf0ef578d55d67f951b4f32b63c8aea5b0f99c67d7d814a7ff5a6f52df83e894a3a5d9c8b82e7f8bc8daf4c80175ff8972fda79333ec465d806eacc948f1bab22045a95558a48c20226dac003d41fbc9e05ea28a6bb5e10a49de060a0a4f6a2676a34d68c4abe8c61874355b9027e828ca9e064b002d62e8d8cf0744921753d35e3c87c5d5779453e7768617574684461746158c449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976341000000000000000000000000000000000000000000408903fd7dfd2c9770e98cae0123b13a2c27828a106349bc6277140e7290b7e9eb7976aa3c04ed347027caf7da3a2fa76304751c02208acfc4e7fc6c7ebbc375c8a5010203262001215820ad7f7992c335b90d882b2802061b97a4fabca7e2ee3e7a51e728b8055e4eb9c7225820e0966ba7005987fece6f0e0e13447aa98cec248e4000a594b01b74c1cb1d40b3")
|
||||
cborData := hexDecode("a363666d74686669646f2d7532666761747453746d74a26373696758483046022100e7ab373cfbd99fcd55fd59b0f6f17fef5b77a20ddec3db7f7e4d55174e366236022100828336b4822125fb56541fb14a8a273876acd339395ec2dad95cf41c1dd2a9ae637835638159024e3082024a30820132a0030201020204124a72fe300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a302c312a302806035504030c2159756269636f205532462045452053657269616c203234393431343937323135383059301306072a8648ce3d020106082a8648ce3d030107034200043d8b1bbd2fcbf6086e107471601468484153c1c6d3b4b68a5e855e6e40757ee22bcd8988bf3befd7cdf21cb0bf5d7a150d844afe98103c6c6607d9faae287c02a33b3039302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e313013060b2b0601040182e51c020101040403020520300d06092a864886f70d01010b05000382010100a14f1eea0076f6b8476a10a2be72e60d0271bb465b2dfbfc7c1bd12d351989917032631d795d097fa30a26a325634e85721bc2d01a86303f6bc075e5997319e122148b0496eec8d1f4f94cf4110de626c289443d1f0f5bbb239ca13e81d1d5aa9df5af8e36126475bfc23af06283157252762ff68879bcf0ef578d55d67f951b4f32b63c8aea5b0f99c67d7d814a7ff5a6f52df83e894a3a5d9c8b82e7f8bc8daf4c80175ff8972fda79333ec465d806eacc948f1bab22045a95558a48c20226dac003d41fbc9e05ea28a6bb5e10a49de060a0a4f6a2676a34d68c4abe8c61874355b9027e828ca9e064b002d62e8d8cf0744921753d35e3c87c5d5779453e7768617574684461746158c449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976341000000000000000000000000000000000000000000408903fd7dfd2c9770e98cae0123b13a2c27828a106349bc6277140e7290b7e9eb7976aa3c04ed347027caf7da3a2fa76304751c02208acfc4e7fc6c7ebbc375c8a5010203262001215820ad7f7992c335b90d882b2802061b97a4fabca7e2ee3e7a51e728b8055e4eb9c7225820e0966ba7005987fece6f0e0e13447aa98cec248e4000a594b01b74c1cb1d40b3")
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v attestationObject
|
||||
if err := Unmarshal(data, &v); err != nil {
|
||||
if err := Unmarshal(cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
}
|
||||
@ -729,9 +587,9 @@ func BenchmarkUnmarshalWebAuthn(b *testing.B) {
|
||||
|
||||
func BenchmarkMarshalWebAuthn(b *testing.B) {
|
||||
// Data generated from Yubico security key
|
||||
data := hexDecode("a363666d74686669646f2d7532666761747453746d74a26373696758483046022100e7ab373cfbd99fcd55fd59b0f6f17fef5b77a20ddec3db7f7e4d55174e366236022100828336b4822125fb56541fb14a8a273876acd339395ec2dad95cf41c1dd2a9ae637835638159024e3082024a30820132a0030201020204124a72fe300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a302c312a302806035504030c2159756269636f205532462045452053657269616c203234393431343937323135383059301306072a8648ce3d020106082a8648ce3d030107034200043d8b1bbd2fcbf6086e107471601468484153c1c6d3b4b68a5e855e6e40757ee22bcd8988bf3befd7cdf21cb0bf5d7a150d844afe98103c6c6607d9faae287c02a33b3039302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e313013060b2b0601040182e51c020101040403020520300d06092a864886f70d01010b05000382010100a14f1eea0076f6b8476a10a2be72e60d0271bb465b2dfbfc7c1bd12d351989917032631d795d097fa30a26a325634e85721bc2d01a86303f6bc075e5997319e122148b0496eec8d1f4f94cf4110de626c289443d1f0f5bbb239ca13e81d1d5aa9df5af8e36126475bfc23af06283157252762ff68879bcf0ef578d55d67f951b4f32b63c8aea5b0f99c67d7d814a7ff5a6f52df83e894a3a5d9c8b82e7f8bc8daf4c80175ff8972fda79333ec465d806eacc948f1bab22045a95558a48c20226dac003d41fbc9e05ea28a6bb5e10a49de060a0a4f6a2676a34d68c4abe8c61874355b9027e828ca9e064b002d62e8d8cf0744921753d35e3c87c5d5779453e7768617574684461746158c449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976341000000000000000000000000000000000000000000408903fd7dfd2c9770e98cae0123b13a2c27828a106349bc6277140e7290b7e9eb7976aa3c04ed347027caf7da3a2fa76304751c02208acfc4e7fc6c7ebbc375c8a5010203262001215820ad7f7992c335b90d882b2802061b97a4fabca7e2ee3e7a51e728b8055e4eb9c7225820e0966ba7005987fece6f0e0e13447aa98cec248e4000a594b01b74c1cb1d40b3")
|
||||
cborData := hexDecode("a363666d74686669646f2d7532666761747453746d74a26373696758483046022100e7ab373cfbd99fcd55fd59b0f6f17fef5b77a20ddec3db7f7e4d55174e366236022100828336b4822125fb56541fb14a8a273876acd339395ec2dad95cf41c1dd2a9ae637835638159024e3082024a30820132a0030201020204124a72fe300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a302c312a302806035504030c2159756269636f205532462045452053657269616c203234393431343937323135383059301306072a8648ce3d020106082a8648ce3d030107034200043d8b1bbd2fcbf6086e107471601468484153c1c6d3b4b68a5e855e6e40757ee22bcd8988bf3befd7cdf21cb0bf5d7a150d844afe98103c6c6607d9faae287c02a33b3039302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e313013060b2b0601040182e51c020101040403020520300d06092a864886f70d01010b05000382010100a14f1eea0076f6b8476a10a2be72e60d0271bb465b2dfbfc7c1bd12d351989917032631d795d097fa30a26a325634e85721bc2d01a86303f6bc075e5997319e122148b0496eec8d1f4f94cf4110de626c289443d1f0f5bbb239ca13e81d1d5aa9df5af8e36126475bfc23af06283157252762ff68879bcf0ef578d55d67f951b4f32b63c8aea5b0f99c67d7d814a7ff5a6f52df83e894a3a5d9c8b82e7f8bc8daf4c80175ff8972fda79333ec465d806eacc948f1bab22045a95558a48c20226dac003d41fbc9e05ea28a6bb5e10a49de060a0a4f6a2676a34d68c4abe8c61874355b9027e828ca9e064b002d62e8d8cf0744921753d35e3c87c5d5779453e7768617574684461746158c449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976341000000000000000000000000000000000000000000408903fd7dfd2c9770e98cae0123b13a2c27828a106349bc6277140e7290b7e9eb7976aa3c04ed347027caf7da3a2fa76304751c02208acfc4e7fc6c7ebbc375c8a5010203262001215820ad7f7992c335b90d882b2802061b97a4fabca7e2ee3e7a51e728b8055e4eb9c7225820e0966ba7005987fece6f0e0e13447aa98cec248e4000a594b01b74c1cb1d40b3")
|
||||
var v attestationObject
|
||||
if err := Unmarshal(data, &v); err != nil {
|
||||
if err := Unmarshal(cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
@ -743,11 +601,11 @@ func BenchmarkMarshalWebAuthn(b *testing.B) {
|
||||
|
||||
func BenchmarkUnmarshalCOSEMAC(b *testing.B) {
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
|
||||
data := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
|
||||
cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v macedCOSE
|
||||
if err := Unmarshal(data, &v); err != nil {
|
||||
if err := Unmarshal(cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal:", err)
|
||||
}
|
||||
}
|
||||
@ -755,7 +613,7 @@ func BenchmarkUnmarshalCOSEMAC(b *testing.B) {
|
||||
|
||||
func BenchmarkUnmarshalCOSEMACWithTag(b *testing.B) {
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
|
||||
data := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
|
||||
cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
|
||||
|
||||
// Register tag CBOR Web Token (CWT) 61 and COSE_Mac0 17 with macedCOSE type
|
||||
tags := NewTagSet()
|
||||
@ -768,17 +626,17 @@ func BenchmarkUnmarshalCOSEMACWithTag(b *testing.B) {
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v macedCOSE
|
||||
if err := dm.Unmarshal(data, &v); err != nil {
|
||||
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
|
||||
data := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
|
||||
cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
|
||||
|
||||
var v macedCOSE
|
||||
if err := Unmarshal(data, &v); err != nil {
|
||||
if err := Unmarshal(cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal():", err)
|
||||
}
|
||||
|
||||
@ -791,7 +649,7 @@ func BenchmarkMarshalCOSEMAC(b *testing.B) {
|
||||
|
||||
func BenchmarkMarshalCOSEMACWithTag(b *testing.B) {
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
|
||||
data := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
|
||||
cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
|
||||
|
||||
// Register tag CBOR Web Token (CWT) 61 and COSE_Mac0 17 with macedCOSE type
|
||||
tags := NewTagSet()
|
||||
@ -804,7 +662,7 @@ func BenchmarkMarshalCOSEMACWithTag(b *testing.B) {
|
||||
em, _ := EncOptions{}.EncModeWithTags(tags)
|
||||
|
||||
var v macedCOSE
|
||||
if err := dm.Unmarshal(data, &v); err != nil {
|
||||
if err := dm.Unmarshal(cborData, &v); err != nil {
|
||||
b.Fatal("Unmarshal():", err)
|
||||
}
|
||||
|
||||
@ -814,209 +672,3 @@ func BenchmarkMarshalCOSEMACWithTag(b *testing.B) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalMapToStruct(b *testing.B) {
|
||||
type S struct {
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M bool
|
||||
}
|
||||
|
||||
var (
|
||||
allKnownFields = hexDecode("ad6141f56142f56143f56144f56145f56146f56147f56148f56149f5614af5614bf5614cf5614df5") // {"A": true, ... "M": true }
|
||||
allKnownDuplicateFields = hexDecode("ad6141f56141f56141f56141f56141f56141f56141f56141f56141f56141f56141f56141f56141f5") // {"A": true, "A": true, "A": true, ...}
|
||||
allUnknownFields = hexDecode("ad614ef5614ff56150f56151f56152f56153f56154f56155f56156f56157f56158f56159f5615af5") // {"N": true, ... "Z": true }
|
||||
allUnknownDuplicateFields = hexDecode("ad614ef5614ef5614ef5614ef5614ef5614ef5614ef5614ef5614ef5614ef5614ef5614ef5614ef5") // {"N": true, "N": true, "N": true, ...}
|
||||
)
|
||||
|
||||
type ManyFields struct {
|
||||
AA, AB, AC, AD, AE, AF, AG, AH, AI, AJ, AK, AL, AM, AN, AO, AP, AQ, AR, AS, AT, AU, AV, AW, AX, AY, AZ bool
|
||||
BA, BB, BC, BD, BE, BF, BG, BH, BI, BJ, BK, BL, BM, BN, BO, BP, BQ, BR, BS, BT, BU, BV, BW, BX, BY, BZ bool
|
||||
CA, CB, CC, CD, CE, CF, CG, CH, CI, CJ, CK, CL, CM, CN, CO, CP, CQ, CR, CS, CT, CU, CV, CW, CX, CY, CZ bool
|
||||
DA, DB, DC, DD, DE, DF, DG, DH, DI, DJ, DK, DL, DM, DN, DO, DP, DQ, DR, DS, DT, DU, DV, DW, DX, DY, DZ bool
|
||||
}
|
||||
var manyFieldsOneKeyPerField []byte
|
||||
{
|
||||
// An EncOption that accepts a function to sort or shuffle keys might be useful for
|
||||
// cases like this. Here we are manually encoding the fields in reverse order to
|
||||
// target worst-case key-to-field matching.
|
||||
rt := reflect.TypeOf(ManyFields{})
|
||||
var buf bytes.Buffer
|
||||
if rt.NumField() > 255 {
|
||||
b.Fatalf("invalid test assumption: ManyFields expected to have no more than 255 fields, has %d", rt.NumField())
|
||||
}
|
||||
buf.WriteByte(0xb8)
|
||||
buf.WriteByte(byte(rt.NumField()))
|
||||
for i := rt.NumField() - 1; i >= 0; i-- { // backwards
|
||||
f := rt.Field(i)
|
||||
if len(f.Name) > 23 {
|
||||
b.Fatalf("invalid test assumption: field name %q longer than 23 bytes", f.Name)
|
||||
}
|
||||
buf.WriteByte(byte(0x60 + len(f.Name)))
|
||||
buf.WriteString(f.Name)
|
||||
buf.WriteByte(0xf5) // true
|
||||
}
|
||||
manyFieldsOneKeyPerField = buf.Bytes()
|
||||
}
|
||||
|
||||
type input struct {
|
||||
name string
|
||||
data []byte
|
||||
into interface{}
|
||||
reject bool
|
||||
}
|
||||
|
||||
for _, tc := range []*struct {
|
||||
name string
|
||||
opts DecOptions
|
||||
inputs []input
|
||||
}{
|
||||
{
|
||||
name: "default options",
|
||||
opts: DecOptions{},
|
||||
inputs: []input{
|
||||
{
|
||||
name: "all known fields",
|
||||
data: allKnownFields,
|
||||
into: S{},
|
||||
reject: false,
|
||||
},
|
||||
{
|
||||
name: "all known duplicate fields",
|
||||
data: allKnownDuplicateFields,
|
||||
into: S{},
|
||||
reject: false,
|
||||
},
|
||||
{
|
||||
name: "all unknown fields",
|
||||
data: allUnknownFields,
|
||||
into: S{},
|
||||
reject: false,
|
||||
},
|
||||
{
|
||||
name: "all unknown duplicate fields",
|
||||
data: allUnknownDuplicateFields,
|
||||
into: S{},
|
||||
reject: false,
|
||||
},
|
||||
{
|
||||
name: "many fields one key per field",
|
||||
data: manyFieldsOneKeyPerField,
|
||||
into: ManyFields{},
|
||||
reject: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reject unknown",
|
||||
opts: DecOptions{ExtraReturnErrors: ExtraDecErrorUnknownField},
|
||||
inputs: []input{
|
||||
{
|
||||
name: "all known fields",
|
||||
data: allKnownFields,
|
||||
into: S{},
|
||||
reject: false,
|
||||
},
|
||||
{
|
||||
name: "all known duplicate fields",
|
||||
data: allKnownDuplicateFields,
|
||||
into: S{},
|
||||
reject: false,
|
||||
},
|
||||
{
|
||||
name: "all unknown fields",
|
||||
data: allUnknownFields,
|
||||
into: S{},
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
name: "all unknown duplicate fields",
|
||||
data: allUnknownDuplicateFields,
|
||||
into: S{},
|
||||
reject: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reject duplicate",
|
||||
opts: DecOptions{DupMapKey: DupMapKeyEnforcedAPF},
|
||||
inputs: []input{
|
||||
{
|
||||
name: "all known fields",
|
||||
data: allKnownFields,
|
||||
into: S{},
|
||||
reject: false,
|
||||
},
|
||||
{
|
||||
name: "all known duplicate fields",
|
||||
data: allKnownDuplicateFields,
|
||||
into: S{},
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
name: "all unknown fields",
|
||||
data: allUnknownFields,
|
||||
into: S{},
|
||||
reject: false,
|
||||
},
|
||||
{
|
||||
name: "all unknown duplicate fields",
|
||||
data: allUnknownDuplicateFields,
|
||||
into: S{},
|
||||
reject: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reject unknown and duplicate",
|
||||
opts: DecOptions{
|
||||
DupMapKey: DupMapKeyEnforcedAPF,
|
||||
ExtraReturnErrors: ExtraDecErrorUnknownField,
|
||||
},
|
||||
inputs: []input{
|
||||
{
|
||||
name: "all known fields",
|
||||
data: allKnownFields,
|
||||
into: S{},
|
||||
reject: false,
|
||||
},
|
||||
{
|
||||
name: "all known duplicate fields",
|
||||
data: allKnownDuplicateFields,
|
||||
into: S{},
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
name: "all unknown fields",
|
||||
data: allUnknownFields,
|
||||
into: S{},
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
name: "all unknown duplicate fields",
|
||||
data: allUnknownDuplicateFields,
|
||||
into: S{},
|
||||
reject: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
for _, in := range tc.inputs {
|
||||
b.Run(fmt.Sprintf("%s/%s", tc.name, in.name), func(b *testing.B) {
|
||||
dm, err := tc.opts.DecMode()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
dst := reflect.New(reflect.TypeOf(in.into)).Interface()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := dm.Unmarshal(in.data, dst); !in.reject && err != nil {
|
||||
b.Fatalf("unexpected error: %v", err)
|
||||
} else if in.reject && err == nil {
|
||||
b.Fatal("expected non-nil error")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ByteString represents CBOR byte string (major type 2). ByteString can be used
|
||||
// when using a Go []byte is not possible or convenient. For example, Go doesn't
|
||||
// allow []byte as map key, so ByteString can be used to support data formats
|
||||
// having CBOR map with byte string keys. ByteString can also be used to
|
||||
// encode invalid UTF-8 string as CBOR byte string.
|
||||
// See DecOption.MapKeyByteStringMode for more details.
|
||||
type ByteString string
|
||||
|
||||
// Bytes returns bytes representing ByteString.
|
||||
func (bs ByteString) Bytes() []byte {
|
||||
return []byte(bs)
|
||||
}
|
||||
|
||||
// MarshalCBOR encodes ByteString as CBOR byte string (major type 2).
|
||||
func (bs ByteString) MarshalCBOR() ([]byte, error) {
|
||||
e := getEncodeBuffer()
|
||||
defer putEncodeBuffer(e)
|
||||
|
||||
// Encode length
|
||||
encodeHead(e, byte(cborTypeByteString), uint64(len(bs)))
|
||||
|
||||
// Encode data
|
||||
buf := make([]byte, e.Len()+len(bs))
|
||||
n := copy(buf, e.Bytes())
|
||||
copy(buf[n:], bs)
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// UnmarshalCBOR decodes CBOR byte string (major type 2) to ByteString.
|
||||
// Decoding CBOR null and CBOR undefined sets ByteString to be empty.
|
||||
func (bs *ByteString) UnmarshalCBOR(data []byte) error {
|
||||
if bs == nil {
|
||||
return errors.New("cbor.ByteString: UnmarshalCBOR on nil pointer")
|
||||
}
|
||||
|
||||
// Decoding CBOR null and CBOR undefined to ByteString resets data.
|
||||
// This behavior is similar to decoding CBOR null and CBOR undefined to []byte.
|
||||
if len(data) == 1 && (data[0] == 0xf6 || data[0] == 0xf7) {
|
||||
*bs = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
d := decoder{data: data, dm: defaultDecMode}
|
||||
|
||||
// Check if CBOR data type is byte string
|
||||
if typ := d.nextCBORType(); typ != cborTypeByteString {
|
||||
return &UnmarshalTypeError{CBORType: typ.String(), GoType: typeByteString.String()}
|
||||
}
|
||||
|
||||
b, _ := d.parseByteString()
|
||||
*bs = ByteString(b)
|
||||
return nil
|
||||
}
|
||||
@ -1,101 +0,0 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
package cbor
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestByteString(t *testing.T) {
|
||||
type s1 struct {
|
||||
A ByteString `cbor:"a"`
|
||||
}
|
||||
type s2 struct {
|
||||
A *ByteString `cbor:"a"`
|
||||
}
|
||||
type s3 struct {
|
||||
A ByteString `cbor:"a,omitempty"`
|
||||
}
|
||||
type s4 struct {
|
||||
A *ByteString `cbor:"a,omitempty"`
|
||||
}
|
||||
|
||||
emptybs := ByteString("")
|
||||
bs := ByteString("\x01\x02\x03\x04")
|
||||
|
||||
testCases := []roundTripTest{
|
||||
{
|
||||
name: "empty",
|
||||
obj: emptybs,
|
||||
wantCborData: hexDecode("40"),
|
||||
},
|
||||
{
|
||||
name: "not empty",
|
||||
obj: bs,
|
||||
wantCborData: hexDecode("4401020304"),
|
||||
},
|
||||
{
|
||||
name: "array",
|
||||
obj: []ByteString{bs},
|
||||
wantCborData: hexDecode("814401020304"),
|
||||
},
|
||||
{
|
||||
name: "map with ByteString key",
|
||||
obj: map[ByteString]bool{bs: true},
|
||||
wantCborData: hexDecode("a14401020304f5"),
|
||||
},
|
||||
{
|
||||
name: "empty ByteString field",
|
||||
obj: s1{},
|
||||
wantCborData: hexDecode("a1616140"),
|
||||
},
|
||||
{
|
||||
name: "not empty ByteString field",
|
||||
obj: s1{A: bs},
|
||||
wantCborData: hexDecode("a161614401020304"),
|
||||
},
|
||||
{
|
||||
name: "nil *ByteString field",
|
||||
obj: s2{},
|
||||
wantCborData: hexDecode("a16161f6"),
|
||||
},
|
||||
{
|
||||
name: "empty *ByteString field",
|
||||
obj: s2{A: &emptybs},
|
||||
wantCborData: hexDecode("a1616140"),
|
||||
},
|
||||
{
|
||||
name: "not empty *ByteString field",
|
||||
obj: s2{A: &bs},
|
||||
wantCborData: hexDecode("a161614401020304"),
|
||||
},
|
||||
{
|
||||
name: "empty ByteString field with omitempty option",
|
||||
obj: s3{},
|
||||
wantCborData: hexDecode("a0"),
|
||||
},
|
||||
{
|
||||
name: "not empty ByteString field with omitempty option",
|
||||
obj: s3{A: bs},
|
||||
wantCborData: hexDecode("a161614401020304"),
|
||||
},
|
||||
{
|
||||
name: "nil *ByteString field with omitempty option",
|
||||
obj: s4{},
|
||||
wantCborData: hexDecode("a0"),
|
||||
},
|
||||
{
|
||||
name: "empty *ByteString field with omitempty option",
|
||||
obj: s4{A: &emptybs},
|
||||
wantCborData: hexDecode("a1616140"),
|
||||
},
|
||||
{
|
||||
name: "not empty *ByteString field with omitempty option",
|
||||
obj: s4{A: &bs},
|
||||
wantCborData: hexDecode("a161614401020304"),
|
||||
},
|
||||
}
|
||||
|
||||
em, _ := EncOptions{}.EncMode()
|
||||
dm, _ := DecOptions{}.DecMode()
|
||||
testRoundTrip(t, testCases, em, dm)
|
||||
}
|
||||
173
cache.go
173
cache.go
@ -6,7 +6,6 @@ package cbor
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
@ -14,15 +13,10 @@ import (
|
||||
"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
|
||||
encodeFuncCache sync.Map // map[reflect.Type]encodeFunc
|
||||
typeInfoCache sync.Map // map[reflect.Type]*typeInfo
|
||||
)
|
||||
|
||||
@ -32,7 +26,6 @@ const (
|
||||
specialTypeNone specialType = iota
|
||||
specialTypeUnmarshalerIface
|
||||
specialTypeEmptyIface
|
||||
specialTypeIface
|
||||
specialTypeTag
|
||||
specialTypeTime
|
||||
)
|
||||
@ -59,12 +52,8 @@ func newTypeInfo(t reflect.Type) *typeInfo {
|
||||
tInfo.nonPtrType = t
|
||||
tInfo.nonPtrKind = k
|
||||
|
||||
if k == reflect.Interface {
|
||||
if t.NumMethod() == 0 {
|
||||
tInfo.spclType = specialTypeEmptyIface
|
||||
} else {
|
||||
tInfo.spclType = specialTypeIface
|
||||
}
|
||||
if k == reflect.Interface && t.NumMethod() == 0 {
|
||||
tInfo.spclType = specialTypeEmptyIface
|
||||
} else if t == typeTag {
|
||||
tInfo.spclType = specialTypeTag
|
||||
} else if t == typeTime {
|
||||
@ -85,25 +74,9 @@ func newTypeInfo(t reflect.Type) *typeInfo {
|
||||
}
|
||||
|
||||
type decodingStructType struct {
|
||||
fields fields
|
||||
fieldIndicesByName map[string]int
|
||||
err error
|
||||
toArray bool
|
||||
}
|
||||
|
||||
// The stdlib errors.Join was introduced in Go 1.20, and we still support Go 1.17, so instead,
|
||||
// here's a very basic implementation of an aggregated error.
|
||||
type multierror []error
|
||||
|
||||
func (m multierror) Error() string {
|
||||
var sb strings.Builder
|
||||
for i, err := range m {
|
||||
sb.WriteString(err.Error())
|
||||
if i < len(m)-1 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
fields fields
|
||||
err error
|
||||
toArray bool
|
||||
}
|
||||
|
||||
func getDecodingStructType(t reflect.Type) *decodingStructType {
|
||||
@ -115,12 +88,12 @@ func getDecodingStructType(t reflect.Type) *decodingStructType {
|
||||
|
||||
toArray := hasToArrayOption(structOptions)
|
||||
|
||||
var errs []error
|
||||
var err error
|
||||
for i := 0; i < len(flds); i++ {
|
||||
if flds[i].keyAsInt {
|
||||
nameAsInt, numErr := strconv.Atoi(flds[i].name)
|
||||
if numErr != nil {
|
||||
errs = append(errs, errors.New("cbor: failed to parse field name \""+flds[i].name+"\" to int ("+numErr.Error()+")"))
|
||||
err = errors.New("cbor: failed to parse field name \"" + flds[i].name + "\" to int (" + numErr.Error() + ")")
|
||||
break
|
||||
}
|
||||
flds[i].nameAsInt = int64(nameAsInt)
|
||||
@ -129,58 +102,29 @@ func getDecodingStructType(t reflect.Type) *decodingStructType {
|
||||
flds[i].typInfo = getTypeInfo(flds[i].typ)
|
||||
}
|
||||
|
||||
fieldIndicesByName := make(map[string]int, len(flds))
|
||||
for i, fld := range flds {
|
||||
if _, ok := fieldIndicesByName[fld.name]; ok {
|
||||
errs = append(errs, fmt.Errorf("cbor: two or more fields of %v have the same name %q", t, fld.name))
|
||||
continue
|
||||
}
|
||||
fieldIndicesByName[fld.name] = i
|
||||
}
|
||||
|
||||
var err error
|
||||
{
|
||||
var multi multierror
|
||||
for _, each := range errs {
|
||||
if each != nil {
|
||||
multi = append(multi, each)
|
||||
}
|
||||
}
|
||||
if len(multi) == 1 {
|
||||
err = multi[0]
|
||||
} else if len(multi) > 1 {
|
||||
err = multi
|
||||
}
|
||||
}
|
||||
|
||||
structType := &decodingStructType{
|
||||
fields: flds,
|
||||
fieldIndicesByName: fieldIndicesByName,
|
||||
err: err,
|
||||
toArray: toArray,
|
||||
}
|
||||
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
|
||||
fields fields
|
||||
bytewiseFields fields
|
||||
lengthFirstFields fields
|
||||
err error
|
||||
toArray bool
|
||||
omitEmpty bool
|
||||
hasAnonymousField bool
|
||||
}
|
||||
|
||||
func (st *encodingStructType) getFields(em *encMode) fields {
|
||||
switch em.sort {
|
||||
case SortNone, SortFastShuffle:
|
||||
if em.sort == SortNone {
|
||||
return st.fields
|
||||
case SortLengthFirst:
|
||||
return st.lengthFirstFields
|
||||
default:
|
||||
return st.bytewiseFields
|
||||
}
|
||||
if em.sort == SortLengthFirst {
|
||||
return st.lengthFirstFields
|
||||
}
|
||||
return st.bytewiseFields
|
||||
}
|
||||
|
||||
type bytewiseFieldSorter struct {
|
||||
@ -218,10 +162,9 @@ func (x *lengthFirstFieldSorter) Less(i, j int) bool {
|
||||
return bytes.Compare(x.fields[i].cborName, x.fields[j].cborName) <= 0
|
||||
}
|
||||
|
||||
func getEncodingStructType(t reflect.Type) (*encodingStructType, error) {
|
||||
func getEncodingStructType(t reflect.Type) *encodingStructType {
|
||||
if v, _ := encodingStructTypeCache.Load(t); v != nil {
|
||||
structType := v.(*encodingStructType)
|
||||
return structType, structType.err
|
||||
return v.(*encodingStructType)
|
||||
}
|
||||
|
||||
flds, structOptions := getFields(t)
|
||||
@ -231,13 +174,14 @@ func getEncodingStructType(t reflect.Type) (*encodingStructType, error) {
|
||||
}
|
||||
|
||||
var err error
|
||||
var omitEmpty bool
|
||||
var hasAnonymousField bool
|
||||
var hasKeyAsInt bool
|
||||
var hasKeyAsStr bool
|
||||
var omitEmptyIdx []int
|
||||
e := getEncodeBuffer()
|
||||
e := getEncodeState()
|
||||
for i := 0; i < len(flds); i++ {
|
||||
// Get field's encodeFunc
|
||||
flds[i].ef, flds[i].ief = getEncodeFunc(flds[i].typ)
|
||||
flds[i].ef = getEncodeFunc(flds[i].typ)
|
||||
if flds[i].ef == nil {
|
||||
err = &UnsupportedTypeError{t}
|
||||
break
|
||||
@ -269,30 +213,25 @@ func getEncodingStructType(t reflect.Type) (*encodingStructType, error) {
|
||||
copy(flds[i].cborName[n:], flds[i].name)
|
||||
e.Reset()
|
||||
|
||||
// If cborName contains a text string, then cborNameByteString contains a
|
||||
// string that has the byte string major type but is otherwise identical to
|
||||
// cborName.
|
||||
flds[i].cborNameByteString = make([]byte, len(flds[i].cborName))
|
||||
copy(flds[i].cborNameByteString, flds[i].cborName)
|
||||
// Reset encoded CBOR type to byte string, preserving the "additional
|
||||
// information" bits:
|
||||
flds[i].cborNameByteString[0] = byte(cborTypeByteString) |
|
||||
getAdditionalInformation(flds[i].cborNameByteString[0])
|
||||
|
||||
hasKeyAsStr = true
|
||||
}
|
||||
|
||||
// Check if field is from embedded struct
|
||||
if len(flds[i].idx) > 1 {
|
||||
hasAnonymousField = true
|
||||
}
|
||||
|
||||
// Check if field can be omitted when empty
|
||||
if flds[i].omitEmpty {
|
||||
omitEmptyIdx = append(omitEmptyIdx, i)
|
||||
omitEmpty = true
|
||||
}
|
||||
}
|
||||
putEncodeBuffer(e)
|
||||
putEncodeState(e)
|
||||
|
||||
if err != nil {
|
||||
structType := &encodingStructType{err: err}
|
||||
encodingStructTypeCache.Store(t, structType)
|
||||
return structType, structType.err
|
||||
return structType
|
||||
}
|
||||
|
||||
// Sort fields by canonical order
|
||||
@ -308,43 +247,49 @@ func getEncodingStructType(t reflect.Type) (*encodingStructType, error) {
|
||||
}
|
||||
|
||||
structType := &encodingStructType{
|
||||
fields: flds,
|
||||
bytewiseFields: bytewiseFields,
|
||||
lengthFirstFields: lengthFirstFields,
|
||||
omitEmptyFieldsIdx: omitEmptyIdx,
|
||||
fields: flds,
|
||||
bytewiseFields: bytewiseFields,
|
||||
lengthFirstFields: lengthFirstFields,
|
||||
omitEmpty: omitEmpty,
|
||||
hasAnonymousField: hasAnonymousField,
|
||||
}
|
||||
|
||||
encodingStructTypeCache.Store(t, structType)
|
||||
return structType, structType.err
|
||||
return structType
|
||||
}
|
||||
|
||||
func getEncodingStructToArrayType(t reflect.Type, flds fields) (*encodingStructType, error) {
|
||||
func getEncodingStructToArrayType(t reflect.Type, flds fields) *encodingStructType {
|
||||
var hasAnonymousField bool
|
||||
for i := 0; i < len(flds); i++ {
|
||||
// Get field's encodeFunc
|
||||
flds[i].ef, flds[i].ief = getEncodeFunc(flds[i].typ)
|
||||
flds[i].ef = getEncodeFunc(flds[i].typ)
|
||||
if flds[i].ef == nil {
|
||||
structType := &encodingStructType{err: &UnsupportedTypeError{t}}
|
||||
encodingStructTypeCache.Store(t, structType)
|
||||
return structType, structType.err
|
||||
return structType
|
||||
}
|
||||
|
||||
// Check if field is from embedded struct
|
||||
if len(flds[i].idx) > 1 {
|
||||
hasAnonymousField = true
|
||||
}
|
||||
}
|
||||
|
||||
structType := &encodingStructType{
|
||||
fields: flds,
|
||||
toArray: true,
|
||||
fields: flds,
|
||||
toArray: true,
|
||||
hasAnonymousField: hasAnonymousField,
|
||||
}
|
||||
encodingStructTypeCache.Store(t, structType)
|
||||
return structType, structType.err
|
||||
return structType
|
||||
}
|
||||
|
||||
func getEncodeFunc(t reflect.Type) (encodeFunc, isEmptyFunc) {
|
||||
func getEncodeFunc(t reflect.Type) encodeFunc {
|
||||
if v, _ := encodeFuncCache.Load(t); v != nil {
|
||||
fs := v.(encodeFuncs)
|
||||
return fs.ef, fs.ief
|
||||
return v.(encodeFunc)
|
||||
}
|
||||
ef, ief := getEncodeFuncInternal(t)
|
||||
encodeFuncCache.Store(t, encodeFuncs{ef, ief})
|
||||
return ef, ief
|
||||
f := getEncodeFuncInternal(t)
|
||||
encodeFuncCache.Store(t, f)
|
||||
return f
|
||||
}
|
||||
|
||||
func getTypeInfo(t reflect.Type) *typeInfo {
|
||||
|
||||
182
common.go
182
common.go
@ -1,182 +0,0 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
type additionalInformation uint8
|
||||
|
||||
const (
|
||||
maxAdditionalInformationWithoutArgument = 23
|
||||
additionalInformationWith1ByteArgument = 24
|
||||
additionalInformationWith2ByteArgument = 25
|
||||
additionalInformationWith4ByteArgument = 26
|
||||
additionalInformationWith8ByteArgument = 27
|
||||
|
||||
// For major type 7.
|
||||
additionalInformationAsFalse = 20
|
||||
additionalInformationAsTrue = 21
|
||||
additionalInformationAsNull = 22
|
||||
additionalInformationAsUndefined = 23
|
||||
additionalInformationAsFloat16 = 25
|
||||
additionalInformationAsFloat32 = 26
|
||||
additionalInformationAsFloat64 = 27
|
||||
|
||||
// For major type 2, 3, 4, 5.
|
||||
additionalInformationAsIndefiniteLengthFlag = 31
|
||||
)
|
||||
|
||||
const (
|
||||
maxSimpleValueInAdditionalInformation = 23
|
||||
minSimpleValueIn1ByteArgument = 32
|
||||
)
|
||||
|
||||
func (ai additionalInformation) isIndefiniteLength() bool {
|
||||
return ai == additionalInformationAsIndefiniteLengthFlag
|
||||
}
|
||||
|
||||
const (
|
||||
// From RFC 8949 Section 3:
|
||||
// "The initial byte of each encoded data item contains both information about the major type
|
||||
// (the high-order 3 bits, described in Section 3.1) and additional information
|
||||
// (the low-order 5 bits)."
|
||||
|
||||
// typeMask is used to extract major type in initial byte of encoded data item.
|
||||
typeMask = 0xe0
|
||||
|
||||
// additionalInformationMask is used to extract additional information in initial byte of encoded data item.
|
||||
additionalInformationMask = 0x1f
|
||||
)
|
||||
|
||||
func getType(raw byte) cborType {
|
||||
return cborType(raw & typeMask)
|
||||
}
|
||||
|
||||
func getAdditionalInformation(raw byte) byte {
|
||||
return raw & additionalInformationMask
|
||||
}
|
||||
|
||||
func isBreakFlag(raw byte) bool {
|
||||
return raw == cborBreakFlag
|
||||
}
|
||||
|
||||
func parseInitialByte(b byte) (t cborType, ai byte) {
|
||||
return getType(b), getAdditionalInformation(b)
|
||||
}
|
||||
|
||||
const (
|
||||
tagNumRFC3339Time = 0
|
||||
tagNumEpochTime = 1
|
||||
tagNumUnsignedBignum = 2
|
||||
tagNumNegativeBignum = 3
|
||||
tagNumExpectedLaterEncodingBase64URL = 21
|
||||
tagNumExpectedLaterEncodingBase64 = 22
|
||||
tagNumExpectedLaterEncodingBase16 = 23
|
||||
tagNumSelfDescribedCBOR = 55799
|
||||
)
|
||||
|
||||
const (
|
||||
cborBreakFlag = byte(0xff)
|
||||
cborByteStringWithIndefiniteLengthHead = byte(0x5f)
|
||||
cborTextStringWithIndefiniteLengthHead = byte(0x7f)
|
||||
cborArrayWithIndefiniteLengthHead = byte(0x9f)
|
||||
cborMapWithIndefiniteLengthHead = byte(0xbf)
|
||||
)
|
||||
|
||||
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}
|
||||
)
|
||||
|
||||
// validBuiltinTag checks that supported built-in tag numbers are followed by expected content types.
|
||||
func validBuiltinTag(tagNum uint64, contentHead byte) error {
|
||||
t := getType(contentHead)
|
||||
switch tagNum {
|
||||
case tagNumRFC3339Time:
|
||||
// Tag content (date/time text string in RFC 3339 format) must be string type.
|
||||
if t != cborTypeTextString {
|
||||
return newInadmissibleTagContentTypeError(
|
||||
tagNumRFC3339Time,
|
||||
"text string",
|
||||
t.String())
|
||||
}
|
||||
return nil
|
||||
|
||||
case tagNumEpochTime:
|
||||
// Tag content (epoch date/time) must be uint, int, or float type.
|
||||
if t != cborTypePositiveInt && t != cborTypeNegativeInt && (contentHead < 0xf9 || contentHead > 0xfb) {
|
||||
return newInadmissibleTagContentTypeError(
|
||||
tagNumEpochTime,
|
||||
"integer or floating-point number",
|
||||
t.String())
|
||||
}
|
||||
return nil
|
||||
|
||||
case tagNumUnsignedBignum, tagNumNegativeBignum:
|
||||
// Tag content (bignum) must be byte type.
|
||||
if t != cborTypeByteString {
|
||||
return newInadmissibleTagContentTypeErrorf(
|
||||
fmt.Sprintf(
|
||||
"tag number %d or %d must be followed by byte string, got %s",
|
||||
tagNumUnsignedBignum,
|
||||
tagNumNegativeBignum,
|
||||
t.String(),
|
||||
))
|
||||
}
|
||||
return nil
|
||||
|
||||
case tagNumExpectedLaterEncodingBase64URL, tagNumExpectedLaterEncodingBase64, tagNumExpectedLaterEncodingBase16:
|
||||
// From RFC 8949 3.4.5.2:
|
||||
// The data item tagged can be a byte string or any other data item. In the latter
|
||||
// case, the tag applies to all of the byte string data items contained in the data
|
||||
// item, except for those contained in a nested data item tagged with an expected
|
||||
// conversion.
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build !tinygo
|
||||
|
||||
package cbor
|
||||
|
||||
import "reflect"
|
||||
|
||||
const (
|
||||
defaultMaxNestedLevels = 32
|
||||
minMaxNestedLevels = 4
|
||||
maxMaxNestedLevels = 65535
|
||||
)
|
||||
|
||||
func implements(concreteType reflect.Type, interfaceType reflect.Type) bool {
|
||||
return concreteType.Implements(interfaceType) ||
|
||||
reflect.PtrTo(concreteType).Implements(interfaceType)
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build !tinygo
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUnmarshalDeepNesting(t *testing.T) {
|
||||
// Construct this object rather than embed such a large constant in the code
|
||||
type TestNode struct {
|
||||
Value int
|
||||
Child *TestNode
|
||||
}
|
||||
n := &TestNode{Value: 0}
|
||||
root := n
|
||||
for i := 0; i < 65534; i++ {
|
||||
child := &TestNode{Value: i}
|
||||
n.Child = child
|
||||
n = child
|
||||
}
|
||||
em, err := EncOptions{}.EncMode()
|
||||
if err != nil {
|
||||
t.Errorf("EncMode() returned error %v", err)
|
||||
}
|
||||
data, err := em.Marshal(root)
|
||||
if err != nil {
|
||||
t.Errorf("Marshal() deeply nested object returned error %v", err)
|
||||
}
|
||||
|
||||
// Try unmarshal it
|
||||
dm, err := DecOptions{MaxNestedLevels: 65535}.DecMode()
|
||||
if err != nil {
|
||||
t.Errorf("DecMode() returned error %v", err)
|
||||
}
|
||||
var readback TestNode
|
||||
err = dm.Unmarshal(data, &readback)
|
||||
if err != nil {
|
||||
t.Errorf("Unmarshal() of deeply nested object returned error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(root, &readback) {
|
||||
t.Errorf("Unmarshal() of deeply nested object did not match\nGot: %#v\n Want: %#v\n",
|
||||
&readback, root)
|
||||
}
|
||||
}
|
||||
|
||||
func 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: "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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
8505
decode_test.go
8505
decode_test.go
File diff suppressed because it is too large
Load Diff
@ -1,24 +0,0 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build tinygo
|
||||
|
||||
package cbor
|
||||
|
||||
import "reflect"
|
||||
|
||||
const (
|
||||
defaultMaxNestedLevels = 16 // was 32 for non-tinygo (24+ for tinygo v0.33 panics tests)
|
||||
minMaxNestedLevels = 4 // same as non-tinygo
|
||||
maxMaxNestedLevels = 65535 // same as non-tinygo (to allow testing)
|
||||
)
|
||||
|
||||
// tinygo v0.33 doesn't implement Type.AssignableTo() and it panics.
|
||||
// Type.AssignableTo() is used under the hood for Type.Implements().
|
||||
//
|
||||
// More details in https://github.com/tinygo-org/tinygo/issues/4277.
|
||||
//
|
||||
// implements() always returns false until tinygo implements Type.AssignableTo().
|
||||
func implements(concreteType reflect.Type, interfaceType reflect.Type) bool {
|
||||
return false
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build tinygo
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestUnmarshalDeepNesting tests marshaling and unmarshaling of deeply nesting objects.
|
||||
// tinygo v0.33 fails with roughly 24+ levels of nested objects.
|
||||
func TestUnmarshalDeepNesting(t *testing.T) {
|
||||
// Construct this object rather than embed such a large constant in the code.
|
||||
type TestNode struct {
|
||||
Value int
|
||||
Child *TestNode
|
||||
}
|
||||
n := &TestNode{Value: 0}
|
||||
root := n
|
||||
const tinygoNestedLevels = 24
|
||||
for i := 1; i < tinygoNestedLevels; i++ {
|
||||
child := &TestNode{Value: i}
|
||||
n.Child = child
|
||||
n = child
|
||||
}
|
||||
em, err := EncOptions{}.EncMode()
|
||||
if err != nil {
|
||||
t.Errorf("EncMode() returned error %v", err)
|
||||
}
|
||||
data, err := em.Marshal(root)
|
||||
if err != nil {
|
||||
t.Errorf("Marshal() deeply nested object returned error %v", err)
|
||||
}
|
||||
|
||||
// Try unmarshal it
|
||||
dm, err := DecOptions{MaxNestedLevels: tinygoNestedLevels}.DecMode()
|
||||
if err != nil {
|
||||
t.Errorf("DecMode() returned error %v", err)
|
||||
}
|
||||
var readback TestNode
|
||||
err = dm.Unmarshal(data, &readback)
|
||||
if err != nil {
|
||||
t.Errorf("Unmarshal() of deeply nested object returned error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(root, &readback) {
|
||||
t.Errorf("Unmarshal() of deeply nested object did not match\nGot: %#v\n Want: %#v\n",
|
||||
&readback, root)
|
||||
}
|
||||
}
|
||||
|
||||
func 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: "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 no error, expect error", tc.data)
|
||||
} else if _, ok := err.(*UnmarshalTypeError); !ok {
|
||||
t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", tc.data, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
724
diagnose.go
724
diagnose.go
@ -1,724 +0,0 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/x448/float16"
|
||||
)
|
||||
|
||||
// DiagMode is the main interface for CBOR diagnostic notation.
|
||||
type DiagMode interface {
|
||||
// Diagnose returns extended diagnostic notation (EDN) of CBOR data items using this DiagMode.
|
||||
Diagnose([]byte) (string, error)
|
||||
|
||||
// DiagnoseFirst returns extended diagnostic notation (EDN) of the first CBOR data item using the DiagMode. Any remaining bytes are returned in rest.
|
||||
DiagnoseFirst([]byte) (string, []byte, error)
|
||||
|
||||
// DiagOptions returns user specified options used to create this DiagMode.
|
||||
DiagOptions() DiagOptions
|
||||
}
|
||||
|
||||
// ByteStringEncoding specifies the base encoding that byte strings are notated.
|
||||
type ByteStringEncoding uint8
|
||||
|
||||
const (
|
||||
// ByteStringBase16Encoding encodes byte strings in base16, without padding.
|
||||
ByteStringBase16Encoding ByteStringEncoding = iota
|
||||
|
||||
// ByteStringBase32Encoding encodes byte strings in base32, without padding.
|
||||
ByteStringBase32Encoding
|
||||
|
||||
// ByteStringBase32HexEncoding encodes byte strings in base32hex, without padding.
|
||||
ByteStringBase32HexEncoding
|
||||
|
||||
// ByteStringBase64Encoding encodes byte strings in base64url, without padding.
|
||||
ByteStringBase64Encoding
|
||||
|
||||
maxByteStringEncoding
|
||||
)
|
||||
|
||||
func (bse ByteStringEncoding) valid() error {
|
||||
if bse >= maxByteStringEncoding {
|
||||
return errors.New("cbor: invalid ByteStringEncoding " + strconv.Itoa(int(bse)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiagOptions specifies Diag options.
|
||||
type DiagOptions struct {
|
||||
// ByteStringEncoding specifies the base encoding that byte strings are notated.
|
||||
// Default is ByteStringBase16Encoding.
|
||||
ByteStringEncoding ByteStringEncoding
|
||||
|
||||
// ByteStringHexWhitespace specifies notating with whitespace in byte string
|
||||
// when ByteStringEncoding is ByteStringBase16Encoding.
|
||||
ByteStringHexWhitespace bool
|
||||
|
||||
// ByteStringText specifies notating with text in byte string
|
||||
// if it is a valid UTF-8 text.
|
||||
ByteStringText bool
|
||||
|
||||
// ByteStringEmbeddedCBOR specifies notating embedded CBOR in byte string
|
||||
// if it is a valid CBOR bytes.
|
||||
ByteStringEmbeddedCBOR bool
|
||||
|
||||
// CBORSequence specifies notating CBOR sequences.
|
||||
// otherwise, it returns an error if there are more bytes after the first CBOR.
|
||||
CBORSequence bool
|
||||
|
||||
// FloatPrecisionIndicator specifies appending a suffix to indicate float precision.
|
||||
// Refer to https://www.rfc-editor.org/rfc/rfc8949.html#name-encoding-indicators.
|
||||
FloatPrecisionIndicator bool
|
||||
|
||||
// MaxNestedLevels specifies the max nested levels allowed for any combination of CBOR array, maps, and tags.
|
||||
// Default is 32 levels and it can be set to [4, 65535]. Note that higher maximum levels of nesting can
|
||||
// require larger amounts of stack to deserialize. Don't increase this higher than you require.
|
||||
MaxNestedLevels int
|
||||
|
||||
// MaxArrayElements specifies the max number of elements for CBOR arrays.
|
||||
// Default is 128*1024=131072 and it can be set to [16, 2147483647]
|
||||
MaxArrayElements int
|
||||
|
||||
// MaxMapPairs specifies the max number of key-value pairs for CBOR maps.
|
||||
// Default is 128*1024=131072 and it can be set to [16, 2147483647]
|
||||
MaxMapPairs int
|
||||
}
|
||||
|
||||
// DiagMode returns a DiagMode with immutable options.
|
||||
func (opts DiagOptions) DiagMode() (DiagMode, error) {
|
||||
return opts.diagMode()
|
||||
}
|
||||
|
||||
func (opts DiagOptions) diagMode() (*diagMode, error) {
|
||||
if err := opts.ByteStringEncoding.valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decMode, err := DecOptions{
|
||||
MaxNestedLevels: opts.MaxNestedLevels,
|
||||
MaxArrayElements: opts.MaxArrayElements,
|
||||
MaxMapPairs: opts.MaxMapPairs,
|
||||
}.decMode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &diagMode{
|
||||
byteStringEncoding: opts.ByteStringEncoding,
|
||||
byteStringHexWhitespace: opts.ByteStringHexWhitespace,
|
||||
byteStringText: opts.ByteStringText,
|
||||
byteStringEmbeddedCBOR: opts.ByteStringEmbeddedCBOR,
|
||||
cborSequence: opts.CBORSequence,
|
||||
floatPrecisionIndicator: opts.FloatPrecisionIndicator,
|
||||
decMode: decMode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type diagMode struct {
|
||||
byteStringEncoding ByteStringEncoding
|
||||
byteStringHexWhitespace bool
|
||||
byteStringText bool
|
||||
byteStringEmbeddedCBOR bool
|
||||
cborSequence bool
|
||||
floatPrecisionIndicator bool
|
||||
decMode *decMode
|
||||
}
|
||||
|
||||
// DiagOptions returns user specified options used to create this DiagMode.
|
||||
func (dm *diagMode) DiagOptions() DiagOptions {
|
||||
return DiagOptions{
|
||||
ByteStringEncoding: dm.byteStringEncoding,
|
||||
ByteStringHexWhitespace: dm.byteStringHexWhitespace,
|
||||
ByteStringText: dm.byteStringText,
|
||||
ByteStringEmbeddedCBOR: dm.byteStringEmbeddedCBOR,
|
||||
CBORSequence: dm.cborSequence,
|
||||
FloatPrecisionIndicator: dm.floatPrecisionIndicator,
|
||||
MaxNestedLevels: dm.decMode.maxNestedLevels,
|
||||
MaxArrayElements: dm.decMode.maxArrayElements,
|
||||
MaxMapPairs: dm.decMode.maxMapPairs,
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnose returns extended diagnostic notation (EDN) of CBOR data items using the DiagMode.
|
||||
func (dm *diagMode) Diagnose(data []byte) (string, error) {
|
||||
return newDiagnose(data, dm.decMode, dm).diag(dm.cborSequence)
|
||||
}
|
||||
|
||||
// DiagnoseFirst returns extended diagnostic notation (EDN) of the first CBOR data item using the DiagMode. Any remaining bytes are returned in rest.
|
||||
func (dm *diagMode) DiagnoseFirst(data []byte) (diagNotation string, rest []byte, err error) {
|
||||
return newDiagnose(data, dm.decMode, dm).diagFirst()
|
||||
}
|
||||
|
||||
var defaultDiagMode, _ = DiagOptions{}.diagMode()
|
||||
|
||||
// Diagnose returns extended diagnostic notation (EDN) of CBOR data items
|
||||
// using the default diagnostic mode.
|
||||
//
|
||||
// Refer to https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation.
|
||||
func Diagnose(data []byte) (string, error) {
|
||||
return defaultDiagMode.Diagnose(data)
|
||||
}
|
||||
|
||||
// Diagnose returns extended diagnostic notation (EDN) of the first CBOR data item using the DiagMode. Any remaining bytes are returned in rest.
|
||||
func DiagnoseFirst(data []byte) (diagNotation string, rest []byte, err error) {
|
||||
return defaultDiagMode.DiagnoseFirst(data)
|
||||
}
|
||||
|
||||
type diagnose struct {
|
||||
dm *diagMode
|
||||
d *decoder
|
||||
w *bytes.Buffer
|
||||
}
|
||||
|
||||
func newDiagnose(data []byte, decm *decMode, diagm *diagMode) *diagnose {
|
||||
return &diagnose{
|
||||
dm: diagm,
|
||||
d: &decoder{data: data, dm: decm},
|
||||
w: &bytes.Buffer{},
|
||||
}
|
||||
}
|
||||
|
||||
func (di *diagnose) diag(cborSequence bool) (string, error) {
|
||||
// CBOR Sequence
|
||||
firstItem := true
|
||||
for {
|
||||
switch err := di.wellformed(cborSequence); err {
|
||||
case nil:
|
||||
if !firstItem {
|
||||
di.w.WriteString(", ")
|
||||
}
|
||||
firstItem = false
|
||||
if itemErr := di.item(); itemErr != nil {
|
||||
return di.w.String(), itemErr
|
||||
}
|
||||
|
||||
case io.EOF:
|
||||
if firstItem {
|
||||
return di.w.String(), err
|
||||
}
|
||||
return di.w.String(), nil
|
||||
|
||||
default:
|
||||
return di.w.String(), err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (di *diagnose) diagFirst() (diagNotation string, rest []byte, err error) {
|
||||
err = di.wellformed(true)
|
||||
if err == nil {
|
||||
err = di.item()
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
// Return EDN and the rest of the data slice (which might be len 0)
|
||||
return di.w.String(), di.d.data[di.d.off:], nil
|
||||
}
|
||||
|
||||
return di.w.String(), nil, err
|
||||
}
|
||||
|
||||
func (di *diagnose) wellformed(allowExtraData bool) error {
|
||||
off := di.d.off
|
||||
err := di.d.wellformed(allowExtraData, false)
|
||||
di.d.off = off
|
||||
return err
|
||||
}
|
||||
|
||||
func (di *diagnose) item() error { //nolint:gocyclo
|
||||
initialByte := di.d.data[di.d.off]
|
||||
switch initialByte {
|
||||
case cborByteStringWithIndefiniteLengthHead,
|
||||
cborTextStringWithIndefiniteLengthHead: // indefinite-length byte/text string
|
||||
di.d.off++
|
||||
if isBreakFlag(di.d.data[di.d.off]) {
|
||||
di.d.off++
|
||||
switch initialByte {
|
||||
case cborByteStringWithIndefiniteLengthHead:
|
||||
// indefinite-length bytes with no chunks.
|
||||
di.w.WriteString(`''_`)
|
||||
return nil
|
||||
case cborTextStringWithIndefiniteLengthHead:
|
||||
// indefinite-length text with no chunks.
|
||||
di.w.WriteString(`""_`)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
di.w.WriteString("(_ ")
|
||||
|
||||
i := 0
|
||||
for !di.d.foundBreak() {
|
||||
if i > 0 {
|
||||
di.w.WriteString(", ")
|
||||
}
|
||||
|
||||
i++
|
||||
// wellformedIndefiniteString() already checked that the next item is a byte/text string.
|
||||
if err := di.item(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
di.w.WriteByte(')')
|
||||
return nil
|
||||
|
||||
case cborArrayWithIndefiniteLengthHead: // indefinite-length array
|
||||
di.d.off++
|
||||
di.w.WriteString("[_ ")
|
||||
|
||||
i := 0
|
||||
for !di.d.foundBreak() {
|
||||
if i > 0 {
|
||||
di.w.WriteString(", ")
|
||||
}
|
||||
|
||||
i++
|
||||
if err := di.item(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
di.w.WriteByte(']')
|
||||
return nil
|
||||
|
||||
case cborMapWithIndefiniteLengthHead: // indefinite-length map
|
||||
di.d.off++
|
||||
di.w.WriteString("{_ ")
|
||||
|
||||
i := 0
|
||||
for !di.d.foundBreak() {
|
||||
if i > 0 {
|
||||
di.w.WriteString(", ")
|
||||
}
|
||||
|
||||
i++
|
||||
// key
|
||||
if err := di.item(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
di.w.WriteString(": ")
|
||||
|
||||
// value
|
||||
if err := di.item(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
di.w.WriteByte('}')
|
||||
return nil
|
||||
}
|
||||
|
||||
t := di.d.nextCBORType()
|
||||
switch t {
|
||||
case cborTypePositiveInt:
|
||||
_, _, val := di.d.getHead()
|
||||
di.w.WriteString(strconv.FormatUint(val, 10))
|
||||
return nil
|
||||
|
||||
case cborTypeNegativeInt:
|
||||
_, _, val := di.d.getHead()
|
||||
if val > math.MaxInt64 {
|
||||
// CBOR negative integer overflows int64, use big.Int to store value.
|
||||
bi := new(big.Int)
|
||||
bi.SetUint64(val)
|
||||
bi.Add(bi, big.NewInt(1))
|
||||
bi.Neg(bi)
|
||||
di.w.WriteString(bi.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
nValue := int64(-1) ^ int64(val)
|
||||
di.w.WriteString(strconv.FormatInt(nValue, 10))
|
||||
return nil
|
||||
|
||||
case cborTypeByteString:
|
||||
b, _ := di.d.parseByteString()
|
||||
return di.encodeByteString(b)
|
||||
|
||||
case cborTypeTextString:
|
||||
b, err := di.d.parseTextString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return di.encodeTextString(string(b), '"')
|
||||
|
||||
case cborTypeArray:
|
||||
_, _, val := di.d.getHead()
|
||||
count := int(val)
|
||||
di.w.WriteByte('[')
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
if i > 0 {
|
||||
di.w.WriteString(", ")
|
||||
}
|
||||
if err := di.item(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
di.w.WriteByte(']')
|
||||
return nil
|
||||
|
||||
case cborTypeMap:
|
||||
_, _, val := di.d.getHead()
|
||||
count := int(val)
|
||||
di.w.WriteByte('{')
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
if i > 0 {
|
||||
di.w.WriteString(", ")
|
||||
}
|
||||
// key
|
||||
if err := di.item(); err != nil {
|
||||
return err
|
||||
}
|
||||
di.w.WriteString(": ")
|
||||
// value
|
||||
if err := di.item(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
di.w.WriteByte('}')
|
||||
return nil
|
||||
|
||||
case cborTypeTag:
|
||||
_, _, tagNum := di.d.getHead()
|
||||
switch tagNum {
|
||||
case tagNumUnsignedBignum:
|
||||
if nt := di.d.nextCBORType(); nt != cborTypeByteString {
|
||||
return newInadmissibleTagContentTypeError(
|
||||
tagNumUnsignedBignum,
|
||||
"byte string",
|
||||
nt.String())
|
||||
}
|
||||
|
||||
b, _ := di.d.parseByteString()
|
||||
bi := new(big.Int).SetBytes(b)
|
||||
di.w.WriteString(bi.String())
|
||||
return nil
|
||||
|
||||
case tagNumNegativeBignum:
|
||||
if nt := di.d.nextCBORType(); nt != cborTypeByteString {
|
||||
return newInadmissibleTagContentTypeError(
|
||||
tagNumNegativeBignum,
|
||||
"byte string",
|
||||
nt.String(),
|
||||
)
|
||||
}
|
||||
|
||||
b, _ := di.d.parseByteString()
|
||||
bi := new(big.Int).SetBytes(b)
|
||||
bi.Add(bi, big.NewInt(1))
|
||||
bi.Neg(bi)
|
||||
di.w.WriteString(bi.String())
|
||||
return nil
|
||||
|
||||
default:
|
||||
di.w.WriteString(strconv.FormatUint(tagNum, 10))
|
||||
di.w.WriteByte('(')
|
||||
if err := di.item(); err != nil {
|
||||
return err
|
||||
}
|
||||
di.w.WriteByte(')')
|
||||
return nil
|
||||
}
|
||||
|
||||
case cborTypePrimitives:
|
||||
_, ai, val := di.d.getHead()
|
||||
switch ai {
|
||||
case additionalInformationAsFalse:
|
||||
di.w.WriteString("false")
|
||||
return nil
|
||||
|
||||
case additionalInformationAsTrue:
|
||||
di.w.WriteString("true")
|
||||
return nil
|
||||
|
||||
case additionalInformationAsNull:
|
||||
di.w.WriteString("null")
|
||||
return nil
|
||||
|
||||
case additionalInformationAsUndefined:
|
||||
di.w.WriteString("undefined")
|
||||
return nil
|
||||
|
||||
case additionalInformationAsFloat16,
|
||||
additionalInformationAsFloat32,
|
||||
additionalInformationAsFloat64:
|
||||
return di.encodeFloat(ai, val)
|
||||
|
||||
default:
|
||||
di.w.WriteString("simple(")
|
||||
di.w.WriteString(strconv.FormatUint(val, 10))
|
||||
di.w.WriteByte(')')
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeU16 format a rune as "\uxxxx"
|
||||
func (di *diagnose) writeU16(val rune) {
|
||||
di.w.WriteString("\\u")
|
||||
var in [2]byte
|
||||
in[0] = byte(val >> 8)
|
||||
in[1] = byte(val)
|
||||
sz := hex.EncodedLen(len(in))
|
||||
di.w.Grow(sz)
|
||||
dst := di.w.Bytes()[di.w.Len() : di.w.Len()+sz]
|
||||
hex.Encode(dst, in[:])
|
||||
di.w.Write(dst)
|
||||
}
|
||||
|
||||
var rawBase32Encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
|
||||
var rawBase32HexEncoding = base32.HexEncoding.WithPadding(base32.NoPadding)
|
||||
|
||||
func (di *diagnose) encodeByteString(val []byte) error {
|
||||
if len(val) > 0 {
|
||||
if di.dm.byteStringText && utf8.Valid(val) {
|
||||
return di.encodeTextString(string(val), '\'')
|
||||
}
|
||||
|
||||
if di.dm.byteStringEmbeddedCBOR {
|
||||
di2 := newDiagnose(val, di.dm.decMode, di.dm)
|
||||
// should always notating embedded CBOR sequence.
|
||||
if str, err := di2.diag(true); err == nil {
|
||||
di.w.WriteString("<<")
|
||||
di.w.WriteString(str)
|
||||
di.w.WriteString(">>")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch di.dm.byteStringEncoding {
|
||||
case ByteStringBase16Encoding:
|
||||
di.w.WriteString("h'")
|
||||
if di.dm.byteStringHexWhitespace {
|
||||
sz := hex.EncodedLen(len(val))
|
||||
if len(val) > 0 {
|
||||
sz += len(val) - 1
|
||||
}
|
||||
di.w.Grow(sz)
|
||||
|
||||
dst := di.w.Bytes()[di.w.Len():]
|
||||
for i := range val {
|
||||
if i > 0 {
|
||||
dst = append(dst, ' ')
|
||||
}
|
||||
hex.Encode(dst[len(dst):len(dst)+2], val[i:i+1])
|
||||
dst = dst[:len(dst)+2]
|
||||
}
|
||||
di.w.Write(dst)
|
||||
} else {
|
||||
sz := hex.EncodedLen(len(val))
|
||||
di.w.Grow(sz)
|
||||
dst := di.w.Bytes()[di.w.Len() : di.w.Len()+sz]
|
||||
hex.Encode(dst, val)
|
||||
di.w.Write(dst)
|
||||
}
|
||||
di.w.WriteByte('\'')
|
||||
return nil
|
||||
|
||||
case ByteStringBase32Encoding:
|
||||
di.w.WriteString("b32'")
|
||||
sz := rawBase32Encoding.EncodedLen(len(val))
|
||||
di.w.Grow(sz)
|
||||
dst := di.w.Bytes()[di.w.Len() : di.w.Len()+sz]
|
||||
rawBase32Encoding.Encode(dst, val)
|
||||
di.w.Write(dst)
|
||||
di.w.WriteByte('\'')
|
||||
return nil
|
||||
|
||||
case ByteStringBase32HexEncoding:
|
||||
di.w.WriteString("h32'")
|
||||
sz := rawBase32HexEncoding.EncodedLen(len(val))
|
||||
di.w.Grow(sz)
|
||||
dst := di.w.Bytes()[di.w.Len() : di.w.Len()+sz]
|
||||
rawBase32HexEncoding.Encode(dst, val)
|
||||
di.w.Write(dst)
|
||||
di.w.WriteByte('\'')
|
||||
return nil
|
||||
|
||||
case ByteStringBase64Encoding:
|
||||
di.w.WriteString("b64'")
|
||||
sz := base64.RawURLEncoding.EncodedLen(len(val))
|
||||
di.w.Grow(sz)
|
||||
dst := di.w.Bytes()[di.w.Len() : di.w.Len()+sz]
|
||||
base64.RawURLEncoding.Encode(dst, val)
|
||||
di.w.Write(dst)
|
||||
di.w.WriteByte('\'')
|
||||
return nil
|
||||
|
||||
default:
|
||||
// It should not be possible for users to construct a *diagMode with an invalid byte
|
||||
// string encoding.
|
||||
panic(fmt.Sprintf("diagmode has invalid ByteStringEncoding %v", di.dm.byteStringEncoding))
|
||||
}
|
||||
}
|
||||
|
||||
const utf16SurrSelf = rune(0x10000)
|
||||
|
||||
// quote should be either `'` or `"`
|
||||
func (di *diagnose) encodeTextString(val string, quote byte) error {
|
||||
di.w.WriteByte(quote)
|
||||
|
||||
for i := 0; i < len(val); {
|
||||
if b := val[i]; b < utf8.RuneSelf {
|
||||
switch {
|
||||
case b == '\t', b == '\n', b == '\r', b == '\\', b == quote:
|
||||
di.w.WriteByte('\\')
|
||||
|
||||
switch b {
|
||||
case '\t':
|
||||
b = 't'
|
||||
case '\n':
|
||||
b = 'n'
|
||||
case '\r':
|
||||
b = 'r'
|
||||
}
|
||||
di.w.WriteByte(b)
|
||||
|
||||
case b >= ' ' && b <= '~':
|
||||
di.w.WriteByte(b)
|
||||
|
||||
default:
|
||||
di.writeU16(rune(b))
|
||||
}
|
||||
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
c, size := utf8.DecodeRuneInString(val[i:])
|
||||
switch {
|
||||
case c == utf8.RuneError:
|
||||
return &SemanticError{"cbor: invalid UTF-8 string"}
|
||||
|
||||
case c < utf16SurrSelf:
|
||||
di.writeU16(c)
|
||||
|
||||
default:
|
||||
c1, c2 := utf16.EncodeRune(c)
|
||||
di.writeU16(c1)
|
||||
di.writeU16(c2)
|
||||
}
|
||||
|
||||
i += size
|
||||
}
|
||||
|
||||
di.w.WriteByte(quote)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (di *diagnose) encodeFloat(ai byte, val uint64) error {
|
||||
f64 := float64(0)
|
||||
switch ai {
|
||||
case additionalInformationAsFloat16:
|
||||
f16 := float16.Frombits(uint16(val))
|
||||
switch {
|
||||
case f16.IsNaN():
|
||||
di.w.WriteString("NaN")
|
||||
return nil
|
||||
case f16.IsInf(1):
|
||||
di.w.WriteString("Infinity")
|
||||
return nil
|
||||
case f16.IsInf(-1):
|
||||
di.w.WriteString("-Infinity")
|
||||
return nil
|
||||
default:
|
||||
f64 = float64(f16.Float32())
|
||||
}
|
||||
|
||||
case additionalInformationAsFloat32:
|
||||
f32 := math.Float32frombits(uint32(val))
|
||||
switch {
|
||||
case f32 != f32:
|
||||
di.w.WriteString("NaN")
|
||||
return nil
|
||||
case f32 > math.MaxFloat32:
|
||||
di.w.WriteString("Infinity")
|
||||
return nil
|
||||
case f32 < -math.MaxFloat32:
|
||||
di.w.WriteString("-Infinity")
|
||||
return nil
|
||||
default:
|
||||
f64 = float64(f32)
|
||||
}
|
||||
|
||||
case additionalInformationAsFloat64:
|
||||
f64 = math.Float64frombits(val)
|
||||
switch {
|
||||
case f64 != f64:
|
||||
di.w.WriteString("NaN")
|
||||
return nil
|
||||
case f64 > math.MaxFloat64:
|
||||
di.w.WriteString("Infinity")
|
||||
return nil
|
||||
case f64 < -math.MaxFloat64:
|
||||
di.w.WriteString("-Infinity")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Use ES6 number to string conversion which should match most JSON generators.
|
||||
// Inspired by https://github.com/golang/go/blob/4df10fba1687a6d4f51d7238a403f8f2298f6a16/src/encoding/json/encode.go#L585
|
||||
const bitSize = 64
|
||||
b := make([]byte, 0, 32)
|
||||
if abs := math.Abs(f64); abs != 0 && (abs < 1e-6 || abs >= 1e21) {
|
||||
b = strconv.AppendFloat(b, f64, 'e', -1, bitSize)
|
||||
// clean up e-09 to e-9
|
||||
n := len(b)
|
||||
if n >= 4 && string(b[n-4:n-1]) == "e-0" {
|
||||
b = append(b[:n-2], b[n-1])
|
||||
}
|
||||
} else {
|
||||
b = strconv.AppendFloat(b, f64, 'f', -1, bitSize)
|
||||
}
|
||||
|
||||
// add decimal point and trailing zero if needed
|
||||
if bytes.IndexByte(b, '.') < 0 {
|
||||
if i := bytes.IndexByte(b, 'e'); i < 0 {
|
||||
b = append(b, '.', '0')
|
||||
} else {
|
||||
b = append(b[:i+2], b[i:]...)
|
||||
b[i] = '.'
|
||||
b[i+1] = '0'
|
||||
}
|
||||
}
|
||||
|
||||
di.w.WriteString(string(b))
|
||||
|
||||
if di.dm.floatPrecisionIndicator {
|
||||
switch ai {
|
||||
case additionalInformationAsFloat16:
|
||||
di.w.WriteString("_1")
|
||||
return nil
|
||||
|
||||
case additionalInformationAsFloat32:
|
||||
di.w.WriteString("_2")
|
||||
return nil
|
||||
|
||||
case additionalInformationAsFloat64:
|
||||
di.w.WriteString("_3")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
1150
diagnose_test.go
1150
diagnose_test.go
File diff suppressed because it is too large
Load Diff
130
doc.go
130
doc.go
@ -2,111 +2,93 @@
|
||||
// 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.
|
||||
Package cbor is a fast & safe CBOR encoder & decoder (RFC 7049) with a
|
||||
standard API + toarray & keyasint struct tags, CBOR tags, float64->32->16,
|
||||
CTAP2 & Canonical CBOR, duplicate map key options, and is customizable via
|
||||
simple API.
|
||||
|
||||
Encoding options allow "preferred serialization" by encoding integers and floats
|
||||
to their smallest forms (e.g. float16) when values fit.
|
||||
CBOR encoding options allow "preferred serialization" by encoding integers and floats
|
||||
to their smallest forms (like float16) when values fit.
|
||||
|
||||
Struct tags like "keyasint", "toarray" and "omitempty" make CBOR data smaller
|
||||
and easier to use with structs.
|
||||
Struct tags like "keyasint", "toarray" and "omitempty" makes CBOR data smaller.
|
||||
|
||||
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.
|
||||
For example, "toarray" makes struct fields encode to array elements. And "keyasint"
|
||||
makes struct fields encode to elements of CBOR map with int keys.
|
||||
|
||||
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
|
||||
Basics
|
||||
|
||||
Function signatures identical to encoding/json include:
|
||||
|
||||
Marshal, Unmarshal, NewEncoder, NewDecoder, (*Encoder).Encode, (*Decoder).Decode.
|
||||
Marshal, Unmarshal, NewEncoder, NewDecoder, encoder.Encode, decoder.Decode.
|
||||
|
||||
Standard interfaces include:
|
||||
Codec functions are available at package-level (using defaults) or by creating modes
|
||||
from options at runtime.
|
||||
|
||||
BinaryMarshaler, BinaryUnmarshaler, Marshaler, and Unmarshaler.
|
||||
"Mode" in this API means definite way of encoding or decoding. Specifically, EncMode or DecMode.
|
||||
|
||||
Custom encoding and decoding is possible by implementing standard interfaces for
|
||||
user-defined Go types.
|
||||
EncMode and DecMode interfaces are created from EncOptions or DecOptions structs. For example,
|
||||
|
||||
Codec functions are available at package-level (using defaults options) or by
|
||||
creating modes from options at runtime.
|
||||
em := cbor.EncOptions{...}.EncMode()
|
||||
em := cbor.CanonicalEncOptions().EncMode()
|
||||
em := cbor.CTAP2EncOptions().EncMode()
|
||||
|
||||
"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, err := cbor.EncOptions{...}.EncMode()
|
||||
em, err := cbor.CanonicalEncOptions().EncMode()
|
||||
em, err := cbor.CTAP2EncOptions().EncMode()
|
||||
|
||||
Modes use immutable options to avoid side-effects and simplify concurrency. Behavior of
|
||||
modes won't accidentally change at runtime after they're created.
|
||||
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
|
||||
}
|
||||
// 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
|
||||
}
|
||||
// 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)
|
||||
b, err := cbor.Marshal(v)
|
||||
|
||||
encoder := cbor.NewEncoder(w)
|
||||
err = encoder.Encode(v)
|
||||
encoder := cbor.NewEncoder(w)
|
||||
err = encoder.Encode(v)
|
||||
|
||||
Using Default Decoding Mode
|
||||
|
||||
err := cbor.Unmarshal(b, &v)
|
||||
err := cbor.Unmarshal(b, &v)
|
||||
|
||||
decoder := cbor.NewDecoder(r)
|
||||
err = decoder.Decode(&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()
|
||||
// Create EncOptions using either struct literal or a function.
|
||||
opts := cbor.CanonicalEncOptions()
|
||||
|
||||
// If needed, modify encoding options
|
||||
opts.Time = cbor.TimeUnix
|
||||
// If needed, modify encoding options
|
||||
opts.Time = cbor.TimeUnix
|
||||
|
||||
// Create reusable EncMode interface with immutable options, safe for concurrent use.
|
||||
em, err := opts.EncMode()
|
||||
// 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)
|
||||
// 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).
|
||||
Default Options
|
||||
|
||||
# CBOR Options
|
||||
Default encoding options are listed at https://github.com/fxamacker/cbor#api
|
||||
|
||||
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
|
||||
|
||||
Struct tags like `cbor:"name,omitempty"` and `json:"name,omitempty"` work as expected.
|
||||
If both struct tags are specified then `cbor` is used.
|
||||
@ -119,11 +101,9 @@ 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
|
||||
|
||||
# Tests and Fuzzing
|
||||
|
||||
Over 375 tests are included in this package. Cover-guided fuzzing is handled by
|
||||
a private fuzzer that replaced fxamacker/cbor-fuzz years ago.
|
||||
Over 375 tests are included in this package. Cover-guided fuzzing is handled by a separate package:
|
||||
fxamacker/cbor-fuzz.
|
||||
*/
|
||||
package cbor
|
||||
|
||||
@ -1,94 +0,0 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build go1.20 && !tinygo
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type mapKeyValueEncodeFunc struct {
|
||||
kf, ef encodeFunc
|
||||
kpool, vpool sync.Pool
|
||||
}
|
||||
|
||||
func (me *mapKeyValueEncodeFunc) encodeKeyValues(e *bytes.Buffer, em *encMode, v reflect.Value, kvs []keyValue) error {
|
||||
iterk := me.kpool.Get().(*reflect.Value)
|
||||
defer func() {
|
||||
iterk.SetZero()
|
||||
me.kpool.Put(iterk)
|
||||
}()
|
||||
iterv := me.vpool.Get().(*reflect.Value)
|
||||
defer func() {
|
||||
iterv.SetZero()
|
||||
me.vpool.Put(iterv)
|
||||
}()
|
||||
|
||||
if kvs == nil {
|
||||
for i, iter := 0, v.MapRange(); iter.Next(); i++ {
|
||||
iterk.SetIterKey(iter)
|
||||
iterv.SetIterValue(iter)
|
||||
|
||||
if err := me.kf(e, em, *iterk); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := me.ef(e, em, *iterv); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
initial := e.Len()
|
||||
for i, iter := 0, v.MapRange(); iter.Next(); i++ {
|
||||
iterk.SetIterKey(iter)
|
||||
iterv.SetIterValue(iter)
|
||||
|
||||
offset := e.Len()
|
||||
if err := me.kf(e, em, *iterk); err != nil {
|
||||
return err
|
||||
}
|
||||
valueOffset := e.Len()
|
||||
if err := me.ef(e, em, *iterv); err != nil {
|
||||
return err
|
||||
}
|
||||
kvs[i] = keyValue{
|
||||
offset: offset - initial,
|
||||
valueOffset: valueOffset - initial,
|
||||
nextOffset: e.Len() - initial,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEncodeMapFunc(t reflect.Type) encodeFunc {
|
||||
kf, _ := getEncodeFunc(t.Key())
|
||||
ef, _ := getEncodeFunc(t.Elem())
|
||||
if kf == nil || ef == nil {
|
||||
return nil
|
||||
}
|
||||
mkv := &mapKeyValueEncodeFunc{
|
||||
kf: kf,
|
||||
ef: ef,
|
||||
kpool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
rk := reflect.New(t.Key()).Elem()
|
||||
return &rk
|
||||
},
|
||||
},
|
||||
vpool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
rv := reflect.New(t.Elem()).Elem()
|
||||
return &rv
|
||||
},
|
||||
},
|
||||
}
|
||||
return mapEncodeFunc{
|
||||
e: mkv.encodeKeyValues,
|
||||
}.encode
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build !go1.20 || tinygo
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type mapKeyValueEncodeFunc struct {
|
||||
kf, ef encodeFunc
|
||||
}
|
||||
|
||||
func (me *mapKeyValueEncodeFunc) encodeKeyValues(e *bytes.Buffer, em *encMode, v reflect.Value, kvs []keyValue) error {
|
||||
if kvs == nil {
|
||||
for i, iter := 0, v.MapRange(); iter.Next(); i++ {
|
||||
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
|
||||
}
|
||||
|
||||
initial := e.Len()
|
||||
for i, iter := 0, v.MapRange(); iter.Next(); i++ {
|
||||
offset := e.Len()
|
||||
if err := me.kf(e, em, iter.Key()); err != nil {
|
||||
return err
|
||||
}
|
||||
valueOffset := e.Len()
|
||||
if err := me.ef(e, em, iter.Value()); err != nil {
|
||||
return err
|
||||
}
|
||||
kvs[i] = keyValue{
|
||||
offset: offset - initial,
|
||||
valueOffset: valueOffset - initial,
|
||||
nextOffset: e.Len() - initial,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEncodeMapFunc(t reflect.Type) encodeFunc {
|
||||
kf, _ := getEncodeFunc(t.Key())
|
||||
ef, _ := getEncodeFunc(t.Elem())
|
||||
if kf == nil || ef == nil {
|
||||
return nil
|
||||
}
|
||||
mkv := &mapKeyValueEncodeFunc{kf: kf, ef: ef}
|
||||
return mapEncodeFunc{
|
||||
e: mkv.encodeKeyValues,
|
||||
}.encode
|
||||
}
|
||||
3302
encode_test.go
3302
encode_test.go
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@ import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/fxamacker/cbor/v2" // remove "/v2" suffix if you're not using Go modules (see README.md)
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
)
|
||||
|
||||
func ExampleMarshal() {
|
||||
@ -125,9 +125,9 @@ func ExampleUnmarshal() {
|
||||
Owners []string
|
||||
Male bool
|
||||
}
|
||||
data, _ := hex.DecodeString("a46341676504644e616d656543616e6479664f776e65727382644d617279634a6f65644d616c65f4")
|
||||
cborData, _ := hex.DecodeString("a46341676504644e616d656543616e6479664f776e65727382644d617279634a6f65644d616c65f4")
|
||||
var animal Animal
|
||||
err := cbor.Unmarshal(data, &animal)
|
||||
err := cbor.Unmarshal(cborData, &animal)
|
||||
if err != nil {
|
||||
fmt.Println("error:", err)
|
||||
}
|
||||
@ -334,8 +334,8 @@ func ExampleDecoder() {
|
||||
Owners []string
|
||||
Male bool
|
||||
}
|
||||
data, _ := hex.DecodeString("a46341676504644d616c65f4644e616d656543616e6479664f776e65727382644d617279634a6f65a46341676506644d616c65f5644e616d656452756479664f776e657273816543696e6479a46341676502644d616c65f5644e616d656444756b65664f776e65727381664e6f72746f6e")
|
||||
dec := cbor.NewDecoder(bytes.NewReader(data))
|
||||
cborData, _ := hex.DecodeString("a46341676504644d616c65f4644e616d656543616e6479664f776e65727382644d617279634a6f65a46341676506644d616c65f5644e616d656452756479664f776e657273816543696e6479a46341676502644d616c65f5644e616d656444756b65664f776e65727381664e6f72746f6e")
|
||||
dec := cbor.NewDecoder(bytes.NewReader(cborData))
|
||||
for {
|
||||
var animal Animal
|
||||
if err := dec.Decode(&animal); err != nil {
|
||||
@ -364,9 +364,9 @@ func Example_cWT() {
|
||||
Cti []byte `cbor:"7,keyasint"`
|
||||
}
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1
|
||||
data, _ := hex.DecodeString("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
|
||||
cborData, _ := hex.DecodeString("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
|
||||
var v claims
|
||||
if err := cbor.Unmarshal(data, &v); err != nil {
|
||||
if err := cbor.Unmarshal(cborData, &v); err != nil {
|
||||
fmt.Println("error:", err)
|
||||
}
|
||||
if _, err := cbor.Marshal(v); err != nil {
|
||||
@ -389,12 +389,12 @@ func Example_cWTWithDupMapKeyOption() {
|
||||
}
|
||||
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1
|
||||
data, _ := hex.DecodeString("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
|
||||
cborData, _ := hex.DecodeString("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
|
||||
|
||||
dm, _ := cbor.DecOptions{DupMapKey: cbor.DupMapKeyEnforcedAPF}.DecMode()
|
||||
|
||||
var v claims
|
||||
if err := dm.Unmarshal(data, &v); err != nil {
|
||||
if err := dm.Unmarshal(cborData, &v); err != nil {
|
||||
fmt.Println("error:", err)
|
||||
}
|
||||
fmt.Printf("%+v", v)
|
||||
@ -419,9 +419,9 @@ func Example_signedCWT() {
|
||||
Signature []byte
|
||||
}
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.3
|
||||
data, _ := hex.DecodeString("d28443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7158405427c1ff28d23fbad1f29c4c7c6a555e601d6fa29f9179bc3d7438bacaca5acd08c8d4d4f96131680c429a01f85951ecee743a52b9b63632c57209120e1c9e30")
|
||||
cborData, _ := hex.DecodeString("d28443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7158405427c1ff28d23fbad1f29c4c7c6a555e601d6fa29f9179bc3d7438bacaca5acd08c8d4d4f96131680c429a01f85951ecee743a52b9b63632c57209120e1c9e30")
|
||||
var v signedCWT
|
||||
if err := cbor.Unmarshal(data, &v); err != nil {
|
||||
if err := cbor.Unmarshal(cborData, &v); err != nil {
|
||||
fmt.Println("error:", err)
|
||||
}
|
||||
if _, err := cbor.Marshal(v); err != nil {
|
||||
@ -450,7 +450,7 @@ func Example_signedCWTWithTag() {
|
||||
}
|
||||
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.3
|
||||
data, _ := hex.DecodeString("d28443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7158405427c1ff28d23fbad1f29c4c7c6a555e601d6fa29f9179bc3d7438bacaca5acd08c8d4d4f96131680c429a01f85951ecee743a52b9b63632c57209120e1c9e30")
|
||||
cborData, _ := hex.DecodeString("d28443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7158405427c1ff28d23fbad1f29c4c7c6a555e601d6fa29f9179bc3d7438bacaca5acd08c8d4d4f96131680c429a01f85951ecee743a52b9b63632c57209120e1c9e30")
|
||||
|
||||
// Register tag COSE_Sign1 18 with signedCWT type.
|
||||
tags := cbor.NewTagSet()
|
||||
@ -465,7 +465,7 @@ func Example_signedCWTWithTag() {
|
||||
em, _ := cbor.EncOptions{}.EncModeWithTags(tags)
|
||||
|
||||
var v signedCWT
|
||||
if err := dm.Unmarshal(data, &v); err != nil {
|
||||
if err := dm.Unmarshal(cborData, &v); err != nil {
|
||||
fmt.Println("error:", err)
|
||||
}
|
||||
|
||||
@ -493,9 +493,9 @@ func Example_cOSE() {
|
||||
}
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.2
|
||||
// 128-Bit Symmetric Key
|
||||
data, _ := hex.DecodeString("a42050231f4c4d4d3051fdc2ec0a3851d5b3830104024c53796d6d6574726963313238030a")
|
||||
cborData, _ := hex.DecodeString("a42050231f4c4d4d3051fdc2ec0a3851d5b3830104024c53796d6d6574726963313238030a")
|
||||
var v coseKey
|
||||
if err := cbor.Unmarshal(data, &v); err != nil {
|
||||
if err := cbor.Unmarshal(cborData, &v); err != nil {
|
||||
fmt.Println("error:", err)
|
||||
}
|
||||
if _, err := cbor.Marshal(v); err != nil {
|
||||
@ -526,9 +526,9 @@ func Example_senML() {
|
||||
Sum float64 `cbor:"5,keyasint,omitempty"`
|
||||
}
|
||||
// Data from https://tools.ietf.org/html/rfc8428#section-6
|
||||
data, _ := hex.DecodeString("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333")
|
||||
cborData, _ := hex.DecodeString("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333")
|
||||
var v []*SenMLRecord
|
||||
if err := cbor.Unmarshal(data, &v); err != nil {
|
||||
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.
|
||||
@ -559,9 +559,9 @@ func Example_webAuthn() {
|
||||
Fmt string `cbor:"fmt"`
|
||||
AttStmt cbor.RawMessage `cbor:"attStmt"`
|
||||
}
|
||||
data, _ := hex.DecodeString("a363666d74686669646f2d7532666761747453746d74a26373696758483046022100e7ab373cfbd99fcd55fd59b0f6f17fef5b77a20ddec3db7f7e4d55174e366236022100828336b4822125fb56541fb14a8a273876acd339395ec2dad95cf41c1dd2a9ae637835638159024e3082024a30820132a0030201020204124a72fe300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a302c312a302806035504030c2159756269636f205532462045452053657269616c203234393431343937323135383059301306072a8648ce3d020106082a8648ce3d030107034200043d8b1bbd2fcbf6086e107471601468484153c1c6d3b4b68a5e855e6e40757ee22bcd8988bf3befd7cdf21cb0bf5d7a150d844afe98103c6c6607d9faae287c02a33b3039302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e313013060b2b0601040182e51c020101040403020520300d06092a864886f70d01010b05000382010100a14f1eea0076f6b8476a10a2be72e60d0271bb465b2dfbfc7c1bd12d351989917032631d795d097fa30a26a325634e85721bc2d01a86303f6bc075e5997319e122148b0496eec8d1f4f94cf4110de626c289443d1f0f5bbb239ca13e81d1d5aa9df5af8e36126475bfc23af06283157252762ff68879bcf0ef578d55d67f951b4f32b63c8aea5b0f99c67d7d814a7ff5a6f52df83e894a3a5d9c8b82e7f8bc8daf4c80175ff8972fda79333ec465d806eacc948f1bab22045a95558a48c20226dac003d41fbc9e05ea28a6bb5e10a49de060a0a4f6a2676a34d68c4abe8c61874355b9027e828ca9e064b002d62e8d8cf0744921753d35e3c87c5d5779453e7768617574684461746158c449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976341000000000000000000000000000000000000000000408903fd7dfd2c9770e98cae0123b13a2c27828a106349bc6277140e7290b7e9eb7976aa3c04ed347027caf7da3a2fa76304751c02208acfc4e7fc6c7ebbc375c8a5010203262001215820ad7f7992c335b90d882b2802061b97a4fabca7e2ee3e7a51e728b8055e4eb9c7225820e0966ba7005987fece6f0e0e13447aa98cec248e4000a594b01b74c1cb1d40b3")
|
||||
cborData, _ := hex.DecodeString("a363666d74686669646f2d7532666761747453746d74a26373696758483046022100e7ab373cfbd99fcd55fd59b0f6f17fef5b77a20ddec3db7f7e4d55174e366236022100828336b4822125fb56541fb14a8a273876acd339395ec2dad95cf41c1dd2a9ae637835638159024e3082024a30820132a0030201020204124a72fe300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a302c312a302806035504030c2159756269636f205532462045452053657269616c203234393431343937323135383059301306072a8648ce3d020106082a8648ce3d030107034200043d8b1bbd2fcbf6086e107471601468484153c1c6d3b4b68a5e855e6e40757ee22bcd8988bf3befd7cdf21cb0bf5d7a150d844afe98103c6c6607d9faae287c02a33b3039302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e313013060b2b0601040182e51c020101040403020520300d06092a864886f70d01010b05000382010100a14f1eea0076f6b8476a10a2be72e60d0271bb465b2dfbfc7c1bd12d351989917032631d795d097fa30a26a325634e85721bc2d01a86303f6bc075e5997319e122148b0496eec8d1f4f94cf4110de626c289443d1f0f5bbb239ca13e81d1d5aa9df5af8e36126475bfc23af06283157252762ff68879bcf0ef578d55d67f951b4f32b63c8aea5b0f99c67d7d814a7ff5a6f52df83e894a3a5d9c8b82e7f8bc8daf4c80175ff8972fda79333ec465d806eacc948f1bab22045a95558a48c20226dac003d41fbc9e05ea28a6bb5e10a49de060a0a4f6a2676a34d68c4abe8c61874355b9027e828ca9e064b002d62e8d8cf0744921753d35e3c87c5d5779453e7768617574684461746158c449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976341000000000000000000000000000000000000000000408903fd7dfd2c9770e98cae0123b13a2c27828a106349bc6277140e7290b7e9eb7976aa3c04ed347027caf7da3a2fa76304751c02208acfc4e7fc6c7ebbc375c8a5010203262001215820ad7f7992c335b90d882b2802061b97a4fabca7e2ee3e7a51e728b8055e4eb9c7225820e0966ba7005987fece6f0e0e13447aa98cec248e4000a594b01b74c1cb1d40b3")
|
||||
var v attestationObject
|
||||
if err := cbor.Unmarshal(data, &v); err != nil {
|
||||
if err := cbor.Unmarshal(cborData, &v); err != nil {
|
||||
fmt.Println("error:", err)
|
||||
}
|
||||
if _, err := cbor.Marshal(v); err != nil {
|
||||
|
||||
2
go.mod
2
go.mod
@ -1,5 +1,5 @@
|
||||
module github.com/fxamacker/cbor/v2
|
||||
|
||||
go 1.17 // Compiling with go 1.20+ uses new features and optimizations
|
||||
go 1.12
|
||||
|
||||
require github.com/x448/float16 v0.8.4
|
||||
|
||||
132
json_test.go
132
json_test.go
@ -1,132 +0,0 @@
|
||||
package cbor_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
)
|
||||
|
||||
// TestStdlibJSONCompatibility tests compatibility as a drop-in replacement for the standard library
|
||||
// encoding/json package on a round trip encoding from Go object to interface{}.
|
||||
func TestStdlibJSONCompatibility(t *testing.T) {
|
||||
// TODO: With better coverage and compatibility, it could be useful to expose these option
|
||||
// configurations to users.
|
||||
|
||||
enc, err := cbor.EncOptions{
|
||||
ByteSliceLaterFormat: cbor.ByteSliceLaterFormatBase64,
|
||||
String: cbor.StringToByteString,
|
||||
ByteArray: cbor.ByteArrayToArray,
|
||||
}.EncMode()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dec, err := cbor.DecOptions{
|
||||
DefaultByteStringType: reflect.TypeOf(""),
|
||||
ByteStringToString: cbor.ByteStringToStringAllowedWithExpectedLaterEncoding,
|
||||
ByteStringExpectedFormat: cbor.ByteStringExpectedBase64,
|
||||
}.DecMode()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
original interface{}
|
||||
ifaceEqual bool // require equal intermediate interface{} values from both protocols
|
||||
}{
|
||||
{
|
||||
name: "byte slice to base64-encoded string",
|
||||
original: []byte("hello world"),
|
||||
ifaceEqual: true,
|
||||
},
|
||||
{
|
||||
name: "byte array to array of integers",
|
||||
original: [11]byte{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'},
|
||||
ifaceEqual: false, // encoding/json decodes the array elements to float64
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Logf("original: %#v", tc.original)
|
||||
|
||||
j1, err := json.Marshal(tc.original)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("original to json: %s", string(j1))
|
||||
|
||||
c1, err := enc.Marshal(tc.original)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
diag1, err := cbor.Diagnose(c1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("original to cbor: %s", diag1)
|
||||
|
||||
var jintf interface{}
|
||||
err = json.Unmarshal(j1, &jintf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("json to interface{} (%T): %#v", jintf, jintf)
|
||||
|
||||
var cintf interface{}
|
||||
err = dec.Unmarshal(c1, &cintf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("cbor to interface{} (%T): %#v", cintf, cintf)
|
||||
|
||||
j2, err := json.Marshal(jintf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("interface{} to json: %s", string(j2))
|
||||
|
||||
c2, err := enc.Marshal(cintf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
diag2, err := cbor.Diagnose(c2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("interface{} to cbor: %s", diag2)
|
||||
|
||||
if !reflect.DeepEqual(jintf, cintf) {
|
||||
if tc.ifaceEqual {
|
||||
t.Errorf("native-to-interface{} via cbor differed from native-to-interface{} via json")
|
||||
} else {
|
||||
t.Logf("native-to-interface{} via cbor differed from native-to-interface{} via json")
|
||||
}
|
||||
}
|
||||
|
||||
jfinalValue := reflect.New(reflect.TypeOf(tc.original))
|
||||
err = json.Unmarshal(j2, jfinalValue.Interface())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
jfinal := jfinalValue.Elem().Interface()
|
||||
t.Logf("json to native: %#v", jfinal)
|
||||
if !reflect.DeepEqual(tc.original, jfinal) {
|
||||
t.Error("diff in json roundtrip")
|
||||
}
|
||||
|
||||
cfinalValue := reflect.New(reflect.TypeOf(tc.original))
|
||||
err = dec.Unmarshal(c2, cfinalValue.Interface())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfinal := cfinalValue.Elem().Interface()
|
||||
t.Logf("cbor to native: %#v", cfinal)
|
||||
if !reflect.DeepEqual(tc.original, cfinal) {
|
||||
t.Error("diff in cbor roundtrip")
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
83
maxdepth_test.go
Normal file
83
maxdepth_test.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDepth(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
cborData []byte
|
||||
wantDepth int
|
||||
}{
|
||||
{"uint", hexDecode("00"), 1}, // 0
|
||||
{"int", hexDecode("20"), 1}, // -1
|
||||
{"bool", hexDecode("f4"), 1}, // false
|
||||
{"nil", hexDecode("f6"), 1}, // nil
|
||||
{"float", hexDecode("fa47c35000"), 1}, // 100000.0
|
||||
{"byte string", hexDecode("40"), 1}, // []byte{}
|
||||
{"indefinite length byte string", hexDecode("5f42010243030405ff"), 1}, // []byte{1, 2, 3, 4, 5}
|
||||
{"text string", hexDecode("60"), 1}, // ""
|
||||
{"indefinite length text string", hexDecode("7f657374726561646d696e67ff"), 1}, // "streaming"
|
||||
{"empty array", hexDecode("80"), 1}, // []
|
||||
{"indefinite length empty array", hexDecode("9fff"), 1}, // []
|
||||
{"array", hexDecode("98190102030405060708090a0b0c0d0e0f101112131415161718181819"), 2}, // [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"), 2}, // [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"), 3}, // [1,[2,3],[4,5]]
|
||||
{"indefinite length nested array", hexDecode("83018202039f0405ff"), 3}, // [1,[2,3],[4,5]]
|
||||
{"array and map", hexDecode("826161a161626163"), 3}, // [a", {"b": "c"}]
|
||||
{"indefinite length array and map", hexDecode("826161bf61626163ff"), 3}, // [a", {"b": "c"}]
|
||||
{"empty map", hexDecode("a0"), 1}, // {}
|
||||
{"indefinite length empty map", hexDecode("bfff"), 1}, // {}
|
||||
{"map", hexDecode("a201020304"), 2}, // {1:2, 3:4}
|
||||
{"nested map", hexDecode("a26161016162820203"), 3}, // {"a": 1, "b": [2, 3]}
|
||||
{"indefinite length nested map", hexDecode("bf61610161629f0203ffff"), 3}, // {"a": 1, "b": [2, 3]}
|
||||
{"tag", hexDecode("c074323031332d30332d32315432303a30343a30305a"), 1}, // 0("2013-03-21T20:04:00Z")
|
||||
{"tagged map", hexDecode("d864a26161016162820203"), 3}, // 100({"a": 1, "b": [2, 3]})
|
||||
{"tagged map and array", hexDecode("d864a26161016162d865d866820203"), 4}, // 100({"a": 1, "b": 101(102([2, 3]))})
|
||||
{"nested tag", hexDecode("d864d865d86674323031332d30332d32315432303a30343a30305a"), 3}, // 100(101(102("2013-03-21T20:04:00Z")))
|
||||
{"32-level array", hexDecode("820181818181818181818181818181818181818181818181818181818181818101"), 32},
|
||||
{"32-level indefinite length array", hexDecode("9f0181818181818181818181818181818181818181818181818181818181818101ff"), 32},
|
||||
{"32-level map", hexDecode("a10181818181818181818181818181818181818181818181818181818181818101"), 32},
|
||||
{"32-level indefinite length map", hexDecode("bf0181818181818181818181818181818181818181818181818181818181818101ff"), 32},
|
||||
{"32-level tag", hexDecode("d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d86474323031332d30332d32315432303a30343a30305a"), 32}, // 100(100(...("2013-03-21T20:04:00Z")))
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, depth, err := validInternal(tc.cborData, 0, 1)
|
||||
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
|
||||
wantErrorMsg string
|
||||
}{
|
||||
{"33-level array", hexDecode("82018181818181818181818181818181818181818181818181818181818181818101"), "cbor: reached max depth 32"},
|
||||
{"33-level indefinite length array", hexDecode("9f018181818181818181818181818181818181818181818181818181818181818101ff"), "cbor: reached max depth 32"},
|
||||
{"33-level map", hexDecode("a1018181818181818181818181818181818181818181818181818181818181818101"), "cbor: reached max depth 32"},
|
||||
{"33-level indefinite length map", hexDecode("bf018181818181818181818181818181818181818181818181818181818181818101ff"), "cbor: reached max depth 32"},
|
||||
{"33-level tag", hexDecode("d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d86474323031332d30332d32315432303a30343a30305a"), "cbor: reached max depth 32"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, _, err := validInternal(tc.cborData, 0, 1)
|
||||
if err == nil {
|
||||
t.Errorf("valid(0x%x) didn't return an error, want %q", tc.cborData, tc.wantErrorMsg)
|
||||
} else if err.Error() != tc.wantErrorMsg {
|
||||
t.Errorf("valid(0x%x) returned error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,69 +0,0 @@
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// SimpleValue represents CBOR simple value.
|
||||
// CBOR simple value is:
|
||||
// - an extension point like CBOR tag.
|
||||
// - a subset of CBOR major type 7 that isn't floating-point.
|
||||
// - "identified by a number between 0 and 255, but distinct from that number itself".
|
||||
// For example, "a simple value 2 is not equivalent to an integer 2" as a CBOR map key.
|
||||
//
|
||||
// CBOR simple values identified by 20..23 are: "false", "true" , "null", and "undefined".
|
||||
// Other CBOR simple values are currently unassigned/reserved by IANA.
|
||||
type SimpleValue uint8
|
||||
|
||||
var (
|
||||
typeSimpleValue = reflect.TypeOf(SimpleValue(0))
|
||||
)
|
||||
|
||||
// MarshalCBOR encodes SimpleValue as CBOR simple value (major type 7).
|
||||
func (sv SimpleValue) MarshalCBOR() ([]byte, error) {
|
||||
// RFC 8949 3.3. Floating-Point Numbers and Values with No Content says:
|
||||
// "An encoder MUST NOT issue two-byte sequences that start with 0xf8
|
||||
// (major type 7, additional information 24) and continue with a byte
|
||||
// less than 0x20 (32 decimal). Such sequences are not well-formed.
|
||||
// (This implies that an encoder cannot encode false, true, null, or
|
||||
// undefined in two-byte sequences and that only the one-byte variants
|
||||
// of these are well-formed; more generally speaking, each simple value
|
||||
// only has a single representation variant)."
|
||||
|
||||
switch {
|
||||
case sv <= maxSimpleValueInAdditionalInformation:
|
||||
return []byte{byte(cborTypePrimitives) | byte(sv)}, nil
|
||||
|
||||
case sv >= minSimpleValueIn1ByteArgument:
|
||||
return []byte{byte(cborTypePrimitives) | additionalInformationWith1ByteArgument, byte(sv)}, nil
|
||||
|
||||
default:
|
||||
return nil, &UnsupportedValueError{msg: fmt.Sprintf("SimpleValue(%d)", sv)}
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalCBOR decodes CBOR simple value (major type 7) to SimpleValue.
|
||||
func (sv *SimpleValue) UnmarshalCBOR(data []byte) error {
|
||||
if sv == nil {
|
||||
return errors.New("cbor.SimpleValue: UnmarshalCBOR on nil pointer")
|
||||
}
|
||||
|
||||
d := decoder{data: data, dm: defaultDecMode}
|
||||
|
||||
typ, ai, val := d.getHead()
|
||||
|
||||
if typ != cborTypePrimitives {
|
||||
return &UnmarshalTypeError{CBORType: typ.String(), GoType: "SimpleValue"}
|
||||
}
|
||||
if ai > additionalInformationWith1ByteArgument {
|
||||
return &UnmarshalTypeError{CBORType: typ.String(), GoType: "SimpleValue", errorMsg: "not simple values"}
|
||||
}
|
||||
|
||||
// It is safe to cast val to uint8 here because
|
||||
// - data is already verified to be well-formed CBOR simple value and
|
||||
// - val is <= math.MaxUint8.
|
||||
*sv = SimpleValue(val)
|
||||
return nil
|
||||
}
|
||||
@ -1,177 +0,0 @@
|
||||
// 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"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUnmarshalSimpleValue(t *testing.T) {
|
||||
t.Run("0..23", func(t *testing.T) {
|
||||
for i := 0; i <= 23; i++ {
|
||||
data := []byte{byte(cborTypePrimitives) | byte(i)}
|
||||
want := SimpleValue(i)
|
||||
|
||||
switch i {
|
||||
case 20: // false
|
||||
testUnmarshalSimpleValueToEmptyInterface(t, data, false)
|
||||
case 21: // true
|
||||
testUnmarshalSimpleValueToEmptyInterface(t, data, true)
|
||||
case 22: // null
|
||||
testUnmarshalSimpleValueToEmptyInterface(t, data, nil)
|
||||
case 23: // undefined
|
||||
testUnmarshalSimpleValueToEmptyInterface(t, data, nil)
|
||||
default:
|
||||
testUnmarshalSimpleValueToEmptyInterface(t, data, want)
|
||||
}
|
||||
|
||||
testUnmarshalSimpleValue(t, data, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("24..31", func(t *testing.T) {
|
||||
for i := 24; i <= 31; i++ {
|
||||
data := []byte{byte(cborTypePrimitives) | byte(24), byte(i)}
|
||||
|
||||
testUnmarshalInvalidSimpleValueToEmptyInterface(t, data)
|
||||
testUnmarshalInvalidSimpleValue(t, data)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("32..255", func(t *testing.T) {
|
||||
for i := 32; i <= 255; i++ {
|
||||
data := []byte{byte(cborTypePrimitives) | byte(24), byte(i)}
|
||||
want := SimpleValue(i)
|
||||
testUnmarshalSimpleValueToEmptyInterface(t, data, want)
|
||||
testUnmarshalSimpleValue(t, data, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func testUnmarshalInvalidSimpleValueToEmptyInterface(t *testing.T, data []byte) {
|
||||
var v interface{}
|
||||
if err := Unmarshal(data, v); err == nil {
|
||||
t.Errorf("Unmarshal(0x%x) didn't return an error", data)
|
||||
} else if _, ok := err.(*SyntaxError); !ok {
|
||||
t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*SyntaxError)", data, err)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnmarshalInvalidSimpleValue(t *testing.T, data []byte) {
|
||||
var v SimpleValue
|
||||
if err := Unmarshal(data, v); err == nil {
|
||||
t.Errorf("Unmarshal(0x%x) didn't return an error", data)
|
||||
} else if _, ok := err.(*SyntaxError); !ok {
|
||||
t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*SyntaxError)", data, err)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnmarshalSimpleValueToEmptyInterface(t *testing.T, data []byte, want interface{}) {
|
||||
var v interface{}
|
||||
if err := Unmarshal(data, &v); err != nil {
|
||||
t.Errorf("Unmarshal(0x%x) returned error %v", data, err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(v, want) {
|
||||
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", data, v, v, want, want)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnmarshalSimpleValue(t *testing.T, data []byte, want SimpleValue) {
|
||||
cborNil := isCBORNil(data)
|
||||
|
||||
// Decode to SimpleValue
|
||||
var v SimpleValue
|
||||
err := Unmarshal(data, &v)
|
||||
if err != nil {
|
||||
t.Errorf("Unmarshal(0x%x) returned error %v", data, err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(v, want) {
|
||||
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", data, v, v, want, want)
|
||||
}
|
||||
|
||||
// Decode to uninitialized *SimpleValue
|
||||
var pv *SimpleValue
|
||||
err = Unmarshal(data, &pv)
|
||||
if err != nil {
|
||||
t.Errorf("Unmarshal(0x%x) returned error %v", data, err)
|
||||
return
|
||||
}
|
||||
if cborNil {
|
||||
if pv != nil {
|
||||
t.Errorf("Unmarshal(0x%x) returned %v, want nil *SimpleValue", data, *pv)
|
||||
}
|
||||
} else {
|
||||
if !reflect.DeepEqual(*pv, want) {
|
||||
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", data, *pv, *pv, want, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Decode to initialized *SimpleValue
|
||||
v = SimpleValue(0)
|
||||
pv = &v
|
||||
err = Unmarshal(data, &pv)
|
||||
if err != nil {
|
||||
t.Errorf("Unmarshal(0x%x) returned error %v", data, err)
|
||||
return
|
||||
}
|
||||
if cborNil {
|
||||
if pv != nil {
|
||||
t.Errorf("Unmarshal(0x%x) returned %v, want nil *SimpleValue", data, *pv)
|
||||
}
|
||||
} else {
|
||||
if !reflect.DeepEqual(v, want) {
|
||||
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", data, v, v, want, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalSimpleValue(t *testing.T) {
|
||||
t.Run("0..23", func(t *testing.T) {
|
||||
for i := 0; i <= 23; i++ {
|
||||
wantData := []byte{byte(cborTypePrimitives) | byte(i)}
|
||||
v := SimpleValue(i)
|
||||
|
||||
data, err := Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("Marshal(%v) returned error %v", v, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(data, wantData) {
|
||||
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, data, wantData)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("24..31", func(t *testing.T) {
|
||||
for i := 24; i <= 31; i++ {
|
||||
v := SimpleValue(i)
|
||||
|
||||
if data, err := Marshal(v); err == nil {
|
||||
t.Errorf("Marshal(%v) didn't return an error", data)
|
||||
} else if _, ok := err.(*UnsupportedValueError); !ok {
|
||||
t.Errorf("Marshal(%v) returned wrong error type %T, want (*UnsupportedValueError)", data, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("32..255", func(t *testing.T) {
|
||||
for i := 32; i <= 255; i++ {
|
||||
wantData := []byte{byte(cborTypePrimitives) | byte(24), byte(i)}
|
||||
v := SimpleValue(i)
|
||||
|
||||
data, err := Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("Marshal(%v) returned error %v", v, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(data, wantData) {
|
||||
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, data, wantData)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
181
stream.go
181
stream.go
@ -4,60 +4,48 @@
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Decoder reads and decodes CBOR values from io.Reader.
|
||||
// Decoder reads and decodes CBOR values from an input stream.
|
||||
type Decoder struct {
|
||||
r io.Reader
|
||||
d decoder
|
||||
buf []byte
|
||||
off int // next read offset in buf
|
||||
d decodeState
|
||||
off int // start of unread data in buf
|
||||
bytesRead int
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder that reads and decodes from r using
|
||||
// the default decoding options.
|
||||
// NewDecoder returns a new decoder that reads 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.
|
||||
// Decode reads the next CBOR-encoded value from its input and stores it in
|
||||
// the value pointed to by v.
|
||||
func (dec *Decoder) Decode(v interface{}) error {
|
||||
_, err := dec.readNext()
|
||||
if err != nil {
|
||||
// Return validation error or read error.
|
||||
return err
|
||||
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)
|
||||
|
||||
// Increment dec.off even if decoding err is not nil because
|
||||
// dec.d.off points to the next CBOR data item if current
|
||||
// CBOR data item is valid but failed to be decoded into v.
|
||||
// This allows next CBOR data item to be decoded in next
|
||||
// call to this function.
|
||||
err := dec.d.value(v)
|
||||
dec.off += dec.d.off
|
||||
dec.bytesRead += dec.d.off
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Skip skips to the next CBOR data item (if there is any),
|
||||
// otherwise it returns error such as io.EOF, io.UnexpectedEOF, etc.
|
||||
func (dec *Decoder) Skip() error {
|
||||
n, err := dec.readNext()
|
||||
if err != nil {
|
||||
// Return validation error or read error.
|
||||
return err
|
||||
if err != io.ErrUnexpectedEOF {
|
||||
return err
|
||||
}
|
||||
// Need to read more data.
|
||||
if n, e := dec.read(); n == 0 {
|
||||
return e
|
||||
}
|
||||
return dec.Decode(v)
|
||||
}
|
||||
|
||||
dec.off += n
|
||||
dec.bytesRead += n
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -66,83 +54,20 @@ func (dec *Decoder) NumBytesRead() int {
|
||||
return dec.bytesRead
|
||||
}
|
||||
|
||||
// Buffered returns a reader for data remaining in Decoder's buffer.
|
||||
// Returned reader is valid until the next call to Decode or Skip.
|
||||
func (dec *Decoder) Buffered() io.Reader {
|
||||
return bytes.NewReader(dec.buf[dec.off:])
|
||||
}
|
||||
|
||||
// readNext() reads next CBOR data item from Reader to buffer.
|
||||
// It returns the size of next CBOR data item.
|
||||
// It also returns validation error or read error if any.
|
||||
func (dec *Decoder) readNext() (int, error) {
|
||||
var readErr error
|
||||
var validErr error
|
||||
|
||||
for {
|
||||
// Process any unread data in dec.buf.
|
||||
if dec.off < len(dec.buf) {
|
||||
dec.d.reset(dec.buf[dec.off:])
|
||||
off := dec.off // Save offset before data validation
|
||||
validErr = dec.d.wellformed(true, false)
|
||||
dec.off = off // Restore offset
|
||||
|
||||
if validErr == nil {
|
||||
return dec.d.off, nil
|
||||
}
|
||||
|
||||
if validErr != io.ErrUnexpectedEOF {
|
||||
return 0, validErr
|
||||
}
|
||||
|
||||
// Process last read error on io.ErrUnexpectedEOF.
|
||||
if readErr != nil {
|
||||
if readErr == io.EOF {
|
||||
// current CBOR data item is incomplete.
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
return 0, readErr
|
||||
}
|
||||
}
|
||||
|
||||
// More data is needed and there was no read error.
|
||||
var n int
|
||||
for n == 0 {
|
||||
n, readErr = dec.read()
|
||||
if n == 0 && readErr != nil {
|
||||
// No more data can be read and read error is encountered.
|
||||
// At this point, validErr is either nil or io.ErrUnexpectedEOF.
|
||||
if readErr == io.EOF {
|
||||
if validErr == io.ErrUnexpectedEOF {
|
||||
// current CBOR data item is incomplete.
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
}
|
||||
return 0, readErr
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, dec.buf contains new data from last read (n > 0).
|
||||
}
|
||||
}
|
||||
|
||||
// read() reads data from Reader to buffer.
|
||||
// It returns number of bytes read and any read error encountered.
|
||||
// Postconditions:
|
||||
// - dec.buf contains previously unread data and new data.
|
||||
// - dec.off is 0.
|
||||
func (dec *Decoder) read() (int, error) {
|
||||
// Grow buf if needed.
|
||||
const minRead = 512
|
||||
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:])
|
||||
n := copy(dec.buf, dec.buf[dec.off:])
|
||||
dec.buf = dec.buf[:n]
|
||||
dec.off = 0
|
||||
}
|
||||
|
||||
// Grow buf if needed.
|
||||
const minRead = 512
|
||||
if cap(dec.buf)-len(dec.buf) < minRead {
|
||||
newBuf := make([]byte, len(dec.buf), 2*cap(dec.buf)+minRead)
|
||||
copy(newBuf, dec.buf)
|
||||
dec.buf = newBuf
|
||||
}
|
||||
|
||||
// Read from reader and reslice buf.
|
||||
@ -151,16 +76,11 @@ func (dec *Decoder) read() (int, error) {
|
||||
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.
|
||||
// Encoder writes CBOR values to an output stream.
|
||||
type Encoder struct {
|
||||
w io.Writer
|
||||
em *encMode
|
||||
e *encodeState
|
||||
indefTypes []cborType
|
||||
}
|
||||
|
||||
@ -169,7 +89,7 @@ func NewEncoder(w io.Writer) *Encoder {
|
||||
return defaultEncMode.NewEncoder(w)
|
||||
}
|
||||
|
||||
// Encode writes the CBOR encoding of v.
|
||||
// Encode writes the CBOR encoding of v to the stream.
|
||||
func (enc *Encoder) Encode(v interface{}) error {
|
||||
if len(enc.indefTypes) > 0 && v != nil {
|
||||
indefType := enc.indefTypes[len(enc.indefTypes)-1]
|
||||
@ -187,27 +107,24 @@ func (enc *Encoder) Encode(v interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
buf := getEncodeBuffer()
|
||||
|
||||
err := encode(buf, enc.em, reflect.ValueOf(v))
|
||||
err := encode(enc.e, enc.em, reflect.ValueOf(v))
|
||||
if err == nil {
|
||||
_, err = enc.w.Write(buf.Bytes())
|
||||
_, err = enc.e.WriteTo(enc.w)
|
||||
}
|
||||
|
||||
putEncodeBuffer(buf)
|
||||
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 contiguous string until EndIndefinite is called.
|
||||
// ("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 contiguous string until EndIndefinite is called.
|
||||
// ("chunks") as one continguous string until EndIndefinite is called.
|
||||
func (enc *Encoder) StartIndefiniteTextString() error {
|
||||
return enc.startIndefinite(cborTypeTextString)
|
||||
}
|
||||
@ -231,7 +148,7 @@ 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{cborBreakFlag})
|
||||
_, err := enc.w.Write([]byte{0xff})
|
||||
if err == nil {
|
||||
enc.indefTypes = enc.indefTypes[:len(enc.indefTypes)-1]
|
||||
}
|
||||
@ -239,15 +156,15 @@ func (enc *Encoder) EndIndefinite() error {
|
||||
}
|
||||
|
||||
var cborIndefHeader = map[cborType][]byte{
|
||||
cborTypeByteString: {cborByteStringWithIndefiniteLengthHead},
|
||||
cborTypeTextString: {cborTextStringWithIndefiniteLengthHead},
|
||||
cborTypeArray: {cborArrayWithIndefiniteLengthHead},
|
||||
cborTypeMap: {cborMapWithIndefiniteLengthHead},
|
||||
cborTypeByteString: {0x5f},
|
||||
cborTypeTextString: {0x7f},
|
||||
cborTypeArray: {0x9f},
|
||||
cborTypeMap: {0xbf},
|
||||
}
|
||||
|
||||
func (enc *Encoder) startIndefinite(typ cborType) error {
|
||||
if enc.em.indefLength == IndefLengthForbidden {
|
||||
return &IndefiniteLengthError{typ}
|
||||
if enc.em.disableIndefiniteLength {
|
||||
return errors.New("cbor: indefinite-length items are not allowed")
|
||||
}
|
||||
_, err := enc.w.Write(cborIndefHeader[typ])
|
||||
if err == nil {
|
||||
@ -256,10 +173,12 @@ func (enc *Encoder) startIndefinite(typ cborType) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// RawMessage is a raw encoded CBOR value.
|
||||
// RawMessage is a raw encoded CBOR value. It implements Marshaler and
|
||||
// Unmarshaler interfaces and can be used to delay CBOR decoding or
|
||||
// precompute a CBOR encoding.
|
||||
type RawMessage []byte
|
||||
|
||||
// MarshalCBOR returns m or CBOR nil if m is nil.
|
||||
// MarshalCBOR returns m as the CBOR encoding of m.
|
||||
func (m RawMessage) MarshalCBOR() ([]byte, error) {
|
||||
if len(m) == 0 {
|
||||
return cborNil, nil
|
||||
@ -267,7 +186,7 @@ func (m RawMessage) MarshalCBOR() ([]byte, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// UnmarshalCBOR creates a copy of data and saves to *m.
|
||||
// UnmarshalCBOR sets *m to a copy of data.
|
||||
func (m *RawMessage) UnmarshalCBOR(data []byte) error {
|
||||
if m == nil {
|
||||
return errors.New("cbor.RawMessage: UnmarshalCBOR on nil pointer")
|
||||
|
||||
792
stream_test.go
792
stream_test.go
@ -5,11 +5,8 @@ package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@ -18,293 +15,33 @@ func TestDecoder(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < 5; i++ {
|
||||
for _, tc := range unmarshalTests {
|
||||
buf.Write(tc.data)
|
||||
buf.Write(tc.cborData)
|
||||
}
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
reader io.Reader
|
||||
}{
|
||||
{"bytes.Buffer", &buf},
|
||||
{"1 byte reader", newNBytesReader(buf.Bytes(), 1)},
|
||||
{"toggled reader", newToggledReader(buf.Bytes(), 1)},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
decoder := NewDecoder(tc.reader)
|
||||
bytesRead := 0
|
||||
for i := 0; i < 5; i++ {
|
||||
for _, tc := range unmarshalTests {
|
||||
var v interface{}
|
||||
if err := decoder.Decode(&v); err != nil {
|
||||
t.Fatalf("Decode() returned error %v", err)
|
||||
}
|
||||
if tm, ok := tc.wantInterfaceValue.(time.Time); ok {
|
||||
if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) {
|
||||
t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.wantInterfaceValue, tc.wantInterfaceValue)
|
||||
}
|
||||
} else if !reflect.DeepEqual(v, tc.wantInterfaceValue) {
|
||||
t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.wantInterfaceValue, tc.wantInterfaceValue)
|
||||
}
|
||||
bytesRead += len(tc.data)
|
||||
if decoder.NumBytesRead() != bytesRead {
|
||||
t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead)
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
// no more data
|
||||
var v interface{}
|
||||
err := decoder.Decode(&v)
|
||||
if v != nil {
|
||||
t.Errorf("Decode() = %v (%T), want nil (no more data)", v, v)
|
||||
}
|
||||
if err != io.EOF {
|
||||
t.Errorf("Decode() returned error %v, want io.EOF (no more data)", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderUnmarshalTypeError(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
decoder := NewDecoder(&buf)
|
||||
bytesRead := 0
|
||||
for i := 0; i < 5; i++ {
|
||||
for _, tc := range unmarshalTests {
|
||||
for j := 0; j < len(tc.wrongTypes)*2; j++ {
|
||||
buf.Write(tc.data)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
reader io.Reader
|
||||
}{
|
||||
{"bytes.Buffer", &buf},
|
||||
{"1 byte reader", newNBytesReader(buf.Bytes(), 1)},
|
||||
{"toggled reader", newToggledReader(buf.Bytes(), 1)},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
decoder := NewDecoder(tc.reader)
|
||||
bytesRead := 0
|
||||
for i := 0; i < 5; i++ {
|
||||
for _, tc := range unmarshalTests {
|
||||
for _, typ := range tc.wrongTypes {
|
||||
v := reflect.New(typ)
|
||||
if err := decoder.Decode(v.Interface()); err == nil {
|
||||
t.Errorf("Decode(0x%x) didn't return an error, want UnmarshalTypeError", tc.data)
|
||||
} else if _, ok := err.(*UnmarshalTypeError); !ok {
|
||||
t.Errorf("Decode(0x%x) returned wrong error type %T, want UnmarshalTypeError", tc.data, err)
|
||||
}
|
||||
bytesRead += len(tc.data)
|
||||
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.wantInterfaceValue.(time.Time); ok {
|
||||
if vt, ok := vi.(time.Time); !ok || !tm.Equal(vt) {
|
||||
t.Errorf("Decode() = %v (%T), want %v (%T)", vi, vi, tc.wantInterfaceValue, tc.wantInterfaceValue)
|
||||
}
|
||||
} else if !reflect.DeepEqual(vi, tc.wantInterfaceValue) {
|
||||
t.Errorf("Decode() = %v (%T), want %v (%T)", vi, vi, tc.wantInterfaceValue, tc.wantInterfaceValue)
|
||||
}
|
||||
bytesRead += len(tc.data)
|
||||
if decoder.NumBytesRead() != bytesRead {
|
||||
t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
// no more data
|
||||
var v interface{}
|
||||
err := decoder.Decode(&v)
|
||||
if v != nil {
|
||||
t.Errorf("Decode() = %v (%T), want nil (no more data)", v, v)
|
||||
}
|
||||
if err != io.EOF {
|
||||
t.Errorf("Decode() returned error %v, want io.EOF (no more data)", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderUnexpectedEOFError(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
for _, tc := range unmarshalTests {
|
||||
buf.Write(tc.data)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
reader io.Reader
|
||||
}{
|
||||
{"bytes.Buffer", &buf},
|
||||
{"1 byte reader", newNBytesReader(buf.Bytes(), 1)},
|
||||
{"toggled reader", newToggledReader(buf.Bytes(), 1)},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
decoder := NewDecoder(tc.reader)
|
||||
bytesRead := 0
|
||||
for i := 0; i < len(unmarshalTests)-1; i++ {
|
||||
tc := unmarshalTests[i]
|
||||
var v interface{}
|
||||
if err := decoder.Decode(&v); err != nil {
|
||||
t.Fatalf("Decode() returned error %v", err)
|
||||
}
|
||||
if tm, ok := tc.wantInterfaceValue.(time.Time); ok {
|
||||
if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) {
|
||||
t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.wantInterfaceValue, tc.wantInterfaceValue)
|
||||
}
|
||||
} else if !reflect.DeepEqual(v, tc.wantInterfaceValue) {
|
||||
t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.wantInterfaceValue, tc.wantInterfaceValue)
|
||||
}
|
||||
bytesRead += len(tc.data)
|
||||
if decoder.NumBytesRead() != bytesRead {
|
||||
t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead)
|
||||
}
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
// truncated data
|
||||
var v interface{}
|
||||
err := decoder.Decode(&v)
|
||||
if v != nil {
|
||||
t.Errorf("Decode() = %v (%T), want nil (no more data)", v, v)
|
||||
}
|
||||
if err != io.ErrUnexpectedEOF {
|
||||
t.Errorf("Decode() returned error %v, want io.UnexpectedEOF (truncated data)", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderReadError(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
for _, tc := range unmarshalTests {
|
||||
buf.Write(tc.data)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
|
||||
readerErr := errors.New("reader error")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
reader io.Reader
|
||||
}{
|
||||
{"byte reader", newNBytesReaderWithError(buf.Bytes(), 512, readerErr)},
|
||||
{"1 byte reader", newNBytesReaderWithError(buf.Bytes(), 1, readerErr)},
|
||||
{"toggled reader", newToggledReaderWithError(buf.Bytes(), 1, readerErr)},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
decoder := NewDecoder(tc.reader)
|
||||
bytesRead := 0
|
||||
for i := 0; i < len(unmarshalTests)-1; i++ {
|
||||
tc := unmarshalTests[i]
|
||||
var v interface{}
|
||||
if err := decoder.Decode(&v); err != nil {
|
||||
t.Fatalf("Decode() returned error %v", err)
|
||||
}
|
||||
if tm, ok := tc.wantInterfaceValue.(time.Time); ok {
|
||||
if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) {
|
||||
t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.wantInterfaceValue, tc.wantInterfaceValue)
|
||||
}
|
||||
} else if !reflect.DeepEqual(v, tc.wantInterfaceValue) {
|
||||
t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.wantInterfaceValue, tc.wantInterfaceValue)
|
||||
}
|
||||
bytesRead += len(tc.data)
|
||||
if decoder.NumBytesRead() != bytesRead {
|
||||
t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead)
|
||||
}
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
// truncated data because Reader returned error
|
||||
var v interface{}
|
||||
err := decoder.Decode(&v)
|
||||
if v != nil {
|
||||
t.Errorf("Decode() = %v (%T), want nil (no more data)", v, v)
|
||||
}
|
||||
if err != readerErr {
|
||||
t.Errorf("Decode() returned error %v, want reader error", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderNoData(t *testing.T) {
|
||||
readerErr := errors.New("reader error")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
reader io.Reader
|
||||
wantErr error
|
||||
}{
|
||||
{"byte.Buffer", new(bytes.Buffer), io.EOF},
|
||||
{"1 byte reader", newNBytesReaderWithError(nil, 0, readerErr), readerErr},
|
||||
{"toggled reader", newToggledReaderWithError(nil, 0, readerErr), readerErr},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
decoder := NewDecoder(tc.reader)
|
||||
for i := 0; i < 2; i++ {
|
||||
var v interface{}
|
||||
err := decoder.Decode(&v)
|
||||
if v != nil {
|
||||
t.Errorf("Decode() = %v (%T), want nil", v, v)
|
||||
}
|
||||
if err != tc.wantErr {
|
||||
t.Errorf("Decode() returned error %v, want error %v", err, tc.wantErr)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderRecoverableReadError(t *testing.T) {
|
||||
data := hexDecode("83010203") // [1,2,3]
|
||||
wantValue := []interface{}{uint64(1), uint64(2), uint64(3)}
|
||||
recoverableReaderErr := errors.New("recoverable reader error")
|
||||
|
||||
decoder := NewDecoder(newRecoverableReader(data, 1, recoverableReaderErr))
|
||||
|
||||
// no more data
|
||||
var v interface{}
|
||||
err := decoder.Decode(&v)
|
||||
if err != recoverableReaderErr {
|
||||
t.Fatalf("Decode() returned error %v, want error %v", err, recoverableReaderErr)
|
||||
}
|
||||
|
||||
err = decoder.Decode(&v)
|
||||
if err != nil {
|
||||
t.Fatalf("Decode() returned error %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(v, wantValue) {
|
||||
t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, wantValue, wantValue)
|
||||
}
|
||||
if decoder.NumBytesRead() != len(data) {
|
||||
t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), len(data))
|
||||
}
|
||||
|
||||
// no more data
|
||||
v = interface{}(nil)
|
||||
err = decoder.Decode(&v)
|
||||
if v != nil {
|
||||
t.Errorf("Decode() = %v (%T), want nil (no more data)", v, v)
|
||||
}
|
||||
@ -313,244 +50,57 @@ func TestDecoderRecoverableReadError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderInvalidData(t *testing.T) {
|
||||
data := []byte{0x01, 0x3e}
|
||||
decoder := NewDecoder(bytes.NewReader(data))
|
||||
|
||||
var v1 interface{}
|
||||
err := decoder.Decode(&v1)
|
||||
if err != nil {
|
||||
t.Errorf("Decode() returned error %v when decoding valid data item", err)
|
||||
}
|
||||
|
||||
var v2 interface{}
|
||||
err = decoder.Decode(&v2)
|
||||
if err == nil {
|
||||
t.Errorf("Decode() didn't return error when decoding invalid data item")
|
||||
} else if !strings.Contains(err.Error(), "cbor: invalid additional information") {
|
||||
t.Errorf("Decode() error %q, want \"cbor: invalid additional information\"", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderSkip(t *testing.T) {
|
||||
func TestDecoderUnmarshalTypeError(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < 5; i++ {
|
||||
for _, tc := range unmarshalTests {
|
||||
buf.Write(tc.data)
|
||||
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)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
reader io.Reader
|
||||
}{
|
||||
{"bytes.Buffer", &buf},
|
||||
{"1 byte reader", newNBytesReader(buf.Bytes(), 1)},
|
||||
{"toggled reader", newToggledReader(buf.Bytes(), 1)},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
decoder := NewDecoder(tc.reader)
|
||||
bytesRead := 0
|
||||
for i := 0; i < 5; i++ {
|
||||
for _, tc := range unmarshalTests {
|
||||
if err := decoder.Skip(); err != nil {
|
||||
t.Fatalf("Skip() returned error %v", err)
|
||||
}
|
||||
bytesRead += len(tc.data)
|
||||
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)
|
||||
}
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
// no more data
|
||||
err := decoder.Skip()
|
||||
if err != io.EOF {
|
||||
t.Errorf("Skip() returned error %v, want io.EOF (no more data)", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderSkipInvalidDataError(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
for _, tc := range unmarshalTests {
|
||||
buf.Write(tc.data)
|
||||
}
|
||||
buf.WriteByte(0x3e)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
reader io.Reader
|
||||
}{
|
||||
{"bytes.Buffer", &buf},
|
||||
{"1 byte reader", newNBytesReader(buf.Bytes(), 1)},
|
||||
{"toggled reader", newToggledReader(buf.Bytes(), 1)},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
decoder := NewDecoder(tc.reader)
|
||||
bytesRead := 0
|
||||
for i := 0; i < len(unmarshalTests); i++ {
|
||||
tc := unmarshalTests[i]
|
||||
if err := decoder.Skip(); err != nil {
|
||||
t.Fatalf("Skip() returned error %v", err)
|
||||
}
|
||||
bytesRead += len(tc.data)
|
||||
bytesRead += len(tc.cborData)
|
||||
if decoder.NumBytesRead() != bytesRead {
|
||||
t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead)
|
||||
}
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
// last data item is invalid
|
||||
err := decoder.Skip()
|
||||
if err == nil {
|
||||
t.Fatalf("Skip() didn't return error")
|
||||
} else if !strings.Contains(err.Error(), "cbor: invalid additional information") {
|
||||
t.Errorf("Skip() error %q, want \"cbor: invalid additional information\"", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderSkipUnexpectedEOFError(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
for _, tc := range unmarshalTests {
|
||||
buf.Write(tc.data)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
reader io.Reader
|
||||
}{
|
||||
{"bytes.Buffer", &buf},
|
||||
{"1 byte reader", newNBytesReader(buf.Bytes(), 1)},
|
||||
{"toggled reader", newToggledReader(buf.Bytes(), 1)},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
decoder := NewDecoder(tc.reader)
|
||||
bytesRead := 0
|
||||
for i := 0; i < len(unmarshalTests)-1; i++ {
|
||||
tc := unmarshalTests[i]
|
||||
if err := decoder.Skip(); err != nil {
|
||||
t.Fatalf("Skip() returned error %v", err)
|
||||
}
|
||||
bytesRead += len(tc.data)
|
||||
if decoder.NumBytesRead() != bytesRead {
|
||||
t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead)
|
||||
}
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
// last data item is invalid
|
||||
err := decoder.Skip()
|
||||
if err != io.ErrUnexpectedEOF {
|
||||
t.Errorf("Skip() returned error %v, want io.ErrUnexpectedEOF (truncated data)", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderSkipReadError(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
for _, tc := range unmarshalTests {
|
||||
buf.Write(tc.data)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
|
||||
readerErr := errors.New("reader error")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
reader io.Reader
|
||||
}{
|
||||
{"byte reader", newNBytesReaderWithError(buf.Bytes(), 512, readerErr)},
|
||||
{"1 byte reader", newNBytesReaderWithError(buf.Bytes(), 1, readerErr)},
|
||||
{"toggled reader", newToggledReaderWithError(buf.Bytes(), 1, readerErr)},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
decoder := NewDecoder(tc.reader)
|
||||
bytesRead := 0
|
||||
for i := 0; i < len(unmarshalTests)-1; i++ {
|
||||
tc := unmarshalTests[i]
|
||||
if err := decoder.Skip(); err != nil {
|
||||
t.Fatalf("Skip() returned error %v", err)
|
||||
}
|
||||
bytesRead += len(tc.data)
|
||||
if decoder.NumBytesRead() != bytesRead {
|
||||
t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), bytesRead)
|
||||
}
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
// truncated data because Reader returned error
|
||||
err := decoder.Skip()
|
||||
if err != readerErr {
|
||||
t.Errorf("Skip() returned error %v, want reader error", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderSkipNoData(t *testing.T) {
|
||||
readerErr := errors.New("reader error")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
reader io.Reader
|
||||
wantErr error
|
||||
}{
|
||||
{"byte.Buffer", new(bytes.Buffer), io.EOF},
|
||||
{"1 byte reader", newNBytesReaderWithError(nil, 0, readerErr), readerErr},
|
||||
{"toggled reader", newToggledReaderWithError(nil, 0, readerErr), readerErr},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
decoder := NewDecoder(tc.reader)
|
||||
for i := 0; i < 2; i++ {
|
||||
err := decoder.Skip()
|
||||
if err != tc.wantErr {
|
||||
t.Errorf("Decode() returned error %v, want error %v", err, tc.wantErr)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderSkipRecoverableReadError(t *testing.T) {
|
||||
data := hexDecode("83010203") // [1,2,3]
|
||||
recoverableReaderErr := errors.New("recoverable reader error")
|
||||
|
||||
decoder := NewDecoder(newRecoverableReader(data, 1, recoverableReaderErr))
|
||||
|
||||
err := decoder.Skip()
|
||||
if err != recoverableReaderErr {
|
||||
t.Fatalf("Skip() returned error %v, want error %v", err, recoverableReaderErr)
|
||||
}
|
||||
|
||||
err = decoder.Skip()
|
||||
if err != nil {
|
||||
t.Fatalf("Skip() returned error %v", err)
|
||||
}
|
||||
if decoder.NumBytesRead() != len(data) {
|
||||
t.Errorf("NumBytesRead() = %v, want %v", decoder.NumBytesRead(), len(data))
|
||||
}
|
||||
|
||||
// no more data
|
||||
err = decoder.Skip()
|
||||
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("Skip() returned error %v, want io.EOF (no more data)", err)
|
||||
t.Errorf("Decode() returned error %v, want io.EOF (no more data)", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -565,10 +115,10 @@ func TestDecoderStructTag(t *testing.T) {
|
||||
B: "B",
|
||||
C: "C",
|
||||
}
|
||||
data := hexDecode("a36161614161626142617a6143") // {"a":"A", "b":"B", "z":"C"}
|
||||
cborData := hexDecode("a36161614161626142617a6143") // {"a":"A", "b":"B", "z":"C"}
|
||||
|
||||
var v strc
|
||||
dec := NewDecoder(bytes.NewReader(data))
|
||||
dec := NewDecoder(bytes.NewReader(cborData))
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
t.Errorf("Decode() returned error %v", err)
|
||||
}
|
||||
@ -577,92 +127,6 @@ func TestDecoderStructTag(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderBuffered(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
buffered []byte
|
||||
decodeErr error
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
data: []byte{},
|
||||
buffered: []byte{},
|
||||
decodeErr: io.EOF,
|
||||
},
|
||||
{
|
||||
name: "malformed CBOR data item",
|
||||
data: []byte{0xc0},
|
||||
buffered: []byte{0xc0},
|
||||
decodeErr: io.ErrUnexpectedEOF,
|
||||
},
|
||||
{
|
||||
name: "1 CBOR data item",
|
||||
data: []byte{0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
buffered: []byte{},
|
||||
},
|
||||
{
|
||||
name: "2 CBOR data items",
|
||||
data: []byte{
|
||||
// First CBOR data item
|
||||
0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Second CBOR data item
|
||||
0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
buffered: []byte{
|
||||
// Second CBOR data item
|
||||
0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "1 CBOR data item followed by non-CBOR data",
|
||||
data: []byte{
|
||||
// CBOR data item
|
||||
0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Extraneous non-CBOR data ("abc")
|
||||
0x61, 0x62, 0x63,
|
||||
},
|
||||
buffered: []byte{
|
||||
// non-CBOR data ("abc")
|
||||
0x61, 0x62, 0x63,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := bytes.NewReader(tc.data)
|
||||
|
||||
decoder := NewDecoder(r)
|
||||
|
||||
// Decoder's buffer has no data yet.
|
||||
br := decoder.Buffered()
|
||||
buffered, err := io.ReadAll(br)
|
||||
if err != nil {
|
||||
t.Errorf("failed to read from reader returned by Buffered(): %v", err)
|
||||
}
|
||||
if len(buffered) > 0 {
|
||||
t.Errorf("Buffered() = 0x%x (%d bytes), want 0 bytes", buffered, len(buffered))
|
||||
}
|
||||
|
||||
var v interface{}
|
||||
err = decoder.Decode(&v)
|
||||
if err != tc.decodeErr {
|
||||
t.Errorf("Decode() returned error %v, want %v", err, tc.decodeErr)
|
||||
}
|
||||
|
||||
br = decoder.Buffered()
|
||||
buffered, err = io.ReadAll(br)
|
||||
if err != nil {
|
||||
t.Errorf("failed to read from reader returned by Buffered(): %v", err)
|
||||
}
|
||||
if !bytes.Equal(tc.buffered, buffered) {
|
||||
t.Errorf("Buffered() = 0x%x (%d bytes), want 0x%x (%d bytes)", buffered, len(buffered), tc.buffered, len(tc.buffered))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncoder(t *testing.T) {
|
||||
var want bytes.Buffer
|
||||
var w bytes.Buffer
|
||||
@ -673,7 +137,7 @@ func TestEncoder(t *testing.T) {
|
||||
encoder := em.NewEncoder(&w)
|
||||
for _, tc := range marshalTests {
|
||||
for _, value := range tc.values {
|
||||
want.Write(tc.wantData)
|
||||
want.Write(tc.cborData)
|
||||
|
||||
if err := encoder.Encode(value); err != nil {
|
||||
t.Fatalf("Encode() returned error %v", err)
|
||||
@ -692,20 +156,19 @@ func TestEncoderError(t *testing.T) {
|
||||
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"},
|
||||
{"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) {
|
||||
v := tc.value
|
||||
err := encoder.Encode(&v)
|
||||
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 !strings.HasPrefix(err.Error(), tc.wantErrorMsg) {
|
||||
} else if err.Error() != tc.wantErrorMsg {
|
||||
t.Errorf("Encode(%v) error %q, want %q", tc.value, err.Error(), tc.wantErrorMsg)
|
||||
}
|
||||
})
|
||||
@ -904,39 +367,25 @@ func TestRawMessage(t *testing.T) {
|
||||
B *RawMessage `cbor:"b"`
|
||||
C *RawMessage `cbor:"c"`
|
||||
}
|
||||
data := hexDecode("a361610161628202036163f6") // {"a": 1, "b": [2, 3], "c": nil},
|
||||
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(data, &v); err != nil {
|
||||
t.Fatalf("Unmarshal(0x%x) returned error %v", data, err)
|
||||
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", data, 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, data) {
|
||||
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, data)
|
||||
}
|
||||
|
||||
address := fmt.Sprintf("%p", *v.B)
|
||||
if err := Unmarshal(v.A, v.B); err != nil {
|
||||
t.Fatalf("Unmarshal(0x%x) returned error %v", v.A, err)
|
||||
}
|
||||
if address != fmt.Sprintf("%p", *v.B) {
|
||||
t.Fatalf("Unmarshal RawMessage should reuse underlying array if it has sufficient capacity")
|
||||
}
|
||||
if err := Unmarshal(data, v.B); err != nil {
|
||||
t.Fatalf("Unmarshal(0x%x) returned error %v", data, err)
|
||||
}
|
||||
if address == fmt.Sprintf("%p", *v.B) {
|
||||
t.Fatalf("Unmarshal RawMessage should allocate a new underlying array if it does not have sufficient capacity")
|
||||
if !bytes.Equal(b, cborData) {
|
||||
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, cborData)
|
||||
}
|
||||
}
|
||||
|
||||
@ -967,115 +416,10 @@ func TestEmptyRawMessage(t *testing.T) {
|
||||
func TestNilRawMessageUnmarshalCBORError(t *testing.T) {
|
||||
wantErrorMsg := "cbor.RawMessage: UnmarshalCBOR on nil pointer"
|
||||
var r *RawMessage
|
||||
data := hexDecode("01")
|
||||
if err := r.UnmarshalCBOR(data); err == nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// nBytesReader reads at most maxBytesPerRead into b. It also returns error at the last read.
|
||||
type nBytesReader struct {
|
||||
data []byte
|
||||
maxBytesPerRead int
|
||||
off int
|
||||
err error
|
||||
}
|
||||
|
||||
func newNBytesReader(data []byte, maxBytesPerRead int) *nBytesReader {
|
||||
return &nBytesReader{
|
||||
data: append([]byte{}, data...),
|
||||
maxBytesPerRead: maxBytesPerRead,
|
||||
err: io.EOF,
|
||||
}
|
||||
}
|
||||
|
||||
func newNBytesReaderWithError(data []byte, maxBytesPerRead int, err error) *nBytesReader {
|
||||
return &nBytesReader{
|
||||
data: append([]byte{}, data...),
|
||||
maxBytesPerRead: maxBytesPerRead,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *nBytesReader) Read(b []byte) (int, error) {
|
||||
var n int
|
||||
if r.off < len(r.data) {
|
||||
numOfBytesToRead := len(r.data) - r.off
|
||||
if numOfBytesToRead > r.maxBytesPerRead {
|
||||
numOfBytesToRead = r.maxBytesPerRead
|
||||
}
|
||||
n = copy(b, r.data[r.off:r.off+numOfBytesToRead])
|
||||
r.off += n
|
||||
}
|
||||
if r.off == len(r.data) {
|
||||
return n, r.err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// toggledReader returns (0, nil) for every other read to mimic non-blocking read for stream reader.
|
||||
type toggledReader struct {
|
||||
nBytesReader
|
||||
toggle bool
|
||||
}
|
||||
|
||||
func newToggledReader(data []byte, maxBytesPerRead int) *toggledReader {
|
||||
return &toggledReader{
|
||||
nBytesReader: nBytesReader{
|
||||
data: append([]byte{}, data...),
|
||||
maxBytesPerRead: maxBytesPerRead,
|
||||
err: io.EOF,
|
||||
},
|
||||
toggle: true, // first read returns (0, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func newToggledReaderWithError(data []byte, maxBytesPerRead int, err error) *toggledReader {
|
||||
return &toggledReader{
|
||||
nBytesReader: nBytesReader{
|
||||
data: append([]byte{}, data...),
|
||||
maxBytesPerRead: maxBytesPerRead,
|
||||
err: err,
|
||||
},
|
||||
toggle: true, // first read returns (0, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *toggledReader) Read(b []byte) (int, error) {
|
||||
defer func() {
|
||||
r.toggle = !r.toggle
|
||||
}()
|
||||
if r.toggle {
|
||||
return 0, nil
|
||||
}
|
||||
return r.nBytesReader.Read(b)
|
||||
}
|
||||
|
||||
// recoverableReader returns a recoverable error at first read operation.
|
||||
type recoverableReader struct {
|
||||
nBytesReader
|
||||
recoverableErr error
|
||||
first bool
|
||||
}
|
||||
|
||||
func newRecoverableReader(data []byte, maxBytesPerRead int, err error) *recoverableReader {
|
||||
return &recoverableReader{
|
||||
nBytesReader: nBytesReader{
|
||||
data: append([]byte{}, data...),
|
||||
maxBytesPerRead: maxBytesPerRead,
|
||||
err: io.EOF,
|
||||
},
|
||||
recoverableErr: err,
|
||||
first: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *recoverableReader) Read(b []byte) (int, error) {
|
||||
if r.first {
|
||||
r.first = false
|
||||
return 0, r.recoverableErr
|
||||
}
|
||||
return r.nBytesReader.Read(b)
|
||||
}
|
||||
|
||||
308
structfields.go
308
structfields.go
@ -10,18 +10,16 @@ import (
|
||||
)
|
||||
|
||||
type field struct {
|
||||
name string
|
||||
nameAsInt int64 // used to decoder to match field name with CBOR int
|
||||
cborName []byte
|
||||
cborNameByteString []byte // major type 2 name encoding iff cborName has major type 3
|
||||
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
|
||||
name string
|
||||
nameAsInt int64 // used to decoder to match field name with CBOR int
|
||||
cborName []byte
|
||||
idx []int
|
||||
typ reflect.Type
|
||||
ef encodeFunc
|
||||
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
|
||||
@ -40,13 +38,20 @@ func (x *indexFieldSorter) Swap(i, j int) {
|
||||
}
|
||||
|
||||
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]
|
||||
iIdx := x.fields[i].idx
|
||||
jIdx := x.fields[j].idx
|
||||
for k, d := range iIdx {
|
||||
if k >= len(jIdx) {
|
||||
// fields[j].idx is a subset of fields[i].idx.
|
||||
return false
|
||||
}
|
||||
if d != jIdx[k] {
|
||||
// fields[i].idx and fields[j].idx are different.
|
||||
return d < jIdx[k]
|
||||
}
|
||||
}
|
||||
return len(iIdx) <= len(jIdx)
|
||||
// fields[i].idx is either the same as, or a subset of fields[j].idx.
|
||||
return true
|
||||
}
|
||||
|
||||
// nameLevelAndTagFieldSorter sorts fields by field name, idx depth, and presence of tag.
|
||||
@ -63,54 +68,99 @@ func (x *nameLevelAndTagFieldSorter) Swap(i, j int) {
|
||||
}
|
||||
|
||||
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 x.fields[i].name != x.fields[j].name {
|
||||
return x.fields[i].name < x.fields[j].name
|
||||
}
|
||||
if len(fi.idx) != len(fj.idx) {
|
||||
return len(fi.idx) < len(fj.idx)
|
||||
if len(x.fields[i].idx) != len(x.fields[j].idx) {
|
||||
return len(x.fields[i].idx) < len(x.fields[j].idx)
|
||||
}
|
||||
if fi.tagged != fj.tagged {
|
||||
return fi.tagged
|
||||
if x.fields[i].tagged != x.fields[j].tagged {
|
||||
return x.fields[i].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
|
||||
}
|
||||
}
|
||||
// getFields returns a list of visible fields of struct type typ following Go
|
||||
// visibility rules for struct fields.
|
||||
func getFields(typ reflect.Type) (flds fields, structOptions string) {
|
||||
// Inspired by typeFields() in stdlib's encoding/json/encode.go.
|
||||
|
||||
// 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)
|
||||
var current map[reflect.Type][][]int // key: struct type, value: field index of this struct type at the same level
|
||||
next := map[reflect.Type][][]int{typ: nil}
|
||||
visited := map[reflect.Type]bool{} // Inspected struct type at less nested levels.
|
||||
|
||||
if len(nTypes) > 0 {
|
||||
for len(next) > 0 {
|
||||
current, next = next, map[reflect.Type][][]int{}
|
||||
|
||||
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 structType, structIdx := range current {
|
||||
if len(structIdx) > 1 {
|
||||
continue // Fields of the same embedded struct type at the same level are ignored.
|
||||
}
|
||||
|
||||
for len(nTypes) > 0 {
|
||||
cTypes, nTypes = nTypes, nil
|
||||
if visited[structType] {
|
||||
continue
|
||||
}
|
||||
visited[structType] = true
|
||||
|
||||
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 {
|
||||
var fieldIdx []int
|
||||
if len(structIdx) > 0 {
|
||||
fieldIdx = structIdx[0]
|
||||
}
|
||||
|
||||
for i := 0; i < structType.NumField(); i++ {
|
||||
f := structType.Field(i)
|
||||
ft := f.Type
|
||||
|
||||
if ft.Kind() == reflect.Ptr {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
|
||||
exportable := f.PkgPath == ""
|
||||
if f.Anonymous {
|
||||
if !exportable && ft.Kind() != reflect.Struct {
|
||||
// Nonexportable anonymous fields of non-struct type are ignored.
|
||||
continue
|
||||
}
|
||||
// Nonexportable anonymous field of struct type can contain exportable fields for serialization.
|
||||
} else if !exportable {
|
||||
// Get special field "_" struct options
|
||||
if f.Name == "_" {
|
||||
tag := f.Tag.Get("cbor")
|
||||
if tag != "-" {
|
||||
structOptions = tag
|
||||
}
|
||||
}
|
||||
// Nonexportable fields are ignored.
|
||||
continue
|
||||
}
|
||||
|
||||
// Anonymous field of the same type at deeper nested level is ignored.
|
||||
if vTypes[t] {
|
||||
tag := f.Tag.Get("cbor")
|
||||
if tag == "" {
|
||||
tag = f.Tag.Get("json")
|
||||
}
|
||||
if tag == "-" {
|
||||
continue
|
||||
}
|
||||
vTypes[t] = true
|
||||
|
||||
flds, nTypes = appendFields(t, idx[0], flds, nTypes)
|
||||
idx := make([]int, len(fieldIdx)+1)
|
||||
copy(idx, fieldIdx)
|
||||
idx[len(fieldIdx)] = i
|
||||
|
||||
tagged := len(tag) > 0
|
||||
tagFieldName, omitempty, keyasint := getFieldNameAndOptionsFromTag(tag)
|
||||
|
||||
fieldName := tagFieldName
|
||||
if tagFieldName == "" {
|
||||
fieldName = f.Name
|
||||
}
|
||||
|
||||
if !f.Anonymous || ft.Kind() != reflect.Struct || len(tagFieldName) > 0 {
|
||||
flds = append(flds, &field{name: fieldName, idx: idx, typ: f.Type, tagged: tagged, omitEmpty: omitempty, keyAsInt: keyasint})
|
||||
continue
|
||||
}
|
||||
|
||||
// f is anonymous struct of type ft.
|
||||
next[ft] = append(next[ft], idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,143 +168,43 @@ func getFields(t reflect.Type) (flds fields, structOptions string) {
|
||||
sort.Sort(&nameLevelAndTagFieldSorter{flds})
|
||||
|
||||
// Keep visible fields.
|
||||
j := 0 // index of next unique field
|
||||
for i := 0; i < len(flds); {
|
||||
visibleFields := flds[:0]
|
||||
for i, j := 0, 0; i < len(flds); i = j {
|
||||
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++
|
||||
for j = i + 1; j < len(flds) && flds[j].name == name; j++ {
|
||||
}
|
||||
|
||||
// Skip fields with the same field name.
|
||||
for i++; i < len(flds) && name == flds[i].name; i++ { //nolint:revive
|
||||
}
|
||||
}
|
||||
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,
|
||||
) (
|
||||
_flds fields,
|
||||
_nTypes 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 := tag != ""
|
||||
|
||||
// Parse field tag options
|
||||
var tagFieldName string
|
||||
var omitempty, keyasint bool
|
||||
for j := 0; tag != ""; 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 || tagFieldName != "" {
|
||||
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)
|
||||
if j-i == 1 || len(flds[i].idx) < len(flds[i+1].idx) || (flds[i].tagged && !flds[i+1].tagged) {
|
||||
// Keep the field if the field name is unique, or if the first field
|
||||
// is at a less nested level, or if the first field is tagged and
|
||||
// the second field is not.
|
||||
visibleFields = append(visibleFields, flds[i])
|
||||
}
|
||||
}
|
||||
|
||||
return flds, nTypes
|
||||
sort.Sort(&indexFieldSorter{visibleFields})
|
||||
|
||||
return visibleFields, structOptions
|
||||
}
|
||||
|
||||
// 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 { //nolint:gocritic // ignore hugeParam
|
||||
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()
|
||||
}
|
||||
}
|
||||
func getFieldNameAndOptionsFromTag(tag string) (name string, omitEmpty bool, keyAsInt bool) {
|
||||
if tag == "" {
|
||||
return
|
||||
}
|
||||
return fv, nil
|
||||
idx := strings.Index(tag, ",")
|
||||
if idx == -1 {
|
||||
return tag, false, false
|
||||
}
|
||||
if idx > 0 {
|
||||
name = tag[:idx]
|
||||
tag = tag[idx:]
|
||||
}
|
||||
s := ",omitempty"
|
||||
if idx = strings.Index(tag, s); idx >= 0 && (len(tag) == idx+len(s) || tag[idx+len(s)] == ',') {
|
||||
omitEmpty = true
|
||||
}
|
||||
s = ",keyasint"
|
||||
if idx = strings.Index(tag, s); idx >= 0 && (len(tag) == idx+len(s) || tag[idx+len(s)] == ',') {
|
||||
keyAsInt = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
116
tag.go
116
tag.go
@ -7,14 +7,24 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Tag represents CBOR tag data, including tag number and unmarshaled tag content. Marshaling and
|
||||
// unmarshaling of tag content is subject to any encode and decode options that would apply to
|
||||
// enclosed data item if it were to appear outside of a tag.
|
||||
// Tag represents CBOR tag data, including tag number and unmarshaled tag content.
|
||||
type Tag struct {
|
||||
Number uint64
|
||||
Content interface{}
|
||||
}
|
||||
|
||||
func (t Tag) contentKind() reflect.Kind {
|
||||
c := t.Content
|
||||
for {
|
||||
t, ok := c.(Tag)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
c = t.Content
|
||||
}
|
||||
return reflect.ValueOf(c).Kind()
|
||||
}
|
||||
|
||||
// RawTag represents CBOR tag data, including tag number and raw tag content.
|
||||
// RawTag implements Unmarshaler and Marshaler interfaces.
|
||||
type RawTag struct {
|
||||
@ -28,17 +38,12 @@ func (t *RawTag) UnmarshalCBOR(data []byte) error {
|
||||
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}
|
||||
d := decodeState{data: data, dm: defaultDecMode}
|
||||
|
||||
// Unmarshal tag number.
|
||||
typ, _, num := d.getHead()
|
||||
if typ != cborTypeTag {
|
||||
return &UnmarshalTypeError{CBORType: typ.String(), GoType: typeRawTag.String()}
|
||||
return &UnmarshalTypeError{Value: typ.String(), Type: typeRawTag}
|
||||
}
|
||||
t.Number = num
|
||||
|
||||
@ -51,27 +56,14 @@ func (t *RawTag) UnmarshalCBOR(data []byte) error {
|
||||
|
||||
// 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 := getEncodeBuffer()
|
||||
|
||||
e := getEncodeState()
|
||||
encodeHead(e, byte(cborTypeTag), t.Number)
|
||||
|
||||
content := t.Content
|
||||
if len(content) == 0 {
|
||||
content = cborNil
|
||||
}
|
||||
|
||||
buf := make([]byte, len(e.Bytes())+len(content))
|
||||
buf := make([]byte, len(e.Bytes())+len(t.Content))
|
||||
n := copy(buf, e.Bytes())
|
||||
copy(buf[n:], content)
|
||||
copy(buf[n:], t.Content)
|
||||
|
||||
putEncodeBuffer(e)
|
||||
putEncodeState(e)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
@ -92,7 +84,7 @@ const (
|
||||
)
|
||||
|
||||
func (dtm DecTagMode) valid() bool {
|
||||
return dtm >= 0 && dtm < maxDecTagMode
|
||||
return dtm < maxDecTagMode
|
||||
}
|
||||
|
||||
// EncTagMode specifies how encoder handles tag number.
|
||||
@ -109,7 +101,7 @@ const (
|
||||
)
|
||||
|
||||
func (etm EncTagMode) valid() bool {
|
||||
return etm >= 0 && etm < maxEncTagMode
|
||||
return etm < maxEncTagMode
|
||||
}
|
||||
|
||||
// TagOptions specifies how encoder and decoder handle tag number.
|
||||
@ -131,8 +123,7 @@ type TagSet interface {
|
||||
}
|
||||
|
||||
type tagProvider interface {
|
||||
getTagItemFromType(t reflect.Type) *tagItem
|
||||
getTypeFromTagNum(num []uint64) reflect.Type
|
||||
get(t reflect.Type) *tagItem
|
||||
}
|
||||
|
||||
type tagItem struct {
|
||||
@ -142,25 +133,6 @@ type tagItem struct {
|
||||
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
|
||||
|
||||
@ -170,19 +142,10 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func (t tagSet) getTagItemFromType(typ reflect.Type) *tagItem {
|
||||
func (t tagSet) get(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)}
|
||||
@ -202,13 +165,8 @@ func (t *syncTagSet) Add(opts TagOptions, contentType reflect.Type, num uint64,
|
||||
}
|
||||
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)
|
||||
}
|
||||
if _, ok := t.t[contentType]; ok {
|
||||
return errors.New("cbor: content type " + contentType.String() + " already exists in TagSet")
|
||||
}
|
||||
t.t[contentType] = tag
|
||||
return nil
|
||||
@ -224,20 +182,13 @@ func (t *syncTagSet) Remove(contentType reflect.Type) {
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *syncTagSet) getTagItemFromType(typ reflect.Type) *tagItem {
|
||||
func (t *syncTagSet) get(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")
|
||||
@ -248,9 +199,6 @@ func newTagItem(opts TagOptions, contentType reflect.Type, num uint64, nestedNum
|
||||
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")
|
||||
}
|
||||
@ -260,24 +208,24 @@ func newTagItem(opts TagOptions, contentType reflect.Type, num uint64, nestedNum
|
||||
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 reflect.PtrTo(contentType).Implements(typeMarshaler) && opts.EncTag != EncTagNone {
|
||||
return nil, errors.New("cbor: cannot add cbor.Marshaler to TagSet with EncTag != EncTagNone")
|
||||
}
|
||||
if num == tagNumSelfDescribedCBOR {
|
||||
return nil, errors.New("cbor: cannot add tag number 55799 to TagSet, it's built-in and ignored automatically")
|
||||
if reflect.PtrTo(contentType).Implements(typeUnmarshaler) && opts.DecTag != DecTagIgnored {
|
||||
return nil, errors.New("cbor: cannot add cbor.Unmarshaler to TagSet with DecTag != DecTagIgnored")
|
||||
}
|
||||
|
||||
te := tagItem{num: []uint64{num}, opts: opts, contentType: contentType}
|
||||
te.num = append(te.num, nestedNum...)
|
||||
|
||||
// Cache encoded tag numbers
|
||||
e := getEncodeBuffer()
|
||||
e := getEncodeState()
|
||||
for _, n := range te.num {
|
||||
encodeHead(e, byte(cborTypeTag), n)
|
||||
}
|
||||
te.cborTagNum = make([]byte, e.Len())
|
||||
copy(te.cborTagNum, e.Bytes())
|
||||
putEncodeBuffer(e)
|
||||
putEncodeState(e)
|
||||
|
||||
return &te, nil
|
||||
}
|
||||
|
||||
541
tag_test.go
541
tag_test.go
@ -2,9 +2,7 @@ package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -191,64 +189,6 @@ func TestTagBinaryMarshalerUnmarshaler(t *testing.T) {
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
data := hexDecode("d864a0") // {}
|
||||
var v T
|
||||
if err := dm.Unmarshal(data, &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, data) {
|
||||
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, data)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
data := hexDecode("d864a1617360") // {"s":""}
|
||||
var v T
|
||||
if err := dm.Unmarshal(data, &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, data) {
|
||||
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTagToArrayStruct(t *testing.T) {
|
||||
type coseHeader struct {
|
||||
Alg int `cbor:"1,keyasint,omitempty"`
|
||||
Kid []byte `cbor:"4,keyasint,omitempty"`
|
||||
@ -273,17 +213,17 @@ func TestTagToArrayStruct(t *testing.T) {
|
||||
dm, _ := DecOptions{}.DecModeWithTags(tags)
|
||||
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.3
|
||||
data := hexDecode("d28443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7158405427c1ff28d23fbad1f29c4c7c6a555e601d6fa29f9179bc3d7438bacaca5acd08c8d4d4f96131680c429a01f85951ecee743a52b9b63632c57209120e1c9e30")
|
||||
cborData := hexDecode("d28443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7158405427c1ff28d23fbad1f29c4c7c6a555e601d6fa29f9179bc3d7438bacaca5acd08c8d4d4f96131680c429a01f85951ecee743a52b9b63632c57209120e1c9e30")
|
||||
var v signedCWT
|
||||
if err := dm.Unmarshal(data, &v); err != nil {
|
||||
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, data) {
|
||||
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, data)
|
||||
if !bytes.Equal(b, cborData) {
|
||||
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, cborData)
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,17 +253,17 @@ func TestNestedTagStruct(t *testing.T) {
|
||||
dm, _ := DecOptions{}.DecModeWithTags(tags)
|
||||
|
||||
// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
|
||||
data := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
|
||||
cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
|
||||
var v macedCOSE
|
||||
if err := dm.Unmarshal(data, &v); err != nil {
|
||||
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, data) {
|
||||
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, data)
|
||||
if !bytes.Equal(b, cborData) {
|
||||
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, cborData)
|
||||
}
|
||||
}
|
||||
|
||||
@ -393,28 +333,19 @@ func TestAddTagError(t *testing.T) {
|
||||
wantErrorMsg: "cbor: cannot add cbor.RawTag to TagSet",
|
||||
},
|
||||
{
|
||||
name: "big.Int",
|
||||
typ: reflect.TypeOf(big.Int{}),
|
||||
name: "cbor.Unmarshaler",
|
||||
typ: reflect.TypeOf(number2(0)),
|
||||
num: 107,
|
||||
opts: TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
|
||||
wantErrorMsg: "cbor: cannot add big.Int to TagSet, it's built-in and supported automatically",
|
||||
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: "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)),
|
||||
@ -429,27 +360,6 @@ func TestAddTagError(t *testing.T) {
|
||||
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 {
|
||||
@ -469,7 +379,7 @@ func TestAddTagError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddDuplicateTagContentTypeError(t *testing.T) {
|
||||
func TestAddDuplicateTagError(t *testing.T) {
|
||||
type myInt int
|
||||
myIntType := reflect.TypeOf(myInt(0))
|
||||
wantErrorMsg := "cbor: content type cbor.myInt already exists in TagSet"
|
||||
@ -483,49 +393,7 @@ func TestAddDuplicateTagContentTypeError(t *testing.T) {
|
||||
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)
|
||||
t.Errorf("TagSet.Add(%s, %d) returned error msg %q, want %q", myIntType, 100, err, wantErrorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
@ -906,31 +774,31 @@ func TestDecodeWrongTag(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
obj interface{}
|
||||
data []byte
|
||||
cborData []byte
|
||||
wantErrorMsg string
|
||||
}{
|
||||
{
|
||||
name: "BinaryMarshaler non-struct",
|
||||
obj: number(1234567890),
|
||||
data: hexDecode("d87d4800000000499602d2"),
|
||||
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"},
|
||||
data: hexDecode("d87d45612C622C63"),
|
||||
cborData: hexDecode("d87d45612C622C63"),
|
||||
wantErrorMsg: "cbor: wrong tag number for cbor.stru, got [125], expected [124]",
|
||||
},
|
||||
{
|
||||
name: "non-struct",
|
||||
obj: myInt(1),
|
||||
data: hexDecode("d87d01"),
|
||||
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"},
|
||||
data: hexDecode("d87ea3616161416162614261636143"), // {"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]",
|
||||
},
|
||||
}
|
||||
@ -941,13 +809,13 @@ func TestDecodeWrongTag(t *testing.T) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
dm, _ := DecOptions{}.DecModeWithTags(tag.tagSet)
|
||||
v := reflect.New(reflect.TypeOf(tc.obj))
|
||||
if err := dm.Unmarshal(tc.data, v.Interface()); err == nil {
|
||||
t.Errorf("Unmarshal(0x%x) didn't return an error", tc.data)
|
||||
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.data, err)
|
||||
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.data, err.Error(), tc.wantErrorMsg)
|
||||
t.Errorf("Unmarshal(0x%x) returned error %q, want error %q", tc.cborData, err.Error(), tc.wantErrorMsg)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -960,7 +828,7 @@ func TestDecodeWrongTag(t *testing.T) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
dm, _ := DecOptions{}.DecModeWithTags(tagsDecIgnored)
|
||||
v := reflect.New(reflect.TypeOf(tc.obj))
|
||||
if err := dm.Unmarshal(tc.data, v.Interface()); err != nil {
|
||||
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()) {
|
||||
@ -1050,12 +918,12 @@ func TestDecodeSharedTag(t *testing.T) {
|
||||
// Decode myInt with tag number 123
|
||||
var v myInt
|
||||
wantV := myInt(1)
|
||||
data := hexDecode("d87b01")
|
||||
if err = dm.Unmarshal(data, &v); err != nil {
|
||||
t.Errorf("Unmarshal(0x%x) returned error %v", data, err)
|
||||
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)", data, v, v, wantV, wantV)
|
||||
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV)
|
||||
}
|
||||
|
||||
// Unregister myInt type
|
||||
@ -1063,12 +931,12 @@ func TestDecodeSharedTag(t *testing.T) {
|
||||
|
||||
// Decode myInt without tag number
|
||||
wantV = myInt(2)
|
||||
data = hexDecode("02")
|
||||
if err := dm.Unmarshal(data, &v); err != nil {
|
||||
t.Errorf("Unmarshal(0x%x) returned error %v", data, err)
|
||||
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)", data, v, v, wantV, wantV)
|
||||
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV)
|
||||
}
|
||||
|
||||
// Register myInt type with tag number 234
|
||||
@ -1078,12 +946,12 @@ func TestDecodeSharedTag(t *testing.T) {
|
||||
|
||||
// Decode myInt with tag number 234
|
||||
wantV = myInt(3)
|
||||
data = hexDecode("d8ea03")
|
||||
if err := dm.Unmarshal(data, &v); err != nil {
|
||||
t.Errorf("Unmarshal(0x%x) returned error %v", data, err)
|
||||
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)", data, v, v, wantV, wantV)
|
||||
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1162,8 +1030,8 @@ func TestEncModeWithTagsError(t *testing.T) {
|
||||
func TestNilRawTagUnmarshalCBORError(t *testing.T) {
|
||||
wantErrorMsg := "cbor.RawTag: UnmarshalCBOR on nil pointer"
|
||||
var tag *RawTag
|
||||
data := hexDecode("c249010000000000000000")
|
||||
if err := tag.UnmarshalCBOR(data); err == nil {
|
||||
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)
|
||||
@ -1171,12 +1039,12 @@ func TestNilRawTagUnmarshalCBORError(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTagUnmarshalError(t *testing.T) {
|
||||
data := hexDecode("d87b61fe") // invalid UTF-8 string
|
||||
cborData := hexDecode("d87b61fe") // invalid UTF-8 string
|
||||
var tag Tag
|
||||
if err := Unmarshal(data, &tag); err == nil {
|
||||
t.Errorf("Unmarshal(0x%x) didn't return error", data)
|
||||
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", data, err.Error(), invalidUTF8ErrorMsg)
|
||||
t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err.Error(), invalidUTF8ErrorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1193,53 +1061,7 @@ func TestTagMarshalError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
func TestTagMarshal(t *testing.T) {
|
||||
m := make(map[interface{}]bool)
|
||||
m[10] = true
|
||||
m[100] = true
|
||||
@ -1273,266 +1095,3 @@ func TestEncodeTag(t *testing.T) {
|
||||
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
|
||||
data []byte
|
||||
wantObj interface{}
|
||||
}{
|
||||
{
|
||||
name: "registered myBool",
|
||||
data: hexDecode("d864f5"), // 100(true)
|
||||
wantObj: myBool(true),
|
||||
},
|
||||
{
|
||||
name: "registered myUint",
|
||||
data: hexDecode("d865d86600"), // 101(102(0))
|
||||
wantObj: myUint(0),
|
||||
},
|
||||
{
|
||||
name: "not registered bool",
|
||||
data: hexDecode("d865f5"), // 101(true)
|
||||
wantObj: Tag{101, true},
|
||||
},
|
||||
{
|
||||
name: "not registered uint",
|
||||
data: hexDecode("d865d86700"), // 101(103(0))
|
||||
wantObj: Tag{101, Tag{103, uint64(0)}},
|
||||
},
|
||||
{
|
||||
name: "not registered uint",
|
||||
data: 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.data, &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.data, &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)
|
||||
|
||||
data := hexDecode("d865d8663bffffffffffffffff") // 101(102(-18446744073709551616))
|
||||
|
||||
var v interface{}
|
||||
if err := dm.Unmarshal(data, &v); err == nil {
|
||||
t.Errorf("Unmarshal(0x%x) didn't return an error", data)
|
||||
} else if _, ok := err.(*UnmarshalTypeError); !ok {
|
||||
t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", data, err)
|
||||
} else if !strings.Contains(err.Error(), "cannot unmarshal") {
|
||||
t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", data, 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 getType(rawTag.Content[0]) != cborTypeMap {
|
||||
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)
|
||||
}
|
||||
|
||||
data := 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(data, &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, data) {
|
||||
t.Errorf("Marshal(%v) returned %v, want %v", v1, b, data)
|
||||
}
|
||||
|
||||
// Decode to registered type.
|
||||
var v2 number3
|
||||
if err = dm.Unmarshal(data, &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, data) {
|
||||
t.Errorf("Marshal(%v) returned %v, want %v", v2, b, data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalRawTagContainingMalformedCBORData(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
value interface{}
|
||||
wantErrorMsg string
|
||||
}{
|
||||
// Nil RawMessage and empty RawMessage are encoded as CBOR nil.
|
||||
{
|
||||
name: "truncated data",
|
||||
value: RawTag{Number: 100, Content: RawMessage{0xa6}},
|
||||
wantErrorMsg: "cbor: error calling MarshalCBOR for type cbor.RawTag: unexpected EOF",
|
||||
},
|
||||
{
|
||||
name: "malformed data",
|
||||
value: RawTag{Number: 100, Content: RawMessage{0x1f}},
|
||||
wantErrorMsg: "cbor: error calling MarshalCBOR for type cbor.RawTag: cbor: invalid additional information 31 for type positive integer",
|
||||
},
|
||||
{
|
||||
name: "extraneous data",
|
||||
value: RawTag{Number: 100, Content: RawMessage{0x01, 0x01}},
|
||||
wantErrorMsg: "cbor: error calling MarshalCBOR for type cbor.RawTag: cbor: 1 bytes of extraneous data starting at index 3",
|
||||
},
|
||||
{
|
||||
name: "invalid builtin tag",
|
||||
value: RawTag{Number: 0, Content: RawMessage{0x01}},
|
||||
wantErrorMsg: "cbor: error calling MarshalCBOR for type cbor.RawTag: cbor: tag number 0 must be followed by text string, got positive integer",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
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.(*MarshalerError); !ok {
|
||||
t.Errorf("Marshal(%v) error type %T, want *MarshalerError", tc.value, err)
|
||||
} else if err.Error() != tc.wantErrorMsg {
|
||||
t.Errorf("Marshal(%v) error %q, want %q", tc.value, err.Error(), tc.wantErrorMsg)
|
||||
}
|
||||
if b != nil {
|
||||
t.Errorf("Marshal(%v) = 0x%x, want nil", tc.value, b)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestEncodeBuiltinTag tests that marshaling a value of type Tag "does the right thing" when
|
||||
// marshaling the enclosed data item of a built-in tag number.
|
||||
func TestEncodeBuiltinTag(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
tag Tag
|
||||
opts EncOptions
|
||||
want []byte
|
||||
}{
|
||||
{
|
||||
name: "unsigned bignum content not enclosed in expected encoding tag",
|
||||
tag: Tag{Number: tagNumUnsignedBignum, Content: []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
|
||||
opts: EncOptions{ByteSliceLaterFormat: ByteSliceLaterFormatBase16},
|
||||
want: []byte{0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
},
|
||||
{
|
||||
name: "negative bignum content not enclosed in expected encoding tag",
|
||||
tag: Tag{Number: tagNumNegativeBignum, Content: []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
|
||||
opts: EncOptions{ByteSliceLaterFormat: ByteSliceLaterFormatBase16},
|
||||
want: []byte{0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
},
|
||||
{
|
||||
name: "rfc 3339 content is not encoded as byte string",
|
||||
tag: Tag{Number: tagNumRFC3339Time, Content: "2013-03-21T20:04:00Z"},
|
||||
opts: EncOptions{String: StringToByteString},
|
||||
want: hexDecode("c074323031332d30332d32315432303a30343a30305a"),
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
em, err := tc.opts.EncMode()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, err := em.Marshal(tc.tag)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(got, tc.want) {
|
||||
t.Errorf("unexpected difference\ngot: 0x%x\nwant: 0x%x", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
393
valid.go
393
valid.go
@ -7,10 +7,7 @@ import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/x448/float16"
|
||||
)
|
||||
|
||||
// SyntaxError is a description of a CBOR syntax error.
|
||||
@ -27,131 +24,59 @@ type SemanticError struct {
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
// ExtraneousDataError indicates found extraneous data following well-formed CBOR data item.
|
||||
type ExtraneousDataError struct {
|
||||
numOfBytes int // number of bytes of extraneous data
|
||||
index int // location of extraneous data
|
||||
}
|
||||
|
||||
func (e *ExtraneousDataError) Error() string {
|
||||
return "cbor: " + strconv.Itoa(e.numOfBytes) + " bytes of extraneous data starting at index " + strconv.Itoa(e.index)
|
||||
}
|
||||
|
||||
// wellformed checks whether the CBOR data item is well-formed.
|
||||
// allowExtraData indicates if extraneous data is allowed after the CBOR data item.
|
||||
// - use allowExtraData = true when using Decoder.Decode()
|
||||
// - use allowExtraData = false when using Unmarshal()
|
||||
func (d *decoder) wellformed(allowExtraData bool, checkBuiltinTags bool) error {
|
||||
if len(d.data) == d.off {
|
||||
return io.EOF
|
||||
// valid checks whether CBOR data is complete and well-formed.
|
||||
func valid(data []byte) (rest []byte, err error) {
|
||||
if len(data) == 0 {
|
||||
return nil, io.EOF
|
||||
}
|
||||
_, err := d.wellformedInternal(0, checkBuiltinTags)
|
||||
if err == nil {
|
||||
if !allowExtraData && d.off != len(d.data) {
|
||||
err = &ExtraneousDataError{len(d.data) - d.off, d.off}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// wellformedInternal checks data's well-formedness and returns max depth and error.
|
||||
func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, error) { //nolint:gocyclo
|
||||
t, _, val, indefiniteLength, err := d.wellformedHeadWithIndefiniteLengthFlag()
|
||||
offset, _, err := validInternal(data, 0, 1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return nil, err
|
||||
}
|
||||
return data[offset:], nil
|
||||
}
|
||||
|
||||
const (
|
||||
maxNestingLevel = 32
|
||||
)
|
||||
|
||||
// validInternal checks data's well-formedness and returns data's next offset, max depth, and error.
|
||||
func validInternal(data []byte, off int, depth int) (int, int, error) {
|
||||
if depth > maxNestingLevel {
|
||||
return 0, 0, errors.New("cbor: reached max depth " + strconv.Itoa(maxNestingLevel))
|
||||
}
|
||||
|
||||
off, t, ai, val, err := validHead(data, off)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
if ai == 31 {
|
||||
if t == cborTypeByteString || t == cborTypeTextString {
|
||||
return validIndefiniteString(data, off, t, depth)
|
||||
}
|
||||
return validIndefiniteArrOrMap(data, off, t, depth)
|
||||
}
|
||||
|
||||
dataLen := len(data)
|
||||
|
||||
switch t {
|
||||
case cborTypeByteString, cborTypeTextString:
|
||||
if indefiniteLength {
|
||||
if d.dm.indefLength == IndefLengthForbidden {
|
||||
return 0, &IndefiniteLengthError{t}
|
||||
}
|
||||
return d.wellformedIndefiniteString(t, depth, checkBuiltinTags)
|
||||
}
|
||||
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")
|
||||
return 0, 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
|
||||
if dataLen-off < valInt { // valInt+off may overflow integer
|
||||
return 0, 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
d.off += valInt
|
||||
|
||||
off += valInt
|
||||
case cborTypeArray, cborTypeMap:
|
||||
depth++
|
||||
if depth > d.dm.maxNestedLevels {
|
||||
return 0, &MaxNestedLevelError{d.dm.maxNestedLevels}
|
||||
}
|
||||
|
||||
if indefiniteLength {
|
||||
if d.dm.indefLength == IndefLengthForbidden {
|
||||
return 0, &IndefiniteLengthError{t}
|
||||
}
|
||||
return d.wellformedIndefiniteArrayOrMap(t, depth, checkBuiltinTags)
|
||||
}
|
||||
|
||||
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")
|
||||
return 0, 0, errors.New("cbor: " + t.String() + " length " + strconv.FormatUint(val, 10) + " is too large, causing 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
|
||||
@ -159,236 +84,150 @@ func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, err
|
||||
maxDepth := depth
|
||||
for j := 0; j < count; j++ {
|
||||
for i := 0; i < valInt; i++ {
|
||||
var dpt int
|
||||
if dpt, err = d.wellformedInternal(depth, checkBuiltinTags); err != nil {
|
||||
return 0, err
|
||||
var d int
|
||||
if off, d, err = validInternal(data, off, depth+1); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if dpt > maxDepth {
|
||||
maxDepth = dpt // Save max depth
|
||||
if d > maxDepth {
|
||||
maxDepth = d // Save max depth
|
||||
}
|
||||
}
|
||||
}
|
||||
depth = maxDepth
|
||||
|
||||
case cborTypeTag:
|
||||
if d.dm.tagsMd == TagsForbidden {
|
||||
return 0, &TagsMdError{}
|
||||
}
|
||||
|
||||
tagNum := val
|
||||
|
||||
// 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 dataLen == off { // Tag number must be followed by tag content.
|
||||
return 0, 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
if checkBuiltinTags {
|
||||
err = validBuiltinTag(tagNum, d.data[d.off])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if d.dm.bignumTag == BignumTagForbidden && (tagNum == 2 || tagNum == 3) {
|
||||
return 0, &UnacceptableDataItemError{
|
||||
CBORType: cborTypeTag.String(),
|
||||
Message: "bignum",
|
||||
}
|
||||
}
|
||||
if getType(d.data[d.off]) != cborTypeTag {
|
||||
if cborType(data[off]&0xe0) != cborTypeTag {
|
||||
break
|
||||
}
|
||||
if _, _, tagNum, err = d.wellformedHead(); err != nil {
|
||||
return 0, err
|
||||
if off, _, _, _, err = validHead(data, off); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
depth++
|
||||
if depth > d.dm.maxNestedLevels {
|
||||
return 0, &MaxNestedLevelError{d.dm.maxNestedLevels}
|
||||
}
|
||||
}
|
||||
// Check tag content.
|
||||
return d.wellformedInternal(depth, checkBuiltinTags)
|
||||
return validInternal(data, off, depth)
|
||||
}
|
||||
|
||||
return depth, nil
|
||||
return off, depth, nil
|
||||
}
|
||||
|
||||
// wellformedIndefiniteString checks indefinite length byte/text string's well-formedness and returns max depth and error.
|
||||
func (d *decoder) wellformedIndefiniteString(t cborType, depth int, checkBuiltinTags bool) (int, error) {
|
||||
// validIndefiniteString checks indefinite length byte/text string's well-formedness and returns data's next offset, max depth, and error.
|
||||
func validIndefiniteString(data []byte, off int, t cborType, depth int) (int, int, error) {
|
||||
var err error
|
||||
dataLen := len(data)
|
||||
for {
|
||||
if len(d.data) == d.off {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
if dataLen == off {
|
||||
return 0, 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
if isBreakFlag(d.data[d.off]) {
|
||||
d.off++
|
||||
if data[off] == 0xff {
|
||||
off++
|
||||
break
|
||||
}
|
||||
// Peek ahead to get next type and indefinite length status.
|
||||
nt, ai := parseInitialByte(d.data[d.off])
|
||||
nt := cborType(data[off] & 0xe0)
|
||||
if t != nt {
|
||||
return 0, &SyntaxError{"cbor: wrong element type " + nt.String() + " for indefinite-length " + t.String()}
|
||||
return 0, 0, &SyntaxError{"cbor: wrong element type " + nt.String() + " for indefinite-length " + t.String()}
|
||||
}
|
||||
if additionalInformation(ai).isIndefiniteLength() {
|
||||
return 0, &SyntaxError{"cbor: indefinite-length " + t.String() + " chunk is not definite-length"}
|
||||
if (data[off] & 0x1f) == 31 {
|
||||
return 0, 0, &SyntaxError{"cbor: indefinite-length " + t.String() + " chunk is not definite-length"}
|
||||
}
|
||||
if depth, err = d.wellformedInternal(depth, checkBuiltinTags); err != nil {
|
||||
return 0, err
|
||||
if off, depth, err = validInternal(data, off, depth); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
return depth, nil
|
||||
return off, depth, nil
|
||||
}
|
||||
|
||||
// wellformedIndefiniteArrayOrMap checks indefinite length array/map's well-formedness and returns max depth and error.
|
||||
func (d *decoder) wellformedIndefiniteArrayOrMap(t cborType, depth int, checkBuiltinTags bool) (int, error) {
|
||||
// validIndefiniteArrOrMap checks indefinite length array/map's well-formedness and returns data's next offset, max depth, and error.
|
||||
func validIndefiniteArrOrMap(data []byte, off int, t cborType, depth int) (int, int, error) {
|
||||
var err error
|
||||
maxDepth := depth
|
||||
dataLen := len(data)
|
||||
i := 0
|
||||
for {
|
||||
if len(d.data) == d.off {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
if dataLen == off {
|
||||
return 0, 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
if isBreakFlag(d.data[d.off]) {
|
||||
d.off++
|
||||
if data[off] == 0xff {
|
||||
off++
|
||||
break
|
||||
}
|
||||
var dpt int
|
||||
if dpt, err = d.wellformedInternal(depth, checkBuiltinTags); err != nil {
|
||||
return 0, err
|
||||
var d int
|
||||
if off, d, err = validInternal(data, off, depth+1); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if dpt > maxDepth {
|
||||
maxDepth = dpt
|
||||
if d > maxDepth {
|
||||
maxDepth = d
|
||||
}
|
||||
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 0, 0, &SyntaxError{"cbor: unexpected \"break\" code"}
|
||||
}
|
||||
return maxDepth, nil
|
||||
return off, maxDepth, nil
|
||||
}
|
||||
|
||||
func (d *decoder) wellformedHeadWithIndefiniteLengthFlag() (
|
||||
t cborType,
|
||||
ai byte,
|
||||
val uint64,
|
||||
indefiniteLength bool,
|
||||
err error,
|
||||
) {
|
||||
t, ai, val, err = d.wellformedHead()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
indefiniteLength = additionalInformation(ai).isIndefiniteLength()
|
||||
return
|
||||
}
|
||||
|
||||
func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error) {
|
||||
dataLen := len(d.data) - d.off
|
||||
func validHead(data []byte, off int) (_ int, t cborType, ai byte, val uint64, err error) {
|
||||
dataLen := len(data) - off
|
||||
if dataLen == 0 {
|
||||
return 0, 0, 0, io.ErrUnexpectedEOF
|
||||
return 0, 0, 0, 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
t, ai = parseInitialByte(d.data[d.off])
|
||||
t = cborType(data[off] & 0xe0)
|
||||
ai = data[off] & 0x1f
|
||||
val = uint64(ai)
|
||||
d.off++
|
||||
dataLen--
|
||||
off++
|
||||
|
||||
if ai <= maxAdditionalInformationWithoutArgument {
|
||||
return t, ai, val, nil
|
||||
if ai < 24 {
|
||||
return off, t, ai, val, nil
|
||||
}
|
||||
|
||||
if ai == additionalInformationWith1ByteArgument {
|
||||
const argumentSize = 1
|
||||
if dataLen < argumentSize {
|
||||
return 0, 0, 0, io.ErrUnexpectedEOF
|
||||
if ai == 24 {
|
||||
if dataLen < 2 {
|
||||
return 0, 0, 0, 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
val = uint64(d.data[d.off])
|
||||
d.off++
|
||||
val = uint64(data[off])
|
||||
off++
|
||||
if t == cborTypePrimitives && val < 32 {
|
||||
return 0, 0, 0, &SyntaxError{"cbor: invalid simple value " + strconv.Itoa(int(val)) + " for type " + t.String()}
|
||||
return 0, 0, 0, 0, &SyntaxError{"cbor: invalid simple value " + strconv.Itoa(int(val)) + " for type " + t.String()}
|
||||
}
|
||||
return t, ai, val, nil
|
||||
return off, t, ai, val, nil
|
||||
}
|
||||
|
||||
if ai == additionalInformationWith2ByteArgument {
|
||||
const argumentSize = 2
|
||||
if dataLen < argumentSize {
|
||||
return 0, 0, 0, io.ErrUnexpectedEOF
|
||||
if ai == 25 {
|
||||
if dataLen < 3 {
|
||||
return 0, 0, 0, 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+argumentSize]))
|
||||
d.off += argumentSize
|
||||
if t == cborTypePrimitives {
|
||||
if err := d.acceptableFloat(float64(float16.Frombits(uint16(val)).Float32())); err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
}
|
||||
return t, ai, val, nil
|
||||
val = uint64(binary.BigEndian.Uint16(data[off : off+2]))
|
||||
off += 2
|
||||
return off, t, ai, val, nil
|
||||
}
|
||||
|
||||
if ai == additionalInformationWith4ByteArgument {
|
||||
const argumentSize = 4
|
||||
if dataLen < argumentSize {
|
||||
return 0, 0, 0, io.ErrUnexpectedEOF
|
||||
if ai == 26 {
|
||||
if dataLen < 5 {
|
||||
return 0, 0, 0, 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+argumentSize]))
|
||||
d.off += argumentSize
|
||||
if t == cborTypePrimitives {
|
||||
if err := d.acceptableFloat(float64(math.Float32frombits(uint32(val)))); err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
}
|
||||
return t, ai, val, nil
|
||||
val = uint64(binary.BigEndian.Uint32(data[off : off+4]))
|
||||
off += 4
|
||||
return off, t, ai, val, nil
|
||||
}
|
||||
|
||||
if ai == additionalInformationWith8ByteArgument {
|
||||
const argumentSize = 8
|
||||
if dataLen < argumentSize {
|
||||
return 0, 0, 0, io.ErrUnexpectedEOF
|
||||
if ai == 27 {
|
||||
if dataLen < 9 {
|
||||
return 0, 0, 0, 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
val = binary.BigEndian.Uint64(d.data[d.off : d.off+argumentSize])
|
||||
d.off += argumentSize
|
||||
if t == cborTypePrimitives {
|
||||
if err := d.acceptableFloat(math.Float64frombits(val)); err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
}
|
||||
return t, ai, val, nil
|
||||
val = binary.BigEndian.Uint64(data[off : off+8])
|
||||
off += 8
|
||||
return off, t, ai, val, nil
|
||||
}
|
||||
|
||||
if additionalInformation(ai).isIndefiniteLength() {
|
||||
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 wellformedIndefinite().
|
||||
return 0, 0, 0, &SyntaxError{"cbor: unexpected \"break\" code"}
|
||||
return 0, 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, 0, &SyntaxError{"cbor: unexpected \"break\" code"}
|
||||
}
|
||||
return t, ai, val, nil
|
||||
return off, 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()}
|
||||
}
|
||||
|
||||
func (d *decoder) acceptableFloat(f float64) error {
|
||||
switch {
|
||||
case d.dm.nanDec == NaNDecodeForbidden && math.IsNaN(f):
|
||||
return &UnacceptableDataItemError{
|
||||
CBORType: cborTypePrimitives.String(),
|
||||
Message: "floating-point NaN",
|
||||
}
|
||||
case d.dm.infDec == InfDecodeForbidden && math.IsInf(f, 0):
|
||||
return &UnacceptableDataItemError{
|
||||
CBORType: cborTypePrimitives.String(),
|
||||
Message: "floating-point infinity",
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return 0, 0, 0, 0, &SyntaxError{"cbor: invalid additional information " + strconv.Itoa(int(ai)) + " for type " + t.String()}
|
||||
}
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build !tinygo
|
||||
|
||||
package cbor
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test32Depth(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
wantDepth int
|
||||
}{
|
||||
{"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.data, dm: defaultDecMode}
|
||||
depth, err := d.wellformedInternal(0, false)
|
||||
if err != nil {
|
||||
t.Errorf("wellformed(0x%x) returned error %v", tc.data, err)
|
||||
}
|
||||
if depth != tc.wantDepth {
|
||||
t.Errorf("wellformed(0x%x) returned depth %d, want %d", tc.data, depth, tc.wantDepth)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
299
valid_test.go
299
valid_test.go
@ -1,299 +0,0 @@
|
||||
// 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"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValid1(t *testing.T) {
|
||||
for _, mt := range marshalTests {
|
||||
if err := Wellformed(mt.wantData); err != nil {
|
||||
t.Errorf("Wellformed() returned error %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValid2(t *testing.T) {
|
||||
for _, mt := range marshalTests {
|
||||
dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
|
||||
if err := dm.Wellformed(mt.wantData); err != nil {
|
||||
t.Errorf("Wellformed() returned error %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidExtraneousData(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
extraneousDataNumOfBytes int
|
||||
extraneousDataIndex int
|
||||
}{
|
||||
{"two numbers", []byte{0x00, 0x01}, 1, 1}, // 0, 1
|
||||
{"bytestring and int", []byte{0x44, 0x01, 0x02, 0x03, 0x04, 0x00}, 1, 5}, // h'01020304', 0
|
||||
{"int and partial array", []byte{0x00, 0x83, 0x01, 0x02}, 3, 1},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := Wellformed(tc.data)
|
||||
if err == nil {
|
||||
t.Errorf("Wellformed(0x%x) didn't return an error", tc.data)
|
||||
} else {
|
||||
ederr, ok := err.(*ExtraneousDataError)
|
||||
if !ok {
|
||||
t.Errorf("Wellformed(0x%x) error type %T, want *ExtraneousDataError", tc.data, err)
|
||||
} else if ederr.numOfBytes != tc.extraneousDataNumOfBytes {
|
||||
t.Errorf("Wellformed(0x%x) returned %d bytes of extraneous data, want %d", tc.data, ederr.numOfBytes, tc.extraneousDataNumOfBytes)
|
||||
} else if ederr.index != tc.extraneousDataIndex {
|
||||
t.Errorf("Wellformed(0x%x) returned extraneous data index %d, want %d", tc.data, ederr.index, tc.extraneousDataIndex)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidOnStreamingData(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
for _, t := range marshalTests {
|
||||
buf.Write(t.wantData)
|
||||
}
|
||||
d := decoder{data: buf.Bytes(), dm: defaultDecMode}
|
||||
for i := 0; i < len(marshalTests); i++ {
|
||||
if err := d.wellformed(true, false); err != nil {
|
||||
t.Errorf("wellformed() returned error %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDepth(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []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")))
|
||||
{"16-level array", hexDecode("820181818181818181818181818181818101"), 16},
|
||||
{"16-level indefinite length array", hexDecode("9f0181818181818181818181818181818101ff"), 16},
|
||||
{"16-level map", hexDecode("a10181818181818181818181818181818101"), 16},
|
||||
{"16-level indefinite length map", hexDecode("bf0181818181818181818181818181818101ff"), 16},
|
||||
{"16-level tag", hexDecode("d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d86474323031332d30332d32315432303a30343a30305a"), 16}, // 100(100(...("2013-03-21T20:04:00Z")))
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
d := decoder{data: tc.data, dm: defaultDecMode}
|
||||
depth, err := d.wellformedInternal(0, false)
|
||||
if err != nil {
|
||||
t.Errorf("wellformed(0x%x) returned error %v", tc.data, err)
|
||||
}
|
||||
if depth != tc.wantDepth {
|
||||
t.Errorf("wellformed(0x%x) returned depth %d, want %d", tc.data, depth, tc.wantDepth)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDepthError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
opts DecOptions
|
||||
wantErrorMsg string
|
||||
}{
|
||||
{
|
||||
name: "33-level array",
|
||||
data: hexDecode("82018181818181818181818181818181818181818181818181818181818181818101"),
|
||||
opts: DecOptions{MaxNestedLevels: 4},
|
||||
wantErrorMsg: "cbor: exceeded max nested level 4",
|
||||
},
|
||||
{
|
||||
name: "33-level array",
|
||||
data: hexDecode("82018181818181818181818181818181818181818181818181818181818181818101"),
|
||||
opts: DecOptions{MaxNestedLevels: 10},
|
||||
wantErrorMsg: "cbor: exceeded max nested level 10",
|
||||
},
|
||||
{
|
||||
name: "33-level array",
|
||||
data: hexDecode("8201818181818181818181818181818181818181818181818181818181818181818101"),
|
||||
opts: DecOptions{},
|
||||
wantErrorMsg: "cbor: exceeded max nested level " + strconv.Itoa(defaultMaxNestedLevels),
|
||||
},
|
||||
{
|
||||
name: "33-level indefinite length array",
|
||||
data: hexDecode("9f01818181818181818181818181818181818181818181818181818181818181818101ff"),
|
||||
opts: DecOptions{},
|
||||
wantErrorMsg: "cbor: exceeded max nested level " + strconv.Itoa(defaultMaxNestedLevels),
|
||||
},
|
||||
{
|
||||
name: "33-level map",
|
||||
data: hexDecode("a101818181818181818181818181818181818181818181818181818181818181818101"),
|
||||
opts: DecOptions{},
|
||||
wantErrorMsg: "cbor: exceeded max nested level " + strconv.Itoa(defaultMaxNestedLevels),
|
||||
},
|
||||
{
|
||||
name: "33-level indefinite length map",
|
||||
data: hexDecode("bf01818181818181818181818181818181818181818181818181818181818181818101ff"),
|
||||
opts: DecOptions{},
|
||||
wantErrorMsg: "cbor: exceeded max nested level " + strconv.Itoa(defaultMaxNestedLevels),
|
||||
},
|
||||
{
|
||||
name: "33-level tag",
|
||||
data: hexDecode("d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d864d86474323031332d30332d32315432303a30343a30305a"),
|
||||
opts: DecOptions{},
|
||||
wantErrorMsg: "cbor: exceeded max nested level " + strconv.Itoa(defaultMaxNestedLevels),
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
dm, _ := tc.opts.decMode()
|
||||
d := decoder{data: tc.data, dm: dm}
|
||||
if _, err := d.wellformedInternal(0, false); err == nil {
|
||||
t.Errorf("wellformed(0x%x) didn't return an error", tc.data)
|
||||
} else if _, ok := err.(*MaxNestedLevelError); !ok {
|
||||
t.Errorf("wellformed(0x%x) returned wrong error type %T, want (*MaxNestedLevelError)", tc.data, err)
|
||||
} else if err.Error() != tc.wantErrorMsg {
|
||||
t.Errorf("wellformed(0x%x) returned error %q, want error %q", tc.data, err.Error(), tc.wantErrorMsg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidBuiltinTagTest(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
}{
|
||||
{
|
||||
name: "tag 0",
|
||||
data: hexDecode("c074323031332d30332d32315432303a30343a30305a"),
|
||||
},
|
||||
{
|
||||
name: "tag 1",
|
||||
data: hexDecode("c11a514b67b0"),
|
||||
},
|
||||
{
|
||||
name: "tag 2",
|
||||
data: hexDecode("c249010000000000000000"),
|
||||
},
|
||||
{
|
||||
name: "tag 3",
|
||||
data: hexDecode("c349010000000000000000"),
|
||||
},
|
||||
{
|
||||
name: "nested tag 0",
|
||||
data: hexDecode("d9d9f7c074323031332d30332d32315432303a30343a30305a"),
|
||||
},
|
||||
{
|
||||
name: "nested tag 1",
|
||||
data: hexDecode("d9d9f7c11a514b67b0"),
|
||||
},
|
||||
{
|
||||
name: "nested tag 2",
|
||||
data: hexDecode("d9d9f7c249010000000000000000"),
|
||||
},
|
||||
{
|
||||
name: "nested tag 3",
|
||||
data: hexDecode("d9d9f7c349010000000000000000"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
d := decoder{data: tc.data, dm: defaultDecMode}
|
||||
if err := d.wellformed(true, true); err != nil {
|
||||
t.Errorf("wellformed(0x%x) returned error %v", tc.data, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidBuiltinTagTest(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
wantErrorMsg string
|
||||
}{
|
||||
{
|
||||
name: "tag 0",
|
||||
data: hexDecode("c01a514b67b0"),
|
||||
wantErrorMsg: "cbor: tag number 0 must be followed by text string, got positive integer",
|
||||
},
|
||||
{
|
||||
name: "tag 1",
|
||||
data: hexDecode("c174323031332d30332d32315432303a30343a30305a"),
|
||||
wantErrorMsg: "cbor: tag number 1 must be followed by integer or floating-point number, got UTF-8 text string",
|
||||
},
|
||||
{
|
||||
name: "tag 2",
|
||||
data: hexDecode("c269010000000000000000"),
|
||||
wantErrorMsg: "cbor: tag number 2 or 3 must be followed by byte string, got UTF-8 text string",
|
||||
},
|
||||
{
|
||||
name: "tag 3",
|
||||
data: hexDecode("c300"),
|
||||
wantErrorMsg: "cbor: tag number 2 or 3 must be followed by byte string, got positive integer",
|
||||
},
|
||||
{
|
||||
name: "nested tag 0",
|
||||
data: hexDecode("d9d9f7c01a514b67b0"),
|
||||
wantErrorMsg: "cbor: tag number 0 must be followed by text string, got positive integer",
|
||||
},
|
||||
{
|
||||
name: "nested tag 1",
|
||||
data: hexDecode("d9d9f7c174323031332d30332d32315432303a30343a30305a"),
|
||||
wantErrorMsg: "cbor: tag number 1 must be followed by integer or floating-point number, got UTF-8 text string",
|
||||
},
|
||||
{
|
||||
name: "nested tag 2",
|
||||
data: hexDecode("d9d9f7c269010000000000000000"),
|
||||
wantErrorMsg: "cbor: tag number 2 or 3 must be followed by byte string, got UTF-8 text string",
|
||||
},
|
||||
{
|
||||
name: "nested tag 3",
|
||||
data: hexDecode("d9d9f7c300"),
|
||||
wantErrorMsg: "cbor: tag number 2 or 3 must be followed by byte string, got positive integer",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
d := decoder{data: tc.data, dm: defaultDecMode}
|
||||
err := d.wellformed(true, true)
|
||||
if err == nil {
|
||||
t.Errorf("wellformed(0x%x) didn't return an error", tc.data)
|
||||
} else if err.Error() != tc.wantErrorMsg {
|
||||
t.Errorf("wellformed(0x%x) error %q, want %q", tc.data, err.Error(), tc.wantErrorMsg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user