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