aerc/worker/imap/seqmap.go
Tim Culverhouse a0a5ba1538 imap: create copy of uids to retain sort order
Commit fdfec2c07a seqmap: refactor seqmap to use slice instead of map
introduced a regression to imap sorting (if supported). The slice passed
to seqmap was being sorted in place by UID, thus breaking any sort order
sent by the server.

Create a copy of the slice to retain the sort order reported to the UI
while also keeping a correct map for seqnum -> UID in the worker.

Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
2022-08-22 15:46:49 +02:00

78 lines
1.5 KiB
Go

package imap
import (
"sort"
"sync"
)
type SeqMap struct {
lock sync.Mutex
// map of IMAP sequence numbers to message UIDs
m []uint32
}
// Initialize sets the initial seqmap of the mailbox
func (s *SeqMap) Initialize(uids []uint32) {
s.lock.Lock()
s.m = make([]uint32, len(uids))
copy(s.m, uids)
s.sort()
s.lock.Unlock()
}
func (s *SeqMap) Size() int {
s.lock.Lock()
size := len(s.m)
s.lock.Unlock()
return size
}
// Get returns the UID of the given seqnum
func (s *SeqMap) Get(seqnum uint32) (uint32, bool) {
if int(seqnum) > s.Size() || seqnum < 1 {
return 0, false
}
s.lock.Lock()
uid := s.m[seqnum-1]
s.lock.Unlock()
return uid, true
}
// Put adds a UID to the slice. Put should only be used to add new messages
// into the slice
func (s *SeqMap) Put(uid uint32) {
s.lock.Lock()
for _, n := range s.m {
if n == uid {
// We already have this UID, don't insert it.
s.lock.Unlock()
return
}
}
s.m = append(s.m, uid)
s.sort()
s.lock.Unlock()
}
// Pop removes seqnum from the SeqMap. seqnum must be a valid seqnum, ie
// [1:size of mailbox]
func (s *SeqMap) Pop(seqnum uint32) (uint32, bool) {
if int(seqnum) > s.Size() || seqnum < 1 {
return 0, false
}
s.lock.Lock()
uid := s.m[seqnum-1]
s.m = append(s.m[:seqnum-1], s.m[seqnum:]...)
s.lock.Unlock()
return uid, true
}
// sort sorts the slice in ascending UID order. See:
// https://datatracker.ietf.org/doc/html/rfc3501#section-2.3.1.2
func (s *SeqMap) sort() {
// Always be sure the SeqMap is sorted
sort.Slice(s.m, func(i, j int) bool {
return s.m[i] < s.m[j]
})
}