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]
As recommended, an `internal/assert` package was added with a reduced set of assertions. All tests were then refactored to use the internal assertions. When more complex assertions were used, they have been rewritten using logic and the simplified assertions.
Fancy formatting for failures was omitted. The `internal/assert/assertions.diff` function could be overwritten for better formatting. That is where diff libraries are used in other test suites.
Refs: #872
Co-authored-by: Alex Mikitik <alex.mikitik@oracle.com>
When decoding into a non-empty slice, it needs to be emptied so that only the
tables contained in the document are present in the resulting value.
Arrays are not impacted because their unmarshal offset is tracked separately.
Fixes#931
The test meant to assert that the size of entry does not grow beyond
what it is today. 48 is the current size in bytes on x64. On 32-bit
platform that value should be less. As written the test was doing the
opposite comparison.
Fixes#760
The TOML spec is being clarified to say that dotted keys "define" their
intermediate tables. Therefore the seen tracker needs to verify that none of
them reference an explicit table.
Also added a missing seen expression check for key-values parsed as part of a
table section.
See https://github.com/toml-lang/toml/issues/846
* Use pointers instead of copying around ast.Node
Node is a 56B struct that is constantly in the hot path. Passing nodes
around by copy had a cost that started to add up. This change replaces
them by pointers. Using unsafe pointer arithmetic and converting
sibling/child indexes to relative offsets, it removes the need to carry
around a pointer to the root of the tree. This saves 8B per Node. This
space will be used to store an extra []byte slice to provide contextual
error handling on all nodes, including the ones whose data is different
than the raw input (for example: strings with escaped characters), while
staying under the size of a cache line.
* Remove conditional
* Add Raw to track range in data for parsed values
* Simplify reference tracking
* Benchmark script
* Rewrite unmarshaler using the stack
Instead of tracking the build chain using `target`s, use the stack
instead.
Working and most benchmarks look good, but regression on structs unmarshalling.
~60% slower on ReferenceFile/struct.
* Shortcut to check if last node of iterator
* Remove unecessary pointer allocation
* Skip over unused keys without marking them as seen
* Add some tests
* Fix mktemp on macos
* refactor(tracker): Remove unreachable return
* refactor(unmarshal_imported_test): Mark unused camelCase test
golangci-lint indicates `TestUnmarshalCamelCaseKey` as unused (it's
currently skipped).
* refactor(unmarshal_imported_test): Mark unused type
golangci-lint indicates `customPointerMarshaler` as unused.
* refactor(unmarshal_imported_test): Mark unused type
golangci-lint indicates `textPointerMarshaler` as unused.
* refactor(unmarshal_imported_test): Mark unused type
golangci-lint indicates `precedentMarshaler` and its methods as
unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `testDurationToml2` as unused.
* refactor(unmarshal_imported_test): Mark unused type
golangci-lint indicates `testBadDuration` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `testDurationToml` as unused.
* refactor(unmarshal_imported_test): Mark unused type
golangci-lint indicates `testDuration` as unused.
* refactor(unmarshal_imported_test): Mark unused type
golangci-lint indicates `testDocCustomTag` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `testDocCustomTagData` as unused.
* refactor(unmarshal_imported_test): Mark unused type
golangci-lint indicates `testDocBasicsCustomTag` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `testDocBasicToml` as unused.
* refactor(unmarshal_imported_test): Mark unused type
golangci-lint indicates `structArrayNoTag` as unused.
* refactor(unmarshal_imported_test): Mark unused type
golangci-lint incorrectly indicates `check` as unused.
* refactor(unmarshal_imported_test): Mark unused struct field
golangci-lint indicates `testDoc.err` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `customMultilineTagTestToml` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `customCommentedTagTestToml` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `customCommentTagTestToml` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `customTagTestToml` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `mapsTestToml` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `mapsTestData` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `commentTestToml` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `nestedCustomMarshalerToml` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `nestedCustomMarshalerData` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `customMarshalerToml` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `mapTestDoc` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `quotedKeyMarshalTestToml` as unused.
* refactor(unmarshal_imported_test): Mark unused var
golangci-lint indicates `quotedKeyMarshalTestData` as unused.