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

Commit 8048fde0 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "FTL: Extend enum utilities imported from IF"

parents 30bc17e2 7578845a
Loading
Loading
Loading
Loading
+20 −79
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 * Copyright 2020 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.
@@ -14,79 +14,22 @@
 * limitations under the License.
 */

#include <android-base/stringprintf.h>
#pragma once

#include <ftl/enum.h>
#include <ftl/string.h>

#include <array>
#include <cstdint>
#include <optional>
#include <iterator>
#include <string>
#include <type_traits>

#include <ftl/NamedEnum.h>
#include "utils/BitSet.h"

#pragma once
// TODO(b/185536303): Align with FTL style and namespace.

namespace android {

namespace details {

template <typename F>
inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__;

template <typename F, typename T, T... I>
constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) {
    constexpr size_t count = seq.size();

    std::array<F, count> values{};
    for (size_t i = 0, v = 0; v < count; ++i) {
        values[v++] = static_cast<F>(T{1} << i);
    }

    return values;
}

template <typename F>
inline constexpr auto flag_values = generate_flag_values<F>(
        std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{});

template <typename F, std::size_t... I>
constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept {
    return std::array<std::optional<std::string_view>, sizeof...(I)>{
            {enum_value_name<F, flag_values<F>[I]>()...}};
}

template <typename F>
inline constexpr auto flag_names =
        generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{});

// A trait for determining whether a type is specifically an enum class or not.
template <typename T, bool = std::is_enum_v<T>>
struct is_enum_class : std::false_type {};

// By definition, an enum class is an enum that is not implicitly convertible to its underlying
// type.
template <typename T>
struct is_enum_class<T, true>
      : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {};

template <typename T>
inline constexpr bool is_enum_class_v = is_enum_class<T>::value;
} // namespace details

template <auto V>
constexpr auto flag_name() {
    using F = decltype(V);
    return details::enum_value_name<F, V>();
}

template <typename F>
constexpr std::optional<std::string_view> flag_name(F flag) {
    using U = std::underlying_type_t<F>;
    auto idx = static_cast<size_t>(__builtin_ctzl(static_cast<U>(flag)));
    return details::flag_names<F>[idx];
}

/* A class for handling flags defined by an enum or enum class in a type-safe way. */
template <typename F>
class Flags {
@@ -94,7 +37,7 @@ class Flags {
    // further to avoid this restriction but in general we want to encourage the use of enums
    // anyways.
    static_assert(std::is_enum_v<F>, "Flags type must be an enum");
    using U = typename std::underlying_type_t<F>;
    using U = std::underlying_type_t<F>;

public:
    constexpr Flags(F f) : mFlags(static_cast<U>(f)) {}
@@ -106,11 +49,10 @@ public:
    // should force them to be explicitly constructed from their underlying types to make full use
    // of the type checker.
    template <typename T = U>
    constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr)
          : mFlags(t) {}
    constexpr Flags(T t, std::enable_if_t<!ftl::is_scoped_enum_v<F>, T>* = nullptr) : mFlags(t) {}

    template <typename T = U>
    explicit constexpr Flags(T t,
                             typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr)
    explicit constexpr Flags(T t, std::enable_if_t<ftl::is_scoped_enum_v<F>, T>* = nullptr)
          : mFlags(t) {}

    class Iterator {
@@ -229,16 +171,16 @@ public:
        bool first = true;
        U unstringified = 0;
        for (const F f : *this) {
            std::optional<std::string_view> flagString = flag_name(f);
            if (flagString) {
                appendFlag(result, flagString.value(), first);
            if (const auto flagName = ftl::flag_name(f)) {
                appendFlag(result, flagName.value(), first);
            } else {
                unstringified |= static_cast<U>(f);
            }
        }

        if (unstringified != 0) {
            appendFlag(result, base::StringPrintf("0x%08x", unstringified), first);
            constexpr auto radix = sizeof(U) == 1 ? ftl::Radix::kBin : ftl::Radix::kHex;
            appendFlag(result, ftl::to_string(unstringified, radix), first);
        }

        if (first) {
@@ -265,15 +207,14 @@ private:
// as flags. In order to use these, add them via a `using namespace` declaration.
namespace flag_operators {

template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>>
inline Flags<F> operator~(F f) {
    using U = typename std::underlying_type_t<F>;
    return static_cast<F>(~static_cast<U>(f));
    return static_cast<F>(~ftl::enum_cast(f));
}
template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>

template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>>
Flags<F> operator|(F lhs, F rhs) {
    using U = typename std::underlying_type_t<F>;
    return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs));
    return static_cast<F>(ftl::enum_cast(lhs) | ftl::enum_cast(rhs));
}

} // namespace flag_operators

include/ftl/NamedEnum.h

deleted100644 → 0
+0 −129
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.
 */

#include <android-base/stringprintf.h>

#include <array>
#include <cstdint>
#include <optional>
#include <string>

#pragma once

namespace android {

namespace details {
template <typename E, E V>
constexpr std::optional<std::string_view> enum_value_name() {
    // Should look something like (but all on one line):
    //   std::optional<std::string_view>
    //   android::details::enum_value_name()
    //   [E = android::test::TestEnums, V = android::test::TestEnums::ONE]
    std::string_view view = __PRETTY_FUNCTION__;
    size_t templateStart = view.rfind("[");
    size_t templateEnd = view.rfind("]");
    if (templateStart == std::string::npos || templateEnd == std::string::npos) {
        return std::nullopt;
    }

    // Extract the template parameters without the enclosing braces.
    // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE
    view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
    size_t valStart = view.rfind("V = ");
    if (valStart == std::string::npos) {
        return std::nullopt;
    }

    // Example (cont'd): V = android::test::TestEnums::ONE
    view = view.substr(valStart);
    // Check invalid enum values with cast, like V = (android::test::TestEnums)8.
    if (view.find('(') != std::string::npos) {
        return std::nullopt;
    }
    size_t nameStart = view.rfind("::");
    if (nameStart == std::string::npos) {
        return std::nullopt;
    }

    // Chop off the initial "::"
    nameStart += 2;
    return view.substr(nameStart);
}

template <typename E, typename T, T... I>
constexpr auto generate_enum_values(std::integer_sequence<T, I...> seq) {
    constexpr size_t count = seq.size();

    std::array<E, count> values{};
    for (size_t i = 0, v = 0; v < count; ++i) {
        values[v++] = static_cast<E>(T{0} + i);
    }

    return values;
}

template <typename E, std::size_t N>
inline constexpr auto enum_values =
        generate_enum_values<E>(std::make_integer_sequence<std::underlying_type_t<E>, N>{});

template <typename E, std::size_t N, std::size_t... I>
constexpr auto generate_enum_names(std::index_sequence<I...>) noexcept {
    return std::array<std::optional<std::string_view>, sizeof...(I)>{
            {enum_value_name<E, enum_values<E, N>[I]>()...}};
}

template <typename E, std::size_t N>
inline constexpr auto enum_names = generate_enum_names<E, N>(std::make_index_sequence<N>{});

} // namespace details

class NamedEnum {
public:
    // By default allowed enum value range is 0 ~ 7.
    template <typename E>
    static constexpr size_t max = 8;

    template <auto V>
    static constexpr auto enum_name() {
        using E = decltype(V);
        return details::enum_value_name<E, V>();
    }

    template <typename E>
    static constexpr std::optional<std::string_view> enum_name(E val) {
        auto idx = static_cast<size_t>(val);
        return idx < max<E> ? details::enum_names<E, max<E>>[idx] : std::nullopt;
    }

    // Helper function for parsing enum value to string.
    // Example : enum class TestEnums { ZERO = 0x0 };
    // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO".
    // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16,
    // it should be declared to specialized the maximum enum by below:
    // template <> constexpr size_t NamedEnum::max<TestEnums> = 16;
    // If the enum class definition is sparse and contains enum values starting from a large value,
    // Do not specialize it to a large number to avoid performance issues.
    // The recommended maximum enum number to specialize is 64.
    template <typename E>
    static const std::string string(E val, const char* fallbackFormat = "%02d") {
        std::string result;
        std::optional<std::string_view> enumString = enum_name(val);
        result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val);
        return result;
    }
};

} // namespace android

include/ftl/enum.h

0 → 100644
+299 −0
Original line number Diff line number Diff line
/*
 * Copyright 2021 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.
 */

#pragma once

#include <cstddef>
#include <limits>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>

#include <ftl/string.h>

// Returns the name of enumerator E::V (i.e. "V") as std::optional<std::string_view> by parsing the
// compiler-generated string literal for the signature of this function. The function is defined in
// the global namespace with a short name and inferred return type to reduce bloat in the read-only
// data segment.
template <typename E, E V>
constexpr auto ftl_enum() {
  static_assert(std::is_enum_v<E>);

  using R = std::optional<std::string_view>;
  using namespace std::literals;

  // The "pretty" signature has the following format:
  //
  //   auto ftl_enum() [E = android::test::Enum, V = android::test::Enum::kValue]
  //
  std::string_view view = __PRETTY_FUNCTION__;
  const auto template_begin = view.rfind('[');
  const auto template_end = view.rfind(']');
  if (template_begin == view.npos || template_end == view.npos) return R{};

  // Extract the template parameters without the enclosing brackets. Example (cont'd):
  //
  //   E = android::test::Enum, V = android::test::Enum::kValue
  //
  view = view.substr(template_begin + 1, template_end - template_begin - 1);
  const auto value_begin = view.rfind("V = "sv);
  if (value_begin == view.npos) return R{};

  // Example (cont'd):
  //
  //   V = android::test::Enum::kValue
  //
  view = view.substr(value_begin);
  const auto name_begin = view.rfind("::"sv);
  if (name_begin == view.npos) return R{};

  // Chop off the leading "::".
  const auto name = view.substr(name_begin + 2);

  // A value that is not enumerated has the format "Enum)42".
  return name.find(')') == view.npos ? R{name} : R{};
}

namespace android::ftl {

// Trait for determining whether a type is specifically a scoped enum or not. By definition, a
// scoped enum is one that is not implicitly convertible to its underlying type.
//
// TODO: Replace with std::is_scoped_enum in C++23.
//
template <typename T, bool = std::is_enum_v<T>>
struct is_scoped_enum : std::false_type {};

template <typename T>
struct is_scoped_enum<T, true> : std::negation<std::is_convertible<T, std::underlying_type_t<T>>> {
};

template <typename T>
inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value;

// Shorthand for casting an enumerator to its integral value.
//
//   enum class E { A, B, C };
//   static_assert(ftl::enum_cast(E::B) == 1);
//
template <typename E>
constexpr auto enum_cast(E v) {
  return static_cast<std::underlying_type_t<E>>(v);
}

// Traits for retrieving an enum's range. An enum specifies its range by defining enumerators named
// ftl_first and ftl_last. If omitted, ftl_first defaults to 0, whereas ftl_last defaults to N - 1
// where N is the bit width of the underlying type, but only if that type is unsigned, assuming the
// enumerators are flags. Also, note that unscoped enums must define both bounds, as casting out-of-
// range values results in undefined behavior if the underlying type is not fixed.
//
//   enum class E { A, B, C, F = 5, ftl_last = F };
//
//   static_assert(ftl::enum_begin_v<E> == E::A);
//   static_assert(ftl::enum_last_v<E> == E::F);
//   static_assert(ftl::enum_size_v<E> == 6);
//
//   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
//
//   static_assert(ftl::enum_begin_v<F> == F{0});
//   static_assert(ftl::enum_last_v<F> == F{15});
//   static_assert(ftl::enum_size_v<F> == 16);
//
template <typename E, typename = void>
struct enum_begin {
  static_assert(is_scoped_enum_v<E>, "Missing ftl_first enumerator");
  static constexpr E value{0};
};

template <typename E>
struct enum_begin<E, std::void_t<decltype(E::ftl_first)>> {
  static constexpr E value = E::ftl_first;
};

template <typename E>
inline constexpr E enum_begin_v = enum_begin<E>::value;

template <typename E, typename = void>
struct enum_end {
  using U = std::underlying_type_t<E>;
  static_assert(is_scoped_enum_v<E> && std::is_unsigned_v<U>, "Missing ftl_last enumerator");

  static constexpr E value{std::numeric_limits<U>::digits};
};

template <typename E>
struct enum_end<E, std::void_t<decltype(E::ftl_last)>> {
  static constexpr E value = E{enum_cast(E::ftl_last) + 1};
};

template <typename E>
inline constexpr E enum_end_v = enum_end<E>::value;

template <typename E>
inline constexpr E enum_last_v = E{enum_cast(enum_end_v<E>) - 1};

template <typename E>
struct enum_size {
  static constexpr auto kBegin = enum_cast(enum_begin_v<E>);
  static constexpr auto kEnd = enum_cast(enum_end_v<E>);
  static_assert(kBegin < kEnd, "Invalid range");

  static constexpr std::size_t value = kEnd - kBegin;
  static_assert(value <= 64, "Excessive range size");
};

template <typename E>
inline constexpr std::size_t enum_size_v = enum_size<E>::value;

namespace details {

template <auto V>
struct Identity {
  static constexpr auto value = V;
};

template <typename E>
using make_enum_sequence = std::make_integer_sequence<std::underlying_type_t<E>, enum_size_v<E>>;

template <typename E, template <E> class = Identity, typename = make_enum_sequence<E>>
struct EnumRange;

template <typename E, template <E> class F, typename T, T... Vs>
struct EnumRange<E, F, std::integer_sequence<T, Vs...>> {
  static constexpr auto kBegin = enum_cast(enum_begin_v<E>);
  static constexpr auto kSize = enum_size_v<E>;

  using R = decltype(F<E{}>::value);
  const R values[kSize] = {F<static_cast<E>(Vs + kBegin)>::value...};

  constexpr const auto* begin() const { return values; }
  constexpr const auto* end() const { return values + kSize; }
};

template <auto V>
struct EnumName {
  static constexpr auto value = ftl_enum<decltype(V), V>();
};

template <auto I>
struct FlagName {
  using E = decltype(I);
  using U = std::underlying_type_t<E>;

  static constexpr E V{U{1} << enum_cast(I)};
  static constexpr auto value = ftl_enum<E, V>();
};

}  // namespace details

// Returns an iterable over the range of an enum.
//
//   enum class E { A, B, C, F = 5, ftl_last = F };
//
//   std::string string;
//   for (E v : ftl::enum_range<E>()) {
//     string += ftl::enum_name(v).value_or("?");
//   }
//
//   assert(string == "ABC??F");
//
template <typename E>
constexpr auto enum_range() {
  return details::EnumRange<E>{};
}

// Returns a stringified enumerator at compile time.
//
//   enum class E { A, B, C };
//   static_assert(ftl::enum_name<E::B>() == "B");
//
template <auto V>
constexpr std::string_view enum_name() {
  constexpr auto kName = ftl_enum<decltype(V), V>();
  static_assert(kName, "Unknown enumerator");
  return *kName;
}

// Returns a stringified enumerator, possibly at compile time.
//
//   enum class E { A, B, C, F = 5, ftl_last = F };
//
//   static_assert(ftl::enum_name(E::C).value_or("?") == "C");
//   static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
//
template <typename E>
constexpr std::optional<std::string_view> enum_name(E v) {
  const auto value = enum_cast(v);

  constexpr auto kBegin = enum_cast(enum_begin_v<E>);
  constexpr auto kLast = enum_cast(enum_last_v<E>);
  if (value < kBegin || value > kLast) return {};

  constexpr auto kRange = details::EnumRange<E, details::EnumName>{};
  return kRange.values[value - kBegin];
}

// Returns a stringified flag enumerator, possibly at compile time.
//
//   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
//
//   static_assert(ftl::flag_name(F::Z).value_or("?") == "Z");
//   static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?");
//
template <typename E>
constexpr std::optional<std::string_view> flag_name(E v) {
  const auto value = enum_cast(v);

  // TODO: Replace with std::popcount and std::countr_zero in C++20.
  if (__builtin_popcountl(value) != 1) return {};

  constexpr auto kRange = details::EnumRange<E, details::FlagName>{};
  return kRange.values[__builtin_ctzl(value)];
}

// Returns a stringified enumerator, or its integral value if not named.
//
//   enum class E { A, B, C, F = 5, ftl_last = F };
//
//   assert(ftl::enum_string(E::C) == "C");
//   assert(ftl::enum_string(E{3}) == "3");
//
template <typename E>
inline std::string enum_string(E v) {
  if (const auto name = enum_name(v)) {
    return std::string(*name);
  }
  return to_string(enum_cast(v));
}

// Returns a stringified flag enumerator, or its integral value if not named.
//
//   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
//
//   assert(ftl::flag_string(F::Z) == "Z");
//   assert(ftl::flag_string(F{7}) == "0b111");
//
template <typename E>
inline std::string flag_string(E v) {
  if (const auto name = flag_name(v)) {
    return std::string(*name);
  }
  constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex;
  return to_string(enum_cast(v), radix);
}

}  // namespace android::ftl
+6 −4
Original line number Diff line number Diff line
@@ -18,7 +18,8 @@
#define _LIBINPUT_DISPLAY_VIEWPORT_H

#include <android-base/stringprintf.h>
#include <ftl/NamedEnum.h>
#include <ftl/enum.h>
#include <ftl/string.h>
#include <gui/constants.h>
#include <input/Input.h>

@@ -44,6 +45,8 @@ enum class ViewportType : int32_t {
    INTERNAL = 1,
    EXTERNAL = 2,
    VIRTUAL = 3,

    ftl_last = VIRTUAL
};

/*
@@ -132,9 +135,8 @@ struct DisplayViewport {
                            "physicalFrame=[%d, %d, %d, %d], "
                            "deviceSize=[%d, %d], "
                            "isActive=[%d]",
                            NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(),
                            physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str()
                                         : "<none>",
                            ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(),
                            physicalPort ? ftl::to_string(*physicalPort).c_str() : "<none>",
                            orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
                            physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth,
                            deviceHeight, isActive);
+5 −0
Original line number Diff line number Diff line
@@ -84,6 +84,9 @@ enum class InputDeviceSensorType : int32_t {
    GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR,
    GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED,
    SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION,

    ftl_first = ACCELEROMETER,
    ftl_last = SIGNIFICANT_MOTION
};

enum class InputDeviceSensorAccuracy : int32_t {
@@ -105,6 +108,8 @@ enum class InputDeviceLightType : int32_t {
    PLAYER_ID = 1,
    RGB = 2,
    MULTI_COLOR = 3,

    ftl_last = MULTI_COLOR
};

struct InputDeviceSensorInfo {
Loading