[feat] update congestion control feedback

This commit is contained in:
dijunkun
2024-12-18 17:27:42 +08:00
parent c6d4b172fc
commit 2512e1eb15
18 changed files with 1417 additions and 2 deletions

257
src/common/array_view.h Normal file
View File

@@ -0,0 +1,257 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-12-18
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _ARRAY_VIEW_H_
#define _ARRAY_VIEW_H_
#include <algorithm>
#include <array>
#include <cstddef>
#include <iterator>
#include <type_traits>
#include "type_traits.h"
namespace array_view_internal {
// Magic constant for indicating that the size of an ArrayView is variable
// instead of fixed.
enum : std::ptrdiff_t { kArrayViewVarSize = -4711 };
// Base class for ArrayViews of fixed nonzero size.
template <typename T, std::ptrdiff_t Size>
class ArrayViewBase {
static_assert(Size > 0, "ArrayView size must be variable or non-negative");
public:
ArrayViewBase(T* data, size_t /* size */) : data_(data) {}
static constexpr size_t size() { return Size; }
static constexpr bool empty() { return false; }
T* data() const { return data_; }
protected:
static constexpr bool fixed_size() { return true; }
private:
T* data_;
};
// Specialized base class for ArrayViews of fixed zero size.
template <typename T>
class ArrayViewBase<T, 0> {
public:
explicit ArrayViewBase(T* /* data */, size_t /* size */) {}
static constexpr size_t size() { return 0; }
static constexpr bool empty() { return true; }
T* data() const { return nullptr; }
protected:
static constexpr bool fixed_size() { return true; }
};
// Specialized base class for ArrayViews of variable size.
template <typename T>
class ArrayViewBase<T, array_view_internal::kArrayViewVarSize> {
public:
ArrayViewBase(T* data, size_t size)
: data_(size == 0 ? nullptr : data), size_(size) {}
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
T* data() const { return data_; }
protected:
static constexpr bool fixed_size() { return false; }
private:
T* data_;
size_t size_;
};
} // namespace array_view_internal
template <typename T,
std::ptrdiff_t Size = array_view_internal::kArrayViewVarSize>
class ArrayView final : public array_view_internal::ArrayViewBase<T, Size> {
public:
using value_type = T;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using const_iterator = const T*;
// Construct an ArrayView from a pointer and a length.
template <typename U>
ArrayView(U* data, size_t size)
: array_view_internal::ArrayViewBase<T, Size>::ArrayViewBase(data, size) {
RTC_DCHECK_EQ(size == 0 ? nullptr : data, this->data());
RTC_DCHECK_EQ(size, this->size());
RTC_DCHECK_EQ(!this->data(),
this->size() == 0); // data is null iff size == 0.
}
// Construct an empty ArrayView. Note that fixed-size ArrayViews of size > 0
// cannot be empty.
ArrayView() : ArrayView(nullptr, 0) {}
ArrayView(std::nullptr_t) // NOLINT
: ArrayView() {}
ArrayView(std::nullptr_t, size_t size)
: ArrayView(static_cast<T*>(nullptr), size) {
static_assert(Size == 0 || Size == array_view_internal::kArrayViewVarSize,
"");
RTC_DCHECK_EQ(0, size);
}
// Construct an ArrayView from a C-style array.
template <typename U, size_t N>
ArrayView(U (&array)[N]) // NOLINT
: ArrayView(array, N) {
static_assert(Size == N || Size == array_view_internal::kArrayViewVarSize,
"Array size must match ArrayView size");
}
// (Only if size is fixed.) Construct a fixed size ArrayView<T, N> from a
// non-const std::array instance. For an ArrayView with variable size, the
// used ctor is ArrayView(U& u) instead.
template <typename U, size_t N,
typename std::enable_if<
Size == static_cast<std::ptrdiff_t>(N)>::type* = nullptr>
ArrayView(std::array<U, N>& u) // NOLINT
: ArrayView(u.data(), u.size()) {}
// (Only if size is fixed.) Construct a fixed size ArrayView<T, N> where T is
// const from a const(expr) std::array instance. For an ArrayView with
// variable size, the used ctor is ArrayView(U& u) instead.
template <typename U, size_t N,
typename std::enable_if<
Size == static_cast<std::ptrdiff_t>(N)>::type* = nullptr>
ArrayView(const std::array<U, N>& u) // NOLINT
: ArrayView(u.data(), u.size()) {}
// (Only if size is fixed.) Construct an ArrayView from any type U that has a
// static constexpr size() method whose return value is equal to Size, and a
// data() method whose return value converts implicitly to T*. In particular,
// this means we allow conversion from ArrayView<T, N> to ArrayView<const T,
// N>, but not the other way around. We also don't allow conversion from
// ArrayView<T> to ArrayView<T, N>, or from ArrayView<T, M> to ArrayView<T,
// N> when M != N.
template <typename U, typename std::enable_if<
Size != array_view_internal::kArrayViewVarSize &&
HasDataAndSize<U, T>::value>::type* = nullptr>
ArrayView(U& u) // NOLINT
: ArrayView(u.data(), u.size()) {
static_assert(U::size() == Size, "Sizes must match exactly");
}
template <typename U, typename std::enable_if<
Size != array_view_internal::kArrayViewVarSize &&
HasDataAndSize<U, T>::value>::type* = nullptr>
ArrayView(const U& u) // NOLINT(runtime/explicit)
: ArrayView(u.data(), u.size()) {
static_assert(U::size() == Size, "Sizes must match exactly");
}
// (Only if size is variable.) Construct an ArrayView from any type U that
// has a size() method whose return value converts implicitly to size_t, and
// a data() method whose return value converts implicitly to T*. In
// particular, this means we allow conversion from ArrayView<T> to
// ArrayView<const T>, but not the other way around. Other allowed
// conversions include
// ArrayView<T, N> to ArrayView<T> or ArrayView<const T>,
// std::vector<T> to ArrayView<T> or ArrayView<const T>,
// const std::vector<T> to ArrayView<const T>,
// rtc::Buffer to ArrayView<uint8_t> or ArrayView<const uint8_t>, and
// const rtc::Buffer to ArrayView<const uint8_t>.
template <typename U, typename std::enable_if<
Size == array_view_internal::kArrayViewVarSize &&
HasDataAndSize<U, T>::value>::type* = nullptr>
ArrayView(U& u) // NOLINT
: ArrayView(u.data(), u.size()) {}
template <typename U, typename std::enable_if<
Size == array_view_internal::kArrayViewVarSize &&
HasDataAndSize<U, T>::value>::type* = nullptr>
ArrayView(const U& u) // NOLINT(runtime/explicit)
: ArrayView(u.data(), u.size()) {}
// Indexing and iteration. These allow mutation even if the ArrayView is
// const, because the ArrayView doesn't own the array. (To prevent mutation,
// use a const element type.)
T& operator[](size_t idx) const {
RTC_DCHECK_LT(idx, this->size());
RTC_DCHECK(this->data());
return this->data()[idx];
}
T* begin() const { return this->data(); }
T* end() const { return this->data() + this->size(); }
const T* cbegin() const { return this->data(); }
const T* cend() const { return this->data() + this->size(); }
std::reverse_iterator<T*> rbegin() const {
return std::make_reverse_iterator(end());
}
std::reverse_iterator<T*> rend() const {
return std::make_reverse_iterator(begin());
}
std::reverse_iterator<const T*> crbegin() const {
return std::make_reverse_iterator(cend());
}
std::reverse_iterator<const T*> crend() const {
return std::make_reverse_iterator(cbegin());
}
ArrayView<T> subview(size_t offset, size_t size) const {
return offset < this->size()
? ArrayView<T>(this->data() + offset,
std::min(size, this->size() - offset))
: ArrayView<T>();
}
ArrayView<T> subview(size_t offset) const {
return subview(offset, this->size());
}
};
// Comparing two ArrayViews compares their (pointer,size) pairs; it does *not*
// dereference the pointers.
template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
bool operator==(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
return a.data() == b.data() && a.size() == b.size();
}
template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
bool operator!=(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
return !(a == b);
}
// Variable-size ArrayViews are the size of two pointers; fixed-size ArrayViews
// are the size of one pointer. (And as a special case, fixed-size ArrayViews
// of size 0 require no storage.)
static_assert(sizeof(ArrayView<int>) == 2 * sizeof(int*), "");
static_assert(sizeof(ArrayView<int, 17>) == sizeof(int*), "");
static_assert(std::is_empty<ArrayView<int, 0>>::value, "");
template <typename T>
inline ArrayView<T> MakeArrayView(T* data, size_t size) {
return ArrayView<T>(data, size);
}
// Only for primitive types that have the same size and aligment.
// Allow reinterpret cast of the array view to another primitive type of the
// same size.
// Template arguments order is (U, T, Size) to allow deduction of the template
// arguments in client calls: reinterpret_array_view<target_type>(array_view).
template <typename U, typename T, std::ptrdiff_t Size>
inline ArrayView<U, Size> reinterpret_array_view(ArrayView<T, Size> view) {
static_assert(sizeof(U) == sizeof(T) && alignof(U) == alignof(T),
"ArrayView reinterpret_cast is only supported for casting "
"between views that represent the same chunk of memory.");
static_assert(
std::is_fundamental<T>::value && std::is_fundamental<U>::value,
"ArrayView reinterpret_cast is only supported for casting between "
"fundamental types.");
return ArrayView<U, Size>(reinterpret_cast<U*>(view.data()), view.size());
}
#endif

394
src/common/byte_io.h Normal file
View File

@@ -0,0 +1,394 @@
/*
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_
#define MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_
// This file contains classes for reading and writing integer types from/to
// byte array representations. Signed/unsigned, partial (whole byte) sizes,
// and big/little endian byte order is all supported.
//
// Usage examples:
//
// uint8_t* buffer = ...;
//
// // Read an unsigned 4 byte integer in big endian format
// uint32_t val = ByteReader<uint32_t>::ReadBigEndian(buffer);
//
// // Read a signed 24-bit (3 byte) integer in little endian format
// int32_t val = ByteReader<int32_t, 3>::ReadLittle(buffer);
//
// // Write an unsigned 8 byte integer in little endian format
// ByteWriter<uint64_t>::WriteLittleEndian(buffer, val);
//
// Write an unsigned 40-bit (5 byte) integer in big endian format
// ByteWriter<uint64_t, 5>::WriteBigEndian(buffer, val);
//
// These classes are implemented as recursive templetizations, intended to make
// it easy for the compiler to completely inline the reading/writing.
#include <stdint.h>
#include <limits>
// According to ISO C standard ISO/IEC 9899, section 6.2.6.2 (2), the three
// representations of signed integers allowed are two's complement, one's
// complement and sign/magnitude. We can detect which is used by looking at
// the two last bits of -1, which will be 11 in two's complement, 10 in one's
// complement and 01 in sign/magnitude.
// TODO(sprang): In the unlikely event that we actually need to support a
// platform that doesn't use two's complement, implement conversion to/from
// wire format.
// Assume the if any one signed integer type is two's complement, then all
// other will be too.
static_assert(
(-1 & 0x03) == 0x03,
"Only two's complement representation of signed integers supported.");
// Plain const char* won't work for static_assert, use #define instead.
#define kSizeErrorMsg "Byte size must be less than or equal to data type size."
// Utility class for getting the unsigned equivalent of a signed type.
template <typename T>
struct UnsignedOf;
// Class for reading integers from a sequence of bytes.
// T = type of integer, B = bytes to read, is_signed = true if signed integer.
// If is_signed is true and B < sizeof(T), sign extension might be needed.
template <typename T, unsigned int B = sizeof(T),
bool is_signed = std::numeric_limits<T>::is_signed>
class ByteReader;
// Specialization of ByteReader for unsigned types.
template <typename T, unsigned int B>
class ByteReader<T, B, false> {
public:
static T ReadBigEndian(const uint8_t* data) {
static_assert(B <= sizeof(T), kSizeErrorMsg);
return InternalReadBigEndian(data);
}
static T ReadLittleEndian(const uint8_t* data) {
static_assert(B <= sizeof(T), kSizeErrorMsg);
return InternalReadLittleEndian(data);
}
private:
static T InternalReadBigEndian(const uint8_t* data) {
T val(0);
for (unsigned int i = 0; i < B; ++i)
val |= static_cast<T>(data[i]) << ((B - 1 - i) * 8);
return val;
}
static T InternalReadLittleEndian(const uint8_t* data) {
T val(0);
for (unsigned int i = 0; i < B; ++i)
val |= static_cast<T>(data[i]) << (i * 8);
return val;
}
};
// Specialization of ByteReader for signed types.
template <typename T, unsigned int B>
class ByteReader<T, B, true> {
public:
typedef typename UnsignedOf<T>::Type U;
static T ReadBigEndian(const uint8_t* data) {
U unsigned_val = ByteReader<T, B, false>::ReadBigEndian(data);
if (B < sizeof(T)) unsigned_val = SignExtend(unsigned_val);
return ReinterpretAsSigned(unsigned_val);
}
static T ReadLittleEndian(const uint8_t* data) {
U unsigned_val = ByteReader<T, B, false>::ReadLittleEndian(data);
if (B < sizeof(T)) unsigned_val = SignExtend(unsigned_val);
return ReinterpretAsSigned(unsigned_val);
}
private:
// As a hack to avoid implementation-specific or undefined behavior when
// bit-shifting or casting signed integers, read as a signed equivalent
// instead and convert to signed. This is safe since we have asserted that
// two's complement for is used.
static T ReinterpretAsSigned(U unsigned_val) {
// An unsigned value with only the highest order bit set (ex 0x80).
const U kUnsignedHighestBitMask = static_cast<U>(1)
<< ((sizeof(U) * 8) - 1);
// A signed value with only the highest bit set. Since this is two's
// complement form, we can use the min value from std::numeric_limits.
const T kSignedHighestBitMask = std::numeric_limits<T>::min();
T val;
if ((unsigned_val & kUnsignedHighestBitMask) != 0) {
// Casting is only safe when unsigned value can be represented in the
// signed target type, so mask out highest bit and mask it back manually.
val = static_cast<T>(unsigned_val & ~kUnsignedHighestBitMask);
val |= kSignedHighestBitMask;
} else {
val = static_cast<T>(unsigned_val);
}
return val;
}
// If number of bytes is less than native data type (eg 24 bit, in int32_t),
// and the most significant bit of the actual data is set, we must sign
// extend the remaining byte(s) with ones so that the correct negative
// number is retained.
// Ex: 0x810A0B -> 0xFF810A0B, but 0x710A0B -> 0x00710A0B
static U SignExtend(const U val) {
const uint8_t kMsb = static_cast<uint8_t>(val >> ((B - 1) * 8));
if ((kMsb & 0x80) != 0) {
// Create a mask where all bits used by the B bytes are set to one,
// for instance 0x00FFFFFF for B = 3. Bit-wise invert that mask (to
// (0xFF000000 in the example above) and add it to the input value.
// The "B % sizeof(T)" is a workaround to undefined values warnings for
// B == sizeof(T), in which case this code won't be called anyway.
const U kUsedBitsMask = (1 << ((B % sizeof(T)) * 8)) - 1;
return ~kUsedBitsMask | val;
}
return val;
}
};
// Class for writing integers to a sequence of bytes
// T = type of integer, B = bytes to write
template <typename T, unsigned int B = sizeof(T),
bool is_signed = std::numeric_limits<T>::is_signed>
class ByteWriter;
// Specialization of ByteWriter for unsigned types.
template <typename T, unsigned int B>
class ByteWriter<T, B, false> {
public:
static void WriteBigEndian(uint8_t* data, T val) {
static_assert(B <= sizeof(T), kSizeErrorMsg);
for (unsigned int i = 0; i < B; ++i) {
data[i] = val >> ((B - 1 - i) * 8);
}
}
static void WriteLittleEndian(uint8_t* data, T val) {
static_assert(B <= sizeof(T), kSizeErrorMsg);
for (unsigned int i = 0; i < B; ++i) {
data[i] = val >> (i * 8);
}
}
};
// Specialization of ByteWriter for signed types.
template <typename T, unsigned int B>
class ByteWriter<T, B, true> {
public:
typedef typename UnsignedOf<T>::Type U;
static void WriteBigEndian(uint8_t* data, T val) {
ByteWriter<U, B, false>::WriteBigEndian(data, ReinterpretAsUnsigned(val));
}
static void WriteLittleEndian(uint8_t* data, T val) {
ByteWriter<U, B, false>::WriteLittleEndian(data,
ReinterpretAsUnsigned(val));
}
private:
static U ReinterpretAsUnsigned(T val) {
// According to ISO C standard ISO/IEC 9899, section 6.3.1.3 (1, 2) a
// conversion from signed to unsigned keeps the value if the new type can
// represent it, and otherwise adds one more than the max value of T until
// the value is in range. For two's complement, this fortunately means
// that the bit-wise value will be intact. Thus, since we have asserted that
// two's complement form is actually used, a simple cast is sufficient.
return static_cast<U>(val);
}
};
// ----- Below follows specializations of UnsignedOf utility class -----
template <>
struct UnsignedOf<int8_t> {
typedef uint8_t Type;
};
template <>
struct UnsignedOf<int16_t> {
typedef uint16_t Type;
};
template <>
struct UnsignedOf<int32_t> {
typedef uint32_t Type;
};
template <>
struct UnsignedOf<int64_t> {
typedef uint64_t Type;
};
// ----- Below follows specializations for unsigned, B in { 1, 2, 4, 8 } -----
// TODO(sprang): Check if these actually help or if generic cases will be
// unrolled to and optimized to similar performance.
// Specializations for single bytes
template <typename T>
class ByteReader<T, 1, false> {
public:
static T ReadBigEndian(const uint8_t* data) {
static_assert(sizeof(T) == 1, kSizeErrorMsg);
return data[0];
}
static T ReadLittleEndian(const uint8_t* data) {
static_assert(sizeof(T) == 1, kSizeErrorMsg);
return data[0];
}
};
template <typename T>
class ByteWriter<T, 1, false> {
public:
static void WriteBigEndian(uint8_t* data, T val) {
static_assert(sizeof(T) == 1, kSizeErrorMsg);
data[0] = val;
}
static void WriteLittleEndian(uint8_t* data, T val) {
static_assert(sizeof(T) == 1, kSizeErrorMsg);
data[0] = val;
}
};
// Specializations for two byte words
template <typename T>
class ByteReader<T, 2, false> {
public:
static T ReadBigEndian(const uint8_t* data) {
static_assert(sizeof(T) >= 2, kSizeErrorMsg);
return (data[0] << 8) | data[1];
}
static T ReadLittleEndian(const uint8_t* data) {
static_assert(sizeof(T) >= 2, kSizeErrorMsg);
return data[0] | (data[1] << 8);
}
};
template <typename T>
class ByteWriter<T, 2, false> {
public:
static void WriteBigEndian(uint8_t* data, T val) {
static_assert(sizeof(T) >= 2, kSizeErrorMsg);
data[0] = val >> 8;
data[1] = val;
}
static void WriteLittleEndian(uint8_t* data, T val) {
static_assert(sizeof(T) >= 2, kSizeErrorMsg);
data[0] = val;
data[1] = val >> 8;
}
};
// Specializations for four byte words.
template <typename T>
class ByteReader<T, 4, false> {
public:
static T ReadBigEndian(const uint8_t* data) {
static_assert(sizeof(T) >= 4, kSizeErrorMsg);
return (Get(data, 0) << 24) | (Get(data, 1) << 16) | (Get(data, 2) << 8) |
Get(data, 3);
}
static T ReadLittleEndian(const uint8_t* data) {
static_assert(sizeof(T) >= 4, kSizeErrorMsg);
return Get(data, 0) | (Get(data, 1) << 8) | (Get(data, 2) << 16) |
(Get(data, 3) << 24);
}
private:
inline static T Get(const uint8_t* data, unsigned int index) {
return static_cast<T>(data[index]);
}
};
// Specializations for four byte words.
template <typename T>
class ByteWriter<T, 4, false> {
public:
static void WriteBigEndian(uint8_t* data, T val) {
static_assert(sizeof(T) >= 4, kSizeErrorMsg);
data[0] = val >> 24;
data[1] = val >> 16;
data[2] = val >> 8;
data[3] = val;
}
static void WriteLittleEndian(uint8_t* data, T val) {
static_assert(sizeof(T) >= 4, kSizeErrorMsg);
data[0] = val;
data[1] = val >> 8;
data[2] = val >> 16;
data[3] = val >> 24;
}
};
// Specializations for eight byte words.
template <typename T>
class ByteReader<T, 8, false> {
public:
static T ReadBigEndian(const uint8_t* data) {
static_assert(sizeof(T) >= 8, kSizeErrorMsg);
return (Get(data, 0) << 56) | (Get(data, 1) << 48) | (Get(data, 2) << 40) |
(Get(data, 3) << 32) | (Get(data, 4) << 24) | (Get(data, 5) << 16) |
(Get(data, 6) << 8) | Get(data, 7);
}
static T ReadLittleEndian(const uint8_t* data) {
static_assert(sizeof(T) >= 8, kSizeErrorMsg);
return Get(data, 0) | (Get(data, 1) << 8) | (Get(data, 2) << 16) |
(Get(data, 3) << 24) | (Get(data, 4) << 32) | (Get(data, 5) << 40) |
(Get(data, 6) << 48) | (Get(data, 7) << 56);
}
private:
inline static T Get(const uint8_t* data, unsigned int index) {
return static_cast<T>(data[index]);
}
};
template <typename T>
class ByteWriter<T, 8, false> {
public:
static void WriteBigEndian(uint8_t* data, T val) {
static_assert(sizeof(T) >= 8, kSizeErrorMsg);
data[0] = val >> 56;
data[1] = val >> 48;
data[2] = val >> 40;
data[3] = val >> 32;
data[4] = val >> 24;
data[5] = val >> 16;
data[6] = val >> 8;
data[7] = val;
}
static void WriteLittleEndian(uint8_t* data, T val) {
static_assert(sizeof(T) >= 8, kSizeErrorMsg);
data[0] = val;
data[1] = val >> 8;
data[2] = val >> 16;
data[3] = val >> 24;
data[4] = val >> 32;
data[5] = val >> 40;
data[6] = val >> 48;
data[7] = val >> 56;
}
};
#endif // MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_

141
src/common/type_traits.h Normal file
View File

@@ -0,0 +1,141 @@
/*
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef RTC_BASE_TYPE_TRAITS_H_
#define RTC_BASE_TYPE_TRAITS_H_
#include <cstddef>
#include <string>
#include <type_traits>
namespace rtc {
// Determines if the given class has zero-argument .data() and .size() methods
// whose return values are convertible to T* and size_t, respectively.
template <typename DS, typename T>
class HasDataAndSize {
private:
template <
typename C,
typename std::enable_if<
std::is_convertible<decltype(std::declval<C>().data()), T*>::value &&
std::is_convertible<decltype(std::declval<C>().size()),
std::size_t>::value>::type* = nullptr>
static int Test(int);
template <typename>
static char Test(...);
public:
static constexpr bool value = std::is_same<decltype(Test<DS>(0)), int>::value;
};
namespace test_has_data_and_size {
template <typename DR, typename SR>
struct Test1 {
DR data();
SR size();
};
static_assert(HasDataAndSize<Test1<int*, int>, int>::value, "");
static_assert(HasDataAndSize<Test1<int*, int>, const int>::value, "");
static_assert(HasDataAndSize<Test1<const int*, int>, const int>::value, "");
static_assert(!HasDataAndSize<Test1<const int*, int>, int>::value,
"implicit cast of const int* to int*");
static_assert(!HasDataAndSize<Test1<char*, size_t>, int>::value,
"implicit cast of char* to int*");
struct Test2 {
int* data;
size_t size;
};
static_assert(!HasDataAndSize<Test2, int>::value,
".data and .size aren't functions");
struct Test3 {
int* data();
};
static_assert(!HasDataAndSize<Test3, int>::value, ".size() is missing");
class Test4 {
int* data();
size_t size();
};
static_assert(!HasDataAndSize<Test4, int>::value,
".data() and .size() are private");
} // namespace test_has_data_and_size
namespace type_traits_impl {
// Determines if the given type is an enum that converts implicitly to
// an integral type.
template <typename T>
struct IsIntEnum {
private:
// This overload is used if the type is an enum, and unary plus
// compiles and turns it into an integral type.
template <typename X,
typename std::enable_if<
std::is_enum<X>::value &&
std::is_integral<decltype(+std::declval<X>())>::value>::type* =
nullptr>
static int Test(int);
// Otherwise, this overload is used.
template <typename>
static char Test(...);
public:
static constexpr bool value =
std::is_same<decltype(Test<typename std::remove_reference<T>::type>(0)),
int>::value;
};
} // namespace type_traits_impl
// Determines if the given type is integral, or an enum that
// converts implicitly to an integral type.
template <typename T>
struct IsIntlike {
private:
using X = typename std::remove_reference<T>::type;
public:
static constexpr bool value =
std::is_integral<X>::value || type_traits_impl::IsIntEnum<X>::value;
};
namespace test_enum_intlike {
enum E1 { e1 };
enum { e2 };
enum class E3 { e3 };
struct S {};
static_assert(type_traits_impl::IsIntEnum<E1>::value, "");
static_assert(type_traits_impl::IsIntEnum<decltype(e2)>::value, "");
static_assert(!type_traits_impl::IsIntEnum<E3>::value, "");
static_assert(!type_traits_impl::IsIntEnum<int>::value, "");
static_assert(!type_traits_impl::IsIntEnum<float>::value, "");
static_assert(!type_traits_impl::IsIntEnum<S>::value, "");
static_assert(IsIntlike<E1>::value, "");
static_assert(IsIntlike<decltype(e2)>::value, "");
static_assert(!IsIntlike<E3>::value, "");
static_assert(IsIntlike<int>::value, "");
static_assert(!IsIntlike<float>::value, "");
static_assert(!IsIntlike<S>::value, "");
} // namespace test_enum_intlike
} // namespace rtc
#endif // RTC_BASE_TYPE_TRAITS_H_