d9c0159edca1321e3d67a34886b7b438b99f769b
[platform/core/uifw/dali-adaptor.git] / dali / devel-api / adaptor-framework / accessibility-bitset.h
1 #ifndef DALI_ADAPTOR_ACCESSIBILITY_BITSET_H
2 #define DALI_ADAPTOR_ACCESSIBILITY_BITSET_H
3
4 /*
5  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
6  *
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
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  *
19  */
20
21 // EXTERNAL INCLUDES
22 #include <algorithm>
23 #include <array>
24 #include <functional>
25 #include <type_traits>
26
27 // INTERNAL INCLUDES
28 #include <dali/public-api/dali-adaptor-common.h>
29
30 namespace Dali::Accessibility
31 {
32 template<std::size_t>
33 class BitSet;
34
35 namespace Internal
36 {
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;
40
41 /**
42  * @brief A writable reference to a single bit.
43  *
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.
47  *
48  * @see Accessibility::BitSet
49  */
50 class DALI_ADAPTOR_API BitReference
51 {
52   template<std::size_t>
53   friend class Accessibility::BitSet;
54
55 public:
56   using ElementType = std::uint32_t; ///< Integral type used for storing bits.
57   using IndexType   = std::size_t;   ///< Type used for indexing.
58
59   BitReference() = delete;
60
61   /**
62    * @brief Assigns a new value to this bit.
63    *
64    * This operator is used in expressions like @code bitset[i] = true; @endcode
65    *
66    * @param x The new value.
67    */
68   BitReference& operator=(bool x)
69   {
70     if(x)
71     {
72       *mData |= (1U << mIndex);
73     }
74     else
75     {
76       *mData &= ~(1U << mIndex);
77     }
78
79     return *this;
80   }
81
82   /**
83    * @brief Assigns a new value to this bit.
84    *
85    * This operator is used in expressions like @code bitset[i] = otherBitset[j]; @endcode
86    *
87    * @param reference A reference to the new value.
88    */
89   BitReference& operator=(const BitReference& reference)
90   {
91     return (*this = static_cast<bool>(reference));
92   }
93
94   /**
95    * @brief Queries the value of this bit.
96    *
97    * @return true if this bit is set to 1, false if it is set to 0.
98    */
99   operator bool() const
100   {
101     return *mData & (1U << mIndex);
102   }
103
104 private:
105   BitReference(ElementType* data, IndexType index)
106   : mData{data},
107     mIndex{index}
108   {
109     DALI_ASSERT_DEBUG(data && index < 32);
110   }
111
112   ElementType* mData;
113   IndexType    mIndex;
114 };
115
116 } // namespace Internal
117
118 /**
119  * @brief A collection of bits stored in 32-bit chunks for convenient serialization.
120  *
121  * @tparam N Number of 32-bit chunks (the capacity of this BitSet is 32*N).
122  */
123 template<std::size_t N>
124 class DALI_ADAPTOR_API BitSet
125 {
126 public:
127   // Types
128
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.
133
134   // Constructors
135
136   /**
137    * @brief Constructor.
138    */
139   BitSet() = default;
140
141   BitSet(const BitSet&) = default;
142
143   BitSet(BitSet&&) noexcept = default;
144
145   /**
146    * @brief Constructs a new BitSet with all bits inialized with values from the specified array.
147    *
148    * Equivalent to the pseudocode:
149    * @code
150    * for(i = 0; i < max; ++i) bits[i] = (array[i / 32] >> (i % 32)) & 0x1;
151    * @endcode
152    *
153    * @param array Array with the initial values.
154    */
155   explicit BitSet(const ArrayType& array)
156   {
157     std::copy(array.begin(), array.end(), mData.begin());
158   }
159
160   /**
161    * @copydoc BitSet(const ArrayType&)
162    */
163   // Useful for deserializing DBus data that comes in as signed integers
164   explicit BitSet(const std::array<std::int32_t, N>& array)
165   {
166     std::transform(array.begin(), array.end(), mData.begin(), [](std::int32_t x) { return static_cast<ElementType>(x); });
167   }
168
169   /**
170    * @brief Constructs a new BitSet with all bits initialized with bits from the specified integer.
171    *
172    * This constructor is only available for BitSets with 32-bit capacity. Equivalent to the pseudocode:
173    * @code
174    * for(i = 0; i < 32; ++i) bits[i] = (data >> i) & 0x1;
175    * @endcode
176    *
177    * @param data 32-bit integer with the initial values.
178    */
179   template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 1u)>>
180   explicit BitSet(std::uint32_t data)
181   {
182     mData[0] = data;
183   }
184
185   /**
186    * @brief Constructs a new BitSet with all bits initialized with bits from the specified integer.
187    *
188    * This constructor is only available for BitSets with 64-bit capacity. Equivalent to the pseudocode:
189    * @code
190    * for(i = 0; i < 64; ++i) bits[i] = (data >> i) & 0x1;
191    * @endcode
192    *
193    * @param data 64-bit integer with the initial values.
194    */
195   template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 2u)>>
196   explicit BitSet(std::uint64_t data)
197   {
198     mData[0] = static_cast<ElementType>(data);
199     mData[1] = static_cast<ElementType>(data >> 32);
200   }
201
202   // Non-virtual destructor
203
204   ~BitSet() noexcept = default;
205
206   // Operators
207
208   BitSet& operator=(const BitSet&) = default;
209
210   BitSet& operator=(BitSet&&) noexcept = default;
211
212   /**
213    * @brief Checks whether any bits are set to 1.
214    *
215    * Equivalent to the pseudocode:
216    * @code
217    * for(i = 0; i < max; ++i)
218    *   if(bits[i] == 1)
219    *     return true;
220    *  return false;
221    * @endcode
222    *
223    * @return true if at least one bit is set to 1, false otherwise.
224    */
225   explicit operator bool() const
226   {
227     return std::any_of(mData.begin(), mData.end(), [](ElementType s) { return s != 0u; });
228   }
229
230   /**
231    * @brief Compares two bitsets for equality.
232    *
233    * Equivalent to the pseudocode:
234    * @code
235    * for(i = 0; i < max; ++i)
236    *   if(bits[i] != other.bits[i])
237    *     return false;
238    * return true;
239    * @endcode
240    *
241    * @param other The other operand.
242    * @return true if all bits in the two bitsets have the same values, false otherwise.
243    */
244   bool operator==(const BitSet& other) const
245   {
246     return std::equal(mData.begin(), mData.end(), other.mData.begin());
247   }
248
249   /**
250    * @brief Compares two bitsets for inequality.
251    *
252    * @param other The other operand.
253    * @return false if all bits in the two bitsets have the same values, true otherwise.
254    *
255    * @see BitSet::operator==(const BitSet&)
256    */
257   // TODO(C++20): Remove this. Having only operator== will be sufficient.
258   bool operator!=(const BitSet& other) const
259   {
260     return !(*this == other);
261   }
262
263   /**
264    * @brief Computes the bitwise NOT of this bitset.
265    *
266    * Equivalent to the pseudocode:
267    * @code
268    * for(i = 0; i < max; ++i) result.bits[i] = ~bits[i]
269    * @endcode
270    *
271    * @return Result of the bitwise NOT operation.
272    */
273   BitSet operator~() const
274   {
275     BitSet result;
276
277     std::transform(mData.begin(), mData.end(), result.mData.begin(), std::bit_not<ElementType>{});
278
279     return result;
280   }
281
282   /**
283    * @brief Computes the bitwise OR of two bitsets.
284    *
285    * Equivalent to the pseudocode:
286    * @code
287    * for(i = 0; i < max; ++i) result.bits[i] = bits[i] | other.bits[i];
288    * @endcode
289    *
290    * @param other The other operand.
291    * @return Result of the bitwise OR operation.
292    */
293   BitSet operator|(const BitSet& other) const
294   {
295     return ApplyBinaryOperator(other, std::bit_or<ElementType>{});
296   }
297
298   /**
299    * @brief Computes the bitwise AND of two bitsets.
300    *
301    * Equivalent to the pseudocode:
302    * @code
303    * for(i = 0; i < max; ++i) result.bits[i] = bits[i] & other.bits[i];
304    * @endcode
305    *
306    * @param other The other operand.
307    * @return Result of the bitwise AND operation.
308    */
309   BitSet operator&(const BitSet& other) const
310   {
311     return ApplyBinaryOperator(other, std::bit_and<ElementType>{});
312   }
313
314   /**
315    * @brief Computes the bitwise XOR of two bitsets.
316    *
317    * Equivalent to the pseudocode:
318    * @code
319    * for(i = 0; i < max; ++i) result.bits[i] = bits[i] ^ other.bits[i];
320    * @endcode
321    *
322    * @param other The other operand.
323    * @return Result of the bitwise XOR operation.
324    */
325   BitSet operator^(const BitSet& other) const
326   {
327     return ApplyBinaryOperator(other, std::bit_xor<ElementType>{});
328   }
329
330   /**
331    * @brief Queries the value of the specified bit.
332    *
333    * This operator is used in expressions like @code bool b = bitset[i]; @endcode
334    *
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.
337    */
338   bool operator[](IndexType index) const
339   {
340     // Reuse implicit BitReference::operator bool
341     return const_cast<BitSet*>(this)->operator[](index);
342   }
343
344   /**
345    * @brief Obtains a writable reference to the specified bit.
346    *
347    * This operator is used in expressions like @code bitset[i] = true; @endcode
348    *
349    * @param index Index of the bit to query.
350    * @return A writable reference to the specified bit.
351    *
352    * @see BitSet::ReferenceType
353    */
354   ReferenceType operator[](IndexType index)
355   {
356     DALI_ASSERT_ALWAYS(index / 32u < mData.size());
357
358     return {&mData[index / 32u], index % 32u};
359   }
360
361   // Other methods
362
363   /**
364    * @brief Obtains a copy of the internal storage.
365    *
366    * @return A copy of the internal storage.
367    *
368    * @see BitSet::ArrayType
369    * @see BitSet::BitSet(const ArrayType&)
370    */
371   ArrayType GetRawData() const
372   {
373     return mData;
374   }
375
376   /**
377    * @brief Obtains a copy of the internal storage serialized as a single integer.
378    *
379    * This method is only available for BitSets with 32-bit capacity.
380    *
381    * @return A copy of the internal storage.
382    *
383    * @see BitSet::BitSet(std::uint32_t)
384    */
385   template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 1u)>>
386   std::uint32_t GetRawData32() const
387   {
388     return mData[0];
389   }
390
391   /**
392    * @brief Obtains a copy of the internal storage serialized as a single integer.
393    *
394    * This method is only available for BitSets with 64-bit capacity.
395    *
396    * @return A copy of the internal storage.
397    *
398    * @see BitSet::BitSet(std::uint64_t)
399    */
400   template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 2u)>>
401   std::uint64_t GetRawData64() const
402   {
403     return (static_cast<std::uint64_t>(mData[1]) << 32) | mData[0];
404   }
405
406 private:
407   template<typename BinaryOperator>
408   BitSet ApplyBinaryOperator(const BitSet& other, BinaryOperator binaryOperator) const
409   {
410     BitSet result;
411
412     // Same as the pseudocode:
413     // for(i = 0; i < max; ++i) output[i] = input1[i] @ input2[i];
414     // (substitute '@' with the desired operator)
415     std::transform(
416       mData.begin(), mData.end(), // input1
417       other.mData.begin(),        // input2
418       result.mData.begin(),       // output
419       binaryOperator);
420
421     return result;
422   }
423
424   ArrayType mData{};
425 };
426
427 /**
428  * @brief Helper class for storing enumeration values as a BitSet.
429  *
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.
433  *
434  * @tparam Enum The enumeration type to use.
435  * @tparam EnumMax The maximum value for this enumeration.
436  *
437  * @see Dali::Accessibility::Accessible::GetStates
438  * @see Dali::Accessibility::Accessible::GetRoles
439  */
440 template<typename Enum, Enum EnumMax>
441 class DALI_ADAPTOR_API EnumBitSet : public BitSet<Internal::BitSetSize<Enum, EnumMax>>
442 {
443   static constexpr std::size_t N = Internal::BitSetSize<Enum, EnumMax>;
444
445 public:
446   // Types
447
448   using IndexType     = typename BitSet<N>::IndexType;     ///< @copydoc BitSet::IndexType
449   using ReferenceType = typename BitSet<N>::ReferenceType; ///< @copydoc BitSet::ReferenceType
450
451   // Constructors
452
453   using BitSet<N>::BitSet;
454
455   // Operators
456
457   /**
458    * @copydoc BitSet::operator~() const
459    */
460   EnumBitSet operator~() const
461   {
462     return BitSet<N>::operator~();
463   }
464
465   /**
466    * @copydoc BitSet::operator|(const BitSet&) const
467    */
468   EnumBitSet operator|(const EnumBitSet& other) const
469   {
470     return BitSet<N>::operator|(other);
471   }
472
473   /**
474    * @copydoc BitSet::operator&(const BitSet&) const
475    */
476   EnumBitSet operator&(const EnumBitSet& other) const
477   {
478     return BitSet<N>::operator&(other);
479   }
480
481   /**
482    * @copydoc BitSet::operator^(const BitSet&) const
483    */
484   EnumBitSet operator^(const EnumBitSet& other) const
485   {
486     return BitSet<N>::operator^(other);
487   }
488
489   /**
490    * @copydoc BitSet::operator[](IndexType) const
491    */
492   bool operator[](Enum index) const
493   {
494     return BitSet<N>::operator[](static_cast<IndexType>(index));
495   }
496
497   /**
498    * @copydoc BitSet::operator[](IndexType)
499    */
500   ReferenceType operator[](Enum index)
501   {
502     return BitSet<N>::operator[](static_cast<IndexType>(index));
503   }
504
505 private:
506   // For operators '~|&^'
507   EnumBitSet(BitSet<N>&& bitSet)
508   : BitSet<N>(bitSet)
509   {
510   }
511
512   // No data members (non-virtual destructor)
513 };
514
515 } // namespace Dali::Accessibility
516
517 #endif // DALI_ADAPTOR_ACCESSIBILITY_BITSET_H