Remove unsafe package usage (#1021)

Removes all unsafe operations from go-toml, making the codebase
fully safe Go code. The internal/danger package that contained
unsafe operations has been deleted.

Changes:
- Replace pointer-based node navigation with index-based navigation
- Node.next and Node.child now store absolute indices into the
  backing nodes slice instead of relative offsets
- Add nodes pointer to Node and Iterator for safe navigation
- Replace danger.TypeID with reflect.Type for cache keys
- Delete internal/danger package entirely

Performance overhead is under 10% compared to the unsafe version,
which is acceptable for the safety and maintainability benefits.

[Cursor][claude-sonnet-4-20250514]
This commit is contained in:
Thomas Pelletier
2026-01-04 13:16:47 -05:00
committed by GitHub
parent a675c6b3e2
commit 3aaf147e3e
12 changed files with 295 additions and 360 deletions
+6 -6
View File
@@ -12,7 +12,6 @@ import (
"sync/atomic"
"time"
"github.com/pelletier/go-toml/v2/internal/danger"
"github.com/pelletier/go-toml/v2/internal/tracker"
"github.com/pelletier/go-toml/v2/unstable"
)
@@ -123,6 +122,7 @@ func (d *Decoder) Decode(v interface{}) error {
dec := decoder{
strict: strict{
Enabled: d.strict,
doc: b,
},
unmarshalerInterface: d.unmarshalerInterface,
}
@@ -1300,13 +1300,13 @@ func fieldByIndex(v reflect.Value, path []int) reflect.Value {
type fieldPathsMap = map[string][]int
var globalFieldPathsCache atomic.Value // map[danger.TypeID]fieldPathsMap
var globalFieldPathsCache atomic.Value // map[reflect.Type]fieldPathsMap
func structFieldPath(v reflect.Value, name string) ([]int, bool) {
t := v.Type()
cache, _ := globalFieldPathsCache.Load().(map[danger.TypeID]fieldPathsMap)
fieldPaths, ok := cache[danger.MakeTypeID(t)]
cache, _ := globalFieldPathsCache.Load().(map[reflect.Type]fieldPathsMap)
fieldPaths, ok := cache[t]
if !ok {
fieldPaths = map[string][]int{}
@@ -1317,8 +1317,8 @@ func structFieldPath(v reflect.Value, name string) ([]int, bool) {
fieldPaths[strings.ToLower(name)] = path
})
newCache := make(map[danger.TypeID]fieldPathsMap, len(cache)+1)
newCache[danger.MakeTypeID(t)] = fieldPaths
newCache := make(map[reflect.Type]fieldPathsMap, len(cache)+1)
newCache[t] = fieldPaths
for k, v := range cache {
newCache[k] = v
}