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
50 class DALI_ADAPTOR_API BitReference
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>
124 class DALI_ADAPTOR_API BitSet
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 64-bit capacity. Equivalent to the pseudocode:
174 * for(i = 0; i < 64; ++i) bits[i] = (data >> i) & 0x1;
177 * @param data 64-bit integer with the initial values.
179 template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 2u)>>
180 explicit BitSet(std::uint64_t data)
182 mData[0] = static_cast<ElementType>(data);
183 mData[1] = static_cast<ElementType>(data >> 32);
186 // Non-virtual destructor
188 ~BitSet() noexcept = default;
192 BitSet& operator=(const BitSet&) = default;
194 BitSet& operator=(BitSet&&) noexcept = default;
197 * @brief Checks whether any bits are set to 1.
199 * Equivalent to the pseudocode:
201 * for(i = 0; i < max; ++i)
207 * @return true if at least one bit is set to 1, false otherwise.
209 explicit operator bool() const
211 return std::any_of(mData.begin(), mData.end(), [](ElementType s) { return s != 0u; });
215 * @brief Compares two bitsets for equality.
217 * Equivalent to the pseudocode:
219 * for(i = 0; i < max; ++i)
220 * if(bits[i] != other.bits[i])
225 * @param other The other operand.
226 * @return true if all bits in the two bitsets have the same values, false otherwise.
228 bool operator==(const BitSet& other) const
230 return std::equal(mData.begin(), mData.end(), other.mData.begin());
234 * @brief Compares two bitsets for inequality.
236 * @param other The other operand.
237 * @return false if all bits in the two bitsets have the same values, true otherwise.
239 * @see BitSet::operator==(const BitSet&)
241 // TODO(C++20): Remove this. Having only operator== will be sufficient.
242 bool operator!=(const BitSet& other) const
244 return !(*this == other);
248 * @brief Computes the bitwise NOT of this bitset.
250 * Equivalent to the pseudocode:
252 * for(i = 0; i < max; ++i) result.bits[i] = ~bits[i]
255 * @return Result of the bitwise NOT operation.
257 BitSet operator~() const
261 std::transform(mData.begin(), mData.end(), result.mData.begin(), std::bit_not<ElementType>{});
267 * @brief Computes the bitwise OR of two bitsets.
269 * Equivalent to the pseudocode:
271 * for(i = 0; i < max; ++i) result.bits[i] = bits[i] | other.bits[i];
274 * @param other The other operand.
275 * @return Result of the bitwise OR operation.
277 BitSet operator|(const BitSet& other) const
279 return ApplyBinaryOperator(other, std::bit_or<ElementType>{});
283 * @brief Computes the bitwise AND 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 AND operation.
293 BitSet operator&(const BitSet& other) const
295 return ApplyBinaryOperator(other, std::bit_and<ElementType>{});
299 * @brief Computes the bitwise XOR 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 XOR operation.
309 BitSet operator^(const BitSet& other) const
311 return ApplyBinaryOperator(other, std::bit_xor<ElementType>{});
315 * @brief Queries the value of the specified bit.
317 * This operator is used in expressions like @code bool b = bitset[i]; @endcode
319 * @param index Index of the bit to query.
320 * @return true if the specified bit is set to 1, false if it is set to 0.
322 bool operator[](IndexType index) const
324 // Reuse implicit BitReference::operator bool
325 return const_cast<BitSet*>(this)->operator[](index);
329 * @brief Obtains a writable reference to the specified bit.
331 * This operator is used in expressions like @code bitset[i] = true; @endcode
333 * @param index Index of the bit to query.
334 * @return A writable reference to the specified bit.
336 * @see BitSet::ReferenceType
338 ReferenceType operator[](IndexType index)
340 DALI_ASSERT_ALWAYS(index / 32u < mData.size());
342 return {&mData[index / 32u], index % 32u};
348 * @brief Obtains a copy of the internal storage.
350 * @return A copy of the internal storage.
352 * @see BitSet::ArrayType
353 * @see BitSet::BitSet(const ArrayType&)
355 ArrayType GetRawData() const
361 * @brief Obtains a copy of the internal storage serialized as a single integer.
363 * This method is only available for BitSets with 64-bit capacity.
365 * @return A copy of the internal storage.
367 * @see BitSet::BitSet(std::uint64_t)
369 template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 2u)>>
370 std::uint64_t GetRawData64() const
372 return (static_cast<std::uint64_t>(mData[1]) << 32) | mData[0];
376 template<typename BinaryOperator>
377 BitSet ApplyBinaryOperator(const BitSet& other, BinaryOperator binaryOperator) const
381 // Same as the pseudocode:
382 // for(i = 0; i < max; ++i) output[i] = input1[i] @ input2[i];
383 // (substitute '@' with the desired operator)
385 mData.begin(), mData.end(), // input1
386 other.mData.begin(), // input2
387 result.mData.begin(), // output
397 * @brief Helper class for storing enumeration values as a BitSet.
399 * The enumeration values will be used as indices to the BitSet, so they should be consecutive integers and start from
400 * zero. C++ does not offer a way to query the maximum enumeration value using type traits, hence the additional
401 * template parameter.
403 * @tparam Enum The enumeration type to use.
404 * @tparam EnumMax The maximum value for this enumeration.
406 * @see Dali::Accessibility::Accessible::GetStates
407 * @see Dali::Accessibility::Accessible::GetRoles
409 template<typename Enum, Enum EnumMax>
410 class DALI_ADAPTOR_API EnumBitSet : public BitSet<Internal::BitSetSize<Enum, EnumMax>>
412 static constexpr std::size_t N = Internal::BitSetSize<Enum, EnumMax>;
417 using IndexType = typename BitSet<N>::IndexType; ///< @copydoc BitSet::IndexType
418 using ReferenceType = typename BitSet<N>::ReferenceType; ///< @copydoc BitSet::ReferenceType
422 using BitSet<N>::BitSet;
427 * @copydoc BitSet::operator[](IndexType) const
429 bool operator[](Enum index) const
431 return BitSet<N>::operator[](static_cast<IndexType>(index));
435 * @copydoc BitSet::operator[](IndexType)
437 ReferenceType operator[](Enum index)
439 return BitSet<N>::operator[](static_cast<IndexType>(index));
443 // No data members (non-virtual destructor)
446 } // namespace Dali::Accessibility
448 #endif // DALI_ADAPTOR_ACCESSIBILITY_BITSET_H