diff --git a/README.md b/README.md index f5f173e..61cdd18 100644 --- a/README.md +++ b/README.md @@ -235,17 +235,17 @@ the AST level. See https://pkg.go.dev/github.com/pelletier/go-toml/v2/unstable. Execution time speedup compared to other Go TOML libraries: - - - - - - - - - - - + + + + + + + + + + +
Benchmarkgo-toml v1BurntSushi/toml
Marshal/HugoFrontMatter-22.1x2.0x
Marshal/ReferenceFile/map-21.9x2.0x
Marshal/ReferenceFile/struct-22.3x2.5x
Unmarshal/HugoFrontMatter-23.4x2.8x
Unmarshal/ReferenceFile/map-23.0x3.0x
Unmarshal/ReferenceFile/struct-24.9x5.1x
Benchmarkgo-toml v1BurntSushi/toml
Marshal/HugoFrontMatter-22.1x2.0x
Marshal/ReferenceFile/map-22.0x2.0x
Marshal/ReferenceFile/struct-22.3x2.5x
Unmarshal/HugoFrontMatter-23.3x2.8x
Unmarshal/ReferenceFile/map-22.9x3.0x
Unmarshal/ReferenceFile/struct-24.8x5.0x
See more

The table above has the results of the most common use-cases. The table below @@ -253,22 +253,22 @@ contains the results of all benchmarks, including unrealistic ones. It is provided for completeness.

- - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + +
Benchmarkgo-toml v1BurntSushi/toml
Marshal/SimpleDocument/map-22.0x2.9x
Marshal/SimpleDocument/struct-22.5x3.5x
Unmarshal/SimpleDocument/map-24.3x3.5x
Unmarshal/SimpleDocument/struct-25.9x4.5x
UnmarshalDataset/example-23.2x2.9x
UnmarshalDataset/code-22.4x2.9x
UnmarshalDataset/twitter-22.7x2.5x
UnmarshalDataset/citm_catalog-22.1x2.1x
UnmarshalDataset/canada-21.9x1.5x
UnmarshalDataset/config-25.4x3.1x
geomean2.9x2.8x
Benchmarkgo-toml v1BurntSushi/toml
Marshal/SimpleDocument/map-22.0x2.9x
Marshal/SimpleDocument/struct-22.5x3.6x
Unmarshal/SimpleDocument/map-24.2x3.4x
Unmarshal/SimpleDocument/struct-25.9x4.4x
UnmarshalDataset/example-23.2x2.9x
UnmarshalDataset/code-22.4x2.8x
UnmarshalDataset/twitter-22.7x2.5x
UnmarshalDataset/citm_catalog-22.3x2.3x
UnmarshalDataset/canada-21.9x1.5x
UnmarshalDataset/config-25.4x3.0x
geomean2.9x2.8x

This table can be generated with ./ci.sh benchmark -a -html.

diff --git a/marshaler.go b/marshaler.go index 9ff3a75..ca462d4 100644 --- a/marshaler.go +++ b/marshaler.go @@ -704,15 +704,18 @@ func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte for iter.Next() { v := iter.Value() - if isNil(v) { - // For nil pointers, convert to zero value of the element type. - // This allows round-trip marshaling of maps with nil pointer values. - // For nil interfaces and nil maps, skip since we can't derive a type. - if v.Kind() == reflect.Ptr { + // Handle nil values: convert nil pointers to zero value, + // skip nil interfaces and nil maps. + switch v.Kind() { + case reflect.Ptr: + if v.IsNil() { v = reflect.Zero(v.Type().Elem()) - } else { + } + case reflect.Interface, reflect.Map: + if v.IsNil() { continue } + default: } k, err := enc.keyToString(iter.Key()) @@ -936,7 +939,7 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro if shouldOmitEmpty(kv.Options, kv.Value) { continue } - if shouldOmitZero(kv.Options, kv.Value) { + if kv.Options.omitzero && shouldOmitZero(kv.Options, kv.Value) { continue } hasNonEmptyKV = true @@ -958,7 +961,7 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro if shouldOmitEmpty(table.Options, table.Value) { continue } - if shouldOmitZero(table.Options, table.Value) { + if table.Options.omitzero && shouldOmitZero(table.Options, table.Value) { continue } if first { @@ -995,7 +998,7 @@ func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte if shouldOmitEmpty(kv.Options, kv.Value) { continue } - if shouldOmitZero(kv.Options, kv.Value) { + if kv.Options.omitzero && shouldOmitZero(kv.Options, kv.Value) { continue } diff --git a/unstable/ast.go b/unstable/ast.go index 34ef628..6b21592 100644 --- a/unstable/ast.go +++ b/unstable/ast.go @@ -28,12 +28,16 @@ func (c *Iterator) Next() bool { if c.nodes == nil { return false } + nodes := *c.nodes if !c.started { c.started = true - } else if c.idx >= 0 { - c.idx = (*c.nodes)[c.idx].next + } else { + idx := c.idx + if idx >= 0 && int(idx) < len(nodes) { + c.idx = nodes[idx].next + } } - return c.idx >= 0 && int(c.idx) < len(*c.nodes) + return c.idx >= 0 && int(c.idx) < len(nodes) } // IsLast returns true if the current node of the iterator is the last diff --git a/unstable/parser.go b/unstable/parser.go index d97dc34..e7c68dc 100644 --- a/unstable/parser.go +++ b/unstable/parser.go @@ -363,9 +363,10 @@ func (p *Parser) parseKeyval(b []byte) (reference, []byte, error) { p.builder.Chain(valRef, key) p.builder.AttachChild(ref, valRef) - // Set Raw to span the entire key-value expression - node := p.builder.NodeAt(ref) - node.Raw = p.rangeOfToken(startB[:len(startB)-len(b)], b) + // Set Raw to span the entire key-value expression. + // Access the node directly in the slice to avoid the write barrier + // that NodeAt's nodes-pointer setup would trigger. + p.builder.tree.nodes[ref].Raw = p.rangeOfToken(startB[:len(startB)-len(b)], b) return ref, b, err }