File: IntrinsicISizesCache.h

package info (click to toggle)
firefox 141.0.2-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,550,616 kB
  • sloc: cpp: 7,426,508; javascript: 6,367,238; ansic: 3,707,354; python: 1,368,984; xml: 623,983; asm: 426,916; java: 184,324; sh: 64,488; makefile: 19,203; objc: 13,059; perl: 12,955; yacc: 4,583; cs: 3,846; pascal: 3,352; lex: 1,720; ruby: 1,071; exp: 762; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10
file content (189 lines) | stat: -rw-r--r-- 6,299 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_IntrinsicISizesCache_h
#define mozilla_IntrinsicISizesCache_h

#include "nsIFrame.h"

namespace mozilla {

// Some frame classes keep a cache of intrinsic inline sizes. This class
// encapsulates the logic for caching them depending on the IntrinsicSizeInput.
//
// The cache is intended to take as little space as possible
// (max(sizeof(nscoord) * 2, sizeof(void*))), when there are no percentage-size
// dependencies.
struct IntrinsicISizesCache final {
  IntrinsicISizesCache() {
    new (&mInline) InlineCache();
    MOZ_ASSERT(IsInline());
  }

  ~IntrinsicISizesCache() { delete GetOutOfLine(); }

  template <typename Compute>
  nscoord GetOrSet(nsIFrame& aFrame, IntrinsicISizeType aType,
                   const IntrinsicSizeInput& aInput, Compute aCompute) {
    bool dependentOnPercentBSize = aFrame.HasAnyStateBits(
        NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
    nscoord value = Get(dependentOnPercentBSize, aType, aInput);
    if (value != kNotFound) {
      return value;
    }
    value = aCompute();
    // Inside of aCompute(), we might have newly discovered that we do have a
    // descendant whose intrinsic isize depends on our bsize; so we check that
    // state bit again before updating the cache.
    dependentOnPercentBSize = aFrame.HasAnyStateBits(
        NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
    Set(dependentOnPercentBSize, aType, aInput, value);
    return value;
  }

  void Clear() {
    if (auto* ool = GetOutOfLine()) {
      ool->mCacheWithPercentageBasis.Clear();
      ool->mCacheWithoutPercentageBasis.Clear();
      ool->mLastPercentageBasis.reset();
    } else {
      mInline.Clear();
    }
  }

 private:
  // We use nscoord_MAX rather than NS_INTRINSIC_ISIZE_UNKNOWN as our sentinel
  // value so that our high bit is always free.
  static constexpr nscoord kNotFound = nscoord_MAX;

  nscoord Get(bool aDependentOnPercentBSize, IntrinsicISizeType aType,
              const IntrinsicSizeInput& aInput) const {
    const bool usePercentageAwareCache =
        aDependentOnPercentBSize && aInput.HasSomePercentageBasisForChildren();
    if (!usePercentageAwareCache) {
      if (auto* ool = GetOutOfLine()) {
        return ool->mCacheWithoutPercentageBasis.Get(aType);
      }
      return mInline.Get(aType);
    }
    if (auto* ool = GetOutOfLine()) {
      if (ool->mLastPercentageBasis == aInput.mPercentageBasisForChildren) {
        return ool->mCacheWithPercentageBasis.Get(aType);
      }
    }
    return kNotFound;
  }

  void Set(bool aDependentOnPercentBSize, IntrinsicISizeType aType,
           const IntrinsicSizeInput& aInput, nscoord aValue) {
    // Intrinsic sizes should be nonnegative, so this std::max clamping should
    // rarely be necessary except in cases of integer overflow.  We have to be
    // strict about it, though, because of how we (ab)use the high bit
    // (see kHighBit)
    aValue = std::max(aValue, 0);
    const bool usePercentageAwareCache =
        aDependentOnPercentBSize && aInput.HasSomePercentageBasisForChildren();
    if (usePercentageAwareCache) {
      auto* ool = EnsureOutOfLine();
      if (ool->mLastPercentageBasis != aInput.mPercentageBasisForChildren) {
        ool->mLastPercentageBasis = aInput.mPercentageBasisForChildren;
        ool->mCacheWithPercentageBasis.Clear();
      }
      ool->mCacheWithPercentageBasis.Set(aType, aValue);
    } else if (auto* ool = GetOutOfLine()) {
      ool->mCacheWithoutPercentageBasis.Set(aType, aValue);
    } else {
      mInline.Set(aType, aValue);
      // No inline value should be able to cause us to misinterpret our
      // representation as out-of-line, because intrinsic isizes should always
      // be non-negative.
      MOZ_DIAGNOSTIC_ASSERT(IsInline());
    }
  }

  struct InlineCache {
    nscoord mCachedMinISize = kNotFound;
    nscoord mCachedPrefISize = kNotFound;

    nscoord Get(IntrinsicISizeType aType) const {
      return aType == IntrinsicISizeType::MinISize ? mCachedMinISize
                                                   : mCachedPrefISize;
    }
    void Set(IntrinsicISizeType aType, nscoord aValue) {
      MOZ_ASSERT(aValue >= 0);
      if (aType == IntrinsicISizeType::MinISize) {
        mCachedMinISize = aValue;
      } else {
        mCachedPrefISize = aValue;
      }
    }

    void Clear() { *this = {}; }
  };

  struct OutOfLineCache {
    InlineCache mCacheWithoutPercentageBasis;
    InlineCache mCacheWithPercentageBasis;
    Maybe<LogicalSize> mLastPercentageBasis;
  };

  // If the high bit of mOutOfLine is 1, then it points to an OutOfLineCache.
  union {
    InlineCache mInline;
    struct {
#ifndef HAVE_64BIT_BUILD
      uintptr_t mPadding = 0;
#endif
      uintptr_t mOutOfLine = 0;
    };
  };

  static constexpr uintptr_t kHighBit = uintptr_t(1)
                                        << (sizeof(void*) * CHAR_BIT - 1);

  bool IsOutOfLine() const {
#ifdef HAVE_64BIT_BUILD
    return mOutOfLine & kHighBit;
#else
    return mPadding & kHighBit;
#endif
  }
  bool IsInline() const { return !IsOutOfLine(); }
  OutOfLineCache* EnsureOutOfLine() {
    if (auto* ool = GetOutOfLine()) {
      return ool;
    }
    auto inlineCache = mInline;
    auto* ool = new OutOfLineCache();
    ool->mCacheWithoutPercentageBasis = inlineCache;
#ifdef HAVE_64BIT_BUILD
    MOZ_ASSERT((reinterpret_cast<uintptr_t>(ool) & kHighBit) == 0);
    mOutOfLine = reinterpret_cast<uintptr_t>(ool) | kHighBit;
#else
    mOutOfLine = reinterpret_cast<uintptr_t>(ool);
    mPadding = kHighBit;
#endif
    MOZ_ASSERT(IsOutOfLine());
    return ool;
  }

  OutOfLineCache* GetOutOfLine() const {
    if (!IsOutOfLine()) {
      return nullptr;
    }
#ifdef HAVE_64BIT_BUILD
    return reinterpret_cast<OutOfLineCache*>(mOutOfLine & ~kHighBit);
#else
    return reinterpret_cast<OutOfLineCache*>(mOutOfLine);
#endif
  }
};

static_assert(sizeof(IntrinsicISizesCache) == 8, "Unexpected cache size");

}  // namespace mozilla

#endif