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
22namespace 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 */
105struct _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>
116    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>
149    static constexpr IntFlag lshift(Flag value, int shift) {
150        return shift ? value << shift : value;
151    }
152
153private:
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>
168    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>
205    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     */
236    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
244public:
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 */
319template<typename T, typename Flag>
320struct _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 */
343template<
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>
348class 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
366protected:
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
397public:
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
440template<
441        typename T, typename Flag, Flag MASK, Flag SHARED_MASK, int SHIFT>
442class Flagged<T, Flag, MASK, SHARED_MASK, SHIFT, true> : public _Flagged_helper::base<Flag> {
443private:
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 */
450template<
451        typename T, typename Flag, Flag MASK, Flag SHARED_MASK, int SHIFT, typename IntFlag>
452class 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
470protected:
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
476public:
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