vdf

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Feb 18, 2026 License: MIT Imports: 13 Imported by: 0

README

vdf

This project provides a high performance implementation of Valve Data Format (VDF) text and binary formats.
It is built around io.Reader and io.Writer for streaming workloads, keeps an explicit AST (Document and Node) as the canonical model, preserves node order and duplicate keys, and includes low allocation byte slice encode paths.

Reading VDF

Use ParseString, ParseBytes, or NewDecoder depending on your input source.

doc, err := vdf.ParseString(`"root" { "name" "srv" }`)
if err != nil {
    return err
}

root := doc.Roots[0]
name := root.First("name")

Use auto format detection when input may be text or binary:

doc, err := vdf.ParseAuto(data)
if err != nil {
    return err
}

For file inputs, use ParseFile with optional options or convenience wrappers: ParseTextFile and ParseAutoFile.

Writing VDF

For full document encode, use WriteString, AppendText, AppendBinary, or NewEncoder.

out, err := vdf.WriteString(doc)
if err != nil {
    return err
}

For binary output with low allocations:

bin, err := vdf.AppendBinary(nil, doc, vdf.EncodeOptions{
    Format: vdf.FormatBinary,
})
if err != nil {
    return err
}

If strict AST checks are required before encoding:

enc := vdf.NewEncoder(w, vdf.EncodeOptions{
    Format:   vdf.FormatText,
    Validate: true,
})
err := enc.EncodeDocument(doc)

For file output, use WriteFile with optional options or convenience wrappers: WriteTextFile and WriteBinaryFile.

Building a VDF document

doc := vdf.NewDocumentWithFormat(vdf.FormatText)
root := vdf.NewObjectNode("settings")
root.Add(vdf.NewStringNode("name", "demo"))
root.Add(vdf.NewUint32Node("port", 2302))
doc.AddRoot(root)

NodeObject keeps ordered children and allows duplicate keys. This matches real VDF behavior.

Streaming traversal

Decoder.NextEvent returns a sequence of structural and leaf events that can be consumed incrementally.

dec := vdf.NewDecoder(r, vdf.DecodeOptions{Format: vdf.FormatAuto})
for {
    ev, err := dec.NextEvent()
    if err != nil {
        break
    }

    _ = ev
}

Documentation

Overview

Package vdf implements a parser and encoder for Valve Data Format (VDF) in text and binary variants.

Data model

The canonical model is an AST:

  • Document is a full file with ordered root nodes.
  • NodeObject keeps ordered children and allows duplicate keys.
  • NodeString and NodeUint32 are scalar leaves.

This preserves VDF semantics that are commonly lost in map-based APIs (ordering and duplicate keys).

Decode API

Use Decoder for stream-oriented decoding from io.Reader:

dec := vdf.NewDecoder(r, vdf.DecodeOptions{Format: vdf.FormatAuto})
doc, err := dec.DecodeDocument()

For byte slices and strings use ParseBytes and ParseString. For file paths use ParseFile with optional DecodeOptions, or ParseTextFile/ParseAutoFile.

NextEvent provides traversal events over the decoded document:

event, err := dec.NextEvent()

Encode API

Use Encoder for stream-oriented output to io.Writer:

enc := vdf.NewEncoder(w, vdf.EncodeOptions{Format: vdf.FormatText})
err := enc.EncodeDocument(doc)

Manual streaming methods are available for incremental writing: StartObject, WriteString, WriteUint32, EndObject, Close. For file output use WriteFile with optional EncodeOptions, or WriteTextFile/WriteBinaryFile.

Fast paths

AppendText and AppendBinary append encoded output directly into destination byte slices to reduce allocations on hot paths.

Validation

Document.Validate can be called explicitly when strict AST checks are required. For performance, encoding does not force full validation unless EncodeOptions.Validate is set to true.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidFormat indicates unsupported format selection.
	ErrInvalidFormat = errors.New("invalid VDF format")
	// ErrUnrecognizedType indicates that an unknown binary VDF type byte was encountered.
	ErrUnrecognizedType = errors.New("unrecognized VDF type")
	// ErrBufferOverflow indicates that parsing attempted to read past available input bytes.
	ErrBufferOverflow = errors.New("buffer overflow")
	// ErrNullInString indicates that a binary VDF string contains an embedded null byte.
	ErrNullInString = errors.New("null byte found in string")
	// ErrUnsupportedMapValueType indicates that map conversion encountered an unsupported value type.
	ErrUnsupportedMapValueType = errors.New("unsupported map value type")
	// ErrIntOutOfRange indicates an integer cannot be represented as uint32.
	ErrIntOutOfRange = errors.New("integer out of uint32 range")
	// ErrDuplicateKeyInStrictMode indicates strict map conversion encountered duplicate keys.
	ErrDuplicateKeyInStrictMode = errors.New("duplicate key in strict map conversion")
	// ErrInvalidNodeState indicates AST node fields do not match node kind invariants.
	ErrInvalidNodeState = errors.New("invalid node state")
	// ErrDepthLimitExceeded indicates decode exceeded configured max depth.
	ErrDepthLimitExceeded = errors.New("maximum depth exceeded")
	// ErrNodeLimitExceeded indicates decode exceeded configured max node count.
	ErrNodeLimitExceeded = errors.New("maximum node count exceeded")
	// ErrUnexpectedEOFInQuotedString indicates that a quoted text token ended before its closing quote.
	ErrUnexpectedEOFInQuotedString = errors.New("unexpected EOF in quoted string")
	// ErrUnexpectedEOFInEscapeSequence indicates that an escape sequence ended before its escaped rune.
	ErrUnexpectedEOFInEscapeSequence = errors.New("unexpected EOF in escape sequence")
	// ErrUnexpectedCharacter indicates that the lexer found an invalid token start.
	ErrUnexpectedCharacter = errors.New("unexpected character")
	// ErrExpectedStringKey indicates that the parser expected a string token for a node key.
	ErrExpectedStringKey = errors.New("expected string key")
	// ErrExpectedValueOrObject indicates that the parser expected either a string value or an object start.
	ErrExpectedValueOrObject = errors.New("expected value or '{'")
	// ErrExpectedObjectStart indicates that the parser expected an opening object brace.
	ErrExpectedObjectStart = errors.New("expected '{'")
	// ErrUnexpectedEOFInObject indicates that the parser reached EOF before closing an object.
	ErrUnexpectedEOFInObject = errors.New("unexpected EOF, expected '}'")
)

Functions

func AppendBinary

func AppendBinary(dst []byte, doc *Document, opts EncodeOptions) ([]byte, error)

AppendBinary appends binary VDF output to destination byte slice.

func AppendText

func AppendText(dst []byte, doc *Document, opts EncodeOptions) ([]byte, error)

AppendText appends text VDF output to destination byte slice.

func Write

func Write(w io.Writer, doc *Document) error

Write encodes document as text VDF with default options.

func WriteBinaryFile

func WriteBinaryFile(path string, doc *Document) error

WriteBinaryFile encodes document as binary VDF file.

func WriteFile

func WriteFile(path string, doc *Document, opts ...EncodeOptions) (err error)

WriteFile encodes document to file. Without options it writes text format.

func WriteString

func WriteString(doc *Document) (string, error)

WriteString encodes document as text VDF string.

Example
package main

import (
	"fmt"
	"strings"

	"github.com/woozymasta/vdf"
)

func main() {
	doc := vdf.NewDocumentWithFormat(vdf.FormatText)
	root := vdf.NewObjectNode("app")
	root.Add(vdf.NewStringNode("name", "demo"))
	root.Add(vdf.NewUint32Node("id", 7))
	doc.AddRoot(root)

	text, err := vdf.WriteString(doc)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(strings.Contains(text, `"app"`))
	fmt.Println(strings.Contains(text, `"id"`))

}
Output:
true
true

func WriteTextFile

func WriteTextFile(path string, doc *Document) error

WriteTextFile encodes document as text VDF file.

Types

type DecodeOptions

type DecodeOptions struct {
	// Format selects expected input format.
	Format Format
	// Strict enables stricter validation paths where available.
	Strict bool
	// MaxDepth limits nested object depth (0 means unlimited).
	MaxDepth int
	// MaxNodes limits total parsed nodes (0 means unlimited).
	MaxNodes int
}

DecodeOptions controls decoder behavior.

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

Decoder decodes VDF data from an input stream.

func NewDecoder

func NewDecoder(r io.Reader, opts DecodeOptions) *Decoder

NewDecoder creates a decoder with normalized options.

func (*Decoder) DecodeDocument

func (d *Decoder) DecodeDocument() (*Document, error)

DecodeDocument decodes the full input stream into a document.

func (*Decoder) NextEvent

func (d *Decoder) NextEvent() (Event, error)

NextEvent returns the next DFS event for the decoded document.

Example
package main

import (
	"fmt"
	"strings"

	"github.com/woozymasta/vdf"
)

func main() {
	dec := vdf.NewDecoder(strings.NewReader(`"root" { "k" "v" }`), vdf.DecodeOptions{
		Format: vdf.FormatText,
	})

	count := 0
	for {
		_, err := dec.NextEvent()
		if err != nil {
			break
		}

		count++
	}

	fmt.Println(count)

}
Output:
5

type Document

type Document struct {
	// Roots contains top-level nodes in source order.
	Roots []*Node `json:"roots,omitempty" yaml:"roots,omitempty"`
	// Format is the source or intended encode format.
	Format Format `json:"format,omitempty" yaml:"format,omitempty"`
}

Document represents a complete VDF document.

func FromMap

func FromMap(rootKey string, m Map) (*Document, error)

FromMap builds a document with one object root from a map.

func NewDocument

func NewDocument() *Document

NewDocument creates an empty document with auto format marker.

func NewDocumentWithFormat

func NewDocumentWithFormat(format Format) *Document

NewDocumentWithFormat creates an empty document with explicit format marker.

func Parse

func Parse(r io.Reader) (*Document, error)

Parse decodes text VDF from reader.

func ParseAuto

func ParseAuto(data []byte) (*Document, error)

ParseAuto decodes VDF bytes with automatic format detection.

Example
package main

import (
	"fmt"

	"github.com/woozymasta/vdf"
)

func main() {
	doc, err := vdf.ParseAuto([]byte(`"cfg" { "timeout" "5" }`))
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(doc.Format == vdf.FormatText)

}
Output:
true

func ParseAutoFile

func ParseAutoFile(path string) (*Document, error)

ParseAutoFile decodes VDF file with automatic format detection.

func ParseBytes

func ParseBytes(data []byte, opts DecodeOptions) (*Document, error)

ParseBytes decodes VDF from bytes using the given options.

func ParseFile

func ParseFile(path string, opts ...DecodeOptions) (doc *Document, err error)

ParseFile decodes VDF from file path. Without options it decodes as text format.

func ParseString

func ParseString(s string) (*Document, error)

ParseString decodes text VDF from a string.

Example
package main

import (
	"fmt"

	"github.com/woozymasta/vdf"
)

func main() {
	doc, err := vdf.ParseString(`"root" { "name" "server-1" }`)
	if err != nil {
		fmt.Println(err)
		return
	}

	root := doc.Roots[0]
	fmt.Println(root.Key)
	fmt.Println(*root.First("name").StringValue)

}
Output:
root
server-1

func ParseTextFile

func ParseTextFile(path string) (*Document, error)

ParseTextFile decodes text VDF from file path.

func (*Document) AddRoot

func (d *Document) AddRoot(node *Node)

AddRoot appends a root node to the document.

func (*Document) ToMapLossy

func (d *Document) ToMapLossy() Map

ToMapLossy converts document to map using last-write-wins for duplicate keys.

func (*Document) ToMapStrict

func (d *Document) ToMapStrict() (Map, error)

ToMapStrict converts document to map and fails on duplicate keys.

func (*Document) Validate

func (d *Document) Validate() error

Validate ensures document and node invariants are satisfied.

type EncodeOptions

type EncodeOptions struct {
	// Indent sets one indentation level for text format.
	Indent string
	// Format selects output format.
	Format Format
	// Compact enables compact text encoding.
	Compact bool
	// Deterministic enables stable key ordering during encode.
	Deterministic bool
	// Validate enables full document validation before encoding.
	Validate bool
}

EncodeOptions controls encoder behavior.

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

Encoder encodes VDF documents to an output stream.

func NewEncoder

func NewEncoder(w io.Writer, opts EncodeOptions) *Encoder

NewEncoder creates a VDF encoder.

func (*Encoder) Close

func (e *Encoder) Close() error

Close finalizes manual streaming state.

func (*Encoder) EncodeDocument

func (e *Encoder) EncodeDocument(doc *Document) error

EncodeDocument encodes a complete document in selected output format.

func (*Encoder) EndObject

func (e *Encoder) EndObject() error

EndObject ends an object in manual streaming mode.

func (*Encoder) StartObject

func (e *Encoder) StartObject(key string) error

StartObject begins an object in manual streaming mode.

func (*Encoder) WriteString

func (e *Encoder) WriteString(key, value string) error

WriteString writes a string leaf in manual streaming mode.

func (*Encoder) WriteUint32

func (e *Encoder) WriteUint32(key string, value uint32) error

WriteUint32 writes an unsigned numeric leaf in manual streaming mode.

type Event

type Event struct {
	// StringValue is set for EventString.
	StringValue *string `json:"string_value,omitempty" yaml:"string_value,omitempty"`
	// Uint32Value is set for EventUint32.
	Uint32Value *uint32 `json:"uint32_value,omitempty" yaml:"uint32_value,omitempty"`
	// Key is the node key associated with this event.
	Key string `json:"key,omitempty" yaml:"key,omitempty"`
	// Depth is the traversal depth for this event.
	Depth int `json:"depth" yaml:"depth"`
	// Type is the event kind.
	Type EventType `json:"type" yaml:"type"`
}

Event is a streaming traversal event.

type EventType

type EventType uint8

EventType represents a decoded event type from streaming traversal.

const (
	// EventDocumentStart marks beginning of a document stream.
	EventDocumentStart EventType = iota + 1
	// EventDocumentEnd marks end of a document stream.
	EventDocumentEnd
	// EventObjectStart marks beginning of an object node.
	EventObjectStart
	// EventObjectEnd marks end of an object node.
	EventObjectEnd
	// EventString marks a string leaf node.
	EventString
	// EventUint32 marks a uint32 leaf node.
	EventUint32
)

type Format

type Format uint8

Format defines how encoded/decoded VDF data should be interpreted.

const (
	// FormatAuto enables format auto-detection for decoding.
	FormatAuto Format = iota
	// FormatText selects text VDF format.
	FormatText
	// FormatBinary selects binary VDF format.
	FormatBinary
)

type Map

type Map map[string]any

Map represents a generic key-value mapping used by explicit adapters. It is inherently lossy for duplicate keys and ordering.

type Node

type Node struct {
	// StringValue is set for NodeString.
	StringValue *string `json:"string_value,omitempty" yaml:"string_value,omitempty"`
	// Uint32Value is set for NodeUint32.
	Uint32Value *uint32 `json:"uint32_value,omitempty" yaml:"uint32_value,omitempty"`
	// Key is the node key.
	Key string `json:"key" yaml:"key"`
	// Children are set for NodeObject and preserve source order.
	Children []*Node `json:"children,omitempty" yaml:"children,omitempty"`
	// Kind defines the node payload shape.
	Kind NodeKind `json:"kind" yaml:"kind"`
}

Node represents a VDF AST node.

func NewObjectNode

func NewObjectNode(key string) *Node

NewObjectNode creates an object node with the provided key.

func NewStringNode

func NewStringNode(key, value string) *Node

NewStringNode creates a string node with the provided key and value.

func NewUint32Node

func NewUint32Node(key string, value uint32) *Node

NewUint32Node creates a uint32 node with the provided key and value.

func (*Node) Add

func (n *Node) Add(child *Node)

Add appends a child node to an object node.

func (*Node) All

func (n *Node) All(key string) []*Node

All returns all children with the given key in source order.

func (*Node) First

func (n *Node) First(key string) *Node

First returns the first child with the given key.

type NodeKind

type NodeKind uint8

NodeKind defines the value type represented by a node.

const (
	// NodeObject is a container node with ordered children.
	NodeObject NodeKind = iota + 1
	// NodeString is a leaf node containing a string value.
	NodeString
	// NodeUint32 is a leaf node containing an unsigned 32-bit value.
	NodeUint32
)

Jump to

Keyboard shortcuts

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