Use a PHF map for util_map()

Rather than building a HashMap at compile time, use the phf (and phf_codegen) crate to build the map
at compile time in build.rs
This commit is contained in:
Zachary Dremann 2022-01-30 04:56:44 -05:00
parent 57dc11e586
commit bb41f4ffe5
5 changed files with 96 additions and 81 deletions

46
Cargo.lock generated
View file

@ -302,6 +302,8 @@ dependencies = [
"lazy_static",
"libc",
"nix 0.23.1",
"phf",
"phf_codegen",
"pretty_assertions",
"rand",
"regex",
@ -1347,6 +1349,44 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "phf"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
dependencies = [
"phf_generator",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_shared"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
dependencies = [
"siphasher",
]
[[package]]
name = "pkg-config"
version = "0.3.24"
@ -1758,6 +1798,12 @@ dependencies = [
"libc",
]
[[package]]
name = "siphasher"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e"
[[package]]
name = "smallvec"
version = "1.8.0"

View file

@ -247,6 +247,7 @@ test = [ "uu_test" ]
[dependencies]
clap = { version = "3.0", features = ["wrap_help", "cargo"] }
clap_complete = "3.0"
phf = "0.10.1"
lazy_static = { version="1.3" }
textwrap = { version="0.14", features=["terminal_size"] }
uucore = { version=">=0.0.11", package="uucore", path="src/uucore" }
@ -387,6 +388,9 @@ nix = "0.23.1"
rust-users = { version="0.10", package="users" }
unix_socket = "0.5.0"
[build-dependencies]
phf_codegen = "0.10.0"
[[bin]]
name = "coreutils"
path = "src/bin/coreutils.rs"

121
build.rs
View file

@ -12,9 +12,9 @@ pub fn main() {
println!("cargo:rustc-cfg=build={:?}", profile);
}
let env_feature_prefix: &str = "CARGO_FEATURE_";
let feature_prefix: &str = "feat_";
let override_prefix: &str = "uu_";
const ENV_FEATURE_PREFIX: &str = "CARGO_FEATURE_";
const FEATURE_PREFIX: &str = "feat_";
const OVERRIDE_PREFIX: &str = "uu_";
let out_dir = env::var("OUT_DIR").unwrap();
// println!("cargo:warning=out_dir={}", out_dir);
@ -25,16 +25,16 @@ pub fn main() {
let mut crates = Vec::new();
for (key, val) in env::vars() {
if val == "1" && key.starts_with(env_feature_prefix) {
let krate = key[env_feature_prefix.len()..].to_lowercase();
if val == "1" && key.starts_with(ENV_FEATURE_PREFIX) {
let krate = key[ENV_FEATURE_PREFIX.len()..].to_lowercase();
match krate.as_ref() {
"default" | "macos" | "unix" | "windows" | "selinux" => continue, // common/standard feature names
"nightly" | "test_unimplemented" => continue, // crate-local custom features
"test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test'
s if s.starts_with(feature_prefix) => continue, // crate feature sets
s if s.starts_with(FEATURE_PREFIX) => continue, // crate feature sets
_ => {} // util feature name
}
crates.push(krate.to_string());
crates.push(krate);
}
}
crates.sort();
@ -43,33 +43,23 @@ pub fn main() {
let mut tf = File::create(Path::new(&out_dir).join("test_modules.rs")).unwrap();
mf.write_all(
"type UtilityMap<T> = HashMap<&'static str, (fn(T) -> i32, fn() -> App<'static>)>;\n\
"type UtilityMap<T> = phf::Map<&'static str, (fn(T) -> i32, fn() -> App<'static>)>;\n\
\n\
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n\
\t#[allow(unused_mut)]\n\
\t#[allow(clippy::let_and_return)]\n\
\tlet mut map = UtilityMap::new();\n\
"
.as_bytes(),
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n"
.as_bytes(),
)
.unwrap();
let mut phf_map = phf_codegen::Map::<String>::new();
for krate in crates {
let map_value = format!("({krate}::uumain, {krate}::uu_app)", krate = krate);
match krate.as_ref() {
// 'test' is named uu_test to avoid collision with rust core crate 'test'.
// It can also be invoked by name '[' for the '[ expr ] syntax'.
"uu_test" => {
mf.write_all(
format!(
"\
\tmap.insert(\"test\", ({krate}::uumain, {krate}::uu_app));\n\
\t\tmap.insert(\"[\", ({krate}::uumain, {krate}::uu_app));\n\
",
krate = krate
)
.as_bytes(),
)
.unwrap();
phf_map.entry(String::from("test"), &map_value);
phf_map.entry(String::from("["), &map_value);
tf.write_all(
format!(
"#[path=\"{dir}/test_test.rs\"]\nmod test_test;\n",
@ -79,20 +69,12 @@ pub fn main() {
)
.unwrap()
}
k if k.starts_with(override_prefix) => {
mf.write_all(
format!(
"\tmap.insert(\"{k}\", ({krate}::uumain, {krate}::uu_app));\n",
k = &krate[override_prefix.len()..],
krate = krate
)
.as_bytes(),
)
.unwrap();
k if k.starts_with(OVERRIDE_PREFIX) => {
phf_map.entry(String::from(&krate[OVERRIDE_PREFIX.len()..]), &map_value);
tf.write_all(
format!(
"#[path=\"{dir}/test_{k}.rs\"]\nmod test_{k};\n",
k = &krate[override_prefix.len()..],
k = &krate[OVERRIDE_PREFIX.len()..],
dir = util_tests_dir,
)
.as_bytes(),
@ -100,14 +82,10 @@ pub fn main() {
.unwrap()
}
"false" | "true" => {
mf.write_all(
format!(
"\tmap.insert(\"{krate}\", (r#{krate}::uumain, r#{krate}::uu_app));\n",
krate = krate
)
.as_bytes(),
)
.unwrap();
phf_map.entry(
String::from(&krate),
&format!("(r#{krate}::uumain, r#{krate}::uu_app)", krate = krate),
);
tf.write_all(
format!(
"#[path=\"{dir}/test_{krate}.rs\"]\nmod test_{krate};\n",
@ -119,29 +97,25 @@ pub fn main() {
.unwrap()
}
"hashsum" => {
mf.write_all(
format!(
"\
\tmap.insert(\"{krate}\", ({krate}::uumain, {krate}::uu_app_custom));\n\
\t\tmap.insert(\"md5sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha1sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha224sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha256sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha384sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha512sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha3sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha3-224sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha3-256sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha3-384sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha3-512sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"shake128sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"shake256sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
",
krate = krate
)
.as_bytes(),
)
.unwrap();
phf_map.entry(
String::from(&krate),
&format!("({krate}::uumain, {krate}::uu_app_custom)", krate = krate),
);
let map_value = format!("({krate}::uumain, {krate}::uu_app_common)", krate = krate);
phf_map.entry(String::from("md5sum"), &map_value);
phf_map.entry(String::from("sha1sum"), &map_value);
phf_map.entry(String::from("sha224sum"), &map_value);
phf_map.entry(String::from("sha256sum"), &map_value);
phf_map.entry(String::from("sha384sum"), &map_value);
phf_map.entry(String::from("sha512sum"), &map_value);
phf_map.entry(String::from("sha3sum"), &map_value);
phf_map.entry(String::from("sha3-224sum"), &map_value);
phf_map.entry(String::from("sha3-256sum"), &map_value);
phf_map.entry(String::from("sha3-384sum"), &map_value);
phf_map.entry(String::from("sha3-512sum"), &map_value);
phf_map.entry(String::from("shake128sum"), &map_value);
phf_map.entry(String::from("shake256sum"), &map_value);
tf.write_all(
format!(
"#[path=\"{dir}/test_{krate}.rs\"]\nmod test_{krate};\n",
@ -153,14 +127,7 @@ pub fn main() {
.unwrap()
}
_ => {
mf.write_all(
format!(
"\tmap.insert(\"{krate}\", ({krate}::uumain, {krate}::uu_app));\n",
krate = krate
)
.as_bytes(),
)
.unwrap();
phf_map.entry(String::from(&krate), &map_value);
tf.write_all(
format!(
"#[path=\"{dir}/test_{krate}.rs\"]\nmod test_{krate};\n",
@ -173,8 +140,8 @@ pub fn main() {
}
}
}
mf.write_all(b"map\n}\n").unwrap();
write!(mf, "{}", phf_map.build()).unwrap();
mf.write_all(b"\n}\n").unwrap();
mf.flush().unwrap();
tf.flush().unwrap();

View file

@ -8,7 +8,6 @@
use clap::{App, Arg};
use clap_complete::Shell;
use std::cmp;
use std::collections::hash_map::HashMap;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::io::{self, Write};
@ -171,7 +170,7 @@ fn gen_completions<T: uucore::Args>(
fn gen_coreutils_app<T: uucore::Args>(util_map: UtilityMap<T>) -> App<'static> {
let mut app = App::new("coreutils");
for (_, (_, sub_app)) in util_map {
for (_, (_, sub_app)) in &util_map {
app = app.subcommand(sub_app());
}
app

View file

@ -4,7 +4,6 @@
// file that was distributed with this source code.
use clap::App;
use std::collections::hash_map::HashMap;
use std::ffi::OsString;
use std::fs::File;
use std::io::{self, Write};
@ -32,7 +31,7 @@ fn main() -> io::Result<()> {
* [Multi-call binary](multicall.md)\n",
);
let mut utils = utils.iter().collect::<Vec<_>>();
let mut utils = utils.entries().collect::<Vec<_>>();
utils.sort();
for (&name, (_, app)) in utils {
if name == "[" {