1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef STAGEFRIGHT_FOUNDATION_FLAGGED_H_ 18 #define STAGEFRIGHT_FOUNDATION_FLAGGED_H_ 19 20 #include <media/stagefright/foundation/TypeTraits.h> 21 22 namespace android { 23 24 /** 25 * Flagged<T, Flag> is basically a specialized std::pair<Flag, T> that automatically optimizes out 26 * the flag if the wrapped type T is already flagged and we can combine the outer and inner flags. 27 * 28 * Flags can be queried/manipulated via flags() an setFlags(Flags). The wrapped value can be 29 * accessed via get(). This template is meant to be inherited by other utility/wrapper classes 30 * that need to store integral information along with the value. 31 * 32 * Users must specify the used bits (MASK) in the flags. Flag getters and setters will enforce this 33 * mask. _Flagged_helper::minMask<Flag> is provided to easily calculate a mask for a max value. 34 * 35 * E.g. adding a safe flag can be achieved like this: 36 * 37 * 38 * enum SafeFlags : uint32_t { 39 * kUnsafe, 40 * kSafe, 41 * kSafeMask = _Flagged_helper::minMask(kSafe), 42 * }; 43 * typedef Flagged<int32_t, SafeFlags, kSafeMask> safeInt32; 44 * 45 * safeInt32 a; 46 * a.setFlags(kSafe); 47 * a.get() = 15; 48 * EXPECT_EQ(a.flags(), kSafe); 49 * EXPECT_EQ(a.get(), 15); 50 * 51 * 52 * Flagged also supports lazy or calculated wrapping of already flagged types. Lazy wrapping is 53 * provided automatically (flags are automatically shared if possible, e.g. mask is shifted 54 * automatically to not overlap with used bits of the wrapped type's flags, and fall back to 55 * unshared version of the template.): 56 * 57 * enum OriginFlags : uint32_t { 58 * kUnknown, 59 * kConst, 60 * kCalculated, 61 * kComponent, 62 * kApplication, 63 * kFile, 64 * kBinder, 65 * kOriginMask = _Flagged_helper::minMask(kBinder), 66 * }; 67 * typedef Flagged<safeInt32, OriginFlags, kOriginMask> 68 * trackedSafeInt32; 69 * 70 * static_assert(sizeof(trackedSafeInt32) == sizeof(safeInt32), ""); 71 * 72 * trackedSafeInt32 b(kConst, kSafe, 1); 73 * EXPECT_EQ(b.flags(), kConst); 74 * EXPECT_EQ(b.get().flags(), kSafe); 75 * EXPECT_EQ(b.get().get(), 1); 76 * b.setFlags(kCalculated); 77 * b.get().setFlags(overflow ? kUnsafe : kSafe); 78 * 79 * One can also choose to share some flag-bits with the wrapped class: 80 * 81 * enum ValidatedFlags : uint32_t { 82 * kUnsafeV = kUnsafe, 83 * kSafeV = kSafe, 84 * kValidated = kSafe | 2, 85 * kSharedMaskV = kSafeMask, 86 * kValidatedMask = _Flagged_helper::minMask(kValidated), 87 * }; 88 * typedef Flagged<safeInt32, ValidatedFlags, kValidatedMask, kSharedMaskV> validatedInt32; 89 * 90 * validatedInt32 v(kUnsafeV, kSafe, 10); 91 * EXPECT_EQ(v.flags(), kUnsafeV); 92 * EXPECT_EQ(v.get().flags(), kUnsafe); // !kUnsafeV overrides kSafe 93 * EXPECT_EQ(v.get().get(), 10); 94 * v.setFlags(kValidated); 95 * EXPECT_EQ(v.flags(), kValidated); 96 * EXPECT_EQ(v.get().flags(), kSafe); 97 * v.get().setFlags(kUnsafe); 98 * EXPECT_EQ(v.flags(), 2); // NOTE: sharing masks with enums allows strange situations to occur 99 */ 100 101 /** 102 * Helper class for Flagged support. Encapsulates common utilities used by all 103 * templated classes. 104 */ 105 struct _Flagged_helper { 106 /** 107 * Calculates the value with a given number of top-most bits set. 108 * 109 * This method may be called with a signed flag. 110 * 111 * \param num number of bits to set. This must be between 0 and the number of bits in Flag. 112 * 113 * \return the value where only the given number of top-most bits are set. 114 */ 115 template<typename Flag> topBits_Flagged_helper116 static constexpr Flag topBits(int num) { 117 return Flag(num > 0 ? 118 ~((Flag(1) << (sizeof(Flag) * 8 - is_signed_integral<Flag>::value - num)) - 1) : 119 0); 120 } 121 122 /** 123 * Calculates the minimum mask required to cover a value. Used with the maximum enum value for 124 * an unsigned flag. 125 * 126 * \param maxValue maximum value to cover 127 * \param shift DO NO USE. used internally 128 * 129 * \return mask that can be used that covers the maximum value. 130 */ 131 template<typename Flag> 132 static constexpr Flag minMask(Flag maxValue, int shift=sizeof(Flag) * 4) { 133 static_assert(is_unsigned_integral<Flag>::value, 134 "this method only makes sense for unsigned flags"); 135 return shift ? minMask<Flag>(Flag(maxValue | (maxValue >> shift)), shift >> 1) : maxValue; 136 } 137 138 /** 139 * Returns a value left-shifted by an argument as a potential constexpr. 140 * 141 * This method helps around the C-language limitation, when left-shift of a negative value with 142 * even 0 cannot be a constexpr. 143 * 144 * \param value value to shift 145 * \param shift amount of shift 146 * \returns the shifted value as an integral type 147 */ 148 template<typename Flag, typename IntFlag = typename underlying_integral_type<Flag>::type> lshift_Flagged_helper149 static constexpr IntFlag lshift(Flag value, int shift) { 150 return shift ? value << shift : value; 151 } 152 153 private: 154 155 /** 156 * Determines whether mask can be combined with base-mask for a given left shift. 157 * 158 * \param mask desired mask 159 * \param baseMask mask used by T or 0 if T is not flagged by Flag 160 * \param sharedMask desired shared mask (if this is non-0, this must be mask & baseMask) 161 * \param shift desired left shift to be used for mask 162 * \param baseShift left shift used by T or 0 if T is not flagged by Flag 163 * \param effectiveMask effective mask used by T or 0 if T is not flagged by Flag 164 * 165 * \return bool whether mask can be combined with baseMask using the desired values. 166 */ 167 template<typename Flag, typename IntFlag=typename underlying_integral_type<Flag>::type> canCombine_Flagged_helper168 static constexpr bool canCombine( 169 Flag mask, IntFlag baseMask, Flag sharedMask, int shift, 170 int baseShift, IntFlag effectiveMask) { 171 return 172 // verify that shift is valid and mask can be shifted 173 shift >= 0 && (mask & topBits<Flag>(shift)) == 0 && 174 175 // verify that base mask is part of effective mask (sanity check on arguments) 176 (baseMask & ~(effectiveMask >> baseShift)) == 0 && 177 178 // if sharing masks, shift must be the base's shift. 179 // verify that shared mask is the overlap of base mask and mask 180 (sharedMask ? 181 ((sharedMask ^ (baseMask & mask)) == 0 && 182 shift == baseShift) : 183 184 185 // otherwise, verify that there is no overlap between mask and base's effective mask 186 (mask & (effectiveMask >> shift)) == 0); 187 } 188 189 190 /** 191 * Calculates the minimum (left) shift required to combine a mask with the mask of an 192 * underlying type (T, also flagged by Flag). 193 * 194 * \param mask desired mask 195 * \param baseMask mask used by T or 0 if T is not flagged by Flag 196 * \param sharedMask desired shared mask (if this is non-0, this must be mask & baseMask) 197 * \param baseShift left shift used by T 198 * \param effectiveMask effective mask used by T 199 * 200 * \return a non-negative minimum left shift value if mask can be combined with baseMask, 201 * or -1 if the masks cannot be combined. -2 if the input is invalid. 202 */ 203 template<typename Flag, 204 typename IntFlag = typename underlying_integral_type<Flag>::type> getShift_Flagged_helper205 static constexpr int getShift( 206 Flag mask, IntFlag baseMask, Flag sharedMask, int baseShift, IntFlag effectiveMask) { 207 return 208 // baseMask must be part of the effective mask 209 (baseMask & ~(effectiveMask >> baseShift)) ? -2 : 210 211 // if sharing masks, shift must be base's shift. verify that shared mask is part of 212 // base mask and mask, and that desired mask still fits with base's shift value 213 sharedMask ? 214 (canCombine(mask, baseMask, sharedMask, baseShift /* shift */, 215 baseShift, effectiveMask) ? baseShift : -1) : 216 217 // otherwise, see if 0-shift works 218 ((mask & effectiveMask) == 0) ? 0 : 219 220 // otherwise, verify that mask can be shifted up 221 ((mask & topBits<Flag>(1)) || (mask < 0)) ? -1 : 222 223 incShift(getShift(Flag(mask << 1), baseMask /* unused */, sharedMask /* 0 */, 224 baseShift /* unused */, effectiveMask)); 225 } 226 227 /** 228 * Helper method that increments a non-negative (shift) value. 229 * 230 * This method is used to make it easier to create a constexpr for getShift. 231 * 232 * \param shift (shift) value to increment 233 * 234 * \return original shift if it was negative; otherwise, the shift incremented by one. 235 */ incShift_Flagged_helper236 static constexpr int incShift(int shift) { 237 return shift + (shift >= 0); 238 } 239 240 #ifdef FRIEND_TEST 241 FRIEND_TEST(FlaggedTest, _Flagged_helper_Test); 242 #endif 243 244 public: 245 /** 246 * Base class for all Flagged<T, Flag> classes. 247 * 248 * \note flagged types do not have a member variable for the mask used by the type. As such, 249 * they should be be cast to this base class. 250 * 251 * \todo can we replace this base class check with a static member check to remove possibility 252 * of cast? 253 */ 254 template<typename Flag> 255 struct base {}; 256 257 /** 258 * Type support utility that retrieves the mask of a class (T) if it is a type flagged by 259 * Flag (e.g. Flagged<T, Flag>). 260 * 261 * \note This retrieves 0 if T is a flagged class, that is not flagged by Flag or an equivalent 262 * underlying type. 263 * 264 * Generic implementation for a non-flagged class. 265 */ 266 template< 267 typename T, typename Flag, 268 bool=std::is_base_of<base<typename underlying_integral_type<Flag>::type>, T>::value> 269 struct mask_of { 270 using IntFlag = typename underlying_integral_type<Flag>::type; 271 static constexpr IntFlag value = Flag(0); ///< mask of a potentially flagged class 272 static constexpr int shift = 0; ///<left shift of flags in a potentially flagged class 273 static constexpr IntFlag effective_value = IntFlag(0); ///<effective mask of flagged class 274 }; 275 276 /** 277 * Type support utility that calculates the minimum (left) shift required to combine a mask 278 * with the mask of an underlying type T also flagged by Flag. 279 * 280 * \note if T is not flagged, not flagged by Flag, or the masks cannot be combined due to 281 * incorrect sharing or the flags not having enough bits, the minimum is -1. 282 * 283 * \param MASK desired mask 284 * \param SHARED_MASK desired shared mask (if this is non-0, T must be an type flagged by 285 * Flag with a mask that has exactly these bits common with MASK) 286 */ 287 template<typename T, typename Flag, Flag MASK, Flag SHARED_MASK> 288 struct min_shift { 289 /// minimum (left) shift required, or -1 if masks cannot be combined 290 static constexpr int value = 291 getShift(MASK, mask_of<T, Flag>::value, SHARED_MASK, 292 mask_of<T, Flag>::shift, mask_of<T, Flag>::effective_value); 293 }; 294 295 /** 296 * Type support utility that calculates whether the flags of T can be combined with MASK. 297 * 298 * \param MASK desired mask 299 * \param SHARED_MASK desired shared mask (if this is non-0, T MUST be an type flagged by 300 * Flag with a mask that has exactly these bits common with MASK) 301 */ 302 template< 303 typename T, typename Flag, Flag MASK, 304 Flag SHARED_MASK=Flag(0), 305 int SHIFT=min_shift<T, Flag, MASK, SHARED_MASK>::value> 306 struct can_combine { 307 using IntFlag = typename underlying_integral_type<Flag>::type; 308 /// true if this mask can be combined with T's existing flag. false otherwise. 309 static constexpr bool value = 310 std::is_base_of<base<IntFlag>, T>::value 311 && canCombine(MASK, mask_of<T, Flag>::value, SHARED_MASK, SHIFT, 312 mask_of<T, Flag>::shift, mask_of<T, Flag>::effective_value); 313 }; 314 }; 315 316 /** 317 * Template specialization for the case when T is flagged by Flag or a compatible type. 318 */ 319 template<typename T, typename Flag> 320 struct _Flagged_helper::mask_of<T, Flag, true> { 321 using IntType = typename underlying_integral_type<Flag>::type; 322 static constexpr IntType value = T::sFlagMask; 323 static constexpr int shift = T::sFlagShift; 324 static constexpr IntType effective_value = T::sEffectiveMask; 325 }; 326 327 /** 328 * Main Flagged template that adds flags to an object of another type (in essence, creates a pair) 329 * 330 * Flag must be an integral type (enums are allowed). 331 * 332 * \note We could make SHARED_MASK be a boolean as it must be either 0 or MASK & base's mask, but we 333 * want it to be spelled out for safety. 334 * 335 * \param T type of object wrapped 336 * \param Flag type of flag 337 * \param MASK mask for the bits used in flag (before any shift) 338 * \param SHARED_MASK optional mask to be shared with T (if this is not zero, SHIFT must be 0, and 339 * it must equal to MASK & T's mask) 340 * \param SHIFT optional left shift for MASK to combine with T's mask (or -1, if masks should not 341 * be combined.) 342 */ 343 template< 344 typename T, typename Flag, Flag MASK, Flag SHARED_MASK=(Flag)0, 345 int SHIFT=_Flagged_helper::min_shift<T, Flag, MASK, SHARED_MASK>::value, 346 typename IntFlag=typename underlying_integral_type<Flag>::type, 347 bool=_Flagged_helper::can_combine<T, IntFlag, MASK, SHARED_MASK, SHIFT>::value> 348 class Flagged : public _Flagged_helper::base<IntFlag> { 349 static_assert(SHARED_MASK == 0, 350 "shared mask can only be used with common flag types " 351 "and must be part of mask and mask of base type"); 352 static_assert((_Flagged_helper::topBits<Flag>(SHIFT) & MASK) == 0, "SHIFT overflows MASK"); 353 354 static constexpr Flag sFlagMask = MASK; ///< the mask 355 static constexpr int sFlagShift = SHIFT > 0 ? SHIFT : 0; ///< the left shift applied to flags 356 357 friend struct _Flagged_helper; 358 #ifdef FRIEND_TEST 359 static constexpr bool sFlagCombined = false; 360 FRIEND_TEST(FlaggedTest, _Flagged_helper_Test); 361 #endif 362 363 T mValue; ///< wrapped value 364 IntFlag mFlags; ///< flags 365 366 protected: 367 /// The effective combined mask used by this class and any wrapped classes if the flags are 368 /// combined. 369 static constexpr IntFlag sEffectiveMask = _Flagged_helper::lshift(MASK, sFlagShift); 370 371 /** 372 * Helper method used by subsequent flagged wrappers to query flags. Returns the 373 * flags for a particular mask and left shift. 374 * 375 * \param mask bitmask to use 376 * \param shift left shifts to use 377 * 378 * \return the requested flags 379 */ 380 inline constexpr IntFlag getFlagsHelper(IntFlag mask, int shift) const { 381 return (mFlags >> shift) & mask; 382 } 383 384 /** 385 * Helper method used by subsequent flagged wrappers to apply combined flags. Sets the flags 386 * in the bitmask using a particulare left shift. 387 * 388 * \param mask bitmask to use 389 * \param shift left shifts to use 390 * \param flags flags to update (any flags within the bitmask are updated to their value in this 391 * argument) 392 */ 393 inline void setFlagsHelper(IntFlag mask, int shift, IntFlag flags) { 394 mFlags = Flag((mFlags & ~(mask << shift)) | ((flags & mask) << shift)); 395 } 396 397 public: 398 /** 399 * Wrapper around base class constructor. These take the flags as their first 400 * argument and pass the rest of the arguments to the base class constructor. 401 * 402 * \param flags initial flags 403 */ 404 template<typename ...Args> 405 constexpr Flagged(Flag flags, Args... args) 406 : mValue(std::forward<Args>(args)...), 407 mFlags(Flag(_Flagged_helper::lshift(flags & sFlagMask, sFlagShift))) { } 408 409 /** Gets the wrapped value as const. */ 410 inline constexpr const T &get() const { return mValue; } 411 412 /** Gets the wrapped value. */ 413 inline T &get() { return mValue; } 414 415 /** Gets the flags. */ 416 constexpr Flag flags() const { 417 return Flag(getFlagsHelper(sFlagMask, sFlagShift)); 418 } 419 420 /** Sets the flags. */ 421 void setFlags(Flag flags) { 422 setFlagsHelper(sFlagMask, sFlagShift, flags); 423 } 424 }; 425 426 /* 427 * TRICKY: we cannot implement the specialization as: 428 * 429 * class Flagged : base<Flag> { 430 * T value; 431 * }; 432 * 433 * Because T also inherits from base<Flag> and this runs into a compiler bug where 434 * sizeof(Flagged) > sizeof(T). 435 * 436 * Instead, we must inherit directly from the wrapped class 437 * 438 */ 439 #if 0 440 template< 441 typename T, typename Flag, Flag MASK, Flag SHARED_MASK, int SHIFT> 442 class Flagged<T, Flag, MASK, SHARED_MASK, SHIFT, true> : public _Flagged_helper::base<Flag> { 443 private: 444 T mValue; 445 }; 446 #else 447 /** 448 * Specialization for the case when T is derived from Flagged<U, Flag> and flags can be combined. 449 */ 450 template< 451 typename T, typename Flag, Flag MASK, Flag SHARED_MASK, int SHIFT, typename IntFlag> 452 class Flagged<T, Flag, MASK, SHARED_MASK, SHIFT, IntFlag, true> : private T { 453 static_assert(is_integral_or_enum<Flag>::value, "flag must be integer or enum"); 454 455 static_assert(SHARED_MASK == 0 || SHIFT == 0, "cannot overlap masks when using SHIFT"); 456 static_assert((SHARED_MASK & ~MASK) == 0, "shared mask must be part of the mask"); 457 static_assert((SHARED_MASK & ~T::sEffectiveMask) == 0, 458 "shared mask must be part of the base mask"); 459 static_assert(SHARED_MASK == 0 || (~SHARED_MASK & (MASK & T::sEffectiveMask)) == 0, 460 "mask and base mask can only overlap in shared mask"); 461 462 static constexpr Flag sFlagMask = MASK; ///< the mask 463 static constexpr int sFlagShift = SHIFT; ///< the left shift applied to the flags 464 465 #ifdef FRIEND_TEST 466 const static bool sFlagCombined = true; 467 FRIEND_TEST(FlaggedTest, _Flagged_helper_Test); 468 #endif 469 470 protected: 471 /// The effective combined mask used by this class and any wrapped classes if the flags are 472 /// combined. 473 static constexpr IntFlag sEffectiveMask = Flag((MASK << SHIFT) | T::sEffectiveMask); 474 friend struct _Flagged_helper; 475 476 public: 477 /** 478 * Wrapper around base class constructor. These take the flags as their first 479 * argument and pass the rest of the arguments to the base class constructor. 480 * 481 * \param flags initial flags 482 */ 483 template<typename ...Args> 484 constexpr Flagged(Flag flags, Args... args) 485 : T(std::forward<Args>(args)...) { 486 // we construct the base class first and apply the flags afterwards as 487 // base class may not have a constructor that takes flags even if it is derived from 488 // Flagged<U, Flag> 489 setFlags(flags); 490 } 491 492 /** Gets the wrapped value as const. */ 493 inline constexpr T &get() const { return *this; } 494 495 /** Gets the wrapped value. */ 496 inline T &get() { return *this; } 497 498 /** Gets the flags. */ 499 Flag constexpr flags() const { 500 return Flag(this->getFlagsHelper(sFlagMask, sFlagShift)); 501 } 502 503 /** Sets the flags. */ 504 void setFlags(Flag flags) { 505 this->setFlagsHelper(sFlagMask, sFlagShift, flags); 506 } 507 }; 508 #endif 509 510 } // namespace android 511 512 #endif // STAGEFRIGHT_FOUNDATION_FLAGGED_H_ 513 514