Skip to content

Commit 44dadd5

Browse files
authored
[common] add integer bits operation templates (openthread#11478)
1 parent 36cd82c commit 44dadd5

12 files changed

Lines changed: 323 additions & 139 deletions

src/core/common/num_utils.hpp

Lines changed: 255 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#ifndef NUM_UTILS_HPP_
3535
#define NUM_UTILS_HPP_
3636

37+
#include "common/encoding.hpp"
3738
#include "common/numeric_limits.hpp"
3839
#include "common/type_traits.hpp"
3940

@@ -241,7 +242,7 @@ inline unsigned long ToUlong(uint32_t aUint32) { return static_cast<unsigned lon
241242
/**
242243
* Counts the number of `1` bits in the binary representation of a given unsigned int bit-mask value.
243244
*
244-
* @tparam UintType The unsigned int type (MUST be `uint8_t`, uint16_t`, uint32_t`, or `uint64_t`).
245+
* @tparam UintType The unsigned int type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`).
245246
*
246247
* @param[in] aMask A bit mask.
247248
*
@@ -264,6 +265,259 @@ template <typename UintType> uint8_t CountBitsInMask(UintType aMask)
264265
return count;
265266
}
266267

268+
/**
269+
* Sets the specified bit of the given integer to 1.
270+
*
271+
* @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`).
272+
*
273+
* @param[in,out] aBits The integer to set the bit.
274+
* @param[in] aBitOffset The bit offset to set. The bit offset starts with zero corresponding to the
275+
* least-significant bit.
276+
*/
277+
template <typename UintType> void SetBit(UintType &aBits, uint8_t aBitOffset)
278+
{
279+
static_assert(TypeTraits::IsSame<UintType, uint8_t>::kValue || TypeTraits::IsSame<UintType, uint16_t>::kValue ||
280+
TypeTraits::IsSame<UintType, uint32_t>::kValue || TypeTraits::IsSame<UintType, uint64_t>::kValue,
281+
"UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`");
282+
283+
aBits = aBits | (static_cast<UintType>(1) << aBitOffset);
284+
}
285+
286+
/**
287+
* Clears the specified bit of the given integer.
288+
*
289+
* @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`).
290+
*
291+
* @param[in,out] aBits The integer to clear the bit.
292+
* @param[in] aBitOffset The bit offset to clear. The bit offset starts with zero corresponding to the
293+
* least-significant bit.
294+
*/
295+
template <typename UintType> void ClearBit(UintType &aBits, uint8_t aBitOffset)
296+
{
297+
static_assert(TypeTraits::IsSame<UintType, uint8_t>::kValue || TypeTraits::IsSame<UintType, uint16_t>::kValue ||
298+
TypeTraits::IsSame<UintType, uint32_t>::kValue || TypeTraits::IsSame<UintType, uint64_t>::kValue,
299+
"UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`");
300+
301+
aBits = aBits & (~(static_cast<UintType>(1) << aBitOffset));
302+
}
303+
304+
/**
305+
* Gets the value of the specified bit of the given integer.
306+
*
307+
* @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`).
308+
*
309+
* @param[in] aBits The integer to get the bit.
310+
* @param[in] aBitOffset The bit offset to get. The bit offset starts with zero corresponding to the
311+
* least-significant bit.
312+
*
313+
* @returns The value of the specified bit.
314+
*/
315+
template <typename UintType> bool GetBit(UintType aBits, uint8_t aBitOffset)
316+
{
317+
static_assert(TypeTraits::IsSame<UintType, uint8_t>::kValue || TypeTraits::IsSame<UintType, uint16_t>::kValue ||
318+
TypeTraits::IsSame<UintType, uint32_t>::kValue || TypeTraits::IsSame<UintType, uint64_t>::kValue,
319+
"UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`");
320+
321+
return (aBits & (static_cast<UintType>(1) << aBitOffset)) != 0;
322+
}
323+
324+
/**
325+
* Writes the specified bit of the given integer to the given value (0 or 1).
326+
*
327+
* @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`).
328+
*
329+
* @param[in,out] aBits The integer to write the bit.
330+
* @param[in] aBitOffset The bit offset to get. The bit offset starts with zero corresponding to the
331+
* least-significant bit.
332+
* @param[in] aValue The value to write.
333+
*/
334+
template <typename UintType> void WriteBit(UintType &aBits, uint8_t aBitOffset, bool aValue)
335+
{
336+
static_assert(TypeTraits::IsSame<UintType, uint8_t>::kValue || TypeTraits::IsSame<UintType, uint16_t>::kValue ||
337+
TypeTraits::IsSame<UintType, uint32_t>::kValue || TypeTraits::IsSame<UintType, uint64_t>::kValue,
338+
"UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`");
339+
340+
if (aValue)
341+
{
342+
SetBit<UintType>(aBits, aBitOffset);
343+
}
344+
else
345+
{
346+
ClearBit<UintType>(aBits, aBitOffset);
347+
}
348+
}
349+
350+
/**
351+
* Gets the offset of the lowest non-zero bit in the given mask.
352+
*
353+
* @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`).
354+
*
355+
* @param[in] aMask The mask (MUST not be 0) to calculate the offset of the lowest non-zero bit.
356+
*
357+
* @returns The offset of the lowest non-zero bit in the mask.
358+
*/
359+
template <typename UintType> inline constexpr uint8_t BitOffsetOfMask(UintType aMask)
360+
{
361+
static_assert(TypeTraits::IsSame<UintType, uint8_t>::kValue || TypeTraits::IsSame<UintType, uint16_t>::kValue ||
362+
TypeTraits::IsSame<UintType, uint32_t>::kValue || TypeTraits::IsSame<UintType, uint64_t>::kValue,
363+
"UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`");
364+
365+
return (aMask & 0x1) ? 0 : (1 + BitOffsetOfMask<UintType>(aMask >> 1));
366+
}
367+
368+
/**
369+
* Writes the specified bits of the given integer to the given value.
370+
*
371+
* @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`).
372+
* @tparam kMask The bit mask (MUST not be 0) to write. The @p kMask must be provided in a shifted form.
373+
* @tparam kOffset The bit offset to write. The default @p kOffset is computed from the given @p kMask.
374+
*
375+
* @param[in,out] aBits The integer to write the bits.
376+
* @param[in] aValue The value to write.
377+
*/
378+
template <typename UintType, UintType kMask, UintType kOffset = BitOffsetOfMask(kMask)>
379+
void WriteBits(UintType &aBits, UintType aValue)
380+
{
381+
static_assert(TypeTraits::IsSame<UintType, uint8_t>::kValue || TypeTraits::IsSame<UintType, uint16_t>::kValue ||
382+
TypeTraits::IsSame<UintType, uint32_t>::kValue || TypeTraits::IsSame<UintType, uint64_t>::kValue,
383+
"UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`");
384+
385+
aBits = ((aBits & ~kMask) | ((aValue << kOffset) & kMask));
386+
}
387+
388+
/**
389+
* Writes the specified bits of the given integer to the given value and returns the updated integer.
390+
*
391+
* @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`).
392+
* @tparam kMask The bit mask (MUST not be 0) to write. The @p kMask must be provided in a shifted form.
393+
* @tparam kOffset The bit offset to write. The default @p kOffset is computed from the given @p kMask.
394+
*
395+
* @param[in] aBits The integer to write the bits.
396+
* @param[in] aValue The value to write.
397+
*
398+
* @returns The updated integer.
399+
*/
400+
template <typename UintType, UintType kMask, UintType kOffset = BitOffsetOfMask(kMask)>
401+
UintType UpdateBits(UintType aBits, UintType aValue)
402+
{
403+
static_assert(TypeTraits::IsSame<UintType, uint8_t>::kValue || TypeTraits::IsSame<UintType, uint16_t>::kValue ||
404+
TypeTraits::IsSame<UintType, uint32_t>::kValue || TypeTraits::IsSame<UintType, uint64_t>::kValue,
405+
"UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`");
406+
407+
return ((aBits & ~kMask) | ((aValue << kOffset) & kMask));
408+
}
409+
410+
/**
411+
* Read the value of the specified bits of the given integer.
412+
*
413+
* @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`).
414+
* @tparam kMask The bit mask (MUST not be 0) to write. The @p kMask must be provided in a shifted form.
415+
* @tparam kOffset The bit offset to write. The default @p kOffset is computed from the given @p kMask.
416+
*
417+
* @param[in] aBits The integer to read the bits.
418+
*
419+
* @returns The value of the specified bits.
420+
*/
421+
template <typename UintType, UintType kMask, UintType kOffset = BitOffsetOfMask(kMask)>
422+
UintType ReadBits(UintType aBits)
423+
{
424+
static_assert(TypeTraits::IsSame<UintType, uint8_t>::kValue || TypeTraits::IsSame<UintType, uint16_t>::kValue ||
425+
TypeTraits::IsSame<UintType, uint32_t>::kValue || TypeTraits::IsSame<UintType, uint64_t>::kValue,
426+
"UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`");
427+
428+
return (aBits & kMask) >> kOffset;
429+
}
430+
431+
/**
432+
* Writes the specified bits of the given integer stored in little-endian format to the given value and returns the
433+
* updated integer stored in little-endian format.
434+
*
435+
* @tparam UintType The value type (MUST be `uint16_t`, `uint32_t`, or `uint64_t`).
436+
* @tparam kMask The bit mask (MUST not be 0) to write. The @p kMask must be provided in a shifted form.
437+
* @tparam kOffset The bit offset to write. The default @p kOffset is computed from the given @p kMask.
438+
*
439+
* @param[in] aBits The integer to write the bits.
440+
* @param[in] aValue The value to write.
441+
*
442+
* @returns The updated integer.
443+
*/
444+
template <typename UintType, UintType kMask, UintType kOffset = BitOffsetOfMask(kMask)>
445+
UintType UpdateBitsLittleEndian(UintType aBits, UintType aValue)
446+
{
447+
static_assert(TypeTraits::IsSame<UintType, uint16_t>::kValue || TypeTraits::IsSame<UintType, uint32_t>::kValue ||
448+
TypeTraits::IsSame<UintType, uint64_t>::kValue,
449+
"UintType must be `uint16_t`, `uint32_t`, or `uint64_t`");
450+
451+
return LittleEndian::HostSwap<UintType>((LittleEndian::HostSwap<UintType>(aBits) & ~kMask) |
452+
((aValue << kOffset) & kMask));
453+
}
454+
455+
/**
456+
* Writes the specified bits of the given integer stored in big-endian format to the given value and returns the updated
457+
* integer stored in big-endian format.
458+
*
459+
* @tparam UintType The value type (MUST be `uint16_t`, `uint32_t`, or `uint64_t`).
460+
* @tparam kMask The bit mask (MUST not be 0) to write. The @p kMask must be provided in a shifted form.
461+
* @tparam kOffset The bit offset to write. The default @p kOffset is computed from the given @p kMask.
462+
*
463+
* @param[in] aBits A pointer to the integer to write the bits.
464+
* @param[in] aValue The value to write.
465+
*
466+
* @returns The updated integer.
467+
*/
468+
template <typename UintType, UintType kMask, UintType kOffset = BitOffsetOfMask(kMask)>
469+
UintType UpdateBitsBigEndian(UintType aBits, UintType aValue)
470+
{
471+
static_assert(TypeTraits::IsSame<UintType, uint16_t>::kValue || TypeTraits::IsSame<UintType, uint32_t>::kValue ||
472+
TypeTraits::IsSame<UintType, uint64_t>::kValue,
473+
"UintType must be `uint16_t`, `uint32_t`, or `uint64_t`");
474+
475+
return BigEndian::HostSwap<UintType>((BigEndian::HostSwap<UintType>(aBits) & ~kMask) |
476+
((aValue << kOffset) & kMask));
477+
}
478+
479+
/**
480+
* Read the value of the specified bits of the given integer stored in little-endian format.
481+
*
482+
* @tparam UintType The value type (MUST be `uint16_t`, `uint32_t`, or `uint64_t`).
483+
* @tparam kMask The bit mask (MUST not be 0) to write. The @p kMask must be provided in a shifted form.
484+
* @tparam kOffset The bit offset to write. The default @p kOffset is computed from the given @p kMask.
485+
*
486+
* @param[in] aBits The integer stored in little-endian format to read the bits.
487+
*
488+
* @returns The value of the specified bits.
489+
*/
490+
template <typename UintType, UintType kMask, UintType kOffset = BitOffsetOfMask(kMask)>
491+
UintType ReadBitsLittleEndian(UintType aBits)
492+
{
493+
static_assert(TypeTraits::IsSame<UintType, uint16_t>::kValue || TypeTraits::IsSame<UintType, uint32_t>::kValue ||
494+
TypeTraits::IsSame<UintType, uint64_t>::kValue,
495+
"UintType must be `uint16_t`, `uint32_t`, or `uint64_t`");
496+
497+
return (LittleEndian::HostSwap<UintType>(aBits) & kMask) >> kOffset;
498+
}
499+
500+
/**
501+
* Read the value of the specified bits of the given integer stored in big-endian format.
502+
*
503+
* @tparam UintType The value type (MUST be `uint16_t`, `uint32_t`, or `uint64_t`).
504+
* @tparam kMask The bit mask (MUST not be 0) to write. The @p kMask must be provided in a shifted form.
505+
* @tparam kOffset The bit offset to write. The default @p kOffset is computed from the given @p kMask.
506+
*
507+
* @param[in] aBits The integer stored in big-endian format to read the bits.
508+
*
509+
* @returns The value of the specified bits.
510+
*/
511+
template <typename UintType, UintType kMask, UintType kOffset = BitOffsetOfMask(kMask)>
512+
UintType ReadBitsBigEndian(UintType aBits)
513+
{
514+
static_assert(TypeTraits::IsSame<UintType, uint16_t>::kValue || TypeTraits::IsSame<UintType, uint32_t>::kValue ||
515+
TypeTraits::IsSame<UintType, uint64_t>::kValue,
516+
"UintType must be `uint16_t`, `uint32_t`, or `uint64_t`");
517+
518+
return (BigEndian::HostSwap<UintType>(aBits) & kMask) >> kOffset;
519+
}
520+
267521
} // namespace ot
268522

269523
#endif // NUM_UTILS_HPP_

src/core/mac/channel_mask.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ class ChannelMask : public Unequatable<ChannelMask>
135135
*/
136136
bool ContainsChannel(uint8_t aChannel) const
137137
{
138-
return (aChannel < BitSizeOf(mMask)) ? ((1UL << aChannel) & mMask) != 0 : false;
138+
return (aChannel < BitSizeOf(mMask)) ? GetBit(mMask, aChannel) : false;
139139
}
140140

141141
/**
@@ -147,7 +147,7 @@ class ChannelMask : public Unequatable<ChannelMask>
147147
{
148148
if (aChannel < BitSizeOf(mMask))
149149
{
150-
mMask |= (1UL << aChannel);
150+
SetBit<uint32_t>(mMask, aChannel);
151151
}
152152
}
153153

@@ -160,7 +160,7 @@ class ChannelMask : public Unequatable<ChannelMask>
160160
{
161161
if (aChannel < BitSizeOf(mMask))
162162
{
163-
mMask &= ~(1UL << aChannel);
163+
ClearBit<uint32_t>(mMask, aChannel);
164164
}
165165
}
166166

src/core/mac/mac_header_ie.hpp

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -75,32 +75,28 @@ class HeaderIe
7575
*
7676
* @returns the IE Element Id.
7777
*/
78-
uint16_t GetId(void) const { return (LittleEndian::HostSwap16(mFields.m16) & kIdMask) >> kIdOffset; }
78+
uint16_t GetId(void) const { return ReadBitsLittleEndian<uint16_t, kIdMask>(mFields.m16); }
7979

8080
/**
8181
* Sets the IE Element Id.
8282
*
8383
* @param[in] aId The IE Element Id.
8484
*/
85-
void SetId(uint16_t aId)
86-
{
87-
mFields.m16 = LittleEndian::HostSwap16((LittleEndian::HostSwap16(mFields.m16) & ~kIdMask) |
88-
((aId << kIdOffset) & kIdMask));
89-
}
85+
void SetId(uint16_t aId) { mFields.m16 = UpdateBitsLittleEndian<uint16_t, kIdMask>(mFields.m16, aId); }
9086

9187
/**
9288
* Returns the IE content length.
9389
*
9490
* @returns the IE content length.
9591
*/
96-
uint8_t GetLength(void) const { return mFields.m8[0] & kLengthMask; }
92+
uint8_t GetLength(void) const { return ReadBits<uint8_t, kLengthMask>(mFields.m8[0]); }
9793

9894
/**
9995
* Sets the IE content length.
10096
*
10197
* @param[in] aLength The IE content length.
10298
*/
103-
void SetLength(uint8_t aLength) { mFields.m8[0] = (mFields.m8[0] & ~kLengthMask) | (aLength & kLengthMask); }
99+
void SetLength(uint8_t aLength) { WriteBits<uint8_t, kLengthMask>(mFields.m8[0], aLength); }
104100

105101
private:
106102
// Header IE format:
@@ -351,7 +347,7 @@ class ConnectionIe : public VendorIeHeader
351347
*
352348
* @returns the Retry Interval in the units of Wake-up Intervals (7.5ms by default).
353349
*/
354-
uint8_t GetRetryInterval(void) const { return (mConnectionWindow & kRetryIntervalMask) >> kRetryIntervalOffset; }
350+
uint8_t GetRetryInterval(void) const { return ReadBits<uint8_t, kRetryIntervalMask>(mConnectionWindow); }
355351

356352
/**
357353
* Sets the Retry Interval.
@@ -360,7 +356,7 @@ class ConnectionIe : public VendorIeHeader
360356
*/
361357
void SetRetryInterval(uint8_t aRetryInterval)
362358
{
363-
mConnectionWindow = (aRetryInterval << kRetryIntervalOffset) | (mConnectionWindow & ~kRetryIntervalMask);
359+
WriteBits<uint8_t, kRetryIntervalMask>(mConnectionWindow, aRetryInterval);
364360
}
365361

366362
/**
@@ -371,17 +367,14 @@ class ConnectionIe : public VendorIeHeader
371367
*
372368
* @returns the Retry Count.
373369
*/
374-
uint8_t GetRetryCount(void) const { return mConnectionWindow & kRetryCountMask; }
370+
uint8_t GetRetryCount(void) const { return ReadBits<uint8_t, kRetryCountMask>(mConnectionWindow); }
375371

376372
/**
377373
* Sets the Retry Count
378374
*
379375
* @param[in] aRetryCount The Retry Count.
380376
*/
381-
void SetRetryCount(uint8_t aRetryCount)
382-
{
383-
mConnectionWindow = aRetryCount | (mConnectionWindow & ~kRetryCountMask);
384-
}
377+
void SetRetryCount(uint8_t aRetryCount) { WriteBits<uint8_t, kRetryCountMask>(mConnectionWindow, aRetryCount); }
385378

386379
private:
387380
static constexpr uint8_t kRetryIntervalOffset = 4;

0 commit comments

Comments
 (0)