Bug 1823215 - Extract ElementOrArray<T> 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
This commit is contained in:
Jonathan Kew 2023-03-19 16:36:05 +00:00
parent dfac3c918a
commit ce6caaeacf
2 changed files with 101 additions and 100 deletions

View File

@ -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<T>.
// 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 <typename T>
class ElementOrArray {
union {
T mElement;
nsTArray<T> mArray;
};
enum class Tag : uint8_t {
Element,
Array,
} mTag;
public:
// Construct as an empty array.
ElementOrArray() : mTag(Tag::Array) { new (&mArray) nsTArray<T>(); }
// 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<T>();
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();

View File

@ -417,6 +417,106 @@ class gfxUtils {
namespace mozilla {
// Container for either a single element of type T, or an nsTArray<T>.
// 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 <typename T>
class ElementOrArray {
union {
T mElement;
nsTArray<T> mArray;
};
enum class Tag : uint8_t {
Element,
Array,
} mTag;
public:
// Construct as an empty array.
ElementOrArray() : mTag(Tag::Array) { new (&mArray) nsTArray<T>(); }
// 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<T>();
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 {