Skip to content

Commit

Permalink
Preserve sort order of Go 1.17 implementation (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
shawntoffel committed Oct 14, 2023
1 parent 16a053b commit 45f7bc4
Show file tree
Hide file tree
Showing 6 changed files with 1,020 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
go-version: "1.21"

- name: Build
run: |
Expand Down
5 changes: 2 additions & 3 deletions pool.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package meekstv

import (
"sort"

"github.com/shawntoffel/election"
"github.com/shawntoffel/meekstv/sort_1_17"
)

type Pool struct {
Expand Down Expand Up @@ -119,7 +118,7 @@ func (p *Pool) ExcludeHopeful() {

func (p *Pool) Lowest() MeekCandidates {
candidates := p.Candidates()
sort.Sort(ByVotes(candidates))
sort_1_17.Sort(ByVotes(candidates))

lowest := MeekCandidates{}

Expand Down
127 changes: 127 additions & 0 deletions sort_1_17/genzfunc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright 2016 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.

//go:build ignore
// +build ignore

// This program is run via "go generate" (via a directive in sort.go)
// to generate zfuncversion.go.
//
// It copies sort.go to zfuncversion.go, only retaining funcs which
// take a "data Interface" parameter, and renaming each to have a
// "_func" suffix and taking a "data lessSwap" instead. It then rewrites
// each internal function call to the appropriate _func variants.

package main

import (
"bytes"
"go/ast"
"go/format"
"go/parser"
"go/token"
"log"
"os"
"regexp"
)

var fset = token.NewFileSet()

func main() {
af, err := parser.ParseFile(fset, "sort.go", nil, 0)
if err != nil {
log.Fatal(err)
}
af.Doc = nil
af.Imports = nil
af.Comments = nil

var newDecl []ast.Decl
for _, d := range af.Decls {
fd, ok := d.(*ast.FuncDecl)
if !ok {
continue
}
if fd.Recv != nil || fd.Name.IsExported() {
continue
}
typ := fd.Type
if len(typ.Params.List) < 1 {
continue
}
arg0 := typ.Params.List[0]
arg0Name := arg0.Names[0].Name
arg0Type := arg0.Type.(*ast.Ident)
if arg0Name != "data" || arg0Type.Name != "Interface" {
continue
}
arg0Type.Name = "lessSwap"

newDecl = append(newDecl, fd)
}
af.Decls = newDecl
ast.Walk(visitFunc(rewriteCalls), af)

var out bytes.Buffer
if err := format.Node(&out, fset, af); err != nil {
log.Fatalf("format.Node: %v", err)
}

// Get rid of blank lines after removal of comments.
src := regexp.MustCompile(`\n{2,}`).ReplaceAll(out.Bytes(), []byte("\n"))

// Add comments to each func, for the lost reader.
// This is so much easier than adding comments via the AST
// and trying to get position info correct.
src = regexp.MustCompile(`(?m)^func (\w+)`).ReplaceAll(src, []byte("\n// Auto-generated variant of sort.go:$1\nfunc ${1}_func"))

// Final gofmt.
src, err = format.Source(src)
if err != nil {
log.Fatalf("format.Source: %v on\n%s", err, src)
}

out.Reset()
out.WriteString(`// Code generated from sort.go using genzfunc.go; DO NOT EDIT.
// Copyright 2016 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.
`)
out.Write(src)

const target = "zfuncversion.go"
if err := os.WriteFile(target, out.Bytes(), 0644); err != nil {
log.Fatal(err)
}
}

type visitFunc func(ast.Node) ast.Visitor

func (f visitFunc) Visit(n ast.Node) ast.Visitor { return f(n) }

func rewriteCalls(n ast.Node) ast.Visitor {
ce, ok := n.(*ast.CallExpr)
if ok {
rewriteCall(ce)
}
return visitFunc(rewriteCalls)
}

func rewriteCall(ce *ast.CallExpr) {
ident, ok := ce.Fun.(*ast.Ident)
if !ok {
// e.g. skip SelectorExpr (data.Less(..) calls)
return
}
// skip casts
if ident.Name == "int" || ident.Name == "uint" {
return
}
if len(ce.Args) < 1 {
return
}
ident.Name += "_func"
}
48 changes: 48 additions & 0 deletions sort_1_17/slice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2017 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 sort_1_17

import "reflect"

// Slice sorts the slice x given the provided less function.
// It panics if x is not a slice.
//
// The sort is not guaranteed to be stable: equal elements
// may be reversed from their original order.
// For a stable sort, use SliceStable.
//
// The less function must satisfy the same requirements as
// the Interface type's Less method.
func Slice(x interface{}, less func(i, j int) bool) {
rv := reflect.ValueOf(x)
swap := reflect.Swapper(x)
length := rv.Len()
quickSort_func(lessSwap{less, swap}, 0, length, maxDepth(length))
}

// SliceStable sorts the slice x using the provided less
// function, keeping equal elements in their original order.
// It panics if x is not a slice.
//
// The less function must satisfy the same requirements as
// the Interface type's Less method.
func SliceStable(x interface{}, less func(i, j int) bool) {
rv := reflect.ValueOf(x)
swap := reflect.Swapper(x)
stable_func(lessSwap{less, swap}, rv.Len())
}

// SliceIsSorted reports whether the slice x is sorted according to the provided less function.
// It panics if x is not a slice.
func SliceIsSorted(x interface{}, less func(i, j int) bool) bool {
rv := reflect.ValueOf(x)
n := rv.Len()
for i := n - 1; i > 0; i-- {
if less(i, i-1) {
return false
}
}
return true
}
Loading

0 comments on commit 45f7bc4

Please sign in to comment.