514 lines
19 KiB
C++
514 lines
19 KiB
C++
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef STAGEFRIGHT_FOUNDATION_AML_MP_FLAGGED_H_
|
|
#define STAGEFRIGHT_FOUNDATION_AML_MP_FLAGGED_H_
|
|
|
|
#include "AmlMpTypeTraits.h"
|
|
|
|
namespace aml_mp {
|
|
|
|
/**
|
|
* Flagged<T, Flag> is basically a specialized std::pair<Flag, T> that automatically optimizes out
|
|
* the flag if the wrapped type T is already flagged and we can combine the outer and inner flags.
|
|
*
|
|
* Flags can be queried/manipulated via flags() an setFlags(Flags). The wrapped value can be
|
|
* accessed via get(). This template is meant to be inherited by other utility/wrapper classes
|
|
* that need to store integral information along with the value.
|
|
*
|
|
* Users must specify the used bits (MASK) in the flags. Flag getters and setters will enforce this
|
|
* mask. _Flagged_helper::minMask<Flag> is provided to easily calculate a mask for a max value.
|
|
*
|
|
* E.g. adding a safe flag can be achieved like this:
|
|
*
|
|
*
|
|
* enum SafeFlags : uint32_t {
|
|
* kUnsafe,
|
|
* kSafe,
|
|
* kSafeMask = _Flagged_helper::minMask(kSafe),
|
|
* };
|
|
* typedef Flagged<int32_t, SafeFlags, kSafeMask> safeInt32;
|
|
*
|
|
* safeInt32 a;
|
|
* a.setFlags(kSafe);
|
|
* a.get() = 15;
|
|
* EXPECT_EQ(a.flags(), kSafe);
|
|
* EXPECT_EQ(a.get(), 15);
|
|
*
|
|
*
|
|
* Flagged also supports lazy or calculated wrapping of already flagged types. Lazy wrapping is
|
|
* provided automatically (flags are automatically shared if possible, e.g. mask is shifted
|
|
* automatically to not overlap with used bits of the wrapped type's flags, and fall back to
|
|
* unshared version of the template.):
|
|
*
|
|
* enum OriginFlags : uint32_t {
|
|
* kUnknown,
|
|
* kConst,
|
|
* kCalculated,
|
|
* kComponent,
|
|
* kApplication,
|
|
* kFile,
|
|
* kBinder,
|
|
* kOriginMask = _Flagged_helper::minMask(kBinder),
|
|
* };
|
|
* typedef Flagged<safeInt32, OriginFlags, kOriginMask>
|
|
* trackedSafeInt32;
|
|
*
|
|
* static_assert(sizeof(trackedSafeInt32) == sizeof(safeInt32), "");
|
|
*
|
|
* trackedSafeInt32 b(kConst, kSafe, 1);
|
|
* EXPECT_EQ(b.flags(), kConst);
|
|
* EXPECT_EQ(b.get().flags(), kSafe);
|
|
* EXPECT_EQ(b.get().get(), 1);
|
|
* b.setFlags(kCalculated);
|
|
* b.get().setFlags(overflow ? kUnsafe : kSafe);
|
|
*
|
|
* One can also choose to share some flag-bits with the wrapped class:
|
|
*
|
|
* enum ValidatedFlags : uint32_t {
|
|
* kUnsafeV = kUnsafe,
|
|
* kSafeV = kSafe,
|
|
* kValidated = kSafe | 2,
|
|
* kSharedMaskV = kSafeMask,
|
|
* kValidatedMask = _Flagged_helper::minMask(kValidated),
|
|
* };
|
|
* typedef Flagged<safeInt32, ValidatedFlags, kValidatedMask, kSharedMaskV> validatedInt32;
|
|
*
|
|
* validatedInt32 v(kUnsafeV, kSafe, 10);
|
|
* EXPECT_EQ(v.flags(), kUnsafeV);
|
|
* EXPECT_EQ(v.get().flags(), kUnsafe); // !kUnsafeV overrides kSafe
|
|
* EXPECT_EQ(v.get().get(), 10);
|
|
* v.setFlags(kValidated);
|
|
* EXPECT_EQ(v.flags(), kValidated);
|
|
* EXPECT_EQ(v.get().flags(), kSafe);
|
|
* v.get().setFlags(kUnsafe);
|
|
* EXPECT_EQ(v.flags(), 2); // NOTE: sharing masks with enums allows strange situations to occur
|
|
*/
|
|
|
|
/**
|
|
* Helper class for Flagged support. Encapsulates common utilities used by all
|
|
* templated classes.
|
|
*/
|
|
struct _Flagged_helper {
|
|
/**
|
|
* Calculates the value with a given number of top-most bits set.
|
|
*
|
|
* This method may be called with a signed flag.
|
|
*
|
|
* \param num number of bits to set. This must be between 0 and the number of bits in Flag.
|
|
*
|
|
* \return the value where only the given number of top-most bits are set.
|
|
*/
|
|
template<typename Flag>
|
|
static constexpr Flag topBits(int num) {
|
|
return Flag(num > 0 ?
|
|
~((Flag(1) << (sizeof(Flag) * 8 - is_signed_integral<Flag>::value - num)) - 1) :
|
|
0);
|
|
}
|
|
|
|
/**
|
|
* Calculates the minimum mask required to cover a value. Used with the maximum enum value for
|
|
* an unsigned flag.
|
|
*
|
|
* \param maxValue maximum value to cover
|
|
* \param shift DO NO USE. used internally
|
|
*
|
|
* \return mask that can be used that covers the maximum value.
|
|
*/
|
|
template<typename Flag>
|
|
static constexpr Flag minMask(Flag maxValue, int shift=sizeof(Flag) * 4) {
|
|
static_assert(is_unsigned_integral<Flag>::value,
|
|
"this method only makes sense for unsigned flags");
|
|
return shift ? minMask<Flag>(Flag(maxValue | (maxValue >> shift)), shift >> 1) : maxValue;
|
|
}
|
|
|
|
/**
|
|
* Returns a value left-shifted by an argument as a potential constexpr.
|
|
*
|
|
* This method helps around the C-language limitation, when left-shift of a negative value with
|
|
* even 0 cannot be a constexpr.
|
|
*
|
|
* \param value value to shift
|
|
* \param shift amount of shift
|
|
* \returns the shifted value as an integral type
|
|
*/
|
|
template<typename Flag, typename IntFlag = typename underlying_integral_type<Flag>::type>
|
|
static constexpr IntFlag lshift(Flag value, int shift) {
|
|
return shift ? value << shift : value;
|
|
}
|
|
|
|
private:
|
|
|
|
/**
|
|
* Determines whether mask can be combined with base-mask for a given left shift.
|
|
*
|
|
* \param mask desired mask
|
|
* \param baseMask mask used by T or 0 if T is not flagged by Flag
|
|
* \param sharedMask desired shared mask (if this is non-0, this must be mask & baseMask)
|
|
* \param shift desired left shift to be used for mask
|
|
* \param baseShift left shift used by T or 0 if T is not flagged by Flag
|
|
* \param effectiveMask effective mask used by T or 0 if T is not flagged by Flag
|
|
*
|
|
* \return bool whether mask can be combined with baseMask using the desired values.
|
|
*/
|
|
template<typename Flag, typename IntFlag=typename underlying_integral_type<Flag>::type>
|
|
static constexpr bool canCombine(
|
|
Flag mask, IntFlag baseMask, Flag sharedMask, int shift,
|
|
int baseShift, IntFlag effectiveMask) {
|
|
return
|
|
// verify that shift is valid and mask can be shifted
|
|
shift >= 0 && (mask & topBits<Flag>(shift)) == 0 &&
|
|
|
|
// verify that base mask is part of effective mask (sanity check on arguments)
|
|
(baseMask & ~(effectiveMask >> baseShift)) == 0 &&
|
|
|
|
// if sharing masks, shift must be the base's shift.
|
|
// verify that shared mask is the overlap of base mask and mask
|
|
(sharedMask ?
|
|
((sharedMask ^ (baseMask & mask)) == 0 &&
|
|
shift == baseShift) :
|
|
|
|
|
|
// otherwise, verify that there is no overlap between mask and base's effective mask
|
|
(mask & (effectiveMask >> shift)) == 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Calculates the minimum (left) shift required to combine a mask with the mask of an
|
|
* underlying type (T, also flagged by Flag).
|
|
*
|
|
* \param mask desired mask
|
|
* \param baseMask mask used by T or 0 if T is not flagged by Flag
|
|
* \param sharedMask desired shared mask (if this is non-0, this must be mask & baseMask)
|
|
* \param baseShift left shift used by T
|
|
* \param effectiveMask effective mask used by T
|
|
*
|
|
* \return a non-negative minimum left shift value if mask can be combined with baseMask,
|
|
* or -1 if the masks cannot be combined. -2 if the input is invalid.
|
|
*/
|
|
template<typename Flag,
|
|
typename IntFlag = typename underlying_integral_type<Flag>::type>
|
|
static constexpr int getShift(
|
|
Flag mask, IntFlag baseMask, Flag sharedMask, int baseShift, IntFlag effectiveMask) {
|
|
return
|
|
// baseMask must be part of the effective mask
|
|
(baseMask & ~(effectiveMask >> baseShift)) ? -2 :
|
|
|
|
// if sharing masks, shift must be base's shift. verify that shared mask is part of
|
|
// base mask and mask, and that desired mask still fits with base's shift value
|
|
sharedMask ?
|
|
(canCombine(mask, baseMask, sharedMask, baseShift /* shift */,
|
|
baseShift, effectiveMask) ? baseShift : -1) :
|
|
|
|
// otherwise, see if 0-shift works
|
|
((mask & effectiveMask) == 0) ? 0 :
|
|
|
|
// otherwise, verify that mask can be shifted up
|
|
((mask & topBits<Flag>(1)) || (mask < 0)) ? -1 :
|
|
|
|
incShift(getShift(Flag(mask << 1), baseMask /* unused */, sharedMask /* 0 */,
|
|
baseShift /* unused */, effectiveMask));
|
|
}
|
|
|
|
/**
|
|
* Helper method that increments a non-negative (shift) value.
|
|
*
|
|
* This method is used to make it easier to create a constexpr for getShift.
|
|
*
|
|
* \param shift (shift) value to increment
|
|
*
|
|
* \return original shift if it was negative; otherwise, the shift incremented by one.
|
|
*/
|
|
static constexpr int incShift(int shift) {
|
|
return shift + (shift >= 0);
|
|
}
|
|
|
|
#ifdef FRIEND_TEST
|
|
FRIEND_TEST(FlaggedTest, _Flagged_helper_Test);
|
|
#endif
|
|
|
|
public:
|
|
/**
|
|
* Base class for all Flagged<T, Flag> classes.
|
|
*
|
|
* \note flagged types do not have a member variable for the mask used by the type. As such,
|
|
* they should be be cast to this base class.
|
|
*
|
|
* \todo can we replace this base class check with a static member check to remove possibility
|
|
* of cast?
|
|
*/
|
|
template<typename Flag>
|
|
struct base {};
|
|
|
|
/**
|
|
* Type support utility that retrieves the mask of a class (T) if it is a type flagged by
|
|
* Flag (e.g. Flagged<T, Flag>).
|
|
*
|
|
* \note This retrieves 0 if T is a flagged class, that is not flagged by Flag or an equivalent
|
|
* underlying type.
|
|
*
|
|
* Generic implementation for a non-flagged class.
|
|
*/
|
|
template<
|
|
typename T, typename Flag,
|
|
bool=std::is_base_of<base<typename underlying_integral_type<Flag>::type>, T>::value>
|
|
struct mask_of {
|
|
using IntFlag = typename underlying_integral_type<Flag>::type;
|
|
static constexpr IntFlag value = Flag(0); ///< mask of a potentially flagged class
|
|
static constexpr int shift = 0; ///<left shift of flags in a potentially flagged class
|
|
static constexpr IntFlag effective_value = IntFlag(0); ///<effective mask of flagged class
|
|
};
|
|
|
|
/**
|
|
* Type support utility that calculates the minimum (left) shift required to combine a mask
|
|
* with the mask of an underlying type T also flagged by Flag.
|
|
*
|
|
* \note if T is not flagged, not flagged by Flag, or the masks cannot be combined due to
|
|
* incorrect sharing or the flags not having enough bits, the minimum is -1.
|
|
*
|
|
* \param MASK desired mask
|
|
* \param SHARED_MASK desired shared mask (if this is non-0, T must be an type flagged by
|
|
* Flag with a mask that has exactly these bits common with MASK)
|
|
*/
|
|
template<typename T, typename Flag, Flag MASK, Flag SHARED_MASK>
|
|
struct min_shift {
|
|
/// minimum (left) shift required, or -1 if masks cannot be combined
|
|
static constexpr int value =
|
|
getShift(MASK, mask_of<T, Flag>::value, SHARED_MASK,
|
|
mask_of<T, Flag>::shift, mask_of<T, Flag>::effective_value);
|
|
};
|
|
|
|
/**
|
|
* Type support utility that calculates whether the flags of T can be combined with MASK.
|
|
*
|
|
* \param MASK desired mask
|
|
* \param SHARED_MASK desired shared mask (if this is non-0, T MUST be an type flagged by
|
|
* Flag with a mask that has exactly these bits common with MASK)
|
|
*/
|
|
template<
|
|
typename T, typename Flag, Flag MASK,
|
|
Flag SHARED_MASK=Flag(0),
|
|
int SHIFT=min_shift<T, Flag, MASK, SHARED_MASK>::value>
|
|
struct can_combine {
|
|
using IntFlag = typename underlying_integral_type<Flag>::type;
|
|
/// true if this mask can be combined with T's existing flag. false otherwise.
|
|
static constexpr bool value =
|
|
std::is_base_of<base<IntFlag>, T>::value
|
|
&& canCombine(MASK, mask_of<T, Flag>::value, SHARED_MASK, SHIFT,
|
|
mask_of<T, Flag>::shift, mask_of<T, Flag>::effective_value);
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Template specialization for the case when T is flagged by Flag or a compatible type.
|
|
*/
|
|
template<typename T, typename Flag>
|
|
struct _Flagged_helper::mask_of<T, Flag, true> {
|
|
using IntType = typename underlying_integral_type<Flag>::type;
|
|
static constexpr IntType value = T::sFlagMask;
|
|
static constexpr int shift = T::sFlagShift;
|
|
static constexpr IntType effective_value = T::sEffectiveMask;
|
|
};
|
|
|
|
/**
|
|
* Main Flagged template that adds flags to an object of another type (in essence, creates a pair)
|
|
*
|
|
* Flag must be an integral type (enums are allowed).
|
|
*
|
|
* \note We could make SHARED_MASK be a boolean as it must be either 0 or MASK & base's mask, but we
|
|
* want it to be spelled out for safety.
|
|
*
|
|
* \param T type of object wrapped
|
|
* \param Flag type of flag
|
|
* \param MASK mask for the bits used in flag (before any shift)
|
|
* \param SHARED_MASK optional mask to be shared with T (if this is not zero, SHIFT must be 0, and
|
|
* it must equal to MASK & T's mask)
|
|
* \param SHIFT optional left shift for MASK to combine with T's mask (or -1, if masks should not
|
|
* be combined.)
|
|
*/
|
|
template<
|
|
typename T, typename Flag, Flag MASK, Flag SHARED_MASK=(Flag)0,
|
|
int SHIFT=_Flagged_helper::min_shift<T, Flag, MASK, SHARED_MASK>::value,
|
|
typename IntFlag=typename underlying_integral_type<Flag>::type,
|
|
bool=_Flagged_helper::can_combine<T, IntFlag, MASK, SHARED_MASK, SHIFT>::value>
|
|
class Flagged : public _Flagged_helper::base<IntFlag> {
|
|
static_assert(SHARED_MASK == 0,
|
|
"shared mask can only be used with common flag types "
|
|
"and must be part of mask and mask of base type");
|
|
static_assert((_Flagged_helper::topBits<Flag>(SHIFT) & MASK) == 0, "SHIFT overflows MASK");
|
|
|
|
static constexpr Flag sFlagMask = MASK; ///< the mask
|
|
static constexpr int sFlagShift = SHIFT > 0 ? SHIFT : 0; ///< the left shift applied to flags
|
|
|
|
friend struct _Flagged_helper;
|
|
#ifdef FRIEND_TEST
|
|
static constexpr bool sFlagCombined = false;
|
|
FRIEND_TEST(FlaggedTest, _Flagged_helper_Test);
|
|
#endif
|
|
|
|
T mValue; ///< wrapped value
|
|
IntFlag mFlags; ///< flags
|
|
|
|
protected:
|
|
/// The effective combined mask used by this class and any wrapped classes if the flags are
|
|
/// combined.
|
|
static constexpr IntFlag sEffectiveMask = _Flagged_helper::lshift(MASK, sFlagShift);
|
|
|
|
/**
|
|
* Helper method used by subsequent flagged wrappers to query flags. Returns the
|
|
* flags for a particular mask and left shift.
|
|
*
|
|
* \param mask bitmask to use
|
|
* \param shift left shifts to use
|
|
*
|
|
* \return the requested flags
|
|
*/
|
|
inline constexpr IntFlag getFlagsHelper(IntFlag mask, int shift) const {
|
|
return (mFlags >> shift) & mask;
|
|
}
|
|
|
|
/**
|
|
* Helper method used by subsequent flagged wrappers to apply combined flags. Sets the flags
|
|
* in the bitmask using a particulare left shift.
|
|
*
|
|
* \param mask bitmask to use
|
|
* \param shift left shifts to use
|
|
* \param flags flags to update (any flags within the bitmask are updated to their value in this
|
|
* argument)
|
|
*/
|
|
inline void setFlagsHelper(IntFlag mask, int shift, IntFlag flags) {
|
|
mFlags = Flag((mFlags & ~(mask << shift)) | ((flags & mask) << shift));
|
|
}
|
|
|
|
public:
|
|
/**
|
|
* Wrapper around base class constructor. These take the flags as their first
|
|
* argument and pass the rest of the arguments to the base class constructor.
|
|
*
|
|
* \param flags initial flags
|
|
*/
|
|
template<typename ...Args>
|
|
constexpr Flagged(Flag flags, Args... args)
|
|
: mValue(std::forward<Args>(args)...),
|
|
mFlags(Flag(_Flagged_helper::lshift(flags & sFlagMask, sFlagShift))) { }
|
|
|
|
/** Gets the wrapped value as const. */
|
|
inline constexpr const T &get() const { return mValue; }
|
|
|
|
/** Gets the wrapped value. */
|
|
inline T &get() { return mValue; }
|
|
|
|
/** Gets the flags. */
|
|
constexpr Flag flags() const {
|
|
return Flag(getFlagsHelper(sFlagMask, sFlagShift));
|
|
}
|
|
|
|
/** Sets the flags. */
|
|
void setFlags(Flag flags) {
|
|
setFlagsHelper(sFlagMask, sFlagShift, flags);
|
|
}
|
|
};
|
|
|
|
/*
|
|
* TRICKY: we cannot implement the specialization as:
|
|
*
|
|
* class Flagged : base<Flag> {
|
|
* T value;
|
|
* };
|
|
*
|
|
* Because T also inherits from base<Flag> and this runs into a compiler bug where
|
|
* sizeof(Flagged) > sizeof(T).
|
|
*
|
|
* Instead, we must inherit directly from the wrapped class
|
|
*
|
|
*/
|
|
#if 0
|
|
template<
|
|
typename T, typename Flag, Flag MASK, Flag SHARED_MASK, int SHIFT>
|
|
class Flagged<T, Flag, MASK, SHARED_MASK, SHIFT, true> : public _Flagged_helper::base<Flag> {
|
|
private:
|
|
T mValue;
|
|
};
|
|
#else
|
|
/**
|
|
* Specialization for the case when T is derived from Flagged<U, Flag> and flags can be combined.
|
|
*/
|
|
template<
|
|
typename T, typename Flag, Flag MASK, Flag SHARED_MASK, int SHIFT, typename IntFlag>
|
|
class Flagged<T, Flag, MASK, SHARED_MASK, SHIFT, IntFlag, true> : private T {
|
|
static_assert(is_integral_or_enum<Flag>::value, "flag must be integer or enum");
|
|
|
|
static_assert(SHARED_MASK == 0 || SHIFT == 0, "cannot overlap masks when using SHIFT");
|
|
static_assert((SHARED_MASK & ~MASK) == 0, "shared mask must be part of the mask");
|
|
static_assert((SHARED_MASK & ~T::sEffectiveMask) == 0,
|
|
"shared mask must be part of the base mask");
|
|
static_assert(SHARED_MASK == 0 || (~SHARED_MASK & (MASK & T::sEffectiveMask)) == 0,
|
|
"mask and base mask can only overlap in shared mask");
|
|
|
|
static constexpr Flag sFlagMask = MASK; ///< the mask
|
|
static constexpr int sFlagShift = SHIFT; ///< the left shift applied to the flags
|
|
|
|
#ifdef FRIEND_TEST
|
|
const static bool sFlagCombined = true;
|
|
FRIEND_TEST(FlaggedTest, _Flagged_helper_Test);
|
|
#endif
|
|
|
|
protected:
|
|
/// The effective combined mask used by this class and any wrapped classes if the flags are
|
|
/// combined.
|
|
static constexpr IntFlag sEffectiveMask = Flag((MASK << SHIFT) | T::sEffectiveMask);
|
|
friend struct _Flagged_helper;
|
|
|
|
public:
|
|
/**
|
|
* Wrapper around base class constructor. These take the flags as their first
|
|
* argument and pass the rest of the arguments to the base class constructor.
|
|
*
|
|
* \param flags initial flags
|
|
*/
|
|
template<typename ...Args>
|
|
constexpr Flagged(Flag flags, Args... args)
|
|
: T(std::forward<Args>(args)...) {
|
|
// we construct the base class first and apply the flags afterwards as
|
|
// base class may not have a constructor that takes flags even if it is derived from
|
|
// Flagged<U, Flag>
|
|
setFlags(flags);
|
|
}
|
|
|
|
/** Gets the wrapped value as const. */
|
|
inline constexpr T &get() const { return *this; }
|
|
|
|
/** Gets the wrapped value. */
|
|
inline T &get() { return *this; }
|
|
|
|
/** Gets the flags. */
|
|
Flag constexpr flags() const {
|
|
return Flag(this->getFlagsHelper(sFlagMask, sFlagShift));
|
|
}
|
|
|
|
/** Sets the flags. */
|
|
void setFlags(Flag flags) {
|
|
this->setFlagsHelper(sFlagMask, sFlagShift, flags);
|
|
}
|
|
};
|
|
#endif
|
|
|
|
} // namespace android
|
|
|
|
#endif // STAGEFRIGHT_FOUNDATION_FLAGGED_H_
|
|
|