Coverage Report

Created: 2025-08-26 06:41

/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