How to Configure vtprotobuf Features (marshal+unmarshal+size+equal+clone) in protoc Commands

Enable vtprotobuf helpers in protobuf-go-lite by passing --go-lite_opt=features=marshal+unmarshal+size+equal+clone to your protoc command, which activates the corresponding code generators for high-performance serialization methods.

The aperturerobotics/protobuf-go-lite repository provides a drop-in replacement for the standard protoc-gen-go and protoc-gen-go-vtprotobuf plugins. Unlike the standard toolchain, this lite generator allows you to selectively enable specific vtprotobuf features through a single protoc option, generating only the helper methods you need without reflection overhead.

Understanding the vtprotobuf Feature Flags

The protobuf-go-lite plugin uses a feature registry system where each vtprotobuf capability registers itself under a specific name. When you configure the features option, the generator looks up these names and executes only the corresponding code generators.

Available Feature Options

The following feature tokens can be combined using + separators in the features option:

  • marshal – Generates MarshalVT(), MarshalToVT(), and MarshalToSizedBufferVT() methods for efficient serialization without reflection.
  • marshal_strict – Generates strict marshaling variants that validate UTF-8 in string fields.
  • unmarshal – Generates UnmarshalVT() methods for fast deserialization with validation.
  • unmarshal_unsafe – Generates unsafe unmarshaling variants that reuse the input buffer's underlying memory.
  • size – Generates SizeVT() methods that calculate the wire format size without allocation.
  • equal – Generates EqualVT() and EqualMessageVT() methods for deep equality comparison.
  • clone – Generates CloneVT() and CloneMessageVT() methods for deep copying.

How Feature Selection Works

In cmd/protoc-gen-go-lite/main.go, the plugin parses the features flag and splits the value on + characters using strings.Split(features, "+"). This slice is passed to generator.NewGenerator(), which calls findFeatures() in generator/features.go to resolve each token against the defaultFeatures registry. The registry is populated during init() functions in each feature package (e.g., features/marshal/marshal.go calls generator.RegisterFeature("marshal", ...)).

Configuring vtprotobuf Features in protoc Commands

To generate Go code with specific vtprotobuf helpers, you must declare the lite plugin, specify the output directory, and pass the feature configuration option.

Basic protoc Command Structure


# Install the plugin binary

go install github.com/aperturerobotics/protobuf-go-lite/cmd/protoc-gen-go-lite@latest

# Generate with all vtprotobuf features enabled

protoc \
  --plugin protoc-gen-go-lite="${GOBIN}/protoc-gen-go-lite" \
  --go-lite_out=. \
  --go-lite_opt=features=marshal+unmarshal+size+equal+clone \
  proto/myfile.proto

Breaking Down the Command Options

Option Purpose
--plugin protoc-gen-go-lite="${GOBIN}/protoc-gen-go-lite" Tells protoc where to find the custom plugin binary.
--go-lite_out=. Sets the destination directory for generated .pb.go files.
--go-lite_opt=features=marshal+unmarshal+size+equal+clone Activates specific vtprotobuf code generators; tokens are split on + and looked up in the feature registry.
proto/myfile.proto The input Protocol Buffer definition file.

You can customize the feature combination by removing or adding tokens. For example, to generate only marshaling and sizing helpers, use --go-lite_opt=features=marshal+size.

Implementation Details in protobuf-go-lite

The feature configuration system is implemented across several key files in the repository.

Flag Parsing in main.go

In cmd/protoc-gen-go-lite/main.go, the plugin defines a features flag with a default value. The main function parses this flag and splits the comma-separated string (though the documentation shows + separation, the implementation uses strings.Split(features, "+") as seen in the source analysis). The resulting slice is passed to the generator constructor.

Feature Registry and Lookup

The generator/features.go file contains the defaultFeatures map and the findFeatures function. When NewGenerator receives the feature name slice, it calls findFeatures to resolve each name against registered features. The registry is populated during package initialization via RegisterFeature calls in each feature implementation package.

Code Generation for Each Feature

Each vtprotobuf capability lives in its own package under features/:

When the generator runs, it iterates over the resolved features and calls each one's GenerateFile method, which walks the protobuf AST and emits the corresponding helper methods into the generated .pb.go files.

Practical Code Example

When you run the protoc command with --go-lite_opt=features=marshal+unmarshal+size+equal+clone, the generated Go code for each message includes optimized methods like these:

// MarshalVT serializes the message to bytes without reflection
func (m *MyMessage) MarshalVT() ([]byte, error) {
    size := m.SizeVT()
    dAtA := make([]byte, size)
    _, err := m.MarshalToSizedBufferVT(dAtA[:size])
    if err != nil {
        return nil, err
    }
    return dAtA, nil
}

// UnmarshalVT deserializes bytes into the message
func (m *MyMessage) UnmarshalVT(dAtA []byte) error {
    // Fast, zero-allocation unmarshaling logic
    return m.UnmarshalVTUnsafe(dAtA)
}

// SizeVT returns the wire format size
func (m *MyMessage) SizeVT() int {
    // Calculated without reflection
    return size
}

// EqualVT performs deep equality comparison
func (m *MyMessage) EqualVT(that *MyMessage) bool {
    // Field-by-field comparison
    return true
}

// CloneVT creates a deep copy
func (m *MyMessage) CloneVT() *MyMessage {
    // Deep copy implementation
    return cloned
}

These methods are generated only when their corresponding feature tokens are included in the --go-lite_opt=features= option.

Summary

  • protobuf-go-lite consolidates standard Go protobuf generation with vtprotobuf optimizations into a single plugin.
  • Feature selection is controlled via the --go-lite_opt=features= option, accepting a +-separated list of feature names.
  • Available features include marshal, marshal_strict, unmarshal, unmarshal_unsafe, size, equal, and clone.
  • Implementation involves flag parsing in cmd/protoc-gen-go-lite/main.go, feature registry lookup in generator/features.go, and code generation in individual features/ packages.
  • Generated code provides zero-allocation, reflection-free methods like MarshalVT(), UnmarshalVT(), SizeVT(), EqualVT(), and CloneVT().

Frequently Asked Questions

What is the difference between protobuf-go-lite and standard protoc-gen-go?

protobuf-go-lite replaces both the standard protoc-gen-go and protoc-gen-go-vtprotobuf plugins with a single binary. While standard protoc-gen-go generates basic Go structs and reflection-based marshaling, protobuf-go-lite generates optimized, zero-allocation helper methods (like MarshalVT and UnmarshalVT) when you enable the corresponding features via the --go-lite_opt=features= option.

Can I enable only specific vtprotobuf features like just marshal and size?

Yes, you can selectively enable any combination of features by specifying only the tokens you need in the features option. For example, to generate only marshaling and sizing helpers, use --go-lite_opt=features=marshal+size. The plugin parses this string in cmd/protoc-gen-go-lite/main.go and only invokes the code generators for the specified features.

How does protobuf-go-lite handle feature name validation?

Feature name validation occurs in generator/features.go via the findFeatures function. When the generator receives the slice of feature names from the command line, it looks up each name against the defaultFeatures registry. If a feature name is not registered (for example, if you typo "marschal" instead of "marshal"), the lookup will fail and the generator will not produce code for that invalid token, ensuring only valid, implemented features are used.

Are the generated vtprotobuf methods compatible with standard proto.Message interfaces?

The generated MarshalVT, UnmarshalVT, SizeVT, EqualVT, and CloneVT methods are designed to work alongside the standard proto.Message interface implementations, not replace them. They provide optimized alternatives to the reflection-based proto.Marshal and proto.Unmarshal functions from the standard library. Your generated structs still implement proto.Message, but you can call the VT methods directly when you need zero-allocation performance or strict unmarshaling behavior.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →