summaryrefslogtreecommitdiff
path: root/src/atom
diff options
context:
space:
mode:
authorJohn Turner <jturner.usa@gmail.com>2025-11-19 05:01:34 +0000
committerJohn Turner <jturner.usa@gmail.com>2025-11-19 05:04:40 +0000
commit0d40608404b878ebff9035990b754fa03f859c65 (patch)
treed283083a948ae4cdfe6dcd5e58cc9afda9266db1 /src/atom
parent8d3cf7c83d9c4fd7c2c4c5cd695ed6ccef6f76b0 (diff)
downloadgentoo-utils-0d40608404b878ebff9035990b754fa03f859c65.tar.gz
compare versions as strings rather than parsing them to ints
Parsing version numbers to u64s could cause an panic on int overflow with very large versions.
Diffstat (limited to 'src/atom')
-rw-r--r--src/atom/mod.rs94
1 files changed, 37 insertions, 57 deletions
diff --git a/src/atom/mod.rs b/src/atom/mod.rs
index 6b2b51f..cee9c29 100644
--- a/src/atom/mod.rs
+++ b/src/atom/mod.rs
@@ -185,6 +185,28 @@ impl Atom {
}
}
+impl VersionNumber {
+ #[must_use]
+ pub fn cmp_as_ints(&self, other: &Self) -> Ordering {
+ let a = self.get().trim_start_matches('0');
+ let b = other.get().trim_start_matches('0');
+
+ a.len().cmp(&b.len()).then_with(|| a.cmp(b))
+ }
+
+ #[must_use]
+ pub fn cmp_as_str(&self, other: &Self) -> Ordering {
+ if self.get().starts_with('0') || other.get().starts_with('0') {
+ let a = self.get().trim_end_matches('0');
+ let b = other.get().trim_end_matches('0');
+
+ a.cmp(b)
+ } else {
+ self.cmp_as_ints(other)
+ }
+ }
+}
+
impl PartialEq for VersionSuffix {
fn eq(&self, other: &Self) -> bool {
self.kind == other.kind
@@ -212,11 +234,7 @@ impl Ord for VersionSuffix {
Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,
Ordering::Equal => match (&self.number, &other.number) {
- (Some(a), Some(b)) => {
- a.0.parse::<u64>()
- .unwrap()
- .cmp(&b.0.parse::<u64>().unwrap())
- }
+ (Some(a), Some(b)) => a.cmp_as_ints(b),
(Some(a), None) if a.get().chars().all(|c| c == '0') => Ordering::Equal,
(None, Some(b)) if b.get().chars().all(|c| c == '0') => Ordering::Equal,
(Some(_), None) => Ordering::Greater,
@@ -276,26 +294,21 @@ impl Ord for VersionSuffixes {
impl PartialEq for VersionNumbers {
fn eq(&self, other: &Self) -> bool {
- self.get().first().unwrap().get().parse::<u64>().unwrap()
- == other.get().first().unwrap().get().parse().unwrap()
+ self.get()
+ .first()
+ .unwrap()
+ .cmp_as_ints(other.get().first().unwrap())
+ == Ordering::Equal
&& {
let mut a = self.get().iter().skip(1);
let mut b = other.get().iter().skip(1);
loop {
match (a.next(), b.next()) {
- (Some(a), Some(b)) if a.get().starts_with('0') => {
- let a = a.get().trim_end_matches('0');
- let b = b.get().trim_end_matches('0');
-
- if a != b {
- break false;
- }
- }
(Some(a), Some(b)) => {
- if a.get().parse::<u64>().unwrap() != b.get().parse::<u64>().unwrap() {
- break false;
- }
+ let Ordering::Equal = a.cmp_as_str(b) else {
+ return false;
+ };
}
(Some(a), None) if a.get().chars().all(|c| c == '0') => (),
(None, Some(b)) if b.get().chars().all(|c| c == '0') => (),
@@ -321,10 +334,7 @@ impl Ord for VersionNumbers {
.get()
.first()
.unwrap()
- .get()
- .parse::<u64>()
- .unwrap()
- .cmp(&other.get().first().unwrap().get().parse::<u64>().unwrap())
+ .cmp_as_ints(other.get().first().unwrap())
{
Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,
@@ -334,24 +344,7 @@ impl Ord for VersionNumbers {
loop {
match (a.next(), b.next()) {
- (Some(a), Some(b))
- if a.get().starts_with('0') || b.get().starts_with('0') =>
- {
- let a = a.get().trim_end_matches('0');
- let b = b.get().trim_end_matches('0');
-
- match a.cmp(b) {
- Ordering::Less => break Ordering::Less,
- Ordering::Greater => break Ordering::Greater,
- Ordering::Equal => (),
- }
- }
- (Some(a), Some(b)) => match a
- .get()
- .parse::<u64>()
- .unwrap()
- .cmp(&b.get().parse::<u64>().unwrap())
- {
+ (Some(a), Some(b)) => match a.cmp_as_str(b) {
Ordering::Less => break Ordering::Less,
Ordering::Greater => break Ordering::Greater,
Ordering::Equal => (),
@@ -373,18 +366,14 @@ impl PartialEq for Version {
&& self.suffixes == other.suffixes
&& self.letter == other.letter
&& match (&self.rev, &other.rev) {
- (Some(a), Some(b)) => {
- a.get().parse::<u64>().unwrap() == b.get().parse::<u64>().unwrap()
- }
+ (Some(a), Some(b)) => matches!(a.cmp_as_ints(b), Ordering::Equal),
(Some(a), None) if a.get().chars().all(|c| c == '0') => true,
(None, Some(b)) if b.get().chars().all(|c| c == '0') => true,
(Some(_), None) | (None, Some(_)) => false,
(None, None) => true,
}
&& match (&self.build_id, &other.build_id) {
- (Some(a), Some(b)) => {
- a.get().parse::<u64>().unwrap() == b.get().parse::<u64>().unwrap()
- }
+ (Some(a), Some(b)) => matches!(a.cmp_as_ints(b), Ordering::Equal),
(Some(_), None) | (None, Some(_)) => false,
(None, None) => true,
}
@@ -424,12 +413,7 @@ impl Ord for Version {
}
match (&self.rev, &other.rev) {
- (Some(a), Some(b)) => match a
- .get()
- .parse::<u64>()
- .unwrap()
- .cmp(&b.get().parse().unwrap())
- {
+ (Some(a), Some(b)) => match a.cmp_as_ints(b) {
Ordering::Less => return Ordering::Less,
Ordering::Greater => return Ordering::Greater,
Ordering::Equal => (),
@@ -442,11 +426,7 @@ impl Ord for Version {
}
match (&self.build_id, &other.build_id) {
- (Some(a), Some(b)) => a
- .get()
- .parse::<u64>()
- .unwrap()
- .cmp(&b.get().parse::<u64>().unwrap()),
+ (Some(a), Some(b)) => a.cmp_as_ints(b),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,