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.
WalkEvents decodes the full document into an AST on the first call, then returns DFS traversal events on subsequent calls. Use it when you need the document available after iteration:
event, err := dec.WalkEvents()
NextEvent is a true streaming decoder: it reads and yields events one at a time without building an AST, making it suitable for large inputs. WalkEvents and NextEvent are mutually exclusive on a single Decoder instance.
Security limits ¶
DecodeOptions.MaxDepth and MaxNodes bound recursion and node count. MaxKeyBytes, MaxValueBytes, and MaxStringBytes bound string lengths for keys and values respectively. All limits use 0 to mean unlimited.
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.
Builder ¶
NewBuilder provides a fluent, ordered API for constructing Documents without manual AST manipulation:
doc, err := vdf.NewBuilder("root").
Set("key", "value").
Object("child", func(b *vdf.Builder) { b.SetUint32("n", 1) }).
Document()
Reflection API ¶
Marshal and Unmarshal convert between Go structs and VDF Documents using struct field tags of the form vdf:"name,option":
type S struct {
Name string `vdf:"name"`
Port uint32 `vdf:"port"`
}
doc, _ := vdf.Marshal("Server", S{Name: "x", Port: 2302})
var s S
_ = vdf.Unmarshal(doc, "Server", &s)
Supported options: omitempty, inline, repeated, indexed. Fields implementing encoding.TextMarshaler/TextUnmarshaler are handled automatically.
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 ¶
- Variables
- func AppendBinary(dst []byte, doc *Document, opts EncodeOptions) ([]byte, error)
- func AppendText(dst []byte, doc *Document, opts EncodeOptions) ([]byte, error)
- func Unmarshal(doc *Document, root string, out any) error
- func Write(w io.Writer, doc *Document) error
- func WriteBinaryFile(path string, doc *Document) error
- func WriteFile(path string, doc *Document, opts ...EncodeOptions) (err error)
- func WriteString(doc *Document) (string, error)
- func WriteTextFile(path string, doc *Document) error
- type Builder
- type DecodeOptions
- type Decoder
- type Document
- func FromMap(rootKey string, m Map) (*Document, error)
- func FromMapSorted(rootKey string, m Map) (*Document, error)
- func Marshal(root string, v any) (*Document, error)
- func NewDocument() *Document
- func NewDocumentWithFormat(format Format) *Document
- func Parse(r io.Reader) (*Document, error)
- func ParseAuto(data []byte) (*Document, error)
- func ParseAutoFile(path string) (*Document, error)
- func ParseBytes(data []byte, opts DecodeOptions) (*Document, error)
- func ParseFile(path string, opts ...DecodeOptions) (doc *Document, err error)
- func ParseString(s string) (*Document, error)
- func ParseTextFile(path string) (*Document, error)
- type EncodeOptions
- type Encoder
- type Event
- type EventType
- type Format
- type Map
- type Node
- type NodeKind
Examples ¶
Constants ¶
This section is empty.
Variables ¶
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 '}'") // ErrKeyTooLong indicates that a parsed key exceeded the configured byte limit. ErrKeyTooLong = errors.New("key exceeds maximum byte length") // ErrValueTooLong indicates that a parsed string value exceeded the configured byte limit. ErrValueTooLong = errors.New("string value exceeds maximum byte length") // ErrReflectUnsupportedType indicates that a Go type cannot be mapped to a VDF node. ErrReflectUnsupportedType = errors.New("unsupported Go type for VDF reflection") // ErrReflectFieldMismatch indicates a type mismatch or missing key during Unmarshal. ErrReflectFieldMismatch = errors.New("VDF field mismatch during unmarshal") )
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 Unmarshal ¶ added in v0.2.0
Unmarshal decodes the named root object from doc into the struct pointed to by out. out must be a non-nil pointer to a struct.
Example ¶
package main
import (
"fmt"
"github.com/woozymasta/vdf"
)
func main() {
type Server struct {
Name string `vdf:"name"`
Port uint32 `vdf:"port"`
}
doc, _ := vdf.ParseString(`"Server" { "name" "game-1" "port" "2302" }`)
var s Server
if err := vdf.Unmarshal(doc, "Server", &s); err != nil {
fmt.Println(err)
return
}
fmt.Println(s.Name)
fmt.Println(s.Port)
}
Output: game-1 2302
func WriteBinaryFile ¶
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 ¶
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 ¶
WriteTextFile encodes document as text VDF file.
Types ¶
type Builder ¶ added in v0.2.0
type Builder struct {
// contains filtered or unexported fields
}
Builder constructs a VDF Document using a fluent API. Methods return the receiver so calls can be chained. The first error encountered is stored and silently skips subsequent calls; Document returns it at finalization.
func NewBuilder ¶ added in v0.2.0
NewBuilder creates a Builder with a single root object node.
Example ¶
package main
import (
"fmt"
"github.com/woozymasta/vdf"
)
func main() {
doc, err := vdf.NewBuilder("config").
Set("name", "server").
SetUint32("port", 2302).
Object("db", func(b *vdf.Builder) {
b.Set("host", "localhost")
}).
Document()
if err != nil {
fmt.Println(err)
return
}
fmt.Println(doc.Roots[0].Key)
fmt.Println(*doc.Roots[0].First("name").StringValue)
}
Output: config server
func (*Builder) Document ¶ added in v0.2.0
Document finalizes the builder and returns the completed Document. Returns an error if any call previously failed or if unclosed objects remain. After a successful call, the builder is considered finalized; further calls return an error.
func (*Builder) Object ¶ added in v0.2.0
Object opens a nested object scope, invokes fn within it, then closes it.
type DecodeOptions ¶
type DecodeOptions struct {
// MaxDepth limits nested object depth (0 means unlimited).
MaxDepth int
// MaxNodes limits total parsed nodes (0 means unlimited).
MaxNodes int
// MaxKeyBytes limits the byte length of any parsed key (0 means unlimited).
MaxKeyBytes int
// MaxValueBytes limits the byte length of any parsed string value (0 means unlimited).
MaxValueBytes int
// MaxStringBytes is a convenience ceiling applied to both keys and values
// when their specific limit is zero (0 means unlimited).
MaxStringBytes int
// Format selects expected input format.
Format Format
// Strict enables stricter validation paths where available.
Strict bool
}
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 ¶
DecodeDocument decodes the full input stream into a document.
func (*Decoder) NextEvent ¶
NextEvent returns the next event from the input stream without building an AST. On the first call it initialises a streaming reader; subsequent calls continue from that position. Returns io.EOF when all events have been consumed.
NextEvent is a true streaming decoder: it reads and yields events one at a time with no intermediate AST, making it suitable for large inputs or constrained memory environments. The decoded document is not available after iteration; use WalkEvents when post-iteration document access is required.
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
func (*Decoder) WalkEvents ¶ added in v0.2.0
WalkEvents returns the next DFS traversal event for the decoded document. The full document is decoded into an AST on the first call; subsequent calls traverse that AST in depth-first order. Returns io.EOF when all events have been emitted.
Use WalkEvents when you need the full document available for further access after iteration. For a one-pass, lower-memory alternative see NextEvent.
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.WalkEvents()
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 FromMapSorted ¶ added in v0.2.0
FromMapSorted builds a document with one object root from a map with keys sorted. Unlike FromMap, key iteration order is deterministic (lexicographic).
func Marshal ¶ added in v0.2.0
Marshal encodes a Go struct into a Document with one root object node. v must be a struct or a pointer to a struct.
Example ¶
package main
import (
"fmt"
"github.com/woozymasta/vdf"
)
func main() {
type Server struct {
Name string `vdf:"name"`
Port uint32 `vdf:"port"`
}
doc, err := vdf.Marshal("Server", Server{Name: "game-1", Port: 2302})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(doc.Roots[0].Key)
fmt.Println(*doc.Roots[0].First("name").StringValue)
}
Output: Server game-1
func NewDocument ¶
func NewDocument() *Document
NewDocument creates an empty document with auto format marker.
func NewDocumentWithFormat ¶
NewDocumentWithFormat creates an empty document with explicit format marker.
func ParseAuto ¶
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 ¶
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 ¶
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 ¶
ParseTextFile decodes text VDF from file path.
func (*Document) ToMapLossy ¶
ToMapLossy converts document to map using last-write-wins for duplicate keys.
func (*Document) ToMapStrict ¶
ToMapStrict converts document to map and fails on duplicate keys.
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) EncodeDocument ¶
EncodeDocument encodes a complete document in selected output format.
func (*Encoder) StartObject ¶
StartObject begins an object in manual streaming mode.
func (*Encoder) WriteString ¶
WriteString writes a string 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 Map ¶
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 ¶
NewObjectNode creates an object node with the provided key.
func NewStringNode ¶
NewStringNode creates a string node with the provided key and value.
func NewUint32Node ¶
NewUint32Node creates a uint32 node with the provided key and value.