ea225df3ed
``` name old time/op new time/op delta UnmarshalDataset/config-32 86.7ms ± 2% 87.5ms ± 2% ~ (p=0.113 n=9+10) UnmarshalDataset/canada-32 129ms ± 4% 106ms ± 3% -17.94% (p=0.000 n=10+10) UnmarshalDataset/citm_catalog-32 59.4ms ± 5% 58.7ms ± 5% ~ (p=0.393 n=10+10) UnmarshalDataset/twitter-32 27.0ms ± 7% 26.9ms ± 6% ~ (p=0.720 n=10+9) UnmarshalDataset/code-32 326ms ± 4% 322ms ± 7% ~ (p=0.661 n=9+10) UnmarshalDataset/example-32 510µs ±11% 526µs ± 7% ~ (p=0.182 n=10+9) UnmarshalSimple-32 1.41µs ± 6% 1.41µs ± 4% ~ (p=0.736 n=10+9) ReferenceFile-32 45.6µs ± 3% 43.9µs ±10% ~ (p=0.089 n=10+10) name old speed new speed delta UnmarshalDataset/config-32 12.1MB/s ± 2% 12.0MB/s ± 2% ~ (p=0.108 n=9+10) UnmarshalDataset/canada-32 17.1MB/s ± 4% 20.9MB/s ± 3% +21.86% (p=0.000 n=10+10) UnmarshalDataset/citm_catalog-32 9.41MB/s ± 5% 9.51MB/s ± 5% ~ (p=0.362 n=10+10) UnmarshalDataset/twitter-32 16.4MB/s ± 8% 16.5MB/s ± 6% ~ (p=0.704 n=10+9) UnmarshalDataset/code-32 8.24MB/s ± 4% 8.34MB/s ± 7% ~ (p=0.675 n=9+10) UnmarshalDataset/example-32 15.9MB/s ±11% 15.4MB/s ± 7% ~ (p=0.182 n=10+9) ReferenceFile-32 115MB/s ± 4% 120MB/s ±10% ~ (p=0.085 n=10+10) name old alloc/op new alloc/op delta UnmarshalDataset/config-32 16.9MB ± 0% 16.9MB ± 0% -0.02% (p=0.000 n=10+10) UnmarshalDataset/canada-32 76.8MB ± 0% 74.3MB ± 0% -3.31% (p=0.000 n=10+10) UnmarshalDataset/citm_catalog-32 37.3MB ± 0% 37.1MB ± 0% -0.60% (p=0.000 n=9+10) UnmarshalDataset/twitter-32 15.6MB ± 0% 15.6MB ± 0% -0.09% (p=0.000 n=10+10) UnmarshalDataset/code-32 60.2MB ± 0% 59.3MB ± 0% -1.51% (p=0.000 n=10+9) UnmarshalDataset/example-32 238kB ± 0% 238kB ± 0% -0.18% (p=0.000 n=10+10) ReferenceFile-32 11.8kB ± 0% 11.8kB ± 0% ~ (all equal) name old allocs/op new allocs/op delta UnmarshalDataset/config-32 653k ± 0% 645k ± 0% -1.20% (p=0.000 n=10+6) UnmarshalDataset/canada-32 1.01M ± 0% 0.90M ± 0% -11.04% (p=0.000 n=9+10) UnmarshalDataset/citm_catalog-32 384k ± 0% 370k ± 0% -3.75% (p=0.000 n=10+10) UnmarshalDataset/twitter-32 160k ± 0% 157k ± 0% -1.32% (p=0.000 n=10+10) UnmarshalDataset/code-32 2.97M ± 0% 2.91M ± 0% -2.15% (p=0.000 n=10+7) UnmarshalDataset/example-32 3.69k ± 0% 3.63k ± 0% -1.52% (p=0.000 n=10+10) ReferenceFile-32 253 ± 0% 253 ± 0% ~ (all equal) ```
201 lines
4.0 KiB
Go
201 lines
4.0 KiB
Go
package tracker
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/pelletier/go-toml/v2/internal/ast"
|
|
)
|
|
|
|
type keyKind uint8
|
|
|
|
const (
|
|
invalidKind keyKind = iota
|
|
valueKind
|
|
tableKind
|
|
arrayTableKind
|
|
)
|
|
|
|
func (k keyKind) String() string {
|
|
switch k {
|
|
case invalidKind:
|
|
return "invalid"
|
|
case valueKind:
|
|
return "value"
|
|
case tableKind:
|
|
return "table"
|
|
case arrayTableKind:
|
|
return "array table"
|
|
}
|
|
panic("missing keyKind string mapping")
|
|
}
|
|
|
|
// SeenTracker tracks which keys have been seen with which TOML type to flag duplicates
|
|
// and mismatches according to the spec.
|
|
type SeenTracker struct {
|
|
root *info
|
|
current *info
|
|
}
|
|
|
|
type info struct {
|
|
parent *info
|
|
kind keyKind
|
|
children map[string]*info
|
|
explicit bool
|
|
}
|
|
|
|
func (i *info) Clear() {
|
|
i.children = nil
|
|
}
|
|
|
|
func (i *info) Has(k string) (*info, bool) {
|
|
c, ok := i.children[k]
|
|
return c, ok
|
|
}
|
|
|
|
func (i *info) SetKind(kind keyKind) {
|
|
i.kind = kind
|
|
}
|
|
|
|
func (i *info) CreateTable(k string, explicit bool) *info {
|
|
return i.createChild(k, tableKind, explicit)
|
|
}
|
|
|
|
func (i *info) CreateArrayTable(k string, explicit bool) *info {
|
|
return i.createChild(k, arrayTableKind, explicit)
|
|
}
|
|
|
|
func (i *info) createChild(k string, kind keyKind, explicit bool) *info {
|
|
if i.children == nil {
|
|
i.children = make(map[string]*info, 1)
|
|
}
|
|
|
|
x := &info{
|
|
parent: i,
|
|
kind: kind,
|
|
explicit: explicit,
|
|
}
|
|
i.children[k] = x
|
|
return x
|
|
}
|
|
|
|
// CheckExpression takes a top-level node and checks that it does not contain keys
|
|
// that have been seen in previous calls, and validates that types are consistent.
|
|
func (s *SeenTracker) CheckExpression(node ast.Node) error {
|
|
if s.root == nil {
|
|
s.root = &info{
|
|
kind: tableKind,
|
|
}
|
|
s.current = s.root
|
|
}
|
|
switch node.Kind {
|
|
case ast.KeyValue:
|
|
return s.checkKeyValue(s.current, node)
|
|
case ast.Table:
|
|
return s.checkTable(node)
|
|
case ast.ArrayTable:
|
|
return s.checkArrayTable(node)
|
|
default:
|
|
panic(fmt.Errorf("this should not be a top level node type: %s", node.Kind))
|
|
}
|
|
|
|
}
|
|
func (s *SeenTracker) checkTable(node ast.Node) error {
|
|
s.current = s.root
|
|
|
|
it := node.Key()
|
|
// handle the first parts of the key, excluding the last one
|
|
for it.Next() {
|
|
if !it.Node().Next().Valid() {
|
|
break
|
|
}
|
|
|
|
k := string(it.Node().Data)
|
|
child, found := s.current.Has(k)
|
|
if !found {
|
|
child = s.current.CreateTable(k, false)
|
|
}
|
|
s.current = child
|
|
}
|
|
|
|
// handle the last part of the key
|
|
k := string(it.Node().Data)
|
|
|
|
i, found := s.current.Has(k)
|
|
if found {
|
|
if i.kind != tableKind {
|
|
return fmt.Errorf("toml: key %s should be a table, not a %s", k, i.kind)
|
|
}
|
|
if i.explicit {
|
|
return fmt.Errorf("toml: table %s already exists", k)
|
|
}
|
|
i.explicit = true
|
|
s.current = i
|
|
} else {
|
|
s.current = s.current.CreateTable(k, true)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *SeenTracker) checkArrayTable(node ast.Node) error {
|
|
s.current = s.root
|
|
|
|
it := node.Key()
|
|
|
|
// handle the first parts of the key, excluding the last one
|
|
for it.Next() {
|
|
if !it.Node().Next().Valid() {
|
|
break
|
|
}
|
|
|
|
k := string(it.Node().Data)
|
|
child, found := s.current.Has(k)
|
|
if !found {
|
|
child = s.current.CreateTable(k, false)
|
|
}
|
|
s.current = child
|
|
}
|
|
|
|
// handle the last part of the key
|
|
k := string(it.Node().Data)
|
|
|
|
info, found := s.current.Has(k)
|
|
if found {
|
|
if info.kind != arrayTableKind {
|
|
return fmt.Errorf("toml: key %s already exists as a %s, but should be an array table", info.kind, k)
|
|
}
|
|
info.Clear()
|
|
} else {
|
|
info = s.current.CreateArrayTable(k, true)
|
|
}
|
|
|
|
s.current = info
|
|
return nil
|
|
}
|
|
|
|
func (s *SeenTracker) checkKeyValue(context *info, node ast.Node) error {
|
|
it := node.Key()
|
|
|
|
// handle the first parts of the key, excluding the last one
|
|
for it.Next() {
|
|
k := string(it.Node().Data)
|
|
child, found := context.Has(k)
|
|
if found {
|
|
if child.kind != tableKind {
|
|
return fmt.Errorf("toml: expected %s to be a table, not a %s", k, child.kind)
|
|
}
|
|
} else {
|
|
child = context.CreateTable(k, false)
|
|
}
|
|
context = child
|
|
}
|
|
|
|
if node.Value().Kind == ast.InlineTable {
|
|
context.SetKind(tableKind)
|
|
} else {
|
|
context.SetKind(valueKind)
|
|
}
|
|
|
|
return nil
|
|
}
|