1
1
mirror of https://github.com/wader/fq.git synced 2024-12-24 13:52:02 +03:00

Merge pull request #405 from wader/sort-refactor

sortex: Package with type safe sort helpers
This commit is contained in:
Mattias Wadman 2022-08-30 11:11:10 +02:00 committed by GitHub
commit a87616763e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 64 additions and 59 deletions

View File

@ -137,6 +137,7 @@
"SLEB", "SLEB",
"SLONG", "SLONG",
"SMPTE", "SMPTE",
"sortex",
"SRATIONAL", "SRATIONAL",
"stco", "stco",
"stedolan", "stedolan",

View File

@ -18,9 +18,9 @@ package mp4
import ( import (
"embed" "embed"
"fmt" "fmt"
"sort"
"github.com/wader/fq/format" "github.com/wader/fq/format"
"github.com/wader/fq/internal/sortex"
"github.com/wader/fq/pkg/decode" "github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/interp" "github.com/wader/fq/pkg/interp"
) )
@ -210,7 +210,7 @@ func mp4Tracks(d *decode.D, ctx *decodeContext) {
for _, t := range ctx.tracks { for _, t := range ctx.tracks {
sortedTracks = append(sortedTracks, t) sortedTracks = append(sortedTracks, t)
} }
sort.Slice(sortedTracks, func(i, j int) bool { return sortedTracks[i].id < sortedTracks[j].id }) sortex.Slice(sortedTracks, func(a, b *track) bool { return a.id < b.id })
d.FieldArray("tracks", func(d *decode.D) { d.FieldArray("tracks", func(d *decode.D) {
for _, t := range sortedTracks { for _, t := range sortedTracks {

View File

@ -14,13 +14,12 @@ import (
"html" "html"
"io" "io"
"regexp" "regexp"
"sort"
"strconv" "strconv"
"strings" "strings"
"github.com/wader/fq/format" "github.com/wader/fq/format"
"github.com/wader/fq/internal/gojqex" "github.com/wader/fq/internal/gojqex"
"github.com/wader/fq/internal/proxysort" "github.com/wader/fq/internal/sortex"
"github.com/wader/fq/internal/stringsex" "github.com/wader/fq/internal/stringsex"
"github.com/wader/fq/pkg/bitio" "github.com/wader/fq/pkg/bitio"
"github.com/wader/fq/pkg/decode" "github.com/wader/fq/pkg/decode"
@ -392,14 +391,12 @@ func toXMLFromObject(c any, opts ToXMLOpts) any {
// if one #seq was found, assume all have them, otherwise sort by name // if one #seq was found, assume all have them, otherwise sort by name
if orderHasSeq { if orderHasSeq {
proxysort.Sort(orderSeqs, n.Nodes, func(ss []int, i, j int) bool { return ss[i] < ss[j] }) sortex.ProxySort(orderSeqs, n.Nodes, func(a, b int) bool { return a < b })
} else { } else {
proxysort.Sort(orderNames, n.Nodes, func(ss []string, i, j int) bool { return ss[i] < ss[j] }) sortex.ProxySort(orderNames, n.Nodes, func(a, b string) bool { return a < b })
} }
sort.Slice(n.Attrs, func(i, j int) bool { sortex.Slice(n.Attrs, func(a, b xml.Attr) bool { return xmlNameSort(a.Name, b.Name) })
return xmlNameSort(n.Attrs[i].Name, n.Attrs[j].Name)
})
return n, seq, hasSeq return n, seq, hasSeq
} }
@ -472,9 +469,7 @@ func toXMLFromArray(c any, opts ToXMLOpts) any {
} }
} }
sort.Slice(n.Attrs, func(i, j int) bool { sortex.Slice(n.Attrs, func(a, b xml.Attr) bool { return xmlNameSort(a.Name, b.Name) })
return xmlNameSort(n.Attrs[i].Name, n.Attrs[j].Name)
})
for _, c := range children { for _, c := range children {
c, ok := c.([]any) c, ok := c.([]any)

View File

@ -34,9 +34,10 @@ import (
"io" "io"
"math" "math"
"math/big" "math/big"
"sort"
"strconv" "strconv"
"unicode/utf8" "unicode/utf8"
"github.com/wader/fq/internal/sortex"
) )
type Colors struct { type Colors struct {
@ -238,9 +239,7 @@ func (e *Encoder) encodeMap(vs map[string]any) {
kvs[i] = keyVal{k, v} kvs[i] = keyVal{k, v}
i++ i++
} }
sort.Slice(kvs, func(i, j int) bool { sortex.Slice(kvs, func(a, b keyVal) bool { return a.key < b.key })
return kvs[i].key < kvs[j].key
})
for i, kv := range kvs { for i, kv := range kvs {
if e.wErr != nil { if e.wErr != nil {
return return

View File

@ -1,26 +0,0 @@
// proxysort sorts one slice and but swaps index in two
// assumes both slices have same length.
package proxysort
import "sort"
type proxySort[Tae any, Ta []Tae, Tbe any, Tb []Tbe] struct {
a Ta
b Tb
fn func(Ta []Tae, i, j int) bool
}
func (p proxySort[Tae, Ta, Tbe, Tb]) Len() int { return len(p.a) }
func (p proxySort[Tae, Ta, Tbe, Tb]) Less(i, j int) bool { return p.fn(p.a, i, j) }
func (p proxySort[Tae, Ta, Tbe, Tb]) Swap(i, j int) {
p.a[i], p.a[j] = p.a[j], p.a[i]
p.b[i], p.b[j] = p.b[j], p.b[i]
}
func Sort[Tae any, Ta []Tae, Tbe any, Tb []Tbe](a Ta, b Tb, fn func(Ta []Tae, i, j int) bool) {
sort.Sort(proxySort[Tae, Ta, Tbe, Tb]{a: a, b: b, fn: fn})
}
func Stable[Tae any, Ta []Tae, Tbe any, Tb []Tbe](a Ta, b Tb, fn func(Ta []Tae, i, j int) bool) {
sort.Stable(proxySort[Tae, Ta, Tbe, Tb]{a: a, b: b, fn: fn})
}

View File

@ -10,11 +10,11 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"sort"
"strconv" "strconv"
"strings" "strings"
"github.com/wader/fq/internal/shquote" "github.com/wader/fq/internal/shquote"
"github.com/wader/fq/internal/sortex"
"github.com/wader/fq/pkg/bitio" "github.com/wader/fq/pkg/bitio"
"github.com/wader/fq/pkg/interp" "github.com/wader/fq/pkg/interp"
) )
@ -273,9 +273,7 @@ type Case struct {
func (c *Case) ToActual() string { func (c *Case) ToActual() string {
var partsLineSorted []part var partsLineSorted []part
partsLineSorted = append(partsLineSorted, c.Parts...) partsLineSorted = append(partsLineSorted, c.Parts...)
sort.Slice(partsLineSorted, func(i, j int) bool { sortex.Slice(partsLineSorted, func(a, b part) bool { return a.Line() < b.Line() })
return partsLineSorted[i].Line() < partsLineSorted[j].Line()
})
sb := &strings.Builder{} sb := &strings.Builder{}
for _, p := range partsLineSorted { for _, p := range partsLineSorted {

View File

@ -0,0 +1,28 @@
package sortex
import "sort"
type proxySort[Tae any, Ta []Tae, Tbe any, Tb []Tbe] struct {
a Ta
b Tb
fn func(a, b Tae) bool
}
func (p proxySort[Tae, Ta, Tbe, Tb]) Len() int { return len(p.a) }
func (p proxySort[Tae, Ta, Tbe, Tb]) Less(i, j int) bool { return p.fn(p.a[i], p.a[j]) }
func (p proxySort[Tae, Ta, Tbe, Tb]) Swap(i, j int) {
p.a[i], p.a[j] = p.a[j], p.a[i]
p.b[i], p.b[j] = p.b[j], p.b[i]
}
// ProxySort same as sort.Sort but is type safe, swaps an additional slice b and also does indexing
// Assumes both slices have same length.
func ProxySort[Tae any, Ta []Tae, Tbe any, Tb []Tbe](a Ta, b Tb, fn func(a, b Tae) bool) {
sort.Sort(proxySort[Tae, Ta, Tbe, Tb]{a: a, b: b, fn: fn})
}
// ProxyStable same as sort.Proxy but is type safe, swaps an additional slice b and also does indexing
// Assumes both slices have same length.
func ProxyStable[Tae any, Ta []Tae, Tbe any, Tb []Tbe](a Ta, b Tb, fn func(a, b Tae) bool) {
sort.Stable(proxySort[Tae, Ta, Tbe, Tb]{a: a, b: b, fn: fn})
}

13
internal/sortex/sort.go Normal file
View File

@ -0,0 +1,13 @@
package sortex
import "sort"
// Slice same as sort.Slice but type safe and also indexes for you
func Slice[T any](s []T, less func(a, b T) bool) {
sort.Slice(s, func(i, j int) bool { return less(s[i], s[j]) })
}
// SliceStable same as sort.SliceStable but type safe and also indexes for you
func SliceStable[T any](s []T, less func(a, b T) bool) {
sort.SliceStable(s, func(i, j int) bool { return less(s[i], s[j]) })
}

View File

@ -2,8 +2,8 @@ package decode
import ( import (
"errors" "errors"
"sort"
"github.com/wader/fq/internal/sortex"
"github.com/wader/fq/pkg/bitio" "github.com/wader/fq/pkg/bitio"
"github.com/wader/fq/pkg/ranges" "github.com/wader/fq/pkg/ranges"
"github.com/wader/fq/pkg/scalar" "github.com/wader/fq/pkg/scalar"
@ -200,9 +200,7 @@ func (v *Value) postProcess() {
// TODO: really sort array? if sort it needs to be stable to keep the order // TODO: really sort array? if sort it needs to be stable to keep the order
// of value with same range start, think null values etc // of value with same range start, think null values etc
if vv.RangeSorted { if vv.RangeSorted {
sort.SliceStable(vv.Children, func(i, j int) bool { sortex.SliceStable(vv.Children, func(a, b *Value) bool { return a.Range.Start < b.Range.Start })
return (vv.Children)[i].Range.Start < (vv.Children)[j].Range.Start
})
} }
v.Index = -1 v.Index = -1

View File

@ -4,10 +4,10 @@ import (
"errors" "errors"
"fmt" "fmt"
"io/fs" "io/fs"
"sort"
"sync" "sync"
"github.com/wader/fq/internal/gojqex" "github.com/wader/fq/internal/gojqex"
"github.com/wader/fq/internal/sortex"
"github.com/wader/fq/pkg/decode" "github.com/wader/fq/pkg/decode"
) )
@ -67,11 +67,11 @@ func (r *Registry) Func(funcFn EnvFuncFn) {
} }
func sortFormats(g decode.Group) { func sortFormats(g decode.Group) {
sort.Slice(g, func(i, j int) bool { sortex.Slice(g, func(a, b decode.Format) bool {
if g[i].ProbeOrder == g[j].ProbeOrder { if a.ProbeOrder == b.ProbeOrder {
return g[i].Name < g[j].Name return a.Name < b.Name
} }
return g[i].ProbeOrder < g[j].ProbeOrder return a.ProbeOrder < b.ProbeOrder
}) })
} }

View File

@ -2,9 +2,10 @@ package ranges
import ( import (
"fmt" "fmt"
"sort"
"strconv" "strconv"
"strings" "strings"
"github.com/wader/fq/internal/sortex"
) )
func max(a, b int64) int64 { func max(a, b int64) int64 {
@ -62,9 +63,7 @@ func Gaps(total Range, ranges []Range) []Range {
return []Range{total} return []Range{total}
} }
sort.Slice(ranges, func(i, j int) bool { sortex.Slice(ranges, func(a, b Range) bool { return a.Start < b.Start })
return ranges[i].Start < ranges[j].Start
})
// worst case ranges+1 gaps // worst case ranges+1 gaps
merged := make([]Range, 0, len(ranges)+1) merged := make([]Range, 0, len(ranges)+1)