995 lines
36 KiB
C++
995 lines
36 KiB
C++
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef STAGEFRIGHT_FOUNDATION_AML_MP_DATA_H_
|
|
#define STAGEFRIGHT_FOUNDATION_AML_MP_DATA_H_
|
|
|
|
#include <memory> // for std::shared_ptr, weak_ptr and unique_ptr
|
|
#include <type_traits> // for std::aligned_union
|
|
|
|
#include "AmlMpStrongPointer.h"
|
|
|
|
#include "AmlMpTypeTraits.h"
|
|
#include "AmlMpFlagged.h"
|
|
#include <string.h>
|
|
#include <limits>
|
|
#include <cstdint>
|
|
|
|
#undef HIDE
|
|
#define HIDE __attribute__((visibility("hidden")))
|
|
|
|
namespace aml_mp {
|
|
|
|
/**
|
|
* 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 HIDE _AUnion_impl {
|
|
/**
|
|
* Calls placement constructor 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 destructor 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<>
|
|
HIDE inline void _AUnion_impl::emplace<void>(size_t totalSize, void *addr) {
|
|
memset(addr, 0, totalSize);
|
|
}
|
|
|
|
/** Destructor specialization for void type */
|
|
template<>
|
|
HIDE 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 copyable 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 HIDE _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<sptr<Tp>>>
|
|
inline static void assign(sptr<Tp> *data, const sptr<U> &src) {
|
|
*data = static_cast<Tp*>(src.get());
|
|
}
|
|
|
|
template<typename Tp, typename U, typename=enable_if_T_is_same_as<wptr<Tp>>>
|
|
inline static void assign(wptr<Tp> *data, const wptr<U> &src) {
|
|
sptr<U> __tmp = src.promote();
|
|
*data = static_cast<Tp*>(__tmp.get());
|
|
}
|
|
|
|
template<typename Tp, typename U, typename=enable_if_T_is_same_as<sptr<Tp>>>
|
|
inline static void assign(sptr<Tp> *data, sptr<U> &&src) {
|
|
sptr<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 HIDE _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 HIDE _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<sptr<Tp>>>
|
|
inline static void assign(sptr<Tp> *data, sptr<U> &&src) {
|
|
sptr<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 HIDE _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 HIDE _AData_deleter;
|
|
|
|
/**
|
|
* Template specialization when there are still types to consider (T and rest)
|
|
*/
|
|
template<typename Flagger, typename U, typename T, typename ...Ts>
|
|
struct HIDE _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 HIDE _AData_deleter<Flagger, U> {
|
|
inline static bool del(typename Flagger::type, U &) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Helper template that copy assigns 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 copy assigned
|
|
* \param Ts types to consider for the member
|
|
*/
|
|
template<typename Flagger, typename U, typename ...Ts>
|
|
struct HIDE _AData_copy_assigner;
|
|
|
|
/**
|
|
* Template specialization when there are still types to consider (T and rest)
|
|
*/
|
|
template<typename Flagger, typename U, typename T, typename ...Ts>
|
|
struct HIDE _AData_copy_assigner<Flagger, U, T, Ts...> {
|
|
static bool assign(typename Flagger::type flags, U &dst, const U &src) {
|
|
static_assert(std::is_copy_constructible<T>::value, "T must be copy constructible");
|
|
// if we can delete as, we can also assign as
|
|
if (Flagger::canDeleteAs(flags, Flagger::flagFor((T*)0))) {
|
|
dst.template emplace<T>(src.template get<T>());
|
|
return true;
|
|
}
|
|
return _AData_copy_assigner<Flagger, U, Ts...>::assign(flags, dst, src);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Template specialization when there are no more types to consider.
|
|
*/
|
|
template<typename Flagger, typename U>
|
|
struct HIDE _AData_copy_assigner<Flagger, U> {
|
|
inline static bool assign(typename Flagger::type, U &, const U &) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Helper template that move assigns 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 copy assigned
|
|
* \param Ts types to consider for the member
|
|
*/
|
|
template<typename Flagger, typename U, typename ...Ts>
|
|
struct HIDE _AData_move_assigner;
|
|
|
|
/**
|
|
* Template specialization when there are still types to consider (T and rest)
|
|
*/
|
|
template<typename Flagger, typename U, typename T, typename ...Ts>
|
|
struct HIDE _AData_move_assigner<Flagger, U, T, Ts...> {
|
|
template<typename V = T>
|
|
static typename std::enable_if<std::is_move_constructible<V>::value, bool>::type
|
|
assign(typename Flagger::type flags, U &dst, U &src) {
|
|
// if we can delete as, we can also assign as
|
|
if (Flagger::canDeleteAs(flags, Flagger::flagFor((T*)0))) {
|
|
dst.template emplace<T>(std::move(src.template get<T>()));
|
|
return true;
|
|
}
|
|
return _AData_move_assigner<Flagger, U, Ts...>::assign(flags, dst, src);
|
|
}
|
|
|
|
// Fall back to copy construction if T is not move constructible
|
|
template<typename V = T>
|
|
static typename std::enable_if<!std::is_move_constructible<V>::value, bool>::type
|
|
assign(typename Flagger::type flags, U &dst, U &src) {
|
|
static_assert(std::is_copy_constructible<T>::value, "T must be copy constructible");
|
|
// if we can delete as, we can also assign as
|
|
if (Flagger::canDeleteAs(flags, Flagger::flagFor((T*)0))) {
|
|
dst.template emplace<T>(src.template get<T>());
|
|
return true;
|
|
}
|
|
return _AData_move_assigner<Flagger, U, Ts...>::assign(flags, dst, src);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Template specialization when there are no more types to consider.
|
|
*/
|
|
template<typename Flagger, typename U>
|
|
struct HIDE _AData_move_assigner<Flagger, U> {
|
|
inline static bool assign(typename Flagger::type, U &, 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>
|
|
//coverity[definition_tu]
|
|
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(sptr<T>*p) { return relaxedFlagFor(p, (T*)0); }
|
|
template<typename T>
|
|
static constexpr Flag flagFor(wptr<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)) { }
|
|
|
|
/**
|
|
* Copy assignment operator.
|
|
*/
|
|
Custom& operator=(const Custom &o) {
|
|
if (&o != this) {
|
|
if (this->used() && !this->clear()) {
|
|
__builtin_trap();
|
|
}
|
|
if (o.used()) {
|
|
if (_AData_copy_assigner<Flagger, data_t, Ts...>::assign(
|
|
o.flags(), this->get(), o.get())) {
|
|
this->setFlags(o.flags());
|
|
} else {
|
|
__builtin_trap();
|
|
}
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* Copy constructor.
|
|
*/
|
|
Custom(const Custom &o) : Custom() {
|
|
*this = o;
|
|
}
|
|
|
|
/**
|
|
* Move assignment operator.
|
|
*/
|
|
Custom& operator=(Custom &&o) noexcept {
|
|
if (&o != this) {
|
|
if (this->used() && !this->clear()) {
|
|
__builtin_trap();
|
|
}
|
|
if (o.used()) {
|
|
if (_AData_move_assigner<Flagger, data_t, Ts...>::assign(
|
|
o.flags(), this->get(), o.get())) {
|
|
this->setFlags(o.flags());
|
|
//coverity[check_return]
|
|
o.clear();
|
|
} else {
|
|
__builtin_trap();
|
|
}
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* Move constructor.
|
|
*/
|
|
Custom(Custom &&o) noexcept : Custom() {
|
|
*this = std::move(o);
|
|
}
|
|
|
|
/**
|
|
* 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 copyable 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_
|
|
|