seen.go 8.1 KB

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