seen.go raw

   1  package tracker
   2  
   3  import (
   4  	"bytes"
   5  	"fmt"
   6  	"sync"
   7  
   8  	"github.com/pelletier/go-toml/v2/unstable"
   9  )
  10  
  11  type keyKind uint8
  12  
  13  const (
  14  	invalidKind keyKind = iota
  15  	valueKind
  16  	tableKind
  17  	arrayTableKind
  18  )
  19  
  20  func (k keyKind) String() string {
  21  	switch k {
  22  	case invalidKind:
  23  		return "invalid"
  24  	case valueKind:
  25  		return "value"
  26  	case tableKind:
  27  		return "table"
  28  	case arrayTableKind:
  29  		return "array table"
  30  	}
  31  	panic("missing keyKind string mapping")
  32  }
  33  
  34  // SeenTracker tracks which keys have been seen with which TOML type to flag
  35  // duplicates and mismatches according to the spec.
  36  //
  37  // Each node in the visited tree is represented by an entry. Each entry has an
  38  // identifier, which is provided by a counter. Entries are stored in the array
  39  // entries. As new nodes are discovered (referenced for the first time in the
  40  // TOML document), entries are created and appended to the array. An entry
  41  // points to its parent using its id.
  42  //
  43  // To find whether a given key (sequence of []byte) has already been visited,
  44  // the entries are linearly searched, looking for one with the right name and
  45  // parent id.
  46  //
  47  // Given that all keys appear in the document after their parent, it is
  48  // guaranteed that all descendants of a node are stored after the node, this
  49  // speeds up the search process.
  50  //
  51  // When encountering [[array tables]], the descendants of that node are removed
  52  // to allow that branch of the tree to be "rediscovered". To maintain the
  53  // invariant above, the deletion process needs to keep the order of entries.
  54  // This results in more copies in that case.
  55  type SeenTracker struct {
  56  	entries    []entry
  57  	currentIdx int
  58  }
  59  
  60  var pool sync.Pool
  61  
  62  func (s *SeenTracker) reset() {
  63  	// Always contains a root element at index 0.
  64  	s.currentIdx = 0
  65  	if len(s.entries) == 0 {
  66  		s.entries = make([]entry, 1, 2)
  67  	} else {
  68  		s.entries = s.entries[:1]
  69  	}
  70  	s.entries[0].child = -1
  71  	s.entries[0].next = -1
  72  }
  73  
  74  type entry struct {
  75  	// Use -1 to indicate no child or no sibling.
  76  	child int
  77  	next  int
  78  
  79  	name     []byte
  80  	kind     keyKind
  81  	explicit bool
  82  	kv       bool
  83  }
  84  
  85  // Find the index of the child of parentIdx with key k. Returns -1 if
  86  // it does not exist.
  87  func (s *SeenTracker) find(parentIdx int, k []byte) int {
  88  	for i := s.entries[parentIdx].child; i >= 0; i = s.entries[i].next {
  89  		if bytes.Equal(s.entries[i].name, k) {
  90  			return i
  91  		}
  92  	}
  93  	return -1
  94  }
  95  
  96  // Remove all descendants of node at position idx.
  97  func (s *SeenTracker) clear(idx int) {
  98  	if idx >= len(s.entries) {
  99  		return
 100  	}
 101  
 102  	for i := s.entries[idx].child; i >= 0; {
 103  		next := s.entries[i].next
 104  		n := s.entries[0].next
 105  		s.entries[0].next = i
 106  		s.entries[i].next = n
 107  		s.entries[i].name = nil
 108  		s.clear(i)
 109  		i = next
 110  	}
 111  
 112  	s.entries[idx].child = -1
 113  }
 114  
 115  func (s *SeenTracker) create(parentIdx int, name []byte, kind keyKind, explicit bool, kv bool) int {
 116  	e := entry{
 117  		child: -1,
 118  		next:  s.entries[parentIdx].child,
 119  
 120  		name:     name,
 121  		kind:     kind,
 122  		explicit: explicit,
 123  		kv:       kv,
 124  	}
 125  	var idx int
 126  	if s.entries[0].next >= 0 {
 127  		idx = s.entries[0].next
 128  		s.entries[0].next = s.entries[idx].next
 129  		s.entries[idx] = e
 130  	} else {
 131  		idx = len(s.entries)
 132  		s.entries = append(s.entries, e)
 133  	}
 134  
 135  	s.entries[parentIdx].child = idx
 136  
 137  	return idx
 138  }
 139  
 140  func (s *SeenTracker) setExplicitFlag(parentIdx int) {
 141  	for i := s.entries[parentIdx].child; i >= 0; i = s.entries[i].next {
 142  		if s.entries[i].kv {
 143  			s.entries[i].explicit = true
 144  			s.entries[i].kv = false
 145  		}
 146  		s.setExplicitFlag(i)
 147  	}
 148  }
 149  
 150  // CheckExpression takes a top-level node and checks that it does not contain
 151  // keys that have been seen in previous calls, and validates that types are
 152  // consistent.
 153  func (s *SeenTracker) CheckExpression(node *unstable.Node) error {
 154  	if s.entries == nil {
 155  		s.reset()
 156  	}
 157  	switch node.Kind {
 158  	case unstable.KeyValue:
 159  		return s.checkKeyValue(node)
 160  	case unstable.Table:
 161  		return s.checkTable(node)
 162  	case unstable.ArrayTable:
 163  		return s.checkArrayTable(node)
 164  	default:
 165  		panic(fmt.Errorf("this should not be a top level node type: %s", node.Kind))
 166  	}
 167  }
 168  
 169  func (s *SeenTracker) checkTable(node *unstable.Node) error {
 170  	if s.currentIdx >= 0 {
 171  		s.setExplicitFlag(s.currentIdx)
 172  	}
 173  
 174  	it := node.Key()
 175  
 176  	parentIdx := 0
 177  
 178  	// This code is duplicated in checkArrayTable. This is because factoring
 179  	// it in a function requires to copy the iterator, or allocate it to the
 180  	// heap, which is not cheap.
 181  	for it.Next() {
 182  		if it.IsLast() {
 183  			break
 184  		}
 185  
 186  		k := it.Node().Data
 187  
 188  		idx := s.find(parentIdx, k)
 189  
 190  		if idx < 0 {
 191  			idx = s.create(parentIdx, k, tableKind, false, false)
 192  		} else {
 193  			entry := s.entries[idx]
 194  			if entry.kind == valueKind {
 195  				return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind)
 196  			}
 197  		}
 198  		parentIdx = idx
 199  	}
 200  
 201  	k := it.Node().Data
 202  	idx := s.find(parentIdx, k)
 203  
 204  	if idx >= 0 {
 205  		kind := s.entries[idx].kind
 206  		if kind != tableKind {
 207  			return fmt.Errorf("toml: key %s should be a table, not a %s", string(k), kind)
 208  		}
 209  		if s.entries[idx].explicit {
 210  			return fmt.Errorf("toml: table %s already exists", string(k))
 211  		}
 212  		s.entries[idx].explicit = true
 213  	} else {
 214  		idx = s.create(parentIdx, k, tableKind, true, false)
 215  	}
 216  
 217  	s.currentIdx = idx
 218  
 219  	return nil
 220  }
 221  
 222  func (s *SeenTracker) checkArrayTable(node *unstable.Node) error {
 223  	if s.currentIdx >= 0 {
 224  		s.setExplicitFlag(s.currentIdx)
 225  	}
 226  
 227  	it := node.Key()
 228  
 229  	parentIdx := 0
 230  
 231  	for it.Next() {
 232  		if it.IsLast() {
 233  			break
 234  		}
 235  
 236  		k := it.Node().Data
 237  
 238  		idx := s.find(parentIdx, k)
 239  
 240  		if idx < 0 {
 241  			idx = s.create(parentIdx, k, tableKind, false, false)
 242  		} else {
 243  			entry := s.entries[idx]
 244  			if entry.kind == valueKind {
 245  				return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind)
 246  			}
 247  		}
 248  
 249  		parentIdx = idx
 250  	}
 251  
 252  	k := it.Node().Data
 253  	idx := s.find(parentIdx, k)
 254  
 255  	if idx >= 0 {
 256  		kind := s.entries[idx].kind
 257  		if kind != arrayTableKind {
 258  			return fmt.Errorf("toml: key %s already exists as a %s,  but should be an array table", kind, string(k))
 259  		}
 260  		s.clear(idx)
 261  	} else {
 262  		idx = s.create(parentIdx, k, arrayTableKind, true, false)
 263  	}
 264  
 265  	s.currentIdx = idx
 266  
 267  	return nil
 268  }
 269  
 270  func (s *SeenTracker) checkKeyValue(node *unstable.Node) error {
 271  	parentIdx := s.currentIdx
 272  	it := node.Key()
 273  
 274  	for it.Next() {
 275  		k := it.Node().Data
 276  
 277  		idx := s.find(parentIdx, k)
 278  
 279  		if idx < 0 {
 280  			idx = s.create(parentIdx, k, tableKind, false, true)
 281  		} else {
 282  			entry := s.entries[idx]
 283  			if it.IsLast() {
 284  				return fmt.Errorf("toml: key %s is already defined", string(k))
 285  			} else if entry.kind != tableKind {
 286  				return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind)
 287  			} else if entry.explicit {
 288  				return fmt.Errorf("toml: cannot redefine table %s that has already been explicitly defined", string(k))
 289  			}
 290  		}
 291  
 292  		parentIdx = idx
 293  	}
 294  
 295  	s.entries[parentIdx].kind = valueKind
 296  
 297  	value := node.Value()
 298  
 299  	switch value.Kind {
 300  	case unstable.InlineTable:
 301  		return s.checkInlineTable(value)
 302  	case unstable.Array:
 303  		return s.checkArray(value)
 304  	}
 305  
 306  	return nil
 307  }
 308  
 309  func (s *SeenTracker) checkArray(node *unstable.Node) error {
 310  	it := node.Children()
 311  	for it.Next() {
 312  		n := it.Node()
 313  		switch n.Kind {
 314  		case unstable.InlineTable:
 315  			err := s.checkInlineTable(n)
 316  			if err != nil {
 317  				return err
 318  			}
 319  		case unstable.Array:
 320  			err := s.checkArray(n)
 321  			if err != nil {
 322  				return err
 323  			}
 324  		}
 325  	}
 326  	return nil
 327  }
 328  
 329  func (s *SeenTracker) checkInlineTable(node *unstable.Node) error {
 330  	if pool.New == nil {
 331  		pool.New = func() interface{} {
 332  			return &SeenTracker{}
 333  		}
 334  	}
 335  
 336  	s = pool.Get().(*SeenTracker)
 337  	s.reset()
 338  
 339  	it := node.Children()
 340  	for it.Next() {
 341  		n := it.Node()
 342  		err := s.checkKeyValue(n)
 343  		if err != nil {
 344  			return err
 345  		}
 346  	}
 347  
 348  	// As inline tables are self-contained, the tracker does not
 349  	// need to retain the details of what they contain. The
 350  	// keyValue element that creates the inline table is kept to
 351  	// mark the presence of the inline table and prevent
 352  	// redefinition of its keys: check* functions cannot walk into
 353  	// a value.
 354  	pool.Put(s)
 355  	return nil
 356  }
 357