Use `*const ()` to represent `CompactLength` and add implementation for 32 bit platforms (#789)
* Use *const() to represent CompactLength and add implementation for 32 bit platforms
Signed-off-by: Nico Burns <nico@nicoburns.com>
* Add strict_provenance feature
* Add strict_provenance feature to benchmarks
* Fix serde impls for CompactLength
Signed-off-by: Nico Burns <nico@nicoburns.com>
---------
Signed-off-by: Nico Burns <nico@nicoburns.com>
8d2a036c193d008e1364d4fb37ba442c852c7525 · por Nico Burns · (hace 1 año) · padre 9da7a72b9 · ver el árbol en este commit
Delta semántico · 96
examples/custom_tree_owned_partial.rs
examples/custom_tree_owned_unsafe.rs
examples/custom_tree_vec.rs
src/compute/grid/explicit_grid.rs
src/compute/grid/track_sizing.rs
src/compute/grid/types/grid_item.rs
src/style/compact_length.rs
diff --git a/Cargo.toml b/Cargo.toml
index ef7b9f669..c7bba5fa5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -57,6 +57,8 @@ grid = ["alloc", "dep:grid"]
content_size = []
## Causes algorithms to stores detailed information of the nodes in TaffyTree, with only CSS Grid supporting this.
detailed_layout_info = []
+## Use strict provenance APIs for pointer manipulation. Using this feature requires Rust 1.84 or higher.
+strict_provenance = []
#! ### Taffy Tree
diff --git a/benches/Cargo.toml b/benches/Cargo.toml
index d4e72bc4b..ec9a30af5 100644
--- a/benches/Cargo.toml
+++ b/benches/Cargo.toml
@@ -27,6 +27,7 @@ yoga = ["dep:yoga", "dep:slotmap", "dep:ordered-float"]
yoga-super-deep = ["yoga"]
taffy03 = ["dep:taffy_03"]
content_size = ["taffy/content_size"]
+strict_provenance = ["taffy/strict_provenance"]
small = []
large = []
diff --git a/examples/custom_tree_owned_partial.rs b/examples/custom_tree_owned_partial.rs
index a53af338e..fbf4c435b 100644
--- a/examples/custom_tree_owned_partial.rs
+++ b/examples/custom_tree_owned_partial.rs
@@ -146,7 +146,7 @@ impl taffy::LayoutPartialTree for Node {
self.node_from_id_mut(node_id).layout = *layout
}
- fn resolve_calc_value(&self, _val: u64, _basis: f32) -> f32 {
+ fn resolve_calc_value(&self, _val: *const (), _basis: f32) -> f32 {
0.0
}
diff --git a/examples/custom_tree_owned_unsafe.rs b/examples/custom_tree_owned_unsafe.rs
index c5fa77ad1..50481f207 100644
--- a/examples/custom_tree_owned_unsafe.rs
+++ b/examples/custom_tree_owned_unsafe.rs
@@ -143,7 +143,7 @@ impl LayoutPartialTree for StatelessLayoutTree {
unsafe { node_from_id_mut(node_id).unrounded_layout = *layout };
}
- fn resolve_calc_value(&self, _val: u64, _basis: f32) -> f32 {
+ fn resolve_calc_value(&self, _val: *const (), _basis: f32) -> f32 {
0.0
}
diff --git a/examples/custom_tree_vec.rs b/examples/custom_tree_vec.rs
index 5a9db60ae..20684a2e6 100644
--- a/examples/custom_tree_vec.rs
+++ b/examples/custom_tree_vec.rs
@@ -152,7 +152,7 @@ impl taffy::LayoutPartialTree for Tree {
self.node_from_id_mut(node_id).unrounded_layout = *layout;
}
- fn resolve_calc_value(&self, _val: u64, _basis: f32) -> f32 {
+ fn resolve_calc_value(&self, _val: *const (), _basis: f32) -> f32 {
0.0
}
diff --git a/src/compute/grid/explicit_grid.rs b/src/compute/grid/explicit_grid.rs
index fbb5168bd..709c0dd36 100644
--- a/src/compute/grid/explicit_grid.rs
+++ b/src/compute/grid/explicit_grid.rs
@@ -14,7 +14,7 @@ pub(crate) fn compute_explicit_grid_size_in_axis(
style: &impl GridContainerStyle,
template: &[TrackSizingFunction],
inner_container_size: Size<Option<f32>>,
- resolve_calc_value: impl Fn(u64, f32) -> f32,
+ resolve_calc_value: impl Fn(*const (), f32) -> f32,
axis: AbsoluteAxis,
) -> u16 {
// If template contains no tracks, then there are trivially zero explicit tracks
@@ -106,7 +106,7 @@ pub(crate) fn compute_explicit_grid_size_in_axis(
fn track_definite_value(
sizing_function: &NonRepeatedTrackSizingFunction,
parent_size: Option<f32>,
- calc_resolver: impl Fn(u64, f32) -> f32,
+ calc_resolver: impl Fn(*const (), f32) -> f32,
) -> f32 {
let max_size = sizing_function.max.definite_value(parent_size, &calc_resolver);
let min_size = sizing_function.min.definite_value(parent_size, &calc_resolver);
diff --git a/src/compute/grid/track_sizing.rs b/src/compute/grid/track_sizing.rs
index 8bd4e7f34..0b353b19a 100644
--- a/src/compute/grid/track_sizing.rs
+++ b/src/compute/grid/track_sizing.rs
@@ -110,7 +110,7 @@ where
/// Simple pass-through function to `LayoutPartialTreeExt::calc`
#[inline(always)]
- fn calc(&self, val: u64, basis: f32) -> f32 {
+ fn calc(&self, val: *const (), basis: f32) -> f32 {
self.tree.calc(val, basis)
}
diff --git a/src/compute/grid/types/grid_item.rs b/src/compute/grid/types/grid_item.rs
index 28f81a125..a84c77429 100644
--- a/src/compute/grid/types/grid_item.rs
+++ b/src/compute/grid/types/grid_item.rs
@@ -189,7 +189,7 @@ impl GridItem {
axis: AbstractAxis,
axis_tracks: &[GridTrack],
axis_parent_size: Option<f32>,
- resolve_calc_value: &dyn Fn(u64, f32) -> f32,
+ resolve_calc_value: &dyn Fn(*const (), f32) -> f32,
) -> Option<f32> {
let spanned_tracks = &axis_tracks[self.track_range_excluding_lines(axis)];
let tracks_all_fixed = spanned_tracks.iter().all(|track| {
@@ -215,7 +215,7 @@ impl GridItem {
axis: AbstractAxis,
axis_tracks: &[GridTrack],
axis_parent_size: Option<f32>,
- resolve_calc_value: &dyn Fn(u64, f32) -> f32,
+ resolve_calc_value: &dyn Fn(*const (), f32) -> f32,
) -> Option<f32> {
let spanned_tracks = &axis_tracks[self.track_range_excluding_lines(axis)];
let tracks_all_fixed = spanned_tracks.iter().all(|track| {
diff --git a/src/compute/leaf.rs b/src/compute/leaf.rs
index 9adbcc9cf..be7e49b02 100644
--- a/src/compute/leaf.rs
+++ b/src/compute/leaf.rs
@@ -15,7 +15,7 @@ use core::unreachable;
pub fn compute_leaf_layout<MeasureFunction>(
inputs: LayoutInput,
style: &impl CoreStyle,
- resolve_calc_value: impl Fn(u64, f32) -> f32,
+ resolve_calc_value: impl Fn(*const (), f32) -> f32,
measure_function: MeasureFunction,
) -> LayoutOutput
where
diff --git a/src/style/compact_length.rs b/src/style/compact_length.rs
index cca0d48b6..7e6be88f8 100644
--- a/src/style/compact_length.rs
+++ b/src/style/compact_length.rs
@@ -4,43 +4,233 @@ use super::LengthPercentage;
use crate::style_helpers::{
FromFr, FromLength, FromPercent, TaffyAuto, TaffyFitContent, TaffyMaxContent, TaffyMinContent, TaffyZero,
};
-use compat::{f32_from_bits, f32_to_bits};
-/// A representation of a length as a compact 64-bit tagged pointer
-#[derive(Copy, Clone, PartialEq, Debug)]
-#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-pub struct CompactLength(u64);
+/// Note: these two functions are copied directly from the std (core) library. But by duplicating them
+/// here we can reduce MSRV from 1.84 all the way down to 1.65 while retaining const constructors and
+/// strict pointer provenance
+mod compat {
+ #![allow(unsafe_code)]
+ #![allow(clippy::transmute_float_to_int)]
-impl CompactLength {
- // Masks
+ /// Raw transmutation from `f32` to `u32`.
+ pub const fn f32_to_bits(val: f32) -> u32 {
+ // SAFETY: `u32` is a plain old datatype so we can always transmute to it.
+ unsafe { core::mem::transmute(val) }
+ }
+ /// Raw transmutation from `u32` to `f32`.
+ pub const fn f32_from_bits(v: u32) -> f32 {
+ // SAFETY: `u32` is a plain old datatype so we can always transmute from it.
+ unsafe { core::mem::transmute(v) }
+ }
+
+ /// Tag a pointer preserving provenance (requires Rust 1.84)
+ #[inline(always)]
+ #[cfg(all(target_pointer_width = "64", feature = "strict_provenance"))]
+ pub fn tag_ptr(ptr: *const (), tag: usize) -> *const () {
+ ptr.map_addr(|a| a | tag)
+ }
+
+ /// Tag a pointer exposing provenance (works back to Rust 1.0)
+ #[inline(always)]
+ #[cfg(all(target_pointer_width = "64", not(feature = "strict_provenance")))]
+ pub fn tag_ptr(ptr: *const (), tag: usize) -> *const () {
+ (ptr as usize | tag) as *const ()
+ }
+}
+
+#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
+std::compile_error!("Taffy only supports targets with a pointer width of 32 or 64 bits");
+
+// /// Error serializing
+// enum SerializeErr {
+// /// Serializing calc values is not supported
+// CalcValue,
+// }
+
+/// CompactLengthInner implementation for 64 bit platforms
+#[cfg(target_pointer_width = "64")]
+mod inner {
+ use super::compat::{f32_from_bits, f32_to_bits, tag_ptr};
/// The low byte (8 bits)
- pub const TAG_MASK: u64 = 0b11111111;
+ const TAG_MASK: usize = 0b11111111;
/// The low 3 bits
- pub const CALC_TAG_MASK: u64 = 0b111;
- /// The high 63 bits
- pub const CALC_PTR_MASK: u64 = u64::MAX ^ 0b111;
+ const CALC_TAG_MASK: usize = 0b111;
+ // The high 63 bits
+ // const CALC_PTR_MASK: usize = usize::MAX ^ 0b111;
+
+ /// On 64 bit platforms the tag, value and pointer are packed into a single 64 bit pointer
+ ///
+ /// The tagged pointer always has a tag and may contain an f32 value or a pointer
+ /// (or neither) depending on the variant indicated by the tag.
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ pub(super) struct CompactLengthInner {
+ /// The tagged pointer
+ tagged_ptr: *const (),
+ }
+ impl CompactLengthInner {
+ /// Construct a `CompactLengthInner` from a tag and pointer
+ #[inline(always)]
+ pub(super) fn from_ptr(ptr: *const (), tag: usize) -> Self {
+ let tagged_ptr = tag_ptr(ptr, tag);
+ Self { tagged_ptr }
+ }
+
+ /// Construct a `CompactLengthInner` from a tag and numeric value
+ #[inline(always)]
+ pub(super) const fn from_val(val: f32, tag: usize) -> Self {
+ let tagged_ptr = (((f32_to_bits(val) as usize) << 32) | tag) as *const ();
+ Self { tagged_ptr }
+ }
- // Primary tags
+ /// Construct a `CompactLengthInner` from only a tag
+ #[inline(always)]
+ pub(super) const fn from_tag(tag: usize) -> Self {
+ let tagged_ptr = tag as *const ();
+ Self { tagged_ptr }
+ }
+
+ /// Get the calc tag (low 3 bits)
+ #[inline(always)]
+ pub(super) fn calc_tag(self) -> usize {
+ (self.tagged_ptr as usize) & CALC_TAG_MASK
+ }
+ /// Get the general tag (low 8 bits)
+ #[inline(always)]
+ pub(super) fn tag(self) -> usize {
+ (self.tagged_ptr as usize) & TAG_MASK
+ }
+
+ /// Get the pointer value
+ #[inline(always)]
+ pub(super) fn ptr(self) -> *const () {
+ self.tagged_ptr
+ }
+
+ /// Get the numeric value
+ #[inline(always)]
+ pub(super) fn value(self) -> f32 {
+ f32_from_bits((self.tagged_ptr as usize >> 32) as u32)
+ }
+
+ /// Get the serialized value.
+ #[inline(always)]
+ #[cfg(feature = "serde")]
+ pub(super) fn serialized(self) -> u64 {
+ (self.tagged_ptr as usize as u64).rotate_left(32)
+ }
+
+ /// Derialized from a value.
+ #[inline(always)]
+ #[cfg(feature = "serde")]
+ pub(super) fn from_serialized(value: u64) -> Self {
+ Self { tagged_ptr: value.rotate_right(32) as usize as *const () }
+ }
+ }
+}
+
+/// CompactLengthInner implementation for 32 bit platforms
+#[cfg(target_pointer_width = "32")]
+mod inner {
+ use super::compat::{f32_from_bits, f32_to_bits};
+
+ /// On 32 bit platforms the tag is stored separately.
+ /// Either an f32 value or a pointer (or neither) are packed into the ptr field
+ /// depending on the variant indicated by the tag
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ pub(super) struct CompactLengthInner {
+ /// The tag indicating what kind of value we are storing
+ tag: usize,
+ /// The pointer of numeric value
+ ptr: *const (),
+ }
+
+ impl CompactLengthInner {
+ /// Construct a `CompactLengthInner` from a tag and pointer
+ #[inline(always)]
+ pub(super) fn from_ptr(ptr: *const (), tag: usize) -> Self {
+ Self { ptr, tag }
+ }
+
+ /// Construct a `CompactLengthInner` from a tag and numeric value
+ #[inline(always)]
+ pub(super) const fn from_val(val: f32, tag: usize) -> Self {
+ Self { ptr: f32_to_bits(val) as usize as *const (), tag }
+ }
+
+ /// Construct a `CompactLengthInner` from only a tag
+ #[inline(always)]
+ pub(super) const fn from_tag(tag: usize) -> Self {
+ Self { ptr: 0 as *const (), tag }
+ }
+
+ /// Get the calc tag (low 3 bits)
+ #[inline(always)]
+ pub(super) fn calc_tag(self) -> usize {
+ self.tag
+ }
+
+ /// Get the general tag (low 8 bits)
+ #[inline(always)]
+ pub(super) fn tag(self) -> usize {
+ self.tag
+ }
+
+ /// Get the pointer value
+ #[inline(always)]
+ pub(super) fn ptr(self) -> *const () {
+ self.ptr
+ }
+
+ /// Get the numeric value
+ #[inline(always)]
+ pub(super) fn value(self) -> f32 {
+ f32_from_bits(self.ptr as u32)
+ }
+
+ /// Get the serialized value.
+ #[inline(always)]
+ #[cfg(feature = "serde")]
+ pub(super) fn serialized(self) -> u64 {
+ (self.tag as u64) << 32 | (self.ptr as u64)
+ }
+
+ /// Derialized from a value.
+ #[inline(always)]
+ #[cfg(feature = "serde")]
+ pub(super) fn from_serialized(value: u64) -> Self {
+ Self { tag: value >> 32 as usize, ptr: value & 0xFFFFFFFF as usize as *const () }
+ }
+ }
+}
+
+use inner::CompactLengthInner;
+
+/// A representation of a length as a compact 64-bit tagged pointer
+#[derive(Copy, Clone, PartialEq, Debug)]
+#[repr(transparent)]
+pub struct CompactLength(CompactLengthInner);
+
+impl CompactLength {
/// The tag indicating a calc() value
- pub const CALC_TAG: u64 = 0b000;
+ pub const CALC_TAG: usize = 0b000;
/// The tag indicating a length value
- pub const LENGTH_TAG: u64 = 0b0000_0001;
+ pub const LENGTH_TAG: usize = 0b0000_0001;
/// The tag indicating a percentage value
- pub const PERCENT_TAG: u64 = 0b0000_0010;
+ pub const PERCENT_TAG: usize = 0b0000_0010;
/// The tag indicating an auto value
- pub const AUTO_TAG: u64 = 0b0000_0011;
+ pub const AUTO_TAG: usize = 0b0000_0011;
/// The tag indicating an fr value
- pub const FR_TAG: u64 = 0b0000_0100;
+ pub const FR_TAG: usize = 0b0000_0100;
/// The tag indicating a min-content value
- pub const MIN_CONTENT_TAG: u64 = 0b00000111;
+ pub const MIN_CONTENT_TAG: usize = 0b00000111;
/// The tag indicating a max-content value
- pub const MAX_CONTENT_TAG: u64 = 0b00001111;
+ pub const MAX_CONTENT_TAG: usize = 0b00001111;
/// The tag indicating a fit-content value with px limit
- pub const FIT_CONTENT_PX_TAG: u64 = 0b00010111;
+ pub const FIT_CONTENT_PX_TAG: usize = 0b00010111;
/// The tag indicating a fit-content value with percent limit
- pub const FIT_CONTENT_PERCENT_TAG: u64 = 0b00011111;
+ pub const FIT_CONTENT_PERCENT_TAG: usize = 0b00011111;
}
impl CompactLength {
@@ -48,7 +238,7 @@ impl CompactLength {
/// to in their application (pixels, logical pixels, mm, etc) as they see fit.
#[inline(always)]
pub const fn length(val: f32) -> Self {
- Self(((f32_to_bits(val) as u64) << 32) | Self::LENGTH_TAG)
+ Self(CompactLengthInner::from_val(val, Self::LENGTH_TAG))
}
/// A percentage length relative to the size of the containing block.
@@ -56,7 +246,7 @@ impl CompactLength {
/// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
#[inline(always)]
pub const fn percent(val: f32) -> Self {
- Self(((f32_to_bits(val) as u64) << 32) | Self::PERCENT_TAG)
+ Self(CompactLengthInner::from_val(val, Self::PERCENT_TAG))
}
/// A `calc()` value. The value passed here is treated as an opaque handle to
@@ -67,14 +257,14 @@ impl CompactLength {
pub fn calc(ptr: *const ()) -> Self {
assert_ne!(ptr as u64, 0);
assert_eq!(ptr as u64 & 0b111, 0);
- Self(ptr as u64 | Self::CALC_TAG)
+ Self(CompactLengthInner::from_ptr(ptr, Self::CALC_TAG))
}
/// The dimension should be automatically computed according to algorithm-specific rules
/// regarding the default size of boxes.
#[inline(always)]
pub const fn auto() -> Self {
- Self(Self::AUTO_TAG)
+ Self(CompactLengthInner::from_tag(Self::AUTO_TAG))
}
/// The dimension as a fraction of the total available grid space (`fr` units in CSS)
@@ -82,21 +272,21 @@ impl CompactLength {
/// Spec: <https://www.w3.org/TR/css3-grid-layout/#fr-unit>
#[inline(always)]
pub const fn fr(val: f32) -> Self {
- Self(((f32_to_bits(val) as u64) << 32) | Self::FR_TAG)
+ Self(CompactLengthInner::from_val(val, Self::FR_TAG))
}
/// The size should be the "min-content" size.
/// This is the smallest size that can fit the item's contents with ALL soft line-wrapping opportunities taken
#[inline(always)]
pub const fn min_content() -> Self {
- Self(Self::MIN_CONTENT_TAG)
+ Self(CompactLengthInner::from_tag(Self::MIN_CONTENT_TAG))
}
/// The size should be the "max-content" size.
/// This is the smallest size that can fit the item's contents with NO soft line-wrapping opportunities taken
#[inline(always)]
pub const fn max_content() -> Self {
- Self(Self::MAX_CONTENT_TAG)
+ Self(CompactLengthInner::from_tag(Self::MAX_CONTENT_TAG))
}
/// The size should be computed according to the "fit content" formula:
@@ -110,7 +300,7 @@ impl CompactLength {
/// by the min-content and max-content sizes.
#[inline(always)]
pub const fn fit_content_px(limit: f32) -> Self {
- Self(((f32_to_bits(limit) as u64) << 32) | Self::FIT_CONTENT_PX_TAG)
+ Self(CompactLengthInner::from_val(limit, Self::FIT_CONTENT_PX_TAG))
}
/// The size should be computed according to the "fit content" formula:
@@ -124,74 +314,73 @@ impl CompactLength {
/// by the min-content and max-content sizes.
#[inline(always)]
pub const fn fit_content_percent(limit: f32) -> Self {
- Self(((f32_to_bits(limit) as u64) << 32) | Self::FIT_CONTENT_PERCENT_TAG)
+ Self(CompactLengthInner::from_val(limit, Self::FIT_CONTENT_PERCENT_TAG))
}
/// Get the primary tag
#[inline(always)]
- pub const fn tag(self) -> u64 {
- self.0 & Self::TAG_MASK
+ pub fn tag(self) -> usize {
+ self.0.tag()
}
/// Get the numeric value associated with the `CompactLength`
/// (e.g. the pixel value for a LENGTH variant)
#[inline(always)]
- pub const fn value(self) -> f32 {
- f32_from_bits((self.0 >> 32) as u32)
+ pub fn value(self) -> f32 {
+ self.0.value()
}
- /// Get the numeric value associated with the `CompactLength`
- /// (e.g. the pixel value for a LENGTH variant)
+ /// Get the calc pointer of the `CompactLength`
#[inline(always)]
- pub const fn calc_value(self) -> u64 {
- self.0
+ pub fn calc_value(self) -> *const () {
+ self.0.ptr()
}
/// Returns true if the value is 0 px
#[inline(always)]
- pub const fn is_calc(self) -> bool {
- self.0 & Self::CALC_TAG_MASK == 0
+ pub fn is_calc(self) -> bool {
+ self.0.calc_tag() == 0
}
/// Returns true if the value is 0 px
#[inline(always)]
- pub const fn is_zero(self) -> bool {
+ pub fn is_zero(self) -> bool {
self.0 == Self::ZERO.0
}
/// Returns true if the value is a length or percentage value
#[inline(always)]
- pub const fn is_length_or_percentage(self) -> bool {
+ pub fn is_length_or_percentage(self) -> bool {
matches!(self.tag(), Self::LENGTH_TAG | Self::PERCENT_TAG)
}
/// Returns true if the value is auto
#[inline(always)]
- pub const fn is_auto(self) -> bool {
+ pub fn is_auto(self) -> bool {
self.tag() == Self::AUTO_TAG
}
/// Returns true if the value is min-content
#[inline(always)]
- pub const fn is_min_content(self) -> bool {
+ pub fn is_min_content(self) -> bool {
matches!(self.tag(), Self::MIN_CONTENT_TAG)
}
/// Returns true if the value is max-content
#[inline(always)]
- pub const fn is_max_content(self) -> bool {
+ pub fn is_max_content(self) -> bool {
matches!(self.tag(), Self::MAX_CONTENT_TAG)
}
/// Returns true if the value is a fit-content(...) value
#[inline(always)]
- pub const fn is_fit_content(self) -> bool {
+ pub fn is_fit_content(self) -> bool {
matches!(self.tag(), Self::FIT_CONTENT_PX_TAG | Self::FIT_CONTENT_PERCENT_TAG)
}
/// Returns true if the value is max-content or a fit-content(...) value
#[inline(always)]
- pub const fn is_max_or_fit_content(self) -> bool {
+ pub fn is_max_or_fit_content(self) -> bool {
matches!(self.tag(), Self::MAX_CONTENT_TAG | Self::FIT_CONTENT_PX_TAG | Self::FIT_CONTENT_PERCENT_TAG)
}
@@ -217,7 +406,7 @@ impl CompactLength {
/// Returns true if the value is auto, min-content, max-content, or fit-content(...)
#[inline(always)]
- pub const fn is_intrinsic(self) -> bool {
+ pub fn is_intrinsic(self) -> bool {
matches!(
self.tag(),
Self::AUTO_TAG
@@ -230,13 +419,13 @@ impl CompactLength {
/// Returns true if the value is and fr value
#[inline(always)]
- pub const fn is_fr(self) -> bool {
+ pub fn is_fr(self) -> bool {
self.tag() == Self::FR_TAG
}
/// Whether the track sizing functions depends on the size of the parent node
#[inline(always)]
- pub const fn uses_percentage(self) -> bool {
+ pub fn uses_percentage(self) -> bool {
// TODO: handle calc() values
matches!(self.tag(), CompactLength::PERCENT_TAG | CompactLength::FIT_CONTENT_PERCENT_TAG) || self.is_calc()
}
@@ -244,10 +433,14 @@ impl CompactLength {
/// Resolve percentage values against the passed parent_size, returning Some(value)
/// Non-percentage values always return None.
#[inline(always)]
- pub fn resolved_percentage_size(self, parent_size: f32, calc_resolver: impl Fn(u64, f32) -> f32) -> Option<f32> {
+ pub fn resolved_percentage_size(
+ self,
+ parent_size: f32,
+ calc_resolver: impl Fn(*const (), f32) -> f32,
+ ) -> Option<f32> {
match self.tag() {
CompactLength::PERCENT_TAG => Some(self.value() * parent_size),
- _ if self.is_calc() => Some(calc_resolver(self.0, parent_size)),
+ _ if self.is_calc() => Some(calc_resolver(self.0.ptr(), parent_size)),
_ => None,
}
}
@@ -291,20 +484,32 @@ impl TaffyFitContent for CompactLength {
}
}
-/// Note: these two functions are copied directly from the std (core) library. But by duplicating them
-/// here we can reduce MSRV from 1.83 all the way down to 1.65 while retaining const constructors.
-mod compat {
- #![allow(unsafe_code)]
- #![allow(clippy::transmute_float_to_int)]
-
- /// Raw transmutation from `f32` to `u32`.
- pub const fn f32_to_bits(val: f32) -> u32 {
- // SAFETY: `u32` is a plain old datatype so we can always transmute to it.
- unsafe { core::mem::transmute(val) }
+#[cfg(feature = "serde")]
+impl serde::Serialize for CompactLength {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ if self.tag() == Self::CALC_TAG {
+ Err(serde::ser::Error::custom("Cannot serialize Calc value"))
+ } else {
+ serializer.serialize_u64(self.0.serialized())
+ }
}
- /// Raw transmutation from `u32` to `f32`.
- pub const fn f32_from_bits(v: u32) -> f32 {
- // SAFETY: `u32` is a plain old datatype so we can always transmute from it.
- unsafe { core::mem::transmute(v) }
+}
+
+#[cfg(feature = "serde")]
+impl<'de> serde::Deserialize<'de> for CompactLength {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ let bits: u64 = u64::deserialize(deserializer)?;
+ let value = Self(CompactLengthInner::from_serialized(bits));
+ if value.is_calc() {
+ Err(serde::de::Error::custom("Cannot deserialize Calc value"))
+ } else {
+ Ok(value)
+ }
}
}
diff --git a/src/style/dimension.rs b/src/style/dimension.rs
index 238805609..83cddd79b 100644
--- a/src/style/dimension.rs
+++ b/src/style/dimension.rs
@@ -42,7 +42,7 @@ impl LengthPercentage {
/// the actual calc representation and may be a pointer, index, etc.
///
/// The low 3 bits are used as a tag value and will be returned as 0.
- #[inline]
+ #[inline(always)]
pub fn calc(ptr: *const ()) -> Self {
Self(CompactLength::calc(ptr))
}
@@ -139,7 +139,7 @@ impl LengthPercentageAuto {
/// - Some(resolved) using the provided context for Percent variants
/// - None for Auto variants
#[inline(always)]
- pub fn resolve_to_option(self, context: f32, calc_resolver: impl Fn(u64, f32) -> f32) -> Option<f32> {
+ pub fn resolve_to_option(self, context: f32, calc_resolver: impl Fn(*const (), f32) -> f32) -> Option<f32> {
match self.0.tag() {
CompactLength::LENGTH_TAG => Some(self.0.value()),
CompactLength::PERCENT_TAG => Some(context * self.0.value()),
@@ -249,7 +249,7 @@ impl Dimension {
}
/// Get the raw `CompactLength` tag
- pub fn tag(self) -> u64 {
+ pub fn tag(self) -> usize {
self.0.tag()
}
diff --git a/src/style/grid.rs b/src/style/grid.rs
index e32e38f53..28c71dd6c 100644
--- a/src/style/grid.rs
+++ b/src/style/grid.rs
@@ -581,7 +581,11 @@ impl MaxTrackSizingFunction {
/// the passed available_space and returns if this results in a concrete value (which it
/// will if the available_space is `Some`). Otherwise returns None.
#[inline(always)]
- pub fn definite_value(self, parent_size: Option<f32>, calc_resolver: impl Fn(u64, f32) -> f32) -> Option<f32> {
+ pub fn definite_value(
+ self,
+ parent_size: Option<f32>,
+ calc_resolver: impl Fn(*const (), f32) -> f32,
+ ) -> Option<f32> {
match self.0.tag() {
CompactLength::LENGTH_TAG => Some(self.0.value()),
CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
@@ -597,7 +601,11 @@ impl MaxTrackSizingFunction {
/// - A fit-content sizing function with percentage argument (with definite available space)
/// All other kinds of track sizing function return None.
#[inline(always)]
- pub fn definite_limit(self, parent_size: Option<f32>, calc_resolver: impl Fn(u64, f32) -> f32) -> Option<f32> {
+ pub fn definite_limit(
+ self,
+ parent_size: Option<f32>,
+ calc_resolver: impl Fn(*const (), f32) -> f32,
+ ) -> Option<f32> {
match self.0.tag() {
CompactLength::FIT_CONTENT_PX_TAG => Some(self.0.value()),
CompactLength::FIT_CONTENT_PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
@@ -608,7 +616,11 @@ impl MaxTrackSizingFunction {
/// Resolve percentage values against the passed parent_size, returning Some(value)
/// Non-percentage values always return None.
#[inline(always)]
- pub fn resolved_percentage_size(self, parent_size: f32, calc_resolver: impl Fn(u64, f32) -> f32) -> Option<f32> {
+ pub fn resolved_percentage_size(
+ self,
+ parent_size: f32,
+ calc_resolver: impl Fn(*const (), f32) -> f32,
+ ) -> Option<f32> {
self.0.resolved_percentage_size(parent_size, calc_resolver)
}
@@ -764,7 +776,11 @@ impl MinTrackSizingFunction {
/// the passed available_space and returns if this results in a concrete value (which it
/// will if the available_space is `Some`). Otherwise returns `None`.
#[inline(always)]
- pub fn definite_value(self, parent_size: Option<f32>, calc_resolver: impl Fn(u64, f32) -> f32) -> Option<f32> {
+ pub fn definite_value(
+ self,
+ parent_size: Option<f32>,
+ calc_resolver: impl Fn(*const (), f32) -> f32,
+ ) -> Option<f32> {
match self.0.tag() {
CompactLength::LENGTH_TAG => Some(self.0.value()),
CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
@@ -776,7 +792,11 @@ impl MinTrackSizingFunction {
/// Resolve percentage values against the passed parent_size, returning Some(value)
/// Non-percentage values always return None.
#[inline(always)]
- pub fn resolved_percentage_size(self, parent_size: f32, calc_resolver: impl Fn(u64, f32) -> f32) -> Option<f32> {
+ pub fn resolved_percentage_size(
+ self,
+ parent_size: f32,
+ calc_resolver: impl Fn(*const (), f32) -> f32,
+ ) -> Option<f32> {
self.0.resolved_percentage_size(parent_size, calc_resolver)
}
diff --git a/src/tree/taffy_tree.rs b/src/tree/taffy_tree.rs
index 014c49952..4c4df42e5 100644
--- a/src/tree/taffy_tree.rs
+++ b/src/tree/taffy_tree.rs
@@ -338,7 +338,7 @@ where
}
#[inline(always)]
- fn resolve_calc_value(&self, _val: u64, _basis: f32) -> f32 {
+ fn resolve_calc_value(&self, _val: *const (), _basis: f32) -> f32 {
0.0
}
diff --git a/src/tree/traits.rs b/src/tree/traits.rs
index 9c291c824..191de42cb 100644
--- a/src/tree/traits.rs
+++ b/src/tree/traits.rs
@@ -181,7 +181,7 @@ pub trait LayoutPartialTree: TraversePartialTree {
fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_>;
/// Resolve calc value
- fn resolve_calc_value(&self, val: u64, basis: f32) -> f32;
+ fn resolve_calc_value(&self, val: *const (), basis: f32) -> f32;
/// Set the node's unrounded layout
fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout);
@@ -366,7 +366,7 @@ pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree {
/// Alias to `resolve_calc_value` with a shorter function name
#[inline(always)]
- fn calc(&self, val: u64, basis: f32) -> f32 {
+ fn calc(&self, val: *const (), basis: f32) -> f32 {
self.resolve_calc_value(val, basis)
}
}
diff --git a/src/util/resolve.rs b/src/util/resolve.rs
index e52861cec..a0e47b0ee 100644
--- a/src/util/resolve.rs
+++ b/src/util/resolve.rs
@@ -12,7 +12,7 @@ use crate::CompactLength;
/// Will return a `None` if it unable to resolve.
pub trait MaybeResolve<In, Out> {
/// Resolve a dimension that might be dependent on a context, with `None` as fallback value
- fn maybe_resolve(self, context: In, calc: impl Fn(u64, f32) -> f32) -> Out;
+ fn maybe_resolve(self, context: In, calc: impl Fn(*const (), f32) -> f32) -> Out;
}
/// Trait to encapsulate behaviour where we need to resolve from a
@@ -22,13 +22,13 @@ pub trait MaybeResolve<In, Out> {
/// Will return a default value if it unable to resolve.
pub trait ResolveOrZero<TContext, TOutput: TaffyZero> {
/// Resolve a dimension that might be dependent on a context, with a default fallback value
- fn resolve_or_zero(self, context: TContext, calc: impl Fn(u64, f32) -> f32) -> TOutput;
+ fn resolve_or_zero(self, context: TContext, calc: impl Fn(*const (), f32) -> f32) -> TOutput;
}
impl MaybeResolve<Option<f32>, Option<f32>> for LengthPercentage {
/// Converts the given [`LengthPercentage`] into an absolute length
/// Can return `None`
- fn maybe_resolve(self, context: Option<f32>, calc: impl Fn(u64, f32) -> f32) -> Option<f32> {
+ fn maybe_resolve(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> Option<f32> {
match self.0.tag() {
CompactLength::LENGTH_TAG => Some(self.0.value()),
CompactLength::PERCENT_TAG => context.map(|dim| dim * self.0.value()),
@@ -41,7 +41,7 @@ impl MaybeResolve<Option<f32>, Option<f32>> for LengthPercentage {
impl MaybeResolve<Option<f32>, Option<f32>> for LengthPercentageAuto {
/// Converts the given [`LengthPercentageAuto`] into an absolute length
/// Can return `None`
- fn maybe_resolve(self, context: Option<f32>, calc: impl Fn(u64, f32) -> f32) -> Option<f32> {
+ fn maybe_resolve(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> Option<f32> {
match self.0.tag() {
CompactLength::AUTO_TAG => None,
CompactLength::LENGTH_TAG => Some(self.0.value()),
@@ -56,7 +56,7 @@ impl MaybeResolve<Option<f32>, Option<f32>> for Dimension {
/// Converts the given [`Dimension`] into an absolute length
///
/// Can return `None`
- fn maybe_resolve(self, context: Option<f32>, calc: impl Fn(u64, f32) -> f32) -> Option<f32> {
+ fn maybe_resolve(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> Option<f32> {
match self.0.tag() {
CompactLength::AUTO_TAG => None,
CompactLength::LENGTH_TAG => Some(self.0.value()),
@@ -72,7 +72,7 @@ impl MaybeResolve<Option<f32>, Option<f32>> for Dimension {
impl<T: MaybeResolve<Option<f32>, Option<f32>>> MaybeResolve<f32, Option<f32>> for T {
/// Converts the given MaybeResolve value into an absolute length
/// Can return `None`
- fn maybe_resolve(self, context: f32, calc: impl Fn(u64, f32) -> f32) -> Option<f32> {
+ fn maybe_resolve(self, context: f32, calc: impl Fn(*const (), f32) -> f32) -> Option<f32> {
self.maybe_resolve(Some(context), calc)
}
}
@@ -80,7 +80,7 @@ impl<T: MaybeResolve<Option<f32>, Option<f32>>> MaybeResolve<f32, Option<f32>> f
// Generic MaybeResolve for Size
impl<In, Out, T: MaybeResolve<In, Out>> MaybeResolve<Size<In>, Size<Out>> for Size<T> {
/// Converts any `parent`-relative values for size into an absolute size
- fn maybe_resolve(self, context: Size<In>, calc: impl Fn(u64, f32) -> f32) -> Size<Out> {
+ fn maybe_resolve(self, context: Size<In>, calc: impl Fn(*const (), f32) -> f32) -> Size<Out> {
Size {
width: self.width.maybe_resolve(context.width, &calc),
height: self.height.maybe_resolve(context.height, &calc),
@@ -90,21 +90,21 @@ impl<In, Out, T: MaybeResolve<In, Out>> MaybeResolve<Size<In>, Size<Out>> for Si
impl ResolveOrZero<Option<f32>, f32> for LengthPercentage {
/// Will return a default value of result is evaluated to `None`
- fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(u64, f32) -> f32) -> f32 {
+ fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> f32 {
self.maybe_resolve(context, calc).unwrap_or(0.0)
}
}
impl ResolveOrZero<Option<f32>, f32> for LengthPercentageAuto {
/// Will return a default value of result is evaluated to `None`
- fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(u64, f32) -> f32) -> f32 {
+ fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> f32 {
self.maybe_resolve(context, calc).unwrap_or(0.0)
}
}
impl ResolveOrZero<Option<f32>, f32> for Dimension {
/// Will return a default value of result is evaluated to `None`
- fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(u64, f32) -> f32) -> f32 {
+ fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> f32 {
self.maybe_resolve(context, calc).unwrap_or(0.0)
}
}
@@ -112,7 +112,7 @@ impl ResolveOrZero<Option<f32>, f32> for Dimension {
// Generic ResolveOrZero for Size
impl<In, Out: TaffyZero, T: ResolveOrZero<In, Out>> ResolveOrZero<Size<In>, Size<Out>> for Size<T> {
/// Converts any `parent`-relative values for size into an absolute size
- fn resolve_or_zero(self, context: Size<In>, calc: impl Fn(u64, f32) -> f32) -> Size<Out> {
+ fn resolve_or_zero(self, context: Size<In>, calc: impl Fn(*const (), f32) -> f32) -> Size<Out> {
Size {
width: self.width.resolve_or_zero(context.width, &calc),
height: self.height.resolve_or_zero(context.height, &calc),
@@ -123,7 +123,7 @@ impl<In, Out: TaffyZero, T: ResolveOrZero<In, Out>> ResolveOrZero<Size<In>, Size
// Generic ResolveOrZero for resolving Rect against Size
impl<In: Copy, Out: TaffyZero, T: ResolveOrZero<In, Out>> ResolveOrZero<Size<In>, Rect<Out>> for Rect<T> {
/// Converts any `parent`-relative values for Rect into an absolute Rect
- fn resolve_or_zero(self, context: Size<In>, calc: impl Fn(u64, f32) -> f32) -> Rect<Out> {
+ fn resolve_or_zero(self, context: Size<In>, calc: impl Fn(*const (), f32) -> f32) -> Rect<Out> {
Rect {
left: self.left.resolve_or_zero(context.width, &calc),
right: self.right.resolve_or_zero(context.width, &calc),
@@ -136,7 +136,7 @@ impl<In: Copy, Out: TaffyZero, T: ResolveOrZero<In, Out>> ResolveOrZero<Size<In>
// Generic ResolveOrZero for resolving Rect against Option
impl<Out: TaffyZero, T: ResolveOrZero<Option<f32>, Out>> ResolveOrZero<Option<f32>, Rect<Out>> for Rect<T> {
/// Converts any `parent`-relative values for Rect into an absolute Rect
- fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(u64, f32) -> f32) -> Rect<Out> {
+ fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> Rect<Out> {
Rect {
left: self.left.resolve_or_zero(context, &calc),
right: self.right.resolve_or_zero(context, &calc),