From 89d7b412d87493fafefc8cf0612fd0652bec374e Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Thu, 7 Apr 2022 21:49:16 -0400 Subject: [PATCH] decode: allow subtables to be defined later (#750) Fixes #739 --- internal/tracker/seen.go | 19 ++++++++++++------- internal/tracker/seen_test.go | 15 +++++++++++++++ unmarshaler_test.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 internal/tracker/seen_test.go diff --git a/internal/tracker/seen.go b/internal/tracker/seen.go index 434b02c..a7ee05b 100644 --- a/internal/tracker/seen.go +++ b/internal/tracker/seen.go @@ -79,6 +79,7 @@ type entry struct { name []byte kind keyKind explicit bool + kv bool } // Find the index of the child of parentIdx with key k. Returns -1 if @@ -111,7 +112,7 @@ func (s *SeenTracker) clear(idx int) { s.entries[idx].child = -1 } -func (s *SeenTracker) create(parentIdx int, name []byte, kind keyKind, explicit bool) int { +func (s *SeenTracker) create(parentIdx int, name []byte, kind keyKind, explicit bool, kv bool) int { e := entry{ child: -1, next: s.entries[parentIdx].child, @@ -119,6 +120,7 @@ func (s *SeenTracker) create(parentIdx int, name []byte, kind keyKind, explicit name: name, kind: kind, explicit: explicit, + kv: kv, } var idx int if s.entries[0].next >= 0 { @@ -137,7 +139,10 @@ func (s *SeenTracker) create(parentIdx int, name []byte, kind keyKind, explicit func (s *SeenTracker) setExplicitFlag(parentIdx int) { for i := s.entries[parentIdx].child; i >= 0; i = s.entries[i].next { - s.entries[i].explicit = true + if s.entries[i].kv { + s.entries[i].explicit = true + s.entries[i].kv = false + } s.setExplicitFlag(i) } } @@ -183,7 +188,7 @@ func (s *SeenTracker) checkTable(node *ast.Node) error { idx := s.find(parentIdx, k) if idx < 0 { - idx = s.create(parentIdx, k, tableKind, false) + idx = s.create(parentIdx, k, tableKind, false, false) } else { entry := s.entries[idx] if entry.kind == valueKind { @@ -206,7 +211,7 @@ func (s *SeenTracker) checkTable(node *ast.Node) error { } s.entries[idx].explicit = true } else { - idx = s.create(parentIdx, k, tableKind, true) + idx = s.create(parentIdx, k, tableKind, true, false) } s.currentIdx = idx @@ -233,7 +238,7 @@ func (s *SeenTracker) checkArrayTable(node *ast.Node) error { idx := s.find(parentIdx, k) if idx < 0 { - idx = s.create(parentIdx, k, tableKind, false) + idx = s.create(parentIdx, k, tableKind, false, false) } else { entry := s.entries[idx] if entry.kind == valueKind { @@ -254,7 +259,7 @@ func (s *SeenTracker) checkArrayTable(node *ast.Node) error { } s.clear(idx) } else { - idx = s.create(parentIdx, k, arrayTableKind, true) + idx = s.create(parentIdx, k, arrayTableKind, true, false) } s.currentIdx = idx @@ -272,7 +277,7 @@ func (s *SeenTracker) checkKeyValue(node *ast.Node) error { idx := s.find(parentIdx, k) if idx < 0 { - idx = s.create(parentIdx, k, tableKind, false) + idx = s.create(parentIdx, k, tableKind, false, true) } else { entry := s.entries[idx] if it.IsLast() { diff --git a/internal/tracker/seen_test.go b/internal/tracker/seen_test.go new file mode 100644 index 0000000..9f283f3 --- /dev/null +++ b/internal/tracker/seen_test.go @@ -0,0 +1,15 @@ +package tracker + +import ( + "testing" + "unsafe" + + "github.com/stretchr/testify/require" +) + +func TestEntrySize(t *testing.T) { + // Validate no regression on the size of entry{}. This is a critical bit for + // performance of unmarshaling documents. Should only be increased with care + // and a very good reason. + require.LessOrEqual(t, 48, int(unsafe.Sizeof(entry{}))) +} diff --git a/unmarshaler_test.go b/unmarshaler_test.go index 08ceb3b..f15a069 100644 --- a/unmarshaler_test.go +++ b/unmarshaler_test.go @@ -545,6 +545,35 @@ func TestUnmarshal(t *testing.T) { } }, }, + { + desc: "issue 739 - table redefinition", + input: ` +[foo.bar.baz] +wibble = 'wobble' + +[foo] + +[foo.bar] +huey = 'dewey' + `, + gen: func() test { + m := map[string]interface{}{} + + return test{ + target: &m, + expected: &map[string]interface{}{ + `foo`: map[string]interface{}{ + "bar": map[string]interface{}{ + "huey": "dewey", + "baz": map[string]interface{}{ + "wibble": "wobble", + }, + }, + }, + }, + } + }, + }, { desc: "multiline basic string", input: `A = """\