diff --git a/Source/engine/cel_sprite.hpp b/Source/engine/cel_sprite.hpp index ea849815f..eaf4f7bc3 100644 --- a/Source/engine/cel_sprite.hpp +++ b/Source/engine/cel_sprite.hpp @@ -3,6 +3,7 @@ #include #include +#include "utils/pointer_value_union.hpp" #include "utils/stdcompat/cstddef.hpp" namespace devilution { @@ -23,7 +24,7 @@ public: CelSprite(const byte *data, const uint16_t *widths) : data_ptr_(data) - , widths_(widths) + , width_(widths) { } @@ -41,7 +42,7 @@ public: [[nodiscard]] uint16_t Width(std::size_t frame = 1) const { - return widths_ == nullptr ? width_ : widths_[frame]; + return width_.HoldsPointer() ? width_.AsPointer()[frame] : width_.AsValue(); } [[nodiscard]] bool operator==(CelSprite other) const @@ -55,8 +56,7 @@ public: private: const byte *data_ptr_; - uint16_t width_ = 0; - const uint16_t *widths_ = nullptr; // unowned + PointerOrValue width_; }; /** diff --git a/Source/utils/pointer_value_union.hpp b/Source/utils/pointer_value_union.hpp new file mode 100644 index 000000000..4ad6f5740 --- /dev/null +++ b/Source/utils/pointer_value_union.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace devilution { + +/** + * @brief A tagged union of a pointer and a value in the space of a single pointer. + * + * Requires the type T to have alignment > 1. + * Internally, uses the last bit to hold the tag: + * 0 if it is a pointer, guaranteed by T's alignment requirements. + * 1 if it is a value. + */ +template +class PointerOrValue { + static_assert(alignof(T) > 1, "requires alignof > 1"); + static_assert(sizeof(T) < sizeof(T *), "type too large"); + +public: + explicit PointerOrValue(const T *ptr) + : repr_(reinterpret_cast(ptr)) + { + } + + explicit PointerOrValue(T val) + : repr_((static_cast(val) << 1) | 1) + { + } + + [[nodiscard]] bool HoldsPointer() const + { + return (repr_ & 1) == 0; + } + + [[nodiscard]] const T *AsPointer() const + { + return reinterpret_cast(repr_); + } + + [[nodiscard]] T AsValue() const + { + return repr_ >> 1; + } + +private: + uintptr_t repr_; +}; + +} // namespace devilution