Documentation
¶
Overview ¶
Package cate implements a Compact ASN.1 Tagged Encoding scheme.
Cate is a lightweight binary encoding with explicit type tags that is compatible with ASN.1 BER/DER structure. It's optimized for space efficiency while maintaining self-describing data. The package can encode and decode primitive types (integers, strings, booleans) and composite types (structs, slices).
Cate is not a general-purpose ASN.1 encoder or decoder. It can decode arbitrary ASN.1 TLVs, but value decoding support is intentionally limited to only what is necessary for encoding Go values.
Basic usage:
// Encoding
data, err := cate.Marshal(value)
if err != nil {
// handle error
}
// Decoding
var result MyType
err = cate.Unmarshal(data, &result)
if err != nil {
// handle error
}
For more control over encoding/decoding behavior, use the Options API:
// Enable implicit tagging and omit zero values data, err := cate.Marshal(value, cate.UseImplicitTags(true), cate.OmitZero(true)) // Decode with the same options err = cate.Unmarshal(data, &result, cate.UseImplicitTags(true), cate.OmitZero(true))
Custom types can implement the Marshaller and Unmarshaller interfaces to control their ASN.1 representation:
func (v MyType) MarshalCATE(enc *cate.Encoder) (cate.Value, error) {
// Custom encoding logic
}
func (v *MyType) UnmarshalCATE(dec *cate.Decoder, val cate.Value) error {
// Custom decoding logic
}
Reference Handling ¶
**References are not currently supported for external types.**
Cate supports encoding and decoding of data structures with cyclic references. This allows for complex data structures like linked lists, trees with parent pointers, or general graph structures.
When encoding with references enabled:
- The encoder tracks each value it has seen using a map from value to reference ID.
- When a value is first encountered, it's assigned a unique reference ID and encoded normally.
- When a value is encountered again, only its reference ID is encoded.
During decoding:
- The decoder maintains a map from reference IDs to their corresponding values.
- When a reference is decoded, the decoder looks up the corresponding value.
- For forward references (references to values not yet seen), placeholder values are created and later populated when the actual value is decoded.
References are encoded using application-specific tags:
- For a reference definition: Application-specific tag with reference type number, containing a SEQUENCE of (INTEGER reference-id, actual-value).
- For a reference use: Application-specific tag with reference type number, containing just the INTEGER reference-id.
Types must implement the referenceable interface to participate in reference handling:
type referenceable interface {
refKey() TagNumber
}
The refKey method returns the tag number to use for references to this type.
REAL ¶
The first byte of a REAL value specifies how it is encoded. Bits 7-6 indicate the encoding:
- 00 - decimal encoding
- 01 - special values
- 1x - binary encoding
The supported special values are:
- 0x40 = +∞
- 0x41 = -∞
- 0x42 = NaN
- 0x43 = -0.0
Decimal encoding is not supported. Decimal encoded values will be returned as [Raw]. For binary encoded values, the bit fields of the first byte are:
- Bit 7 - always 1 for binary encoding.
- Bit 6 - sign (0 = positive, 1 = negative).
- Bit 5-4 - exponent base (0 = base 2, 1 = base 8, 2 = base 16).
- Bit 3-2 - "scale factor", an extra base 2 exponent/multiplier with a value of 0-3.
- Bit 1-0 - exponent length (0-2 = exponent is the next 1-3 bytes, 3 = the next byte is the exponent length in bytes).
The remaining bytes (after the exponent) are the mantissa. The value is S × N × 2^F × base^E (S=sign, N=mantissa, F=scale factor, E=exponent).
When encoding, CATE only uses base 2 and does not use the scale factor.
Index ¶
- Constants
- func Copy(dst io.Writer, src io.Reader) error
- func Dump(wr io.Writer, rd io.Reader, prefix, indent string)
- func GetOption[V any](o Options, opt func(V) Options) V
- func GetParent[V any](dec *Decoder) (V, bool)
- func Marshal(value any, opts ...Options) ([]byte, error)
- func Unmarshal(data []byte, target any, opts ...Options) error
- type BigInt
- type BigReal
- type Boolean
- type Class
- type ConstructedValue
- type DateTime
- type Decoder
- type EOC
- type Encoder
- type Form
- type Integer
- type Marshaller
- type MarshallerTo
- type Null
- type OctetString
- type Options
- func EmitSchemas(v bool) Options
- func MatchFieldsByName(v bool) Options
- func MergeOptions(opts ...Options) Options
- func OmitZero(v bool) Options
- func OrderedMaps(v bool) Options
- func PassByReference[T Referenceable](tag TagNumber) Options
- func ReserveAppSpecific(num TagNumber) Options
- func UseImplicitTags(v bool) Options
- func WithExternalReferences[T Referenceable](tag TagNumber, refs map[uint64]T) Options
- func WithExternalSchemas(refs map[uint64]Schema) Options
- func WithKnownReferences[T Referenceable](tag TagNumber, refs map[uint64]T) Options
- func WithKnownSchemas(refs map[uint64]Schema) Options
- type PrimitiveValue
- type Reader
- type Real
- type Referenceable
- type Schema
- type Sequence
- type SequenceValue
- type Set
- type Tag
- type TagNumber
- type UTF8String
- type Unmarshaller
- type UnmarshallerFrom
- type Value
- type Writer
Constants ¶
const ( // Universal is the universal ASN.1 tag class. Universal Class = 0x00 // Application is the application-specific ASN.1 tag class. Application Class = 0x40 // ContextSpecific is the context-specific ASN.1 tag class. ContextSpecific Class = 0x80 // Private is the private ASN.1 tag class. Private Class = 0xC0 // Primitive form indicates a single value. Primitive Form = 0x00 // Constructed form indicates a structured value containing other values. Constructed Form = 0x20 )
Variables ¶
This section is empty.
Functions ¶
func Dump ¶
Dump writes a human-readable representation of the ASN.1 TLV data read from rd to wr. prefix is prepended to each line and indent is appended to the prefix for each level of nesting.
func GetParent ¶
GetParent walks up the decode stack and returns the first parent/ancestor of type V.
func Marshal ¶
Marshal encodes the provided value into ASN.1 BER-encoded data. It handles encoding various Go types such as basic types, structs, and slices.
func Unmarshal ¶
Unmarshal decodes ASN.1 BER-encoded data into the provided target value. It handles decoding into various Go types such as basic types, structs, and slices.
The data parameter should contain the complete ASN.1 TLV (Tag-Length-Value) encoding. The target parameter should be a pointer to the value to decode into. The opts parameter allows customizing the decoding behavior.
Types ¶
type BigReal ¶
BigReal represents an ASN.1 REAL value for arbitrary precision floating point numbers.
type Class ¶
type Class byte
Class represents the class part of an ASN.1 tag.
func (Class) IsApplication ¶
IsApplication returns true if the class is Application.
func (Class) IsContextSpecific ¶
IsContextSpecific returns true if the class is ContextSpecific.
func (Class) IsUniversal ¶
IsUniversal returns true if the class is Universal.
type ConstructedValue ¶
ConstructedValue represents an ASN.1 constructed value.
func (*ConstructedValue) String ¶
func (v *ConstructedValue) String() string
func (*ConstructedValue) Tag ¶
func (v *ConstructedValue) Tag() Tag
Tag returns an ASN.1 constructed value tag.
type Decoder ¶
type Decoder struct {
// contains filtered or unexported fields
}
Decoder is an ASN.1 decoder that can decode ASN.1 values.
func NewDecoder ¶
NewDecoder creates a new ASN.1 decoder with the specified options.
func (*Decoder) Decode ¶
Decode decodes an ASN.1 value into the provided target. The target should be a pointer to the value to decode into.
type EOC ¶
type EOC struct{}
EOC represents an ASN.1 End-Of-Content value, used to terminate constructed values with indefinite length.
type Encoder ¶
type Encoder struct {
// contains filtered or unexported fields
}
Encoder is an ASN.1 encoder that can encode Go values to ASN.1 values.
func NewEncoder ¶
NewEncoder creates a new ASN.1 encoder with the specified options.
func (*Encoder) Encode ¶
Encode converts a Go value to its ASN.1 representation. Various Go types are mapped to specific ASN.1 types as defined by the package.
type Form ¶
type Form byte
Form represents the form (primitive or constructed) part of an ASN.1 tag.
func (Form) IsConstructed ¶
IsConstructed returns true if the form is Constructed.
func (Form) IsPrimitive ¶
IsPrimitive returns true if the form is Primitive.
type Integer ¶
type Integer int64
Integer represents an ASN.1 INTEGER value that fits within an int64.
type Marshaller ¶
type Marshaller interface {
// MarshalCATE returns the ASN.1 representation of the value.
MarshalCATE(*Encoder) (Value, error)
}
Marshaller is the interface implemented by types that can marshal themselves into ASN.1 values.
type MarshallerTo ¶
MarshallerTo is the interface implemented by types that can marshal themselves directly into a stream. It is the stream-based counterpart to Marshaller and avoids building an intermediate Value tree.
type OctetString ¶
type OctetString []byte
OctetString represents an ASN.1 OCTET STRING, which is a sequence of bytes.
func (OctetString) String ¶
func (v OctetString) String() string
func (OctetString) Tag ¶
func (v OctetString) Tag() Tag
Tag returns the ASN.1 tag for OctetString values.
type Options ¶
type Options interface {
// contains filtered or unexported methods
}
Options represents a set of configuration options for encoding or decoding.
func EmitSchemas ¶
EmitSchemas configures whether to include type schema information in the encoding. When enabled, values are encoded with their type schema, allowing for type checking during decoding.
func MatchFieldsByName ¶
MatchFieldsByName enables matching fields by name when decoding, instead of by field number. Specifically, when decoding struct values tagged with a schema, allow T to be decoded into S if, for each field F of T there is a corresponding field G of S with the same name and a compatible type.
func MergeOptions ¶
MergeOptions combines multiple Options into a single Options. Later options take precedence over earlier ones.
func OmitZero ¶
OmitZero configures whether to omit zero values when encoding. When enabled, struct fields with zero values will not be included in the encoding.
func OrderedMaps ¶
OrderedMaps enables ordering of map entries when encoding a map that has keys of a type that has a natural ordering, such as strings or numbers.
func PassByReference ¶
func PassByReference[T Referenceable](tag TagNumber) Options
PassByReference enables pass-by-reference encoding for values of type T. When a value of type T is encoded or decoded, it is assigned a reference ID and tagged with the given application tag number. Subsequent occurrences of the same value are encoded as a reference to that ID rather than re-encoding the full value.
func ReserveAppSpecific ¶
ReserveAppSpecific marks an application-specific tag number as in-use without associating it with a Go type. This prevents CATE from interpreting data tagged with that number as reference or schema framing. Use it when manually encoding data that uses a particular application tag and you need to protect it from being misinterpreted by CATE.
func UseImplicitTags ¶
UseImplicitTags configures whether to use implicit tagging for context-specific types. When enabled, primitive values are encoded directly with context-specific tags instead of being wrapped in a constructed value.
func WithExternalReferences ¶
func WithExternalReferences[T Referenceable](tag TagNumber, refs map[uint64]T) Options
WithExternalReferences establishes a pre-agreed map of reference IDs to values of type T that are negotiated out-of-band. Unlike WithKnownReferences, the encoder will NOT emit these values into the stream — it encodes only the reference ID. The decoder resolves IDs from the provided map without expecting to see the full value definition. Implies PassByReference for T.
Use this when both sides have agreed on a reference dictionary in advance and the stream should contain only reference IDs, not full definitions.
func WithExternalSchemas ¶
WithExternalSchemas is a convenience wrapper around WithExternalReferences for Schema values. The encoder will not emit these schemas into the stream — it encodes only their IDs. The decoder resolves IDs from the provided map. Implies EmitSchemas.
Use this when both sides have agreed on a schema dictionary in advance and the stream should contain only schema IDs, not full schema definitions.
func WithKnownReferences ¶
func WithKnownReferences[T Referenceable](tag TagNumber, refs map[uint64]T) Options
WithKnownReferences establishes a pre-agreed map of reference IDs to values of type T. Both encoder and decoder share this map: the encoder emits each value with its assigned ID, and the decoder can resolve IDs that appear in the stream using the map. Implies PassByReference for T.
Use this when both sides share a reference dictionary and full value definitions should still be included in the stream.
func WithKnownSchemas ¶
WithKnownSchemas is a convenience wrapper around WithKnownReferences for Schema values. The encoder emits each schema with its assigned ID; subsequent uses encode only the ID rather than the full schema. The decoder resolves schema IDs from the provided map. Implies EmitSchemas.
Use this when both sides share a schema dictionary and full schema definitions should still be included in the stream.
type PrimitiveValue ¶
PrimitiveValue represents an ASN.1 primitive value.
func (*PrimitiveValue) String ¶
func (v *PrimitiveValue) String() string
func (*PrimitiveValue) Tag ¶
func (v *PrimitiveValue) Tag() Tag
Tag returns an ASN.1 primitive value tag.
type Reader ¶
type Reader interface {
io.Reader
io.ByteReader
Next() error
Tag() Tag
Len() int
Value() (Value, error)
}
Reader reads ASN.1 TLV-encoded data as a stream.
Next advances to the next TLV element, reading its tag and length. After a successful call to Next, Tag returns the element's tag, and the caller may consume its content in one of three ways:
Value returns the element as a fully-parsed Value. For constructed values this recursively parses all children.
Read and ReadByte read raw content bytes from a primitive element.
For a constructed element, the caller calls Next again to iterate over child elements. When all children have been consumed, Next returns an end-of-contents sentinel (Tag == 0, length == 0).
Read and ReadByte must only be called after Next advances to a primitive element; they return an error for constructed elements. Conversely, calling Next to descend into children is only valid for constructed elements. The element's length limits how many bytes Read and ReadByte may return.
Tag and Value must not be called before a successful call to Next.
type Referenceable ¶
type Referenceable interface {
IsReferenceable() bool
}
Referenceable is implemented by types that may participate in pass-by-reference encoding. IsReferenceable returns true when a specific value should be tracked by reference; returning false excludes that value. Registering a type with PassByReference is necessary for its values to be encoded by reference.
type Schema ¶
type Schema interface {
Referenceable
// contains filtered or unexported methods
}
Schema is the schema for a type. It is used internally to validate decoded values. Its only external use is for applications that wish to manage schema IDs externally.
type Sequence ¶
type Sequence []Value
Sequence represents an ASN.1 SEQUENCE, which is an ordered collection of values.
type SequenceValue ¶
SequenceValue is a Value that contains an ordered sequence of child values. It is implemented by Sequence, Set, and ConstructedValue.
type Set ¶
type Set []Value
Set represents an ASN.1 SET, which is an unordered collection of values.
type Tag ¶
type Tag uint
Tag represents an ASN.1 tag, composed of class, form, and number components.
func (Tag) MayBeConstructed ¶
MayBeConstructed returns true if the tagged value may be represented in constructed form.
func (Tag) MayBePrimitive ¶
MayBePrimitive returns true if the tagged value may be represented in primitive form.
func (Tag) MayHaveIndefiniteLength ¶
MayHaveIndefiniteLength returns true if the tagged value may have an indefinite length.
type TagNumber ¶
type TagNumber uint
TagNumber represents the number part of an ASN.1 tag.
const ( EOCTag TagNumber = 0x00 BooleanTag TagNumber = 0x01 IntegerTag TagNumber = 0x02 BitStringTag TagNumber = 0x03 OctetStringTag TagNumber = 0x04 NullTag TagNumber = 0x05 ObjectIdentifierTag TagNumber = 0x06 ObjectDescriptorTag TagNumber = 0x07 ExternalTag TagNumber = 0x08 RealTag TagNumber = 0x09 EnumeratedTag TagNumber = 0x0A EmbeddedPdvTag TagNumber = 0x0B UTF8StringTag TagNumber = 0x0C RelativeOidTag TagNumber = 0x0D TimeTag TagNumber = 0x0E SequenceTag TagNumber = 0x10 SetTag TagNumber = 0x11 NumericStringTag TagNumber = 0x12 PrintableStringTag TagNumber = 0x13 TeletexStringTag TagNumber = 0x14 VideotexStringTag TagNumber = 0x15 IA5StringTag TagNumber = 0x16 UTCTimeTag TagNumber = 0x17 GeneralizedTimeTag TagNumber = 0x18 GraphicStringTag TagNumber = 0x19 VisibleStringTag TagNumber = 0x1A GeneralStringTag TagNumber = 0x1B UniversalStringTag TagNumber = 0x1C CharacterStringTag TagNumber = 0x1D BmpStringTag TagNumber = 0x1E DateTag TagNumber = 0x1F TimeOfDayTag TagNumber = 0x20 DateTimeTag TagNumber = 0x21 DurationTag TagNumber = 0x22 OidIriTag TagNumber = 0x23 RelativeOidIriTag TagNumber = 0x24 )
ASN.1 universal tag numbers
type UTF8String ¶
type UTF8String string
UTF8String represents an ASN.1 UTF8String, which is a UTF-8 encoded string.
func (UTF8String) Tag ¶
func (v UTF8String) Tag() Tag
Tag returns the ASN.1 tag for UTF8String values.
type Unmarshaller ¶
type Unmarshaller interface {
// UnmarshalCATE sets the value from the provided ASN.1 value.
UnmarshalCATE(*Decoder, Value) error
}
Unmarshaller is the interface implemented by types that can unmarshal ASN.1 values into themselves.
type UnmarshallerFrom ¶
UnmarshallerFrom is the interface implemented by types that can unmarshal themselves directly from a stream. It is the stream-based counterpart to Unmarshaller and avoids building an intermediate Value tree.
type Value ¶
type Value interface {
// Tag returns the ASN.1 tag for this value.
Tag() Tag
}
Value is an ASN.1 value.
type Writer ¶
type Writer interface {
io.Writer
io.StringWriter
io.ByteWriter
WriteTag(Tag, int) error
WriteValue(Value) error
End() error
}
Writer writes ASN.1 TLV-encoded data as a stream.
A value is written in one of two ways:
WriteValue writes a complete Value (tag, length, and content) in one call. No call to End is needed.
WriteTag opens a new value by writing its tag and length. For primitive values, the caller writes the content bytes via Write, WriteByte, or WriteString. No call to End is needed; the value is complete once all content bytes have been written. For constructed values, the caller writes child values (via WriteTag/End or WriteValue), then calls End.
For constructed values, WriteTag pushes a frame. End pops the frame: if the length was indefinite (n < 0) it emits an end-of-contents marker; if definite, it verifies that all bytes were written.
As a special case, WriteTag with n == 0 does not push a frame and the caller must not call Write, WriteByte, WriteString, or End for that value.
Write, WriteByte, and WriteString must only be called after WriteTag opens a primitive value. Conversely, WriteTag and WriteValue must not be called after opening a primitive value. In other words, primitive values accept only raw bytes; constructed values accept only child values.