Cleanup sorting logic

There was an unused error value as well as unnecessary usage of the sort
interface. There should now be less copying so a bit better performance
in some cases.
This commit is contained in:
Jeffas 2020-02-29 01:27:31 +00:00 committed by Drew DeVault
parent 583b129c94
commit e8b7b3bcc1
1 changed files with 55 additions and 155 deletions

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"sort" "sort"
"strings" "strings"
"time"
"git.sr.ht/~sircmpwn/aerc/models" "git.sr.ht/~sircmpwn/aerc/models"
"git.sr.ht/~sircmpwn/aerc/worker/types" "git.sr.ht/~sircmpwn/aerc/worker/types"
@ -15,51 +14,44 @@ func Sort(messageInfos []*models.MessageInfo,
// loop through in reverse to ensure we sort by non-primary fields first // loop through in reverse to ensure we sort by non-primary fields first
for i := len(criteria) - 1; i >= 0; i-- { for i := len(criteria) - 1; i >= 0; i-- {
criterion := criteria[i] criterion := criteria[i]
var err error
switch criterion.Field { switch criterion.Field {
case types.SortArrival: case types.SortArrival:
err = sortDate(messageInfos, criterion, sortSlice(criterion, messageInfos, func(i, j int) bool {
func(msgInfo *models.MessageInfo) time.Time { return messageInfos[i].InternalDate.Before(messageInfos[j].InternalDate)
return msgInfo.InternalDate })
})
case types.SortCc: case types.SortCc:
err = sortAddresses(messageInfos, criterion, sortAddresses(messageInfos, criterion,
func(msgInfo *models.MessageInfo) []*models.Address { func(msgInfo *models.MessageInfo) []*models.Address {
return msgInfo.Envelope.Cc return msgInfo.Envelope.Cc
}) })
case types.SortDate: case types.SortDate:
err = sortDate(messageInfos, criterion, sortSlice(criterion, messageInfos, func(i, j int) bool {
func(msgInfo *models.MessageInfo) time.Time { return messageInfos[i].Envelope.Date.Before(messageInfos[j].Envelope.Date)
return msgInfo.Envelope.Date })
})
case types.SortFrom: case types.SortFrom:
err = sortAddresses(messageInfos, criterion, sortAddresses(messageInfos, criterion,
func(msgInfo *models.MessageInfo) []*models.Address { func(msgInfo *models.MessageInfo) []*models.Address {
return msgInfo.Envelope.From return msgInfo.Envelope.From
}) })
case types.SortRead: case types.SortRead:
err = sortFlags(messageInfos, criterion, models.SeenFlag) sortFlags(messageInfos, criterion, models.SeenFlag)
case types.SortSize: case types.SortSize:
err = sortInts(messageInfos, criterion, sortSlice(criterion, messageInfos, func(i, j int) bool {
func(msgInfo *models.MessageInfo) uint32 { return messageInfos[i].Size < messageInfos[j].Size
return msgInfo.Size })
})
case types.SortSubject: case types.SortSubject:
err = sortStrings(messageInfos, criterion, sortStrings(messageInfos, criterion,
func(msgInfo *models.MessageInfo) string { func(msgInfo *models.MessageInfo) string {
subject := strings.ToLower(msgInfo.Envelope.Subject) subject := strings.ToLower(msgInfo.Envelope.Subject)
subject = strings.TrimPrefix(subject, "re: ") subject = strings.TrimPrefix(subject, "re: ")
return strings.TrimPrefix(subject, "fwd: ") return strings.TrimPrefix(subject, "fwd: ")
}) })
case types.SortTo: case types.SortTo:
err = sortAddresses(messageInfos, criterion, sortAddresses(messageInfos, criterion,
func(msgInfo *models.MessageInfo) []*models.Address { func(msgInfo *models.MessageInfo) []*models.Address {
return msgInfo.Envelope.To return msgInfo.Envelope.To
}) })
} }
if err != nil {
return nil, err
}
} }
var uids []uint32 var uids []uint32
// copy in reverse as msgList displays backwards // copy in reverse as msgList displays backwards
@ -69,40 +61,38 @@ func Sort(messageInfos []*models.MessageInfo,
return uids, nil return uids, nil
} }
func sortDate(messageInfos []*models.MessageInfo, criterion *types.SortCriterion,
getValue func(*models.MessageInfo) time.Time) error {
var slice []*dateStore
for _, msgInfo := range messageInfos {
slice = append(slice, &dateStore{
Value: getValue(msgInfo),
MsgInfo: msgInfo,
})
}
sortSlice(criterion, dateSlice{slice})
for i := 0; i < len(messageInfos); i++ {
messageInfos[i] = slice[i].MsgInfo
}
return nil
}
func sortAddresses(messageInfos []*models.MessageInfo, criterion *types.SortCriterion, func sortAddresses(messageInfos []*models.MessageInfo, criterion *types.SortCriterion,
getValue func(*models.MessageInfo) []*models.Address) error { getValue func(*models.MessageInfo) []*models.Address) {
var slice []*addressStore sortSlice(criterion, messageInfos, func(i, j int) bool {
for _, msgInfo := range messageInfos { addressI, addressJ := getValue(messageInfos[i]), getValue(messageInfos[j])
slice = append(slice, &addressStore{ var firstI, firstJ *models.Address
Value: getValue(msgInfo), if len(addressI) > 0 {
MsgInfo: msgInfo, firstI = addressI[0]
}) }
} if len(addressJ) > 0 {
sortSlice(criterion, addressSlice{slice}) firstJ = addressJ[0]
for i := 0; i < len(messageInfos); i++ { }
messageInfos[i] = slice[i].MsgInfo if firstI == nil && firstJ == nil {
} return false
return nil } else if firstI == nil && firstJ != nil {
return false
} else if firstI != nil && firstJ == nil {
return true
} else /* firstI != nil && firstJ != nil */ {
getName := func(addr *models.Address) string {
if addr.Name != "" {
return addr.Name
} else {
return fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host)
}
}
return getName(firstI) < getName(firstJ)
}
})
} }
func sortFlags(messageInfos []*models.MessageInfo, criterion *types.SortCriterion, func sortFlags(messageInfos []*models.MessageInfo, criterion *types.SortCriterion,
testFlag models.Flag) error { testFlag models.Flag) {
var slice []*boolStore var slice []*boolStore
for _, msgInfo := range messageInfos { for _, msgInfo := range messageInfos {
flagPresent := false flagPresent := false
@ -116,31 +106,17 @@ func sortFlags(messageInfos []*models.MessageInfo, criterion *types.SortCriterio
MsgInfo: msgInfo, MsgInfo: msgInfo,
}) })
} }
sortSlice(criterion, boolSlice{slice}) sortSlice(criterion, slice, func(i, j int) bool {
valI, valJ := slice[i].Value, slice[j].Value
return valI && !valJ
})
for i := 0; i < len(messageInfos); i++ { for i := 0; i < len(messageInfos); i++ {
messageInfos[i] = slice[i].MsgInfo messageInfos[i] = slice[i].MsgInfo
} }
return nil
}
func sortInts(messageInfos []*models.MessageInfo, criterion *types.SortCriterion,
getValue func(*models.MessageInfo) uint32) error {
var slice []*intStore
for _, msgInfo := range messageInfos {
slice = append(slice, &intStore{
Value: getValue(msgInfo),
MsgInfo: msgInfo,
})
}
sortSlice(criterion, intSlice{slice})
for i := 0; i < len(messageInfos); i++ {
messageInfos[i] = slice[i].MsgInfo
}
return nil
} }
func sortStrings(messageInfos []*models.MessageInfo, criterion *types.SortCriterion, func sortStrings(messageInfos []*models.MessageInfo, criterion *types.SortCriterion,
getValue func(*models.MessageInfo) string) error { getValue func(*models.MessageInfo) string) {
var slice []*lexiStore var slice []*lexiStore
for _, msgInfo := range messageInfos { for _, msgInfo := range messageInfos {
slice = append(slice, &lexiStore{ slice = append(slice, &lexiStore{
@ -148,11 +124,12 @@ func sortStrings(messageInfos []*models.MessageInfo, criterion *types.SortCriter
MsgInfo: msgInfo, MsgInfo: msgInfo,
}) })
} }
sortSlice(criterion, lexiSlice{slice}) sortSlice(criterion, slice, func(i, j int) bool {
return slice[i].Value < slice[j].Value
})
for i := 0; i < len(messageInfos); i++ { for i := 0; i < len(messageInfos); i++ {
messageInfos[i] = slice[i].MsgInfo messageInfos[i] = slice[i].MsgInfo
} }
return nil
} }
type lexiStore struct { type lexiStore struct {
@ -160,94 +137,17 @@ type lexiStore struct {
MsgInfo *models.MessageInfo MsgInfo *models.MessageInfo
} }
type lexiSlice struct{ Slice []*lexiStore }
func (s lexiSlice) Len() int { return len(s.Slice) }
func (s lexiSlice) Swap(i, j int) { s.Slice[i], s.Slice[j] = s.Slice[j], s.Slice[i] }
func (s lexiSlice) Less(i, j int) bool {
return s.Slice[i].Value < s.Slice[j].Value
}
type dateStore struct {
Value time.Time
MsgInfo *models.MessageInfo
}
type dateSlice struct{ Slice []*dateStore }
func (s dateSlice) Len() int { return len(s.Slice) }
func (s dateSlice) Swap(i, j int) { s.Slice[i], s.Slice[j] = s.Slice[j], s.Slice[i] }
func (s dateSlice) Less(i, j int) bool {
return s.Slice[i].Value.Before(s.Slice[j].Value)
}
type intStore struct {
Value uint32
MsgInfo *models.MessageInfo
}
type intSlice struct{ Slice []*intStore }
func (s intSlice) Len() int { return len(s.Slice) }
func (s intSlice) Swap(i, j int) { s.Slice[i], s.Slice[j] = s.Slice[j], s.Slice[i] }
func (s intSlice) Less(i, j int) bool {
return s.Slice[i].Value < s.Slice[j].Value
}
type addressStore struct {
Value []*models.Address
MsgInfo *models.MessageInfo
}
type addressSlice struct{ Slice []*addressStore }
func (s addressSlice) Len() int { return len(s.Slice) }
func (s addressSlice) Swap(i, j int) { s.Slice[i], s.Slice[j] = s.Slice[j], s.Slice[i] }
func (s addressSlice) Less(i, j int) bool {
addressI, addressJ := s.Slice[i].Value, s.Slice[j].Value
var firstI, firstJ *models.Address
if len(addressI) > 0 {
firstI = addressI[0]
}
if len(addressJ) > 0 {
firstJ = addressJ[0]
}
if firstI == nil && firstJ == nil {
return false
} else if firstI == nil && firstJ != nil {
return false
} else if firstI != nil && firstJ == nil {
return true
} else /* firstI != nil && firstJ != nil */ {
getName := func(addr *models.Address) string {
if addr.Name != "" {
return addr.Name
} else {
return fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host)
}
}
return getName(firstI) < getName(firstJ)
}
}
type boolStore struct { type boolStore struct {
Value bool Value bool
MsgInfo *models.MessageInfo MsgInfo *models.MessageInfo
} }
type boolSlice struct{ Slice []*boolStore } func sortSlice(criterion *types.SortCriterion, slice interface{}, less func(i, j int) bool) {
func (s boolSlice) Len() int { return len(s.Slice) }
func (s boolSlice) Swap(i, j int) { s.Slice[i], s.Slice[j] = s.Slice[j], s.Slice[i] }
func (s boolSlice) Less(i, j int) bool {
valI, valJ := s.Slice[i].Value, s.Slice[j].Value
return valI && !valJ
}
func sortSlice(criterion *types.SortCriterion, interfce sort.Interface) {
if criterion.Reverse { if criterion.Reverse {
sort.Stable(sort.Reverse(interfce)) sort.SliceStable(slice, func(i, j int) bool {
return less(j, i)
})
} else { } else {
sort.Stable(interfce) sort.SliceStable(slice, less)
} }
} }