1 #ifndef DALI_ADAPTOR_ACCESSIBILITY_BITSET_H
2 #define DALI_ADAPTOR_ACCESSIBILITY_BITSET_H
5 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
25 #include <type_traits>
28 #include <dali/public-api/dali-adaptor-common.h>
30 namespace Dali::Accessibility
37 // Number of 32-bit chunks required to hold N bits
38 template<typename Enum, Enum EnumMax, typename = std::enable_if_t<std::is_enum_v<Enum>>>
39 inline constexpr std::size_t BitSetSize = (static_cast<std::size_t>(EnumMax) + 31u) / 32u;
42 * @brief A writable reference to a single bit.
44 * C++ does not offer a built-in mechanism to directly access specific bits in integral types, i.e.
45 * @code std::uint32_t x = 0; x[5] = true; @endcode is not possible. The BitSet type uses this proxy
46 * class to make such operations possible.
48 * @see Accessibility::BitSet
53 friend class Accessibility::BitSet;
56 using ElementType = std::uint32_t; ///< Integral type used for storing bits.
57 using IndexType = std::size_t; ///< Type used for indexing.
59 BitReference() = delete;
62 * @brief Assigns a new value to this bit.
64 * This operator is used in expressions like @code bitset[i] = true; @endcode
66 * @param x The new value.
68 BitReference& operator=(bool x)
72 *mData |= (1U << mIndex);
76 *mData &= ~(1U << mIndex);
83 * @brief Assigns a new value to this bit.
85 * This operator is used in expressions like @code bitset[i] = otherBitset[j]; @endcode
87 * @param reference A reference to the new value.
89 BitReference& operator=(const BitReference& reference)
91 return (*this = static_cast<bool>(reference));
95 * @brief Queries the value of this bit.
97 * @return true if this bit is set to 1, false if it is set to 0.
101 return *mData & (1U << mIndex);
105 BitReference(ElementType* data, IndexType index)
109 DALI_ASSERT_DEBUG(data && index < 32);
116 } // namespace Internal
119 * @brief A collection of bits stored in 32-bit chunks for convenient serialization.
121 * @tparam N Number of 32-bit chunks (the capacity of this BitSet is 32*N).
123 template<std::size_t N>
129 using ReferenceType = Internal::BitReference; ///< @copybrief Dali::Accessibility::Internal::BitReference
130 using ElementType = ReferenceType::ElementType; ///< @copydoc Dali::Accessibility::Internal::BitReference::ElementType
131 using IndexType = ReferenceType::IndexType; ///< @copydoc Dali::Accessibility::Internal::BitReference::IndexType
132 using ArrayType = std::array<ElementType, N>; ///< An array of N integers that can store 32*N bits.
137 * @brief Constructor.
141 BitSet(const BitSet&) = default;
143 BitSet(BitSet&&) noexcept = default;
146 * @brief Constructs a new BitSet with all bits inialized with values from the specified array.
148 * Equivalent to the pseudocode:
150 * for(i = 0; i < max; ++i) bits[i] = (array[i / 32] >> (i % 32)) & 0x1;
153 * @param array Array with the initial values.
155 explicit BitSet(const ArrayType& array)
157 std::copy(array.begin(), array.end(), mData.begin());
161 * @copydoc BitSet(const ArrayType&)
163 // Useful for deserializing DBus data that comes in as signed integers
164 explicit BitSet(const std::array<std::int32_t, N>& array)
166 std::transform(array.begin(), array.end(), mData.begin(), [](std::int32_t x) { return static_cast<ElementType>(x); });
170 * @brief Constructs a new BitSet with all bits initialized with bits from the specified integer.
172 * This constructor is only available for BitSets with 32-bit capacity. Equivalent to the pseudocode:
174 * for(i = 0; i < 32; ++i) bits[i] = (data >> i) & 0x1;
177 * @param data 32-bit integer with the initial values.
179 template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 1u)>>
180 explicit BitSet(std::uint32_t data)
186 * @brief Constructs a new BitSet with all bits initialized with bits from the specified integer.
188 * This constructor is only available for BitSets with 64-bit capacity. Equivalent to the pseudocode:
190 * for(i = 0; i < 64; ++i) bits[i] = (data >> i) & 0x1;
193 * @param data 64-bit integer with the initial values.
195 template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 2u)>>
196 explicit BitSet(std::uint64_t data)
198 mData[0] = static_cast<ElementType>(data);
199 mData[1] = static_cast<ElementType>(data >> 32);
202 // Non-virtual destructor
204 ~BitSet() noexcept = default;
208 BitSet& operator=(const BitSet&) = default;
210 BitSet& operator=(BitSet&&) noexcept = default;
213 * @brief Checks whether any bits are set to 1.
215 * Equivalent to the pseudocode:
217 * for(i = 0; i < max; ++i)
223 * @return true if at least one bit is set to 1, false otherwise.
225 explicit operator bool() const
227 return std::any_of(mData.begin(), mData.end(), [](ElementType s) { return s != 0u; });
231 * @brief Compares two bitsets for equality.
233 * Equivalent to the pseudocode:
235 * for(i = 0; i < max; ++i)
236 * if(bits[i] != other.bits[i])
241 * @param other The other operand.
242 * @return true if all bits in the two bitsets have the same values, false otherwise.
244 bool operator==(const BitSet& other) const
246 return std::equal(mData.begin(), mData.end(), other.mData.begin());
250 * @brief Compares two bitsets for inequality.
252 * @param other The other operand.
253 * @return false if all bits in the two bitsets have the same values, true otherwise.
255 * @see BitSet::operator==(const BitSet&)
257 // TODO(C++20): Remove this. Having only operator== will be sufficient.
258 bool operator!=(const BitSet& other) const
260 return !(*this == other);
264 * @brief Computes the bitwise NOT of this bitset.
266 * Equivalent to the pseudocode:
268 * for(i = 0; i < max; ++i) result.bits[i] = ~bits[i]
271 * @return Result of the bitwise NOT operation.
273 BitSet operator~() const
277 std::transform(mData.begin(), mData.end(), result.mData.begin(), std::bit_not<ElementType>{});
283 * @brief Computes the bitwise OR of two bitsets.
285 * Equivalent to the pseudocode:
287 * for(i = 0; i < max; ++i) result.bits[i] = bits[i] | other.bits[i];
290 * @param other The other operand.
291 * @return Result of the bitwise OR operation.
293 BitSet operator|(const BitSet& other) const
295 return ApplyBinaryOperator(other, std::bit_or<ElementType>{});
299 * @brief Computes the bitwise AND of two bitsets.
301 * Equivalent to the pseudocode:
303 * for(i = 0; i < max; ++i) result.bits[i] = bits[i] & other.bits[i];
306 * @param other The other operand.
307 * @return Result of the bitwise AND operation.
309 BitSet operator&(const BitSet& other) const
311 return ApplyBinaryOperator(other, std::bit_and<ElementType>{});
315 * @brief Computes the bitwise XOR of two bitsets.
317 * Equivalent to the pseudocode:
319 * for(i = 0; i < max; ++i) result.bits[i] = bits[i] ^ other.bits[i];
322 * @param other The other operand.
323 * @return Result of the bitwise XOR operation.
325 BitSet operator^(const BitSet& other) const
327 return ApplyBinaryOperator(other, std::bit_xor<ElementType>{});
331 * @brief Queries the value of the specified bit.
333 * This operator is used in expressions like @code bool b = bitset[i]; @endcode
335 * @param index Index of the bit to query.
336 * @return true if the specified bit is set to 1, false if it is set to 0.
338 bool operator[](IndexType index) const
340 // Reuse implicit BitReference::operator bool
341 return const_cast<BitSet*>(this)->operator[](index);
345 * @brief Obtains a writable reference to the specified bit.
347 * This operator is used in expressions like @code bitset[i] = true; @endcode
349 * @param index Index of the bit to query.
350 * @return A writable reference to the specified bit.
352 * @see BitSet::ReferenceType
354 ReferenceType operator[](IndexType index)
356 DALI_ASSERT_ALWAYS(index / 32u < mData.size());
358 return {&mData[index / 32u], index % 32u};
364 * @brief Obtains a copy of the internal storage.
366 * @return A copy of the internal storage.
368 * @see BitSet::ArrayType
369 * @see BitSet::BitSet(const ArrayType&)
371 ArrayType GetRawData() const
377 * @brief Obtains a copy of the internal storage serialized as a single integer.
379 * This method is only available for BitSets with 32-bit capacity.
381 * @return A copy of the internal storage.
383 * @see BitSet::BitSet(std::uint32_t)
385 template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 1u)>>
386 std::uint32_t GetRawData32() const
392 * @brief Obtains a copy of the internal storage serialized as a single integer.
394 * This method is only available for BitSets with 64-bit capacity.
396 * @return A copy of the internal storage.
398 * @see BitSet::BitSet(std::uint64_t)
400 template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 2u)>>
401 std::uint64_t GetRawData64() const
403 return (static_cast<std::uint64_t>(mData[1]) << 32) | mData[0];
407 template<typename BinaryOperator>
408 BitSet ApplyBinaryOperator(const BitSet& other, BinaryOperator binaryOperator) const
412 // Same as the pseudocode:
413 // for(i = 0; i < max; ++i) output[i] = input1[i] @ input2[i];
414 // (substitute '@' with the desired operator)
416 mData.begin(), mData.end(), // input1
417 other.mData.begin(), // input2
418 result.mData.begin(), // output
428 * @brief Helper class for storing enumeration values as a BitSet.
430 * The enumeration values will be used as indices to the BitSet, so they should be consecutive integers and start from
431 * zero. C++ does not offer a way to query the maximum enumeration value using type traits, hence the additional
432 * template parameter.
434 * @tparam Enum The enumeration type to use.
435 * @tparam EnumMax The maximum value for this enumeration.
437 * @see Dali::Accessibility::Accessible::GetStates
438 * @see Dali::Accessibility::Accessible::GetRoles
440 template<typename Enum, Enum EnumMax>
441 class EnumBitSet : public BitSet<Internal::BitSetSize<Enum, EnumMax>>
443 static constexpr std::size_t N = Internal::BitSetSize<Enum, EnumMax>;
448 using IndexType = typename BitSet<N>::IndexType; ///< @copydoc Dali::Accessibility::BitSet::IndexType
449 using ReferenceType = typename BitSet<N>::ReferenceType; ///< @copydoc Dali::Accessibility::BitSet::ReferenceType
453 using BitSet<N>::BitSet;
458 * @copydoc Dali::Accessibility::BitSet::operator~() const
460 EnumBitSet operator~() const
462 return BitSet<N>::operator~();
466 * @copydoc Dali::Accessibility::BitSet::operator|(const BitSet&) const
468 EnumBitSet operator|(const EnumBitSet& other) const
470 return BitSet<N>::operator|(other);
474 * @copydoc Dali::Accessibility::BitSet::operator&(const BitSet&) const
476 EnumBitSet operator&(const EnumBitSet& other) const
478 return BitSet<N>::operator&(other);
482 * @copydoc Dali::Accessibility::BitSet::operator^(const BitSet&) const
484 EnumBitSet operator^(const EnumBitSet& other) const
486 return BitSet<N>::operator^(other);
490 * @copydoc Dali::Accessibility::BitSet::operator[](IndexType) const
492 bool operator[](Enum index) const
494 return BitSet<N>::operator[](static_cast<IndexType>(index));
498 * @copydoc Dali::Accessibility::BitSet::operator[](IndexType)
500 ReferenceType operator[](Enum index)
502 return BitSet<N>::operator[](static_cast<IndexType>(index));
506 // For operators '~|&^'
507 EnumBitSet(BitSet<N>&& bitSet)
512 // No data members (non-virtual destructor)
515 } // namespace Dali::Accessibility
517 #endif // DALI_ADAPTOR_ACCESSIBILITY_BITSET_H