/src/yoga/yoga/style/StyleValuePool.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #pragma once |
9 | | |
10 | | #include <cassert> |
11 | | #include <cstdint> |
12 | | |
13 | | #include <yoga/numeric/FloatOptional.h> |
14 | | #include <yoga/style/SmallValueBuffer.h> |
15 | | #include <yoga/style/StyleLength.h> |
16 | | #include <yoga/style/StyleSizeLength.h> |
17 | | #include <yoga/style/StyleValueHandle.h> |
18 | | |
19 | | namespace facebook::yoga { |
20 | | |
21 | | /** |
22 | | * StyleValuePool allows compact storage for a sparse collection of assigned |
23 | | * lengths and numbers. Values are referred to using StyleValueHandle. In most |
24 | | * cases StyleValueHandle can embed the value directly, but if not, the value is |
25 | | * stored within a buffer provided by the pool. The pool contains a fixed number |
26 | | * of inline slots before falling back to heap allocating additional slots. |
27 | | */ |
28 | | class StyleValuePool { |
29 | | public: |
30 | 3.94M | void store(StyleValueHandle& handle, StyleLength length) { |
31 | 3.94M | if (length.isUndefined()) { |
32 | 0 | handle.setType(StyleValueHandle::Type::Undefined); |
33 | 3.94M | } else if (length.isAuto()) { |
34 | 0 | handle.setType(StyleValueHandle::Type::Auto); |
35 | 3.94M | } else { |
36 | 3.94M | auto type = length.isPoints() ? StyleValueHandle::Type::Point |
37 | 3.94M | : StyleValueHandle::Type::Percent; |
38 | 3.94M | storeValue(handle, length.value().unwrap(), type); |
39 | 3.94M | } |
40 | 3.94M | } |
41 | | |
42 | 7.89M | void store(StyleValueHandle& handle, StyleSizeLength sizeValue) { |
43 | 7.89M | if (sizeValue.isUndefined()) { |
44 | 0 | handle.setType(StyleValueHandle::Type::Undefined); |
45 | 7.89M | } else if (sizeValue.isAuto()) { |
46 | 0 | handle.setType(StyleValueHandle::Type::Auto); |
47 | 7.89M | } else if (sizeValue.isMaxContent()) { |
48 | 0 | storeKeyword(handle, StyleValueHandle::Keyword::MaxContent); |
49 | 7.89M | } else if (sizeValue.isStretch()) { |
50 | 0 | storeKeyword(handle, StyleValueHandle::Keyword::Stretch); |
51 | 7.89M | } else if (sizeValue.isFitContent()) { |
52 | 0 | storeKeyword(handle, StyleValueHandle::Keyword::FitContent); |
53 | 7.89M | } else { |
54 | 7.89M | auto type = sizeValue.isPoints() ? StyleValueHandle::Type::Point |
55 | 7.89M | : StyleValueHandle::Type::Percent; |
56 | 7.89M | storeValue(handle, sizeValue.value().unwrap(), type); |
57 | 7.89M | } |
58 | 7.89M | } |
59 | | |
60 | 0 | void store(StyleValueHandle& handle, FloatOptional number) { |
61 | 0 | if (number.isUndefined()) { |
62 | 0 | handle.setType(StyleValueHandle::Type::Undefined); |
63 | 0 | } else { |
64 | 0 | storeValue(handle, number.unwrap(), StyleValueHandle::Type::Number); |
65 | 0 | } |
66 | 0 | } |
67 | | |
68 | 738M | StyleLength getLength(StyleValueHandle handle) const { |
69 | 738M | if (handle.isUndefined()) { |
70 | 733M | return StyleLength::undefined(); |
71 | 733M | } else if (handle.isAuto()) { |
72 | 0 | return StyleLength::ofAuto(); |
73 | 4.44M | } else { |
74 | 4.44M | assert( |
75 | 4.44M | handle.type() == StyleValueHandle::Type::Point || |
76 | 4.44M | handle.type() == StyleValueHandle::Type::Percent); |
77 | 4.44M | float value = (handle.isValueIndexed()) |
78 | 4.44M | ? std::bit_cast<float>(buffer_.get32(handle.value())) |
79 | 4.44M | : unpackInlineInteger(handle.value()); |
80 | | |
81 | 4.44M | return handle.type() == StyleValueHandle::Type::Point |
82 | 4.44M | ? StyleLength::points(value) |
83 | 4.44M | : StyleLength::percent(value); |
84 | 4.44M | } |
85 | 738M | } |
86 | | |
87 | 235M | StyleSizeLength getSize(StyleValueHandle handle) const { |
88 | 235M | if (handle.isUndefined()) { |
89 | 194M | return StyleSizeLength::undefined(); |
90 | 194M | } else if (handle.isAuto()) { |
91 | 19.1M | return StyleSizeLength::ofAuto(); |
92 | 22.4M | } else if (handle.isKeyword(StyleValueHandle::Keyword::MaxContent)) { |
93 | 0 | return StyleSizeLength::ofMaxContent(); |
94 | 22.4M | } else if (handle.isKeyword(StyleValueHandle::Keyword::FitContent)) { |
95 | 0 | return StyleSizeLength::ofFitContent(); |
96 | 22.4M | } else if (handle.isKeyword(StyleValueHandle::Keyword::Stretch)) { |
97 | 0 | return StyleSizeLength::ofStretch(); |
98 | 22.4M | } else { |
99 | 22.4M | assert( |
100 | 22.4M | handle.type() == StyleValueHandle::Type::Point || |
101 | 22.4M | handle.type() == StyleValueHandle::Type::Percent); |
102 | 22.4M | float value = (handle.isValueIndexed()) |
103 | 22.4M | ? std::bit_cast<float>(buffer_.get32(handle.value())) |
104 | 22.4M | : unpackInlineInteger(handle.value()); |
105 | | |
106 | 22.4M | return handle.type() == StyleValueHandle::Type::Point |
107 | 22.4M | ? StyleSizeLength::points(value) |
108 | 22.4M | : StyleSizeLength::percent(value); |
109 | 22.4M | } |
110 | 235M | } |
111 | | |
112 | 124M | FloatOptional getNumber(StyleValueHandle handle) const { |
113 | 124M | if (handle.isUndefined()) { |
114 | 124M | return FloatOptional{}; |
115 | 124M | } else { |
116 | 0 | assert(handle.type() == StyleValueHandle::Type::Number); |
117 | 0 | float value = (handle.isValueIndexed()) |
118 | 0 | ? std::bit_cast<float>(buffer_.get32(handle.value())) |
119 | 0 | : unpackInlineInteger(handle.value()); |
120 | 0 | return FloatOptional{value}; |
121 | 0 | } |
122 | 124M | } |
123 | | |
124 | | private: |
125 | | void storeValue( |
126 | | StyleValueHandle& handle, |
127 | | float value, |
128 | 11.8M | StyleValueHandle::Type type) { |
129 | 11.8M | handle.setType(type); |
130 | | |
131 | 11.8M | if (handle.isValueIndexed()) { |
132 | 0 | auto newIndex = |
133 | 0 | buffer_.replace(handle.value(), std::bit_cast<uint32_t>(value)); |
134 | 0 | handle.setValue(newIndex); |
135 | 11.8M | } else if (isIntegerPackable(value)) { |
136 | 387k | handle.setValue(packInlineInteger(value)); |
137 | 11.4M | } else { |
138 | 11.4M | auto newIndex = buffer_.push(std::bit_cast<uint32_t>(value)); |
139 | 11.4M | handle.setValue(newIndex); |
140 | 11.4M | handle.setValueIsIndexed(); |
141 | 11.4M | } |
142 | 11.8M | } |
143 | | |
144 | | void storeKeyword( |
145 | | StyleValueHandle& handle, |
146 | 0 | StyleValueHandle::Keyword keyword) { |
147 | 0 | handle.setType(StyleValueHandle::Type::Keyword); |
148 | |
|
149 | 0 | if (handle.isValueIndexed()) { |
150 | 0 | auto newIndex = |
151 | 0 | buffer_.replace(handle.value(), static_cast<uint32_t>(keyword)); |
152 | 0 | handle.setValue(newIndex); |
153 | 0 | } else { |
154 | 0 | handle.setValue(static_cast<uint16_t>(keyword)); |
155 | 0 | } |
156 | 0 | } |
157 | | |
158 | 11.8M | static constexpr bool isIntegerPackable(float f) { |
159 | 11.8M | constexpr uint16_t kMaxInlineAbsValue = (1 << 11) - 1; |
160 | | |
161 | 11.8M | auto i = static_cast<int32_t>(f); |
162 | 11.8M | return static_cast<float>(i) == f && i >= -kMaxInlineAbsValue && |
163 | 11.8M | i <= +kMaxInlineAbsValue; |
164 | 11.8M | } |
165 | | |
166 | 387k | static constexpr uint16_t packInlineInteger(float value) { |
167 | 387k | uint16_t isNegative = value < 0 ? 1 : 0; |
168 | 387k | return static_cast<uint16_t>( |
169 | 387k | (isNegative << 11) | |
170 | 387k | (static_cast<int32_t>(value) * (isNegative != 0u ? -1 : 1))); |
171 | 387k | } |
172 | | |
173 | 500k | static constexpr float unpackInlineInteger(uint16_t value) { |
174 | 500k | constexpr uint16_t kValueSignMask = 0b0000'1000'0000'0000; |
175 | 500k | constexpr uint16_t kValueMagnitudeMask = 0b0000'0111'1111'1111; |
176 | 500k | const bool isNegative = (value & kValueSignMask) != 0; |
177 | 500k | return static_cast<float>( |
178 | 500k | (value & kValueMagnitudeMask) * (isNegative ? -1 : 1)); |
179 | 500k | } |
180 | | |
181 | | SmallValueBuffer<4> buffer_; |
182 | | }; |
183 | | |
184 | | } // namespace facebook::yoga |