--- /dev/null
+#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
*/\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
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