perf: Let users choose Str implementation

The binary size and performance difference is enough to make it
configurable.

Code size:
- default: 565.7 KiB
- perf: 578.5 KiB

Build time:
- default: 9.1706 us
- perf: 7.0479 us

Parse time:
- default: 12.673 us
- perf: 8.1708 us

Parse with subcommand time:
- default: 12.112 us
- perf: 7.9874 us
This commit is contained in:
Ed Page 2022-08-22 15:09:25 -05:00
parent 71752f56ea
commit fc499ac0ec
6 changed files with 146 additions and 37 deletions

View file

@ -60,7 +60,7 @@ default = [
"suggestions",
]
debug = ["clap_derive/debug", "dep:backtrace"] # Enables debug messages
unstable-doc = ["derive", "cargo", "wrap_help", "env", "unicode", "unstable-replace", "unstable-grouped"] # for docs.rs
unstable-doc = ["derive", "cargo", "wrap_help", "env", "unicode", "perf", "unstable-replace", "unstable-grouped"] # for docs.rs
# Used in default
std = [] # support for no_std in a backwards-compatible way
@ -74,6 +74,7 @@ cargo = ["dep:once_cell"] # Disable if you're not using Cargo, enables Cargo-env
wrap_help = ["dep:terminal_size", "textwrap/terminal_size"]
env = [] # Use environment variables during arg parsing
unicode = ["textwrap/unicode-width", "dep:unicase"] # Support for unicode characters in arguments and help messages
perf = [] # Optimize for runtime performance
# In-work features
unstable-replace = []

View file

@ -15,8 +15,8 @@ MSRV?=1.60.0
_FEATURES = minimal default wasm full debug release
_FEATURES_minimal = --no-default-features --features "std"
_FEATURES_default =
_FEATURES_wasm = --features "deprecated derive cargo env unicode unstable-replace unstable-grouped"
_FEATURES_full = --features "deprecated derive cargo env unicode unstable-replace unstable-grouped wrap_help"
_FEATURES_wasm = --features "deprecated derive cargo env unicode perf unstable-replace unstable-grouped"
_FEATURES_full = --features "deprecated derive cargo env unicode perf unstable-replace unstable-grouped wrap_help"
_FEATURES_next = ${_FEATURES_full} --features unstable-v5
_FEATURES_debug = ${_FEATURES_full} --features debug
_FEATURES_release = ${_FEATURES_full} --release

View file

@ -16,6 +16,7 @@
//! * **env**: Turns on the usage of environment variables during parsing.
//! * **unicode**: Turns on support for unicode characters (including emoji) in arguments and help messages.
//! * **wrap_help**: Turns on the help text wrapping feature, based on the terminal size.
//! * **perf**: Optimized for runtime performance.
//!
//! #### Experimental features
//!

View file

@ -16,6 +16,8 @@ pub(crate) use self::flat_map::Entry;
pub(crate) use self::flat_map::FlatMap;
pub(crate) use self::flat_set::FlatSet;
pub(crate) use self::graph::ChildGraph;
#[allow(unused_imports)]
pub(crate) use self::str::Inner as StrInner;
pub(crate) use self::str_to_bool::str_to_bool;
pub(crate) use self::str_to_bool::FALSE_LITERALS;
pub(crate) use self::str_to_bool::TRUE_LITERALS;

View file

@ -40,12 +40,34 @@ impl From<&'_ OsStr> for OsStr {
}
}
#[cfg(feature = "perf")]
impl From<crate::Str> for OsStr {
fn from(id: crate::Str) -> Self {
match id.into_inner() {
crate::util::StrInner::Static(s) => Self::from_static_ref(std::ffi::OsStr::new(s)),
crate::util::StrInner::Owned(s) => Self::from_ref(std::ffi::OsStr::new(s.as_ref())),
}
}
}
#[cfg(not(feature = "perf"))]
impl From<crate::Str> for OsStr {
fn from(id: crate::Str) -> Self {
Self::from_ref(std::ffi::OsStr::new(id.as_str()))
}
}
#[cfg(feature = "perf")]
impl From<&'_ crate::Str> for OsStr {
fn from(id: &'_ crate::Str) -> Self {
match id.clone().into_inner() {
crate::util::StrInner::Static(s) => Self::from_static_ref(std::ffi::OsStr::new(s)),
crate::util::StrInner::Owned(s) => Self::from_ref(std::ffi::OsStr::new(s.as_ref())),
}
}
}
#[cfg(not(feature = "perf"))]
impl From<&'_ crate::Str> for OsStr {
fn from(id: &'_ crate::Str) -> Self {
Self::from_ref(std::ffi::OsStr::new(id.as_str()))
@ -214,31 +236,70 @@ impl PartialEq<OsStr> for std::ffi::OsString {
}
}
#[derive(Clone)]
struct Inner(Box<std::ffi::OsStr>);
impl Inner {
fn from_string(name: std::ffi::OsString) -> Self {
Self(name.into_boxed_os_str())
#[cfg(feature = "perf")]
pub(crate) mod inner {
#[derive(Clone)]
pub(crate) enum Inner {
Static(&'static std::ffi::OsStr),
Owned(Box<std::ffi::OsStr>),
}
fn from_ref(name: &std::ffi::OsStr) -> Self {
Self(Box::from(name))
}
impl Inner {
pub(crate) fn from_string(name: std::ffi::OsString) -> Self {
Self::Owned(name.into_boxed_os_str())
}
fn from_static_ref(name: &'static std::ffi::OsStr) -> Self {
Self::from_ref(name)
}
pub(crate) fn from_ref(name: &std::ffi::OsStr) -> Self {
Self::Owned(Box::from(name))
}
fn as_os_str(&self) -> &std::ffi::OsStr {
&self.0
}
pub(crate) fn from_static_ref(name: &'static std::ffi::OsStr) -> Self {
Self::Static(name)
}
fn into_os_string(self) -> std::ffi::OsString {
self.as_os_str().to_owned()
pub(crate) fn as_os_str(&self) -> &std::ffi::OsStr {
match self {
Self::Static(s) => s,
Self::Owned(s) => s.as_ref(),
}
}
pub(crate) fn into_os_string(self) -> std::ffi::OsString {
self.as_os_str().to_owned()
}
}
}
#[cfg(not(feature = "perf"))]
pub(crate) mod inner {
#[derive(Clone)]
pub(crate) struct Inner(Box<std::ffi::OsStr>);
impl Inner {
pub(crate) fn from_string(name: std::ffi::OsString) -> Self {
Self(name.into_boxed_os_str())
}
pub(crate) fn from_ref(name: &std::ffi::OsStr) -> Self {
Self(Box::from(name))
}
pub(crate) fn from_static_ref(name: &'static std::ffi::OsStr) -> Self {
Self::from_ref(name)
}
pub(crate) fn as_os_str(&self) -> &std::ffi::OsStr {
&self.0
}
pub(crate) fn into_os_string(self) -> std::ffi::OsString {
self.as_os_str().to_owned()
}
}
}
pub(crate) use inner::Inner;
impl Default for Inner {
fn default() -> Self {
Self::from_static_ref(std::ffi::OsStr::new(""))

View file

@ -23,6 +23,11 @@ impl Str {
}
}
#[cfg(feature = "perf")]
pub(crate) fn into_inner(self) -> Inner {
self.name
}
/// Get the raw string of the `Str`
pub fn as_str(&self) -> &str {
self.name.as_str()
@ -206,31 +211,70 @@ impl PartialEq<Str> for std::string::String {
}
}
#[derive(Clone)]
pub(crate) struct Inner(Box<str>);
impl Inner {
fn from_string(name: std::string::String) -> Self {
Self(name.into_boxed_str())
#[cfg(feature = "perf")]
pub(crate) mod inner {
#[derive(Clone)]
pub(crate) enum Inner {
Static(&'static str),
Owned(Box<str>),
}
fn from_ref(name: &str) -> Self {
Self(Box::from(name))
}
impl Inner {
pub(crate) fn from_string(name: std::string::String) -> Self {
Self::Owned(name.into_boxed_str())
}
fn from_static_ref(name: &'static str) -> Self {
Self::from_ref(name)
}
pub(crate) fn from_ref(name: &str) -> Self {
Self::Owned(Box::from(name))
}
fn as_str(&self) -> &str {
&self.0
}
pub(crate) fn from_static_ref(name: &'static str) -> Self {
Self::Static(name)
}
fn into_string(self) -> String {
self.as_str().to_owned()
pub(crate) fn as_str(&self) -> &str {
match self {
Self::Static(s) => s,
Self::Owned(s) => s.as_ref(),
}
}
pub(crate) fn into_string(self) -> String {
self.as_str().to_owned()
}
}
}
#[cfg(not(feature = "perf"))]
pub(crate) mod inner {
#[derive(Clone)]
pub(crate) struct Inner(Box<str>);
impl Inner {
pub(crate) fn from_string(name: std::string::String) -> Self {
Self(name.into_boxed_str())
}
pub(crate) fn from_ref(name: &str) -> Self {
Self(Box::from(name))
}
pub(crate) fn from_static_ref(name: &'static str) -> Self {
Self::from_ref(name)
}
pub(crate) fn as_str(&self) -> &str {
&self.0
}
pub(crate) fn into_string(self) -> String {
self.as_str().to_owned()
}
}
}
pub(crate) use inner::Inner;
impl Default for Inner {
fn default() -> Self {
Self::from_static_ref("")