Skip to content

Commit ab48c06

Browse files
authored
feat(no_std)!: option to disable layout cache for no_std compatibility (#1795)
Resolves #1780 BREAKING CHANGE: Disabling `default-features` will now disable layout cache, which can have a negative impact on performance. `Layout::init_cache` and `Layout::DEFAULT_CACHE_SIZE` are now only available if `layout-cache` feature is enabled.
1 parent 09173d1 commit ab48c06

File tree

5 files changed

+69
-17
lines changed

5 files changed

+69
-17
lines changed

BREAKING-CHANGES.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ This is a quick summary of the sections below:
2020
- `Backend` now uses `Self::Error` for error handling instead of `std::io::Error`
2121
- `Terminal<B>` now uses `B::Error` for error handling instead of `std::io::Error`
2222
- `TestBackend` now uses `core::convert::Infallible` for error handling instead of `std::io::Error`
23+
- Disabling `default-features` will now disable layout cache, which can have a negative impact on performance
24+
- `Layout::init_cache` and `Layout::DEFAULT_CACHE_SIZE` are now only available if `layout-cache` feature is enabled
2325
- [v0.29.0](#v0290)
2426
- `Sparkline::data` takes `IntoIterator<Item = SparklineBar>` instead of `&[u64]` and is no longer const
2527
- Removed public fields from `Rect` iterators
@@ -84,6 +86,26 @@ This is a quick summary of the sections below:
8486

8587
## Unreleased (0.30.0)
8688

89+
### `Layout::init_cache` and `Layout::DEFAULT_CACHE_SIZE` are now only available if `layout-cache` feature is enabled ([#1795])
90+
91+
[#1795]: https://github.com/ratatui/ratatui/pull/1795
92+
93+
Previously, `Layout::init_cache` and `Layout::DEFAULT_CACHE_SIZE` were available independently of
94+
enabled feature flags.
95+
96+
### Disabling `default-features` will now disable layout cache, which can have a negative impact on performance ([#1795])
97+
98+
[#1795]: https://github.com/ratatui/ratatui/pull/1795
99+
100+
Layout cache is now opt-in in `ratatui-core` and enabled by default in `ratatui`. If app doesn't
101+
make use of `no_std`-compatibility, and disables `default-feature`, it is recommended to explicitly
102+
re-enable layout cache. Not doing so may impact performance.
103+
104+
```diff
105+
- ratatui = { version = "0.29.0", default-features = false }
106+
+ ratatui = { version = "0.30.0", default-features = false, features = ["layout-cache"] }
107+
```
108+
87109
### `TestBackend` now uses `core::convert::Infallible` for error handling instead of `std::io::Error` ([#1823])
88110

89111
[#1823]: https://github.com/ratatui/ratatui/pull/1823

ratatui-core/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ rustdoc-args = ["--cfg", "docsrs"]
2424
[features]
2525
default = []
2626

27+
## enables std
28+
std = []
29+
30+
## enables layout cache
31+
layout-cache = ["std"]
32+
2733
## enables conversions to / from colors, modifiers, and styles in the ['anstyle'] crate
2834
anstyle = ["dep:anstyle"]
2935

ratatui-core/src/layout/layout.rs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
use alloc::format;
22
use alloc::rc::Rc;
33
use alloc::vec::Vec;
4-
use core::cell::RefCell;
54
use core::iter;
65
use core::num::NonZeroUsize;
7-
use std::{dbg, thread_local};
6+
use std::dbg;
87

98
use hashbrown::HashMap;
109
use itertools::Itertools;
@@ -39,8 +38,9 @@ type Cache = LruCache<(Rect, Layout), (Segments, Spacers)>;
3938
// calculations.
4039
const FLOAT_PRECISION_MULTIPLIER: f64 = 100.0;
4140

42-
thread_local! {
43-
static LAYOUT_CACHE: RefCell<Cache> = RefCell::new(Cache::new(
41+
#[cfg(feature = "layout-cache")]
42+
std::thread_local! {
43+
static LAYOUT_CACHE: core::cell::RefCell<Cache> = core::cell::RefCell::new(Cache::new(
4444
NonZeroUsize::new(Layout::DEFAULT_CACHE_SIZE).unwrap(),
4545
));
4646
}
@@ -188,6 +188,8 @@ impl Layout {
188188
/// bit more to make it a round number. This gives enough entries to store a layout for every
189189
/// row and every column, twice over, which should be enough for most apps. For those that need
190190
/// more, the cache size can be set with [`Layout::init_cache()`].
191+
/// This const is unused if layout cache is disabled.
192+
#[cfg(feature = "layout-cache")]
191193
pub const DEFAULT_CACHE_SIZE: usize = 500;
192194

193195
/// Creates a new layout with default values.
@@ -280,8 +282,9 @@ impl Layout {
280282
/// grows until `cache_size` is reached.
281283
///
282284
/// By default, the cache size is [`Self::DEFAULT_CACHE_SIZE`].
285+
#[cfg(feature = "layout-cache")]
283286
pub fn init_cache(cache_size: NonZeroUsize) {
284-
LAYOUT_CACHE.with_borrow_mut(|c| c.resize(cache_size));
287+
LAYOUT_CACHE.with_borrow_mut(|cache| cache.resize(cache_size));
285288
}
286289

287290
/// Set the direction of the layout.
@@ -653,11 +656,18 @@ impl Layout {
653656
/// );
654657
/// ```
655658
pub fn split_with_spacers(&self, area: Rect) -> (Segments, Spacers) {
656-
LAYOUT_CACHE.with_borrow_mut(|c| {
657-
let key = (area, self.clone());
658-
c.get_or_insert(key, || self.try_split(area).expect("failed to split"))
659-
.clone()
660-
})
659+
let split = || self.try_split(area).expect("failed to split");
660+
661+
#[cfg(feature = "layout-cache")]
662+
{
663+
LAYOUT_CACHE.with_borrow_mut(|cache| {
664+
let key = (area, self.clone());
665+
cache.get_or_insert(key, split).clone()
666+
})
667+
}
668+
669+
#[cfg(not(feature = "layout-cache"))]
670+
split()
661671
}
662672

663673
fn try_split(&self, area: Rect) -> Result<(Segments, Spacers), AddConstraintError> {
@@ -1200,14 +1210,15 @@ mod tests {
12001210
}
12011211

12021212
#[test]
1213+
#[cfg(feature = "layout-cache")]
12031214
fn cache_size() {
1204-
LAYOUT_CACHE.with_borrow(|c| {
1205-
assert_eq!(c.cap().get(), Layout::DEFAULT_CACHE_SIZE);
1215+
LAYOUT_CACHE.with_borrow(|cache| {
1216+
assert_eq!(cache.cap().get(), Layout::DEFAULT_CACHE_SIZE);
12061217
});
12071218

12081219
Layout::init_cache(NonZeroUsize::new(10).unwrap());
1209-
LAYOUT_CACHE.with_borrow(|c| {
1210-
assert_eq!(c.cap().get(), 10);
1220+
LAYOUT_CACHE.with_borrow(|cache| {
1221+
assert_eq!(cache.cap().get(), 10);
12111222
});
12121223
}
12131224

ratatui/Cargo.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ rustdoc-args = ["--cfg", "docsrs"]
2525
#!
2626
## By default, we enable the crossterm backend as this is a reasonable choice for most applications
2727
## as it is supported on Linux/Mac/Windows systems. We also enable the `underline-color` feature
28-
## which allows you to set the underline color of text and the `macros` feature which provides
29-
## some useful macros.
30-
default = ["crossterm", "underline-color", "all-widgets", "macros"]
28+
## which allows you to set the underline color of text, the `macros` feature which provides
29+
## some useful macros and `layout-cache` which speeds up layout cache calculations
30+
## in `std`-enabled environments.
31+
default = ["crossterm", "underline-color", "all-widgets", "macros", "layout-cache"]
3132
#! Generally an application will only use one backend, so you should only enable one of the following features:
3233
## enables the [`CrosstermBackend`](backend::CrosstermBackend) backend and adds a dependency on [`crossterm`].
3334
crossterm = ["dep:ratatui-crossterm"]
@@ -48,6 +49,9 @@ serde = [
4849
"ratatui-termwiz?/serde",
4950
]
5051

52+
## enables layout cache
53+
layout-cache = ["ratatui-core/layout-cache"]
54+
5155
## enables conversions from colors in the [`palette`] crate to [`Color`](crate::style::Color).
5256
palette = ["ratatui-core/palette", "dep:palette"]
5357

xtask/src/commands/backend.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ impl Run for TestBackend {
5454
Backend::Termion => "termion",
5555
Backend::Termwiz => "termwiz",
5656
};
57+
// This is a temporary hack to run tests both with and without layout cache.
58+
// https://github.com/ratatui/ratatui/issues/1820
59+
run_cargo(vec![
60+
"test",
61+
"--all-targets",
62+
"--no-default-features",
63+
"--features",
64+
format!("{backend},layout-cache").as_str(),
65+
])?;
5766
run_cargo(vec![
5867
"test",
5968
"--all-targets",

0 commit comments

Comments
 (0)