ssztypes

package
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package ssztypes provides SSZ type descriptors, caching, and struct tag parsing for the dynamic-ssz library.

It defines the core type system used to describe how Go types map to SSZ encoding: containers, lists, vectors, bitlists, bitvectors, unions, and all basic SSZ types. Type descriptors are computed from Go reflect types and SSZ struct tags (ssz-size, ssz-max, dynssz-size, dynssz-max, etc.) and cached in a TypeCache for reuse.

Index

Constants

SszCompatFlagDynamicMethods covers the spec-aware methods a type can carry for its own value. A custom type that carries any of them takes the spec set, and is treated as depending on it.

SszCompatFlagFastsszSurface is every fastssz-style method that answers for a whole value. A type serving every SSZ operation without its spec-aware methods provides all of them; the marshal half is satisfied by either marshal method.

Variables

This section is empty.

Functions

func EvaluateMinSize added in v1.4.0

func EvaluateMinSize(specs sszutils.DynamicSpecs, literal uint64, expr string) int64

EvaluateMinSize resolves a declared floor against specs: the expression with the literal as its fallback, as a size tag resolves, or the literal alone. A floor is a bound and never a refusal: one that does not resolve, or lies beyond the SSZ size limit, states no floor, as generated code treats it.

func FixupRecursiveFlags added in v1.4.0

func FixupRecursiveFlags(root *TypeDescriptor)

FixupRecursiveFlags re-derives the child-propagated type flags across a descriptor graph that contains at least one recursive cycle. It is shared by the reflection type cache and the code generator's parser, which build their descriptor graphs independently but propagate flags by the same rules.

During a recursive build, a back-edge hands out a descriptor whose child-derived flags are not final yet, and descriptors built inside the cycle complete (and are cached) before the cycle head does — so they can permanently miss flags that only become visible once the head is finished. This pass runs after the whole graph is built and back-patched: it walks every reachable descriptor and ORs the child-derived flags upward until no flag changes. Flags only accumulate (the pass never clears a type flag), so own-hint flags are preserved, the fixpoint is unique regardless of iteration order, and re-running over already-exact subtrees is a no-op.

When a flag is raised on a descriptor, the fastssz compatibility flags that were detected under the old (incomplete) flags are suppressed to match how detectCompatFlags gates them: a fastssz method is only valid while no resolved size or limit below the type differs from the static tags it baked in. Custom types keep their compat flags — they have no traversed children, so their flags can never change here anyway.

func IsSszExcluded added in v1.4.0

func IsSszExcluded(tag reflect.StructTag) bool

IsSszExcluded reports whether a struct field is excluded from SSZ processing via `ssz-type:"-"`. Such a field is omitted from the SSZ layout entirely (not encoded, decoded, sized or hashed) while remaining an ordinary Go field, so its type need not be SSZ-compatible.

func JoinFieldAnnotationTag added in v1.4.0

func JoinFieldAnnotationTag(fieldTag reflect.StructTag, annotationTag string) reflect.StructTag

JoinFieldAnnotationTag returns the effective SSZ tag for a struct field whose type carries a registered annotation: the field tag is joined in front of the annotation tag, so a key present in both resolves to the field's value (reflect.StructTag.Lookup returns the first occurrence) while annotation-only keys still apply.

func MarkNoSszRoot added in v1.4.0

func MarkNoSszRoot(extendedTypes bool, desc *TypeDescriptor)

MarkNoSszRoot flags a list or bitlist that carries no limit, unless extended types are enabled. It is shared by the reflection type cache and the code generator's parser so both classify the same types the same way.

A limit is part of the type in SSZ: List[T, N] and Bitlist[N] need N to merkleize, so a list without one has no defined hash tree root. The library still hashes it -- merkleizing to the chunks the value occupies and mixing in the length, so the root at least identifies the value -- but that root is outside the spec and no other implementation will agree on it, which is why it is an opt-in extension rather than the default.

Serialization is unaffected: a limit only bounds a list, and encoding and decoding never need it. So the flag is set here but only acted on where the root is produced, which also keeps a view's data type usable: it carries no tags because the layout lives in the view schema, and hashing it through the view uses the view's descriptor, which does have the limit.

A progressive list or bitlist is unbounded by design (EIP-7916) and is unaffected.

func NoSszRootError added in v1.4.0

func NoSszRootError(desc *TypeDescriptor) error

NoSszRootError reports that a limit-less list or bitlist cannot be hashed. Both engines raise it where the root would be produced: the reflection engine at hash time, the code generator while emitting the hash method.

func ParseMinSizeDeclaration added in v1.4.0

func ParseMinSizeDeclaration(tag reflect.StructTag) (literal uint64, expr string, ok bool)

ParseMinSizeDeclaration reads the floor a variable-size type declares beside ssz-static: ssz-minsize, the bytes every value holds in its fixed section when no spec value is defined, and dynssz-minsize, the expression the spec resolves to those bytes, which resolves each spec-decided part on its own through the evaluator's :N fallback (see ResolveSpecValue). A literal that does not parse states no floor.

func ParseTags added in v1.3.0

func ParseTags(tag string) (typeHints []SszTypeHint, sizeHints []SszSizeHint, maxSizeHints []SszMaxSizeHint, err error)

ParseTags parses SSZ annotations from a string in struct tag format. This is used for extracting SSZ annotations from sources other than struct fields, such as type-level annotations registered via sszutils.Annotate[T]().

func RejectMaxOnVector added in v1.4.0

func RejectMaxOnVector(sizeHints []SszSizeHint, maxSizeHints []SszMaxSizeHint, arrayLen uint64, typeName string) error

RejectMaxOnVector rejects a limit declared for a dimension that is fixed with a different length. A vector's length is its type, so it has no capacity left to bound: a diverging limit describes nothing and was being ignored, which reads as a bounded list to anyone writing the tag. A limit equal to the fixed length is accepted — it states what the type already says, and tags are positional, so the common spelling that bounds an outer list repeats the inner vector's length in both families ([][48]byte `ssz-size:"?,48" ssz-max:"4096,48"`). It is shared by the reflection type cache and the code generator's parser so both read the same tags the same way.

The dimension is fixed either because ssz-size gave it a length or because the Go type is an array, so this catches both spellings; arrayLen carries the Go array length (0 otherwise). Only the dimension being built is examined: an outer array of bounded lists carries `?` for its own dimension and its limits belong to the inner ones.

func SameHints added in v1.4.0

func SameHints(typeA, typeB []SszTypeHint, sizeA, sizeB []SszSizeHint, maxA, maxB []SszMaxSizeHint) bool

SameHints reports whether two sets of parsed tag hints describe the same SSZ shape. A struct field whose joined tag reads the same as its type's annotation says nothing new about the type.

func SameSszTypes added in v1.4.0

func SameSszTypes(a, b []SszTypeHint) bool

SameSszTypes reports whether two type-hint lists declare the same SSZ type per dimension.

func UnionVariantTypes added in v1.4.0

func UnionVariantTypes(descriptorType reflect.Type) (map[uint8]reflect.Type, error)

UnionVariantTypes returns the selector-to-variant-type mapping a compatible union descriptor struct declares: field order starting at 1, or the fields' ssz-index tags when present (all-or-none, valid range 1..127, no duplicates). It is the selector authority shared by descriptor extraction and the union constructors, so a selector accepted by one is accepted by the other.

Types

type ContainerDescriptor

type ContainerDescriptor struct {
	Fields    []FieldDescriptor    `json:"fields"`     // For structs
	DynFields []DynFieldDescriptor `json:"dyn_fields"` // Dynamic struct fields
}

ContainerDescriptor holds the field descriptors of a struct.

type DynFieldDescriptor

type DynFieldDescriptor struct {
	Field        *FieldDescriptor `json:"field"`
	HeaderOffset int64            `json:"offset"`
	Index        int32            `json:"index"` // Index of the field in the struct
}

DynFieldDescriptor represents a dynamic field descriptor for a struct

type FieldDescriptor

type FieldDescriptor struct {
	Name       string          `json:"name"`                  // Name of the field (from schema struct)
	Type       *TypeDescriptor `json:"type"`                  // Type descriptor (built from runtime/schema pair)
	SszIndex   uint16          `json:"index,omitempty"`       // SSZ index for progressive containers
	FieldIndex uint32          `json:"field_index,omitempty"` // Index into the runtime struct's field list
}

FieldDescriptor represents a cached descriptor for a struct field. When using view descriptors (schema type differs from runtime type), the FieldIndex points to the corresponding field in the runtime struct, while Name and Type come from the schema struct's field definition.

type GoTypeFlag

type GoTypeFlag uint8

GoTypeFlag is a bitmask indicating Go-specific type properties that affect SSZ encoding behavior.

const (
	GoTypeFlagIsPointer   GoTypeFlag = 1 << iota // Whether the type is a pointer type
	GoTypeFlagIsByteArray                        // Whether the type is a byte array
	GoTypeFlagIsString                           // Whether the type is a string type
	GoTypeFlagIsTime                             // Whether the type is a time.Time type
	GoTypeFlagIsView                             // Whether the type uses a view descriptor
)

type SszCompatFlag

type SszCompatFlag uint32

SszCompatFlag is a flag indicating whether a type implements a specific SSZ compatibility interface. Each flag names one method, so a caller asks for the method it is about to call rather than for a family the two type front ends could read differently.

const (
	SszCompatFlagFastsszValueMarshaler  SszCompatFlag = 1 << iota // Whether the type implements FastsszValueMarshaler
	SszCompatFlagFastsszBufferMarshaler                           // Whether the type implements FastsszBufferMarshaler
	SszCompatFlagFastsszSizer                                     // Whether the type implements FastsszSizer
	SszCompatFlagFastsszUnmarshaler                               // Whether the type implements FastsszUnmarshaler
	SszCompatFlagFastsszHashRoot                                  // Whether the type implements FastsszHashRoot
	SszCompatFlagFastsszHashRootWith                              // Whether the type implements FastsszHashRootWith, or its equivalent over another walker interface
	SszCompatFlagDynamicMarshaler                                 // Whether the type implements DynamicMarshaler
	SszCompatFlagDynamicUnmarshaler                               // Whether the type implements DynamicUnmarshaler
	SszCompatFlagDynamicSizer                                     // Whether the type implements DynamicSizer
	SszCompatFlagDynamicHashRoot                                  // Whether the type implements DynamicHashRoot
	SszCompatFlagDynamicEncoder                                   // Whether the type implements DynamicEncoder
	SszCompatFlagDynamicDecoder                                   // Whether the type implements DynamicDecoder
	SszCompatFlagDynamicViewMarshaler                             // Whether the type implements DynamicViewMarshaler
	SszCompatFlagDynamicViewUnmarshaler                           // Whether the type implements DynamicViewUnmarshaler
	SszCompatFlagDynamicViewSizer                                 // Whether the type implements DynamicViewSizer
	SszCompatFlagDynamicViewHashRoot                              // Whether the type implements DynamicViewHashRoot
	SszCompatFlagDynamicViewEncoder                               // Whether the type implements DynamicViewEncoder
	SszCompatFlagDynamicViewDecoder                               // Whether the type implements DynamicViewDecoder
)

type SszMaxSizeHint

type SszMaxSizeHint struct {
	Size    uint64
	NoValue bool
	Custom  bool
	Expr    string
}

SszMaxSizeHint encapsulates max size information for SSZ encoding and decoding, derived from 'ssz-max'/'ssz-bitmax' and 'dynssz-max'/'dynssz-bitmax' tag annotations. It provides detailed insights into the max size attributes of fields or types, particularly noting whether max sizes are fixed or dynamic, and if special specification values are applied, differing from default assumptions.

Fields:

  • size: A uint64 value indicating the statically annotated max size of the type or field, as specified by 'ssz-max'/'ssz-bitmax' tag annotations. For dynamic fields, where the max size may vary depending on the instance of the data, this field is set to 0, and the dynamic flag is used to indicate its dynamic nature.
  • dynamic: A boolean flag indicating whether the field's max size is dynamic, set to true for fields whose max size can change or is not fixed at compile time. This determination is based on the presence of 'dynssz-max'/'dynssz-bitmax' annotations or the inherent variability of the type.
  • custom: A boolean indicating whether a non-default specification value has been applied to the type or field, typically through 'dynssz-max'/'dynssz-bitmax' annotations, suggesting a deviation from standard max size expectations that might influence the encoding or decoding process.
  • expr: The dynamic expression used to calculate the max size of the field, typically through 'dynssz-max'/'dynssz-bitmax' annotations.

type SszSizeHint

type SszSizeHint struct {
	Size    int64
	Dynamic bool
	Custom  bool
	Bits    bool
	Expr    string
}

type SszType

type SszType uint8

SszType identifies the SSZ encoding type for a field or value. It covers basic types (bool, uintN), complex types (container, list, vector, bitlist, bitvector), progressive types, unions, and extended non-standard types (signed integers, floats, bigint).

const (
	SszUnspecifiedType SszType = iota
	SszCustomType
	SszTypeWrapperType

	// basic types
	SszBoolType
	SszUint8Type
	SszUint16Type
	SszUint32Type
	SszUint64Type
	SszUint128Type
	SszUint256Type

	// complex types
	SszContainerType
	SszListType
	SszVectorType
	SszBitlistType
	SszBitvectorType
	SszUnionType
	SszProgressiveListType
	SszProgressiveBitlistType
	SszProgressiveContainerType
	SszCompatibleUnionType

	// extended types (not supported by SSZ spec)
	SszInt8Type
	SszInt16Type
	SszInt32Type
	SszInt64Type
	SszBigIntType
	SszFloat32Type
	SszFloat64Type
	SszOptionalType

	// canonical SSZ encodings expressed via Go conveniences
	SszOptionalListType // pointer encoded as canonical List[T, 1]
)

func ParseSszType

func ParseSszType(typeStr string) (SszType, error)

ParseSszType converts an ssz-type tag string value (e.g., "container", "list", "uint64") into the corresponding SszType constant. Returns an error for unrecognized type strings.

func WellKnownExternalType added in v1.4.0

func WellKnownExternalType(pkgPath, name string) SszType

WellKnownExternalType returns the SSZ type a well-known external type resolves to without a tag, or SszUnspecifiedType. The name is the type's declared name, with or without the type arguments reflect appends to an instantiated generic.

func (SszType) IsBasic added in v1.4.0

func (t SszType) IsBasic() bool

IsBasic reports whether t is a basic type: a boolean or a fixed-width number. Basic values are packed into 32-byte chunks when a list or vector of them is merkleized.

type SszTypeFlag

type SszTypeFlag uint16

SszTypeFlag is a flag indicating whether a type has a specific SSZ type feature

const (
	SszTypeFlagIsDynamic       SszTypeFlag = 1 << iota // Whether the type is a dynamic type (or has nested dynamic types)
	SszTypeFlagHasLimit                                // Whether the type has a max size tag
	SszTypeFlagHasDynamicSize                          // Whether this type or any of its nested types uses dynamic spec size value that differs from the default
	SszTypeFlagHasDynamicMax                           // Whether this type or any of its nested types uses dynamic spec max value that differs from the default
	SszTypeFlagHasSizeExpr                             // Whether this type or any of its nested types uses a dynamic expression to calculate the size
	SszTypeFlagHasMaxExpr                              // Whether this type or any of its nested types uses a dynamic expression to calculate the max size
	SszTypeFlagHasBitSize                              // Whether the type has a bit size tag
	SszTypeFlagHasNoneVariant                          // Whether a classic union declares the None option at selector 0
	SszTypeFlagNoSszRoot                               // Whether the type is a list or bitlist without a limit, which has no SSZ hash tree root
	SszTypeFlagRecursionMember                         // Whether the type lies on a recursive cycle and counts as a level against the nesting bound
	SszTypeFlagSizerWidth                              // Whether this type or any of its nested types has a width read from a custom type's sizer rather than declared; such a width never packs
)

type SszTypeHint

type SszTypeHint struct {
	Type SszType
}

SszTypeHint holds a parsed SSZ type hint from an ssz-type struct tag. Multiple hints may be present for nested types (e.g., a list of vectors).

type TypeCache

type TypeCache struct {
	CompatFlags   map[string]SszCompatFlag
	ExtendedTypes bool
	NoDelegation  bool
	// contains filtered or unexported fields
}

TypeCache manages cached type descriptors

func NewTypeCache

func NewTypeCache(specs sszutils.DynamicSpecs) *TypeCache

NewTypeCache creates a new type cache

func (*TypeCache) DisableSpecResolution added in v1.4.0

func (tc *TypeCache) DisableSpecResolution()

DisableSpecResolution switches the cache to building descriptors for code generation rather than for this process. Spec expressions are recorded as written instead of being resolved, because the generated code resolves them against whatever specs it runs under; resolving them here would bake the generator's own values into the output and would classify a dimension by whether a value happened to be loaded. The values are dropped rather than kept aside, so nothing can reach them by accident.

func (*TypeCache) GetAllTypes

func (tc *TypeCache) GetAllTypes() [][2]reflect.Type

GetAllTypes returns a slice of all type keys currently cached in the TypeCache.

This method is useful for cache inspection, debugging, and understanding which types have been processed and cached during the application's lifetime. The returned slice contains pairs of (runtime, schema) types in no particular order.

When runtime == schema (normal usage), these represent standard type descriptors. When they differ, the pair represents a view descriptor for fork-dependent SSZ.

The method acquires a read lock to ensure thread-safe access to the cache.

Returns:

  • [][2]reflect.Type: A slice of [runtime, schema] type pairs

Example:

cachedTypes := cache.GetAllTypes()
fmt.Printf("TypeCache contains %d type pairs\n", len(cachedTypes))
for _, pair := range cachedTypes {
    if pair[0] == pair[1] {
        fmt.Printf("  - %s\n", pair[0].String())
    } else {
        fmt.Printf("  - %s (view: %s)\n", pair[0].String(), pair[1].String())
    }
}

func (*TypeCache) GetTypeDescriptor

func (tc *TypeCache) GetTypeDescriptor(t reflect.Type, sizeHints []SszSizeHint, maxSizeHints []SszMaxSizeHint, typeHints []SszTypeHint) (*TypeDescriptor, error)

GetTypeDescriptor returns a cached type descriptor for the given type, computing it if necessary.

This method is the primary interface for obtaining type descriptors, which contain optimized metadata about how to serialize, deserialize, and hash types according to SSZ specifications. Type descriptors are cached for performance, avoiding repeated reflection and analysis of the same types.

The method is thread-safe and ensures sequential processing to prevent duplicate computation of type descriptors when called concurrently for the same type.

Parameters:

  • t: The reflect.Type for which to obtain a descriptor
  • sizeHints: Optional size hints from parent structures' tags. Pass nil for top-level types.
  • maxSizeHints: Optional max size hints from parent structures' tags. Pass nil for top-level types.
  • typeHints: Optional type hints from parent structures' tags. Pass nil for top-level types.

Returns:

  • *TypeDescriptor: The type descriptor containing metadata for SSZ operations
  • error: An error if the type cannot be analyzed or contains unsupported features

Type descriptors are only cached when no size hints are provided (i.e., for root types). When size hints are present, the descriptor is computed dynamically to accommodate the specific constraints.

Example:

typeDesc, err := cache.GetTypeDescriptor(reflect.TypeOf(myStruct), nil, nil, nil)
if err != nil {
    log.Fatal("Failed to get type descriptor:", err)
}
fmt.Printf("Type size: %d bytes (dynamic: %v)\n", typeDesc.Size,
    typeDesc.SszTypeFlags&ssztypes.SszTypeFlagIsDynamic != 0)

func (*TypeCache) GetTypeDescriptorWithSchema added in v1.3.0

func (tc *TypeCache) GetTypeDescriptorWithSchema(runtimeType, schemaType reflect.Type, sizeHints []SszSizeHint, maxSizeHints []SszMaxSizeHint, typeHints []SszTypeHint) (*TypeDescriptor, error)

GetTypeDescriptorWithSchema returns a cached type descriptor for a (runtime, schema) type pair.

This method supports fork-dependent SSZ schemas (view descriptors) where the schema type defines the SSZ layout while the runtime type holds the actual data. This allows different SSZ serializations of the same runtime data based on the schema provided.

When runtimeType == schemaType, this behaves identically to GetTypeDescriptor. When they differ, the descriptor is built using schema's field definitions (names, tags, order) but accessing data from the runtime type's fields.

Parameters:

  • runtimeType: The reflect.Type where actual data lives
  • schemaType: The reflect.Type that defines SSZ layout (field order, tags, limits)
  • sizeHints: Optional size hints from parent structures' tags
  • maxSizeHints: Optional max size hints from parent structures' tags
  • typeHints: Optional type hints from parent structures' tags

Returns:

  • *TypeDescriptor: The type descriptor for the (runtime, schema) pair
  • error: An error if types are incompatible or analysis fails

Example with view descriptor:

// Runtime type (superset)
type BeaconBlockBody struct { ... }
// Schema/view type for Altair fork
type BodyAltairView struct { ... }

desc, err := cache.GetTypeDescriptorWithSchema(
    reflect.TypeOf(BeaconBlockBody{}),
    reflect.TypeOf(BodyAltairView{}),
    nil, nil, nil,
)

func (*TypeCache) PromotedDelegationMethods added in v1.4.0

func (tc *TypeCache) PromotedDelegationMethods(targetType reflect.Type) map[string]bool

PromotedDelegationMethods returns the SSZ delegation methods a struct type only satisfies through a wrapper promoted from an embedded (anonymous) field, rather than a declaration of its own. Go promotes an embedded field's methods to the outer struct, so such a struct "implements" the interface even though the promoted method only knows how to serialize the embedded field — delegating through it silently drops the struct's sibling fields. Callers suppress delegation per interface: only the interfaces whose methods are promoted force the container walk, while an interface the struct declares itself (including a declaration shadowing an embedded method) keeps delegating.

A declared method is a real source-level function, whereas a promoted method is a compiler-generated wrapper the runtime attributes to a synthetic "<autogenerated>" file. That file distinction (see methodIsPromotedWrapper) tells the two apart.

The result is a pure function of the type and this sits on the per-call delegation hot path, so it is memoized per TypeCache. A nil map means no delegation method is promoted.

func (*TypeCache) RemoveAllTypes

func (tc *TypeCache) RemoveAllTypes()

RemoveAllTypes clears all cached type descriptors from the cache, both the plain entries and the hint-carrying variants.

This method is useful for:

  • Memory management in long-running applications
  • Testing scenarios requiring a clean cache state

The method acquires a write lock to ensure thread-safe clearing. After calling this method, all subsequent type descriptor requests will trigger recomputation. Spec values are resolved and memoized by the owning DynSsz instance, so a changed spec set needs a new DynSsz rather than a cleared cache.

Example:

cache.RemoveAllTypes()

// All types will be recomputed
desc, err := cache.GetTypeDescriptor(reflect.TypeOf(MyStruct{}), nil, nil, nil)

func (*TypeCache) RemoveType

func (tc *TypeCache) RemoveType(t reflect.Type)

RemoveType removes a specific type (with runtime == schema) from the cache, in both its value and pointer form.

This method is useful for cache management scenarios where you need to force recomputation of a type descriptor, such as after configuration changes or when testing different type configurations.

The method acquires a write lock to ensure thread-safe removal. Note: This only removes entries where runtime type equals schema type. Use RemoveTypeKey to remove view descriptor entries.

Parameters:

  • t: The reflect.Type to remove from the cache

Example:

// Remove a type to force recomputation
cache.RemoveType(reflect.TypeOf(MyStruct{}))

// Next call to GetTypeDescriptor will rebuild the descriptor
desc, err := cache.GetTypeDescriptor(reflect.TypeOf(MyStruct{}), nil, nil)

func (*TypeCache) RemoveTypeKey added in v1.3.0

func (tc *TypeCache) RemoveTypeKey(runtimeType, schemaType reflect.Type)

RemoveTypeKey removes a specific (runtime, schema) type pair from the cache.

This method supports removing view descriptor entries where runtime differs from schema.

Parameters:

  • runtimeType: The runtime type of the cache entry to remove
  • schemaType: The schema type of the cache entry to remove

type TypeDescriptor

type TypeDescriptor struct {
	// Read for every walked value.
	SszCompatFlags SszCompatFlag        `json:"compat"`              // SSZ compatibility flags, one per delegate method
	SszTypeFlags   SszTypeFlag          `json:"flags"`               // SSZ type flags
	SszType        SszType              `json:"type"`                // SSZ type of the type
	GoTypeFlags    GoTypeFlag           `json:"go_flags"`            // Additional go type flags
	ElemDesc       *TypeDescriptor      `json:"field,omitempty"`     // For slices/arrays
	ContainerDesc  *ContainerDescriptor `json:"container,omitempty"` // For structs
	Size           int64                `json:"size"`                // Serialized size of the fixed part; a dynamic type carries SszTypeFlagIsDynamic and sizes its tail at runtime
	Len            int64                `json:"len"`                 // Length of array/slice / static size of container
	Limit          uint64               `json:"limit"`               // Limit of array/slice (ssz-max tag)
	Type           reflect.Type         `json:"-"`                   // Reflect type (runtime type where data lives)

	// Read only for the SSZ kinds that carry them.
	Kind                   reflect.Kind              `json:"kind"`                    // Reflect kind of the type
	WrapperFieldIndex      uint32                    `json:"wrapper_field,omitempty"` // Index of the wrapped value field in a wrapper struct (excluded fields may precede it)
	MinSize                int64                     `json:"min_size,omitempty"`      // Smallest serialization of this type; 0 when it has no floor (see SetMinSize)
	BitSize                int64                     `json:"bit_size,omitempty"`      // Bit size for bit vector types (ssz-bitsize tag)
	UnionVariants          map[uint8]*TypeDescriptor `json:"union,omitempty"`         // Union variant types by index (for CompatibleUnion)
	CodegenInfo            *any                      `json:"-"`                       // Codegen information or view pointer
	HashTreeRootWithMethod *reflect.Method           `json:"-"`                       // Cached HashTreeRootWith method for performance
	SizeExpression         *string                   `json:"size_expr,omitempty"`     // The dynamic expression used to calculate the size of the type
	MaxExpression          *string                   `json:"max_expr,omitempty"`      // The dynamic expression used to calculate the max size of the type
	SchemaType             reflect.Type              `json:"-"`                       // Schema type that defines SSZ layout (may differ from Type for view descriptors)
}

TypeDescriptor represents a cached, optimized descriptor for a type's SSZ encoding/decoding

Field order is load-bearing: fields are grouped by how often the reflection engine reads them. The first four are read for every value, the next six for every composite; on amd64 the ten fill the first cache line. The rest are read only for the kind that carries them, or not at all.

GetTypeHash marshals this struct as JSON in declaration order, so reordering moves every type hash and every generated "// Hash:" header. TestTypeDescriptorHotFieldsShareACacheLine pins the grouping.

func (*TypeDescriptor) GetTypeHash

func (td *TypeDescriptor) GetTypeHash() [32]byte

GetTypeHash computes a SHA-256 hash of the TypeDescriptor's JSON representation. This hash uniquely identifies the type's SSZ layout and is used by the code generator to detect when a type's structure has changed and regeneration is needed.

Recursive types form a cyclic descriptor graph that standard JSON marshalling cannot represent; those fall back to a deterministic reference-based serialization so distinct recursive layouts hash to distinct values. Acyclic descriptors keep the plain JSON form.

The hash covers that encoding, not the wire format: encoding/json emits fields in declaration order, so reordering TypeDescriptor moves every hash without changing any serialization. It identifies a layout within one version of this package, not across versions.

func (*TypeDescriptor) SetMinSize added in v1.4.0

func (td *TypeDescriptor) SetMinSize()

SetMinSize records the smallest number of bytes a value of this type can serialize to. Decoders use it to reject an offset table that declares more elements than the region can hold, before that count sizes an allocation.

A type with no floor -- a list, an optional list -- keeps 0, which states no bound rather than a wrong one. A bit list holds its termination bit, a union its selector, an optional its presence byte and a big.Int its sign byte, so each holds at least one byte.

It is stored rather than derived at decode time because Len carries different meanings per type (bytes for a container's fixed section, elements for a vector), so the rule would otherwise be re-derived on every decode. Children are read from their own cached value, so a recursive type resolves without walking back into itself: a cycle's back edge simply contributes nothing.

Both front ends record it, so the descriptor hashes agree. The reflection cache resolves every size against the active spec; the code generator's go/types parser records the static tag values and emits the bound as a runtime expression instead (see minSizeExpr), since a preset may resolve them smaller than the static defaults.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL