gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

seen.go (7946B)


      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 }