util/uniq: use generics instead of reflect (#5491)
This takes 75% less time per operation per some benchmarks on my mac. Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
This commit is contained in:
@ -6,60 +6,30 @@
|
||||
// It is similar to the unix command uniq.
|
||||
package uniq
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type badTypeError struct {
|
||||
typ reflect.Type
|
||||
}
|
||||
|
||||
func (e badTypeError) Error() string {
|
||||
return fmt.Sprintf("uniq.ModifySlice's first argument must have type *[]T, got %v", e.typ)
|
||||
}
|
||||
|
||||
// ModifySlice removes adjacent duplicate elements from the slice pointed to by sliceptr.
|
||||
// It adjusts the length of the slice appropriately and zeros the tail.
|
||||
// eq reports whether (*sliceptr)[i] and (*sliceptr)[j] are equal.
|
||||
// ModifySlice does O(len(*sliceptr)) operations.
|
||||
func ModifySlice(sliceptr any, eq func(i, j int) bool) {
|
||||
rvp := reflect.ValueOf(sliceptr)
|
||||
if rvp.Type().Kind() != reflect.Ptr {
|
||||
panic(badTypeError{rvp.Type()})
|
||||
}
|
||||
rv := rvp.Elem()
|
||||
if rv.Type().Kind() != reflect.Slice {
|
||||
panic(badTypeError{rvp.Type()})
|
||||
}
|
||||
|
||||
length := rv.Len()
|
||||
// ModifySlice removes adjacent duplicate elements from the given slice. It
|
||||
// adjusts the length of the slice appropriately and zeros the tail.
|
||||
//
|
||||
// ModifySlice does O(len(*slice)) operations.
|
||||
func ModifySlice[E comparable](slice *[]E) {
|
||||
// Remove duplicates
|
||||
dst := 0
|
||||
for i := 1; i < length; i++ {
|
||||
if eq(dst, i) {
|
||||
for i := 1; i < len(*slice); i++ {
|
||||
if (*slice)[i] == (*slice)[dst] {
|
||||
continue
|
||||
}
|
||||
dst++
|
||||
// slice[dst] = slice[i]
|
||||
rv.Index(dst).Set(rv.Index(i))
|
||||
(*slice)[dst] = (*slice)[i]
|
||||
}
|
||||
|
||||
// Zero out the elements we removed at the end of the slice
|
||||
end := dst + 1
|
||||
var zero reflect.Value
|
||||
if end < length {
|
||||
zero = reflect.Zero(rv.Type().Elem())
|
||||
var zero E
|
||||
for i := end; i < len(*slice); i++ {
|
||||
(*slice)[i] = zero
|
||||
}
|
||||
|
||||
// for i := range slice[end:] {
|
||||
// size[i] = 0/nil/{}
|
||||
// }
|
||||
for i := end; i < length; i++ {
|
||||
// slice[i] = 0/nil/{}
|
||||
rv.Index(i).Set(zero)
|
||||
}
|
||||
|
||||
// slice = slice[:end]
|
||||
if end < length {
|
||||
rv.SetLen(end)
|
||||
// Truncate the slice
|
||||
if end < len(*slice) {
|
||||
*slice = (*slice)[:end]
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user