Use `ArcStr` for marker values (#10453)

N.B. After fixing #10430, `ArcStr` became the fastest implementation
(and the gains were significantly reduced, down to 1-2%). See:
https://github.com/astral-sh/uv/pull/10453#issuecomment-2583344414.

## Summary

I tried out a variety of small string crates, but `Arc<str>`
outperformed them, giving a ~10% speed-up:

```console
❯ hyperfine "../arcstr lock" "../flexstr lock" "uv lock" "../arc lock" "../compact_str lock" --prepare "rm -f uv.lock" --min-runs 50 --warmup 20
Benchmark 1: ../arcstr lock
  Time (mean ± σ):     304.6 ms ±   2.3 ms    [User: 302.9 ms, System: 117.8 ms]
  Range (min … max):   299.0 ms … 311.3 ms    50 runs

Benchmark 2: ../flexstr lock
  Time (mean ± σ):     319.2 ms ±   1.7 ms    [User: 317.7 ms, System: 118.2 ms]
  Range (min … max):   316.8 ms … 323.3 ms    50 runs

Benchmark 3: uv lock
  Time (mean ± σ):     330.6 ms ±   1.5 ms    [User: 328.1 ms, System: 139.3 ms]
  Range (min … max):   326.6 ms … 334.2 ms    50 runs

Benchmark 4: ../arc lock
  Time (mean ± σ):     303.0 ms ±   1.2 ms    [User: 301.6 ms, System: 118.4 ms]
  Range (min … max):   300.3 ms … 305.3 ms    50 runs

Benchmark 5: ../compact_str lock
  Time (mean ± σ):     320.4 ms ±   2.0 ms    [User: 318.7 ms, System: 120.8 ms]
  Range (min … max):   317.3 ms … 326.7 ms    50 runs

Summary
  ../arc lock ran
    1.01 ± 0.01 times faster than ../arcstr lock
    1.05 ± 0.01 times faster than ../flexstr lock
    1.06 ± 0.01 times faster than ../compact_str lock
    1.09 ± 0.01 times faster than uv lock
```
This commit is contained in:
Charlie Marsh 2025-01-10 15:15:12 -05:00 committed by GitHub
parent 7a21b713b4
commit 8420195aa7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 80 additions and 60 deletions

3
Cargo.lock generated
View File

@ -5013,6 +5013,7 @@ name = "uv-distribution-types"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arcstr",
"bitflags 2.6.0", "bitflags 2.6.0",
"fs-err 3.0.0", "fs-err 3.0.0",
"itertools 0.14.0", "itertools 0.14.0",
@ -5272,6 +5273,7 @@ dependencies = [
name = "uv-pep508" name = "uv-pep508"
version = "0.6.0" version = "0.6.0"
dependencies = [ dependencies = [
"arcstr",
"boxcar", "boxcar",
"indexmap", "indexmap",
"insta", "insta",
@ -5508,6 +5510,7 @@ dependencies = [
name = "uv-resolver" name = "uv-resolver"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"arcstr",
"clap", "clap",
"dashmap", "dashmap",
"either", "either",

View File

@ -29,6 +29,7 @@ uv-platform-tags = { workspace = true }
uv-pypi-types = { workspace = true } uv-pypi-types = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
arcstr = { workspace = true }
bitflags = { workspace = true } bitflags = { workspace = true }
fs-err = { workspace = true } fs-err = { workspace = true }
itertools = { workspace = true } itertools = { workspace = true }

View File

@ -1,5 +1,6 @@
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use arcstr::ArcStr;
use tracing::debug; use tracing::debug;
use uv_distribution_filename::{BuildTag, WheelFilename}; use uv_distribution_filename::{BuildTag, WheelFilename};
@ -662,12 +663,12 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
let mut tag_marker = MarkerTree::expression(MarkerExpression::String { let mut tag_marker = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform, key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "win32".to_string(), value: arcstr::literal!("win32"),
}); });
tag_marker.and(MarkerTree::expression(MarkerExpression::String { tag_marker.and(MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::PlatformMachine, key: MarkerValueString::PlatformMachine,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "x86".to_string(), value: arcstr::literal!("x86"),
})); }));
marker.or(tag_marker); marker.or(tag_marker);
} }
@ -675,12 +676,12 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
let mut tag_marker = MarkerTree::expression(MarkerExpression::String { let mut tag_marker = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform, key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "win32".to_string(), value: arcstr::literal!("win32"),
}); });
tag_marker.and(MarkerTree::expression(MarkerExpression::String { tag_marker.and(MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::PlatformMachine, key: MarkerValueString::PlatformMachine,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "x86_64".to_string(), value: arcstr::literal!("x86_64"),
})); }));
marker.or(tag_marker); marker.or(tag_marker);
} }
@ -688,12 +689,12 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
let mut tag_marker = MarkerTree::expression(MarkerExpression::String { let mut tag_marker = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform, key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "win32".to_string(), value: arcstr::literal!("win32"),
}); });
tag_marker.and(MarkerTree::expression(MarkerExpression::String { tag_marker.and(MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::PlatformMachine, key: MarkerValueString::PlatformMachine,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "arm64".to_string(), value: arcstr::literal!("arm64"),
})); }));
marker.or(tag_marker); marker.or(tag_marker);
} }
@ -703,7 +704,7 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
let mut tag_marker = MarkerTree::expression(MarkerExpression::String { let mut tag_marker = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform, key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "darwin".to_string(), value: arcstr::literal!("darwin"),
}); });
// Parse the macOS version from the tag. // Parse the macOS version from the tag.
@ -787,7 +788,7 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
arch_marker.or(MarkerTree::expression(MarkerExpression::String { arch_marker.or(MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::PlatformMachine, key: MarkerValueString::PlatformMachine,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: (*arch).to_string(), value: ArcStr::from(*arch),
})); }));
} }
tag_marker.and(arch_marker); tag_marker.and(arch_marker);
@ -800,7 +801,7 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
let mut tag_marker = MarkerTree::expression(MarkerExpression::String { let mut tag_marker = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform, key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "linux".to_string(), value: arcstr::literal!("linux"),
}); });
// Parse the architecture from the tag. // Parse the architecture from the tag.
@ -866,7 +867,7 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
tag_marker.and(MarkerTree::expression(MarkerExpression::String { tag_marker.and(MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::PlatformMachine, key: MarkerValueString::PlatformMachine,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: arch.to_string(), value: ArcStr::from(arch),
})); }));
marker.or(tag_marker); marker.or(tag_marker);

View File

@ -23,6 +23,7 @@ uv-fs = { workspace = true }
uv-normalize = { workspace = true } uv-normalize = { workspace = true }
uv-pep440 = { workspace = true } uv-pep440 = { workspace = true }
arcstr = { workspace = true}
boxcar = { workspace = true } boxcar = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
itertools = { workspace = true } itertools = { workspace = true }

View File

@ -1334,17 +1334,17 @@ mod tests {
let mut b = MarkerTree::expression(MarkerExpression::String { let mut b = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform, key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "win32".to_string(), value: arcstr::literal!("win32"),
}); });
let mut c = MarkerTree::expression(MarkerExpression::String { let mut c = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::OsName, key: MarkerValueString::OsName,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "linux".to_string(), value: arcstr::literal!("linux"),
}); });
let d = MarkerTree::expression(MarkerExpression::String { let d = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::ImplementationName, key: MarkerValueString::ImplementationName,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "cpython".to_string(), value: arcstr::literal!("cpython"),
}); });
c.and(d); c.and(d);

View File

@ -51,6 +51,7 @@ use std::ops::Bound;
use std::sync::Mutex; use std::sync::Mutex;
use std::sync::MutexGuard; use std::sync::MutexGuard;
use arcstr::ArcStr;
use itertools::{Either, Itertools}; use itertools::{Either, Itertools};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::sync::LazyLock; use std::sync::LazyLock;
@ -287,28 +288,31 @@ impl InternerGuard<'_> {
// values in `exclusions`. // values in `exclusions`.
// //
// See: https://discuss.python.org/t/clarify-usage-of-platform-system/70900 // See: https://discuss.python.org/t/clarify-usage-of-platform-system/70900
let (key, value) = match (key, value.as_str()) { let (key, value) = match (key, value.as_ref()) {
(MarkerValueString::PlatformSystem, "Windows") => { (MarkerValueString::PlatformSystem, "Windows") => (
(CanonicalMarkerValueString::SysPlatform, "win32".to_string()) CanonicalMarkerValueString::SysPlatform,
} arcstr::literal!("win32"),
),
(MarkerValueString::PlatformSystem, "Darwin") => ( (MarkerValueString::PlatformSystem, "Darwin") => (
CanonicalMarkerValueString::SysPlatform, CanonicalMarkerValueString::SysPlatform,
"darwin".to_string(), arcstr::literal!("darwin"),
),
(MarkerValueString::PlatformSystem, "Linux") => (
CanonicalMarkerValueString::SysPlatform,
arcstr::literal!("linux"),
),
(MarkerValueString::PlatformSystem, "AIX") => (
CanonicalMarkerValueString::SysPlatform,
arcstr::literal!("aix"),
), ),
(MarkerValueString::PlatformSystem, "Linux") => {
(CanonicalMarkerValueString::SysPlatform, "linux".to_string())
}
(MarkerValueString::PlatformSystem, "AIX") => {
(CanonicalMarkerValueString::SysPlatform, "aix".to_string())
}
(MarkerValueString::PlatformSystem, "Emscripten") => ( (MarkerValueString::PlatformSystem, "Emscripten") => (
CanonicalMarkerValueString::SysPlatform, CanonicalMarkerValueString::SysPlatform,
"emscripten".to_string(), arcstr::literal!("emscripten"),
), ),
// See: https://peps.python.org/pep-0738/#sys // See: https://peps.python.org/pep-0738/#sys
(MarkerValueString::PlatformSystem, "Android") => ( (MarkerValueString::PlatformSystem, "Android") => (
CanonicalMarkerValueString::SysPlatform, CanonicalMarkerValueString::SysPlatform,
"android".to_string(), arcstr::literal!("android"),
), ),
_ => (key.into(), value), _ => (key.into(), value),
}; };
@ -869,48 +873,48 @@ impl InternerGuard<'_> {
MarkerExpression::String { MarkerExpression::String {
key: MarkerValueString::OsName, key: MarkerValueString::OsName,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "nt".to_string(), value: arcstr::literal!("nt"),
}, },
MarkerExpression::String { MarkerExpression::String {
key: MarkerValueString::SysPlatform, key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "linux".to_string(), value: arcstr::literal!("linux"),
}, },
), ),
( (
MarkerExpression::String { MarkerExpression::String {
key: MarkerValueString::OsName, key: MarkerValueString::OsName,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "nt".to_string(), value: arcstr::literal!("nt"),
}, },
MarkerExpression::String { MarkerExpression::String {
key: MarkerValueString::SysPlatform, key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "darwin".to_string(), value: arcstr::literal!("darwin"),
}, },
), ),
( (
MarkerExpression::String { MarkerExpression::String {
key: MarkerValueString::OsName, key: MarkerValueString::OsName,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "nt".to_string(), value: arcstr::literal!("nt"),
}, },
MarkerExpression::String { MarkerExpression::String {
key: MarkerValueString::SysPlatform, key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "ios".to_string(), value: arcstr::literal!("ios"),
}, },
), ),
( (
MarkerExpression::String { MarkerExpression::String {
key: MarkerValueString::OsName, key: MarkerValueString::OsName,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "posix".to_string(), value: arcstr::literal!("posix"),
}, },
MarkerExpression::String { MarkerExpression::String {
key: MarkerValueString::SysPlatform, key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "win32".to_string(), value: arcstr::literal!("win32"),
}, },
), ),
]; ];
@ -950,12 +954,12 @@ impl InternerGuard<'_> {
MarkerExpression::String { MarkerExpression::String {
key: MarkerValueString::PlatformSystem, key: MarkerValueString::PlatformSystem,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: platform_system.to_string(), value: ArcStr::from(platform_system),
}, },
MarkerExpression::String { MarkerExpression::String {
key: MarkerValueString::SysPlatform, key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: sys_platform.to_string(), value: ArcStr::from(sys_platform),
}, },
)); ));
} }
@ -996,13 +1000,13 @@ pub(crate) enum Variable {
/// string marker and value. /// string marker and value.
In { In {
key: CanonicalMarkerValueString, key: CanonicalMarkerValueString,
value: String, value: ArcStr,
}, },
/// A variable representing a `<value> in <key>` expression for a particular /// A variable representing a `<value> in <key>` expression for a particular
/// string marker and value. /// string marker and value.
Contains { Contains {
key: CanonicalMarkerValueString, key: CanonicalMarkerValueString,
value: String, value: ArcStr,
}, },
/// A variable representing the existence or absence of a given extra. /// A variable representing the existence or absence of a given extra.
/// ///
@ -1128,7 +1132,7 @@ pub(crate) enum Edges {
// Invariant: All ranges are simple, meaning they can be represented by a bounded // Invariant: All ranges are simple, meaning they can be represented by a bounded
// interval without gaps. Additionally, there are at least two edges in the set. // interval without gaps. Additionally, there are at least two edges in the set.
String { String {
edges: SmallVec<(Ranges<String>, NodeId)>, edges: SmallVec<(Ranges<ArcStr>, NodeId)>,
}, },
// The edges of a boolean variable, representing the values `true` (the `high` child) // The edges of a boolean variable, representing the values `true` (the `high` child)
// and `false` (the `low` child). // and `false` (the `low` child).
@ -1158,8 +1162,8 @@ impl Edges {
/// ///
/// This function will panic for the `In` and `Contains` marker operators, which /// This function will panic for the `In` and `Contains` marker operators, which
/// should be represented as separate boolean variables. /// should be represented as separate boolean variables.
fn from_string(operator: MarkerOperator, value: String) -> Edges { fn from_string(operator: MarkerOperator, value: ArcStr) -> Edges {
let range: Ranges<String> = match operator { let range: Ranges<ArcStr> = match operator {
MarkerOperator::Equal => Ranges::singleton(value), MarkerOperator::Equal => Ranges::singleton(value),
MarkerOperator::NotEqual => Ranges::singleton(value).complement(), MarkerOperator::NotEqual => Ranges::singleton(value).complement(),
MarkerOperator::GreaterThan => Ranges::strictly_higher_than(value), MarkerOperator::GreaterThan => Ranges::strictly_higher_than(value),

View File

@ -1,5 +1,5 @@
use arcstr::ArcStr;
use std::str::FromStr; use std::str::FromStr;
use uv_normalize::ExtraName; use uv_normalize::ExtraName;
use uv_pep440::{Version, VersionPattern, VersionSpecifier}; use uv_pep440::{Version, VersionPattern, VersionSpecifier};
@ -92,7 +92,7 @@ pub(crate) fn parse_marker_value<T: Pep508Url>(
Some((start_pos, quotation_mark @ ('"' | '\''))) => { Some((start_pos, quotation_mark @ ('"' | '\''))) => {
cursor.next(); cursor.next();
let (start, len) = cursor.take_while(|c| c != quotation_mark); let (start, len) = cursor.take_while(|c| c != quotation_mark);
let value = cursor.slice(start, len).to_string(); let value = ArcStr::from(cursor.slice(start, len));
cursor.next_expect_char(quotation_mark, start_pos)?; cursor.next_expect_char(quotation_mark, start_pos)?;
Ok(MarkerValue::QuotedString(value)) Ok(MarkerValue::QuotedString(value))
} }

View File

@ -1,12 +1,14 @@
use std::fmt; use std::fmt;
use std::ops::Bound; use std::ops::Bound;
use arcstr::ArcStr;
use indexmap::IndexMap; use indexmap::IndexMap;
use itertools::Itertools; use itertools::Itertools;
use rustc_hash::FxBuildHasher; use rustc_hash::FxBuildHasher;
use uv_pep440::{Version, VersionSpecifier};
use version_ranges::Ranges; use version_ranges::Ranges;
use uv_pep440::{Version, VersionSpecifier};
use crate::{ExtraOperator, MarkerExpression, MarkerOperator, MarkerTree, MarkerTreeKind}; use crate::{ExtraOperator, MarkerExpression, MarkerOperator, MarkerTree, MarkerTreeKind};
/// Returns a simplified DNF expression for a given marker tree. /// Returns a simplified DNF expression for a given marker tree.
@ -131,7 +133,7 @@ fn collect_dnf(
let expr = MarkerExpression::String { let expr = MarkerExpression::String {
key: marker.key().into(), key: marker.key().into(),
value: marker.value().to_owned(), value: ArcStr::from(marker.value()),
operator, operator,
}; };
@ -150,7 +152,7 @@ fn collect_dnf(
let expr = MarkerExpression::String { let expr = MarkerExpression::String {
key: marker.key().into(), key: marker.key().into(),
value: marker.value().to_owned(), value: ArcStr::from(marker.value()),
operator, operator,
}; };

View File

@ -3,6 +3,7 @@ use std::fmt::{self, Display, Formatter};
use std::ops::{Bound, Deref}; use std::ops::{Bound, Deref};
use std::str::FromStr; use std::str::FromStr;
use arcstr::ArcStr;
use itertools::Itertools; use itertools::Itertools;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use version_ranges::Ranges; use version_ranges::Ranges;
@ -129,7 +130,7 @@ pub enum MarkerValue {
/// `extra`. This one is special because it's a list and not env but user given /// `extra`. This one is special because it's a list and not env but user given
Extra, Extra,
/// Not a constant, but a user given quoted string with a value inside such as '3.8' or "windows" /// Not a constant, but a user given quoted string with a value inside such as '3.8' or "windows"
QuotedString(String), QuotedString(ArcStr),
} }
impl FromStr for MarkerValue { impl FromStr for MarkerValue {
@ -272,8 +273,8 @@ impl MarkerOperator {
/// Returns the marker operator and value whose union represents the given range. /// Returns the marker operator and value whose union represents the given range.
pub fn from_bounds( pub fn from_bounds(
bounds: (&Bound<String>, &Bound<String>), bounds: (&Bound<ArcStr>, &Bound<ArcStr>),
) -> impl Iterator<Item = (MarkerOperator, String)> { ) -> impl Iterator<Item = (MarkerOperator, ArcStr)> {
let (b1, b2) = match bounds { let (b1, b2) = match bounds {
(Bound::Included(v1), Bound::Included(v2)) if v1 == v2 => { (Bound::Included(v1), Bound::Included(v2)) if v1 == v2 => {
(Some((MarkerOperator::Equal, v1.clone())), None) (Some((MarkerOperator::Equal, v1.clone())), None)
@ -291,7 +292,7 @@ impl MarkerOperator {
} }
/// Returns a value specifier representing the given lower bound. /// Returns a value specifier representing the given lower bound.
pub fn from_lower_bound(bound: &Bound<String>) -> Option<(MarkerOperator, String)> { pub fn from_lower_bound(bound: &Bound<ArcStr>) -> Option<(MarkerOperator, ArcStr)> {
match bound { match bound {
Bound::Included(value) => Some((MarkerOperator::GreaterEqual, value.clone())), Bound::Included(value) => Some((MarkerOperator::GreaterEqual, value.clone())),
Bound::Excluded(value) => Some((MarkerOperator::GreaterThan, value.clone())), Bound::Excluded(value) => Some((MarkerOperator::GreaterThan, value.clone())),
@ -300,7 +301,7 @@ impl MarkerOperator {
} }
/// Returns a value specifier representing the given upper bound. /// Returns a value specifier representing the given upper bound.
pub fn from_upper_bound(bound: &Bound<String>) -> Option<(MarkerOperator, String)> { pub fn from_upper_bound(bound: &Bound<ArcStr>) -> Option<(MarkerOperator, ArcStr)> {
match bound { match bound {
Bound::Included(value) => Some((MarkerOperator::LessEqual, value.clone())), Bound::Included(value) => Some((MarkerOperator::LessEqual, value.clone())),
Bound::Excluded(value) => Some((MarkerOperator::LessThan, value.clone())), Bound::Excluded(value) => Some((MarkerOperator::LessThan, value.clone())),
@ -485,7 +486,7 @@ pub enum MarkerExpression {
String { String {
key: MarkerValueString, key: MarkerValueString,
operator: MarkerOperator, operator: MarkerOperator,
value: String, value: ArcStr,
}, },
/// `extra <extra op> '...'` or `'...' <extra op> extra`. /// `extra <extra op> '...'` or `'...' <extra op> extra`.
Extra { Extra {
@ -1383,7 +1384,7 @@ impl Ord for VersionMarkerTree<'_> {
pub struct StringMarkerTree<'a> { pub struct StringMarkerTree<'a> {
id: NodeId, id: NodeId,
key: CanonicalMarkerValueString, key: CanonicalMarkerValueString,
map: &'a [(Ranges<String>, NodeId)], map: &'a [(Ranges<ArcStr>, NodeId)],
} }
impl StringMarkerTree<'_> { impl StringMarkerTree<'_> {
@ -1393,7 +1394,7 @@ impl StringMarkerTree<'_> {
} }
/// The edges of this node, corresponding to possible output ranges of the given variable. /// The edges of this node, corresponding to possible output ranges of the given variable.
pub fn children(&self) -> impl ExactSizeIterator<Item = (&Ranges<String>, MarkerTree)> { pub fn children(&self) -> impl ExactSizeIterator<Item = (&Ranges<ArcStr>, MarkerTree)> {
self.map self.map
.iter() .iter()
.map(|(range, node)| (range, MarkerTree(node.negate(self.id)))) .map(|(range, node)| (range, MarkerTree(node.negate(self.id))))
@ -1418,7 +1419,7 @@ impl Ord for StringMarkerTree<'_> {
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct InMarkerTree<'a> { pub struct InMarkerTree<'a> {
key: CanonicalMarkerValueString, key: CanonicalMarkerValueString,
value: &'a str, value: &'a ArcStr,
high: NodeId, high: NodeId,
low: NodeId, low: NodeId,
} }
@ -1430,7 +1431,7 @@ impl InMarkerTree<'_> {
} }
/// The value (RHS) for this expression. /// The value (RHS) for this expression.
pub fn value(&self) -> &str { pub fn value(&self) -> &ArcStr {
self.value self.value
} }
@ -1654,6 +1655,7 @@ mod test {
use std::str::FromStr; use std::str::FromStr;
use insta::assert_snapshot; use insta::assert_snapshot;
use uv_normalize::ExtraName; use uv_normalize::ExtraName;
use uv_pep440::Version; use uv_pep440::Version;
@ -2041,7 +2043,7 @@ mod test {
MarkerExpression::String { MarkerExpression::String {
key: MarkerValueString::OsName, key: MarkerValueString::OsName,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: "nt".to_string(), value: arcstr::literal!("nt")
} }
); );
} }

View File

@ -38,6 +38,7 @@ uv-types = { workspace = true }
uv-warnings = { workspace = true } uv-warnings = { workspace = true }
uv-workspace = { workspace = true } uv-workspace = { workspace = true }
arcstr = { workspace = true }
clap = { workspace = true, features = ["derive"], optional = true } clap = { workspace = true, features = ["derive"], optional = true }
dashmap = { workspace = true } dashmap = { workspace = true }
either = { workspace = true } either = { workspace = true }

View File

@ -7,6 +7,7 @@ use petgraph::{
Directed, Direction, Directed, Direction,
}; };
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use uv_configuration::{Constraints, Overrides}; use uv_configuration::{Constraints, Overrides};
use uv_distribution::Metadata; use uv_distribution::Metadata;
use uv_distribution_types::{ use uv_distribution_types::{
@ -726,7 +727,7 @@ impl ResolverOutput {
MarkerExpression::String { MarkerExpression::String {
key: value_string.into(), key: value_string.into(),
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: from_env.to_string(), value: from_env.into(),
} }
} }
}; };

View File

@ -1424,11 +1424,15 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
// macOS. But if _neither_ version supports Intel macOS, we'd rather use `sys_platform == 'darwin'` // macOS. But if _neither_ version supports Intel macOS, we'd rather use `sys_platform == 'darwin'`
// instead of `sys_platform == 'darwin' and platform_machine == 'arm64'`, since it's much // instead of `sys_platform == 'darwin' and platform_machine == 'arm64'`, since it's much
// simpler, and _neither_ version will succeed with Intel macOS anyway. // simpler, and _neither_ version will succeed with Intel macOS anyway.
for sys_platform in &["darwin", "linux", "win32"] { for value in [
arcstr::literal!("darwin"),
arcstr::literal!("linux"),
arcstr::literal!("win32"),
] {
let sys_platform = MarkerTree::expression(MarkerExpression::String { let sys_platform = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform, key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal, operator: MarkerOperator::Equal,
value: (*sys_platform).to_string(), value,
}); });
if dist.implied_markers().is_disjoint(sys_platform) if dist.implied_markers().is_disjoint(sys_platform)
&& !remainder.is_disjoint(sys_platform) && !remainder.is_disjoint(sys_platform)