blob: c1f7efb80611ca791a133e37a6a999c6761676ea [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* Copyright (c) 2013-2017 Nest Labs, Inc.
*
* 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.
*/
/**
* @file
* This file defines functions for manipulating Boolean flags in
* a bitfield.
*
*/
#pragma once
#include <stdint.h>
#include <type_traits>
#include <utility>
namespace chip {
/**
* Stores bit flags in a type safe manner.
*
* @tparam FlagsEnum is an `enum` or (preferably) `enum class` type.
* @tparam StorageType is the underlying storage type (like uint16_t, uint32_t etc.)
* and defaults to the underlying storage type of `FlagsEnum`.
*/
template <typename FlagsEnum, typename StorageType = typename std::underlying_type_t<FlagsEnum>>
class BitFlags
{
public:
static_assert(sizeof(StorageType) >= sizeof(FlagsEnum), "All flags should fit in the storage type");
using IntegerType = StorageType;
constexpr BitFlags() : mValue(0) {}
BitFlags(const BitFlags & other) = default;
BitFlags & operator=(const BitFlags &) = default;
explicit constexpr BitFlags(FlagsEnum value) : mValue(static_cast<IntegerType>(value)) {}
explicit constexpr BitFlags(IntegerType value) : mValue(value) {}
template <typename... Args>
constexpr BitFlags(FlagsEnum flag, Args &&... args) : mValue(Or(flag, std::forward<Args>(args)...))
{}
template <typename... Args>
constexpr BitFlags(const BitFlags<FlagsEnum> & flags, Args &&... args) : mValue(Or(flags, std::forward<Args>(args)...))
{}
template <typename... Args>
constexpr BitFlags(IntegerType value, Args &&... args) : mValue(value | Or(std::forward<Args>(args)...))
{}
/**
* Set flag(s).
*
* @param other Flag(s) to set. Any flags not set in @a other are unaffected.
*/
BitFlags & Set(const BitFlags & other)
{
mValue |= other.mValue;
return *this;
}
/**
* Set flag(s).
*
* @param flag Typed flag(s) to set. Any flags not in @a v are unaffected.
*/
constexpr BitFlags & Set(FlagsEnum flag)
{
mValue |= static_cast<IntegerType>(flag);
return *this;
}
/**
* Set or clear flag(s).
*
* @param flag Typed flag(s) to set or clear. Any flags not in @a flag are unaffected.
* @param isSet If true, set the flag; if false, clear it.
*/
constexpr BitFlags & Set(FlagsEnum flag, bool isSet) { return isSet ? Set(flag) : Clear(flag); }
/**
* Clear flag(s).
*
* @param other Typed flag(s) to clear. Any flags not in @a other are unaffected.
*/
BitFlags & Clear(const BitFlags & other)
{
mValue &= static_cast<IntegerType>(~static_cast<IntegerType>(other.mValue));
return *this;
}
/**
* Clear flag(s).
*
* @param flag Typed flag(s) to clear. Any flags not in @a flag are unaffected.
*/
constexpr BitFlags & Clear(FlagsEnum flag)
{
mValue &= static_cast<IntegerType>(~static_cast<IntegerType>(flag));
return *this;
}
/**
* Clear all flags.
*/
BitFlags & ClearAll()
{
mValue = 0;
return *this;
}
/**
* Check whether flag(s) are set.
*
* @param flag Flag(s) to test.
* @returns True if any flag in @a flag is set, otherwise false.
*/
constexpr bool Has(FlagsEnum flag) const { return (mValue & static_cast<IntegerType>(flag)) != 0; }
/**
* Check that no flags outside the arguments are set.
*
* @param args Flags to test. Arguments can be BitFlags<FlagsEnum>, BitFlags<FlagsEnum>, or FlagsEnum.
* @returns True if no flag is set other than those passed.
* False if any flag is set other than those passed.
*
* @note Flags passed need not be set; this test only requires that no *other* flags be set.
*/
template <typename... Args>
bool HasOnly(Args &&... args) const
{
return (mValue & Or(std::forward<Args>(args)...)) == mValue;
}
/**
* Check that all given flags are set.
*
* @param args Flags to test. Arguments can be BitFlags<FlagsEnum>, BitFlags<FlagsEnum>, or FlagsEnum.
* @returns True if all given flags are set.
* False if any given flag is not set.
*/
template <typename... Args>
bool HasAll(Args &&... args) const
{
const IntegerType all = Or(std::forward<Args>(args)...);
return (mValue & all) == all;
}
/**
* Check that at least one of the given flags is set.
*
* @param args Flags to test. Arguments can be BitFlags<FlagsEnum>, BitFlags<FlagsEnum>, or FlagsEnum.
* @returns True if all given flags are set.
* False if any given flag is not set.
*/
template <typename... Args>
bool HasAny(Args &&... args) const
{
return (mValue & Or(std::forward<Args>(args)...)) != 0;
}
/**
* Check that at least one flag is set.
*
* @returns True if any flag is set, false otherwise.
*/
bool HasAny() const { return mValue != 0; }
/**
* Find the logical intersection of flags.
*
* @param lhs Some flags.
* @param rhs Some flags.
* @returns Flags set in both @a lhs and @a rhs.
*
* @note: A multi-argument `BitFlags` constructor serves the function of `operator|`.
*/
friend BitFlags<FlagsEnum> operator&(BitFlags<FlagsEnum> lhs, const BitFlags<FlagsEnum> & rhs)
{
return BitFlags<FlagsEnum>(lhs.mValue & rhs.mValue);
}
/**
* Get the flags as the type FlagsEnum.
*
* @note This allows easily storing flags as a base FlagsEnum in a POD type,
* and enables equality comparisons.
*/
operator FlagsEnum() const { return static_cast<FlagsEnum>(mValue); }
/**
* Set and/or clear all flags with a value of the underlying storage type.
*
* @param value New storage value.
*/
BitFlags & SetRaw(IntegerType value)
{
mValue = value;
return *this;
}
/**
* Get the flags as the underlying integer type.
*
* @note This is intended to be used only to store flags into a raw binary record.
*/
constexpr IntegerType Raw() const { return mValue; }
/**
* Get the address of the flags as a pointer to the underlying integer type.
*
* @note This is intended to be used only to read flags from a raw binary record.
*/
StorageType * RawStorage() { return &mValue; }
private:
// Find the union of BitFlags and/or FlagsEnum values.
template <typename... Args>
static constexpr IntegerType Or(FlagsEnum flag, Args &&... args)
{
return static_cast<IntegerType>(flag) | Or(std::forward<Args>(args)...);
}
template <typename... Args>
static constexpr IntegerType Or(const BitFlags<FlagsEnum> & flags, Args &&... args)
{
return flags.mValue | Or(std::forward<Args>(args)...);
}
static constexpr IntegerType Or(FlagsEnum value) { return static_cast<IntegerType>(value); }
static constexpr IntegerType Or(const BitFlags<FlagsEnum> & flags) { return flags.Raw(); }
StorageType mValue = 0;
};
} // namespace chip