diff --git a/format/mp4/mp4.go b/format/mp4/mp4.go index ed06520e6..d58eb64e1 100644 --- a/format/mp4/mp4.go +++ b/format/mp4/mp4.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/wader/fq/format" + "github.com/wader/fq/internal/cmpex" "github.com/wader/fq/pkg/decode" "github.com/wader/fq/pkg/interp" "golang.org/x/exp/slices" @@ -224,7 +225,7 @@ func mp4Tracks(d *decode.D, ctx *decodeContext) { for _, t := range ctx.tracks { sortedTracks = append(sortedTracks, t) } - slices.SortFunc(sortedTracks, func(a, b *track) bool { return a.id < b.id }) + slices.SortFunc(sortedTracks, func(a, b *track) int { return cmpex.Compare(a.id, b.id) }) d.FieldArray("tracks", func(d *decode.D) { for _, t := range sortedTracks { diff --git a/format/xml/xml.go b/format/xml/xml.go index e5d1e6000..335963453 100644 --- a/format/xml/xml.go +++ b/format/xml/xml.go @@ -356,14 +356,14 @@ func xmlNameFromStr(s string) xml.Name { return xml.Name{Local: s} } -func xmlNameSort(a, b xml.Name) bool { +func xmlNameSort(a, b xml.Name) int { if a.Space != b.Space { if a.Space == "" { - return true + return 1 } - return a.Space < b.Space + return strings.Compare(a.Space, b.Space) } - return a.Local < b.Local + return strings.Compare(a.Local, b.Local) } type ToXMLOpts struct { @@ -442,7 +442,7 @@ func toXMLFromObject(c any, opts ToXMLOpts) any { sortex.ProxySort(orderNames, n.Nodes, func(a, b string) bool { return a < b }) } - slices.SortFunc(n.Attrs, func(a, b xml.Attr) bool { return xmlNameSort(a.Name, b.Name) }) + slices.SortFunc(n.Attrs, func(a, b xml.Attr) int { return xmlNameSort(a.Name, b.Name) }) return n, seq, hasSeq } @@ -515,7 +515,7 @@ func toXMLFromArray(c any, opts ToXMLOpts) any { } } - slices.SortFunc(n.Attrs, func(a, b xml.Attr) bool { return xmlNameSort(a.Name, b.Name) }) + slices.SortFunc(n.Attrs, func(a, b xml.Attr) int { return xmlNameSort(a.Name, b.Name) }) for _, c := range children { c, ok := c.([]any) diff --git a/go.mod b/go.mod index 66bc4bbc9..8fed4435f 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( // has no tags // go get -d github.com/gomarkdown/markdown@master && go mod tidy - github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c + github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12 // bump: gomod-gopacket /github\.com\/gopacket\/gopacket v(.*)/ https://github.com/gopacket/gopacket.git|^1 // bump: gomod-gopacket command go get -d github.com/gopacket/gopacket@v$LATEST && go mod tidy @@ -56,7 +56,7 @@ require ( // has no tags // go get -d golang.org/x/exp@master && go mod tidy - golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 + golang.org/x/exp v0.0.0-20230809094429-853ea248256d // bump: gomod-golang-x-net /golang\.org\/x\/net v(.*)/ https://github.com/golang/net.git|^0 // bump: gomod-golang-x-net command go get -d golang.org/x/net@v$LATEST && go mod tidy diff --git a/go.sum b/go.sum index ad13b6d77..5614921e4 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdB github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c h1:iyaGYbCmcYK0Ja9a3OUa2Fo+EaN0cbLu0eKpBwPFzc8= -github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= +github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12 h1:uK3X/2mt4tbSGoHvbLBHUny7CKiuwUip3MArtukol4E= +github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/gopacket/gopacket v1.1.1 h1:zbx9F9d6A7sWNkFKrvMBZTfGgxFoY4NgUudFVVHMfcw= github.com/gopacket/gopacket v1.1.1/go.mod h1:HavMeONEl7W9036of9LbSWoonqhH7HA1+ZRO+rMIvFs= github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= @@ -29,8 +29,8 @@ github.com/wader/readline v0.0.0-20230307172220-bcb7158e7448 h1:AzpBtmgdXa3uznrb github.com/wader/readline v0.0.0-20230307172220-bcb7158e7448/go.mod h1:Zgz8IJWvJoe7NK23CCPpC109XMCqJCpUhpHcnnA4XaM= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 h1:BEABXpNXLEz0WxtA+6CQIz2xkg80e+1zrhWyMcq8VzE= -golang.org/x/exp v0.0.0-20230131160201-f062dba9d201/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230809094429-853ea248256d h1:wu5bD43Ana/nF1ZmaLr3lW/FQeJU8CcI+Ln7yWHViXE= +golang.org/x/exp v0.0.0-20230809094429-853ea248256d/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/cmpex/cmpex.go b/internal/cmpex/cmpex.go new file mode 100644 index 000000000..70e182556 --- /dev/null +++ b/internal/cmpex/cmpex.go @@ -0,0 +1,59 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cmp provides types and functions related to comparing +// ordered values. +package cmpex + +// Ordered is a constraint that permits any ordered type: any type +// that supports the operators < <= >= >. +// If future releases of Go add new ordered types, +// this constraint will be modified to include them. +// +// Note that floating-point types may contain NaN ("not-a-number") values. +// An operator such as == or < will always report false when +// comparing a NaN value with any other value, NaN or not. +// See the [Compare] function for a consistent way to compare NaN values. +type Ordered interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~string +} + +// Less reports whether x is less than y. +// For floating-point types, a NaN is considered less than any non-NaN, +// and -0.0 is not less than (is equal to) 0.0. +func Less[T Ordered](x, y T) bool { + return (isNaN(x) && !isNaN(y)) || x < y +} + +// Compare returns +// +// -1 if x is less than y, +// 0 if x equals y, +// +1 if x is greater than y. +// +// For floating-point types, a NaN is considered less than any non-NaN, +// a NaN is considered equal to a NaN, and -0.0 is equal to 0.0. +func Compare[T Ordered](x, y T) int { + xNaN := isNaN(x) + yNaN := isNaN(y) + if xNaN && yNaN { + return 0 + } + if xNaN || x < y { + return -1 + } + if yNaN || x > y { + return +1 + } + return 0 +} + +// isNaN reports whether x is a NaN without requiring the math package. +// This will always return false if T is not floating-point. +func isNaN[T Ordered](x T) bool { + return x != x +} diff --git a/internal/mathex/num.go b/internal/mathex/num.go index 86858e662..c0d410658 100644 --- a/internal/mathex/num.go +++ b/internal/mathex/num.go @@ -7,9 +7,8 @@ import ( "strconv" "strings" - "golang.org/x/exp/constraints" - "github.com/wader/fq/pkg/ranges" + "golang.org/x/exp/constraints" ) var BasePrefixMap = map[int]string{ diff --git a/internal/script/script.go b/internal/script/script.go index e47ad55c1..7b77a527c 100644 --- a/internal/script/script.go +++ b/internal/script/script.go @@ -13,6 +13,7 @@ import ( "strconv" "strings" + "github.com/wader/fq/internal/cmpex" "github.com/wader/fq/internal/shquote" "github.com/wader/fq/pkg/bitio" "github.com/wader/fq/pkg/interp" @@ -274,7 +275,7 @@ type Case struct { func (c *Case) ToActual() string { var partsLineSorted []part partsLineSorted = append(partsLineSorted, c.Parts...) - slices.SortFunc(partsLineSorted, func(a, b part) bool { return a.Line() < b.Line() }) + slices.SortFunc(partsLineSorted, func(a, b part) int { return cmpex.Compare(a.Line(), b.Line()) }) sb := &strings.Builder{} for _, p := range partsLineSorted { diff --git a/pkg/decode/value.go b/pkg/decode/value.go index b4e4ddf59..afe65f94d 100644 --- a/pkg/decode/value.go +++ b/pkg/decode/value.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + "github.com/wader/fq/internal/cmpex" "github.com/wader/fq/pkg/bitio" "github.com/wader/fq/pkg/ranges" "github.com/wader/fq/pkg/scalar" @@ -200,7 +201,9 @@ func (v *Value) postProcess() { // sort struct fields and make sure to keep order if range is the same if !vv.IsArray { - slices.SortStableFunc(vv.Children, func(a, b *Value) bool { return a.Range.Start < b.Range.Start }) + slices.SortStableFunc(vv.Children, func(a, b *Value) int { + return cmpex.Compare(a.Range.Start, b.Range.Start) + }) } v.Index = -1 diff --git a/pkg/interp/registry.go b/pkg/interp/registry.go index 6241fcf5b..6d25bdf61 100644 --- a/pkg/interp/registry.go +++ b/pkg/interp/registry.go @@ -4,8 +4,10 @@ import ( "errors" "fmt" "io/fs" + "strings" "sync" + "github.com/wader/fq/internal/cmpex" "github.com/wader/fq/internal/gojqex" "github.com/wader/fq/pkg/decode" "golang.org/x/exp/slices" @@ -64,11 +66,11 @@ func (r *Registry) Func(funcFn EnvFuncFn) { } func sortFormats(g *decode.Group) { - slices.SortFunc(g.Formats, func(a, b *decode.Format) bool { + slices.SortFunc(g.Formats, func(a, b *decode.Format) int { if a.ProbeOrder == b.ProbeOrder { - return a.Name < b.Name + return strings.Compare(a.Name, b.Name) } - return a.ProbeOrder < b.ProbeOrder + return cmpex.Compare(a.ProbeOrder, b.ProbeOrder) }) } diff --git a/pkg/ranges/ranges.go b/pkg/ranges/ranges.go index a26513e79..10b1d028a 100644 --- a/pkg/ranges/ranges.go +++ b/pkg/ranges/ranges.go @@ -6,6 +6,8 @@ import ( "strings" "golang.org/x/exp/slices" + + "github.com/wader/fq/internal/cmpex" ) func max(a, b int64) int64 { @@ -63,7 +65,7 @@ func Gaps(total Range, ranges []Range) []Range { return []Range{total} } - slices.SortFunc(ranges, func(a, b Range) bool { return a.Start < b.Start }) + slices.SortFunc(ranges, func(a, b Range) int { return cmpex.Compare(a.Start, b.Start) }) // worst case ranges+1 gaps merged := make([]Range, 0, len(ranges)+1)