Skip to content

Commit 3ce5a4b

Browse files
committed
refactor: improve merge logic
1 parent 71f5ba0 commit 3ce5a4b

1 file changed

Lines changed: 131 additions & 45 deletions

File tree

imap-types/src/sequence.rs

Lines changed: 131 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{
22
cmp::{Ordering, max},
3-
collections::VecDeque,
3+
collections::{HashSet, VecDeque},
44
fmt::Debug,
55
iter::Rev,
66
mem,
@@ -37,44 +37,71 @@ pub struct SequenceSet(pub Vec1<Sequence>);
3737

3838
impl SequenceSet {
3939
pub fn normalize(&mut self) -> &mut Self {
40+
let mut singles = HashSet::with_capacity(self.0.0.len());
41+
let mut ranges = Vec::with_capacity(self.0.0.len());
42+
43+
// First, normalize all sequences
4044
for seq in &mut self.0.0 {
41-
seq.normalize();
45+
match seq.normalize() {
46+
Sequence::Single(id) => {
47+
singles.insert(id.to_non_zero_u32());
48+
// Push a 1-lenth range so they can merge nicely
49+
// later on
50+
ranges.push(id.to_non_zero_u32()..id.to_non_zero_u32().saturating_add(1));
51+
}
52+
Sequence::Range(a, b) => {
53+
ranges.push(a.to_non_zero_u32()..b.to_non_zero_u32().saturating_add(1));
54+
}
55+
}
4256
}
4357

44-
let set = &mut self.0.0;
45-
46-
if set.len() == 1 {
47-
return self;
58+
// TODO: Improve this loop, for eg. with a .retain().
59+
for single in singles.clone() {
60+
for range in &mut ranges {
61+
if single.get() == range.start.get().max(2) - 1 {
62+
singles.remove(&single);
63+
range.start = single;
64+
} else if single.get() == range.end.get() {
65+
singles.remove(&single);
66+
range.end = single;
67+
} else if range.contains(&single) {
68+
singles.remove(&single);
69+
}
70+
}
4871
}
4972

50-
set.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
51-
52-
let mut a = 0;
53-
let mut b = 1;
54-
55-
while b < set.len() {
56-
if set[a].partial_cmp(&set[b]).is_some() {
57-
a += 1;
58-
b += 1;
59-
continue;
60-
}
73+
// Rebuild the inner sequence set with merged ranges
74+
self.0.0.clear();
75+
for range in merge_ranges(ranges) {
76+
singles.retain(|single| !range.contains(single));
6177

62-
match (&set[a], &set[b]) {
63-
(Sequence::Single(_), _) => {
64-
set.remove(a);
78+
match (range.start, range.end) {
79+
(a, b) if a == b || a.saturating_add(1) == b => {
80+
let a = NonZeroU32::new(a.get()).unwrap();
81+
self.0.0.push(Sequence::Single(a.into()));
6582
}
66-
(Sequence::Range(_, _), Sequence::Single(_)) => {
67-
set.remove(b);
83+
(a, NonZeroU32::MAX) => {
84+
self.0.0.push(Sequence::Range(a.into(), SeqOrUid::Asterisk))
6885
}
69-
(Sequence::Range(a1, a2), Sequence::Range(b1, b2)) => {
70-
let min = a1.clone().min(a2.clone()).min(b1.clone()).min(b2.clone());
71-
let max = a1.clone().max(a2.clone()).max(b1.clone()).max(b2.clone());
72-
let _ = mem::replace(&mut set[a], Sequence::Range(min, max));
73-
set.remove(b);
86+
(a, b) => {
87+
let b = NonZeroU32::new(b.get().max(2) - 1).unwrap();
88+
self.0.0.push(Sequence::Range(a.into(), b.into()));
7489
}
7590
}
7691
}
7792

93+
// Add remaining singles that don't belong to any range to the
94+
// sequence set
95+
for single in singles {
96+
self.0.0.push(single.into());
97+
}
98+
99+
// Sort the merged sequence set
100+
self.0.0.sort_by_key(|seq| match seq {
101+
Sequence::Single(a) => a.to_non_zero_u32(),
102+
Sequence::Range(a, _) => a.to_non_zero_u32(),
103+
});
104+
78105
self
79106
}
80107
}
@@ -188,22 +215,17 @@ pub enum Sequence {
188215
impl Sequence {
189216
pub fn normalize(&mut self) -> &mut Self {
190217
match self {
191-
Sequence::Single(SeqOrUid::Asterisk) => {
192-
let begin = NonZeroU32::new(1).unwrap();
193-
let range = Sequence::Range(begin.into(), SeqOrUid::Asterisk);
194-
let _ = mem::replace(self, range);
195-
}
196-
Sequence::Range(SeqOrUid::Value(a), SeqOrUid::Value(b)) if *a == *b => {
197-
let range = Sequence::Single(a.clone().into());
218+
Sequence::Range(a, b) if *a == *b => {
219+
let range = Sequence::Single((*a).into());
198220
let _ = mem::replace(self, range);
199221
}
200-
Sequence::Range(SeqOrUid::Value(a), SeqOrUid::Value(b)) if *a > *b => {
222+
Sequence::Range(a, b) if *a > *b => {
201223
mem::swap(a, b);
202224
}
203225
_ => {
204226
// already normalized
205227
}
206-
}
228+
};
207229

208230
self
209231
}
@@ -302,13 +324,22 @@ pub enum SeqOrUid {
302324
Asterisk,
303325
}
304326

327+
impl SeqOrUid {
328+
pub fn to_non_zero_u32(&self) -> NonZeroU32 {
329+
match self {
330+
SeqOrUid::Value(n) => *n,
331+
SeqOrUid::Asterisk => NonZeroU32::MAX,
332+
}
333+
}
334+
}
335+
305336
impl Ord for SeqOrUid {
306337
fn cmp(&self, other: &Self) -> Ordering {
307338
match (self, other) {
308-
(SeqOrUid::Asterisk, SeqOrUid::Asterisk) => Ordering::Equal,
309-
(_, SeqOrUid::Asterisk) => Ordering::Greater,
310-
(SeqOrUid::Asterisk, _) => Ordering::Less,
311339
(SeqOrUid::Value(a), SeqOrUid::Value(b)) => a.cmp(b),
340+
(SeqOrUid::Asterisk, SeqOrUid::Asterisk) => Ordering::Equal,
341+
(_, SeqOrUid::Asterisk) => Ordering::Less,
342+
(SeqOrUid::Asterisk, _) => Ordering::Greater,
312343
}
313344
}
314345
}
@@ -321,7 +352,11 @@ impl PartialOrd for SeqOrUid {
321352

322353
impl From<NonZeroU32> for SeqOrUid {
323354
fn from(value: NonZeroU32) -> Self {
324-
Self::Value(value)
355+
if value == NonZeroU32::MAX {
356+
Self::Asterisk
357+
} else {
358+
Self::Value(value)
359+
}
325360
}
326361
}
327362

@@ -742,6 +777,28 @@ fn cleanup(remaining: VecDeque<(u32, u32)>) -> VecDeque<(u32, u32)> {
742777
stack
743778
}
744779

780+
fn merge_ranges<T: Ord + Copy>(mut ranges: Vec<Range<T>>) -> Vec<Range<T>> {
781+
if ranges.is_empty() {
782+
return ranges;
783+
}
784+
785+
ranges.sort_unstable_by_key(|r| r.start);
786+
787+
let mut merged = Vec::with_capacity(ranges.len());
788+
merged.push(ranges[0].clone());
789+
790+
for range in ranges.into_iter().skip(1) {
791+
let last = merged.last_mut().unwrap();
792+
if range.start < last.end {
793+
last.end = last.end.max(range.end);
794+
} else {
795+
merged.push(range);
796+
}
797+
}
798+
799+
merged
800+
}
801+
745802
#[cfg(test)]
746803
mod tests {
747804
use std::num::NonZeroU32;
@@ -1022,12 +1079,40 @@ mod tests {
10221079
}
10231080
}
10241081

1082+
#[test]
1083+
fn ordering_sequence() {
1084+
let tests = vec![
1085+
("1", "1", Some(Ordering::Equal)),
1086+
("1", "2", Some(Ordering::Less)),
1087+
("2", "1", Some(Ordering::Greater)),
1088+
("1", "2:4", Some(Ordering::Less)),
1089+
("2", "2:4", None),
1090+
("4", "2:4", None),
1091+
("7", "2:4", Some(Ordering::Greater)),
1092+
("2:4", "1", Some(Ordering::Greater)),
1093+
("2:4", "2", None),
1094+
("2:4", "4", None),
1095+
("2:4", "7", Some(Ordering::Less)),
1096+
("1:2", "3:4", Some(Ordering::Less)),
1097+
("3:4", "1:2", Some(Ordering::Greater)),
1098+
("1:2", "2:4", None),
1099+
("2:4", "3:8", None),
1100+
];
1101+
1102+
for (a, b, expected) in tests {
1103+
let a = Sequence::try_from(a).unwrap();
1104+
let b = Sequence::try_from(b).unwrap();
1105+
let got = a.partial_cmp(&b);
1106+
1107+
assert_eq!(expected, got);
1108+
}
1109+
}
1110+
10251111
#[test]
10261112
fn normalize_sequence() {
10271113
let tests = vec![
1028-
("*", "1:*"),
10291114
("1:*", "1:*"),
1030-
("*:1", "*:1"),
1115+
("*:1", "1:*"),
10311116
("1", "1"),
10321117
("1:1", "1"),
10331118
("1:2", "1:2"),
@@ -1045,16 +1130,17 @@ mod tests {
10451130
#[test]
10461131
fn normalize_sequence_set() {
10471132
let tests = [
1048-
("1,2,3,4", "1,2,3,4"),
1049-
("3,1,2,4", "1,2,3,4"),
1133+
("1,2,3,4", "1:4"),
1134+
("4,1,3,2", "1:4"),
1135+
("3,1,2,4", "1:4"),
10501136
("3:1,5", "1:3,5"),
10511137
("5,3:1", "1:3,5"),
10521138
("3,3:1", "1:3"),
10531139
("3:1,1", "1:3"),
10541140
("3:1,2:5", "1:5"),
10551141
("3:1,4:9", "1:3,4:9"),
10561142
("9:4,3:1", "1:3,4:9"),
1057-
("8:10,3:1,2:5,9", "1:5,8:10"),
1143+
("8:10,12,3:1,2:5,9", "1:5,8:10,12"),
10581144
];
10591145

10601146
for (test, expected) in tests {

0 commit comments

Comments
 (0)