[AT-SPI] Refactor Accessibility::BitSet 02/270102/8
authorArtur Świgoń <a.swigon@samsung.com>
Tue, 25 Jan 2022 17:17:59 +0000 (18:17 +0100)
committerArtur Świgoń <a.swigon@samsung.com>
Tue, 8 Feb 2022 08:19:57 +0000 (09:19 +0100)
BitSets is renamed to BitSet (like std::bitset or java.util.BitSet) and
split into two parts: BitSet and EnumBitSet. This way, the enumeration
type can be effectively erased, so that EnumBitSet<SomeEnum, 13> and
EnumBitSet<OtherEnum, 30> can share the same copy of the code in the
resulting binary. EnumBitSet also calculates the required storage size
automatically based on the maximum enum value.

Additionally, the BitSet class is moved from accessibility.h (which
contains mostly enum classes) to a separate file and is enriched with
extensive documentation.

Change-Id: Ib79a370fa9e6241402acb00e859ba20760dbb88c

dali/devel-api/adaptor-framework/accessibility-bitset.h [new file with mode: 0644]
dali/devel-api/adaptor-framework/accessibility.h
dali/devel-api/file.list
dali/internal/accessibility/bridge/bridge-collection.cpp

diff --git a/dali/devel-api/adaptor-framework/accessibility-bitset.h b/dali/devel-api/adaptor-framework/accessibility-bitset.h
new file mode 100644 (file)
index 0000000..2606291
--- /dev/null
@@ -0,0 +1,456 @@
+#ifndef DALI_ADAPTOR_ACCESSIBILITY_BITSET_H
+#define DALI_ADAPTOR_ACCESSIBILITY_BITSET_H
+
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <algorithm>
+#include <array>
+#include <functional>
+#include <type_traits>
+
+// INTERNAL INCLUDES
+#include <dali/public-api/dali-adaptor-common.h>
+
+namespace Dali::Accessibility
+{
+template<std::size_t>
+class BitSet;
+
+namespace Internal
+{
+// Number of 32-bit chunks required to hold N bits
+template<typename Enum, Enum EnumMax, typename = std::enable_if_t<std::is_enum_v<Enum>>>
+inline constexpr std::size_t BitSetSize = (static_cast<std::size_t>(EnumMax) + 31u) / 32u;
+
+/**
+ * @brief A writable reference to a single bit.
+ *
+ * C++ does not offer a built-in mechanism to directly access specific bits in integral types, i.e.
+ * @code std::uint32_t x = 0; x[5] = true; @endcode is not possible. The BitSet type uses this proxy
+ * class to make such operations possible.
+ *
+ * @see Accessibility::BitSet
+ */
+class DALI_ADAPTOR_API BitReference
+{
+  template<std::size_t>
+  friend class Accessibility::BitSet;
+
+public:
+  using ElementType = std::uint32_t; ///< Integral type used for storing bits.
+  using IndexType   = std::size_t;   ///< Type used for indexing.
+
+  BitReference() = delete;
+
+  /**
+   * @brief Assigns a new value to this bit.
+   *
+   * This operator is used in expressions like @code bitset[i] = true; @endcode
+   *
+   * @param x The new value.
+   */
+  BitReference& operator=(bool x)
+  {
+    if(x)
+    {
+      *mData |= (1U << mIndex);
+    }
+    else
+    {
+      *mData &= ~(1U << mIndex);
+    }
+
+    return *this;
+  }
+
+  /**
+   * @brief Assigns a new value to this bit.
+   *
+   * This operator is used in expressions like @code bitset[i] = otherBitset[j]; @endcode
+   *
+   * @param reference A reference to the new value.
+   */
+  BitReference& operator=(const BitReference& reference)
+  {
+    return (*this = static_cast<bool>(reference));
+  }
+
+  /**
+   * @brief Queries the value of this bit.
+   *
+   * @return true if this bit is set to 1, false if it is set to 0.
+   */
+  operator bool() const
+  {
+    return *mData & (1U << mIndex);
+  }
+
+private:
+  BitReference(ElementType* data, IndexType index)
+  : mData{data},
+    mIndex{index}
+  {
+    DALI_ASSERT_DEBUG(data && index < 32);
+  }
+
+  ElementType* mData;
+  IndexType    mIndex;
+};
+
+} // namespace Internal
+
+/**
+ * @brief A collection of bits stored in 32-bit chunks for convenient serialization.
+ *
+ * @tparam N Number of 32-bit chunks (the capacity of this BitSet is 32*N).
+ */
+template<std::size_t N>
+class DALI_ADAPTOR_API BitSet
+{
+public:
+  // Types
+
+  using ReferenceType = Internal::BitReference;     ///< @copybrief Dali::Accessibility::Internal::BitReference
+  using ElementType   = ReferenceType::ElementType; ///< @copydoc Dali::Accessibility::Internal::BitReference::ElementType
+  using IndexType     = ReferenceType::IndexType;   ///< @copydoc Dali::Accessibility::Internal::BitReference::IndexType
+  using ArrayType     = std::array<ElementType, N>; ///< An array of N integers that can store 32*N bits.
+
+  // Constructors
+
+  /**
+   * @brief Constructs a new BitSet with all bits set to 0.
+   *
+   * Equivalent to the pseudocode:
+   * @code
+   * for(i = 0; i < max; ++i) bits[i] = 0;
+   * @endcode
+   */
+  BitSet()
+  {
+    std::fill(mData.begin(), mData.end(), 0u);
+  }
+
+  BitSet(const BitSet&) = default;
+
+  BitSet(BitSet&&) noexcept = default;
+
+  /**
+   * @brief Constructs a new BitSet with all bits inialized with values from the specified array.
+   *
+   * Equivalent to the pseudocode:
+   * @code
+   * for(i = 0; i < max; ++i) bits[i] = (array[i / 32] >> (i % 32)) & 0x1;
+   * @endcode
+   *
+   * @param array Array with the initial values.
+   */
+  explicit BitSet(const ArrayType& array)
+  {
+    std::copy(array.begin(), array.end(), mData.begin());
+  }
+
+  /**
+   * @copydoc BitSet(const ArrayType&)
+   */
+  // Useful for deserializing DBus data that comes in as signed integers
+  explicit BitSet(const std::array<std::int32_t, N>& array)
+  {
+    std::transform(array.begin(), array.end(), mData.begin(), [](std::int32_t x) { return static_cast<ElementType>(x); });
+  }
+
+  /**
+   * @brief Constructs a new BitSet with all bits initialized with bits from the specified integer.
+   *
+   * This constructor is only available for BitSets with 64-bit capacity. Equivalent to the pseudocode:
+   * @code
+   * for(i = 0; i < 64; ++i) bits[i] = (data >> i) & 0x1;
+   * @endcode
+   *
+   * @param data 64-bit integer with the initial values.
+   */
+  template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 2u)>>
+  explicit BitSet(std::uint64_t data)
+  {
+    mData[0] = static_cast<ElementType>(data);
+    mData[1] = static_cast<ElementType>(data >> 32);
+  }
+
+  // Non-virtual destructor
+
+  ~BitSet() noexcept = default;
+
+  // Operators
+
+  BitSet& operator=(const BitSet&) = default;
+
+  BitSet& operator=(BitSet&&) noexcept = default;
+
+  /**
+   * @brief Checks whether any bits are set to 1.
+   *
+   * Equivalent to the pseudocode:
+   * @code
+   * for(i = 0; i < max; ++i)
+   *   if(bits[i] == 1)
+   *     return true;
+   *  return false;
+   * @endcode
+   *
+   * @return true if at least one bit is set to 1, false otherwise.
+   */
+  explicit operator bool() const
+  {
+    return std::any_of(mData.begin(), mData.end(), [](ElementType s) { return s != 0u; });
+  }
+
+  /**
+   * @brief Compares two bitsets for equality.
+   *
+   * Equivalent to the pseudocode:
+   * @code
+   * for(i = 0; i < max; ++i)
+   *   if(bits[i] != other.bits[i])
+   *     return false;
+   * return true;
+   * @endcode
+   *
+   * @param other The other operand.
+   * @return true if all bits in the two bitsets have the same values, false otherwise.
+   */
+  bool operator==(const BitSet& other) const
+  {
+    return std::equal(mData.begin(), mData.end(), other.mData.begin());
+  }
+
+  /**
+   * @brief Compares two bitsets for inequality.
+   *
+   * @param other The other operand.
+   * @return false if all bits in the two bitsets have the same values, true otherwise.
+   *
+   * @see BitSet::operator==(const BitSet&)
+   */
+  // TODO(C++20): Remove this. Having only operator== will be sufficient.
+  bool operator!=(const BitSet& other) const
+  {
+    return !(*this == other);
+  }
+
+  /**
+   * @brief Computes the bitwise NOT of this bitset.
+   *
+   * Equivalent to the pseudocode:
+   * @code
+   * for(i = 0; i < max; ++i) result.bits[i] = ~bits[i]
+   * @endcode
+   *
+   * @return Result of the bitwise NOT operation.
+   */
+  BitSet operator~() const
+  {
+    BitSet result;
+
+    std::transform(mData.begin(), mData.end(), result.mData.begin(), std::bit_not<ElementType>{});
+
+    return result;
+  }
+
+  /**
+   * @brief Computes the bitwise OR of two bitsets.
+   *
+   * Equivalent to the pseudocode:
+   * @code
+   * for(i = 0; i < max; ++i) result.bits[i] = bits[i] | other.bits[i];
+   * @endcode
+   *
+   * @param other The other operand.
+   * @return Result of the bitwise OR operation.
+   */
+  BitSet operator|(const BitSet& other) const
+  {
+    return ApplyBinaryOperator(other, std::bit_or<ElementType>{});
+  }
+
+  /**
+   * @brief Computes the bitwise AND of two bitsets.
+   *
+   * Equivalent to the pseudocode:
+   * @code
+   * for(i = 0; i < max; ++i) result.bits[i] = bits[i] & other.bits[i];
+   * @endcode
+   *
+   * @param other The other operand.
+   * @return Result of the bitwise AND operation.
+   */
+  BitSet operator&(const BitSet& other) const
+  {
+    return ApplyBinaryOperator(other, std::bit_and<ElementType>{});
+  }
+
+  /**
+   * @brief Computes the bitwise XOR of two bitsets.
+   *
+   * Equivalent to the pseudocode:
+   * @code
+   * for(i = 0; i < max; ++i) result.bits[i] = bits[i] ^ other.bits[i];
+   * @endcode
+   *
+   * @param other The other operand.
+   * @return Result of the bitwise XOR operation.
+   */
+  BitSet operator^(const BitSet& other) const
+  {
+    return ApplyBinaryOperator(other, std::bit_xor<ElementType>{});
+  }
+
+  /**
+   * @brief Queries the value of the specified bit.
+   *
+   * This operator is used in expressions like @code bool b = bitset[i]; @endcode
+   *
+   * @param index Index of the bit to query.
+   * @return true if the specified bit is set to 1, false if it is set to 0.
+   */
+  bool operator[](IndexType index) const
+  {
+    // Reuse implicit BitReference::operator bool
+    return const_cast<BitSet*>(this)->operator[](index);
+  }
+
+  /**
+   * @brief Obtains a writable reference to the specified bit.
+   *
+   * This operator is used in expressions like @code bitset[i] = true; @endcode
+   *
+   * @param index Index of the bit to query.
+   * @return A writable reference to the specified bit.
+   *
+   * @see BitSet::ReferenceType
+   */
+  ReferenceType operator[](IndexType index)
+  {
+    DALI_ASSERT_ALWAYS(index / 32u < mData.size());
+
+    return {&mData[index / 32u], index % 32u};
+  }
+
+  // Other methods
+
+  /**
+   * @brief Obtains a copy of the internal storage.
+   *
+   * @return A copy of the internal storage.
+   *
+   * @see BitSet::ArrayType
+   * @see BitSet::BitSet(const ArrayType&)
+   */
+  ArrayType GetRawData() const
+  {
+    return mData;
+  }
+
+  /**
+   * @brief Obtains a copy of the internal storage serialized as a single integer.
+   *
+   * This method is only available for BitSets with 64-bit capacity.
+   *
+   * @return A copy of the internal storage.
+   *
+   * @see BitSet::BitSet(std::uint64_t)
+   */
+  template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 2u)>>
+  std::uint64_t GetRawData64() const
+  {
+    return (static_cast<std::uint64_t>(mData[1]) << 32) | mData[0];
+  }
+
+private:
+  template<typename BinaryOperator>
+  BitSet ApplyBinaryOperator(const BitSet& other, BinaryOperator binaryOperator) const
+  {
+    BitSet result;
+
+    // Same as the pseudocode:
+    // for(i = 0; i < max; ++i) output[i] = input1[i] @ input2[i];
+    // (substitute '@' with the desired operator)
+    std::transform(
+      mData.begin(), mData.end(), // input1
+      other.mData.begin(),        // input2
+      result.mData.begin(),       // output
+      binaryOperator);
+
+    return result;
+  }
+
+  ArrayType mData;
+};
+
+/**
+ * @brief Helper class for storing enumeration values as a BitSet.
+ *
+ * The enumeration values will be used as indices to the BitSet, so they should be consecutive integers and start from
+ * zero. C++ does not offer a way to query the maximum enumeration value using type traits, hence the additional
+ * template parameter.
+ *
+ * @tparam Enum The enumeration type to use.
+ * @tparam EnumMax The maximum value for this enumeration.
+ *
+ * @see Dali::Accessibility::Accessible::GetStates
+ * @see Dali::Accessibility::Accessible::GetRoles
+ */
+template<typename Enum, Enum EnumMax>
+class DALI_ADAPTOR_API EnumBitSet : public BitSet<Internal::BitSetSize<Enum, EnumMax>>
+{
+  static constexpr std::size_t N = Internal::BitSetSize<Enum, EnumMax>;
+
+public:
+  // Types
+
+  using IndexType     = typename BitSet<N>::IndexType;     ///< @copydoc BitSet::IndexType
+  using ReferenceType = typename BitSet<N>::ReferenceType; ///< @copydoc BitSet::ReferenceType
+
+  // Constructors
+
+  using BitSet<N>::BitSet;
+
+  // Operators
+
+  /**
+   * @copydoc BitSet::operator[](IndexType) const
+   */
+  bool operator[](Enum index) const
+  {
+    return BitSet<N>::operator[](static_cast<IndexType>(index));
+  }
+
+  /**
+   * @copydoc BitSet::operator[](IndexType)
+   */
+  ReferenceType operator[](Enum index)
+  {
+    return BitSet<N>::operator[](static_cast<IndexType>(index));
+  }
+
+private:
+  // No data members (non-virtual destructor)
+};
+
+} // namespace Dali::Accessibility
+
+#endif // DALI_ADAPTOR_ACCESSIBILITY_BITSET_H
index 1d4b8e6..ef54cd3 100644 (file)
  */\r
 \r
 // EXTERNAL INCLUDES\r
-\r
-#include <string.h>\r
-#include <array>\r
-#include <atomic>\r
-#include <bitset>\r
-#include <cassert>\r
-#include <exception>\r
-#include <functional>\r
-#include <list>\r
-#include <map>\r
-#include <memory>\r
-#include <sstream>\r
 #include <string>\r
 #include <unordered_map>\r
 #include <vector>\r
 \r
 // INTERNAL INCLUDES\r
+#include <dali/devel-api/adaptor-framework/accessibility-bitset.h>\r
 #include <dali/public-api/dali-adaptor-common.h>\r
 \r
 namespace Dali\r
@@ -435,202 +424,12 @@ enum class ReadingInfoType
   NAME,\r
   ROLE,\r
   DESCRIPTION,\r
-  STATE\r
+  STATE,\r
+  MAX_COUNT\r
 };\r
 \r
-/**\r
- * @brief Helper class for storing values treated as bit sets\r
- * This class provides all bitset-like methods for bitset size larger, than long long int\r
- * @see Dali::Accessibility::Accessible::GetStates\r
- * @see Dali::Accessibility::Accessible::GetRoles\r
- */\r
-template<size_t I, typename S>\r
-class BitSets\r
-{\r
-  std::array<uint32_t, I> mData;\r
-\r
-  void Set()\r
-  {\r
-  }\r
-\r
-  static constexpr bool Accepts()\r
-  {\r
-    return true;\r
-  }\r
-\r
-  template<typename T>\r
-  static constexpr bool Accepts()\r
-  {\r
-    return std::is_enum<T>::value;\r
-  }\r
-\r
-  template<typename T, typename T2, typename... ARGS>\r
-  static constexpr bool Accepts()\r
-  {\r
-    return std::is_enum<T>::value && Accepts<T2, ARGS...>();\r
-  }\r
-\r
-  template<typename T, typename... ARGS>\r
-  void Set(T t, ARGS... args)\r
-  {\r
-    (*this)[t] = true;\r
-    Set(args...);\r
-  }\r
-\r
-public:\r
-  BitSets()\r
-  {\r
-    for(auto& u : mData)\r
-    {\r
-      u = 0;\r
-    }\r
-  }\r
-  BitSets(const BitSets&) = default;\r
-  BitSets(BitSets&&)      = default;\r
-\r
-  template<typename T, typename... ARGS, typename std::enable_if<Accepts<T, ARGS...>()>::type* = nullptr>\r
-  BitSets(T t, ARGS... args)\r
-  {\r
-    for(auto& u : mData)\r
-    {\r
-      u = 0;\r
-    }\r
-    Set(t, args...);\r
-  }\r
-\r
-  explicit BitSets(std::array<uint32_t, I> d)\r
-  {\r
-    for(auto i = 0u; i < I; ++i)\r
-    {\r
-      mData[i] = d[i];\r
-    }\r
-  }\r
-\r
-  explicit BitSets(std::array<int32_t, I> d)\r
-  {\r
-    for(auto i = 0u; i < I; ++i)\r
-    {\r
-      mData[i] = static_cast<uint32_t>(d[i]);\r
-    }\r
-  }\r
-\r
-  BitSets& operator=(const BitSets&) = default;\r
-  BitSets& operator=(BitSets&&) = default;\r
-\r
-  struct Reference\r
-  {\r
-    std::array<uint32_t, I>& data;\r
-    size_t                   pos;\r
-\r
-    Reference& operator=(Reference r)\r
-    {\r
-      (*this) = static_cast<bool>(r);\r
-      return *this;\r
-    }\r
-\r
-    Reference& operator=(bool v)\r
-    {\r
-      if(v)\r
-      {\r
-        data[pos / 32] |= 1 << (pos & 31);\r
-      }\r
-      else\r
-      {\r
-        data[pos / 32] &= ~(1 << (pos & 31));\r
-      }\r
-      return *this;\r
-    }\r
-\r
-    operator bool() const\r
-    {\r
-      auto i = static_cast<size_t>(pos);\r
-      return (data[i / 32] & (1 << (i & 31))) != 0;\r
-    }\r
-  }; // Reference struct\r
-\r
-  Reference operator[](S index)\r
-  {\r
-    return {mData, static_cast<size_t>(index)};\r
-  }\r
-\r
-  bool operator[](S index) const\r
-  {\r
-    auto i = static_cast<size_t>(index);\r
-    return (mData[i / 32] & (1 << (i & 31))) != 0;\r
-  }\r
-\r
-  std::array<uint32_t, I> GetRawData() const\r
-  {\r
-    return mData;\r
-  }\r
-\r
-  BitSets operator|(BitSets b) const\r
-  {\r
-    BitSets r;\r
-    for(auto i = 0u; i < I; ++i)\r
-    {\r
-      r.mData[i] = mData[i] | b.mData[i];\r
-    }\r
-    return r;\r
-  }\r
-\r
-  BitSets operator^(BitSets b) const\r
-  {\r
-    BitSets r;\r
-    for(auto i = 0u; i < I; ++i)\r
-    {\r
-      r.mData[i] = mData[i] ^ b.mData[i];\r
-    }\r
-    return r;\r
-  }\r
-\r
-  BitSets operator&(BitSets b) const\r
-  {\r
-    BitSets r;\r
-    for(auto i = 0u; i < I; ++i)\r
-    {\r
-      r.mData[i] = mData[i] & b.mData[i];\r
-    }\r
-    return r;\r
-  }\r
-\r
-  bool operator==(BitSets b) const\r
-  {\r
-    for(auto i = 0u; i < I; ++i)\r
-    {\r
-      if(mData[i] != b.mData[i])\r
-      {\r
-        return false;\r
-      }\r
-    }\r
-    return true;\r
-  }\r
-\r
-  bool operator!=(BitSets b) const\r
-  {\r
-    return !((*this) == b);\r
-  }\r
-\r
-  explicit operator bool() const\r
-  {\r
-    for(auto& u : mData)\r
-    {\r
-      if(u)\r
-      {\r
-        return true;\r
-      }\r
-    }\r
-    return false;\r
-  }\r
-\r
-  size_t size() const\r
-  {\r
-    return I;\r
-  }\r
-}; // BitSets class\r
-\r
-using ReadingInfoTypes = BitSets<1, ReadingInfoType>;\r
-using States           = BitSets<2, State>;\r
+using ReadingInfoTypes = EnumBitSet<ReadingInfoType, ReadingInfoType::MAX_COUNT>;\r
+using States           = EnumBitSet<State, State::MAX_COUNT>;\r
 using Attributes       = std::unordered_map<std::string, std::string>;\r
 \r
 /**\r
index 1390bf9..427e304 100755 (executable)
@@ -55,6 +55,7 @@ SET( devel_api_src_files
 
 SET( devel_api_adaptor_framework_header_files
   ${adaptor_devel_api_dir}/adaptor-framework/accessibility.h
+  ${adaptor_devel_api_dir}/adaptor-framework/accessibility-bitset.h
   ${adaptor_devel_api_dir}/adaptor-framework/accessibility-bridge.h
   ${adaptor_devel_api_dir}/adaptor-framework/actor-accessible.h
   ${adaptor_devel_api_dir}/adaptor-framework/animated-image-loading.h
index 6795674..413e23c 100644 (file)
@@ -220,7 +220,7 @@ struct BridgeCollection::Comparer
    */
   struct ComparerRoles
   {
-    using Roles = BitSets<4, Role>;
+    using Roles = EnumBitSet<Role, Role::MAX_COUNT>;
 
     Roles mRequested;
     Roles mObject;