1#![allow(clippy::match_same_arms)]
20
21use crate::css::CssPropertyValue;
22use crate::props::basic::color::ColorU;
23use crate::props::basic::font::{StyleFontStyle, StyleFontWeight};
24use crate::props::basic::length::{FloatValue, SizeMetric};
25use crate::props::basic::pixel::PixelValue;
26use crate::props::layout::dimensions::LayoutBoxSizing;
27use crate::props::layout::display::LayoutFloat;
28use crate::props::layout::{
29 dimensions::{
30 LayoutHeight, LayoutMaxHeight, LayoutMaxWidth, LayoutMinHeight, LayoutMinWidth, LayoutWidth,
31 },
32 display::LayoutDisplay,
33 flex::{
34 LayoutAlignContent, LayoutAlignItems, LayoutAlignSelf, LayoutFlexDirection, LayoutFlexWrap,
35 LayoutJustifyContent,
36 },
37 grid::{LayoutGridAutoFlow, LayoutJustifyItems, LayoutJustifySelf},
38 overflow::LayoutOverflow,
39 position::LayoutPosition,
40 table::StyleBorderCollapse,
41 wrapping::{LayoutClear, LayoutWritingMode},
42};
43use crate::props::property::{CssProperty, CssPropertyType};
44use crate::props::style::border::BorderStyle;
45use crate::props::style::effects::StyleCursor;
46use crate::props::style::{
47 StyleDirection, StyleTextAlign, StyleVerticalAlign, StyleVisibility, StyleWhiteSpace,
48};
49use alloc::boxed::Box;
50use alloc::vec::Vec;
51
52pub const U16_SENTINEL: u16 = 0xFFFF;
58pub const U16_SENTINEL_THRESHOLD: u16 = 0xFFF9;
60
61pub const I16_SENTINEL: i16 = 0x7FFF; pub const I16_AUTO: i16 = 0x7FFE; pub const I16_INHERIT: i16 = 0x7FFD; pub const I16_INITIAL: i16 = 0x7FFC; pub const I16_SENTINEL_THRESHOLD: i16 = 0x7FFC; pub const U32_SENTINEL: u32 = 0xFFFF_FFFF;
71pub const U32_AUTO: u32 = 0xFFFF_FFFE;
72pub const U32_NONE: u32 = 0xFFFF_FFFD;
73pub const U32_INHERIT: u32 = 0xFFFF_FFFC;
74pub const U32_INITIAL: u32 = 0xFFFF_FFFB;
75pub const U32_MIN_CONTENT: u32 = 0xFFFF_FFFA;
76pub const U32_MAX_CONTENT: u32 = 0xFFFF_FFF9;
77pub const U32_SENTINEL_THRESHOLD: u32 = 0xFFFF_FFF9;
79
80pub const DISPLAY_SHIFT: u32 = 0;
110pub const POSITION_SHIFT: u32 = 5;
111pub const FLOAT_SHIFT: u32 = 8;
112pub const OVERFLOW_X_SHIFT: u32 = 10;
113pub const OVERFLOW_Y_SHIFT: u32 = 13;
114pub const BOX_SIZING_SHIFT: u32 = 16;
115pub const FLEX_DIRECTION_SHIFT: u32 = 17;
116pub const FLEX_WRAP_SHIFT: u32 = 19;
117pub const JUSTIFY_CONTENT_SHIFT: u32 = 21;
118pub const ALIGN_ITEMS_SHIFT: u32 = 24;
119pub const ALIGN_CONTENT_SHIFT: u32 = 27;
120pub const WRITING_MODE_SHIFT: u32 = 30;
121pub const CLEAR_SHIFT: u32 = 32;
122pub const FONT_WEIGHT_SHIFT: u32 = 34;
123pub const FONT_STYLE_SHIFT: u32 = 38;
124pub const TEXT_ALIGN_SHIFT: u32 = 40;
125pub const VISIBILITY_SHIFT: u32 = 43;
126pub const WHITE_SPACE_SHIFT: u32 = 45;
127pub const DIRECTION_SHIFT: u32 = 48;
128pub const VERTICAL_ALIGN_SHIFT: u32 = 49;
129pub const BORDER_COLLAPSE_SHIFT: u32 = 52;
130pub const DISPLAY_MASK: u64 = 0x1F; pub const POSITION_MASK: u64 = 0x07; pub const FLOAT_MASK: u64 = 0x03; pub const OVERFLOW_MASK: u64 = 0x07; pub const BOX_SIZING_MASK: u64 = 0x01; pub const FLEX_DIR_MASK: u64 = 0x03; pub const FLEX_WRAP_MASK: u64 = 0x03; pub const JUSTIFY_MASK: u64 = 0x07; pub const ALIGN_MASK: u64 = 0x07; pub const WRITING_MODE_MASK: u64 = 0x03; pub const CLEAR_MASK: u64 = 0x03; pub const FONT_WEIGHT_MASK: u64 = 0x0F; pub const FONT_STYLE_MASK: u64 = 0x03; pub const TEXT_ALIGN_MASK: u64 = 0x07; pub const VISIBILITY_MASK: u64 = 0x03; pub const WHITE_SPACE_MASK: u64 = 0x07; pub const DIRECTION_MASK: u64 = 0x01; pub const VERTICAL_ALIGN_MASK: u64 = 0x07; pub const BORDER_COLLAPSE_MASK: u64 = 0x01; pub const CURSOR_CODE_MAX: u8 = 0x1F;
166
167pub const ALIGN_SELF_SHIFT: u32 = 53;
168pub const JUSTIFY_SELF_SHIFT: u32 = 56;
169pub const GRID_AUTO_FLOW_SHIFT: u32 = 59;
170pub const JUSTIFY_ITEMS_SHIFT: u32 = 61;
171pub const ALIGN_SELF_MASK: u64 = 0x07; pub const JUSTIFY_SELF_MASK: u64 = 0x07; pub const GRID_AUTO_FLOW_MASK: u64 = 0x03; pub const JUSTIFY_ITEMS_MASK: u64 = 0x03; pub const TIER1_POPULATED_BIT: u64 = 1 << 63;
184
185#[inline]
193#[must_use]
194pub const fn layout_display_from_u8(v: u8) -> LayoutDisplay {
195 match v {
196 0 => LayoutDisplay::Block, 1 => LayoutDisplay::Inline,
198 2 => LayoutDisplay::InlineBlock,
199 3 => LayoutDisplay::Flex,
200 4 => LayoutDisplay::None,
201 5 => LayoutDisplay::InlineFlex,
202 6 => LayoutDisplay::Table,
203 7 => LayoutDisplay::InlineTable,
204 8 => LayoutDisplay::TableRowGroup,
205 9 => LayoutDisplay::TableHeaderGroup,
206 10 => LayoutDisplay::TableFooterGroup,
207 11 => LayoutDisplay::TableRow,
208 12 => LayoutDisplay::TableColumnGroup,
209 13 => LayoutDisplay::TableColumn,
210 14 => LayoutDisplay::TableCell,
211 15 => LayoutDisplay::TableCaption,
212 16 => LayoutDisplay::FlowRoot,
213 17 => LayoutDisplay::ListItem,
214 18 => LayoutDisplay::RunIn,
215 19 => LayoutDisplay::Marker,
216 20 => LayoutDisplay::Grid,
217 21 => LayoutDisplay::InlineGrid,
218 22 => LayoutDisplay::Contents,
219 _ => LayoutDisplay::Block, }
221}
222
223#[inline]
225#[must_use]
226pub const fn layout_display_to_u8(v: LayoutDisplay) -> u8 {
227 match v {
228 LayoutDisplay::Block => 0, LayoutDisplay::Inline => 1,
230 LayoutDisplay::InlineBlock => 2,
231 LayoutDisplay::Flex => 3,
232 LayoutDisplay::None => 4,
233 LayoutDisplay::InlineFlex => 5,
234 LayoutDisplay::Table => 6,
235 LayoutDisplay::InlineTable => 7,
236 LayoutDisplay::TableRowGroup => 8,
237 LayoutDisplay::TableHeaderGroup => 9,
238 LayoutDisplay::TableFooterGroup => 10,
239 LayoutDisplay::TableRow => 11,
240 LayoutDisplay::TableColumnGroup => 12,
241 LayoutDisplay::TableColumn => 13,
242 LayoutDisplay::TableCell => 14,
243 LayoutDisplay::TableCaption => 15,
244 LayoutDisplay::FlowRoot => 16,
245 LayoutDisplay::ListItem => 17,
246 LayoutDisplay::RunIn => 18,
247 LayoutDisplay::Marker => 19,
248 LayoutDisplay::Grid => 20,
249 LayoutDisplay::InlineGrid => 21,
250 LayoutDisplay::Contents => 22,
251 }
252}
253
254#[inline]
255#[must_use]
256pub const fn layout_position_from_u8(v: u8) -> LayoutPosition {
257 match v {
258 0 => LayoutPosition::Static,
259 1 => LayoutPosition::Relative,
260 2 => LayoutPosition::Absolute,
261 3 => LayoutPosition::Fixed,
262 4 => LayoutPosition::Sticky,
263 _ => LayoutPosition::Static,
264 }
265}
266
267#[inline]
268#[must_use]
269pub const fn layout_position_to_u8(v: LayoutPosition) -> u8 {
270 match v {
271 LayoutPosition::Static => 0,
272 LayoutPosition::Relative => 1,
273 LayoutPosition::Absolute => 2,
274 LayoutPosition::Fixed => 3,
275 LayoutPosition::Sticky => 4,
276 }
277}
278
279#[inline]
281#[must_use]
282pub const fn layout_float_from_u8(v: u8) -> LayoutFloat {
283 match v {
284 0 => LayoutFloat::None, 1 => LayoutFloat::Left,
286 2 => LayoutFloat::Right,
287 _ => LayoutFloat::None,
288 }
289}
290
291#[inline]
293#[must_use]
294pub const fn layout_float_to_u8(v: LayoutFloat) -> u8 {
295 match v {
296 LayoutFloat::None => 0,
297 LayoutFloat::Left => 1,
298 LayoutFloat::Right => 2,
299 }
300}
301
302#[inline]
304#[must_use]
305pub const fn layout_overflow_from_u8(v: u8) -> LayoutOverflow {
306 match v {
307 0 => LayoutOverflow::Visible, 1 => LayoutOverflow::Hidden,
309 2 => LayoutOverflow::Scroll,
310 3 => LayoutOverflow::Auto,
311 4 => LayoutOverflow::Clip,
312 _ => LayoutOverflow::Visible,
313 }
314}
315
316#[inline]
318#[must_use]
319pub const fn layout_overflow_to_u8(v: LayoutOverflow) -> u8 {
320 match v {
321 LayoutOverflow::Visible => 0, LayoutOverflow::Hidden => 1,
323 LayoutOverflow::Scroll => 2,
324 LayoutOverflow::Auto => 3,
325 LayoutOverflow::Clip => 4,
326 }
327}
328
329#[inline]
330#[must_use]
331pub const fn layout_box_sizing_from_u8(v: u8) -> LayoutBoxSizing {
332 match v {
333 0 => LayoutBoxSizing::ContentBox,
334 1 => LayoutBoxSizing::BorderBox,
335 _ => LayoutBoxSizing::ContentBox,
336 }
337}
338
339#[inline]
340#[must_use]
341pub const fn layout_box_sizing_to_u8(v: LayoutBoxSizing) -> u8 {
342 match v {
343 LayoutBoxSizing::ContentBox => 0,
344 LayoutBoxSizing::BorderBox => 1,
345 }
346}
347
348#[inline]
349#[must_use]
350pub const fn layout_flex_direction_from_u8(v: u8) -> LayoutFlexDirection {
351 match v {
352 0 => LayoutFlexDirection::Row,
353 1 => LayoutFlexDirection::RowReverse,
354 2 => LayoutFlexDirection::Column,
355 3 => LayoutFlexDirection::ColumnReverse,
356 _ => LayoutFlexDirection::Row,
357 }
358}
359
360#[inline]
361#[must_use]
362pub const fn layout_flex_direction_to_u8(v: LayoutFlexDirection) -> u8 {
363 match v {
364 LayoutFlexDirection::Row => 0,
365 LayoutFlexDirection::RowReverse => 1,
366 LayoutFlexDirection::Column => 2,
367 LayoutFlexDirection::ColumnReverse => 3,
368 }
369}
370
371#[inline]
373#[must_use]
374pub const fn layout_flex_wrap_from_u8(v: u8) -> LayoutFlexWrap {
375 match v {
376 0 => LayoutFlexWrap::NoWrap, 1 => LayoutFlexWrap::Wrap,
378 2 => LayoutFlexWrap::WrapReverse,
379 _ => LayoutFlexWrap::NoWrap,
380 }
381}
382
383#[inline]
384#[must_use]
385pub const fn layout_flex_wrap_to_u8(v: LayoutFlexWrap) -> u8 {
386 match v {
387 LayoutFlexWrap::NoWrap => 0,
388 LayoutFlexWrap::Wrap => 1,
389 LayoutFlexWrap::WrapReverse => 2,
390 }
391}
392
393#[inline]
394#[must_use]
395pub const fn layout_justify_content_from_u8(v: u8) -> LayoutJustifyContent {
396 match v {
397 0 => LayoutJustifyContent::FlexStart,
398 1 => LayoutJustifyContent::FlexEnd,
399 2 => LayoutJustifyContent::Start,
400 3 => LayoutJustifyContent::End,
401 4 => LayoutJustifyContent::Center,
402 5 => LayoutJustifyContent::SpaceBetween,
403 6 => LayoutJustifyContent::SpaceAround,
404 7 => LayoutJustifyContent::SpaceEvenly,
405 _ => LayoutJustifyContent::FlexStart,
406 }
407}
408
409#[inline]
410#[must_use]
411pub const fn layout_justify_content_to_u8(v: LayoutJustifyContent) -> u8 {
412 match v {
413 LayoutJustifyContent::FlexStart => 0,
414 LayoutJustifyContent::FlexEnd => 1,
415 LayoutJustifyContent::Start => 2,
416 LayoutJustifyContent::End => 3,
417 LayoutJustifyContent::Center => 4,
418 LayoutJustifyContent::SpaceBetween => 5,
419 LayoutJustifyContent::SpaceAround => 6,
420 LayoutJustifyContent::SpaceEvenly => 7,
421 }
422}
423
424#[inline]
425#[must_use]
426pub const fn layout_align_items_from_u8(v: u8) -> LayoutAlignItems {
427 match v {
428 0 => LayoutAlignItems::Stretch,
429 1 => LayoutAlignItems::Center,
430 2 => LayoutAlignItems::Start,
431 3 => LayoutAlignItems::End,
432 4 => LayoutAlignItems::Baseline,
433 _ => LayoutAlignItems::Stretch,
434 }
435}
436
437#[inline]
438#[must_use]
439pub const fn layout_align_items_to_u8(v: LayoutAlignItems) -> u8 {
440 match v {
441 LayoutAlignItems::Stretch => 0,
442 LayoutAlignItems::Center => 1,
443 LayoutAlignItems::Start => 2,
444 LayoutAlignItems::End => 3,
445 LayoutAlignItems::Baseline => 4,
446 }
447}
448
449#[inline]
450#[must_use]
451pub const fn layout_align_self_to_u8(v: LayoutAlignSelf) -> u8 {
452 match v {
453 LayoutAlignSelf::Auto => 0,
454 LayoutAlignSelf::Stretch => 1,
455 LayoutAlignSelf::Center => 2,
456 LayoutAlignSelf::Start => 3,
457 LayoutAlignSelf::End => 4,
458 LayoutAlignSelf::Baseline => 5,
459 }
460}
461
462#[inline]
463#[must_use]
464pub const fn layout_align_self_from_u8(v: u8) -> LayoutAlignSelf {
465 match v {
466 0 => LayoutAlignSelf::Auto,
467 1 => LayoutAlignSelf::Stretch,
468 2 => LayoutAlignSelf::Center,
469 3 => LayoutAlignSelf::Start,
470 4 => LayoutAlignSelf::End,
471 5 => LayoutAlignSelf::Baseline,
472 _ => LayoutAlignSelf::Auto,
473 }
474}
475
476#[inline]
477#[must_use]
478pub const fn layout_justify_self_to_u8(v: LayoutJustifySelf) -> u8 {
479 match v {
480 LayoutJustifySelf::Auto => 0,
481 LayoutJustifySelf::Start => 1,
482 LayoutJustifySelf::End => 2,
483 LayoutJustifySelf::Center => 3,
484 LayoutJustifySelf::Stretch => 4,
485 }
486}
487
488#[inline]
489#[must_use]
490pub const fn layout_justify_self_from_u8(v: u8) -> LayoutJustifySelf {
491 match v {
492 0 => LayoutJustifySelf::Auto,
493 1 => LayoutJustifySelf::Start,
494 2 => LayoutJustifySelf::End,
495 3 => LayoutJustifySelf::Center,
496 4 => LayoutJustifySelf::Stretch,
497 _ => LayoutJustifySelf::Auto,
498 }
499}
500
501#[inline]
508#[must_use]
509pub const fn layout_justify_items_to_u8(v: LayoutJustifyItems) -> u8 {
510 match v {
511 LayoutJustifyItems::Stretch => 0,
512 LayoutJustifyItems::Start => 1,
513 LayoutJustifyItems::End => 2,
514 LayoutJustifyItems::Center => 3,
515 }
516}
517
518#[inline]
519#[must_use]
520pub const fn layout_justify_items_from_u8(v: u8) -> LayoutJustifyItems {
521 match v {
522 0 => LayoutJustifyItems::Stretch,
523 1 => LayoutJustifyItems::Start,
524 2 => LayoutJustifyItems::End,
525 3 => LayoutJustifyItems::Center,
526 _ => LayoutJustifyItems::Stretch,
527 }
528}
529
530#[inline]
531#[must_use]
532pub const fn layout_grid_auto_flow_to_u8(v: LayoutGridAutoFlow) -> u8 {
533 match v {
534 LayoutGridAutoFlow::Row => 0,
535 LayoutGridAutoFlow::Column => 1,
536 LayoutGridAutoFlow::RowDense => 2,
537 LayoutGridAutoFlow::ColumnDense => 3,
538 }
539}
540
541#[inline]
542#[must_use]
543pub const fn layout_grid_auto_flow_from_u8(v: u8) -> LayoutGridAutoFlow {
544 match v {
545 0 => LayoutGridAutoFlow::Row,
546 1 => LayoutGridAutoFlow::Column,
547 2 => LayoutGridAutoFlow::RowDense,
548 3 => LayoutGridAutoFlow::ColumnDense,
549 _ => LayoutGridAutoFlow::Row,
550 }
551}
552
553#[inline]
554#[must_use]
555pub const fn layout_align_content_from_u8(v: u8) -> LayoutAlignContent {
556 match v {
557 0 => LayoutAlignContent::Stretch,
558 1 => LayoutAlignContent::Center,
559 2 => LayoutAlignContent::Start,
560 3 => LayoutAlignContent::End,
561 4 => LayoutAlignContent::SpaceBetween,
562 5 => LayoutAlignContent::SpaceAround,
563 _ => LayoutAlignContent::Stretch,
564 }
565}
566
567#[inline]
568#[must_use]
569pub const fn layout_align_content_to_u8(v: LayoutAlignContent) -> u8 {
570 match v {
571 LayoutAlignContent::Stretch => 0,
572 LayoutAlignContent::Center => 1,
573 LayoutAlignContent::Start => 2,
574 LayoutAlignContent::End => 3,
575 LayoutAlignContent::SpaceBetween => 4,
576 LayoutAlignContent::SpaceAround => 5,
577 }
578}
579
580#[inline]
581#[must_use]
582pub const fn layout_writing_mode_from_u8(v: u8) -> LayoutWritingMode {
583 match v {
584 0 => LayoutWritingMode::HorizontalTb,
585 1 => LayoutWritingMode::VerticalRl,
586 2 => LayoutWritingMode::VerticalLr,
587 _ => LayoutWritingMode::HorizontalTb,
588 }
589}
590
591#[inline]
592#[must_use]
593pub const fn layout_writing_mode_to_u8(v: LayoutWritingMode) -> u8 {
594 match v {
595 LayoutWritingMode::HorizontalTb => 0,
596 LayoutWritingMode::VerticalRl => 1,
597 LayoutWritingMode::VerticalLr => 2,
598 }
599}
600
601#[inline]
602#[must_use]
603pub const fn layout_clear_from_u8(v: u8) -> LayoutClear {
604 match v {
605 0 => LayoutClear::None,
606 1 => LayoutClear::Left,
607 2 => LayoutClear::Right,
608 3 => LayoutClear::Both,
609 _ => LayoutClear::None,
610 }
611}
612
613#[inline]
614#[must_use]
615pub const fn layout_clear_to_u8(v: LayoutClear) -> u8 {
616 match v {
617 LayoutClear::None => 0,
618 LayoutClear::Left => 1,
619 LayoutClear::Right => 2,
620 LayoutClear::Both => 3,
621 }
622}
623
624#[inline]
625#[must_use]
627pub const fn style_font_weight_from_u8(v: u8) -> StyleFontWeight {
628 match v {
629 0 => StyleFontWeight::Normal, 1 => StyleFontWeight::W100,
631 2 => StyleFontWeight::W200,
632 3 => StyleFontWeight::W300,
633 4 => StyleFontWeight::W500,
634 5 => StyleFontWeight::W600,
635 6 => StyleFontWeight::Bold, 7 => StyleFontWeight::W800,
637 8 => StyleFontWeight::W900,
638 9 => StyleFontWeight::Lighter,
639 10 => StyleFontWeight::Bolder,
640 _ => StyleFontWeight::Normal,
641 }
642}
643
644#[inline]
645#[must_use]
647pub const fn style_font_weight_to_u8(v: StyleFontWeight) -> u8 {
648 match v {
649 StyleFontWeight::Normal => 0, StyleFontWeight::W100 => 1,
651 StyleFontWeight::W200 => 2,
652 StyleFontWeight::W300 => 3,
653 StyleFontWeight::W500 => 4,
654 StyleFontWeight::W600 => 5,
655 StyleFontWeight::Bold => 6, StyleFontWeight::W800 => 7,
657 StyleFontWeight::W900 => 8,
658 StyleFontWeight::Lighter => 9,
659 StyleFontWeight::Bolder => 10,
660 }
661}
662
663#[inline]
664#[must_use]
665pub const fn style_font_style_from_u8(v: u8) -> StyleFontStyle {
666 match v {
667 0 => StyleFontStyle::Normal,
668 1 => StyleFontStyle::Italic,
669 2 => StyleFontStyle::Oblique,
670 _ => StyleFontStyle::Normal,
671 }
672}
673
674#[inline]
675#[must_use]
676pub const fn style_font_style_to_u8(v: StyleFontStyle) -> u8 {
677 match v {
678 StyleFontStyle::Normal => 0,
679 StyleFontStyle::Italic => 1,
680 StyleFontStyle::Oblique => 2,
681 }
682}
683
684#[inline]
685#[must_use]
686pub const fn style_text_align_from_u8(v: u8) -> StyleTextAlign {
687 match v {
692 0 => StyleTextAlign::Start,
693 1 => StyleTextAlign::Center,
694 2 => StyleTextAlign::Right,
695 3 => StyleTextAlign::Justify,
696 4 => StyleTextAlign::Left,
697 5 => StyleTextAlign::End,
698 _ => StyleTextAlign::Start,
699 }
700}
701
702#[inline]
703#[must_use]
704pub const fn style_text_align_to_u8(v: StyleTextAlign) -> u8 {
705 match v {
707 StyleTextAlign::Start => 0,
708 StyleTextAlign::Center => 1,
709 StyleTextAlign::Right => 2,
710 StyleTextAlign::Justify => 3,
711 StyleTextAlign::Left => 4,
712 StyleTextAlign::End => 5,
713 }
714}
715
716#[inline]
717#[must_use]
718pub const fn style_visibility_from_u8(v: u8) -> StyleVisibility {
719 match v {
720 0 => StyleVisibility::Visible,
721 1 => StyleVisibility::Hidden,
722 2 => StyleVisibility::Collapse,
723 _ => StyleVisibility::Visible,
724 }
725}
726
727#[inline]
728#[must_use]
729pub const fn style_visibility_to_u8(v: StyleVisibility) -> u8 {
730 match v {
731 StyleVisibility::Visible => 0,
732 StyleVisibility::Hidden => 1,
733 StyleVisibility::Collapse => 2,
734 }
735}
736
737#[inline]
738#[must_use]
739pub const fn style_white_space_from_u8(v: u8) -> StyleWhiteSpace {
740 match v {
741 0 => StyleWhiteSpace::Normal,
742 1 => StyleWhiteSpace::Pre,
743 2 => StyleWhiteSpace::Nowrap,
744 3 => StyleWhiteSpace::PreWrap,
745 4 => StyleWhiteSpace::PreLine,
746 5 => StyleWhiteSpace::BreakSpaces,
747 _ => StyleWhiteSpace::Normal,
748 }
749}
750
751#[inline]
752#[must_use]
753pub const fn style_white_space_to_u8(v: StyleWhiteSpace) -> u8 {
754 match v {
755 StyleWhiteSpace::Normal => 0,
756 StyleWhiteSpace::Pre => 1,
757 StyleWhiteSpace::Nowrap => 2,
758 StyleWhiteSpace::PreWrap => 3,
759 StyleWhiteSpace::PreLine => 4,
760 StyleWhiteSpace::BreakSpaces => 5,
761 }
762}
763
764#[inline]
765#[must_use]
766pub const fn style_direction_from_u8(v: u8) -> StyleDirection {
767 match v {
768 0 => StyleDirection::Ltr,
769 1 => StyleDirection::Rtl,
770 _ => StyleDirection::Ltr,
771 }
772}
773
774#[inline]
775#[must_use]
776pub const fn style_direction_to_u8(v: StyleDirection) -> u8 {
777 match v {
778 StyleDirection::Ltr => 0,
779 StyleDirection::Rtl => 1,
780 }
781}
782
783#[inline]
784#[must_use]
785pub const fn style_vertical_align_from_u8(v: u8) -> StyleVerticalAlign {
786 match v {
787 0 => StyleVerticalAlign::Baseline,
788 1 => StyleVerticalAlign::Top,
789 2 => StyleVerticalAlign::Middle,
790 3 => StyleVerticalAlign::Bottom,
791 4 => StyleVerticalAlign::Sub,
792 5 => StyleVerticalAlign::Superscript,
793 6 => StyleVerticalAlign::TextTop,
794 7 => StyleVerticalAlign::TextBottom,
795 _ => StyleVerticalAlign::Baseline,
796 }
797}
798
799#[inline]
800#[must_use]
801pub const fn style_vertical_align_to_u8(v: StyleVerticalAlign) -> u8 {
802 match v {
803 StyleVerticalAlign::Baseline => 0,
804 StyleVerticalAlign::Top => 1,
805 StyleVerticalAlign::Middle => 2,
806 StyleVerticalAlign::Bottom => 3,
807 StyleVerticalAlign::Sub => 4,
808 StyleVerticalAlign::Superscript => 5,
809 StyleVerticalAlign::TextTop => 6,
810 StyleVerticalAlign::TextBottom => 7,
811 StyleVerticalAlign::Percentage(_) | StyleVerticalAlign::Length(_) => 0,
815 }
816}
817
818#[inline]
822#[must_use]
823pub const fn cursor_to_u8(v: StyleCursor) -> u8 {
824 match v {
825 StyleCursor::Default => 0,
826 StyleCursor::Alias => 1,
827 StyleCursor::AllScroll => 2,
828 StyleCursor::Cell => 3,
829 StyleCursor::ColResize => 4,
830 StyleCursor::ContextMenu => 5,
831 StyleCursor::Copy => 6,
832 StyleCursor::Crosshair => 7,
833 StyleCursor::EResize => 8,
834 StyleCursor::EwResize => 9,
835 StyleCursor::Grab => 10,
836 StyleCursor::Grabbing => 11,
837 StyleCursor::Help => 12,
838 StyleCursor::Move => 13,
839 StyleCursor::NResize => 14,
840 StyleCursor::NsResize => 15,
841 StyleCursor::NeswResize => 16,
842 StyleCursor::NwseResize => 17,
843 StyleCursor::Pointer => 18,
844 StyleCursor::Progress => 19,
845 StyleCursor::RowResize => 20,
846 StyleCursor::SResize => 21,
847 StyleCursor::SeResize => 22,
848 StyleCursor::Text => 23,
849 StyleCursor::Unset => 24,
850 StyleCursor::VerticalText => 25,
851 StyleCursor::WResize => 26,
852 StyleCursor::Wait => 27,
853 StyleCursor::ZoomIn => 28,
854 StyleCursor::ZoomOut => 29,
855 }
856}
857
858#[inline]
859#[must_use]
860pub const fn cursor_from_u8(v: u8) -> StyleCursor {
861 match v {
862 1 => StyleCursor::Alias,
863 2 => StyleCursor::AllScroll,
864 3 => StyleCursor::Cell,
865 4 => StyleCursor::ColResize,
866 5 => StyleCursor::ContextMenu,
867 6 => StyleCursor::Copy,
868 7 => StyleCursor::Crosshair,
869 8 => StyleCursor::EResize,
870 9 => StyleCursor::EwResize,
871 10 => StyleCursor::Grab,
872 11 => StyleCursor::Grabbing,
873 12 => StyleCursor::Help,
874 13 => StyleCursor::Move,
875 14 => StyleCursor::NResize,
876 15 => StyleCursor::NsResize,
877 16 => StyleCursor::NeswResize,
878 17 => StyleCursor::NwseResize,
879 18 => StyleCursor::Pointer,
880 19 => StyleCursor::Progress,
881 20 => StyleCursor::RowResize,
882 21 => StyleCursor::SResize,
883 22 => StyleCursor::SeResize,
884 23 => StyleCursor::Text,
885 24 => StyleCursor::Unset,
886 25 => StyleCursor::VerticalText,
887 26 => StyleCursor::WResize,
888 27 => StyleCursor::Wait,
889 28 => StyleCursor::ZoomIn,
890 29 => StyleCursor::ZoomOut,
891 _ => StyleCursor::Default,
892 }
893}
894
895
896#[inline]
897#[must_use]
898pub const fn border_collapse_from_u8(v: u8) -> StyleBorderCollapse {
899 match v {
900 0 => StyleBorderCollapse::Separate,
901 1 => StyleBorderCollapse::Collapse,
902 _ => StyleBorderCollapse::Separate,
903 }
904}
905
906#[inline]
907#[must_use]
908pub const fn border_collapse_to_u8(v: StyleBorderCollapse) -> u8 {
909 match v {
910 StyleBorderCollapse::Separate => 0,
911 StyleBorderCollapse::Collapse => 1,
912 }
913}
914
915#[inline]
916#[must_use]
917pub const fn border_style_from_u8(v: u8) -> BorderStyle {
918 match v {
919 0 => BorderStyle::None,
920 1 => BorderStyle::Solid,
921 2 => BorderStyle::Double,
922 3 => BorderStyle::Dotted,
923 4 => BorderStyle::Dashed,
924 5 => BorderStyle::Hidden,
925 6 => BorderStyle::Groove,
926 7 => BorderStyle::Ridge,
927 8 => BorderStyle::Inset,
928 9 => BorderStyle::Outset,
929 _ => BorderStyle::None,
930 }
931}
932
933#[inline]
934#[must_use]
935pub const fn border_style_to_u8(v: BorderStyle) -> u8 {
936 match v {
937 BorderStyle::None => 0,
938 BorderStyle::Solid => 1,
939 BorderStyle::Double => 2,
940 BorderStyle::Dotted => 3,
941 BorderStyle::Dashed => 4,
942 BorderStyle::Hidden => 5,
943 BorderStyle::Groove => 6,
944 BorderStyle::Ridge => 7,
945 BorderStyle::Inset => 8,
946 BorderStyle::Outset => 9,
947 }
948}
949
950#[inline]
952#[must_use]
953pub const fn encode_border_styles_packed(
954 top: BorderStyle,
955 right: BorderStyle,
956 bottom: BorderStyle,
957 left: BorderStyle,
958) -> u16 {
959 (border_style_to_u8(top) as u16)
960 | ((border_style_to_u8(right) as u16) << 4)
961 | ((border_style_to_u8(bottom) as u16) << 8)
962 | ((border_style_to_u8(left) as u16) << 12)
963}
964
965#[inline]
967#[must_use]
968pub const fn decode_border_top_style(packed: u16) -> BorderStyle {
969 border_style_from_u8((packed & 0x0F) as u8)
970}
971
972#[inline]
974#[must_use]
975pub const fn decode_border_right_style(packed: u16) -> BorderStyle {
976 border_style_from_u8(((packed >> 4) & 0x0F) as u8)
977}
978
979#[inline]
981#[must_use]
982pub const fn decode_border_bottom_style(packed: u16) -> BorderStyle {
983 border_style_from_u8(((packed >> 8) & 0x0F) as u8)
984}
985
986#[inline]
988#[must_use]
989pub const fn decode_border_left_style(packed: u16) -> BorderStyle {
990 border_style_from_u8(((packed >> 12) & 0x0F) as u8)
991}
992
993#[inline]
995#[must_use]
996pub const fn encode_color_u32(c: &ColorU) -> u32 {
997 ((c.r as u32) << 24) | ((c.g as u32) << 16) | ((c.b as u32) << 8) | (c.a as u32)
998}
999
1000#[inline]
1006#[must_use]
1007pub const fn decode_color_u32(v: u32) -> Option<ColorU> {
1008 if v == 0 {
1009 return None;
1010 }
1011 Some(ColorU {
1012 r: ((v >> 24) & 0xFF) as u8,
1013 g: ((v >> 16) & 0xFF) as u8,
1014 b: ((v >> 8) & 0xFF) as u8,
1015 a: (v & 0xFF) as u8,
1016 })
1017}
1018
1019#[inline]
1025#[must_use]
1026pub const fn encode_tier1(
1027 display: LayoutDisplay,
1028 position: LayoutPosition,
1029 float: LayoutFloat,
1030 overflow_x: LayoutOverflow,
1031 overflow_y: LayoutOverflow,
1032 box_sizing: LayoutBoxSizing,
1033 flex_direction: LayoutFlexDirection,
1034 flex_wrap: LayoutFlexWrap,
1035 justify_content: LayoutJustifyContent,
1036 align_items: LayoutAlignItems,
1037 align_content: LayoutAlignContent,
1038 writing_mode: LayoutWritingMode,
1039 clear: LayoutClear,
1040 font_weight: StyleFontWeight,
1041 font_style: StyleFontStyle,
1042 text_align: StyleTextAlign,
1043 visibility: StyleVisibility,
1044 white_space: StyleWhiteSpace,
1045 direction: StyleDirection,
1046 vertical_align: StyleVerticalAlign,
1047 border_collapse: StyleBorderCollapse,
1048) -> u64 {
1049 let mut v: u64 = TIER1_POPULATED_BIT;
1050 v |= (layout_display_to_u8(display) as u64) << DISPLAY_SHIFT;
1051 v |= (layout_position_to_u8(position) as u64) << POSITION_SHIFT;
1052 v |= (layout_float_to_u8(float) as u64) << FLOAT_SHIFT;
1053 v |= (layout_overflow_to_u8(overflow_x) as u64) << OVERFLOW_X_SHIFT;
1054 v |= (layout_overflow_to_u8(overflow_y) as u64) << OVERFLOW_Y_SHIFT;
1055 v |= (layout_box_sizing_to_u8(box_sizing) as u64) << BOX_SIZING_SHIFT;
1056 v |= (layout_flex_direction_to_u8(flex_direction) as u64) << FLEX_DIRECTION_SHIFT;
1057 v |= (layout_flex_wrap_to_u8(flex_wrap) as u64) << FLEX_WRAP_SHIFT;
1058 v |= (layout_justify_content_to_u8(justify_content) as u64) << JUSTIFY_CONTENT_SHIFT;
1059 v |= (layout_align_items_to_u8(align_items) as u64) << ALIGN_ITEMS_SHIFT;
1060 v |= (layout_align_content_to_u8(align_content) as u64) << ALIGN_CONTENT_SHIFT;
1061 v |= (layout_writing_mode_to_u8(writing_mode) as u64) << WRITING_MODE_SHIFT;
1062 v |= (layout_clear_to_u8(clear) as u64) << CLEAR_SHIFT;
1063 v |= (style_font_weight_to_u8(font_weight) as u64) << FONT_WEIGHT_SHIFT;
1064 v |= (style_font_style_to_u8(font_style) as u64) << FONT_STYLE_SHIFT;
1065 v |= (style_text_align_to_u8(text_align) as u64) << TEXT_ALIGN_SHIFT;
1066 v |= (style_visibility_to_u8(visibility) as u64) << VISIBILITY_SHIFT;
1067 v |= (style_white_space_to_u8(white_space) as u64) << WHITE_SPACE_SHIFT;
1068 v |= (style_direction_to_u8(direction) as u64) << DIRECTION_SHIFT;
1069 v |= (style_vertical_align_to_u8(vertical_align) as u64) << VERTICAL_ALIGN_SHIFT;
1070 v |= (border_collapse_to_u8(border_collapse) as u64) << BORDER_COLLAPSE_SHIFT;
1071 v
1072}
1073
1074#[inline]
1078#[must_use]
1079pub const fn decode_display(t1: u64) -> LayoutDisplay {
1080 layout_display_from_u8(((t1 >> DISPLAY_SHIFT) & DISPLAY_MASK) as u8)
1081}
1082
1083#[inline]
1084#[must_use]
1085pub const fn decode_position(t1: u64) -> LayoutPosition {
1086 layout_position_from_u8(((t1 >> POSITION_SHIFT) & POSITION_MASK) as u8)
1087}
1088
1089#[inline]
1090#[must_use]
1091pub const fn decode_float(t1: u64) -> LayoutFloat {
1092 layout_float_from_u8(((t1 >> FLOAT_SHIFT) & FLOAT_MASK) as u8)
1093}
1094
1095#[inline]
1096#[must_use]
1097pub const fn decode_overflow_x(t1: u64) -> LayoutOverflow {
1098 layout_overflow_from_u8(((t1 >> OVERFLOW_X_SHIFT) & OVERFLOW_MASK) as u8)
1099}
1100
1101#[inline]
1102#[must_use]
1103pub const fn decode_overflow_y(t1: u64) -> LayoutOverflow {
1104 layout_overflow_from_u8(((t1 >> OVERFLOW_Y_SHIFT) & OVERFLOW_MASK) as u8)
1105}
1106
1107#[inline]
1108#[must_use]
1109pub const fn decode_box_sizing(t1: u64) -> LayoutBoxSizing {
1110 layout_box_sizing_from_u8(((t1 >> BOX_SIZING_SHIFT) & BOX_SIZING_MASK) as u8)
1111}
1112
1113#[inline]
1114#[must_use]
1115pub const fn decode_flex_direction(t1: u64) -> LayoutFlexDirection {
1116 layout_flex_direction_from_u8(((t1 >> FLEX_DIRECTION_SHIFT) & FLEX_DIR_MASK) as u8)
1117}
1118
1119#[inline]
1120#[must_use]
1121pub const fn decode_flex_wrap(t1: u64) -> LayoutFlexWrap {
1122 layout_flex_wrap_from_u8(((t1 >> FLEX_WRAP_SHIFT) & FLEX_WRAP_MASK) as u8)
1123}
1124
1125#[inline]
1126#[must_use]
1127pub const fn decode_justify_content(t1: u64) -> LayoutJustifyContent {
1128 layout_justify_content_from_u8(((t1 >> JUSTIFY_CONTENT_SHIFT) & JUSTIFY_MASK) as u8)
1129}
1130
1131#[inline]
1132#[must_use]
1133pub const fn decode_align_items(t1: u64) -> LayoutAlignItems {
1134 layout_align_items_from_u8(((t1 >> ALIGN_ITEMS_SHIFT) & ALIGN_MASK) as u8)
1135}
1136
1137#[inline]
1138#[must_use]
1139pub const fn decode_align_content(t1: u64) -> LayoutAlignContent {
1140 layout_align_content_from_u8(((t1 >> ALIGN_CONTENT_SHIFT) & ALIGN_MASK) as u8)
1141}
1142
1143#[inline]
1144#[must_use]
1145pub const fn decode_writing_mode(t1: u64) -> LayoutWritingMode {
1146 layout_writing_mode_from_u8(((t1 >> WRITING_MODE_SHIFT) & WRITING_MODE_MASK) as u8)
1147}
1148
1149#[inline]
1150#[must_use]
1151pub const fn decode_clear(t1: u64) -> LayoutClear {
1152 layout_clear_from_u8(((t1 >> CLEAR_SHIFT) & CLEAR_MASK) as u8)
1153}
1154
1155#[inline]
1156#[must_use]
1157pub const fn decode_font_weight(t1: u64) -> StyleFontWeight {
1158 style_font_weight_from_u8(((t1 >> FONT_WEIGHT_SHIFT) & FONT_WEIGHT_MASK) as u8)
1159}
1160
1161#[inline]
1162#[must_use]
1163pub const fn decode_font_style(t1: u64) -> StyleFontStyle {
1164 style_font_style_from_u8(((t1 >> FONT_STYLE_SHIFT) & FONT_STYLE_MASK) as u8)
1165}
1166
1167#[inline]
1168#[must_use]
1169pub const fn decode_text_align(t1: u64) -> StyleTextAlign {
1170 style_text_align_from_u8(((t1 >> TEXT_ALIGN_SHIFT) & TEXT_ALIGN_MASK) as u8)
1171}
1172
1173#[inline]
1174#[must_use]
1175pub const fn decode_visibility(t1: u64) -> StyleVisibility {
1176 style_visibility_from_u8(((t1 >> VISIBILITY_SHIFT) & VISIBILITY_MASK) as u8)
1177}
1178
1179#[inline]
1180#[must_use]
1181pub const fn decode_white_space(t1: u64) -> StyleWhiteSpace {
1182 style_white_space_from_u8(((t1 >> WHITE_SPACE_SHIFT) & WHITE_SPACE_MASK) as u8)
1183}
1184
1185#[inline]
1186#[must_use]
1187pub const fn decode_direction(t1: u64) -> StyleDirection {
1188 style_direction_from_u8(((t1 >> DIRECTION_SHIFT) & DIRECTION_MASK) as u8)
1189}
1190
1191#[inline]
1192#[must_use]
1193pub const fn decode_vertical_align(t1: u64) -> StyleVerticalAlign {
1194 style_vertical_align_from_u8(((t1 >> VERTICAL_ALIGN_SHIFT) & VERTICAL_ALIGN_MASK) as u8)
1195}
1196
1197#[inline]
1198#[must_use]
1199pub const fn decode_border_collapse(t1: u64) -> StyleBorderCollapse {
1200 border_collapse_from_u8(((t1 >> BORDER_COLLAPSE_SHIFT) & BORDER_COLLAPSE_MASK) as u8)
1201}
1202
1203#[inline]
1205#[cfg(test)]
1206#[must_use]
1207pub const fn tier1_is_populated(t1: u64) -> bool {
1208 (t1 & TIER1_POPULATED_BIT) != 0
1209}
1210
1211#[inline]
1226#[must_use]
1227pub fn encode_pixel_value_u32(pv: &PixelValue) -> u32 {
1228 let metric = u32::from(size_metric_to_u8(pv.metric));
1229 let raw = pv.number.number; if !(-134_217_728..=134_217_727).contains(&raw) {
1232 return U32_SENTINEL; }
1234 let value_bits = i32::try_from(raw).unwrap_or(0).cast_unsigned() << 4;
1237 let packed = value_bits | metric;
1238 if packed >= U32_SENTINEL_THRESHOLD {
1243 return U32_SENTINEL;
1244 }
1245 packed
1246}
1247
1248#[inline]
1250#[must_use]
1251pub const fn decode_pixel_value_u32(encoded: u32) -> Option<PixelValue> {
1252 if encoded >= U32_SENTINEL_THRESHOLD {
1253 return None; }
1255 let metric = size_metric_from_u8((encoded & 0xF) as u8);
1256 let value_bits = encoded.cast_signed() >> 4;
1258 let raw = value_bits as isize; Some(PixelValue {
1260 metric,
1261 number: FloatValue { number: raw },
1262 })
1263}
1264
1265#[inline]
1268#[must_use]
1269pub fn encode_resolved_px_i16(px: f32) -> i16 {
1270 let scaled = crate::cast::f32_to_i32((px * 10.0).round());
1271 if scaled < -32768 || scaled > i32::from(I16_SENTINEL_THRESHOLD) - 1 {
1272 return I16_SENTINEL; }
1274 i16::try_from(scaled).unwrap_or(I16_SENTINEL)
1275}
1276
1277#[inline]
1279#[must_use]
1280pub fn decode_resolved_px_i16(v: i16) -> Option<f32> {
1281 if v >= I16_SENTINEL_THRESHOLD {
1282 return None;
1283 }
1284 Some(f32::from(v) / 10.0)
1285}
1286
1287#[inline]
1290#[must_use]
1291pub fn encode_flex_u16(value: f32) -> u16 {
1292 let scaled = crate::cast::f32_to_i32((value * 100.0).round());
1293 if scaled < 0 || scaled >= i32::from(U16_SENTINEL_THRESHOLD) {
1294 return U16_SENTINEL;
1295 }
1296 u16::try_from(scaled).unwrap_or(U16_SENTINEL)
1297}
1298
1299#[inline]
1301#[must_use]
1302pub fn decode_flex_u16(v: u16) -> Option<f32> {
1303 if v >= U16_SENTINEL_THRESHOLD {
1304 return None;
1305 }
1306 Some(f32::from(v) / 100.0)
1307}
1308
1309#[inline]
1311#[must_use]
1312pub const fn size_metric_to_u8(m: SizeMetric) -> u8 {
1313 match m {
1314 SizeMetric::Px => 0,
1315 SizeMetric::Pt => 1,
1316 SizeMetric::Em => 2,
1317 SizeMetric::Rem => 3,
1318 SizeMetric::In => 4,
1319 SizeMetric::Cm => 5,
1320 SizeMetric::Mm => 6,
1321 SizeMetric::Percent => 7,
1322 SizeMetric::Vw => 8,
1323 SizeMetric::Vh => 9,
1324 SizeMetric::Vmin => 10,
1325 SizeMetric::Vmax => 11,
1326 }
1327}
1328
1329#[inline]
1331#[must_use]
1332pub const fn size_metric_from_u8(v: u8) -> SizeMetric {
1333 match v {
1334 0 => SizeMetric::Px,
1335 1 => SizeMetric::Pt,
1336 2 => SizeMetric::Em,
1337 3 => SizeMetric::Rem,
1338 4 => SizeMetric::In,
1339 5 => SizeMetric::Cm,
1340 6 => SizeMetric::Mm,
1341 7 => SizeMetric::Percent,
1342 8 => SizeMetric::Vw,
1343 9 => SizeMetric::Vh,
1344 10 => SizeMetric::Vmin,
1345 11 => SizeMetric::Vmax,
1346 _ => SizeMetric::Px,
1347 }
1348}
1349
1350#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1354#[repr(C)]
1355pub struct CompactNodeProps {
1356 pub width: u32,
1358 pub height: u32,
1359 pub min_width: u32,
1360 pub max_width: u32,
1361 pub min_height: u32,
1362 pub max_height: u32,
1363 pub flex_basis: u32,
1364 pub font_size: u32,
1365
1366 pub padding_top: i16,
1368 pub padding_right: i16,
1369 pub padding_bottom: i16,
1370 pub padding_left: i16,
1371 pub margin_top: i16,
1372 pub margin_right: i16,
1373 pub margin_bottom: i16,
1374 pub margin_left: i16,
1375 pub border_top_width: i16,
1376 pub border_right_width: i16,
1377 pub border_bottom_width: i16,
1378 pub border_left_width: i16,
1379 pub top: i16,
1380 pub right: i16,
1381 pub bottom: i16,
1382 pub left: i16,
1383
1384 pub flex_grow: u16,
1386 pub flex_shrink: u16,
1387
1388 pub row_gap: i16,
1390 pub column_gap: i16,
1391}
1392
1393#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1396#[repr(C)]
1397pub struct CompactNodePropsCold {
1398 pub border_top_color: u32,
1400 pub border_right_color: u32,
1401 pub border_bottom_color: u32,
1402 pub border_left_color: u32,
1403
1404 pub border_top_left_radius: i16,
1406 pub border_top_right_radius: i16,
1407 pub border_bottom_left_radius: i16,
1408 pub border_bottom_right_radius: i16,
1409
1410 pub z_index: i16, pub border_styles_packed: u16,
1414 pub border_spacing_h: i16,
1415 pub border_spacing_v: i16,
1416 pub tab_size: i16,
1417 pub grid_col_start: i16,
1419 pub grid_col_end: i16,
1421 pub grid_row_start: i16,
1423 pub grid_row_end: i16,
1425
1426 pub opacity: u8,
1429 pub cursor: u8,
1437 pub hot_flags: u8,
1448 pub extra_flags: u8,
1462}
1463
1464pub const OPACITY_SENTINEL: u8 = 255;
1465pub const HOT_FLAG_HAS_TRANSFORM: u8 = 1 << 0;
1466pub const HOT_FLAG_HAS_TRANSFORM_ORIGIN: u8 = 1 << 1;
1467pub const HOT_FLAG_HAS_BOX_SHADOW: u8 = 1 << 2;
1468pub const HOT_FLAG_HAS_TEXT_DECORATION: u8 = 1 << 3;
1469pub const HOT_FLAG_SCROLLBAR_GUTTER_SHIFT: u8 = 4;
1470pub const HOT_FLAG_SCROLLBAR_GUTTER_MASK: u8 = 0b0011_0000;
1471pub const HOT_FLAG_HAS_BACKGROUND: u8 = 1 << 6;
1472pub const HOT_FLAG_HAS_CLIP_PATH: u8 = 1 << 7;
1473pub const EXTRA_FLAG_HAS_SCROLLBAR_CSS: u8 = 1 << 0;
1474pub const EXTRA_FLAG_HAS_COUNTER: u8 = 1 << 1;
1475pub const EXTRA_FLAG_HAS_BREAK: u8 = 1 << 2;
1476pub const EXTRA_FLAG_HAS_TEXT_ORIENTATION: u8 = 1 << 3;
1477pub const EXTRA_FLAG_HAS_TEXT_SHADOW: u8 = 1 << 4;
1478pub const EXTRA_FLAG_HAS_BACKDROP_FILTER: u8 = 1 << 5;
1479pub const EXTRA_FLAG_HAS_FILTER: u8 = 1 << 6;
1480pub const EXTRA_FLAG_HAS_MIX_BLEND_MODE: u8 = 1 << 7;
1481
1482pub const DOM_HAS_SHAPE_INSIDE: u32 = 1 << 0;
1489pub const DOM_HAS_SHAPE_OUTSIDE: u32 = 1 << 1;
1490pub const DOM_HAS_TEXT_JUSTIFY: u32 = 1 << 2;
1491pub const DOM_HAS_TEXT_INDENT: u32 = 1 << 3;
1492pub const DOM_HAS_COLUMN_COUNT: u32 = 1 << 4;
1493pub const DOM_HAS_COLUMN_GAP: u32 = 1 << 5;
1494pub const DOM_HAS_INITIAL_LETTER: u32 = 1 << 6;
1495pub const DOM_HAS_INITIAL_LETTER_ALIGN: u32 = 1 << 7;
1496pub const DOM_HAS_LINE_CLAMP: u32 = 1 << 8;
1497pub const DOM_HAS_HANGING_PUNCTUATION: u32 = 1 << 9;
1498pub const DOM_HAS_TEXT_COMBINE_UPRIGHT: u32 = 1 << 10;
1499pub const DOM_HAS_EXCLUSION_MARGIN: u32 = 1 << 11;
1500pub const DOM_HAS_HYPHENATION_LANGUAGE: u32 = 1 << 12;
1501pub const DOM_HAS_UNICODE_BIDI: u32 = 1 << 13;
1502pub const DOM_HAS_TEXT_BOX_TRIM: u32 = 1 << 14;
1503pub const DOM_HAS_HYPHENS: u32 = 1 << 15;
1504pub const DOM_HAS_WORD_BREAK: u32 = 1 << 16;
1505pub const DOM_HAS_OVERFLOW_WRAP: u32 = 1 << 17;
1506pub const DOM_HAS_LINE_BREAK: u32 = 1 << 18;
1507pub const DOM_HAS_TEXT_ALIGN_LAST: u32 = 1 << 19;
1508pub const DOM_HAS_LINE_HEIGHT: u32 = 1 << 20;
1509pub const DOM_HAS_COLUMN_WIDTH: u32 = 1 << 21;
1510pub const DOM_HAS_SHAPE_MARGIN: u32 = 1 << 22;
1511pub const SCROLLBAR_GUTTER_AUTO: u8 = 0;
1512pub const SCROLLBAR_GUTTER_STABLE: u8 = 1;
1513pub const SCROLLBAR_GUTTER_BOTH_EDGES: u8 = 2;
1514pub const SCROLLBAR_GUTTER_MIRROR: u8 = 3;
1515
1516impl Default for CompactNodeProps {
1517 fn default() -> Self {
1518 Self {
1519 width: U32_AUTO,
1521 height: U32_AUTO,
1522 min_width: U32_AUTO,
1523 max_width: U32_NONE,
1524 min_height: U32_AUTO,
1525 max_height: U32_NONE,
1526 flex_basis: U32_AUTO,
1527 font_size: U32_INITIAL,
1528 padding_top: 0,
1530 padding_right: 0,
1531 padding_bottom: 0,
1532 padding_left: 0,
1533 margin_top: 0,
1534 margin_right: 0,
1535 margin_bottom: 0,
1536 margin_left: 0,
1537 border_top_width: 0,
1538 border_right_width: 0,
1539 border_bottom_width: 0,
1540 border_left_width: 0,
1541 top: I16_AUTO,
1542 right: I16_AUTO,
1543 bottom: I16_AUTO,
1544 left: I16_AUTO,
1545 flex_grow: 0,
1547 flex_shrink: encode_flex_u16(1.0), row_gap: 0,
1551 column_gap: 0,
1552 }
1553 }
1554}
1555
1556impl Default for CompactNodePropsCold {
1557 fn default() -> Self {
1558 Self {
1559 border_top_color: 0,
1561 border_right_color: 0,
1562 border_bottom_color: 0,
1563 border_left_color: 0,
1564 border_top_left_radius: I16_SENTINEL,
1566 border_top_right_radius: I16_SENTINEL,
1567 border_bottom_left_radius: I16_SENTINEL,
1568 border_bottom_right_radius: I16_SENTINEL,
1569 z_index: I16_AUTO,
1571 border_styles_packed: 0, border_spacing_h: 0,
1573 border_spacing_v: 0,
1574 tab_size: I16_SENTINEL, grid_col_start: I16_AUTO,
1576 grid_col_end: I16_AUTO,
1577 grid_row_start: I16_AUTO,
1578 grid_row_end: I16_AUTO,
1579 opacity: OPACITY_SENTINEL,
1580 cursor: 0,
1582 hot_flags: 0,
1583 extra_flags: 0,
1584 }
1585 }
1586}
1587
1588#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1594#[repr(C)]
1595pub struct CompactTextProps {
1596 pub text_color: u32, pub font_family_hash: u64, pub line_height: i16,
1603 pub letter_spacing: i16, pub word_spacing: i16, pub text_indent: i16, }
1607
1608impl Default for CompactTextProps {
1609 fn default() -> Self {
1610 Self {
1611 text_color: 0,
1612 font_family_hash: 0,
1613 line_height: I16_SENTINEL, letter_spacing: 0,
1615 word_spacing: 0,
1616 text_indent: 0,
1617 }
1618 }
1619}
1620
1621#[derive(Debug, Clone, PartialEq, Eq)]
1642pub struct CompactLayoutCache {
1643 pub uses_viewport_units: bool,
1658 pub tier1_enums: Vec<u64>,
1660 pub tier2_dims: Vec<CompactNodeProps>,
1662 pub tier2_cold: Vec<CompactNodePropsCold>,
1664 pub tier2b_text: Vec<CompactTextProps>,
1666 pub font_dirty_nodes: Vec<usize>,
1676 pub prev_font_hashes: Vec<u64>,
1682 pub font_hash_to_families:
1689 alloc::collections::BTreeMap<u64, crate::props::basic::font::StyleFontFamilyVec>,
1690 pub has_dynamic_conditions: bool,
1697 pub dom_declared_flags: u32,
1703 pub inline_viewport_w: Vec<u32>,
1712 pub inline_viewport_h: Vec<u32>,
1714}
1715
1716impl CompactLayoutCache {
1717 #[must_use]
1719 pub const fn empty() -> Self {
1720 Self {
1721 uses_viewport_units: false,
1722 tier1_enums: Vec::new(),
1723 tier2_dims: Vec::new(),
1724 tier2_cold: Vec::new(),
1725 tier2b_text: Vec::new(),
1726 font_dirty_nodes: Vec::new(),
1727 prev_font_hashes: Vec::new(),
1728 font_hash_to_families: alloc::collections::BTreeMap::new(),
1729 dom_declared_flags: 0,
1730 has_dynamic_conditions: false,
1731 inline_viewport_w: Vec::new(),
1732 inline_viewport_h: Vec::new(),
1733 }
1734 }
1735
1736 #[must_use]
1738 pub fn with_capacity(node_count: usize) -> Self {
1739 Self {
1740 uses_viewport_units: false,
1741 tier1_enums: vec![0u64; node_count],
1742 tier2_dims: vec![CompactNodeProps::default(); node_count],
1743 tier2_cold: vec![CompactNodePropsCold::default(); node_count],
1744 tier2b_text: vec![CompactTextProps::default(); node_count],
1745 font_dirty_nodes: Vec::new(),
1746 prev_font_hashes: vec![0u64; node_count],
1747 font_hash_to_families: alloc::collections::BTreeMap::new(),
1748 dom_declared_flags: 0,
1749 has_dynamic_conditions: false,
1750 inline_viewport_w: Vec::new(),
1751 inline_viewport_h: Vec::new(),
1752 }
1753 }
1754
1755 #[inline]
1757 #[must_use]
1758 pub const fn node_count(&self) -> usize {
1759 self.tier1_enums.len()
1760 }
1761
1762 #[inline]
1765 #[must_use]
1766 pub fn get_display(&self, node_idx: usize) -> LayoutDisplay {
1767 decode_display(self.tier1_enums[node_idx])
1768 }
1769
1770 #[inline]
1771 #[must_use]
1772 pub fn get_position(&self, node_idx: usize) -> LayoutPosition {
1773 decode_position(self.tier1_enums[node_idx])
1774 }
1775
1776 #[inline]
1777 #[must_use]
1778 pub fn get_float(&self, node_idx: usize) -> LayoutFloat {
1779 decode_float(self.tier1_enums[node_idx])
1780 }
1781
1782 #[inline]
1783 #[must_use]
1784 pub fn get_overflow_x(&self, node_idx: usize) -> LayoutOverflow {
1785 decode_overflow_x(self.tier1_enums[node_idx])
1786 }
1787
1788 #[inline]
1789 #[must_use]
1790 pub fn get_overflow_y(&self, node_idx: usize) -> LayoutOverflow {
1791 decode_overflow_y(self.tier1_enums[node_idx])
1792 }
1793
1794 #[inline]
1795 #[must_use]
1796 pub fn get_box_sizing(&self, node_idx: usize) -> LayoutBoxSizing {
1797 decode_box_sizing(self.tier1_enums[node_idx])
1798 }
1799
1800 #[inline]
1801 #[must_use]
1802 pub fn get_flex_direction(&self, node_idx: usize) -> LayoutFlexDirection {
1803 decode_flex_direction(self.tier1_enums[node_idx])
1804 }
1805
1806 #[inline]
1807 #[must_use]
1808 pub fn get_flex_wrap(&self, node_idx: usize) -> LayoutFlexWrap {
1809 decode_flex_wrap(self.tier1_enums[node_idx])
1810 }
1811
1812 #[inline]
1813 #[must_use]
1814 pub fn get_justify_content(&self, node_idx: usize) -> LayoutJustifyContent {
1815 decode_justify_content(self.tier1_enums[node_idx])
1816 }
1817
1818 #[inline]
1819 #[must_use]
1820 pub fn get_align_items(&self, node_idx: usize) -> LayoutAlignItems {
1821 decode_align_items(self.tier1_enums[node_idx])
1822 }
1823
1824 #[inline]
1825 #[must_use]
1826 pub fn get_align_content(&self, node_idx: usize) -> LayoutAlignContent {
1827 decode_align_content(self.tier1_enums[node_idx])
1828 }
1829
1830 #[inline]
1831 #[must_use]
1832 pub fn get_writing_mode(&self, node_idx: usize) -> LayoutWritingMode {
1833 decode_writing_mode(self.tier1_enums[node_idx])
1834 }
1835
1836 #[inline]
1837 #[must_use]
1838 pub fn get_clear(&self, node_idx: usize) -> LayoutClear {
1839 decode_clear(self.tier1_enums[node_idx])
1840 }
1841
1842 #[inline]
1843 #[must_use]
1844 pub fn get_font_weight(&self, node_idx: usize) -> StyleFontWeight {
1845 decode_font_weight(self.tier1_enums[node_idx])
1846 }
1847
1848 #[inline]
1849 #[must_use]
1850 pub fn get_font_style(&self, node_idx: usize) -> StyleFontStyle {
1851 decode_font_style(self.tier1_enums[node_idx])
1852 }
1853
1854 #[inline]
1855 #[must_use]
1856 pub fn get_text_align(&self, node_idx: usize) -> StyleTextAlign {
1857 decode_text_align(self.tier1_enums[node_idx])
1858 }
1859
1860 #[inline]
1861 #[must_use]
1862 pub fn get_visibility(&self, node_idx: usize) -> StyleVisibility {
1863 decode_visibility(self.tier1_enums[node_idx])
1864 }
1865
1866 #[inline]
1867 #[must_use]
1868 pub fn get_white_space(&self, node_idx: usize) -> StyleWhiteSpace {
1869 decode_white_space(self.tier1_enums[node_idx])
1870 }
1871
1872 #[inline]
1873 #[must_use]
1874 pub fn get_direction(&self, node_idx: usize) -> StyleDirection {
1875 decode_direction(self.tier1_enums[node_idx])
1876 }
1877
1878 #[inline]
1879 #[must_use]
1880 pub fn get_vertical_align(&self, node_idx: usize) -> StyleVerticalAlign {
1881 decode_vertical_align(self.tier1_enums[node_idx])
1882 }
1883
1884 #[inline]
1885 #[must_use]
1886 pub fn get_border_collapse(&self, node_idx: usize) -> StyleBorderCollapse {
1887 decode_border_collapse(self.tier1_enums[node_idx])
1888 }
1889
1890 #[inline]
1892 #[must_use]
1893 pub fn get_cursor(&self, node_idx: usize) -> StyleCursor {
1894 cursor_from_u8(self.tier2_cold[node_idx].cursor)
1895 }
1896
1897 #[inline]
1901 #[must_use]
1902 pub fn get_width_raw(&self, node_idx: usize) -> u32 {
1903 self.tier2_dims[node_idx].width
1904 }
1905
1906 #[inline]
1907 #[must_use]
1908 pub fn get_height_raw(&self, node_idx: usize) -> u32 {
1909 self.tier2_dims[node_idx].height
1910 }
1911
1912 #[inline]
1913 #[must_use]
1914 pub fn get_min_width_raw(&self, node_idx: usize) -> u32 {
1915 self.tier2_dims[node_idx].min_width
1916 }
1917
1918 #[inline]
1919 #[must_use]
1920 pub fn get_max_width_raw(&self, node_idx: usize) -> u32 {
1921 self.tier2_dims[node_idx].max_width
1922 }
1923
1924 #[inline]
1925 #[must_use]
1926 pub fn get_min_height_raw(&self, node_idx: usize) -> u32 {
1927 self.tier2_dims[node_idx].min_height
1928 }
1929
1930 #[inline]
1931 #[must_use]
1932 pub fn get_max_height_raw(&self, node_idx: usize) -> u32 {
1933 self.tier2_dims[node_idx].max_height
1934 }
1935
1936 #[inline]
1937 #[must_use]
1938 pub fn get_font_size_raw(&self, node_idx: usize) -> u32 {
1939 self.tier2_dims[node_idx].font_size
1940 }
1941
1942 #[inline]
1943 #[must_use]
1944 pub fn get_flex_basis_raw(&self, node_idx: usize) -> u32 {
1945 self.tier2_dims[node_idx].flex_basis
1946 }
1947
1948 #[inline]
1950 #[must_use]
1951 pub fn get_padding_top(&self, node_idx: usize) -> Option<f32> {
1952 decode_resolved_px_i16(self.tier2_dims[node_idx].padding_top)
1953 }
1954
1955 #[inline]
1956 #[must_use]
1957 pub fn get_padding_right(&self, node_idx: usize) -> Option<f32> {
1958 decode_resolved_px_i16(self.tier2_dims[node_idx].padding_right)
1959 }
1960
1961 #[inline]
1962 #[must_use]
1963 pub fn get_padding_bottom(&self, node_idx: usize) -> Option<f32> {
1964 decode_resolved_px_i16(self.tier2_dims[node_idx].padding_bottom)
1965 }
1966
1967 #[inline]
1968 #[must_use]
1969 pub fn get_padding_left(&self, node_idx: usize) -> Option<f32> {
1970 decode_resolved_px_i16(self.tier2_dims[node_idx].padding_left)
1971 }
1972
1973 #[inline]
1974 #[must_use]
1975 pub fn get_margin_top(&self, node_idx: usize) -> Option<f32> {
1976 let v = self.tier2_dims[node_idx].margin_top;
1977 if v == I16_AUTO {
1978 return None;
1979 } decode_resolved_px_i16(v)
1981 }
1982
1983 #[inline]
1984 #[must_use]
1985 pub fn get_margin_right(&self, node_idx: usize) -> Option<f32> {
1986 let v = self.tier2_dims[node_idx].margin_right;
1987 if v == I16_AUTO {
1988 return None;
1989 }
1990 decode_resolved_px_i16(v)
1991 }
1992
1993 #[inline]
1994 #[must_use]
1995 pub fn get_margin_bottom(&self, node_idx: usize) -> Option<f32> {
1996 let v = self.tier2_dims[node_idx].margin_bottom;
1997 if v == I16_AUTO {
1998 return None;
1999 }
2000 decode_resolved_px_i16(v)
2001 }
2002
2003 #[inline]
2004 #[must_use]
2005 pub fn get_margin_left(&self, node_idx: usize) -> Option<f32> {
2006 let v = self.tier2_dims[node_idx].margin_left;
2007 if v == I16_AUTO {
2008 return None;
2009 }
2010 decode_resolved_px_i16(v)
2011 }
2012
2013 #[inline]
2015 #[must_use]
2016 pub fn is_margin_top_auto(&self, node_idx: usize) -> bool {
2017 self.tier2_dims[node_idx].margin_top == I16_AUTO
2018 }
2019
2020 #[inline]
2021 #[must_use]
2022 pub fn is_margin_right_auto(&self, node_idx: usize) -> bool {
2023 self.tier2_dims[node_idx].margin_right == I16_AUTO
2024 }
2025
2026 #[inline]
2027 #[must_use]
2028 pub fn is_margin_bottom_auto(&self, node_idx: usize) -> bool {
2029 self.tier2_dims[node_idx].margin_bottom == I16_AUTO
2030 }
2031
2032 #[inline]
2033 #[must_use]
2034 pub fn is_margin_left_auto(&self, node_idx: usize) -> bool {
2035 self.tier2_dims[node_idx].margin_left == I16_AUTO
2036 }
2037
2038 #[inline]
2039 #[must_use]
2040 pub fn get_border_top_width(&self, node_idx: usize) -> Option<f32> {
2041 decode_resolved_px_i16(self.tier2_dims[node_idx].border_top_width)
2042 }
2043
2044 #[inline]
2045 #[must_use]
2046 pub fn get_border_right_width(&self, node_idx: usize) -> Option<f32> {
2047 decode_resolved_px_i16(self.tier2_dims[node_idx].border_right_width)
2048 }
2049
2050 #[inline]
2051 #[must_use]
2052 pub fn get_border_bottom_width(&self, node_idx: usize) -> Option<f32> {
2053 decode_resolved_px_i16(self.tier2_dims[node_idx].border_bottom_width)
2054 }
2055
2056 #[inline]
2057 #[must_use]
2058 pub fn get_border_left_width(&self, node_idx: usize) -> Option<f32> {
2059 decode_resolved_px_i16(self.tier2_dims[node_idx].border_left_width)
2060 }
2061
2062 #[inline]
2065 #[must_use]
2066 pub fn get_padding_top_raw(&self, node_idx: usize) -> i16 {
2067 self.tier2_dims[node_idx].padding_top
2068 }
2069
2070 #[inline]
2071 #[must_use]
2072 pub fn get_padding_right_raw(&self, node_idx: usize) -> i16 {
2073 self.tier2_dims[node_idx].padding_right
2074 }
2075
2076 #[inline]
2077 #[must_use]
2078 pub fn get_padding_bottom_raw(&self, node_idx: usize) -> i16 {
2079 self.tier2_dims[node_idx].padding_bottom
2080 }
2081
2082 #[inline]
2083 #[must_use]
2084 pub fn get_padding_left_raw(&self, node_idx: usize) -> i16 {
2085 self.tier2_dims[node_idx].padding_left
2086 }
2087
2088 #[inline]
2089 #[must_use]
2090 pub fn get_margin_top_raw(&self, node_idx: usize) -> i16 {
2091 self.tier2_dims[node_idx].margin_top
2092 }
2093
2094 #[inline]
2095 #[must_use]
2096 pub fn get_margin_right_raw(&self, node_idx: usize) -> i16 {
2097 self.tier2_dims[node_idx].margin_right
2098 }
2099
2100 #[inline]
2101 #[must_use]
2102 pub fn get_margin_bottom_raw(&self, node_idx: usize) -> i16 {
2103 self.tier2_dims[node_idx].margin_bottom
2104 }
2105
2106 #[inline]
2107 #[must_use]
2108 pub fn get_margin_left_raw(&self, node_idx: usize) -> i16 {
2109 self.tier2_dims[node_idx].margin_left
2110 }
2111
2112 #[inline]
2113 #[must_use]
2114 pub fn get_border_top_width_raw(&self, node_idx: usize) -> i16 {
2115 self.tier2_dims[node_idx].border_top_width
2116 }
2117
2118 #[inline]
2119 #[must_use]
2120 pub fn get_border_right_width_raw(&self, node_idx: usize) -> i16 {
2121 self.tier2_dims[node_idx].border_right_width
2122 }
2123
2124 #[inline]
2125 #[must_use]
2126 pub fn get_border_bottom_width_raw(&self, node_idx: usize) -> i16 {
2127 self.tier2_dims[node_idx].border_bottom_width
2128 }
2129
2130 #[inline]
2131 #[must_use]
2132 pub fn get_border_left_width_raw(&self, node_idx: usize) -> i16 {
2133 self.tier2_dims[node_idx].border_left_width
2134 }
2135
2136 #[inline]
2137 #[must_use]
2138 pub fn get_top(&self, node_idx: usize) -> i16 {
2139 self.tier2_dims[node_idx].top
2140 }
2141
2142 #[inline]
2143 #[must_use]
2144 pub fn get_right(&self, node_idx: usize) -> i16 {
2145 self.tier2_dims[node_idx].right
2146 }
2147
2148 #[inline]
2149 #[must_use]
2150 pub fn get_bottom(&self, node_idx: usize) -> i16 {
2151 self.tier2_dims[node_idx].bottom
2152 }
2153
2154 #[inline]
2155 #[must_use]
2156 pub fn get_left(&self, node_idx: usize) -> i16 {
2157 self.tier2_dims[node_idx].left
2158 }
2159
2160 #[inline]
2161 #[must_use]
2162 pub fn get_flex_grow(&self, node_idx: usize) -> Option<f32> {
2163 decode_flex_u16(self.tier2_dims[node_idx].flex_grow)
2164 }
2165
2166 #[inline]
2167 #[must_use]
2168 pub fn get_flex_shrink(&self, node_idx: usize) -> Option<f32> {
2169 decode_flex_u16(self.tier2_dims[node_idx].flex_shrink)
2170 }
2171
2172 #[inline]
2173 #[must_use]
2174 pub fn get_z_index(&self, node_idx: usize) -> i16 {
2175 self.tier2_cold[node_idx].z_index
2176 }
2177
2178 #[inline]
2181 #[must_use]
2182 pub fn get_border_top_color_raw(&self, node_idx: usize) -> u32 {
2183 self.tier2_cold[node_idx].border_top_color
2184 }
2185
2186 #[inline]
2187 #[must_use]
2188 pub fn get_border_right_color_raw(&self, node_idx: usize) -> u32 {
2189 self.tier2_cold[node_idx].border_right_color
2190 }
2191
2192 #[inline]
2193 #[must_use]
2194 pub fn get_border_bottom_color_raw(&self, node_idx: usize) -> u32 {
2195 self.tier2_cold[node_idx].border_bottom_color
2196 }
2197
2198 #[inline]
2199 #[must_use]
2200 pub fn get_border_left_color_raw(&self, node_idx: usize) -> u32 {
2201 self.tier2_cold[node_idx].border_left_color
2202 }
2203
2204 #[inline]
2207 #[must_use]
2208 pub fn get_border_styles_packed(&self, node_idx: usize) -> u16 {
2209 self.tier2_cold[node_idx].border_styles_packed
2210 }
2211
2212 #[inline]
2213 #[must_use]
2214 pub fn get_border_top_style(&self, node_idx: usize) -> BorderStyle {
2215 decode_border_top_style(self.tier2_cold[node_idx].border_styles_packed)
2216 }
2217
2218 #[inline]
2219 #[must_use]
2220 pub fn get_border_right_style(&self, node_idx: usize) -> BorderStyle {
2221 decode_border_right_style(self.tier2_cold[node_idx].border_styles_packed)
2222 }
2223
2224 #[inline]
2225 #[must_use]
2226 pub fn get_border_bottom_style(&self, node_idx: usize) -> BorderStyle {
2227 decode_border_bottom_style(self.tier2_cold[node_idx].border_styles_packed)
2228 }
2229
2230 #[inline]
2231 #[must_use]
2232 pub fn get_border_left_style(&self, node_idx: usize) -> BorderStyle {
2233 decode_border_left_style(self.tier2_cold[node_idx].border_styles_packed)
2234 }
2235
2236 #[inline]
2239 #[must_use]
2240 pub fn get_border_spacing_h_raw(&self, node_idx: usize) -> i16 {
2241 self.tier2_cold[node_idx].border_spacing_h
2242 }
2243
2244 #[inline]
2245 #[must_use]
2246 pub fn get_border_spacing_v_raw(&self, node_idx: usize) -> i16 {
2247 self.tier2_cold[node_idx].border_spacing_v
2248 }
2249
2250 #[inline]
2253 #[must_use]
2254 pub fn get_tab_size_raw(&self, node_idx: usize) -> i16 {
2255 self.tier2_cold[node_idx].tab_size
2256 }
2257
2258 #[inline]
2261 #[must_use]
2262 pub fn get_border_top_left_radius_raw(&self, node_idx: usize) -> i16 {
2263 self.tier2_cold[node_idx].border_top_left_radius
2264 }
2265
2266 #[inline]
2267 #[must_use]
2268 pub fn get_border_top_right_radius_raw(&self, node_idx: usize) -> i16 {
2269 self.tier2_cold[node_idx].border_top_right_radius
2270 }
2271
2272 #[inline]
2273 #[must_use]
2274 pub fn get_border_bottom_left_radius_raw(&self, node_idx: usize) -> i16 {
2275 self.tier2_cold[node_idx].border_bottom_left_radius
2276 }
2277
2278 #[inline]
2279 #[must_use]
2280 pub fn get_border_bottom_right_radius_raw(&self, node_idx: usize) -> i16 {
2281 self.tier2_cold[node_idx].border_bottom_right_radius
2282 }
2283
2284 #[inline]
2289 #[must_use]
2290 pub fn get_opacity_raw(&self, node_idx: usize) -> u8 {
2291 self.tier2_cold[node_idx].opacity
2292 }
2293
2294 #[inline]
2295 #[must_use]
2296 pub fn get_hot_flags(&self, node_idx: usize) -> u8 {
2297 self.tier2_cold[node_idx].hot_flags
2298 }
2299
2300 #[inline]
2301 #[must_use]
2302 pub fn has_transform(&self, node_idx: usize) -> bool {
2303 self.tier2_cold[node_idx].hot_flags & HOT_FLAG_HAS_TRANSFORM != 0
2304 }
2305
2306 #[inline]
2307 #[must_use]
2308 pub fn has_transform_origin(&self, node_idx: usize) -> bool {
2309 self.tier2_cold[node_idx].hot_flags & HOT_FLAG_HAS_TRANSFORM_ORIGIN != 0
2310 }
2311
2312 #[inline]
2313 #[must_use]
2314 pub fn has_box_shadow(&self, node_idx: usize) -> bool {
2315 self.tier2_cold[node_idx].hot_flags & HOT_FLAG_HAS_BOX_SHADOW != 0
2316 }
2317
2318 #[inline]
2319 #[must_use]
2320 pub fn has_text_decoration(&self, node_idx: usize) -> bool {
2321 self.tier2_cold[node_idx].hot_flags & HOT_FLAG_HAS_TEXT_DECORATION != 0
2322 }
2323
2324 #[inline]
2325 #[must_use]
2326 pub fn has_background(&self, node_idx: usize) -> bool {
2327 self.tier2_cold[node_idx].hot_flags & HOT_FLAG_HAS_BACKGROUND != 0
2328 }
2329
2330 #[inline]
2331 #[must_use]
2332 pub fn has_clip_path(&self, node_idx: usize) -> bool {
2333 self.tier2_cold[node_idx].hot_flags & HOT_FLAG_HAS_CLIP_PATH != 0
2334 }
2335
2336 #[inline]
2337 #[must_use]
2338 pub fn has_scrollbar_css(&self, node_idx: usize) -> bool {
2339 self.tier2_cold[node_idx].extra_flags & EXTRA_FLAG_HAS_SCROLLBAR_CSS != 0
2340 }
2341
2342 #[inline]
2343 #[must_use]
2344 pub fn has_counter(&self, node_idx: usize) -> bool {
2345 self.tier2_cold[node_idx].extra_flags & EXTRA_FLAG_HAS_COUNTER != 0
2346 }
2347
2348 #[inline]
2349 #[must_use]
2350 pub fn has_break(&self, node_idx: usize) -> bool {
2351 self.tier2_cold[node_idx].extra_flags & EXTRA_FLAG_HAS_BREAK != 0
2352 }
2353
2354 #[inline]
2355 #[must_use]
2356 pub fn has_text_orientation(&self, node_idx: usize) -> bool {
2357 self.tier2_cold[node_idx].extra_flags & EXTRA_FLAG_HAS_TEXT_ORIENTATION != 0
2358 }
2359
2360 #[inline]
2361 #[must_use]
2362 pub fn has_text_shadow(&self, node_idx: usize) -> bool {
2363 self.tier2_cold[node_idx].extra_flags & EXTRA_FLAG_HAS_TEXT_SHADOW != 0
2364 }
2365
2366 #[inline]
2367 #[must_use]
2368 pub fn has_backdrop_filter(&self, node_idx: usize) -> bool {
2369 self.tier2_cold[node_idx].extra_flags & EXTRA_FLAG_HAS_BACKDROP_FILTER != 0
2370 }
2371
2372 #[inline]
2373 #[must_use]
2374 pub fn has_filter(&self, node_idx: usize) -> bool {
2375 self.tier2_cold[node_idx].extra_flags & EXTRA_FLAG_HAS_FILTER != 0
2376 }
2377
2378 #[inline]
2379 #[must_use]
2380 pub fn has_mix_blend_mode(&self, node_idx: usize) -> bool {
2381 self.tier2_cold[node_idx].extra_flags & EXTRA_FLAG_HAS_MIX_BLEND_MODE != 0
2382 }
2383
2384 #[inline]
2387 #[must_use]
2388 pub const fn dom_declared(&self, flag: u32) -> bool {
2389 self.dom_declared_flags & flag != 0
2390 }
2391
2392 #[inline]
2394 #[must_use]
2395 pub fn get_scrollbar_gutter_bits(&self, node_idx: usize) -> u8 {
2396 (self.tier2_cold[node_idx].hot_flags & HOT_FLAG_SCROLLBAR_GUTTER_MASK)
2397 >> HOT_FLAG_SCROLLBAR_GUTTER_SHIFT
2398 }
2399
2400 #[inline]
2403 #[must_use]
2404 pub fn get_text_color_raw(&self, node_idx: usize) -> u32 {
2405 self.tier2b_text[node_idx].text_color
2406 }
2407
2408 #[inline]
2409 #[must_use]
2410 pub fn get_font_family_hash(&self, node_idx: usize) -> u64 {
2411 self.tier2b_text[node_idx].font_family_hash
2412 }
2413
2414 #[inline]
2415 #[must_use]
2416 pub fn get_line_height(&self, node_idx: usize) -> Option<f32> {
2417 decode_resolved_px_i16(self.tier2b_text[node_idx].line_height)
2418 }
2419
2420 #[inline]
2421 #[must_use]
2422 pub fn get_letter_spacing(&self, node_idx: usize) -> Option<f32> {
2423 decode_resolved_px_i16(self.tier2b_text[node_idx].letter_spacing)
2424 }
2425
2426 #[inline]
2427 #[must_use]
2428 pub fn get_word_spacing(&self, node_idx: usize) -> Option<f32> {
2429 decode_resolved_px_i16(self.tier2b_text[node_idx].word_spacing)
2430 }
2431
2432 #[inline]
2433 #[must_use]
2434 pub fn get_text_indent(&self, node_idx: usize) -> Option<f32> {
2435 decode_resolved_px_i16(self.tier2b_text[node_idx].text_indent)
2436 }
2437}
2438
2439#[inline]
2449#[must_use]
2450pub fn encode_css_pixel_as_i16(prop: &CssPropertyValue<PixelValue>) -> i16 {
2451 match prop {
2452 CssPropertyValue::Exact(pv) => {
2453 if pv.metric == SizeMetric::Px {
2454 encode_resolved_px_i16(pv.number.get())
2455 } else {
2456 I16_SENTINEL }
2458 }
2459 CssPropertyValue::Auto => I16_AUTO,
2460 CssPropertyValue::Initial => I16_INITIAL,
2461 CssPropertyValue::Inherit => I16_INHERIT,
2462 _ => I16_SENTINEL,
2463 }
2464}
2465
2466#[cfg(test)]
2467mod tests {
2468 use super::*;
2469
2470 #[test]
2471 fn test_tier1_roundtrip() {
2472 let t1 = encode_tier1(
2473 LayoutDisplay::Flex,
2474 LayoutPosition::Relative,
2475 LayoutFloat::Left,
2476 LayoutOverflow::Hidden,
2477 LayoutOverflow::Scroll,
2478 LayoutBoxSizing::BorderBox,
2479 LayoutFlexDirection::Column,
2480 LayoutFlexWrap::Wrap,
2481 LayoutJustifyContent::SpaceBetween,
2482 LayoutAlignItems::Center,
2483 LayoutAlignContent::End,
2484 LayoutWritingMode::VerticalRl,
2485 LayoutClear::Both,
2486 StyleFontWeight::Bold,
2487 StyleFontStyle::Italic,
2488 StyleTextAlign::Center,
2489 StyleVisibility::Hidden,
2490 StyleWhiteSpace::Pre,
2491 StyleDirection::Rtl,
2492 StyleVerticalAlign::Middle,
2493 StyleBorderCollapse::Collapse,
2494 );
2495
2496 assert!(tier1_is_populated(t1));
2497 assert_eq!(decode_display(t1), LayoutDisplay::Flex);
2498 assert_eq!(decode_position(t1), LayoutPosition::Relative);
2499 assert_eq!(decode_float(t1), LayoutFloat::Left);
2500 assert_eq!(decode_overflow_x(t1), LayoutOverflow::Hidden);
2501 assert_eq!(decode_overflow_y(t1), LayoutOverflow::Scroll);
2502 assert_eq!(decode_box_sizing(t1), LayoutBoxSizing::BorderBox);
2503 assert_eq!(decode_flex_direction(t1), LayoutFlexDirection::Column);
2504 assert_eq!(decode_flex_wrap(t1), LayoutFlexWrap::Wrap);
2505 assert_eq!(
2506 decode_justify_content(t1),
2507 LayoutJustifyContent::SpaceBetween
2508 );
2509 assert_eq!(decode_align_items(t1), LayoutAlignItems::Center);
2510 assert_eq!(decode_align_content(t1), LayoutAlignContent::End);
2511 assert_eq!(decode_writing_mode(t1), LayoutWritingMode::VerticalRl);
2512 assert_eq!(decode_clear(t1), LayoutClear::Both);
2513 assert_eq!(decode_font_weight(t1), StyleFontWeight::Bold);
2514 assert_eq!(decode_font_style(t1), StyleFontStyle::Italic);
2515 assert_eq!(decode_text_align(t1), StyleTextAlign::Center);
2516 assert_eq!(decode_visibility(t1), StyleVisibility::Hidden);
2517 assert_eq!(decode_white_space(t1), StyleWhiteSpace::Pre);
2518 assert_eq!(decode_direction(t1), StyleDirection::Rtl);
2519 assert_eq!(decode_vertical_align(t1), StyleVerticalAlign::Middle);
2520 assert_eq!(decode_border_collapse(t1), StyleBorderCollapse::Collapse);
2521 }
2522
2523 #[test]
2524 fn test_tier1_defaults() {
2525 let t1 = encode_tier1(
2526 LayoutDisplay::Block,
2527 LayoutPosition::Static,
2528 LayoutFloat::None,
2529 LayoutOverflow::Visible,
2530 LayoutOverflow::Visible,
2531 LayoutBoxSizing::ContentBox,
2532 LayoutFlexDirection::Row,
2533 LayoutFlexWrap::NoWrap,
2534 LayoutJustifyContent::FlexStart,
2535 LayoutAlignItems::Stretch,
2536 LayoutAlignContent::Stretch,
2537 LayoutWritingMode::HorizontalTb,
2538 LayoutClear::None,
2539 StyleFontWeight::Normal,
2540 StyleFontStyle::Normal,
2541 StyleTextAlign::Left,
2542 StyleVisibility::Visible,
2543 StyleWhiteSpace::Normal,
2544 StyleDirection::Ltr,
2545 StyleVerticalAlign::Baseline,
2546 StyleBorderCollapse::Separate,
2547 );
2548
2549 assert!(tier1_is_populated(t1));
2550 assert_eq!(decode_display(t1), LayoutDisplay::Block);
2551 assert_eq!(decode_position(t1), LayoutPosition::Static);
2552 }
2553
2554 #[test]
2555 fn test_pixel_value_u32_roundtrip() {
2556 let pv = PixelValue::px(123.456);
2557 let encoded = encode_pixel_value_u32(&pv);
2558 assert!(encoded < U32_SENTINEL_THRESHOLD);
2559 let decoded = decode_pixel_value_u32(encoded).unwrap();
2560 assert_eq!(decoded.metric, SizeMetric::Px);
2561 assert!((decoded.number.get() - 123.456).abs() < 0.002);
2563 }
2564
2565 #[test]
2566 fn test_pixel_value_u32_percent() {
2567 let pv = PixelValue {
2568 metric: SizeMetric::Percent,
2569 number: FloatValue::new(50.0),
2570 };
2571 let encoded = encode_pixel_value_u32(&pv);
2572 let decoded = decode_pixel_value_u32(encoded).unwrap();
2573 assert_eq!(decoded.metric, SizeMetric::Percent);
2574 assert!((decoded.number.get() - 50.0).abs() < 0.002);
2575 }
2576
2577 #[test]
2578 fn test_sentinel_values() {
2579 assert_eq!(decode_pixel_value_u32(U32_SENTINEL), None);
2580 assert_eq!(decode_pixel_value_u32(U32_AUTO), None);
2581 assert_eq!(decode_pixel_value_u32(U32_MIN_CONTENT), None);
2582 assert_eq!(decode_resolved_px_i16(I16_SENTINEL), None);
2583 assert_eq!(decode_resolved_px_i16(I16_AUTO), None);
2584 }
2585
2586 #[test]
2587 fn test_resolved_px_i16_roundtrip() {
2588 let px = 123.4f32;
2589 let encoded = encode_resolved_px_i16(px);
2590 let decoded = decode_resolved_px_i16(encoded).unwrap();
2591 assert!((decoded - 123.4).abs() < 0.11);
2592
2593 let px = -50.7f32;
2595 let encoded = encode_resolved_px_i16(px);
2596 let decoded = decode_resolved_px_i16(encoded).unwrap();
2597 assert!((decoded - (-50.7)).abs() < 0.11);
2598 }
2599
2600 #[test]
2601 fn test_flex_u16_roundtrip() {
2602 let v = 2.5f32;
2603 let encoded = encode_flex_u16(v);
2604 let decoded = decode_flex_u16(encoded).unwrap();
2605 assert!((decoded - 2.5).abs() < 0.011);
2606 }
2607
2608 #[test]
2609 fn test_compact_node_props_size() {
2610 assert_eq!(size_of::<CompactNodeProps>(), 72);
2613 }
2614
2615 #[test]
2616 fn test_compact_node_props_cold_size() {
2617 assert_eq!(size_of::<CompactNodePropsCold>(), 48);
2622 }
2623
2624 #[test]
2625 fn test_compact_text_props_size() {
2626 assert_eq!(size_of::<CompactTextProps>(), 24);
2627 }
2628
2629 #[test]
2646 fn test_justify_items_zero_is_stretch() {
2647 assert_eq!(layout_justify_items_from_u8(0), LayoutJustifyItems::Stretch);
2651 assert_eq!(layout_justify_items_to_u8(LayoutJustifyItems::Stretch), 0);
2652 }
2653
2654 #[test]
2655 fn test_tier1_enum_zero_sentinel_is_css_initial() {
2656 assert_eq!(layout_display_from_u8(0), LayoutDisplay::Block);
2657 assert_eq!(layout_position_from_u8(0), LayoutPosition::Static);
2658 assert_eq!(layout_float_from_u8(0), LayoutFloat::None);
2659 assert_eq!(layout_overflow_from_u8(0), LayoutOverflow::Visible);
2660 assert_eq!(layout_box_sizing_from_u8(0), LayoutBoxSizing::ContentBox);
2661 assert_eq!(layout_flex_direction_from_u8(0), LayoutFlexDirection::Row);
2662 assert_eq!(layout_flex_wrap_from_u8(0), LayoutFlexWrap::NoWrap);
2663 assert_eq!(
2664 layout_justify_content_from_u8(0),
2665 LayoutJustifyContent::FlexStart
2666 );
2667 assert_eq!(layout_align_items_from_u8(0), LayoutAlignItems::Stretch);
2668 assert_eq!(layout_align_content_from_u8(0), LayoutAlignContent::Stretch);
2669 assert_eq!(layout_align_self_from_u8(0), LayoutAlignSelf::Auto);
2670 assert_eq!(layout_justify_self_from_u8(0), LayoutJustifySelf::Auto);
2671 assert_eq!(layout_justify_items_from_u8(0), LayoutJustifyItems::Stretch);
2672 assert_eq!(layout_grid_auto_flow_from_u8(0), LayoutGridAutoFlow::Row);
2673 assert_eq!(
2674 layout_writing_mode_from_u8(0),
2675 LayoutWritingMode::HorizontalTb
2676 );
2677 assert_eq!(layout_clear_from_u8(0), LayoutClear::None);
2678 assert_eq!(style_font_weight_from_u8(0), StyleFontWeight::Normal);
2679 assert_eq!(style_font_style_from_u8(0), StyleFontStyle::Normal);
2680 assert_eq!(style_text_align_from_u8(0), StyleTextAlign::Start);
2683 assert_eq!(style_visibility_from_u8(0), StyleVisibility::Visible);
2684 assert_eq!(style_white_space_from_u8(0), StyleWhiteSpace::Normal);
2685 assert_eq!(style_direction_from_u8(0), StyleDirection::Ltr);
2686 assert_eq!(
2687 style_vertical_align_from_u8(0),
2688 StyleVerticalAlign::Baseline
2689 );
2690 assert_eq!(border_collapse_from_u8(0), StyleBorderCollapse::Separate);
2691 }
2692
2693 #[test]
2694 fn test_tier1_enum_initial_encodes_to_zero() {
2695 assert_eq!(layout_display_to_u8(LayoutDisplay::Block), 0);
2700 assert_eq!(layout_position_to_u8(LayoutPosition::Static), 0);
2701 assert_eq!(layout_float_to_u8(LayoutFloat::None), 0);
2702 assert_eq!(layout_overflow_to_u8(LayoutOverflow::Visible), 0);
2703 assert_eq!(layout_box_sizing_to_u8(LayoutBoxSizing::ContentBox), 0);
2704 assert_eq!(layout_flex_direction_to_u8(LayoutFlexDirection::Row), 0);
2705 assert_eq!(layout_flex_wrap_to_u8(LayoutFlexWrap::NoWrap), 0);
2706 assert_eq!(
2707 layout_justify_content_to_u8(LayoutJustifyContent::FlexStart),
2708 0
2709 );
2710 assert_eq!(layout_align_items_to_u8(LayoutAlignItems::Stretch), 0);
2711 assert_eq!(layout_align_content_to_u8(LayoutAlignContent::Stretch), 0);
2712 assert_eq!(layout_align_self_to_u8(LayoutAlignSelf::Auto), 0);
2713 assert_eq!(layout_justify_self_to_u8(LayoutJustifySelf::Auto), 0);
2714 assert_eq!(layout_justify_items_to_u8(LayoutJustifyItems::Stretch), 0);
2715 assert_eq!(layout_grid_auto_flow_to_u8(LayoutGridAutoFlow::Row), 0);
2716 assert_eq!(
2717 layout_writing_mode_to_u8(LayoutWritingMode::HorizontalTb),
2718 0
2719 );
2720 assert_eq!(layout_clear_to_u8(LayoutClear::None), 0);
2721 assert_eq!(style_font_weight_to_u8(StyleFontWeight::Normal), 0);
2722 assert_eq!(style_font_style_to_u8(StyleFontStyle::Normal), 0);
2723 assert_eq!(style_text_align_to_u8(StyleTextAlign::Left), 4);
2724 assert_eq!(style_visibility_to_u8(StyleVisibility::Visible), 0);
2725 assert_eq!(style_white_space_to_u8(StyleWhiteSpace::Normal), 0);
2726 assert_eq!(style_direction_to_u8(StyleDirection::Ltr), 0);
2727 assert_eq!(style_vertical_align_to_u8(StyleVerticalAlign::Baseline), 0);
2728 assert_eq!(border_collapse_to_u8(StyleBorderCollapse::Separate), 0);
2729 }
2730
2731 macro_rules! roundtrip_all {
2738 ($name:ident, $to:ident, $from:ident, [$($variant:expr),+ $(,)?]) => {
2739 #[test]
2740 fn $name() {
2741 for v in [$($variant),+] {
2742 let u = $to(v);
2743 let decoded = $from(u);
2744 assert_eq!(decoded, v, "{:?} != {:?} (via u8 = {})", decoded, v, u);
2745 }
2746 }
2747 };
2748 }
2749
2750 roundtrip_all!(
2751 rt_display,
2752 layout_display_to_u8,
2753 layout_display_from_u8,
2754 [
2755 LayoutDisplay::Block,
2756 LayoutDisplay::Inline,
2757 LayoutDisplay::InlineBlock,
2758 LayoutDisplay::Flex,
2759 LayoutDisplay::None,
2760 LayoutDisplay::InlineFlex,
2761 LayoutDisplay::Table,
2762 LayoutDisplay::InlineTable,
2763 LayoutDisplay::TableRowGroup,
2764 LayoutDisplay::TableHeaderGroup,
2765 LayoutDisplay::TableFooterGroup,
2766 LayoutDisplay::TableRow,
2767 LayoutDisplay::TableColumnGroup,
2768 LayoutDisplay::TableColumn,
2769 LayoutDisplay::TableCell,
2770 LayoutDisplay::TableCaption,
2771 LayoutDisplay::FlowRoot,
2772 LayoutDisplay::ListItem,
2773 LayoutDisplay::RunIn,
2774 LayoutDisplay::Marker,
2775 LayoutDisplay::Grid,
2776 LayoutDisplay::InlineGrid,
2777 LayoutDisplay::Contents,
2778 ]
2779 );
2780
2781 roundtrip_all!(
2782 rt_position,
2783 layout_position_to_u8,
2784 layout_position_from_u8,
2785 [
2786 LayoutPosition::Static,
2787 LayoutPosition::Relative,
2788 LayoutPosition::Absolute,
2789 LayoutPosition::Fixed,
2790 LayoutPosition::Sticky,
2791 ]
2792 );
2793
2794 roundtrip_all!(
2795 rt_float,
2796 layout_float_to_u8,
2797 layout_float_from_u8,
2798 [LayoutFloat::None, LayoutFloat::Left, LayoutFloat::Right,]
2799 );
2800
2801 roundtrip_all!(
2802 rt_overflow,
2803 layout_overflow_to_u8,
2804 layout_overflow_from_u8,
2805 [
2806 LayoutOverflow::Visible,
2807 LayoutOverflow::Hidden,
2808 LayoutOverflow::Scroll,
2809 LayoutOverflow::Auto,
2810 LayoutOverflow::Clip,
2811 ]
2812 );
2813
2814 roundtrip_all!(
2815 rt_box_sizing,
2816 layout_box_sizing_to_u8,
2817 layout_box_sizing_from_u8,
2818 [LayoutBoxSizing::ContentBox, LayoutBoxSizing::BorderBox,]
2819 );
2820
2821 roundtrip_all!(
2822 rt_flex_direction,
2823 layout_flex_direction_to_u8,
2824 layout_flex_direction_from_u8,
2825 [
2826 LayoutFlexDirection::Row,
2827 LayoutFlexDirection::RowReverse,
2828 LayoutFlexDirection::Column,
2829 LayoutFlexDirection::ColumnReverse,
2830 ]
2831 );
2832
2833 roundtrip_all!(
2834 rt_flex_wrap,
2835 layout_flex_wrap_to_u8,
2836 layout_flex_wrap_from_u8,
2837 [
2838 LayoutFlexWrap::NoWrap,
2839 LayoutFlexWrap::Wrap,
2840 LayoutFlexWrap::WrapReverse,
2841 ]
2842 );
2843
2844 roundtrip_all!(
2845 rt_justify_content,
2846 layout_justify_content_to_u8,
2847 layout_justify_content_from_u8,
2848 [
2849 LayoutJustifyContent::FlexStart,
2850 LayoutJustifyContent::FlexEnd,
2851 LayoutJustifyContent::Start,
2852 LayoutJustifyContent::End,
2853 LayoutJustifyContent::Center,
2854 LayoutJustifyContent::SpaceBetween,
2855 LayoutJustifyContent::SpaceAround,
2856 LayoutJustifyContent::SpaceEvenly,
2857 ]
2858 );
2859
2860 roundtrip_all!(
2861 rt_align_items,
2862 layout_align_items_to_u8,
2863 layout_align_items_from_u8,
2864 [
2865 LayoutAlignItems::Stretch,
2866 LayoutAlignItems::Center,
2867 LayoutAlignItems::Start,
2868 LayoutAlignItems::End,
2869 LayoutAlignItems::Baseline,
2870 ]
2871 );
2872
2873 roundtrip_all!(
2874 rt_align_self,
2875 layout_align_self_to_u8,
2876 layout_align_self_from_u8,
2877 [
2878 LayoutAlignSelf::Auto,
2879 LayoutAlignSelf::Stretch,
2880 LayoutAlignSelf::Center,
2881 LayoutAlignSelf::Start,
2882 LayoutAlignSelf::End,
2883 LayoutAlignSelf::Baseline,
2884 ]
2885 );
2886
2887 roundtrip_all!(
2888 rt_justify_self,
2889 layout_justify_self_to_u8,
2890 layout_justify_self_from_u8,
2891 [
2892 LayoutJustifySelf::Auto,
2893 LayoutJustifySelf::Start,
2894 LayoutJustifySelf::End,
2895 LayoutJustifySelf::Center,
2896 LayoutJustifySelf::Stretch,
2897 ]
2898 );
2899
2900 roundtrip_all!(
2901 rt_justify_items,
2902 layout_justify_items_to_u8,
2903 layout_justify_items_from_u8,
2904 [
2905 LayoutJustifyItems::Stretch,
2906 LayoutJustifyItems::Start,
2907 LayoutJustifyItems::End,
2908 LayoutJustifyItems::Center,
2909 ]
2910 );
2911
2912 roundtrip_all!(
2913 rt_grid_auto_flow,
2914 layout_grid_auto_flow_to_u8,
2915 layout_grid_auto_flow_from_u8,
2916 [
2917 LayoutGridAutoFlow::Row,
2918 LayoutGridAutoFlow::Column,
2919 LayoutGridAutoFlow::RowDense,
2920 LayoutGridAutoFlow::ColumnDense,
2921 ]
2922 );
2923
2924 roundtrip_all!(
2925 rt_align_content,
2926 layout_align_content_to_u8,
2927 layout_align_content_from_u8,
2928 [
2929 LayoutAlignContent::Stretch,
2930 LayoutAlignContent::Center,
2931 LayoutAlignContent::Start,
2932 LayoutAlignContent::End,
2933 LayoutAlignContent::SpaceBetween,
2934 LayoutAlignContent::SpaceAround,
2935 ]
2936 );
2937
2938 roundtrip_all!(
2939 rt_writing_mode,
2940 layout_writing_mode_to_u8,
2941 layout_writing_mode_from_u8,
2942 [
2943 LayoutWritingMode::HorizontalTb,
2944 LayoutWritingMode::VerticalRl,
2945 LayoutWritingMode::VerticalLr,
2946 ]
2947 );
2948
2949 roundtrip_all!(
2950 rt_clear,
2951 layout_clear_to_u8,
2952 layout_clear_from_u8,
2953 [
2954 LayoutClear::None,
2955 LayoutClear::Left,
2956 LayoutClear::Right,
2957 LayoutClear::Both,
2958 ]
2959 );
2960
2961 roundtrip_all!(
2962 rt_font_weight,
2963 style_font_weight_to_u8,
2964 style_font_weight_from_u8,
2965 [
2966 StyleFontWeight::Normal,
2967 StyleFontWeight::W100,
2968 StyleFontWeight::W200,
2969 StyleFontWeight::W300,
2970 StyleFontWeight::W500,
2971 StyleFontWeight::W600,
2972 StyleFontWeight::Bold,
2973 StyleFontWeight::W800,
2974 StyleFontWeight::W900,
2975 StyleFontWeight::Lighter,
2976 StyleFontWeight::Bolder,
2977 ]
2978 );
2979
2980 roundtrip_all!(
2981 rt_font_style,
2982 style_font_style_to_u8,
2983 style_font_style_from_u8,
2984 [
2985 StyleFontStyle::Normal,
2986 StyleFontStyle::Italic,
2987 StyleFontStyle::Oblique,
2988 ]
2989 );
2990
2991 roundtrip_all!(
2992 rt_text_align,
2993 style_text_align_to_u8,
2994 style_text_align_from_u8,
2995 [
2996 StyleTextAlign::Left,
2997 StyleTextAlign::Center,
2998 StyleTextAlign::Right,
2999 StyleTextAlign::Justify,
3000 StyleTextAlign::Start,
3001 StyleTextAlign::End,
3002 ]
3003 );
3004
3005 roundtrip_all!(
3006 rt_visibility,
3007 style_visibility_to_u8,
3008 style_visibility_from_u8,
3009 [
3010 StyleVisibility::Visible,
3011 StyleVisibility::Hidden,
3012 StyleVisibility::Collapse,
3013 ]
3014 );
3015
3016 roundtrip_all!(
3017 rt_white_space,
3018 style_white_space_to_u8,
3019 style_white_space_from_u8,
3020 [
3021 StyleWhiteSpace::Normal,
3022 StyleWhiteSpace::Pre,
3023 StyleWhiteSpace::Nowrap,
3024 StyleWhiteSpace::PreWrap,
3025 StyleWhiteSpace::PreLine,
3026 StyleWhiteSpace::BreakSpaces,
3027 ]
3028 );
3029
3030 roundtrip_all!(
3031 rt_direction,
3032 style_direction_to_u8,
3033 style_direction_from_u8,
3034 [StyleDirection::Ltr, StyleDirection::Rtl,]
3035 );
3036
3037 roundtrip_all!(
3038 rt_vertical_align,
3039 style_vertical_align_to_u8,
3040 style_vertical_align_from_u8,
3041 [
3042 StyleVerticalAlign::Baseline,
3043 StyleVerticalAlign::Top,
3044 StyleVerticalAlign::Middle,
3045 StyleVerticalAlign::Bottom,
3046 StyleVerticalAlign::Sub,
3047 StyleVerticalAlign::Superscript,
3048 StyleVerticalAlign::TextTop,
3049 StyleVerticalAlign::TextBottom,
3050 ]
3051 );
3052
3053 roundtrip_all!(
3054 rt_border_collapse,
3055 border_collapse_to_u8,
3056 border_collapse_from_u8,
3057 [StyleBorderCollapse::Separate, StyleBorderCollapse::Collapse,]
3058 );
3059
3060 #[test]
3069 fn test_encoded_u8_fits_in_tier1_mask() {
3070 fn assert_fits(name: &str, val: u8, mask: u64) {
3071 assert!(
3072 u64::from(val) & !mask == 0,
3073 "{name}: encoded u8 {val} overflows mask {mask:b}",
3074 );
3075 }
3076
3077 assert_fits(
3078 "display",
3079 layout_display_to_u8(LayoutDisplay::Contents),
3080 DISPLAY_MASK,
3081 );
3082 assert_fits(
3083 "position",
3084 layout_position_to_u8(LayoutPosition::Sticky),
3085 POSITION_MASK,
3086 );
3087 assert_fits("float", layout_float_to_u8(LayoutFloat::Right), FLOAT_MASK);
3088 assert_fits(
3089 "overflow",
3090 layout_overflow_to_u8(LayoutOverflow::Clip),
3091 OVERFLOW_MASK,
3092 );
3093 assert_fits(
3094 "box_sizing",
3095 layout_box_sizing_to_u8(LayoutBoxSizing::BorderBox),
3096 BOX_SIZING_MASK,
3097 );
3098 assert_fits(
3099 "flex_direction",
3100 layout_flex_direction_to_u8(LayoutFlexDirection::ColumnReverse),
3101 FLEX_DIR_MASK,
3102 );
3103 assert_fits(
3104 "flex_wrap",
3105 layout_flex_wrap_to_u8(LayoutFlexWrap::WrapReverse),
3106 FLEX_WRAP_MASK,
3107 );
3108 assert_fits(
3109 "justify_content",
3110 layout_justify_content_to_u8(LayoutJustifyContent::SpaceEvenly),
3111 JUSTIFY_MASK,
3112 );
3113 assert_fits(
3114 "align_items",
3115 layout_align_items_to_u8(LayoutAlignItems::Baseline),
3116 ALIGN_MASK,
3117 );
3118 assert_fits(
3119 "align_self",
3120 layout_align_self_to_u8(LayoutAlignSelf::Baseline),
3121 ALIGN_SELF_MASK,
3122 );
3123 assert_fits(
3124 "justify_self",
3125 layout_justify_self_to_u8(LayoutJustifySelf::Stretch),
3126 JUSTIFY_SELF_MASK,
3127 );
3128 assert_fits(
3129 "justify_items",
3130 layout_justify_items_to_u8(LayoutJustifyItems::Center),
3131 JUSTIFY_ITEMS_MASK,
3132 );
3133 assert_fits(
3134 "grid_auto_flow",
3135 layout_grid_auto_flow_to_u8(LayoutGridAutoFlow::ColumnDense),
3136 GRID_AUTO_FLOW_MASK,
3137 );
3138 assert_fits(
3139 "align_content",
3140 layout_align_content_to_u8(LayoutAlignContent::SpaceAround),
3141 ALIGN_MASK,
3142 );
3143 assert_fits(
3144 "writing_mode",
3145 layout_writing_mode_to_u8(LayoutWritingMode::VerticalLr),
3146 WRITING_MODE_MASK,
3147 );
3148 assert_fits("clear", layout_clear_to_u8(LayoutClear::Both), CLEAR_MASK);
3149 assert_fits(
3150 "font_weight",
3151 style_font_weight_to_u8(StyleFontWeight::Bolder),
3152 FONT_WEIGHT_MASK,
3153 );
3154 assert_fits(
3155 "font_style",
3156 style_font_style_to_u8(StyleFontStyle::Oblique),
3157 FONT_STYLE_MASK,
3158 );
3159 assert_fits(
3160 "text_align",
3161 style_text_align_to_u8(StyleTextAlign::End),
3162 TEXT_ALIGN_MASK,
3163 );
3164 assert_fits(
3165 "visibility",
3166 style_visibility_to_u8(StyleVisibility::Collapse),
3167 VISIBILITY_MASK,
3168 );
3169 assert_fits(
3170 "white_space",
3171 style_white_space_to_u8(StyleWhiteSpace::BreakSpaces),
3172 WHITE_SPACE_MASK,
3173 );
3174 assert_fits(
3175 "direction",
3176 style_direction_to_u8(StyleDirection::Rtl),
3177 DIRECTION_MASK,
3178 );
3179 assert_fits(
3180 "vertical_align",
3181 style_vertical_align_to_u8(StyleVerticalAlign::TextBottom),
3182 VERTICAL_ALIGN_MASK,
3183 );
3184 assert_fits(
3185 "border_collapse",
3186 border_collapse_to_u8(StyleBorderCollapse::Collapse),
3187 BORDER_COLLAPSE_MASK,
3188 );
3189 }
3190
3191 #[test]
3200 fn test_empty_tier1_decodes_to_initial_values() {
3201 let t1 = TIER1_POPULATED_BIT; assert!(tier1_is_populated(t1));
3203 assert_eq!(decode_display(t1), LayoutDisplay::Block);
3204 assert_eq!(decode_position(t1), LayoutPosition::Static);
3205 assert_eq!(decode_float(t1), LayoutFloat::None);
3206 assert_eq!(decode_overflow_x(t1), LayoutOverflow::Visible);
3207 assert_eq!(decode_overflow_y(t1), LayoutOverflow::Visible);
3208 assert_eq!(decode_box_sizing(t1), LayoutBoxSizing::ContentBox);
3209 assert_eq!(decode_flex_direction(t1), LayoutFlexDirection::Row);
3210 assert_eq!(decode_flex_wrap(t1), LayoutFlexWrap::NoWrap);
3211 assert_eq!(decode_justify_content(t1), LayoutJustifyContent::FlexStart);
3212 assert_eq!(decode_align_items(t1), LayoutAlignItems::Stretch);
3213 assert_eq!(decode_align_content(t1), LayoutAlignContent::Stretch);
3214 assert_eq!(decode_writing_mode(t1), LayoutWritingMode::HorizontalTb);
3215 assert_eq!(decode_clear(t1), LayoutClear::None);
3216 assert_eq!(decode_font_weight(t1), StyleFontWeight::Normal);
3217 assert_eq!(decode_font_style(t1), StyleFontStyle::Normal);
3218 assert_eq!(decode_text_align(t1), StyleTextAlign::Start);
3219 assert_eq!(decode_visibility(t1), StyleVisibility::Visible);
3220 assert_eq!(decode_white_space(t1), StyleWhiteSpace::Normal);
3221 assert_eq!(decode_direction(t1), StyleDirection::Ltr);
3222 assert_eq!(decode_vertical_align(t1), StyleVerticalAlign::Baseline);
3223 assert_eq!(decode_border_collapse(t1), StyleBorderCollapse::Separate);
3224 }
3225}
3226
3227#[cfg(test)]
3228#[allow(
3229 clippy::float_cmp,
3230 clippy::unreadable_literal,
3231 clippy::too_many_lines,
3232 clippy::cast_precision_loss
3233)]
3234mod autotest_generated {
3235 use super::*;
3236 use crate::props::basic::length::PercentageValue;
3237
3238 const ALL_DISPLAY: [LayoutDisplay; 23] = [
3243 LayoutDisplay::Block,
3244 LayoutDisplay::Inline,
3245 LayoutDisplay::InlineBlock,
3246 LayoutDisplay::Flex,
3247 LayoutDisplay::None,
3248 LayoutDisplay::InlineFlex,
3249 LayoutDisplay::Table,
3250 LayoutDisplay::InlineTable,
3251 LayoutDisplay::TableRowGroup,
3252 LayoutDisplay::TableHeaderGroup,
3253 LayoutDisplay::TableFooterGroup,
3254 LayoutDisplay::TableRow,
3255 LayoutDisplay::TableColumnGroup,
3256 LayoutDisplay::TableColumn,
3257 LayoutDisplay::TableCell,
3258 LayoutDisplay::TableCaption,
3259 LayoutDisplay::FlowRoot,
3260 LayoutDisplay::ListItem,
3261 LayoutDisplay::RunIn,
3262 LayoutDisplay::Marker,
3263 LayoutDisplay::Grid,
3264 LayoutDisplay::InlineGrid,
3265 LayoutDisplay::Contents,
3266 ];
3267
3268 const ALL_BORDER_STYLE: [BorderStyle; 10] = [
3269 BorderStyle::None,
3270 BorderStyle::Solid,
3271 BorderStyle::Double,
3272 BorderStyle::Dotted,
3273 BorderStyle::Dashed,
3274 BorderStyle::Hidden,
3275 BorderStyle::Groove,
3276 BorderStyle::Ridge,
3277 BorderStyle::Inset,
3278 BorderStyle::Outset,
3279 ];
3280
3281 const ALL_SIZE_METRIC: [SizeMetric; 12] = [
3282 SizeMetric::Px,
3283 SizeMetric::Pt,
3284 SizeMetric::Em,
3285 SizeMetric::Rem,
3286 SizeMetric::In,
3287 SizeMetric::Cm,
3288 SizeMetric::Mm,
3289 SizeMetric::Percent,
3290 SizeMetric::Vw,
3291 SizeMetric::Vh,
3292 SizeMetric::Vmin,
3293 SizeMetric::Vmax,
3294 ];
3295
3296 fn pv_raw(metric: SizeMetric, raw: isize) -> PixelValue {
3299 PixelValue {
3300 metric,
3301 number: FloatValue { number: raw },
3302 }
3303 }
3304
3305 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
3308 struct T1 {
3309 display: LayoutDisplay,
3310 position: LayoutPosition,
3311 float: LayoutFloat,
3312 overflow_x: LayoutOverflow,
3313 overflow_y: LayoutOverflow,
3314 box_sizing: LayoutBoxSizing,
3315 flex_direction: LayoutFlexDirection,
3316 flex_wrap: LayoutFlexWrap,
3317 justify_content: LayoutJustifyContent,
3318 align_items: LayoutAlignItems,
3319 align_content: LayoutAlignContent,
3320 writing_mode: LayoutWritingMode,
3321 clear: LayoutClear,
3322 font_weight: StyleFontWeight,
3323 font_style: StyleFontStyle,
3324 text_align: StyleTextAlign,
3325 visibility: StyleVisibility,
3326 white_space: StyleWhiteSpace,
3327 direction: StyleDirection,
3328 vertical_align: StyleVerticalAlign,
3329 border_collapse: StyleBorderCollapse,
3330 }
3331
3332 impl T1 {
3333 const fn initial() -> Self {
3335 Self {
3336 display: LayoutDisplay::Block,
3337 position: LayoutPosition::Static,
3338 float: LayoutFloat::None,
3339 overflow_x: LayoutOverflow::Visible,
3340 overflow_y: LayoutOverflow::Visible,
3341 box_sizing: LayoutBoxSizing::ContentBox,
3342 flex_direction: LayoutFlexDirection::Row,
3343 flex_wrap: LayoutFlexWrap::NoWrap,
3344 justify_content: LayoutJustifyContent::FlexStart,
3345 align_items: LayoutAlignItems::Stretch,
3346 align_content: LayoutAlignContent::Stretch,
3347 writing_mode: LayoutWritingMode::HorizontalTb,
3348 clear: LayoutClear::None,
3349 font_weight: StyleFontWeight::Normal,
3350 font_style: StyleFontStyle::Normal,
3351 text_align: StyleTextAlign::Start,
3352 visibility: StyleVisibility::Visible,
3353 white_space: StyleWhiteSpace::Normal,
3354 direction: StyleDirection::Ltr,
3355 vertical_align: StyleVerticalAlign::Baseline,
3356 border_collapse: StyleBorderCollapse::Separate,
3357 }
3358 }
3359
3360 const fn saturated() -> Self {
3363 Self {
3364 display: LayoutDisplay::Contents,
3365 position: LayoutPosition::Sticky,
3366 float: LayoutFloat::Right,
3367 overflow_x: LayoutOverflow::Clip,
3368 overflow_y: LayoutOverflow::Clip,
3369 box_sizing: LayoutBoxSizing::BorderBox,
3370 flex_direction: LayoutFlexDirection::ColumnReverse,
3371 flex_wrap: LayoutFlexWrap::WrapReverse,
3372 justify_content: LayoutJustifyContent::SpaceEvenly,
3373 align_items: LayoutAlignItems::Baseline,
3374 align_content: LayoutAlignContent::SpaceAround,
3375 writing_mode: LayoutWritingMode::VerticalLr,
3376 clear: LayoutClear::Both,
3377 font_weight: StyleFontWeight::Bolder,
3378 font_style: StyleFontStyle::Oblique,
3379 text_align: StyleTextAlign::End,
3380 visibility: StyleVisibility::Collapse,
3381 white_space: StyleWhiteSpace::BreakSpaces,
3382 direction: StyleDirection::Rtl,
3383 vertical_align: StyleVerticalAlign::TextBottom,
3384 border_collapse: StyleBorderCollapse::Collapse,
3385 }
3386 }
3387
3388 fn encode(self) -> u64 {
3389 encode_tier1(
3390 self.display,
3391 self.position,
3392 self.float,
3393 self.overflow_x,
3394 self.overflow_y,
3395 self.box_sizing,
3396 self.flex_direction,
3397 self.flex_wrap,
3398 self.justify_content,
3399 self.align_items,
3400 self.align_content,
3401 self.writing_mode,
3402 self.clear,
3403 self.font_weight,
3404 self.font_style,
3405 self.text_align,
3406 self.visibility,
3407 self.white_space,
3408 self.direction,
3409 self.vertical_align,
3410 self.border_collapse,
3411 )
3412 }
3413
3414 fn decode(t1: u64) -> Self {
3415 Self {
3416 display: decode_display(t1),
3417 position: decode_position(t1),
3418 float: decode_float(t1),
3419 overflow_x: decode_overflow_x(t1),
3420 overflow_y: decode_overflow_y(t1),
3421 box_sizing: decode_box_sizing(t1),
3422 flex_direction: decode_flex_direction(t1),
3423 flex_wrap: decode_flex_wrap(t1),
3424 justify_content: decode_justify_content(t1),
3425 align_items: decode_align_items(t1),
3426 align_content: decode_align_content(t1),
3427 writing_mode: decode_writing_mode(t1),
3428 clear: decode_clear(t1),
3429 font_weight: decode_font_weight(t1),
3430 font_style: decode_font_style(t1),
3431 text_align: decode_text_align(t1),
3432 visibility: decode_visibility(t1),
3433 white_space: decode_white_space(t1),
3434 direction: decode_direction(t1),
3435 vertical_align: decode_vertical_align(t1),
3436 border_collapse: decode_border_collapse(t1),
3437 }
3438 }
3439 }
3440
3441 fn assert_u8_fallback<T: PartialEq + core::fmt::Debug + Copy>(
3449 name: &str,
3450 decode: fn(u8) -> T,
3451 first_invalid: u8,
3452 initial: T,
3453 ) {
3454 for v in first_invalid..=u8::MAX {
3455 assert_eq!(
3456 decode(v),
3457 initial,
3458 "{name}: out-of-range byte {v} must fall back to the CSS initial value",
3459 );
3460 }
3461 }
3462
3463 #[test]
3464 fn out_of_range_u8_falls_back_to_css_initial_for_every_enum() {
3465 assert_u8_fallback("display", layout_display_from_u8, 23, LayoutDisplay::Block);
3466 assert_u8_fallback(
3467 "position",
3468 layout_position_from_u8,
3469 5,
3470 LayoutPosition::Static,
3471 );
3472 assert_u8_fallback("float", layout_float_from_u8, 3, LayoutFloat::None);
3473 assert_u8_fallback(
3474 "overflow",
3475 layout_overflow_from_u8,
3476 5,
3477 LayoutOverflow::Visible,
3478 );
3479 assert_u8_fallback(
3480 "box_sizing",
3481 layout_box_sizing_from_u8,
3482 2,
3483 LayoutBoxSizing::ContentBox,
3484 );
3485 assert_u8_fallback(
3486 "flex_direction",
3487 layout_flex_direction_from_u8,
3488 4,
3489 LayoutFlexDirection::Row,
3490 );
3491 assert_u8_fallback(
3492 "flex_wrap",
3493 layout_flex_wrap_from_u8,
3494 3,
3495 LayoutFlexWrap::NoWrap,
3496 );
3497 assert_u8_fallback(
3498 "justify_content",
3499 layout_justify_content_from_u8,
3500 8,
3501 LayoutJustifyContent::FlexStart,
3502 );
3503 assert_u8_fallback(
3504 "align_items",
3505 layout_align_items_from_u8,
3506 5,
3507 LayoutAlignItems::Stretch,
3508 );
3509 assert_u8_fallback(
3510 "align_self",
3511 layout_align_self_from_u8,
3512 6,
3513 LayoutAlignSelf::Auto,
3514 );
3515 assert_u8_fallback(
3516 "justify_self",
3517 layout_justify_self_from_u8,
3518 5,
3519 LayoutJustifySelf::Auto,
3520 );
3521 assert_u8_fallback(
3522 "justify_items",
3523 layout_justify_items_from_u8,
3524 4,
3525 LayoutJustifyItems::Stretch,
3526 );
3527 assert_u8_fallback(
3528 "grid_auto_flow",
3529 layout_grid_auto_flow_from_u8,
3530 4,
3531 LayoutGridAutoFlow::Row,
3532 );
3533 assert_u8_fallback(
3534 "align_content",
3535 layout_align_content_from_u8,
3536 6,
3537 LayoutAlignContent::Stretch,
3538 );
3539 assert_u8_fallback(
3540 "writing_mode",
3541 layout_writing_mode_from_u8,
3542 3,
3543 LayoutWritingMode::HorizontalTb,
3544 );
3545 assert_u8_fallback("clear", layout_clear_from_u8, 4, LayoutClear::None);
3546 assert_u8_fallback(
3547 "font_weight",
3548 style_font_weight_from_u8,
3549 11,
3550 StyleFontWeight::Normal,
3551 );
3552 assert_u8_fallback(
3553 "font_style",
3554 style_font_style_from_u8,
3555 3,
3556 StyleFontStyle::Normal,
3557 );
3558 assert_u8_fallback(
3559 "text_align",
3560 style_text_align_from_u8,
3561 6,
3562 StyleTextAlign::Start,
3563 );
3564 assert_u8_fallback(
3565 "visibility",
3566 style_visibility_from_u8,
3567 3,
3568 StyleVisibility::Visible,
3569 );
3570 assert_u8_fallback(
3571 "white_space",
3572 style_white_space_from_u8,
3573 6,
3574 StyleWhiteSpace::Normal,
3575 );
3576 assert_u8_fallback("direction", style_direction_from_u8, 2, StyleDirection::Ltr);
3577 assert_u8_fallback(
3578 "vertical_align",
3579 style_vertical_align_from_u8,
3580 8,
3581 StyleVerticalAlign::Baseline,
3582 );
3583 assert_u8_fallback(
3584 "border_collapse",
3585 border_collapse_from_u8,
3586 2,
3587 StyleBorderCollapse::Separate,
3588 );
3589 assert_u8_fallback("border_style", border_style_from_u8, 10, BorderStyle::None);
3590 assert_u8_fallback("size_metric", size_metric_from_u8, 12, SizeMetric::Px);
3591 }
3592
3593 #[test]
3594 fn display_documented_sentinel_31_decodes_to_block() {
3595 assert_eq!(layout_display_from_u8(31), LayoutDisplay::Block);
3600 assert_eq!(layout_display_from_u8(u8::MAX), LayoutDisplay::Block);
3601 }
3602
3603 #[test]
3604 fn every_variant_of_every_enum_fits_its_tier1_mask() {
3605 fn fits(name: &str, val: u8, mask: u64) {
3610 assert!(
3611 u64::from(val) & !mask == 0,
3612 "{name}: encoded u8 {val} does not fit mask {mask:#b}",
3613 );
3614 }
3615
3616 for v in ALL_DISPLAY {
3617 fits("display", layout_display_to_u8(v), DISPLAY_MASK);
3618 }
3619 for v in [
3620 LayoutPosition::Static,
3621 LayoutPosition::Relative,
3622 LayoutPosition::Absolute,
3623 LayoutPosition::Fixed,
3624 LayoutPosition::Sticky,
3625 ] {
3626 fits("position", layout_position_to_u8(v), POSITION_MASK);
3627 }
3628 for v in [LayoutFloat::None, LayoutFloat::Left, LayoutFloat::Right] {
3629 fits("float", layout_float_to_u8(v), FLOAT_MASK);
3630 }
3631 for v in [
3632 LayoutOverflow::Visible,
3633 LayoutOverflow::Hidden,
3634 LayoutOverflow::Scroll,
3635 LayoutOverflow::Auto,
3636 LayoutOverflow::Clip,
3637 ] {
3638 fits("overflow", layout_overflow_to_u8(v), OVERFLOW_MASK);
3639 }
3640 for v in [LayoutBoxSizing::ContentBox, LayoutBoxSizing::BorderBox] {
3641 fits("box_sizing", layout_box_sizing_to_u8(v), BOX_SIZING_MASK);
3642 }
3643 for v in [
3644 LayoutFlexDirection::Row,
3645 LayoutFlexDirection::RowReverse,
3646 LayoutFlexDirection::Column,
3647 LayoutFlexDirection::ColumnReverse,
3648 ] {
3649 fits(
3650 "flex_direction",
3651 layout_flex_direction_to_u8(v),
3652 FLEX_DIR_MASK,
3653 );
3654 }
3655 for v in [
3656 LayoutFlexWrap::NoWrap,
3657 LayoutFlexWrap::Wrap,
3658 LayoutFlexWrap::WrapReverse,
3659 ] {
3660 fits("flex_wrap", layout_flex_wrap_to_u8(v), FLEX_WRAP_MASK);
3661 }
3662 for v in [
3663 LayoutJustifyContent::FlexStart,
3664 LayoutJustifyContent::FlexEnd,
3665 LayoutJustifyContent::Start,
3666 LayoutJustifyContent::End,
3667 LayoutJustifyContent::Center,
3668 LayoutJustifyContent::SpaceBetween,
3669 LayoutJustifyContent::SpaceAround,
3670 LayoutJustifyContent::SpaceEvenly,
3671 ] {
3672 fits(
3673 "justify_content",
3674 layout_justify_content_to_u8(v),
3675 JUSTIFY_MASK,
3676 );
3677 }
3678 for v in [
3679 LayoutAlignItems::Stretch,
3680 LayoutAlignItems::Center,
3681 LayoutAlignItems::Start,
3682 LayoutAlignItems::End,
3683 LayoutAlignItems::Baseline,
3684 ] {
3685 fits("align_items", layout_align_items_to_u8(v), ALIGN_MASK);
3686 }
3687 for v in [
3688 LayoutAlignSelf::Auto,
3689 LayoutAlignSelf::Stretch,
3690 LayoutAlignSelf::Center,
3691 LayoutAlignSelf::Start,
3692 LayoutAlignSelf::End,
3693 LayoutAlignSelf::Baseline,
3694 ] {
3695 fits("align_self", layout_align_self_to_u8(v), ALIGN_SELF_MASK);
3696 }
3697 for v in [
3698 LayoutJustifySelf::Auto,
3699 LayoutJustifySelf::Start,
3700 LayoutJustifySelf::End,
3701 LayoutJustifySelf::Center,
3702 LayoutJustifySelf::Stretch,
3703 ] {
3704 fits(
3705 "justify_self",
3706 layout_justify_self_to_u8(v),
3707 JUSTIFY_SELF_MASK,
3708 );
3709 }
3710 for v in [
3711 LayoutJustifyItems::Stretch,
3712 LayoutJustifyItems::Start,
3713 LayoutJustifyItems::End,
3714 LayoutJustifyItems::Center,
3715 ] {
3716 fits(
3717 "justify_items",
3718 layout_justify_items_to_u8(v),
3719 JUSTIFY_ITEMS_MASK,
3720 );
3721 }
3722 for v in [
3723 LayoutGridAutoFlow::Row,
3724 LayoutGridAutoFlow::Column,
3725 LayoutGridAutoFlow::RowDense,
3726 LayoutGridAutoFlow::ColumnDense,
3727 ] {
3728 fits(
3729 "grid_auto_flow",
3730 layout_grid_auto_flow_to_u8(v),
3731 GRID_AUTO_FLOW_MASK,
3732 );
3733 }
3734 for v in [
3735 LayoutAlignContent::Stretch,
3736 LayoutAlignContent::Center,
3737 LayoutAlignContent::Start,
3738 LayoutAlignContent::End,
3739 LayoutAlignContent::SpaceBetween,
3740 LayoutAlignContent::SpaceAround,
3741 ] {
3742 fits("align_content", layout_align_content_to_u8(v), ALIGN_MASK);
3743 }
3744 for v in [
3745 LayoutWritingMode::HorizontalTb,
3746 LayoutWritingMode::VerticalRl,
3747 LayoutWritingMode::VerticalLr,
3748 ] {
3749 fits(
3750 "writing_mode",
3751 layout_writing_mode_to_u8(v),
3752 WRITING_MODE_MASK,
3753 );
3754 }
3755 for v in [
3756 LayoutClear::None,
3757 LayoutClear::Left,
3758 LayoutClear::Right,
3759 LayoutClear::Both,
3760 ] {
3761 fits("clear", layout_clear_to_u8(v), CLEAR_MASK);
3762 }
3763 for v in [
3764 StyleFontWeight::Normal,
3765 StyleFontWeight::W100,
3766 StyleFontWeight::W200,
3767 StyleFontWeight::W300,
3768 StyleFontWeight::W500,
3769 StyleFontWeight::W600,
3770 StyleFontWeight::Bold,
3771 StyleFontWeight::W800,
3772 StyleFontWeight::W900,
3773 StyleFontWeight::Lighter,
3774 StyleFontWeight::Bolder,
3775 ] {
3776 fits("font_weight", style_font_weight_to_u8(v), FONT_WEIGHT_MASK);
3777 }
3778 for v in [
3779 StyleFontStyle::Normal,
3780 StyleFontStyle::Italic,
3781 StyleFontStyle::Oblique,
3782 ] {
3783 fits("font_style", style_font_style_to_u8(v), FONT_STYLE_MASK);
3784 }
3785 for v in [
3786 StyleTextAlign::Left,
3787 StyleTextAlign::Center,
3788 StyleTextAlign::Right,
3789 StyleTextAlign::Justify,
3790 StyleTextAlign::Start,
3791 StyleTextAlign::End,
3792 ] {
3793 fits("text_align", style_text_align_to_u8(v), TEXT_ALIGN_MASK);
3794 }
3795 for v in [
3796 StyleVisibility::Visible,
3797 StyleVisibility::Hidden,
3798 StyleVisibility::Collapse,
3799 ] {
3800 fits("visibility", style_visibility_to_u8(v), VISIBILITY_MASK);
3801 }
3802 for v in [
3803 StyleWhiteSpace::Normal,
3804 StyleWhiteSpace::Pre,
3805 StyleWhiteSpace::Nowrap,
3806 StyleWhiteSpace::PreWrap,
3807 StyleWhiteSpace::PreLine,
3808 StyleWhiteSpace::BreakSpaces,
3809 ] {
3810 fits("white_space", style_white_space_to_u8(v), WHITE_SPACE_MASK);
3811 }
3812 for v in [StyleDirection::Ltr, StyleDirection::Rtl] {
3813 fits("direction", style_direction_to_u8(v), DIRECTION_MASK);
3814 }
3815 for v in [
3816 StyleVerticalAlign::Baseline,
3817 StyleVerticalAlign::Top,
3818 StyleVerticalAlign::Middle,
3819 StyleVerticalAlign::Bottom,
3820 StyleVerticalAlign::Sub,
3821 StyleVerticalAlign::Superscript,
3822 StyleVerticalAlign::TextTop,
3823 StyleVerticalAlign::TextBottom,
3824 StyleVerticalAlign::Percentage(PercentageValue::new(50.0)),
3825 StyleVerticalAlign::Length(PixelValue::px(4.0)),
3826 ] {
3827 fits(
3828 "vertical_align",
3829 style_vertical_align_to_u8(v),
3830 VERTICAL_ALIGN_MASK,
3831 );
3832 }
3833 for v in [StyleBorderCollapse::Separate, StyleBorderCollapse::Collapse] {
3834 fits(
3835 "border_collapse",
3836 border_collapse_to_u8(v),
3837 BORDER_COLLAPSE_MASK,
3838 );
3839 }
3840 for v in ALL_BORDER_STYLE {
3842 fits("border_style", border_style_to_u8(v), 0x0F);
3843 }
3844 for v in ALL_SIZE_METRIC {
3846 fits("size_metric", size_metric_to_u8(v), 0x0F);
3847 }
3848 }
3849
3850 #[test]
3851 fn vertical_align_percentage_and_length_collapse_to_baseline() {
3852 for v in [
3857 StyleVerticalAlign::Percentage(PercentageValue::new(0.0)),
3858 StyleVerticalAlign::Percentage(PercentageValue::new(-100.0)),
3859 StyleVerticalAlign::Percentage(PercentageValue::new(1e9)),
3860 StyleVerticalAlign::Length(PixelValue::px(0.0)),
3861 StyleVerticalAlign::Length(PixelValue::px(-1e9)),
3862 ] {
3863 assert_eq!(style_vertical_align_to_u8(v), 0);
3864 assert_eq!(
3865 style_vertical_align_from_u8(style_vertical_align_to_u8(v)),
3866 StyleVerticalAlign::Baseline,
3867 );
3868 }
3869
3870 let mut t = T1::initial();
3872 t.vertical_align = StyleVerticalAlign::Percentage(PercentageValue::new(150.0));
3873 t.border_collapse = StyleBorderCollapse::Collapse;
3874 let encoded = t.encode();
3875 assert_eq!(decode_vertical_align(encoded), StyleVerticalAlign::Baseline);
3876 assert_eq!(
3877 decode_border_collapse(encoded),
3878 StyleBorderCollapse::Collapse
3879 );
3880 }
3881
3882 #[test]
3887 fn tier1_each_field_at_max_does_not_leak_into_any_other_field() {
3888 let sat = T1::saturated();
3892 let mut cases: Vec<(&str, T1)> = Vec::new();
3893
3894 macro_rules! case {
3895 ($field:ident) => {{
3896 let mut t = T1::initial();
3897 t.$field = sat.$field;
3898 cases.push((stringify!($field), t));
3899 }};
3900 }
3901
3902 case!(display);
3903 case!(position);
3904 case!(float);
3905 case!(overflow_x);
3906 case!(overflow_y);
3907 case!(box_sizing);
3908 case!(flex_direction);
3909 case!(flex_wrap);
3910 case!(justify_content);
3911 case!(align_items);
3912 case!(align_content);
3913 case!(writing_mode);
3914 case!(clear);
3915 case!(font_weight);
3916 case!(font_style);
3917 case!(text_align);
3918 case!(visibility);
3919 case!(white_space);
3920 case!(direction);
3921 case!(vertical_align);
3922 case!(border_collapse);
3923
3924 assert_eq!(cases.len(), 21, "every tier1 field must be covered");
3925
3926 for (name, expected) in cases {
3927 let decoded = T1::decode(expected.encode());
3928 assert_eq!(
3929 decoded, expected,
3930 "{name} at its max variant leaked into another tier1 field",
3931 );
3932 }
3933 }
3934
3935 #[test]
3936 fn tier1_all_fields_saturated_roundtrips() {
3937 let sat = T1::saturated();
3938 assert_eq!(T1::decode(sat.encode()), sat);
3939 assert!(tier1_is_populated(sat.encode()));
3940 }
3941
3942 #[test]
3943 fn tier1_encode_never_touches_the_grid_bits_or_bits_above_63() {
3944 const GRID_WINDOW: u64 = 0x3FF << 53; for t in [T1::initial(), T1::saturated()] {
3952 let encoded = t.encode();
3953 assert_eq!(
3954 encoded & GRID_WINDOW,
3955 0,
3956 "encode_tier1 wrote into the grid bit window [62:53]",
3957 );
3958 assert_eq!(encoded & TIER1_POPULATED_BIT, TIER1_POPULATED_BIT);
3959 }
3960
3961 let base = T1::saturated().encode();
3963 let with_grid = base
3964 | ((u64::from(layout_align_self_to_u8(LayoutAlignSelf::Baseline))) << ALIGN_SELF_SHIFT)
3965 | ((u64::from(layout_justify_self_to_u8(LayoutJustifySelf::Stretch)))
3966 << JUSTIFY_SELF_SHIFT)
3967 | ((u64::from(layout_grid_auto_flow_to_u8(LayoutGridAutoFlow::ColumnDense)))
3968 << GRID_AUTO_FLOW_SHIFT)
3969 | ((u64::from(layout_justify_items_to_u8(LayoutJustifyItems::Center)))
3970 << JUSTIFY_ITEMS_SHIFT);
3971
3972 assert_eq!(T1::decode(with_grid), T1::saturated());
3973 assert_eq!(
3974 layout_align_self_from_u8(((with_grid >> ALIGN_SELF_SHIFT) & ALIGN_SELF_MASK) as u8),
3975 LayoutAlignSelf::Baseline,
3976 );
3977 assert_eq!(
3978 layout_justify_self_from_u8(
3979 ((with_grid >> JUSTIFY_SELF_SHIFT) & JUSTIFY_SELF_MASK) as u8
3980 ),
3981 LayoutJustifySelf::Stretch,
3982 );
3983 assert_eq!(
3984 layout_grid_auto_flow_from_u8(
3985 ((with_grid >> GRID_AUTO_FLOW_SHIFT) & GRID_AUTO_FLOW_MASK) as u8
3986 ),
3987 LayoutGridAutoFlow::ColumnDense,
3988 );
3989 assert_eq!(
3990 layout_justify_items_from_u8(
3991 ((with_grid >> JUSTIFY_ITEMS_SHIFT) & JUSTIFY_ITEMS_MASK) as u8
3992 ),
3993 LayoutJustifyItems::Center,
3994 );
3995 }
3996
3997 #[test]
3998 fn tier1_zero_is_unpopulated_but_still_decodes_to_css_initial() {
3999 assert!(!tier1_is_populated(0));
4003 assert_eq!(T1::decode(0), T1::initial());
4004 }
4005
4006 #[test]
4007 fn tier1_decodes_arbitrary_u64_deterministically_without_panic() {
4008 let m = u64::MAX;
4012 assert!(tier1_is_populated(m));
4013 assert_eq!(
4014 T1::decode(m),
4015 T1 {
4016 display: LayoutDisplay::Block, position: LayoutPosition::Static, float: LayoutFloat::None, overflow_x: LayoutOverflow::Visible, overflow_y: LayoutOverflow::Visible, box_sizing: LayoutBoxSizing::BorderBox, flex_direction: LayoutFlexDirection::ColumnReverse, flex_wrap: LayoutFlexWrap::NoWrap, justify_content: LayoutJustifyContent::SpaceEvenly, align_items: LayoutAlignItems::Stretch, align_content: LayoutAlignContent::Stretch, writing_mode: LayoutWritingMode::HorizontalTb, clear: LayoutClear::Both, font_weight: StyleFontWeight::Normal, font_style: StyleFontStyle::Normal, text_align: StyleTextAlign::Start, visibility: StyleVisibility::Visible, white_space: StyleWhiteSpace::Normal, direction: StyleDirection::Rtl, vertical_align: StyleVerticalAlign::TextBottom, border_collapse: StyleBorderCollapse::Collapse, },
4038 );
4039
4040 let mut x: u64 = 0x9E37_79B9_7F4A_7C15;
4042 for _ in 0..4096 {
4043 let _ = T1::decode(x);
4044 let _ = tier1_is_populated(x);
4045 x = x
4046 .wrapping_mul(6364136223846793005)
4047 .wrapping_add(1442695040888963407);
4048 }
4049 for x in [
4050 0u64,
4051 1,
4052 u64::MAX,
4053 0xAAAA_AAAA_AAAA_AAAA,
4054 0x5555_5555_5555_5555,
4055 ] {
4056 let _ = T1::decode(x);
4057 }
4058 }
4059
4060 #[test]
4065 fn border_styles_packed_roundtrip_for_all_10000_combinations() {
4066 for top in ALL_BORDER_STYLE {
4067 for right in ALL_BORDER_STYLE {
4068 for bottom in ALL_BORDER_STYLE {
4069 for left in ALL_BORDER_STYLE {
4070 let packed = encode_border_styles_packed(top, right, bottom, left);
4071 assert_eq!(decode_border_top_style(packed), top);
4072 assert_eq!(decode_border_right_style(packed), right);
4073 assert_eq!(decode_border_bottom_style(packed), bottom);
4074 assert_eq!(decode_border_left_style(packed), left);
4075 }
4076 }
4077 }
4078 }
4079 }
4080
4081 #[test]
4082 fn border_styles_packed_nibbles_do_not_alias() {
4083 let packed = encode_border_styles_packed(
4086 BorderStyle::Outset, BorderStyle::None,
4088 BorderStyle::None,
4089 BorderStyle::None,
4090 );
4091 assert_eq!(packed, 0x0009);
4092 assert_eq!(decode_border_top_style(packed), BorderStyle::Outset);
4093 assert_eq!(decode_border_right_style(packed), BorderStyle::None);
4094 assert_eq!(decode_border_bottom_style(packed), BorderStyle::None);
4095 assert_eq!(decode_border_left_style(packed), BorderStyle::None);
4096
4097 let packed = encode_border_styles_packed(
4098 BorderStyle::None,
4099 BorderStyle::None,
4100 BorderStyle::None,
4101 BorderStyle::Outset,
4102 );
4103 assert_eq!(packed, 0x9000);
4104 assert_eq!(decode_border_left_style(packed), BorderStyle::Outset);
4105 assert_eq!(decode_border_top_style(packed), BorderStyle::None);
4106 }
4107
4108 #[test]
4109 fn border_styles_packed_decodes_garbage_u16_without_panic() {
4110 for packed in [0u16, 0xFFFF, 0xAAAA, 0x5555, u16::MAX / 2] {
4113 let _ = decode_border_top_style(packed);
4114 let _ = decode_border_right_style(packed);
4115 let _ = decode_border_bottom_style(packed);
4116 let _ = decode_border_left_style(packed);
4117 }
4118 assert_eq!(decode_border_top_style(0xFFFF), BorderStyle::None);
4119 assert_eq!(decode_border_right_style(0xFFFF), BorderStyle::None);
4120 assert_eq!(decode_border_bottom_style(0xFFFF), BorderStyle::None);
4121 assert_eq!(decode_border_left_style(0xFFFF), BorderStyle::None);
4122 for packed in 0..=u16::MAX {
4124 let _ = decode_border_top_style(packed);
4125 let _ = decode_border_left_style(packed);
4126 }
4127 }
4128
4129 #[test]
4134 fn color_u32_channel_order_is_rrggbbaa() {
4135 let c = ColorU {
4136 r: 0x12,
4137 g: 0x34,
4138 b: 0x56,
4139 a: 0x78,
4140 };
4141 assert_eq!(encode_color_u32(&c), 0x1234_5678);
4142 assert_eq!(decode_color_u32(0x1234_5678), Some(c));
4143 }
4144
4145 #[test]
4146 fn color_u32_roundtrips_every_boundary_channel_combination() {
4147 for r in [0u8, 1, 127, 254, 255] {
4148 for g in [0u8, 1, 127, 254, 255] {
4149 for b in [0u8, 1, 127, 254, 255] {
4150 for a in [0u8, 1, 127, 254, 255] {
4151 let c = ColorU { r, g, b, a };
4152 let encoded = encode_color_u32(&c);
4153 if encoded == 0 {
4154 assert_eq!((r, g, b, a), (0, 0, 0, 0));
4156 assert_eq!(decode_color_u32(encoded), None);
4157 } else {
4158 assert_eq!(decode_color_u32(encoded), Some(c));
4159 }
4160 }
4161 }
4162 }
4163 }
4164 }
4165
4166 #[test]
4167 fn color_u32_transparent_black_is_the_documented_unset_collision() {
4168 let transparent_black = ColorU {
4171 r: 0,
4172 g: 0,
4173 b: 0,
4174 a: 0,
4175 };
4176 assert_eq!(encode_color_u32(&transparent_black), 0);
4177 assert_eq!(decode_color_u32(0), None);
4178
4179 let transparent_red = ColorU {
4181 r: 255,
4182 g: 0,
4183 b: 0,
4184 a: 0,
4185 };
4186 assert_eq!(encode_color_u32(&transparent_red), 0xFF00_0000);
4187 assert_eq!(decode_color_u32(0xFF00_0000), Some(transparent_red));
4188
4189 let almost = ColorU {
4191 r: 0,
4192 g: 0,
4193 b: 0,
4194 a: 1,
4195 };
4196 assert_eq!(decode_color_u32(encode_color_u32(&almost)), Some(almost));
4197 }
4198
4199 #[test]
4200 fn color_u32_max_decodes_to_opaque_white() {
4201 assert_eq!(
4202 decode_color_u32(u32::MAX),
4203 Some(ColorU {
4204 r: 255,
4205 g: 255,
4206 b: 255,
4207 a: 255
4208 }),
4209 );
4210 }
4211
4212 #[test]
4217 fn pixel_value_u32_sentinels_all_decode_to_none_and_are_distinct() {
4218 let sentinels = [
4219 U32_SENTINEL,
4220 U32_AUTO,
4221 U32_NONE,
4222 U32_INHERIT,
4223 U32_INITIAL,
4224 U32_MIN_CONTENT,
4225 U32_MAX_CONTENT,
4226 ];
4227 for (i, a) in sentinels.iter().enumerate() {
4228 assert!(
4229 *a >= U32_SENTINEL_THRESHOLD,
4230 "sentinel {a:#X} sits below the threshold and would decode as a value",
4231 );
4232 assert_eq!(decode_pixel_value_u32(*a), None);
4233 for b in &sentinels[i + 1..] {
4234 assert_ne!(a, b, "two u32 sentinels share a bit pattern");
4235 }
4236 }
4237 assert_eq!(decode_pixel_value_u32(U32_SENTINEL_THRESHOLD), None);
4239 assert!(decode_pixel_value_u32(U32_SENTINEL_THRESHOLD - 1).is_some());
4241 }
4242
4243 #[test]
4244 fn pixel_value_u32_roundtrips_at_the_28_bit_boundaries_for_every_metric() {
4245 for metric in ALL_SIZE_METRIC {
4247 for raw in [0isize, 1, -1, 1000, -1000, 134_217_727, -134_217_728] {
4248 let pv = pv_raw(metric, raw);
4249 let encoded = encode_pixel_value_u32(&pv);
4250
4251 if encoded >= U32_SENTINEL_THRESHOLD {
4254 continue;
4255 }
4256
4257 let decoded = decode_pixel_value_u32(encoded)
4258 .unwrap_or_else(|| panic!("{metric:?} raw {raw} decoded as a sentinel"));
4259 assert_eq!(decoded.metric, metric, "metric nibble lost for raw {raw}");
4260 assert_eq!(
4261 decoded.number.number(),
4262 raw,
4263 "{metric:?}: raw {raw} did not survive the round-trip",
4264 );
4265 }
4266 }
4267 }
4268
4269 #[test]
4270 fn pixel_value_u32_out_of_28_bit_range_returns_sentinel() {
4271 for metric in ALL_SIZE_METRIC {
4272 for raw in [
4273 134_217_728isize,
4274 -134_217_729,
4275 1_000_000_000,
4276 -1_000_000_000,
4277 isize::MAX,
4278 isize::MIN,
4279 ] {
4280 assert_eq!(
4281 encode_pixel_value_u32(&pv_raw(metric, raw)),
4282 U32_SENTINEL,
4283 "{metric:?}: raw {raw} is outside 28 bits and must escape to tier 3",
4284 );
4285 }
4286 }
4287 }
4288
4289 #[test]
4290 fn pixel_value_u32_decodes_every_low_bit_pattern_without_panic() {
4291 for nibble in 12u32..16 {
4293 let encoded = (1u32 << 4) | nibble;
4294 let decoded = decode_pixel_value_u32(encoded).unwrap();
4295 assert_eq!(decoded.metric, SizeMetric::Px);
4296 assert_eq!(decoded.number.number(), 1);
4297 }
4298 let neg = decode_pixel_value_u32(0x8000_0000).unwrap();
4300 assert_eq!(neg.metric, SizeMetric::Px);
4301 assert_eq!(neg.number.number(), -134_217_728);
4302 }
4303
4304 #[test]
4305 fn pixel_value_u32_negative_0_001_in_vh_vmin_vmax_collides_with_sentinels() {
4306 for metric in [SizeMetric::Vh, SizeMetric::Vmin, SizeMetric::Vmax] {
4329 let pv = pv_raw(metric, -1);
4330 let encoded = encode_pixel_value_u32(&pv);
4331 assert_eq!(
4332 encoded, U32_SENTINEL,
4333 "{metric:?}: raw -1 must escape to tier 3"
4334 );
4335 assert_eq!(
4336 decode_pixel_value_u32(encoded),
4337 None,
4338 "{metric:?}: an escaped value decodes as None, never an aliased sentinel",
4339 );
4340 }
4341 }
4342
4343 #[test]
4348 fn resolved_px_i16_nan_and_infinity_are_defined_and_do_not_panic() {
4349 assert_eq!(encode_resolved_px_i16(f32::NAN), 0);
4351 assert_eq!(encode_resolved_px_i16(-f32::NAN), 0);
4352 assert_eq!(encode_resolved_px_i16(f32::INFINITY), I16_SENTINEL);
4353 assert_eq!(encode_resolved_px_i16(f32::NEG_INFINITY), I16_SENTINEL);
4354 assert_eq!(encode_resolved_px_i16(f32::MAX), I16_SENTINEL);
4355 assert_eq!(encode_resolved_px_i16(f32::MIN), I16_SENTINEL);
4356 assert_eq!(encode_resolved_px_i16(f32::MIN_POSITIVE), 0);
4358 assert_eq!(encode_resolved_px_i16(-0.0), 0);
4359 }
4360
4361 #[test]
4362 fn resolved_px_i16_saturates_exactly_at_the_documented_range() {
4363 assert_eq!(encode_resolved_px_i16(3276.3), 32763);
4365 assert_eq!(encode_resolved_px_i16(-3276.8), -32768);
4366
4367 assert_eq!(encode_resolved_px_i16(3276.4), I16_SENTINEL);
4369 assert_eq!(encode_resolved_px_i16(-3276.9), I16_SENTINEL);
4370 assert_eq!(encode_resolved_px_i16(1e9), I16_SENTINEL);
4371 assert_eq!(encode_resolved_px_i16(-1e9), I16_SENTINEL);
4372 }
4373
4374 #[test]
4375 fn resolved_px_i16_sentinels_decode_to_none_and_are_distinct() {
4376 let sentinels = [I16_SENTINEL, I16_AUTO, I16_INHERIT, I16_INITIAL];
4377 for (i, a) in sentinels.iter().enumerate() {
4378 assert!(*a >= I16_SENTINEL_THRESHOLD);
4379 assert_eq!(decode_resolved_px_i16(*a), None);
4380 for b in &sentinels[i + 1..] {
4381 assert_ne!(a, b, "two i16 sentinels share a bit pattern");
4382 }
4383 }
4384 assert_eq!(decode_resolved_px_i16(I16_SENTINEL_THRESHOLD), None);
4385 assert_eq!(
4386 decode_resolved_px_i16(I16_SENTINEL_THRESHOLD - 1),
4387 Some(3276.3)
4388 );
4389 }
4390
4391 #[test]
4392 fn resolved_px_i16_every_non_sentinel_value_roundtrips() {
4393 for v in i16::MIN..I16_SENTINEL_THRESHOLD {
4397 let px = decode_resolved_px_i16(v)
4398 .unwrap_or_else(|| panic!("{v} is below the threshold but decoded as a sentinel"));
4399 assert_eq!(
4400 encode_resolved_px_i16(px),
4401 v,
4402 "i16 {v} decoded to {px} px which re-encodes to a different i16",
4403 );
4404 }
4405 }
4406
4407 #[test]
4412 fn flex_u16_nan_infinity_and_negatives_are_defined_and_do_not_panic() {
4413 assert_eq!(encode_flex_u16(f32::NAN), 0);
4414 assert_eq!(encode_flex_u16(f32::INFINITY), U16_SENTINEL);
4415 assert_eq!(encode_flex_u16(f32::NEG_INFINITY), U16_SENTINEL);
4416 assert_eq!(encode_flex_u16(f32::MAX), U16_SENTINEL);
4417 assert_eq!(encode_flex_u16(-1.0), U16_SENTINEL);
4419 assert_eq!(encode_flex_u16(-0.01), U16_SENTINEL);
4420 assert_eq!(encode_flex_u16(-0.0), 0);
4422 assert_eq!(encode_flex_u16(0.0), 0);
4423 }
4424
4425 #[test]
4426 fn flex_u16_saturates_exactly_at_the_documented_range() {
4427 assert_eq!(encode_flex_u16(655.27), 65527);
4429 assert_eq!(decode_flex_u16(65527), Some(655.27));
4430 assert_eq!(encode_flex_u16(655.28), 65528);
4432 assert_eq!(encode_flex_u16(655.29), U16_SENTINEL);
4433 assert_eq!(encode_flex_u16(1e9), U16_SENTINEL);
4434 }
4435
4436 #[test]
4437 fn flex_u16_sentinel_band_decodes_to_none() {
4438 for v in U16_SENTINEL_THRESHOLD..=u16::MAX {
4439 assert_eq!(
4440 decode_flex_u16(v),
4441 None,
4442 "u16 {v} is reserved and must decode as None"
4443 );
4444 }
4445 assert_eq!(U16_SENTINEL, u16::MAX);
4446 assert!(decode_flex_u16(U16_SENTINEL_THRESHOLD - 1).is_some());
4447 }
4448
4449 #[test]
4450 fn flex_u16_every_non_sentinel_value_roundtrips() {
4451 for v in 0..U16_SENTINEL_THRESHOLD {
4452 let f = decode_flex_u16(v).unwrap_or_else(|| panic!("{v} decoded as a sentinel"));
4453 assert_eq!(
4454 encode_flex_u16(f),
4455 v,
4456 "u16 {v} → {f} → re-encoded differently"
4457 );
4458 }
4459 }
4460
4461 #[test]
4466 fn css_pixel_as_i16_maps_every_keyword_to_its_own_sentinel() {
4467 assert_eq!(encode_css_pixel_as_i16(&CssPropertyValue::Auto), I16_AUTO,);
4468 assert_eq!(
4469 encode_css_pixel_as_i16(&CssPropertyValue::Initial),
4470 I16_INITIAL,
4471 );
4472 assert_eq!(
4473 encode_css_pixel_as_i16(&CssPropertyValue::Inherit),
4474 I16_INHERIT,
4475 );
4476 assert_eq!(
4478 encode_css_pixel_as_i16(&CssPropertyValue::<PixelValue>::None),
4479 I16_SENTINEL,
4480 );
4481 assert_eq!(
4482 encode_css_pixel_as_i16(&CssPropertyValue::<PixelValue>::Revert),
4483 I16_SENTINEL,
4484 );
4485 assert_eq!(
4486 encode_css_pixel_as_i16(&CssPropertyValue::<PixelValue>::Unset),
4487 I16_SENTINEL,
4488 );
4489 }
4490
4491 #[test]
4492 fn css_pixel_as_i16_only_takes_the_fast_path_for_absolute_px() {
4493 assert_eq!(
4496 encode_css_pixel_as_i16(&CssPropertyValue::Exact(PixelValue::px(12.5))),
4497 125,
4498 );
4499 for metric in ALL_SIZE_METRIC {
4500 if metric == SizeMetric::Px {
4501 continue;
4502 }
4503 assert_eq!(
4504 encode_css_pixel_as_i16(&CssPropertyValue::Exact(pv_raw(metric, 12_500))),
4505 I16_SENTINEL,
4506 "{metric:?} needs resolution context and must not be pre-resolved",
4507 );
4508 }
4509 }
4510
4511 #[test]
4512 fn css_pixel_as_i16_out_of_range_px_escapes_to_the_sentinel() {
4513 assert_eq!(
4514 encode_css_pixel_as_i16(&CssPropertyValue::Exact(PixelValue::px(1e6))),
4515 I16_SENTINEL,
4516 );
4517 assert_eq!(
4518 encode_css_pixel_as_i16(&CssPropertyValue::Exact(PixelValue::px(-1e6))),
4519 I16_SENTINEL,
4520 );
4521 for raw in [I16_SENTINEL, I16_AUTO, I16_INHERIT, I16_INITIAL] {
4524 let px = f32::from(raw) / 10.0;
4525 assert_eq!(
4526 encode_css_pixel_as_i16(&CssPropertyValue::Exact(PixelValue::px(px))),
4527 I16_SENTINEL,
4528 "{px} px must not silently encode as the keyword sentinel {raw}",
4529 );
4530 }
4531 }
4532
4533 #[test]
4538 fn empty_cache_is_the_neutral_element() {
4539 let c = CompactLayoutCache::empty();
4540 assert_eq!(c.node_count(), 0);
4541 assert!(c.tier1_enums.is_empty());
4542 assert!(c.tier2_dims.is_empty());
4543 assert!(c.tier2_cold.is_empty());
4544 assert!(c.tier2b_text.is_empty());
4545 assert!(c.font_dirty_nodes.is_empty());
4546 assert!(c.prev_font_hashes.is_empty());
4547 assert!(c.font_hash_to_families.is_empty());
4548 assert_eq!(c.dom_declared_flags, 0);
4549 assert_eq!(CompactLayoutCache::with_capacity(0), c);
4551 }
4552
4553 #[test]
4554 fn with_capacity_keeps_every_tier_the_same_length() {
4555 for n in [0usize, 1, 2, 3, 17, 1024] {
4556 let c = CompactLayoutCache::with_capacity(n);
4557 assert_eq!(c.node_count(), n);
4558 assert_eq!(c.tier1_enums.len(), n);
4559 assert_eq!(c.tier2_dims.len(), n);
4560 assert_eq!(c.tier2_cold.len(), n);
4561 assert_eq!(c.tier2b_text.len(), n);
4562 assert_eq!(c.prev_font_hashes.len(), n);
4563 assert!(c.font_dirty_nodes.is_empty());
4565 assert_eq!(c.dom_declared_flags, 0);
4566 }
4567 }
4568
4569 #[test]
4570 fn freshly_allocated_node_reads_back_every_css_initial_value() {
4571 let c = CompactLayoutCache::with_capacity(3);
4575 for i in 0..3 {
4576 assert_eq!(T1::decode(c.tier1_enums[i]), T1::initial());
4577 assert_eq!(c.get_display(i), LayoutDisplay::Block);
4578 assert_eq!(c.get_position(i), LayoutPosition::Static);
4579 assert_eq!(c.get_float(i), LayoutFloat::None);
4580 assert_eq!(c.get_overflow_x(i), LayoutOverflow::Visible);
4581 assert_eq!(c.get_overflow_y(i), LayoutOverflow::Visible);
4582 assert_eq!(c.get_box_sizing(i), LayoutBoxSizing::ContentBox);
4583 assert_eq!(c.get_flex_direction(i), LayoutFlexDirection::Row);
4584 assert_eq!(c.get_flex_wrap(i), LayoutFlexWrap::NoWrap);
4585 assert_eq!(c.get_justify_content(i), LayoutJustifyContent::FlexStart);
4586 assert_eq!(c.get_align_items(i), LayoutAlignItems::Stretch);
4587 assert_eq!(c.get_align_content(i), LayoutAlignContent::Stretch);
4588 assert_eq!(c.get_writing_mode(i), LayoutWritingMode::HorizontalTb);
4589 assert_eq!(c.get_clear(i), LayoutClear::None);
4590 assert_eq!(c.get_font_weight(i), StyleFontWeight::Normal);
4591 assert_eq!(c.get_font_style(i), StyleFontStyle::Normal);
4592 assert_eq!(c.get_text_align(i), StyleTextAlign::Start);
4593 assert_eq!(c.get_visibility(i), StyleVisibility::Visible);
4594 assert_eq!(c.get_white_space(i), StyleWhiteSpace::Normal);
4595 assert_eq!(c.get_direction(i), StyleDirection::Ltr);
4596 assert_eq!(c.get_vertical_align(i), StyleVerticalAlign::Baseline);
4597 assert_eq!(c.get_border_collapse(i), StyleBorderCollapse::Separate);
4598
4599 assert_eq!(c.get_width_raw(i), U32_AUTO);
4601 assert_eq!(c.get_height_raw(i), U32_AUTO);
4602 assert_eq!(c.get_min_width_raw(i), U32_AUTO);
4603 assert_eq!(c.get_min_height_raw(i), U32_AUTO);
4604 assert_eq!(c.get_max_width_raw(i), U32_NONE);
4605 assert_eq!(c.get_max_height_raw(i), U32_NONE);
4606 assert_eq!(c.get_flex_basis_raw(i), U32_AUTO);
4607 assert_eq!(c.get_font_size_raw(i), U32_INITIAL);
4608 assert_eq!(decode_pixel_value_u32(c.get_width_raw(i)), None);
4609 assert_eq!(decode_pixel_value_u32(c.get_font_size_raw(i)), None);
4610
4611 assert_eq!(c.get_padding_top(i), Some(0.0));
4613 assert_eq!(c.get_padding_right(i), Some(0.0));
4614 assert_eq!(c.get_padding_bottom(i), Some(0.0));
4615 assert_eq!(c.get_padding_left(i), Some(0.0));
4616 assert_eq!(c.get_border_top_width(i), Some(0.0));
4617 assert_eq!(c.get_border_left_width(i), Some(0.0));
4618 assert_eq!(c.get_margin_top(i), Some(0.0));
4620 assert_eq!(c.get_margin_left(i), Some(0.0));
4621 assert!(!c.is_margin_top_auto(i));
4622 assert!(!c.is_margin_right_auto(i));
4623 assert!(!c.is_margin_bottom_auto(i));
4624 assert!(!c.is_margin_left_auto(i));
4625 assert_eq!(c.get_top(i), I16_AUTO);
4627 assert_eq!(c.get_right(i), I16_AUTO);
4628 assert_eq!(c.get_bottom(i), I16_AUTO);
4629 assert_eq!(c.get_left(i), I16_AUTO);
4630
4631 assert_eq!(c.get_flex_grow(i), Some(0.0));
4633 assert_eq!(c.get_flex_shrink(i), Some(1.0));
4634
4635 assert_eq!(c.get_z_index(i), I16_AUTO);
4637 assert_eq!(c.get_border_styles_packed(i), 0);
4638 assert_eq!(c.get_border_top_style(i), BorderStyle::None);
4639 assert_eq!(c.get_border_right_style(i), BorderStyle::None);
4640 assert_eq!(c.get_border_bottom_style(i), BorderStyle::None);
4641 assert_eq!(c.get_border_left_style(i), BorderStyle::None);
4642 assert_eq!(c.get_border_top_color_raw(i), 0);
4643 assert_eq!(decode_color_u32(c.get_border_top_color_raw(i)), None);
4644 assert_eq!(c.get_border_top_left_radius_raw(i), I16_SENTINEL);
4645 assert_eq!(c.get_tab_size_raw(i), I16_SENTINEL);
4646 assert_eq!(c.get_border_spacing_h_raw(i), 0);
4647 assert_eq!(c.get_border_spacing_v_raw(i), 0);
4648 assert_eq!(c.get_opacity_raw(i), OPACITY_SENTINEL);
4649 assert_eq!(c.get_hot_flags(i), 0);
4650 assert_eq!(c.get_scrollbar_gutter_bits(i), SCROLLBAR_GUTTER_AUTO);
4651
4652 assert!(!c.has_transform(i));
4655 assert!(!c.has_transform_origin(i));
4656 assert!(!c.has_box_shadow(i));
4657 assert!(!c.has_text_decoration(i));
4658 assert!(!c.has_background(i));
4659 assert!(!c.has_clip_path(i));
4660 assert!(!c.has_scrollbar_css(i));
4661 assert!(!c.has_counter(i));
4662 assert!(!c.has_break(i));
4663 assert!(!c.has_text_orientation(i));
4664 assert!(!c.has_text_shadow(i));
4665 assert!(!c.has_backdrop_filter(i));
4666 assert!(!c.has_filter(i));
4667 assert!(!c.has_mix_blend_mode(i));
4668
4669 assert_eq!(c.get_text_color_raw(i), 0);
4671 assert_eq!(c.get_font_family_hash(i), 0);
4672 assert_eq!(c.get_line_height(i), None); assert_eq!(c.get_letter_spacing(i), Some(0.0));
4674 assert_eq!(c.get_word_spacing(i), Some(0.0));
4675 assert_eq!(c.get_text_indent(i), Some(0.0));
4676 }
4677 }
4678
4679 #[test]
4680 fn hot_flag_predicates_read_only_their_own_bit() {
4681 let flags = [
4682 ("transform", HOT_FLAG_HAS_TRANSFORM),
4683 ("transform_origin", HOT_FLAG_HAS_TRANSFORM_ORIGIN),
4684 ("box_shadow", HOT_FLAG_HAS_BOX_SHADOW),
4685 ("text_decoration", HOT_FLAG_HAS_TEXT_DECORATION),
4686 ("background", HOT_FLAG_HAS_BACKGROUND),
4687 ("clip_path", HOT_FLAG_HAS_CLIP_PATH),
4688 ];
4689
4690 for (name, bit) in flags {
4691 let mut c = CompactLayoutCache::with_capacity(1);
4692 c.tier2_cold[0].hot_flags = bit;
4693
4694 let observed = [
4695 ("transform", c.has_transform(0)),
4696 ("transform_origin", c.has_transform_origin(0)),
4697 ("box_shadow", c.has_box_shadow(0)),
4698 ("text_decoration", c.has_text_decoration(0)),
4699 ("background", c.has_background(0)),
4700 ("clip_path", c.has_clip_path(0)),
4701 ];
4702 for (other, is_set) in observed {
4703 assert_eq!(
4704 is_set,
4705 other == name,
4706 "hot_flags = {bit:#010b}: has_{other}() should be {}",
4707 other == name,
4708 );
4709 }
4710 assert_eq!(c.get_scrollbar_gutter_bits(0), SCROLLBAR_GUTTER_AUTO);
4712 }
4713
4714 let mut seen = 0u8;
4716 for (_, bit) in flags {
4717 assert_eq!(seen & bit, 0, "two hot flags share bit {bit:#010b}");
4718 assert_eq!(
4719 bit & HOT_FLAG_SCROLLBAR_GUTTER_MASK,
4720 0,
4721 "hot flag {bit:#010b} overlaps the scrollbar-gutter field",
4722 );
4723 seen |= bit;
4724 }
4725 }
4726
4727 #[test]
4728 fn scrollbar_gutter_bits_survive_a_fully_set_hot_flags_byte() {
4729 for gutter in [
4730 SCROLLBAR_GUTTER_AUTO,
4731 SCROLLBAR_GUTTER_STABLE,
4732 SCROLLBAR_GUTTER_BOTH_EDGES,
4733 SCROLLBAR_GUTTER_MIRROR,
4734 ] {
4735 let mut c = CompactLayoutCache::with_capacity(1);
4736 c.tier2_cold[0].hot_flags = HOT_FLAG_HAS_TRANSFORM
4739 | HOT_FLAG_HAS_TRANSFORM_ORIGIN
4740 | HOT_FLAG_HAS_BOX_SHADOW
4741 | HOT_FLAG_HAS_TEXT_DECORATION
4742 | HOT_FLAG_HAS_BACKGROUND
4743 | HOT_FLAG_HAS_CLIP_PATH
4744 | (gutter << HOT_FLAG_SCROLLBAR_GUTTER_SHIFT);
4745
4746 assert_eq!(c.get_scrollbar_gutter_bits(0), gutter);
4747 assert!(c.has_transform(0));
4748 assert!(c.has_clip_path(0));
4749 }
4750
4751 let mut c = CompactLayoutCache::with_capacity(1);
4753 c.tier2_cold[0].hot_flags = u8::MAX;
4754 assert_eq!(c.get_scrollbar_gutter_bits(0), SCROLLBAR_GUTTER_MIRROR);
4755 assert!(c.get_scrollbar_gutter_bits(0) <= 3);
4756 }
4757
4758 #[test]
4759 fn extra_flag_predicates_read_only_their_own_bit() {
4760 let flags = [
4761 ("scrollbar_css", EXTRA_FLAG_HAS_SCROLLBAR_CSS),
4762 ("counter", EXTRA_FLAG_HAS_COUNTER),
4763 ("break", EXTRA_FLAG_HAS_BREAK),
4764 ("text_orientation", EXTRA_FLAG_HAS_TEXT_ORIENTATION),
4765 ("text_shadow", EXTRA_FLAG_HAS_TEXT_SHADOW),
4766 ("backdrop_filter", EXTRA_FLAG_HAS_BACKDROP_FILTER),
4767 ("filter", EXTRA_FLAG_HAS_FILTER),
4768 ("mix_blend_mode", EXTRA_FLAG_HAS_MIX_BLEND_MODE),
4769 ];
4770
4771 let mut seen = 0u8;
4773 for (_, bit) in flags {
4774 assert_eq!(seen & bit, 0, "two extra flags share bit {bit:#010b}");
4775 seen |= bit;
4776 }
4777 assert_eq!(seen, u8::MAX);
4778
4779 for (name, bit) in flags {
4780 let mut c = CompactLayoutCache::with_capacity(1);
4781 c.tier2_cold[0].extra_flags = bit;
4782 let observed = [
4783 ("scrollbar_css", c.has_scrollbar_css(0)),
4784 ("counter", c.has_counter(0)),
4785 ("break", c.has_break(0)),
4786 ("text_orientation", c.has_text_orientation(0)),
4787 ("text_shadow", c.has_text_shadow(0)),
4788 ("backdrop_filter", c.has_backdrop_filter(0)),
4789 ("filter", c.has_filter(0)),
4790 ("mix_blend_mode", c.has_mix_blend_mode(0)),
4791 ];
4792 for (other, is_set) in observed {
4793 assert_eq!(
4794 is_set,
4795 other == name,
4796 "extra_flags = {bit:#010b}: has_{other}() should be {}",
4797 other == name,
4798 );
4799 }
4800 assert!(!c.has_transform(0));
4802 assert!(!c.has_background(0));
4803 }
4804 }
4805
4806 #[test]
4807 fn dom_declared_flags_are_distinct_and_queryable() {
4808 let flags = [
4809 DOM_HAS_SHAPE_INSIDE,
4810 DOM_HAS_SHAPE_OUTSIDE,
4811 DOM_HAS_TEXT_JUSTIFY,
4812 DOM_HAS_TEXT_INDENT,
4813 DOM_HAS_COLUMN_COUNT,
4814 DOM_HAS_COLUMN_GAP,
4815 DOM_HAS_INITIAL_LETTER,
4816 DOM_HAS_INITIAL_LETTER_ALIGN,
4817 DOM_HAS_LINE_CLAMP,
4818 DOM_HAS_HANGING_PUNCTUATION,
4819 DOM_HAS_TEXT_COMBINE_UPRIGHT,
4820 DOM_HAS_EXCLUSION_MARGIN,
4821 DOM_HAS_HYPHENATION_LANGUAGE,
4822 DOM_HAS_UNICODE_BIDI,
4823 DOM_HAS_TEXT_BOX_TRIM,
4824 DOM_HAS_HYPHENS,
4825 DOM_HAS_WORD_BREAK,
4826 DOM_HAS_OVERFLOW_WRAP,
4827 DOM_HAS_LINE_BREAK,
4828 DOM_HAS_TEXT_ALIGN_LAST,
4829 DOM_HAS_LINE_HEIGHT,
4830 DOM_HAS_COLUMN_WIDTH,
4831 DOM_HAS_SHAPE_MARGIN,
4832 ];
4833
4834 let mut seen = 0u32;
4835 for f in flags {
4836 assert_eq!(f.count_ones(), 1, "{f:#X} is not a single-bit flag");
4837 assert_eq!(seen & f, 0, "two DOM_HAS_* flags share bit {f:#X}");
4838 seen |= f;
4839 }
4840
4841 let mut c = CompactLayoutCache::empty();
4842 for f in flags {
4844 assert!(!c.dom_declared(f));
4845 }
4846 assert!(!c.dom_declared(0));
4847 assert!(!c.dom_declared(u32::MAX));
4848
4849 c.dom_declared_flags = DOM_HAS_LINE_HEIGHT;
4851 for f in flags {
4852 assert_eq!(c.dom_declared(f), f == DOM_HAS_LINE_HEIGHT);
4853 }
4854 assert!(
4855 !c.dom_declared(0),
4856 "an empty flag query must never report declared"
4857 );
4858 assert!(c.dom_declared(u32::MAX));
4859
4860 c.dom_declared_flags = u32::MAX;
4862 for f in flags {
4863 assert!(c.dom_declared(f));
4864 }
4865 }
4866
4867 #[test]
4868 fn getters_at_the_last_valid_index_do_not_panic() {
4869 let c = CompactLayoutCache::with_capacity(4);
4870 let last = c.node_count() - 1;
4871 let _ = c.get_display(last);
4872 let _ = c.get_width_raw(last);
4873 let _ = c.get_padding_top(last);
4874 let _ = c.get_z_index(last);
4875 let _ = c.get_border_top_style(last);
4876 let _ = c.get_line_height(last);
4877 let _ = c.has_transform(last);
4878 }
4879
4880 #[test]
4881 #[should_panic(expected = "index out of bounds")]
4882 fn tier1_getter_on_an_empty_cache_panics_rather_than_reading_oob() {
4883 let c = CompactLayoutCache::empty();
4884 let _ = c.get_display(0);
4885 }
4886
4887 #[test]
4888 #[should_panic(expected = "index out of bounds")]
4889 fn tier2_getter_past_the_end_panics_rather_than_reading_oob() {
4890 let c = CompactLayoutCache::with_capacity(2);
4891 let _ = c.get_padding_top(2);
4892 }
4893
4894 #[test]
4895 #[should_panic(expected = "index out of bounds")]
4896 fn cold_tier_getter_at_usize_max_panics_rather_than_wrapping() {
4897 let c = CompactLayoutCache::with_capacity(1);
4900 let _ = c.get_z_index(usize::MAX);
4901 }
4902
4903 #[test]
4904 #[should_panic(expected = "index out of bounds")]
4905 fn text_tier_getter_past_the_end_panics_rather_than_reading_oob() {
4906 let c = CompactLayoutCache::with_capacity(1);
4907 let _ = c.get_font_family_hash(1);
4908 }
4909
4910 #[test]
4915 fn compact_structs_stay_within_their_documented_byte_budget() {
4916 assert_eq!(size_of::<CompactNodeProps>(), 72);
4920 assert_eq!(size_of::<CompactNodePropsCold>(), 48);
4921 assert_eq!(size_of::<CompactTextProps>(), 24);
4922 assert_eq!(size_of::<u64>(), 8);
4924 assert_eq!(align_of::<CompactNodeProps>(), 4);
4926 assert_eq!(align_of::<CompactNodePropsCold>(), 4);
4927 assert_eq!(align_of::<CompactTextProps>(), 8);
4928 }
4929
4930 #[test]
4933 fn every_cursor_variant_survives_the_round_trip() {
4934 use crate::props::style::effects::StyleCursor::{
4935 Alias, AllScroll, Cell, ColResize, ContextMenu, Copy, Crosshair, Default as Def,
4936 EResize, EwResize, Grab, Grabbing, Help, Move, NResize, NeswResize, NsResize,
4937 NwseResize, Pointer, Progress, RowResize, SResize, SeResize, Text, Unset,
4938 VerticalText, WResize, Wait, ZoomIn, ZoomOut,
4939 };
4940 let all = [
4941 Def, Alias, AllScroll, Cell, ColResize, ContextMenu, Copy, Crosshair, EResize,
4942 EwResize, Grab, Grabbing, Help, Move, NResize, NsResize, NeswResize, NwseResize,
4943 Pointer, Progress, RowResize, SResize, SeResize, Text, Unset, VerticalText, WResize,
4944 Wait, ZoomIn, ZoomOut,
4945 ];
4946 let mut seen = alloc::collections::BTreeSet::new();
4947 for c in all {
4948 let code = cursor_to_u8(c);
4949 assert!(
4950 code <= CURSOR_CODE_MAX,
4951 "{c:?} encodes to {code}, past CURSOR_CODE_MAX",
4952 );
4953 assert!(seen.insert(code), "{c:?} shares code {code} with another variant");
4954 assert_eq!(cursor_from_u8(code), c, "{c:?} did not survive the round trip");
4955
4956 let mut cold = CompactNodePropsCold::default();
4958 cold.cursor = code;
4959 assert_eq!(cursor_from_u8(cold.cursor), c);
4960 }
4961 assert_eq!(seen.len(), all.len(), "every variant needs its own code");
4962 }
4963
4964 #[test]
4978 fn tier1_bit_ranges_do_not_overlap() {
4979 let fields: &[(&str, u32, u64)] = &[
4980 ("display", DISPLAY_SHIFT, DISPLAY_MASK),
4981 ("position", POSITION_SHIFT, POSITION_MASK),
4982 ("float", FLOAT_SHIFT, FLOAT_MASK),
4983 ("overflow_x", OVERFLOW_X_SHIFT, OVERFLOW_MASK),
4984 ("overflow_y", OVERFLOW_Y_SHIFT, OVERFLOW_MASK),
4985 ("box_sizing", BOX_SIZING_SHIFT, BOX_SIZING_MASK),
4986 ("flex_direction", FLEX_DIRECTION_SHIFT, FLEX_DIR_MASK),
4987 ("flex_wrap", FLEX_WRAP_SHIFT, FLEX_WRAP_MASK),
4988 ("justify_content", JUSTIFY_CONTENT_SHIFT, JUSTIFY_MASK),
4989 ("align_items", ALIGN_ITEMS_SHIFT, ALIGN_MASK),
4990 ("align_content", ALIGN_CONTENT_SHIFT, ALIGN_MASK),
4991 ("writing_mode", WRITING_MODE_SHIFT, WRITING_MODE_MASK),
4992 ("clear", CLEAR_SHIFT, CLEAR_MASK),
4993 ("font_weight", FONT_WEIGHT_SHIFT, FONT_WEIGHT_MASK),
4994 ("font_style", FONT_STYLE_SHIFT, FONT_STYLE_MASK),
4995 ("text_align", TEXT_ALIGN_SHIFT, TEXT_ALIGN_MASK),
4996 ("visibility", VISIBILITY_SHIFT, VISIBILITY_MASK),
4997 ("white_space", WHITE_SPACE_SHIFT, WHITE_SPACE_MASK),
4998 ("direction", DIRECTION_SHIFT, DIRECTION_MASK),
4999 ("vertical_align", VERTICAL_ALIGN_SHIFT, VERTICAL_ALIGN_MASK),
5000 ("border_collapse", BORDER_COLLAPSE_SHIFT, BORDER_COLLAPSE_MASK),
5001 ("align_self", ALIGN_SELF_SHIFT, ALIGN_SELF_MASK),
5002 ("justify_self", JUSTIFY_SELF_SHIFT, JUSTIFY_SELF_MASK),
5003 ("grid_auto_flow", GRID_AUTO_FLOW_SHIFT, GRID_AUTO_FLOW_MASK),
5004 ("justify_items", JUSTIFY_ITEMS_SHIFT, JUSTIFY_ITEMS_MASK),
5005 ];
5006
5007 for (name, shift, mask) in fields {
5008 let bits = mask << shift;
5009 assert_eq!(
5010 bits >> shift,
5011 *mask,
5012 "{name} at bit {shift} runs off the top of the word",
5013 );
5014 assert_eq!(
5015 bits & TIER1_POPULATED_BIT,
5016 0,
5017 "{name} overlaps TIER1_POPULATED_BIT",
5018 );
5019 }
5020
5021 for (i, (an, ash, am)) in fields.iter().enumerate() {
5022 for (bn, bsh, bm) in &fields[i + 1..] {
5023 let a = am << ash;
5024 let b = bm << bsh;
5025 assert_eq!(
5026 a & b,
5027 0,
5028 "{an} (bit {ash}, mask {am:#x}) and {bn} (bit {bsh}, mask {bm:#x}) \
5029 share bits {:#x} — writing one silently corrupts the other",
5030 a & b,
5031 );
5032 }
5033 }
5034 }
5035}