tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

IntrinsicISizesCache.h (6299B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_IntrinsicISizesCache_h
      8 #define mozilla_IntrinsicISizesCache_h
      9 
     10 #include "nsIFrame.h"
     11 
     12 namespace mozilla {
     13 
     14 // Some frame classes keep a cache of intrinsic inline sizes. This class
     15 // encapsulates the logic for caching them depending on the IntrinsicSizeInput.
     16 //
     17 // The cache is intended to take as little space as possible
     18 // (max(sizeof(nscoord) * 2, sizeof(void*))), when there are no percentage-size
     19 // dependencies.
     20 struct IntrinsicISizesCache final {
     21  IntrinsicISizesCache() {
     22    new (&mInline) InlineCache();
     23    MOZ_ASSERT(IsInline());
     24  }
     25 
     26  ~IntrinsicISizesCache() { delete GetOutOfLine(); }
     27 
     28  template <typename Compute>
     29  nscoord GetOrSet(nsIFrame& aFrame, IntrinsicISizeType aType,
     30                   const IntrinsicSizeInput& aInput, Compute aCompute) {
     31    bool dependentOnPercentBSize = aFrame.HasAnyStateBits(
     32        NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
     33    nscoord value = Get(dependentOnPercentBSize, aType, aInput);
     34    if (value != kNotFound) {
     35      return value;
     36    }
     37    value = aCompute();
     38    // Inside of aCompute(), we might have newly discovered that we do have a
     39    // descendant whose intrinsic isize depends on our bsize; so we check that
     40    // state bit again before updating the cache.
     41    dependentOnPercentBSize = aFrame.HasAnyStateBits(
     42        NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
     43    Set(dependentOnPercentBSize, aType, aInput, value);
     44    return value;
     45  }
     46 
     47  void Clear() {
     48    if (auto* ool = GetOutOfLine()) {
     49      ool->mCacheWithPercentageBasis.Clear();
     50      ool->mCacheWithoutPercentageBasis.Clear();
     51      ool->mLastPercentageBasis.reset();
     52    } else {
     53      mInline.Clear();
     54    }
     55  }
     56 
     57 private:
     58  // We use nscoord_MAX rather than NS_INTRINSIC_ISIZE_UNKNOWN as our sentinel
     59  // value so that our high bit is always free.
     60  static constexpr nscoord kNotFound = nscoord_MAX;
     61 
     62  nscoord Get(bool aDependentOnPercentBSize, IntrinsicISizeType aType,
     63              const IntrinsicSizeInput& aInput) const {
     64    const bool usePercentageAwareCache =
     65        aDependentOnPercentBSize && aInput.HasSomePercentageBasisForChildren();
     66    if (!usePercentageAwareCache) {
     67      if (auto* ool = GetOutOfLine()) {
     68        return ool->mCacheWithoutPercentageBasis.Get(aType);
     69      }
     70      return mInline.Get(aType);
     71    }
     72    if (auto* ool = GetOutOfLine()) {
     73      if (ool->mLastPercentageBasis == aInput.mPercentageBasisForChildren) {
     74        return ool->mCacheWithPercentageBasis.Get(aType);
     75      }
     76    }
     77    return kNotFound;
     78  }
     79 
     80  void Set(bool aDependentOnPercentBSize, IntrinsicISizeType aType,
     81           const IntrinsicSizeInput& aInput, nscoord aValue) {
     82    // Intrinsic sizes should be nonnegative, so this std::max clamping should
     83    // rarely be necessary except in cases of integer overflow.  We have to be
     84    // strict about it, though, because of how we (ab)use the high bit
     85    // (see kHighBit)
     86    aValue = std::max(aValue, 0);
     87    const bool usePercentageAwareCache =
     88        aDependentOnPercentBSize && aInput.HasSomePercentageBasisForChildren();
     89    if (usePercentageAwareCache) {
     90      auto* ool = EnsureOutOfLine();
     91      if (ool->mLastPercentageBasis != aInput.mPercentageBasisForChildren) {
     92        ool->mLastPercentageBasis = aInput.mPercentageBasisForChildren;
     93        ool->mCacheWithPercentageBasis.Clear();
     94      }
     95      ool->mCacheWithPercentageBasis.Set(aType, aValue);
     96    } else if (auto* ool = GetOutOfLine()) {
     97      ool->mCacheWithoutPercentageBasis.Set(aType, aValue);
     98    } else {
     99      mInline.Set(aType, aValue);
    100      // No inline value should be able to cause us to misinterpret our
    101      // representation as out-of-line, because intrinsic isizes should always
    102      // be non-negative.
    103      MOZ_DIAGNOSTIC_ASSERT(IsInline());
    104    }
    105  }
    106 
    107  struct InlineCache {
    108    nscoord mCachedMinISize = kNotFound;
    109    nscoord mCachedPrefISize = kNotFound;
    110 
    111    nscoord Get(IntrinsicISizeType aType) const {
    112      return aType == IntrinsicISizeType::MinISize ? mCachedMinISize
    113                                                   : mCachedPrefISize;
    114    }
    115    void Set(IntrinsicISizeType aType, nscoord aValue) {
    116      MOZ_ASSERT(aValue >= 0);
    117      if (aType == IntrinsicISizeType::MinISize) {
    118        mCachedMinISize = aValue;
    119      } else {
    120        mCachedPrefISize = aValue;
    121      }
    122    }
    123 
    124    void Clear() { *this = {}; }
    125  };
    126 
    127  struct OutOfLineCache {
    128    InlineCache mCacheWithoutPercentageBasis;
    129    InlineCache mCacheWithPercentageBasis;
    130    Maybe<LogicalSize> mLastPercentageBasis;
    131  };
    132 
    133  // If the high bit of mOutOfLine is 1, then it points to an OutOfLineCache.
    134  union {
    135    InlineCache mInline;
    136    struct {
    137 #ifndef HAVE_64BIT_BUILD
    138      uintptr_t mPadding = 0;
    139 #endif
    140      uintptr_t mOutOfLine = 0;
    141    };
    142  };
    143 
    144  static constexpr uintptr_t kHighBit = uintptr_t(1)
    145                                        << (sizeof(void*) * CHAR_BIT - 1);
    146 
    147  bool IsOutOfLine() const {
    148 #ifdef HAVE_64BIT_BUILD
    149    return mOutOfLine & kHighBit;
    150 #else
    151    return mPadding & kHighBit;
    152 #endif
    153  }
    154  bool IsInline() const { return !IsOutOfLine(); }
    155  OutOfLineCache* EnsureOutOfLine() {
    156    if (auto* ool = GetOutOfLine()) {
    157      return ool;
    158    }
    159    auto inlineCache = mInline;
    160    auto* ool = new OutOfLineCache();
    161    ool->mCacheWithoutPercentageBasis = inlineCache;
    162 #ifdef HAVE_64BIT_BUILD
    163    MOZ_ASSERT((reinterpret_cast<uintptr_t>(ool) & kHighBit) == 0);
    164    mOutOfLine = reinterpret_cast<uintptr_t>(ool) | kHighBit;
    165 #else
    166    mOutOfLine = reinterpret_cast<uintptr_t>(ool);
    167    mPadding = kHighBit;
    168 #endif
    169    MOZ_ASSERT(IsOutOfLine());
    170    return ool;
    171  }
    172 
    173  OutOfLineCache* GetOutOfLine() const {
    174    if (!IsOutOfLine()) {
    175      return nullptr;
    176    }
    177 #ifdef HAVE_64BIT_BUILD
    178    return reinterpret_cast<OutOfLineCache*>(mOutOfLine & ~kHighBit);
    179 #else
    180    return reinterpret_cast<OutOfLineCache*>(mOutOfLine);
    181 #endif
    182  }
    183 };
    184 
    185 static_assert(sizeof(IntrinsicISizesCache) == 8, "Unexpected cache size");
    186 
    187 }  // namespace mozilla
    188 
    189 #endif