From 926862961a98eccd2c63574a7c875ab00de9b9dc Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 23 Oct 2023 12:29:17 -0700 Subject: [PATCH] Reduce code size of testing tokens if they're a number This commit is a tiny win in compiled code size of a final binary including `clap` which shaves off 19k of compiled code locally. Previously tokens were checked if they were a number by using `.parse::().is_ok()`, but parsing floats is relatively heavyweight in terms of code size. This replaces the check with a more naive "does this string have lots of ascii digits" check where the compiled size of this check should be much smaller. --- clap_lex/src/lib.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/clap_lex/src/lib.rs b/clap_lex/src/lib.rs index d519a10c5621..edfdee6cc9b4 100644 --- a/clap_lex/src/lib.rs +++ b/clap_lex/src/lib.rs @@ -302,9 +302,7 @@ impl<'s> ParsedArg<'s> { /// Does the argument look like a number pub fn is_number(&self) -> bool { - self.to_value() - .map(|s| s.parse::().is_ok()) - .unwrap_or_default() + self.to_value().map(looks_like_number).unwrap_or_default() } /// Treat as a long-flag @@ -409,7 +407,7 @@ impl<'s> ShortFlags<'s> { /// /// Ideally call this before doing any iterator pub fn is_number(&self) -> bool { - self.invalid_suffix.is_none() && self.utf8_prefix.as_str().parse::().is_ok() + self.invalid_suffix.is_none() && looks_like_number(self.utf8_prefix.as_str()) } /// Advance the iterator, returning the next short flag on success @@ -466,3 +464,20 @@ fn split_nonutf8_once(b: &OsStr) -> (&str, Option<&OsStr>) { } } } + +fn looks_like_number(arg: &str) -> bool { + // strip a leading `-` in case this is a negative number + let arg = arg.strip_prefix("-").unwrap_or(arg); + + // Return true if this looks like an integer or a float where it's all + // digits plus an optional single dot after some digits. + let mut seen_dot = false; + for (i, c) in arg.as_bytes().iter().enumerate() { + match c { + b'0'..=b'9' => {} + b'.' if !seen_dot && i > 0 => seen_dot = true, + _ => return false, + } + } + true +}