Skip to content

Commit

Permalink
Merge pull request #415 from mgeisler/binary-sizes
Browse files Browse the repository at this point in the history
Add demo program to help compute binary sizes
  • Loading branch information
mgeisler committed Nov 7, 2021
2 parents 8e4dde2 + b7a4a8e commit 1ece89e
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 2 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,19 @@ jobs:
- name: Minimize fuzz corpus
run: cargo fuzz cmin fill_first_fit

binary-sizes:
name: Compute binary sizes
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2

- uses: Swatinem/rust-cache@v1

- name: Make binary size table
run: cargo run --example make-table
working-directory: examples/binary-sizes

wasm-build:
name: Build Wasm demo
runs-on: ubuntu-latest
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ By default, this enables word wrapping with support for Unicode
strings. Extra features can be enabled with Cargo features—and the
Unicode support can be disabled if needed. This allows you slim down
the library and so you will only pay for the features you actually
use. Please see the [_Cargo Features_ in the crate
use.

Please see the [_Cargo Features_ in the crate
documentation](https://docs.rs/textwrap/#cargo-features) for a full
list of the available features.
list of the available features as well as their impact on the size of
your binary.

## Documentation

Expand Down
22 changes: 22 additions & 0 deletions examples/binary-sizes/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "textwrap-binary-sizes-demo"
version = "0.1.0"
authors = ["Martin Geisler <martin@geisler.net>"]
description = "Textwrap binary size demo"
repository = "https://github.com/mgeisler/textwrap"
license-file = "../../LICENSE"
edition = "2018"
publish = false # This demo project should not be uploaded to crates.io

[profile.release]
lto = true
codegen-units = 1

[features]
smawk = ["textwrap/smawk"]
unicode-linebreak = ["textwrap/unicode-linebreak"]
unicode-width = ["textwrap/unicode-width"]
hyphenation = ["textwrap/hyphenation"]

[dependencies]
textwrap = { path = "../../", default-features = false, optional = true }
89 changes: 89 additions & 0 deletions examples/binary-sizes/examples/make-table.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//! Compile Textwrap with differnet featurs and record the resulting
//! binary size. Produces a Markdown table.

use std::process::Command;
use std::{fs, io};

fn compile(extra_args: &[&str]) -> io::Result<u64> {
let status = Command::new("cargo")
.arg("build")
.arg("--quiet")
.arg("--release")
.args(extra_args)
.status()?;
if !status.success() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("failed to compile: {}", status),
));
}

let path = "target/release/textwrap-binary-sizes-demo";
let status = Command::new("strip").arg(path).status()?;
if !status.success() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("failed to strip: {}", status),
));
}

let metadata = fs::metadata(path).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("failed to read metadata for {}: {}", path, err),
)
})?;
Ok(metadata.len())
}

struct PrettyError {
error: io::Error,
}

// Simply print the inner error with `Display` (not `Debug`) to get a
// human-readable error message.
impl std::fmt::Debug for PrettyError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(fmt, "{}", self.error)
}
}

impl From<io::Error> for PrettyError {
fn from(error: io::Error) -> Self {
Self { error }
}
}

fn kb(size: u64) -> String {
format!("{} KB", size / 1000)
}

fn main() -> Result<(), PrettyError> {
macro_rules! printcols {
($($value:expr),+) => {
println!("| {:<a$} | {:>b$} | {:>c$} |",
$($value),+,
a = 40, b = 12, c = 8);
};
}

printcols!("Configuration", "Binary Size", "Delta");
printcols!(":---", "---:", "---:");

let features = [
("textwrap", "textwrap without default features"),
("smawk", "textwrap with smawk"),
("unicode-width", "textwrap with unicode-width"),
("unicode-linebreak", "textwrap with unicode-linebreak"),
];
let base_size = compile(&[])?;
printcols!("quick-and-dirty implementation", kb(base_size), "— KB");

for (feature, label) in features.iter() {
let size = compile(&["--features", feature])?;
let delta = size - base_size;
printcols!(label, kb(size), kb(delta));
}

Ok(())
}
36 changes: 36 additions & 0 deletions examples/binary-sizes/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#[cfg(feature = "textwrap")]
use textwrap::fill;

#[cfg(not(feature = "textwrap"))]
/// Quick-and-dirty fill implementation.
///
/// Assumes single space between words, assumes 1 column per Unicode
/// character (no emoji handling) and assumes that the longest word
/// fit on the line (no handling of hyphens or over-long words).
fn fill(text: &str, width: usize) -> String {
let mut result = String::with_capacity(text.len());
let mut line_width = 0;
for word in text.split_whitespace() {
if line_width + 1 + word.len() > width {
result.push('\n');
line_width = 0;
}

result.push_str(word);
result.push(' ');
line_width += word.len() + 1;
}

// Remove final ' '.
result.truncate(result.len() - 1);
result
}

fn main() {
let text = "Hello, welcome to a world with beautifully wrapped \
text in your command-line programs. This includes \
non-ASCII text such as Açai, Jalapeño, Frappé";
for line in fill(text, 18).lines() {
println!("│ {:18} │", line);
}
}
24 changes: 24 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,29 @@
//! This feature can be disabled if you only ever intend to use
//! [`wrap_algorithms::wrap_first_fit`].
//!
//! With Rust 1.54.0, the size impact of the above features on your
//! binary is as follows:
//!
//! | Configuration | Binary Size | Delta |
//! | :--- | ---: | ---: |
//! | quick-and-dirty implementation | 252 KB | — KB |
//! | textwrap without default features | 268 KB | 16 KB |
//! | textwrap with smawk | 284 KB | 32 KB |
//! | textwrap with unicode-width | 276 KB | 24 KB |
//! | textwrap with unicode-linebreak | 362 KB | 110 KB |
//!
//! The above sizes are the stripped sizes and the binary is compiled
//! in release mode with this profile:
//!
//! ```toml
//! [profile.release]
//! lto = true
//! codegen-units = 1
//! ```
//!
//! See the [binary-sizes demo] if you want to reproduce these
//! results.
//!
//! ## Optional Features
//!
//! These Cargo features enable new functionality:
Expand All @@ -174,6 +197,7 @@
//! [unicode-linebreak]: https://docs.rs/unicode-linebreak/
//! [unicode-width]: https://docs.rs/unicode-width/
//! [smawk]: https://docs.rs/smawk/
//! [binary-sizes demo]: https://github.com/mgeisler/textwrap/tree/master/examples/binary-sizes
//! [textwrap-macros]: https://docs.rs/textwrap-macros/
//! [terminal_size]: https://docs.rs/terminal_size/
//! [hyphenation]: https://docs.rs/hyphenation/
Expand Down

0 comments on commit 1ece89e

Please sign in to comment.