/*
 * 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_A_DATA_H_
#define STAGEFRIGHT_FOUNDATION_A_DATA_H_

#include <memory> // for std::shared_ptr, weak_ptr and unique_ptr
#include <type_traits> // for std::aligned_union

#include <utils/StrongPointer.h> // for android::sp and wp

#include <media/stagefright/foundation/TypeTraits.h>
#include <media/stagefright/foundation/Flagged.h>

namespace android {

/**
 * AData is a flexible union type that supports non-POD members. It supports arbitrary types as long
 * as they are either moveable or copyable.
 *
 * Internally, AData is using AUnion - a structure providing the union support. AUnion should not
 * be used by generic code as it is very unsafe - it opens type aliasing errors where an object of
 * one type can be easily accessed as an object of another type. AData prevents this.
 *
 * AData allows a custom type flagger to be used for future extensions (e.g. allowing automatic
 * type conversion). A strict and a relaxed flagger are provided as internal types.
 *
 * Use as follows:
 *
 * AData<int, float>::Basic data; // strict type support
 * int i = 1;
 * float f = 7.0f;
 *
 * data.set(5);
 * EXPECT_TRUE(data.find(&i));
 * EXPECT_FALSE(data.find(&f));
 * EXPECT_EQ(i, 5);
 *
 * data.set(6.0f);
 * EXPECT_FALSE(data.find(&i));
 * EXPECT_TRUE(data.find(&f));
 * EXPECT_EQ(f, 6.0f);
 *
 * AData<int, sp<RefBase>>::RelaxedBasic objdata; // relaxed type support
 * sp<ABuffer> buf = new ABuffer(16), buf2;
 * sp<RefBase> obj;
 *
 * objdata.set(buf);
 * EXPECT_TRUE(objdata.find(&buf2));
 * EXPECT_EQ(buf, buf2);
 * EXPECT_FALSE(objdata.find(&i));
 * EXPECT_TRUE(objdata.find(&obj));
 * EXPECT_TRUE(obj == buf);
 *
 * obj = buf;
 * objdata.set(obj); // storing as sp<RefBase>
 * EXPECT_FALSE(objdata.find(&buf2));  // not stored as ABuffer(!)
 * EXPECT_TRUE(objdata.find(&obj));
 */

/// \cond Internal

/**
 * Helper class to call constructor and destructor for a specific type in AUnion.
 * This class is needed as member function specialization is not allowed for a
 * templated class.
 */
struct _AUnion_impl {
    /**
     * Calls placement constuctor for type T with arbitrary arguments for a storage at an address.
     * Storage MUST be large enough to contain T.
     * Also clears the slack space after type T. \todo This is not technically needed, so we may
     * choose to do this just for debugging.
     *
     * \param totalSize size of the storage
     * \param addr      pointer to where object T should be constructed
     * \param args      arbitrary arguments for constructor
     */
    template<typename T, typename ...Args>
    inline static void emplace(size_t totalSize, T *addr, Args&&... args) {
        new(addr)T(std::forward<Args>(args)...);
        // clear slack space - this is not technically required
        constexpr size_t size = sizeof(T);
        memset(reinterpret_cast<uint8_t*>(addr) + size, 0, totalSize - size);
    }

    /**
     * Calls destuctor for an object of type T located at a specific address.
     *
     * \note we do not clear the storage in this case as the storage should not be used
     * until another object is placed there, at which case the storage will be cleared.
     *
     * \param addr    pointer to where object T is stored
     */
    template<typename T>
    inline static void del(T *addr) {
        addr->~T();
    }
};

/** Constructor specialization for void type */
template<>
inline void _AUnion_impl::emplace<void>(size_t totalSize, void *addr) {
    memset(addr, 0, totalSize);
}

/** Destructor specialization for void type */
template<>
inline void _AUnion_impl::del<void>(void *) {
}

/// \endcond

/**
 * A templated union class that can contain specific types of data, and provides
 * constructors, destructor and access methods strictly for those types.
 *
 * \note This class is VERY UNSAFE compared to a union, but it allows non-POD unions.
 * In particular care must be taken that methods are called in a careful order to
 * prevent accessing objects of one type as another type. This class provides no
 * facilities to help with this ordering. This is meant to be wrapped by safer
 * utility classes that do that.
 *
 * \param Ts types stored in this union.
 */
template<typename ...Ts>
struct AUnion {
private:
    using _type = typename std::aligned_union<0, Ts...>::type; ///< storage type
    _type mValue;                                              ///< storage

public:
    /**
     * Constructs an object of type T with arbitrary arguments in this union. After this call,
     * this union will contain this object.
     *
     * This method MUST be called only when either 1) no object or 2) a void object (equivalent to
     * no object) is contained in this union.
     *
     * \param T     type of object to be constructed. This must be one of the template parameters of
     *              the union class with the same cv-qualification, or void.
     * \param args  arbitrary arguments for the constructor
     */
    template<
            typename T, typename ...Args,
            typename=typename std::enable_if<is_one_of<T, void, Ts...>::value>::type>
    inline void emplace(Args&&... args) {
        _AUnion_impl::emplace(
                sizeof(_type), reinterpret_cast<T*>(&mValue), std::forward<Args>(args)...);
    }

    /**
     * Destructs an object of type T in this union. After this call, this union will contain no
     * object.
     *
     * This method MUST be called only when this union contains an object of type T.
     *
     * \param T     type of object to be destructed. This must be one of the template parameters of
     *              the union class with the same cv-qualification, or void.
     */
    template<
            typename T,
            typename=typename std::enable_if<is_one_of<T, void, Ts...>::value>::type>
    inline void del() {
        _AUnion_impl::del(reinterpret_cast<T*>(&mValue));
    }

    /**
     * Returns a const reference to the object of type T in this union.
     *
     * This method MUST be called only when this union contains an object of type T.
     *
     * \param T     type of object to be returned. This must be one of the template parameters of
     *              the union class with the same cv-qualification.
     */
    template<
            typename T,
            typename=typename std::enable_if<is_one_of<T, Ts...>::value>::type>
    inline const T &get() const {
        return *reinterpret_cast<const T*>(&mValue);
    }

    /**
     * Returns a reference to the object of type T in this union.
     *
     * This method MUST be called only when this union contains an object of type T.
     *
     * \param T     type of object to be returned. This must be one of the template parameters of
     *              the union class with the same cv-qualification.
     */
    template<typename T>
    inline T &get() {
        return *reinterpret_cast<T*>(&mValue);
    }
};

/**
 * Helper utility class that copies an object of type T to a destination.
 *
 * T must be copy assignable or copy constructible.
 *
 * It provides:
 *
 * void assign(T*, const U&) // for copiable types - this leaves the source unchanged, hence const.
 *
 * \param T type of object to assign to
 */
template<
        typename T,
        bool=std::is_copy_assignable<T>::value>
struct _AData_copier {
    static_assert(std::is_copy_assignable<T>::value, "T must be copy assignable here");

    /**
     * Copies src to data without modifying data.
     *
     * \param data pointer to destination
     * \param src source object
     */
    inline static void assign(T *data, const T &src) {
        *data = src;
    }

    template<typename U>
    using enable_if_T_is_same_as = typename std::enable_if<std::is_same<U, T>::value>::type;

    /**
     * Downcast specializations for sp<>, shared_ptr<> and weak_ptr<>
     */
    template<typename Tp, typename U, typename=enable_if_T_is_same_as<sp<Tp>>>
    inline static void assign(sp<Tp> *data, const sp<U> &src) {
        *data = static_cast<Tp*>(src.get());
    }

    template<typename Tp, typename U, typename=enable_if_T_is_same_as<wp<Tp>>>
    inline static void assign(wp<Tp> *data, const wp<U> &src) {
        sp<U> __tmp = src.promote();
        *data = static_cast<Tp*>(__tmp.get());
    }

    template<typename Tp, typename U, typename=enable_if_T_is_same_as<sp<Tp>>>
    inline static void assign(sp<Tp> *data, sp<U> &&src) {
        sp<U> __tmp = std::move(src); // move src out as get cannot
        *data = static_cast<Tp*>(__tmp.get());
    }

    template<typename Tp, typename U, typename=enable_if_T_is_same_as<std::shared_ptr<Tp>>>
    inline static void assign(std::shared_ptr<Tp> *data, const std::shared_ptr<U> &src) {
        *data = std::static_pointer_cast<Tp>(src);
    }

    template<typename Tp, typename U, typename=enable_if_T_is_same_as<std::shared_ptr<Tp>>>
    inline static void assign(std::shared_ptr<Tp> *data, std::shared_ptr<U> &&src) {
        std::shared_ptr<U> __tmp = std::move(src); // move src out as static_pointer_cast cannot
        *data = std::static_pointer_cast<Tp>(__tmp);
    }

    template<typename Tp, typename U, typename=enable_if_T_is_same_as<std::weak_ptr<Tp>>>
    inline static void assign(std::weak_ptr<Tp> *data, const std::weak_ptr<U> &src) {
        *data = std::static_pointer_cast<Tp>(src.lock());
    }

    // shared_ptrs are implicitly convertible to weak_ptrs but not vice versa, but picking the
    // first compatible type in Ts requires having shared_ptr types before weak_ptr types, so that
    // they are stored as shared_ptrs.
    /**
     * Provide sensible error message if encountering shared_ptr/weak_ptr ambiguity. This method
     * is not enough to detect this, only if someone is trying to find the shared_ptr.
     */
    template<typename Tp, typename U>
    inline static void assign(std::shared_ptr<Tp> *, const std::weak_ptr<U> &) {
        static_assert(std::is_same<Tp, void>::value,
                      "shared/weak pointer ambiguity. move shared ptr types before weak_ptrs");
    }
};

/**
 * Template specialization for non copy assignable, but copy constructible types.
 *
 * \todo Test this. No basic classes are copy constructible but not assignable.
 *
 */
template<typename T>
struct _AData_copier<T, false> {
    static_assert(!std::is_copy_assignable<T>::value, "T must not be copy assignable here");
    static_assert(std::is_copy_constructible<T>::value, "T must be copy constructible here");

    inline static void copy(T *data, const T &src) {
        data->~T();
        new(data)T(src);
    }
};

/**
 * Helper utility class that moves an object of type T to a destination.
 *
 * T must be move assignable or move constructible.
 *
 * It provides multiple methods:
 *
 * void assign(T*, T&&)
 *
 * \param T type of object to assign
 */
template<
        typename T,
        bool=std::is_move_assignable<T>::value>
struct _AData_mover {
    static_assert(std::is_move_assignable<T>::value, "T must be move assignable here");

    /**
     * Moves src to data while likely modifying it.
     *
     * \param data pointer to destination
     * \param src source object
     */
    inline static void assign(T *data, T &&src) {
        *data = std::move(src);
    }

    template<typename U>
    using enable_if_T_is_same_as = typename std::enable_if<std::is_same<U, T>::value>::type;

    /**
     * Downcast specializations for sp<>, shared_ptr<> and weak_ptr<>
     */
    template<typename Tp, typename U, typename=enable_if_T_is_same_as<sp<Tp>>>
    inline static void assign(sp<Tp> *data, sp<U> &&src) {
        sp<U> __tmp = std::move(src); // move src out as get cannot
        *data = static_cast<Tp*>(__tmp.get());
    }

    template<typename Tp, typename U, typename=enable_if_T_is_same_as<std::shared_ptr<Tp>>>
    inline static void assign(std::shared_ptr<Tp> *data, std::shared_ptr<U> &&src) {
        std::shared_ptr<U> __tmp = std::move(src); // move src out as static_pointer_cast cannot
        *data = std::static_pointer_cast<Tp>(__tmp);
    }

    template<typename Tp, typename Td, typename U, typename Ud,
            typename=enable_if_T_is_same_as<std::unique_ptr<Tp, Td>>>
    inline static void assign(std::unique_ptr<Tp, Td> *data, std::unique_ptr<U, Ud> &&src) {
        *data = std::unique_ptr<Tp, Td>(static_cast<Tp*>(src.release()));
    }

    // shared_ptrs are implicitly convertible to weak_ptrs but not vice versa, but picking the
    // first compatible type in Ts requires having shared_ptr types before weak_ptr types, so that
    // they are stored as shared_ptrs.
    /**
     * Provide sensible error message if encountering shared_ptr/weak_ptr ambiguity. This method
     * is not enough to detect this, only if someone is trying to remove the shared_ptr.
     */
    template<typename Tp, typename U>
    inline static void assign(std::shared_ptr<Tp> *, std::weak_ptr<U> &&) {
        static_assert(std::is_same<Tp, void>::value,
                      "shared/weak pointer ambiguity. move shared ptr types before weak_ptrs");
    }

    // unique_ptrs are implicitly convertible to shared_ptrs but not vice versa, but picking the
    // first compatible type in Ts requires having unique_ptrs types before shared_ptrs types, so
    // that they are stored as unique_ptrs.
    /**
     * Provide sensible error message if encountering shared_ptr/unique_ptr ambiguity. This method
     * is not enough to detect this, only if someone is trying to remove the unique_ptr.
     */
    template<typename Tp, typename U>
    inline static void assign(std::unique_ptr<Tp> *, std::shared_ptr<U> &&) {
        static_assert(std::is_same<Tp, void>::value,
                      "unique/shared pointer ambiguity. move unique ptr types before shared_ptrs");
    }
};

/**
 * Template specialization for non move assignable, but move constructible types.
 *
 * \todo Test this. No basic classes are move constructible but not assignable.
 *
 */
template<typename T>
struct _AData_mover<T, false> {
    static_assert(!std::is_move_assignable<T>::value, "T must not be move assignable here");
    static_assert(std::is_move_constructible<T>::value, "T must be move constructible here");

    inline static void assign(T *data, T &&src) {
        data->~T();
        new(data)T(std::move(src));
    }
};

/**
 * Helper template that deletes an object of a specific type (member) in an AUnion.
 *
 * \param Flagger type flagger class (see AData)
 * \param U AUnion object in which the member should be deleted
 * \param Ts types to consider for the member
 */
template<typename Flagger, typename U, typename ...Ts>
struct _AData_deleter;

/**
 * Template specialization when there are still types to consider (T and rest)
 */
template<typename Flagger, typename U, typename T, typename ...Ts>
struct _AData_deleter<Flagger, U, T, Ts...> {
    static bool del(typename Flagger::type flags, U &data) {
        if (Flagger::canDeleteAs(flags, Flagger::flagFor((T*)0))) {
            data.template del<T>();
            return true;
        }
        return _AData_deleter<Flagger, U, Ts...>::del(flags, data);
    }
};

/**
 * Template specialization when there are no more types to consider.
 */
template<typename Flagger, typename U>
struct _AData_deleter<Flagger, U> {
    inline static bool del(typename Flagger::type, U &) {
        return false;
    }
};

/**
 * Container that can store an arbitrary object of a set of specified types.
 *
 * This struct is an outer class that contains various inner classes based on desired type
 * strictness. The following inner classes are supported:
 *
 * AData<types...>::Basic   - strict type support using uint32_t flag.
 *
 * AData<types...>::Strict<Flag> - strict type support using custom flag.
 * AData<types...>::Relaxed<Flag, MaxSize, Align>
 *                          - relaxed type support with compatible (usually derived) class support
 *                            for pointer types with added size checking for minimal additional
 *                            safety.
 *
 * AData<types...>::RelaxedBasic - strict type support using uint32_t flag.
 *
 * AData<types...>::Custom<flagger> - custom type support (flaggers determine the supported types
 *                                    and the base type to use for each)
 *
 */
template<typename ...Ts>
struct AData {
private:
    static_assert(are_unique<Ts...>::value, "types must be unique");

    static constexpr size_t num_types = sizeof...(Ts); ///< number of types to support

public:
    /**
     * Default (strict) type flagger provided.
     *
     * The default flagger simply returns the index of the type within Ts, or 0 for void.
     *
     * Type flaggers return a flag for a supported type.
     *
     * They must provide:
     *
     * - a flagFor(T*) method for supported types _and_ for T=void. T=void is used to mark that no
     *   object is stored in the container. For this, an arbitrary unique value may be returned.
     * - a mask field that contains the flag mask.
     * - a canDeleteAs(Flag, Flag) flag comparison method that checks if a type of a flag can be
     *   deleted as another type.
     *
     * \param Flag the underlying unsigned integral to use for the flags.
     */
    template<typename Flag>
    struct flagger {
    private:
        static_assert(std::is_unsigned<Flag>::value, "Flag must be unsigned");
        static_assert(std::is_integral<Flag>::value, "Flag must be an integral type");

        static constexpr Flag count = num_types + 1;

    public:
        typedef Flag type; ///< flag type

        static constexpr Flag mask = _Flagged_helper::minMask<Flag>(count); ///< flag mask

        /**
         * Return the stored type for T. This is itself.
         */
        template<typename T>
        struct store {
            typedef T as_type; ///< the base type that T is stored as
        };

        /**
         * Constexpr method that returns if two flags are compatible for deletion.
         *
         * \param objectFlag flag for object to be deleted
         * \param deleteFlag flag for type that object is to be deleted as
         */
        static constexpr bool canDeleteAs(Flag objectFlag, Flag deleteFlag) {
            // default flagger requires strict type equality
            return objectFlag == deleteFlag;
        }

        /**
         * Constexpr method that returns the flag to use for a given type.
         *
         * Function overload for void*.
         */
        static constexpr Flag flagFor(void*) {
            return 0u;
        }

        /**
         * Constexpr method that returns the flag to use for a given supported type (T).
         */
        template<typename T, typename=typename std::enable_if<is_one_of<T, Ts...>::value>::type>
        static constexpr Flag flagFor(T*) {
            return find_first<T, Ts...>::index;
        }
    };

    /**
     * Relaxed flagger returns the index of the type within Ts. However, for pointers T* it returns
     * the first type in Ts that T* can be converted into (this is normally a base type, but also
     * works for sp<>, shared_ptr<> or unique_ptr<>). For a bit more strictness, the flag also
     * contains the size of the class to avoid finding objects that were stored as a different
     * derived class of the same base class.
     *
     * Flag is basically the index of the (base) type in Ts multiplied by the max size stored plus
     * the size of the type (divided by alignment) for derived pointer types.
     *
     * \param MaxSize max supported size for derived class pointers
     * \param Align alignment to assume for derived class pointers
     */
    template<typename Flag, size_t MaxSize=1024, size_t Align=4>
    struct relaxed_flagger {
    private:
        static_assert(std::is_unsigned<Flag>::value, "Flag must be unsigned");
        static_assert(std::is_integral<Flag>::value, "Flag must be an integral type");

        static constexpr Flag count = num_types + 1;
        static_assert(std::numeric_limits<Flag>::max() / count > (MaxSize / Align),
                      "not enough bits to fit into flag");

        static constexpr Flag max_size_stored = MaxSize / Align + 1;

        // T can be converted if it's size is <= MaxSize and it can be converted to one of the Ts
        template<typename T, size_t size>
        using enable_if_can_be_converted = typename std::enable_if<
                (size / Align < max_size_stored
                        && find_first_convertible_to<T, Ts...>::index)>::type;


        template<typename W, typename T, typename=enable_if_can_be_converted<W, sizeof(T)>>
        static constexpr Flag relaxedFlagFor(W*, T*) {
            return find_first_convertible_to<W, Ts...>::index * max_size_stored
                    + (is_one_of<W, Ts...>::value ? 0 : (sizeof(T) / Align));
        }

    public:
        typedef Flag type; ///< flag type

        static constexpr Flag mask =
            _Flagged_helper::minMask<Flag>(count * max_size_stored); ///< flag mask

        /**
         * Constexpr method that returns if two flags are compatible for deletion.
         *
         * \param objectFlag flag for object to be deleted
         * \param deleteFlag flag for type that object is to be deleted as
         */
        static constexpr bool canDeleteAs(Flag objectFlag, Flag deleteFlag) {
            // can delete if objects have the same base type
            return
                objectFlag / max_size_stored == deleteFlag / max_size_stored &&
                (deleteFlag % max_size_stored) == 0;
        }

        /**
         * Constexpr method that returns the flag to use for a given type.
         *
         * Function overload for void*.
         */
        static constexpr Flag flagFor(void*) {
            return 0u;
        }

        /**
         * Constexpr method that returns the flag to use for a given supported type (T).
         *
         * This is a member method to enable both overloading as well as template specialization.
         */
        template<typename T, typename=typename std::enable_if<is_one_of<T, Ts...>::value>::type>
        static constexpr Flag flagFor(T*) {
            return find_first<T, Ts...>::index * max_size_stored;
        }

        /**
         * For precaution, we only consider converting pointers to their base classes.
         */

        /**
         * Template specialization for derived class pointers and managed pointers.
         */
        template<typename T>
        static constexpr Flag flagFor(T**p) { return relaxedFlagFor(p, (T*)0); }
        template<typename T>
        static constexpr Flag flagFor(std::shared_ptr<T>*p) { return relaxedFlagFor(p, (T*)0); }
        template<typename T>
        static constexpr Flag flagFor(std::unique_ptr<T>*p) { return relaxedFlagFor(p, (T*)0); }
        template<typename T>
        static constexpr Flag flagFor(std::weak_ptr<T>*p) { return relaxedFlagFor(p, (T*)0); }
        template<typename T>
        static constexpr Flag flagFor(sp<T>*p) { return relaxedFlagFor(p, (T*)0); }
        template<typename T>
        static constexpr Flag flagFor(wp<T>*p) { return relaxedFlagFor(p, (T*)0); }

        /**
         * Type support template that provodes the stored type for T.
         * This is itself if it is one of Ts, or the first type in Ts that T is convertible to.
         *
         * NOTE: This template may provide a base class for an unsupported type. Support is
         * determined by flagFor().
         */
        template<typename T>
        struct store {
            typedef typename std::conditional<
                    is_one_of<T, Ts...>::value,
                    T,
                    typename find_first_convertible_to<T, Ts...>::type>::type as_type;
        };
    };

    /**
     * Implementation of AData.
     */
    template<typename Flagger>
    struct Custom : protected Flagged<AUnion<Ts...>, typename Flagger::type, Flagger::mask> {
        using data_t = AUnion<Ts...>;
        using base_t = Flagged<AUnion<Ts...>, typename Flagger::type, Flagger::mask>;

        /**
         * Constructor. Initializes this to a container that does not contain any object.
         */
        Custom() : base_t(Flagger::flagFor((void*)0)) { }

        /**
         * Removes the contained object, if any.
         */
        ~Custom() {
            if (!this->clear()) {
                __builtin_trap();
                // std::cerr << "could not delete data of type " << this->flags() << std::endl;
            }
        }

        /**
         * Returns whether there is any object contained.
         */
        inline bool used() const {
            return this->flags() != Flagger::flagFor((void*)0);
        }

        /**
         * Removes the contained object, if any. Returns true if there are no objects contained,
         * or false on any error (this is highly unexpected).
         */
        bool clear() {
            if (this->used()) {
                if (_AData_deleter<Flagger, data_t, Ts...>::del(this->flags(), this->get())) {
                    this->setFlags(Flagger::flagFor((void*)0));
                    return true;
                }
                return false;
            }
            return true;
        }

        template<typename T>
        using is_supported_by_flagger =
            typename std::enable_if<Flagger::flagFor((T*)0) != Flagger::flagFor((void*)0)>::type;

        /**
         * Checks if there is a copiable object of type T in this container. If there is, it copies
         * that object into the provided address and returns true. Otherwise, it does nothing and
         * returns false.
         *
         * This method normally requires a flag equality between the stored and retrieved types.
         * However, it also allows retrieving the stored object as the stored type
         * (usually base type).
         *
         * \param T type of object to sought
         * \param data address at which the object should be retrieved
         *
         * \return true if the object was retrieved. false if it was not.
         */
        template<
                typename T,
                typename=is_supported_by_flagger<T>>
        bool find(T *data) const {
            using B = typename Flagger::template store<T>::as_type;
            if (this->flags() == Flagger::flagFor((T*)0) ||
                Flagger::canDeleteAs(this->flags(), Flagger::flagFor((T*)0))) {
                _AData_copier<T>::assign(data, this->get().template get<B>());
                return true;
            }
            return false;
        }

        /**
         * Checks if there is an object of type T in this container. If there is, it moves that
         * object into the provided address and returns true. Otherwise, it does nothing and returns
         * false.
         *
         * This method normally requires a flag equality between the stored and retrieved types.
         * However, it also allows retrieving the stored object as the stored type
         * (usually base type).
         *
         * \param T type of object to sought
         * \param data address at which the object should be retrieved.
         *
         * \return true if the object was retrieved. false if it was not.
         */
        template<
                typename T,
                typename=is_supported_by_flagger<T>>
        bool remove(T *data) {
            using B = typename Flagger::template store<T>::as_type;
            if (this->flags() == Flagger::flagFor((T*)0) ||
                Flagger::canDeleteAs(this->flags(), Flagger::flagFor((T*)0))) {
                _AData_mover<T>::assign(data, std::move(this->get().template get<B>()));
                return true;
            }
            return false;
        }

        /**
         * Stores an object into this container by copying. If it was successful, returns true.
         * Otherwise, (e.g. it could not destroy the already stored object) it returns false. This
         * latter would be highly unexpected.
         *
         * \param T type of object to store
         * \param data object to store
         *
         * \return true if the object was stored. false if it was not.
         */
        template<
                typename T,
                typename=is_supported_by_flagger<T>,
                typename=typename std::enable_if<
                        std::is_copy_constructible<T>::value ||
                        (std::is_default_constructible<T>::value &&
                                std::is_copy_assignable<T>::value)>::type>
        bool set(const T &data) {
            using B = typename Flagger::template store<T>::as_type;

            // if already contains an object of this type, simply assign
            if (this->flags() == Flagger::flagFor((T*)0) && std::is_same<T, B>::value) {
                _AData_copier<B>::assign(&this->get().template get<B>(), data);
                return true;
            } else if (this->used()) {
                // destroy previous object
                if (!this->clear()) {
                    return false;
                }
            }
            this->get().template emplace<B>(data);
            this->setFlags(Flagger::flagFor((T *)0));
            return true;
        }

        /**
         * Moves an object into this container. If it was successful, returns true. Otherwise,
         * (e.g. it could not destroy the already stored object) it returns false. This latter
         * would be highly unexpected.
         *
         * \param T type of object to store
         * \param data object to store
         *
         * \return true if the object was stored. false if it was not.
         */
        template<
                typename T,
                typename=is_supported_by_flagger<T>>
        bool set(T &&data) {
            using B = typename Flagger::template store<T>::as_type;

            // if already contains an object of this type, simply assign
            if (this->flags() == Flagger::flagFor((T*)0) && std::is_same<T, B>::value) {
                _AData_mover<B>::assign(&this->get().template get<B>(), std::forward<T&&>(data));
                return true;
            } else if (this->used()) {
                // destroy previous object
                if (!this->clear()) {
                    return false;
                }
            }
            this->get().template emplace<B>(std::forward<T&&>(data));
            this->setFlags(Flagger::flagFor((T *)0));
            return true;
        }
    };

    /**
     * Basic AData using the default type flagger and requested flag type.
     *
     * \param Flag desired flag type to use. Must be an unsigned and std::integral type.
     */
    template<typename Flag>
    using Strict = Custom<flagger<Flag>>;

    /**
     * Basic AData using the default type flagger and uint32_t flag.
     */
    using Basic = Strict<uint32_t>;

    /**
     * AData using the relaxed type flagger for max size and requested flag type.
     *
     * \param Flag desired flag type to use. Must be an unsigned and std::integral type.
     */
    template<typename Flag, size_t MaxSize = 1024, size_t Align = 4>
    using Relaxed = Custom<relaxed_flagger<Flag, MaxSize, Align>>;

    /**
     * Basic AData using the relaxed type flagger and uint32_t flag.
     */
    using RelaxedBasic = Relaxed<uint32_t>;
};

}  // namespace android

#endif  // STAGEFRIGHT_FOUNDATION_A_DATA_H_

