Documentation
¶
Overview ¶
Package cbor implements a bounded, reflection-free subset of RFC 8949.
Decoder exposes typed tokens instead of mapping CBOR to arbitrary Go values. Encoder writes deterministic output and provides helpers for deterministically ordered arrays and maps. Nothing in either path uses reflection, io.ReadAll, or struct tags.
Map key ordering ¶
RFC 8949 defines two deterministic map key orderings, and they produce different bytes for the same map. EncoderOptions.KeyOrder selects between them. The zero value is LengthFirstKeyOrder, the section 4.2.3 length-first ordering that CTAP2 canonical CBOR and COSE require; BytewiseKeyOrder is the section 4.2.1 Core Deterministic Encoding ordering. Callers that care which one reaches the wire should set the field rather than inherit it.
Bounds ¶
Every limit is explicit and every limit is enforced before anything is reserved: a declared length never becomes an allocation on its own, because the length prefix of a string arrives before its bytes and is attacker controlled. DecoderOptions.RejectDuplicateMapKeys additionally validates a bounded root item before exposing any of its tokens, at the cost of retaining up to MaxRawMessageBytes.
Profiles ¶
A Profile is a named subset of CBOR that both ends of a protocol agree on -- CTAP2 canonical CBOR and RFC 8949 deterministic encoding are two, and Canonical and Deterministic supply them. It carries no resource limits: those are a property of the process doing the reading, not of the format, and they live in DecoderOptions. A caller with its own subset writes a struct literal.
Intended uses ¶
Formats where preserving integer labels, holding a byte-exact encoding, and rejecting hostile input matter more than mapping arbitrary Go values. WebAuthn authenticator data and COSE_Key are one such case; a compact fixed-schema message format is another.
The package intentionally does not support struct tags, diagnostic notation, COSE signing, encryption, key management, or indefinite-length output.
Index ¶
- Variables
- func AppendArrayHeader(dst []byte, n int) []byte
- func AppendBool(dst []byte, v bool) []byte
- func AppendBytes(dst, v []byte) []byte
- func AppendFloat(dst []byte, v float64) []byte
- func AppendInt(dst []byte, v int64) []byte
- func AppendMapHeader(dst []byte, n int) []byte
- func AppendNegative(dst []byte, arg uint64) []byte
- func AppendNull(dst []byte) []byte
- func AppendRaw(dst []byte, raw RawMessage) []byte
- func AppendTag(dst []byte, tag uint64) []byte
- func AppendText(dst []byte, v string) []byte
- func AppendUint(dst []byte, v uint64) []byte
- func Validate(data []byte, opts DecoderOptions) error
- type Appender
- type Decodable
- type Decoder
- func (d *Decoder) ReadArray() (length int, indefinite bool, err error)
- func (d *Decoder) ReadBool() (bool, error)
- func (d *Decoder) ReadBytes() ([]byte, error)
- func (d *Decoder) ReadFloat() (float64, error)
- func (d *Decoder) ReadInt() (int64, error)
- func (d *Decoder) ReadMap() (pairs int, indefinite bool, err error)
- func (d *Decoder) ReadNull() error
- func (d *Decoder) ReadRaw() (RawMessage, error)
- func (d *Decoder) ReadTag() (uint64, error)
- func (d *Decoder) ReadText() (string, error)
- func (d *Decoder) ReadToken() (Token, error)
- func (d *Decoder) ReadUint() (uint64, error)
- type DecoderOptions
- type Encoder
- func (e *Encoder) Reset(w io.Writer) error
- func (e *Encoder) WriteArray(items []RawMessage) error
- func (e *Encoder) WriteBool(v bool) error
- func (e *Encoder) WriteBytes(v []byte) error
- func (e *Encoder) WriteFloat(v float64) error
- func (e *Encoder) WriteInt(v int64) error
- func (e *Encoder) WriteMap(entries []MapEntry) error
- func (e *Encoder) WriteNegative(arg uint64) error
- func (e *Encoder) WriteNull() error
- func (e *Encoder) WriteRaw(raw RawMessage) error
- func (e *Encoder) WriteTag(tag uint64, content RawMessage) error
- func (e *Encoder) WriteText(v string) error
- func (e *Encoder) WriteUint(v uint64) error
- type EncoderOptions
- type Error
- type KeyOrder
- type MapEntry
- type Profile
- type RawMessage
- func MarshalBool(v bool) RawMessage
- func MarshalBytes(v []byte) RawMessage
- func MarshalFloat(v float64) RawMessage
- func MarshalInt(v int64) RawMessage
- func MarshalNegative(arg uint64) RawMessage
- func MarshalNull() RawMessage
- func MarshalText(v string) (RawMessage, error)
- func MarshalUint(v uint64) RawMessage
- type Reader
- func (r *Reader) Done() bool
- func (r *Reader) Offset() int
- func (r *Reader) Peek() (TokenKind, error)
- func (r *Reader) ReadArrayHeader() (length int, indefinite bool, err error)
- func (r *Reader) ReadBool() (bool, error)
- func (r *Reader) ReadBreak() error
- func (r *Reader) ReadBytes() ([]byte, error)
- func (r *Reader) ReadFloat() (float64, error)
- func (r *Reader) ReadInt() (int64, error)
- func (r *Reader) ReadInt8() (int8, error)
- func (r *Reader) ReadInt16() (int16, error)
- func (r *Reader) ReadInt32() (int32, error)
- func (r *Reader) ReadInt64() (int64, error)
- func (r *Reader) ReadMapHeader() (pairs int, indefinite bool, err error)
- func (r *Reader) ReadNull() error
- func (r *Reader) ReadRaw() (RawMessage, error)
- func (r *Reader) ReadTag() (uint64, error)
- func (r *Reader) ReadText() (string, error)
- func (r *Reader) ReadTextBytes() ([]byte, error)
- func (r *Reader) ReadUint() (uint64, error)
- func (r *Reader) ReadUint8() (uint8, error)
- func (r *Reader) ReadUint16() (uint16, error)
- func (r *Reader) ReadUint32() (uint32, error)
- func (r *Reader) ReadUint64() (uint64, error)
- func (r *Reader) Remaining() int
- func (r *Reader) Reset(data []byte)
- func (r *Reader) Skip() error
- type Token
- type TokenKind
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrMalformed = errors.New("cbor: malformed input") ErrTruncated = errors.New("cbor: truncated input") ErrLimitExceeded = errors.New("cbor: limit exceeded") ErrDuplicateMapKey = errors.New("cbor: duplicate map key") ErrExtraneousData = errors.New("cbor: extraneous data after root item") ErrUnexpectedToken = errors.New("cbor: unexpected token") ErrIntegerOverflow = errors.New("cbor: integer does not fit int64") // ErrProfileViolation reports CBOR that is well formed but not legal under // the profile it was read or written under. ErrProfileViolation = errors.New("cbor: profile violation") // ErrFloatRefused reports a float where the configuration carries scaled // integers instead. It is an ErrProfileViolation with its own identity, // because a format that excludes floats usually does so to keep results // reproducible, and that is worth naming separately from every other way a // profile can be violated. ErrFloatRefused = fmt.Errorf("%w: float", ErrProfileViolation) )
Functions ¶
func AppendArrayHeader ¶
AppendArrayHeader appends a definite-length array head for n items, which the caller then appends in order.
This is the half of the encoder that was missing. WriteArray takes children that are already finished byte slices, so a parent cannot be written until every descendant has been built and copied; the cost of that scales with the depth of the tree. Appending a header instead lets a nested structure be written in one pass into one buffer.
func AppendFloat ¶
AppendFloat appends a float in the shortest form that round-trips, which is what makes float output deterministic. A wire Profile refuses floats outright; this exists for the world profile and for COSE.
func AppendMapHeader ¶
AppendMapHeader appends a definite-length map head for n key/value pairs.
Nothing sorts the pairs the caller then appends. A streaming writer cannot: sorting needs every key at once, which is the shape this exists to avoid. Generated code knows its field set before it runs and can emit the pairs in order; hand-written callers that need the sort should keep using Encoder.WriteMap, and either way Validate under a Profile is what proves the result.
func AppendNegative ¶
AppendNegative appends the negative integer -1-arg. It reaches the whole encoded negative range, including the half below the int64 floor that AppendInt cannot express; the decoder has always been able to read that far.
func AppendRaw ¶
func AppendRaw(dst []byte, raw RawMessage) []byte
AppendRaw appends an item that is already encoded, without validating it.
func AppendText ¶
AppendText appends a text string. It does not validate UTF-8.
func AppendUint ¶
AppendUint appends an unsigned integer in its shortest form.
func Validate ¶
func Validate(data []byte, opts DecoderOptions) error
Validate checks that data contains exactly one bounded CBOR item.
Types ¶
type Appender ¶
Appender is implemented by a type that carries its own CBOR encoding.
AppendCBORTo appends exactly one complete CBOR item to dst and returns the extended buffer, the way the strconv.Append and time.AppendFormat families do. It takes a destination rather than returning a fresh slice because a value returning bytes allocates once per value and undoes the caller's buffer pooling at every nested field, which a caller encoding thousands of messages a second cannot afford.
It returns no error. The append path below this point carries none, and the obligation that creates is on the implementation: for every value of the type, it must append one valid, complete CBOR item. Nothing here can check that, so an encoder that cares -- one writing under a Profile -- should validate the bytes a foreign implementation appended before they reach the wire. See Profile.ValidateAppended.
type Decodable ¶
Decodable is implemented by a type that carries its own CBOR decoding.
DecodeCBORFrom decodes the single CBOR item in data into the receiver, which must be a pointer. data holds exactly one item and nothing after it; Reader.ReadRaw is what produces it, at whatever depth the field sits.
type Decoder ¶
type Decoder struct {
// contains filtered or unexported fields
}
Decoder incrementally reads CBOR from an io.Reader without io.ReadAll. ReadToken exposes container boundaries; typed Read methods are conveniences that reject a token of the wrong kind.
Example ¶
data := []byte{0xa2, 0x01, 0x02, 0x20, 0x42, 0xca, 0xfe}
d, _ := NewDecoder(bytes.NewReader(data), DecoderOptions{RejectDuplicateMapKeys: true})
pairs, _, _ := d.ReadMap()
fmt.Println(pairs)
Output: 2
func NewDecoder ¶
func NewDecoder(r io.Reader, opts DecoderOptions) (*Decoder, error)
func (*Decoder) ReadRaw ¶
func (d *Decoder) ReadRaw() (RawMessage, error)
ReadRaw reads and validates exactly one item while retaining at most MaxRawMessageBytes. In non-sequence mode it also rejects trailing data.
type DecoderOptions ¶
type DecoderOptions struct {
MaxInputBytes int64
MaxNestedLevels int
MaxContainerItems int
MaxStringBytes int
MaxRawMessageBytes int
RejectDuplicateMapKeys bool
Sequence bool
// RejectFloats refuses every float on input. A format that carries scaled
// integers wants this: a float in it is a protocol violation rather than a
// value, and catching it on arrival makes it an error rather than a
// disagreement about what the message meant. Profile.RejectFloats sets it.
RejectFloats bool
}
DecoderOptions controls resource limits and stream behavior. A zero limit selects a conservative default. Negative limits are rejected by NewDecoder.
type Encoder ¶
type Encoder struct {
// contains filtered or unexported fields
}
Encoder writes deterministic RFC 8949. Each method writes one complete item. WriteArray and WriteMap validate their child RawMessages before writing; WriteMap sorts keys under EncoderOptions.KeyOrder, which defaults to the length-first ordering CTAP2 and COSE require rather than to RFC 8949 Core Deterministic Encoding. Indefinite-length output is not supported, because it cannot be deterministic.
Example ¶
var out bytes.Buffer
e, _ := NewEncoder(&out, EncoderOptions{})
key, _ := MarshalText("alg")
_ = e.WriteMap([]MapEntry{{Key: key, Value: MarshalInt(-7)}})
fmt.Println(len(out.Bytes()))
Output: 6
func NewEncoder ¶
func NewEncoder(w io.Writer, opts EncoderOptions) (*Encoder, error)
Example ¶
package main
import (
"bytes"
"fmt"
"github.com/shibukawa/tinygodriver/encoding/cbor"
)
func main() {
var encoded bytes.Buffer
encoder, err := cbor.NewEncoder(&encoded, cbor.EncoderOptions{})
if err != nil {
panic(err)
}
if err := encoder.WriteText("ok"); err != nil {
panic(err)
}
fmt.Printf("%x\n", encoded.Bytes())
}
Output: 626f6b
func (*Encoder) Reset ¶
Reset points the Encoder at a new writer, keeping its options, so a session can hold one encoder instead of allocating one per message.
func (*Encoder) WriteArray ¶
func (e *Encoder) WriteArray(items []RawMessage) error
func (*Encoder) WriteBytes ¶
func (*Encoder) WriteFloat ¶
func (*Encoder) WriteNegative ¶
WriteNegative writes the negative integer -1-arg. See AppendNegative.
func (*Encoder) WriteRaw ¶
func (e *Encoder) WriteRaw(raw RawMessage) error
WriteRaw writes one already deterministic item after validating it.
type EncoderOptions ¶
type EncoderOptions struct {
MaxNestedLevels int
MaxContainerItems int
MaxStringBytes int
// KeyOrder selects the map key ordering WriteMap emits and WriteRaw
// enforces. The zero value keeps the CTAP2 and COSE ordering.
KeyOrder KeyOrder
}
EncoderOptions controls deterministic output limits and the map key ordering. Zero selects a conservative default. Indefinite-length output is deliberately unsupported.
type Error ¶
type Error struct {
// Offset is the byte position in the input at which the failing item began.
Offset int64
// Path names the container route to the failing item where one is known,
// as it would be written in diagnostic notation, and is empty otherwise.
Path string
Err error
}
Error locates a decode failure. It wraps one of the package sentinels, so errors.Is keeps working unchanged, and adds the offset at which the failing item began.
A byte-format error is otherwise only as useful as the caller's ability to find it. Rejecting one attestation needs the sentinel; comparing two builds that disagree about a message needs the position.
type KeyOrder ¶
type KeyOrder uint8
KeyOrder selects which deterministic map key ordering an Encoder emits and enforces. RFC 8949 defines two, and they produce different bytes for the same map, so the choice is part of the wire contract rather than an internal detail. The zero value is LengthFirstKeyOrder.
const ( // LengthFirstKeyOrder sorts shorter encoded keys first and breaks ties // bytewise. This is the length-first ordering of RFC 8949 section 4.2.3, // which is what CTAP2 canonical CBOR and COSE require. LengthFirstKeyOrder KeyOrder = iota // BytewiseKeyOrder sorts bytewise lexicographically over the whole encoded // key with no length pass. This is RFC 8949 section 4.2.1 Core // Deterministic Encoding. BytewiseKeyOrder )
type MapEntry ¶
type MapEntry struct {
Key RawMessage
Value RawMessage
}
MapEntry is one encoded key/value pair for Encoder.WriteMap. Key and Value must each contain exactly one CBOR item. Keys are sorted according to Core Deterministic Encoding before they are written.
type Profile ¶
type Profile struct {
// Name appears in this profile's refusals. It is otherwise unused.
Name string
// RequireSortedKeys demands that map keys appear in KeyOrder, strictly
// ascending. RFC 8949 deterministic encoding requires this; plain CBOR does
// not, so the zero value does not either.
RequireSortedKeys bool
// KeyOrder selects which deterministic ordering RequireSortedKeys demands.
// See KeyOrder: the two RFC 8949 orderings produce different bytes.
KeyOrder KeyOrder
// RejectMaps refuses major type 5 outright. A format that encodes structs
// as fixed-order arrays has no use for it, and refusing it is what keeps
// field names off the wire.
RejectMaps bool
// RejectTags refuses major type 6.
RejectTags bool
// RejectFloats refuses every float, at encode and at decode. A format
// carrying scaled integers wants this: a float in it is a protocol
// violation rather than a value.
RejectFloats bool
// RejectIndefinite refuses indefinite-length items. Deterministic encoding
// requires this, since an indefinite item has more than one spelling.
RejectIndefinite bool
// RejectTextKeys refuses a text string as a map key, leaving integer
// labels. COSE and CTAP2 use integer labels for the same reason.
RejectTextKeys bool
}
A Profile is a named restriction on which CBOR is legal: a subset of the format that both ends of a protocol agree on. CTAP2 canonical CBOR, COSE and RFC 8949 section 4.2 deterministic encoding are all restrictions of this kind.
A Profile carries no resource limits, and the distinction is the point.
What a profile says is a property of the protocol: both peers must agree on it, a disagreement is a defect, and changing it changes the format. How large an item a particular process is willing to read is a property of that process: a dedicated server and a browser client can hold different answers, and there is nothing to agree on. Those live in DecoderOptions, chosen per deployment, and are passed alongside a profile rather than baked into one.
Mixing them, which this type used to do, makes a deployment decision look like a protocol change and hides a protocol change inside a deployment one.
The zero value restricts nothing: every well-formed CBOR item is legal under it. Build one by naming what to refuse.
wire := cbor.Profile{
Name: "wire",
RejectMaps: true,
RejectTags: true,
RejectFloats: true,
RejectIndefinite: true,
RejectTextKeys: true,
}
A Profile only enforces. It never infers which profile a message should be read under; that belongs to the schema and to the caller.
func Canonical ¶ added in v1.2.7
func Canonical() Profile
Canonical returns the profile CTAP2 canonical CBOR and COSE require: map keys in length-first order, no indefinite lengths, everything else permitted.
This is the shape a WebAuthn attestation is checked against. Floats are not refused, because nothing in COSE forbids them.
func Deterministic ¶ added in v1.2.7
func Deterministic() Profile
Deterministic returns RFC 8949 section 4.2.1 Core Deterministic Encoding: the same restrictions as Canonical with bytewise key ordering instead of length-first.
func (Profile) NewReader ¶
func (p Profile) NewReader(data []byte, opts DecoderOptions) (*Reader, error)
NewReader returns a Reader over data with the caller's limits and this profile's decoder-visible restrictions.
func (Profile) ReaderOver ¶
func (p Profile) ReaderOver(data []byte, opts DecoderOptions) Reader
ReaderOver returns a Reader by value on the same terms as NewReader. See ReaderOver.
func (Profile) Validate ¶
func (p Profile) Validate(data []byte, opts DecoderOptions) error
Validate reports whether data is exactly one item legal under p and within the limits in opts. It answers the question without decoding the item into anything, which is what makes it usable as a boundary check on bytes from somewhere else.
Depth arithmetic ¶
Validate walks the whole document, so nesting adds up: an envelope that wraps a message costs the envelope's depth plus the message's. A patch or delta carrying a subtree of a document is therefore deeper than the document.
Reading does not add up the same way. Reader.ReadRaw measures a captured item from zero, so decoding an envelope field by field and handing each payload on as raw bytes costs the larger of the two depths rather than their sum.
The default nesting bound is a stack safety net set far past any schema, so neither arithmetic matters unless a caller narrows it deliberately.
func (Profile) ValidateAppended ¶
func (p Profile) ValidateAppended(dst []byte, before int, opts DecoderOptions) error
ValidateAppended checks the item a foreign AppendCBORTo just wrote, given the buffer and the length it had before the call.
A type that carries its own encoding cannot be made to honour a profile by the type system: nothing stops it appending a float, an indefinite-length item, or an unsorted map into a message that must not contain one. Nothing downstream would notice either, because the bytes are well-formed CBOR -- they are simply not the CBOR this profile promised. Running this after the call is what turns that from a silent divergence into an error.
type RawMessage ¶
type RawMessage []byte
RawMessage holds exactly one validated CBOR item when returned by Decoder. Callers constructing RawMessage directly should pass it through Validate or Encoder.WriteRaw before trusting it.
func MarshalBool ¶
func MarshalBool(v bool) RawMessage
func MarshalBytes ¶
func MarshalBytes(v []byte) RawMessage
func MarshalFloat ¶
func MarshalFloat(v float64) RawMessage
func MarshalInt ¶
func MarshalInt(v int64) RawMessage
func MarshalNegative ¶
func MarshalNegative(arg uint64) RawMessage
MarshalNegative returns the negative integer -1-arg as a RawMessage, reaching the range MarshalInt cannot. See AppendNegative.
func MarshalNull ¶
func MarshalNull() RawMessage
func MarshalText ¶
func MarshalText(v string) (RawMessage, error)
func MarshalUint ¶
func MarshalUint(v uint64) RawMessage
Raw constructors make deterministic primitive values convenient to use in WriteArray and WriteMap.
type Reader ¶
type Reader struct {
// contains filtered or unexported fields
}
Reader decodes CBOR from a byte slice already in memory. It is the counterpart of Decoder for callers that have the whole item: it borrows strings from the input instead of copying them, skips an item without allocating it, and captures a sub-item at any depth, none of which an incremental io.Reader decoder can do.
Every slice a Reader returns aliases the input. They stay valid for as long as the caller keeps the input alive and does not modify it; Reset severs nothing, so a slice from a previous input outlives the Reader but not the bytes behind it. Callers that need independent storage should copy.
A Reader is not safe for concurrent use. Reuse one per connection rather than allocating one per message; that is what Reset is for.
Nesting ¶
Skip, ReadRaw and Profile.Validate drive their own recursion and bound it by DecoderOptions.MaxNestedLevels. ReadArrayHeader and ReadMapHeader do not: they read one head and return, and the walk over the container is the caller's own loop, so its depth is the caller's to bound. This differs from Decoder, which keeps a frame stack and refuses a container past the limit from ReadToken.
The difference is deliberate. A frame stack is the state a Reader does not keep, and keeping one would cost an allocation per reader in the path that exists to have none. For untrusted input, call Profile.Validate first: it answers whether the bytes are legal under a bound without decoding them into anything, and generated code walking a fixed schema then recurses no deeper than the schema does.
func NewReader ¶
func NewReader(data []byte, opts DecoderOptions) (*Reader, error)
NewReader returns a Reader over data. A zero limit in opts selects the same conservative default NewDecoder uses.
func ReaderOver ¶
func ReaderOver(data []byte, opts DecoderOptions) Reader
ReaderOver returns a Reader by value, for a caller that wants it on the stack.
NewReader returns a pointer, which escapes, and a DecodeCBORFrom implementation runs once per field per message: at that rate the constructor is the allocation. A negative limit is treated as unset here rather than refused, because there is no error to return.
func (*Reader) Offset ¶
Offset reports how many bytes have been consumed, which is where the next item begins.
func (*Reader) Peek ¶
Peek reports the kind of the next item without consuming it. It is the way to branch on an optional field: a caller that would otherwise have to capture and rewind can dispatch instead.
func (*Reader) ReadArrayHeader ¶
ReadArrayHeader reads an array head and returns its length. A definite-length array reports indefinite false and its item count; an indefinite one reports true and a length of -1, and ends at the item Peek reports as EndArray.
It bounds the item count but not the nesting depth, which the caller's own walk owns. See the Nesting section of the Reader documentation.
func (*Reader) ReadInt ¶
ReadInt reads an integer of either sign. A negative integer outside the int64 range is ErrIntegerOverflow; ReadRaw preserves it where the value itself is needed.
func (*Reader) ReadInt8 ¶
Sized integer reads. The encoder writes the shortest form, so a field declared int32 arrives as anything from one to five bytes and its width is carried by the schema rather than the bytes. These enforce the declared width, so a value outside it is a protocol error rather than a silent wrap.
func (*Reader) ReadMapHeader ¶
ReadMapHeader reads a map head and returns its pair count, on the same terms as ReadArrayHeader, nesting included.
func (*Reader) ReadRaw ¶
func (r *Reader) ReadRaw() (RawMessage, error)
ReadRaw returns the bytes of exactly one item, at whatever depth the Reader currently sits, and advances past it. The result aliases the input.
This is the primitive a type that carries its own decoding needs: a field of a foreign type is always nested inside something, and handing that type its own bytes is the only way to decode it without knowing its shape.
The captured item is measured from zero against MaxNestedLevels, so the depth already spent reaching it does not count against it. Decoding an envelope field by field therefore costs the larger of the envelope's depth and its payload's, where validating the whole document at once costs their sum. See Profile.Validate.
func (*Reader) ReadTextBytes ¶
ReadTextBytes returns a text string borrowed from the input, without the allocation ReadText's string conversion costs.
func (*Reader) ReadUint16 ¶
func (*Reader) ReadUint32 ¶
func (*Reader) ReadUint64 ¶
ReadUint64 is ReadUint under the name the sized set uses.
func (*Reader) Reset ¶
Reset points the Reader at a new input, keeping its options. Slices returned before the call still alias the old input.
type Token ¶
type Token struct {
Kind TokenKind
Argument uint64
Bytes []byte
Text string
Bool bool
Float float64
Length int
Indefinite bool
}
Token is a typed CBOR token. Argument stores the unsigned argument for integers and tags. A NegativeInteger represents -1-Argument, retaining the full RFC 8949 range even when it cannot fit in int64.