Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use f64 instead of usize for fragment widths #421

Merged
merged 3 commits into from
Jan 9, 2022
Merged

Conversation

mgeisler
Copy link
Owner

@mgeisler mgeisler commented Jan 2, 2022

This changes the type used for internal width computations in the wrap algorithms. Before, we used usize to represent the fragment widths and for the line widths. This could make the optimal-fit wrapping algorithm overflow when it tries to compute the optimal wrapping cost. The problem is that the algorithm computes a cost using integer values formed by

(line_width - target_width)**2

When line_width is near usize::MAX, this computation can easily overflow.

By using an f64 for the cost computation, we achieve two things:

  • A much larger range for the cost computation: f64::MAX is about 1.8e308 whereas u64::MAX is only 1.8e19. Computing the cost with a fragment width in the range of u64, will thus not exceed 3e38, something which is easily represented with a f64. This means that wrapping fragments derived from a &str cannot overflow.

    Overflows can still be triggered when fragments with extreme proportions are formed directly. The boundary seems to be around 1e170 with fragment widths above this limit triggering overflows.

  • Applications which wrap text using proportional fonts will already be operating with widths measured in floating point units. Using such units internally makes life easier for such applications, as shown by the changes in the Wasm demo.

Fixes #247
Fixes #416

@mgeisler mgeisler force-pushed the f64-fragment-widths branch 2 times, most recently from 6948a3d to 023bb76 Compare January 3, 2022 01:09
mgeisler added a commit that referenced this pull request Jan 9, 2022
The optimization problem solved by the optimal-fit algorithm is
fundamentally a minimization problem. It is therefore not sensible to
allow negative penalties since all penalties are there to discourage
certain features:

* `nline_penalty` discourages breaks with more lines than necessary,

* `overflow_penalty` discourages lines longer than the line width,

* `short_last_line_penalty` discourages short last lines,

* `hyphen_penalty` discourages hyphenation

Making this change surfaces the overflow bug behind #247 and #416.
This will be fixed next via #421 and this commit can be seen as a way
of simplifying that PR.
mgeisler added a commit that referenced this pull request Jan 9, 2022
The optimization problem solved by the optimal-fit algorithm is
fundamentally a minimization problem. It is therefore not sensible to
allow negative penalties since all penalties are there to discourage
certain features:

* `nline_penalty` discourages breaks with more lines than necessary,

* `overflow_penalty` discourages lines longer than the line width,

* `short_last_line_penalty` discourages short last lines,

* `hyphen_penalty` discourages hyphenation

Making this change surfaces the overflow bug behind #247 and #416.
This will be fixed next via #421 and this commit can be seen as a way
of simplifying that PR.
mgeisler added a commit that referenced this pull request Jan 9, 2022
The optimization problem solved by the optimal-fit algorithm is
fundamentally a minimization problem. It is therefore not sensible to
allow negative penalties since all penalties are there to discourage
certain features:

* `nline_penalty` discourages breaks with more lines than necessary,

* `overflow_penalty` discourages lines longer than the line width,

* `short_last_line_penalty` discourages short last lines,

* `hyphen_penalty` discourages hyphenation

Making this change surfaces the overflow bug behind #247 and #416.
This will be fixed next via #421 and this commit can be seen as a way
of simplifying that PR.
mgeisler added a commit that referenced this pull request Jan 9, 2022
The optimization problem solved by the optimal-fit algorithm is
fundamentally a minimization problem. It is therefore not sensible to
allow negative penalties since all penalties are there to discourage
certain features:

* `nline_penalty` discourages breaks with more lines than necessary,

* `overflow_penalty` discourages lines longer than the line width,

* `short_last_line_penalty` discourages short last lines,

* `hyphen_penalty` discourages hyphenation

Making this change surfaces the overflow bug behind #247 and #416.
This will be fixed next via #421 and this commit can be seen as a way
of simplifying that PR.
@mgeisler mgeisler force-pushed the f64-fragment-widths branch 2 times, most recently from dbcc245 to 432f20d Compare January 9, 2022 10:37
This changes the type used for internal width computations in the wrap
algorithms. Before, we used `usize` to represent the fragment widths
and for the line widths. This could make the optimal-fit wrapping
algorithm overflow when it tries to compute the optimal wrapping cost.
The problem is that the algorithm computes a cost using integer values
formed by

    (line_width - target_width)**2

When `line_width` is near `usize::MAX`, this computation can easily
overflow.

By using an `f64` for the cost computation, we achieve two things:

* A much larger range for the cost computation: `f64::MAX` is about
  1.8e308 whereas `u64::MAX` is only 1.8e19. Computing the cost with a
  fragment width in the range of `u64`, will thus not exceed 3e38,
  something which is easily represented with a `f64`. This means that
  wrapping fragments derived from a `&str` cannot overflow.

  Overflows can still be triggered when fragments with extreme
  proportions are formed directly. The boundary seems to be around
  1e170 with fragment widths above this limit triggering overflows.

* Applications which wrap text using proportional fonts will already
  be operating with widths measured in floating point units. Using
  such units internally makes life easier for such applications, as
  shown by the changes in the Wasm demo.

Fixes #247
Fixes #416
This tests the wrapping using fragments with widths which could come
from a &str.
This changes the panics in `wrap_optimal_fit` to a `Result` type,
allowing clients to catch them.
@mgeisler mgeisler merged commit 0f2183e into master Jan 9, 2022
@mgeisler mgeisler deleted the f64-fragment-widths branch January 9, 2022 11:06
mgeisler added a commit that referenced this pull request Feb 5, 2022
Following the advice from [1], this PR updates Cargo.toml to use
precise version numbers for all dependencies. The latest versions at
the time of writing are used.

It turns out that we made precisely the mistake mentioned in the post:

```
% cargo -Z minimal-versions update
% cargo test
```

failed on nightly because of the dependency on smawk 0.3: we need
0.3.1 after #421.

[1]: https://users.rust-lang.org/t/psa-please-specify-precise-dependency-versions-in-cargo-toml/71277
mgeisler added a commit that referenced this pull request Feb 5, 2022
Following the advice from [1], this PR updates Cargo.toml to use
precise version numbers for all dependencies. The latest versions at
the time of writing are used.

It turns out that we made precisely the mistake mentioned in the post:

```
% cargo -Z minimal-versions update
% cargo test
```

failed on nightly because of the dependency on smawk 0.3: we need
0.3.1 after #421.

[1]: https://users.rust-lang.org/t/psa-please-specify-precise-dependency-versions-in-cargo-toml/71277
@github-actions github-actions bot mentioned this pull request Feb 27, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
1 participant