Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5e727b58 authored by Lajos Molnar's avatar Lajos Molnar Committed by Android (Google) Code Review
Browse files

Merge "Codec2: add c2_cntr32_t and c2_ctnr64_t types"

parents a52b4571 07e2bf0b
Loading
Loading
Loading
Loading
+239 −0
Original line number Diff line number Diff line
@@ -210,6 +210,219 @@ enum c2_blocking_t : int32_t {
    } \
    DEFINE_OTHER_COMPARISON_OPERATORS(type)

template<typename T, typename B>
class C2_HIDE c2_cntr_t;

/// \cond INTERNAL

/// \defgroup utils_internal
/// @{

template<typename T>
struct C2_HIDE _c2_cntr_compat_helper {
    template<typename U, typename E=typename std::enable_if<std::is_integral<U>::value>::type>
    __attribute__((no_sanitize("integer")))
    inline static constexpr T get(const U &value) {
        return T(value);
    }

    template<typename U, typename E=typename std::enable_if<(sizeof(U) >= sizeof(T))>::type>
    __attribute__((no_sanitize("integer")))
    inline static constexpr T get(const c2_cntr_t<U, void> &value) {
        return T(value.mValue);
    }
};

/// @}

/// \endcond

/**
 * Integral counter type.
 *
 * This is basically an unsigned integral type that is NEVER checked for overflow/underflow - and
 * comparison operators are redefined.
 *
 * \note Comparison of counter types is not fully transitive, e.g.
 * it could be that a > b > c but a !> c.
 * std::less<>, greater<>, less_equal<> and greater_equal<> specializations yield total ordering,
 * but may not match semantic ordering of the values.
 *
 * Technically: counter types represent integer values: A * 2^N + value, where A can be arbitrary.
 * This makes addition, subtraction, multiplication (as well as bitwise operations) well defined.
 * However, division is in general not well defined, as the result may depend on A. This is also
 * true for logical operators and boolean conversion.
 *
 * Even though well defined, bitwise operators are not implemented for counter types as they are not
 * meaningful.
 */
template<typename T, typename B=typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value>::type>
class C2_HIDE c2_cntr_t {
    using compat = _c2_cntr_compat_helper<T>;

    T mValue;
    constexpr static T HALF_RANGE = T(~0) ^ (T(~0) >> 1);

    template<typename U>
    friend struct _c2_cntr_compat_helper;
public:

    /**
     * Default constructor. Initialized counter to 0.
     */
    inline constexpr c2_cntr_t() : mValue(T(0)) {}

    /**
     * Construct from a compatible type.
     */
    template<typename U>
    inline constexpr c2_cntr_t(const U &value) : mValue(compat::get(value)) {}

    /**
     * Peek as underlying signed type.
     */
    __attribute__((no_sanitize("integer")))
    inline constexpr typename std::make_signed<T>::type peek() const {
        return static_cast<typename std::make_signed<T>::type>(mValue);
    }

    /**
     * Peek as underlying unsigned type.
     */
    inline constexpr T peeku() const {
        return mValue;
    }

    /**
     * Peek as long long - e.g. for printing.
     */
    __attribute__((no_sanitize("integer")))
    inline constexpr long long peekll() const {
        return (long long)mValue;
    }

    /**
     * Peek as unsigned long long - e.g. for printing.
     */
    __attribute__((no_sanitize("integer")))
    inline constexpr unsigned long long peekull() const {
        return (unsigned long long)mValue;
    }

    /**
     * Convert to a smaller counter type. This is always safe.
     */
    template<typename U, typename E=typename std::enable_if<sizeof(U) < sizeof(T)>::type>
    inline operator c2_cntr_t<U>() {
        return c2_cntr_t<U>(mValue);
    }

    /**
     * Arithmetic operators
     */

#define DEFINE_C2_CNTR_BINARY_OP(attrib, op, op_assign) \
    template<typename U> \
    attrib inline c2_cntr_t<T>& operator op_assign(const U &value) { \
        mValue op_assign compat::get(value); \
        return *this; \
    } \
    \
    template<typename U, typename E=decltype(compat::get(U(0)))> \
    attrib inline constexpr c2_cntr_t<T> operator op(const U &value) const { \
        return c2_cntr_t<T>(mValue op compat::get(value)); \
    } \
    \
    template<typename U, typename E=typename std::enable_if<sizeof(U) < sizeof(T)>::type> \
    attrib inline constexpr c2_cntr_t<U> operator op(const c2_cntr_t<U> &value) const { \
        return c2_cntr_t<U>(U(mValue) op value.peeku()); \
    }

#define DEFINE_C2_CNTR_UNARY_OP(attrib, op) \
    attrib inline constexpr c2_cntr_t<T> operator op() const { \
        return c2_cntr_t<T>(op mValue); \
    }

#define DEFINE_C2_CNTR_CREMENT_OP(attrib, op) \
    attrib inline c2_cntr_t<T> &operator op() { \
        op mValue; \
        return *this; \
    } \
    attrib inline c2_cntr_t<T> operator op(int) { \
        return c2_cntr_t<T, void>(mValue op); \
    }

    DEFINE_C2_CNTR_BINARY_OP(__attribute__((no_sanitize("integer"))), +, +=)
    DEFINE_C2_CNTR_BINARY_OP(__attribute__((no_sanitize("integer"))), -, -=)
    DEFINE_C2_CNTR_BINARY_OP(__attribute__((no_sanitize("integer"))), *, *=)

    DEFINE_C2_CNTR_UNARY_OP(__attribute__((no_sanitize("integer"))), -)
    DEFINE_C2_CNTR_UNARY_OP(__attribute__((no_sanitize("integer"))), +)

    DEFINE_C2_CNTR_CREMENT_OP(__attribute__((no_sanitize("integer"))), ++)
    DEFINE_C2_CNTR_CREMENT_OP(__attribute__((no_sanitize("integer"))), --)

    template<typename U, typename E=typename std::enable_if<std::is_unsigned<U>::value>::type>
    __attribute__((no_sanitize("integer")))
    inline constexpr c2_cntr_t<T> operator<<(const U &value) const {
        return c2_cntr_t<T>(mValue << value);
    }

    template<typename U, typename E=typename std::enable_if<std::is_unsigned<U>::value>::type>
    __attribute__((no_sanitize("integer")))
    inline c2_cntr_t<T> &operator<<=(const U &value) {
        mValue <<= value;
        return *this;
    }

    /**
     * Comparison operators
     */
    __attribute__((no_sanitize("integer")))
    inline constexpr bool operator<=(const c2_cntr_t<T> &other) const {
        return T(other.mValue - mValue) < HALF_RANGE;
    }

    __attribute__((no_sanitize("integer")))
    inline constexpr bool operator>=(const c2_cntr_t<T> &other) const {
        return T(mValue - other.mValue) < HALF_RANGE;
    }

    inline constexpr bool operator==(const c2_cntr_t<T> &other) const {
        return mValue == other.mValue;
    }

    inline constexpr bool operator!=(const c2_cntr_t<T> &other) const {
        return !(*this == other);
    }

    inline constexpr bool operator<(const c2_cntr_t<T> &other) const {
        return *this <= other && *this != other;
    }

    inline constexpr bool operator>(const c2_cntr_t<T> &other) const {
        return *this >= other && *this != other;
    }
};

template<typename U, typename T, typename E=typename std::enable_if<std::is_integral<U>::value>::type>
inline constexpr c2_cntr_t<T> operator+(const U &a, const c2_cntr_t<T> &b) {
    return b + a;
}

template<typename U, typename T, typename E=typename std::enable_if<std::is_integral<U>::value>::type>
inline constexpr c2_cntr_t<T> operator-(const U &a, const c2_cntr_t<T> &b) {
    return c2_cntr_t<T>(a) - b;
}

template<typename U, typename T, typename E=typename std::enable_if<std::is_integral<U>::value>::type>
inline constexpr c2_cntr_t<T> operator*(const U &a, const c2_cntr_t<T> &b) {
    return b * a;
}

typedef c2_cntr_t<uint32_t> c2_cntr32_t; /** 32-bit counter type */
typedef c2_cntr_t<uint64_t> c2_cntr64_t; /** 64-bit counter type */

/// \cond INTERNAL

/// \defgroup utils_internal
@@ -326,4 +539,30 @@ constexpr typename c2_types<T, U, V...>::min_type c2_min(const T a, const U b, c
} // namespace android
#endif

#include <functional>
template<typename T>
struct std::less<::android::c2_cntr_t<T>> {
    constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
        return lh.peeku() < rh.peeku();
    }
};
template<typename T>
struct std::less_equal<::android::c2_cntr_t<T>> {
    constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
        return lh.peeku() <= rh.peeku();
    }
};
template<typename T>
struct std::greater<::android::c2_cntr_t<T>> {
    constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
        return lh.peeku() > rh.peeku();
    }
};
template<typename T>
struct std::greater_equal<::android::c2_cntr_t<T>> {
    constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
        return lh.peeku() >= rh.peeku();
    }
};

#endif  // C2_H_
+33 −17
Original line number Diff line number Diff line
@@ -737,15 +737,19 @@ public:
    union Primitive {
        int32_t     i32;   ///< int32_t value
        uint32_t    u32;   ///< uint32_t value
        c2_cntr32_t c32;   ///< c2_cntr32_t value
        int64_t     i64;   ///< int64_t value
        uint64_t    u64;   ///< uint64_t value
        c2_cntr64_t c64;   ///< c2_cntr64_t value
        float       fp;    ///< float value

        // constructors - implicit
        Primitive(int32_t value)     : i32(value) { }
        Primitive(uint32_t value)    : u32(value) { }
        Primitive(c2_cntr32_t value) : c32(value) { }
        Primitive(int64_t value)     : i64(value) { }
        Primitive(uint64_t value)    : u64(value) { }
        Primitive(c2_cntr64_t value) : c64(value) { }
        Primitive(float value)       : fp(value)  { }

        Primitive() : u64(0) { }
@@ -759,8 +763,10 @@ public:
        NO_INIT,
        INT32,
        UINT32,
        CNTR32,
        INT64,
        UINT64,
        CNTR64,
        FLOAT,
    };

@@ -792,12 +798,16 @@ template<> inline const int32_t &C2Value::Primitive::ref<int32_t>() const { retu
template<> inline const int64_t &C2Value::Primitive::ref<int64_t>() const { return i64; }
template<> inline const uint32_t &C2Value::Primitive::ref<uint32_t>() const { return u32; }
template<> inline const uint64_t &C2Value::Primitive::ref<uint64_t>() const { return u64; }
template<> inline const c2_cntr32_t &C2Value::Primitive::ref<c2_cntr32_t>() const { return c32; }
template<> inline const c2_cntr64_t &C2Value::Primitive::ref<c2_cntr64_t>() const { return c64; }
template<> inline const float &C2Value::Primitive::ref<float>() const { return fp; }

template<> constexpr C2Value::type_t C2Value::typeFor<int32_t>() { return INT32; }
template<> constexpr C2Value::type_t C2Value::typeFor<int64_t>() { return INT64; }
template<> constexpr C2Value::type_t C2Value::typeFor<uint32_t>() { return UINT32; }
template<> constexpr C2Value::type_t C2Value::typeFor<uint64_t>() { return UINT64; }
template<> constexpr C2Value::type_t C2Value::typeFor<c2_cntr32_t>() { return CNTR32; }
template<> constexpr C2Value::type_t C2Value::typeFor<c2_cntr64_t>() { return CNTR64; }
template<> constexpr C2Value::type_t C2Value::typeFor<float>() { return FLOAT; }

/**
@@ -817,8 +827,10 @@ struct C2FieldDescriptor {
        // primitive types
        INT32   = C2Value::INT32,  ///< 32-bit signed integer
        UINT32  = C2Value::UINT32, ///< 32-bit unsigned integer
        CNTR32  = C2Value::CNTR32, ///< 32-bit counter
        INT64   = C2Value::INT64,  ///< 64-bit signed integer
        UINT64  = C2Value::UINT64, ///< 64-bit signed integer
        CNTR64  = C2Value::CNTR64, ///< 64-bit counter
        FLOAT   = C2Value::FLOAT,  ///< 32-bit floating point

        // array types
@@ -905,8 +917,10 @@ private:
    // type resolution
    inline static type_t getType(int32_t*)     { return INT32; }
    inline static type_t getType(uint32_t*)    { return UINT32; }
    inline static type_t getType(c2_cntr32_t*) { return CNTR32; }
    inline static type_t getType(int64_t*)     { return INT64; }
    inline static type_t getType(uint64_t*)    { return UINT64; }
    inline static type_t getType(c2_cntr64_t*) { return CNTR64; }
    inline static type_t getType(float*)       { return FLOAT; }
    inline static type_t getType(char*)        { return STRING; }
    inline static type_t getType(uint8_t*)     { return BLOB; }
@@ -936,8 +950,10 @@ template<> inline C2FieldDescriptor::named_values_type C2FieldDescriptor::namedV
// non-enumerated integral types.
DEFINE_NO_NAMED_VALUES_FOR(int32_t)
DEFINE_NO_NAMED_VALUES_FOR(uint32_t)
DEFINE_NO_NAMED_VALUES_FOR(c2_cntr32_t)
DEFINE_NO_NAMED_VALUES_FOR(int64_t)
DEFINE_NO_NAMED_VALUES_FOR(uint64_t)
DEFINE_NO_NAMED_VALUES_FOR(c2_cntr64_t)
DEFINE_NO_NAMED_VALUES_FOR(uint8_t)
DEFINE_NO_NAMED_VALUES_FOR(char)
DEFINE_NO_NAMED_VALUES_FOR(float)
+84 −0
Original line number Diff line number Diff line
@@ -75,4 +75,88 @@ static_assert(c2_const_checker<max_u32_u32>::num() == 2, "should be 2");
static_assert(c2_const_checker<max_u32_u64>::num() == 3, "should be 3");
static_assert(c2_const_checker<max_u32_u8>::num() == 0x7fffffff, "should be 0x7fffffff");

/* ======================================= COUNTER TESTS ======================================= */

void c2_cntr_static_test() {
    // sanity checks for construction/assignment
    constexpr c2_cntr32_t c32_a(123);
    constexpr c2_cntr64_t c64_a(-456);
    c2_cntr32_t c32_b __unused = c64_a;
    // c32_b = 64.; // DISALLOWED
    // c2_cntr64_t c64_b = c32_a; // DISALLOWED

    // sanity checks for some constexpr operators
    static_assert(std::is_same<decltype(c32_a + c64_a), decltype(c64_a + c32_a)>::value, "+ should result same type");
    static_assert(c32_a + c64_a == c2_cntr32_t(-333), "123 + -456 = -333");
    static_assert(c32_a + c32_a == c2_cntr32_t(246), "123 + 123 = 246");
    static_assert(c64_a + c32_a == c2_cntr32_t(-333), "-456 + 123 = 579");
    static_assert(std::is_same<decltype(c32_a + 1), decltype(1 + c32_a)>::value, "+ should result same type");
    static_assert(c32_a + 456 == c2_cntr32_t(579), "123 + 456 = 579");
    static_assert(456 + c64_a == c2_cntr64_t(0), "456 + -456 = 0");
    static_assert(std::is_same<decltype(c32_a - c64_a), decltype(c64_a - c32_a)>::value, "- should result same type");
    static_assert(c32_a - c64_a == c2_cntr32_t(579), "123 - -456 = 579");
    static_assert(c32_a - c32_a == c2_cntr32_t(0), "123 - 123 = 0");
    static_assert(c64_a - c32_a == c2_cntr32_t(-579), "-456 - 123 = -579");
    static_assert(std::is_same<decltype(c32_a - 1), decltype(1 - c32_a)>::value, "- should result same type");
    static_assert(c32_a - 456 == c2_cntr32_t(-333), "123 - 456 = -333");
    static_assert(456 - c64_a == c2_cntr64_t(912), "456 - -456 = 912");
    static_assert(std::is_same<decltype(c32_a * c64_a), decltype(c64_a * c32_a)>::value, "* should result same type");
    static_assert(c32_a * c64_a == c2_cntr32_t(-56088), "123 * -456 = -56088");
    static_assert(c32_a * c32_a == c2_cntr32_t(15129), "123 * 123 = 15129");
    static_assert(c64_a * c32_a == c2_cntr32_t(-56088), "-456 * 123 = -56088");
    static_assert(std::is_same<decltype(c32_a * 1), decltype(1 * c32_a)>::value, "* should result same type");
    static_assert(c32_a * 456 == c2_cntr32_t(56088), "123 * 456 = 56088");
    static_assert(456 * c64_a == c2_cntr64_t(-207936), "456 * -456 = -207936");

    static_assert((c32_a << 26u) == c2_cntr32_t(0xEC000000), "123 << 26 = 0xEC000000");

    // sanity checks for unary operators
    static_assert(c2_cntr32_t(1) == +c2_cntr32_t(1), "1 == +1");
    static_assert(c2_cntr32_t(1) == -c2_cntr32_t(-1), "1 == --1");

    // sanity checks for comparison
    using c8_t = c2_cntr_t<uint8_t>;
    static_assert(c8_t(-0x80) > c8_t(0x7f), "80 > 7F");
    static_assert(c8_t(-0x80) >= c8_t(0x7f), "80 >= 7F");
    static_assert(c8_t(0x7f) > c8_t(0x7e), "7F > 7E");
    static_assert(c8_t(0x7f) >= c8_t(0x7e), "7F >= 7E");
    static_assert(!(c8_t(-0x80) > c8_t(0)), "80 !> 00");
    static_assert(!(c8_t(-0x80) >= c8_t(0)), "80 !>= 00");
    static_assert(!(c8_t(-0x80) > c8_t(-0x80)), "80 !> 80");
    static_assert(c8_t(-0x80) >= c8_t(-0x80), "80 >= 80");

    static_assert(c8_t(-0x80) == c8_t(0x80), "80 == 80");
    static_assert(!(c8_t(-0x80) == c8_t(0)), "80 != 0");
    static_assert(c8_t(-0x80) != c8_t(0x7f), "80 != 7F");
    static_assert(!(c8_t(0x7f) != c8_t(0x7f)), "80 != 7F");

    static_assert(c8_t(0x7f) < c8_t(-0x80), "7F < 80");
    static_assert(c8_t(0x7f) <= c8_t(-0x80), "7F < 80");
    static_assert(c8_t(0x7e) < c8_t(0x7f), "7E < 7F");
    static_assert(c8_t(0x7e) <= c8_t(0x7f), "7E < 7F");
    static_assert(!(c8_t(-0x40) < c8_t(0x40)), "-40 !< 40");
    static_assert(!(c8_t(-0x40) <= c8_t(0x40)), "-40 !<= 40");
    static_assert(!(c8_t(-0x40) < c8_t(-0x40)), "-40 !< -40");
    static_assert(c8_t(-0x40) <= c8_t(-0x40), "-40 <= -40");

    static_assert(c2_cntr32_t(-0x7fffffff - 1) > c2_cntr32_t(0x7fffffff), "80 > 7F");
    static_assert(!(c2_cntr32_t(-0x7fffffff - 1) > c2_cntr32_t(0)), "80 !> 00");
    static_assert(c2_cntr32_t(1) == c2_cntr32_t(c2_cntr64_t(0x100000001ul)), "1 == 1");
}

class C2Test : public ::testing::Test {
};

TEST_F(C2Test, CounterTest) {
    c2_cntr32_t c32_a(123);
    c2_cntr64_t c64_a(-456);
    EXPECT_EQ(c32_a += 3, c2_cntr32_t(126));
    EXPECT_EQ(c32_a += c64_a, c2_cntr32_t(-330));
    EXPECT_EQ(c32_a <<= 2u, c2_cntr32_t(-1320));
    EXPECT_EQ(c64_a *= 3, c2_cntr64_t(-1368));
    EXPECT_EQ(c32_a -= c64_a, c2_cntr32_t(48));
    EXPECT_EQ(c32_a -= 40, c2_cntr32_t(8));
    EXPECT_EQ(c32_a *= c32_a, c2_cntr32_t(64));
}

} // namespace android