From ce6caaeacff9e6589b6add8a1a9afffadacf6dd9 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Sun, 19 Mar 2023 16:36:05 +0000 Subject: [PATCH] Bug 1823215 - Extract ElementOrArray from CanvasRenderingContext2D into gfxUtils.h so it can also be used elsewhere in gfx/thebes. r=gfx-reviewers,lsalzman Differential Revision: https://phabricator.services.mozilla.com/D172938 --- dom/canvas/CanvasRenderingContext2D.h | 101 +------------------------- gfx/thebes/gfxUtils.h | 100 +++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 100 deletions(-) diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index f83d44f4a350..6c7d1da80e6f 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -24,6 +24,7 @@ #include "FilterDescription.h" #include "gfx2DGlue.h" #include "gfxFontConstants.h" +#include "gfxUtils.h" #include "nsICanvasRenderingContextInternal.h" #include "nsColor.h" #include "nsIFrame.h" @@ -78,106 +79,6 @@ struct DOMMatrix2DInit; class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal, public nsWrapperCache, public BasicRenderingContext2D { - // Container for either a single element of type T, or an nsTArray. - // Provides a minimal subset of nsTArray's API, just enough to support use - // by ContextState for the clipsAndTransforms list. - // Using this instead of a simple nsTArray avoids an extra allocation in the - // common case where no more than one element is ever added to the list. - // Unlike an AutoTArray<..., 1>, this class is memmovable and therefore can - // be used in ContextState without breaking its movability. - template - class ElementOrArray { - union { - T mElement; - nsTArray mArray; - }; - enum class Tag : uint8_t { - Element, - Array, - } mTag; - - public: - // Construct as an empty array. - ElementOrArray() : mTag(Tag::Array) { new (&mArray) nsTArray(); } - - // For now, don't support copy/move. - ElementOrArray(const ElementOrArray&) = delete; - ElementOrArray(ElementOrArray&&) = delete; - - ElementOrArray& operator=(const ElementOrArray&) = delete; - ElementOrArray& operator=(ElementOrArray&&) = delete; - - // Destroy the appropriate variant. - ~ElementOrArray() { - switch (mTag) { - case Tag::Element: - mElement.~T(); - break; - case Tag::Array: - mArray.~nsTArray(); - break; - } - } - - size_t Length() const { return mTag == Tag::Element ? 1 : mArray.Length(); } - - T* AppendElement(const T& aElement) { - switch (mTag) { - case Tag::Element: { - // Move the existing element into an array, then append the new one. - T temp = std::move(mElement); - mElement.~T(); - mTag = Tag::Array; - new (&mArray) nsTArray(); - mArray.AppendElement(std::move(temp)); - return mArray.AppendElement(aElement); - } - case Tag::Array: { - // If currently empty, just store the element directly. - if (mArray.IsEmpty()) { - mArray.~nsTArray(); - mTag = Tag::Element; - new (&mElement) T(aElement); - return &mElement; - } - // Otherwise, append it to the array. - return mArray.AppendElement(aElement); - } - default: - MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("invalid tag"); - } - } - - const T& LastElement() const { - return mTag == Tag::Element ? mElement : mArray.LastElement(); - } - - T& LastElement() { - return mTag == Tag::Element ? mElement : mArray.LastElement(); - } - - bool IsEmpty() const { - return mTag == Tag::Element ? false : mArray.IsEmpty(); - } - - // Simple iterators to support range-for loops. - const T* begin() const { - return mTag == Tag::Array ? mArray.IsEmpty() ? nullptr : &*mArray.begin() - : &mElement; - } - T* begin() { - return mTag == Tag::Array ? mArray.IsEmpty() ? nullptr : &*mArray.begin() - : &mElement; - } - - const T* end() const { - return mTag == Tag::Array ? begin() + mArray.Length() : &mElement + 1; - } - T* end() { - return mTag == Tag::Array ? begin() + mArray.Length() : &mElement + 1; - } - }; - protected: virtual ~CanvasRenderingContext2D(); diff --git a/gfx/thebes/gfxUtils.h b/gfx/thebes/gfxUtils.h index 1eefea051ddd..a1854cbbcd9d 100644 --- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -417,6 +417,106 @@ class gfxUtils { namespace mozilla { +// Container for either a single element of type T, or an nsTArray. +// Provides a minimal subset of nsTArray's API, just enough to support use +// by ContextState for the clipsAndTransforms list. +// Using this instead of a simple nsTArray avoids an extra allocation in the +// common case where no more than one element is ever added to the list. +// Unlike an AutoTArray<..., 1>, this class is memmovable and therefore can +// be used in ContextState without breaking its movability. +template +class ElementOrArray { + union { + T mElement; + nsTArray mArray; + }; + enum class Tag : uint8_t { + Element, + Array, + } mTag; + + public: + // Construct as an empty array. + ElementOrArray() : mTag(Tag::Array) { new (&mArray) nsTArray(); } + + // For now, don't support copy/move. + ElementOrArray(const ElementOrArray&) = delete; + ElementOrArray(ElementOrArray&&) = delete; + + ElementOrArray& operator=(const ElementOrArray&) = delete; + ElementOrArray& operator=(ElementOrArray&&) = delete; + + // Destroy the appropriate variant. + ~ElementOrArray() { + switch (mTag) { + case Tag::Element: + mElement.~T(); + break; + case Tag::Array: + mArray.~nsTArray(); + break; + } + } + + size_t Length() const { return mTag == Tag::Element ? 1 : mArray.Length(); } + + T* AppendElement(const T& aElement) { + switch (mTag) { + case Tag::Element: { + // Move the existing element into an array, then append the new one. + T temp = std::move(mElement); + mElement.~T(); + mTag = Tag::Array; + new (&mArray) nsTArray(); + mArray.AppendElement(std::move(temp)); + return mArray.AppendElement(aElement); + } + case Tag::Array: { + // If currently empty, just store the element directly. + if (mArray.IsEmpty()) { + mArray.~nsTArray(); + mTag = Tag::Element; + new (&mElement) T(aElement); + return &mElement; + } + // Otherwise, append it to the array. + return mArray.AppendElement(aElement); + } + default: + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("invalid tag"); + } + } + + const T& LastElement() const { + return mTag == Tag::Element ? mElement : mArray.LastElement(); + } + + T& LastElement() { + return mTag == Tag::Element ? mElement : mArray.LastElement(); + } + + bool IsEmpty() const { + return mTag == Tag::Element ? false : mArray.IsEmpty(); + } + + // Simple iterators to support range-for loops. + const T* begin() const { + return mTag == Tag::Array ? mArray.IsEmpty() ? nullptr : &*mArray.begin() + : &mElement; + } + T* begin() { + return mTag == Tag::Array ? mArray.IsEmpty() ? nullptr : &*mArray.begin() + : &mElement; + } + + const T* end() const { + return mTag == Tag::Array ? begin() + mArray.Length() : &mElement + 1; + } + T* end() { + return mTag == Tag::Array ? begin() + mArray.Length() : &mElement + 1; + } +}; + struct StyleAbsoluteColor; namespace gfx {