Compare commits
7
Commits
d365729b37
..
v1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad6c32d0c6 | ||
|
|
17574a0d15 | ||
|
|
8aa2b1b9c0 | ||
|
|
6a043e2824 | ||
|
|
81033bb27c | ||
|
|
dfd5d240d2 | ||
|
|
78946578d1 |
+8
-2
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
|
## [1.1.0] - 2026-09-18
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
- TOML 1.1 support, on by default: date-times and times without seconds
|
- TOML 1.1 support, on by default: date-times and times without seconds
|
||||||
(`07:32`, `1979-05-27T07:32`, normalised to full seconds on output), the
|
(`07:32`, `1979-05-27T07:32`, normalised to full seconds on output), the
|
||||||
`\e` and `\xHH` escape sequences, and multi-line inline tables with
|
`\e` and `\xHH` escape sequences, and multi-line inline tables with
|
||||||
@@ -35,8 +41,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The compliance suite is [toml-test](https://github.com/toml-lang/toml-test)
|
- The compliance suite is [toml-test](https://github.com/toml-lang/toml-test)
|
||||||
v2.2.0, run in TOML 1.0 mode. The new corpus holds 205 valid and 474 invalid
|
v2.2.0, up from v1.6.0. Its TOML 1.0 corpus holds 205 valid and 474 invalid
|
||||||
cases (v1.6.0 had 185 and 371), and it caught the two documents the parser
|
cases (185 and 371 before), and it caught the two documents the parser
|
||||||
still accepted, fixed below.
|
still accepted, fixed below.
|
||||||
- The flattened struct layout the decoder consults is cached per struct type
|
- The flattened struct layout the decoder consults is cached per struct type
|
||||||
and shared with the encoder, which now resolves duplicate field keys with
|
and shared with the encoder, which now resolves duplicate field keys with
|
||||||
|
|||||||
+6
-4
@@ -53,9 +53,11 @@ just test
|
|||||||
7. Open a pull request against `development`.
|
7. Open a pull request against `development`.
|
||||||
|
|
||||||
Releases are cut by merging `development` into `main` and tagging `vX.Y.Z`. The
|
Releases are cut by merging `development` into `main` and tagging `vX.Y.Z`. The
|
||||||
release workflow runs the full gate set including the race detector and
|
release workflow validates the tag, runs the static gates and the test suite
|
||||||
publishes the Gitea release with the matching `CHANGELOG.md` section as its
|
with the coverage floor, and publishes the Gitea release with the matching
|
||||||
notes.
|
`CHANGELOG.md` section as its notes. The race detector is not in that set: race
|
||||||
|
never runs on a push path, and the local `just gates` raced the tree before the
|
||||||
|
tag was cut.
|
||||||
|
|
||||||
## Code style
|
## Code style
|
||||||
|
|
||||||
@@ -114,7 +116,7 @@ Workflows live in `.gitea/workflows/` and run on the project's own runners:
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Test | push or pull request to `development` | format check, vet, modernisation, build, the test suite with the coverage floor, the toml-test compliance suite |
|
| Test | push or pull request to `development` | format check, vet, modernisation, build, the test suite with the coverage floor, the toml-test compliance suite |
|
||||||
| Race | `workflow_dispatch`, by hand | the suite under the race detector, the same race gate the local `just gates` runs |
|
| Race | `workflow_dispatch`, by hand | the suite under the race detector, the same race gate the local `just gates` runs |
|
||||||
| Release | a `v*` tag | the same gates plus the race detector, then the Gitea release created from the `CHANGELOG.md` section |
|
| Release | a `v*` tag | tag validation, format, vet, modernisation, build and the test suite with the coverage floor, then the Gitea release created from the `CHANGELOG.md` section; no race detector |
|
||||||
|
|
||||||
The local equivalent is `just gates`, which is the same set plus the race
|
The local equivalent is `just gates`, which is the same set plus the race
|
||||||
detector.
|
detector.
|
||||||
|
|||||||
+11
-4
@@ -7,6 +7,11 @@ package. The snippets assume:
|
|||||||
import "sourcedock.dev/petrbalvin/interpres"
|
import "sourcedock.dev/petrbalvin/interpres"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The parser accepts TOML 1.0 documents plus the TOML 1.1 extensions: date-times
|
||||||
|
and times without seconds, the `\e` and `\xHH` escape sequences, and
|
||||||
|
multi-line inline tables with comments and trailing commas. The encoder emits
|
||||||
|
TOML 1.0, which is valid under both versions.
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
|
|
||||||
### `func Parse(data []byte) (map[string]any, error)`
|
### `func Parse(data []byte) (map[string]any, error)`
|
||||||
@@ -139,8 +144,10 @@ offending key or index, for example `p: interpres: integer 300 overflows uint8`.
|
|||||||
Offset date-times decode into `time.Time` and keep their offset. The local
|
Offset date-times decode into `time.Time` and keep their offset. The local
|
||||||
variants decode into `LocalDateTime`, `LocalDate` and `LocalTime`, whose
|
variants decode into `LocalDateTime`, `LocalDate` and `LocalTime`, whose
|
||||||
embedded `time.Time` is normalised to UTC (midnight UTC for a local date, the
|
embedded `time.Time` is normalised to UTC (midnight UTC for a local date, the
|
||||||
zero date for a local time). There is no implicit conversion between the offset
|
zero date for a local time). Every kind may omit the seconds as of TOML 1.1
|
||||||
and local kinds; assigning one to the other is an error.
|
(`07:32`, `1979-05-27T07:32`); such a value carries a zero second, and the
|
||||||
|
canonical rendering writes full seconds. There is no implicit conversion
|
||||||
|
between the offset and local kinds; assigning one to the other is an error.
|
||||||
|
|
||||||
### Arrays of tables
|
### Arrays of tables
|
||||||
|
|
||||||
@@ -362,8 +369,8 @@ out, err := interpres.NewEncoder().UseLiteralMultiline(80).Marshal(cfg)
|
|||||||
Single-line strings keep the basic form regardless of the threshold, and a
|
Single-line strings keep the basic form regardless of the threshold, and a
|
||||||
threshold of `0` or less disables the option. A string the literal form cannot
|
threshold of `0` or less disables the option. A string the literal form cannot
|
||||||
carry verbatim (an embedded run of three single quotes, a control character
|
carry verbatim (an embedded run of three single quotes, a control character
|
||||||
other than tab, or a carriage return outside a CRLF pair) also keeps the basic
|
other than tab or newline, or a carriage return outside a CRLF pair) also keeps
|
||||||
form, so the output always re-parses to the same value.
|
the basic form, so the output always re-parses to the same value.
|
||||||
|
|
||||||
### Cancellation
|
### Cancellation
|
||||||
|
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ sequenceDiagram
|
|||||||
`Decode`, `DecodeContext`, `Marshal` and `MarshalContext` call allocates its
|
`Decode`, `DecodeContext`, `Marshal` and `MarshalContext` call allocates its
|
||||||
own unexported worker, so a configured type is safe for concurrent use; the
|
own unexported worker, so a configured type is safe for concurrent use; the
|
||||||
setter methods are not, and must finish before the value is shared.
|
setter methods are not, and must finish before the value is shared.
|
||||||
- The parser is allocated per `ParseContext` call; nothing is cached between
|
- The parser is allocated per `ParseContext` call; the parser itself caches
|
||||||
documents.
|
nothing between documents.
|
||||||
- The one piece of shared state is the struct-schema cache in `decode.go`: a
|
- The one piece of shared state is the struct-schema cache in `decode.go`: a
|
||||||
`sync.Map` keyed by `reflect.Type`, holding the flattened field layout the
|
`sync.Map` keyed by `reflect.Type`, holding the flattened field layout the
|
||||||
decoder and the encoder both consult. A schema is immutable once published,
|
decoder and the encoder both consult. A schema is immutable once published,
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Benchmarking
|
||||||
|
|
||||||
|
How the performance numbers attached to this project are measured, so that a
|
||||||
|
number in a changelog entry or a release note can be reproduced and trusted.
|
||||||
|
|
||||||
|
## The suite
|
||||||
|
|
||||||
|
The benchmarks live in `bench_test.go`, next to the code they measure:
|
||||||
|
|
||||||
|
| Benchmark | What it measures |
|
||||||
|
|---|---|
|
||||||
|
| `BenchmarkParse` | `Parse` over a representative configuration document |
|
||||||
|
| `BenchmarkMarshal` | `Marshal` of the tree `Parse` produced from the same document |
|
||||||
|
| `BenchmarkStrictDecode` | `Decode` into a struct under `DisallowUnknownFields` |
|
||||||
|
| `BenchmarkParseLong` | `Parse` over a generated document with about 2000 array-of-tables entries |
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
```sh
|
||||||
|
just bench
|
||||||
|
```
|
||||||
|
|
||||||
|
The recipe runs the suite with `-benchmem -count=5`. Every benchmark uses
|
||||||
|
`b.Loop`, so setup runs outside the timed region, and `ReportAllocs` records
|
||||||
|
allocations per operation. The parse and marshal benchmarks set `SetBytes`, so
|
||||||
|
their results read as input bytes per second.
|
||||||
|
|
||||||
|
## Method
|
||||||
|
|
||||||
|
- An idle machine only: a loaded box times whatever else is running, and the
|
||||||
|
fastest sample can land on the wrong function.
|
||||||
|
- An A/B comparison runs both variants inside one process, in one binary;
|
||||||
|
separate processes of identical binaries differ by more than the effect
|
||||||
|
being measured.
|
||||||
|
- The five counts are compared through their medians, allocations and bytes
|
||||||
|
per operation alongside the times. Differences within 1 to 2 percent are
|
||||||
|
noise; only a difference beyond that is a result.
|
||||||
|
- When timing is hopeless, the allocation and byte counts are the result.
|
||||||
|
|
||||||
|
## Reports
|
||||||
|
|
||||||
|
The repository stores no benchmark reports. A performance claim in
|
||||||
|
`CHANGELOG.md` is measured with the method above on the change that makes it,
|
||||||
|
and the number travels with the claim.
|
||||||
+9
-6
@@ -77,7 +77,9 @@ just bench
|
|||||||
```
|
```
|
||||||
|
|
||||||
Benchmark on an idle machine, and compare only runs made in one process against
|
Benchmark on an idle machine, and compare only runs made in one process against
|
||||||
each other. The recipe sweeps `./...` five times with `-benchmem`.
|
each other. The recipe sweeps `./...` five times with `-benchmem`. The binding
|
||||||
|
measurement method, and what counts as a result, is in
|
||||||
|
[docs/BENCHMARKING.md](BENCHMARKING.md).
|
||||||
|
|
||||||
## Debugging the build
|
## Debugging the build
|
||||||
|
|
||||||
@@ -97,12 +99,13 @@ pipeline.
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `test.yml` | push or pull request to `development` | format check, vet, modernisation, build, the test suite with the 80 percent coverage floor, then the toml-test compliance suite |
|
| `test.yml` | push or pull request to `development` | format check, vet, modernisation, build, the test suite with the 80 percent coverage floor, then the toml-test compliance suite |
|
||||||
| `race.yml` | `workflow_dispatch`, by hand | the suite under the race detector; the same race gate `just gates` runs locally |
|
| `race.yml` | `workflow_dispatch`, by hand | the suite under the race detector; the same race gate `just gates` runs locally |
|
||||||
| `release.yml` | a `v*` tag | the same gates plus the race detector, then the Gitea release from the CHANGELOG section |
|
| `release.yml` | a `v*` tag | tag validation, then format, vet, modernisation, build and the test suite with the coverage floor, then the Gitea release from the CHANGELOG section. No race detector: race never runs on a push path, and the local `just gates` raced the tree before the tag was cut |
|
||||||
|
|
||||||
## Releases
|
## Releases
|
||||||
|
|
||||||
Releases are cut by merging `development` into `main` and tagging `vX.Y.Z`. The
|
Releases are cut by merging `development` into `main` and tagging `vX.Y.Z`. The
|
||||||
tag drives the release workflow: it validates the tag, runs the full gate set
|
tag drives the release workflow: it validates the tag, runs the static gates
|
||||||
including the race detector, extracts the matching `## [X.Y.Z]` section from
|
and the test suite with the coverage floor, extracts the matching `## [X.Y.Z]`
|
||||||
`CHANGELOG.md`, and publishes the release with that section as its body. A
|
section from `CHANGELOG.md`, and publishes the release with that section as its
|
||||||
library ships no binaries, so the release carries the notes and nothing else.
|
body. A library ships no binaries, so the release carries the notes and nothing
|
||||||
|
else.
|
||||||
|
|||||||
Reference in New Issue
Block a user