blob: b9f76ee62bc7360b5ad56069b8cc710d37944463 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.
*/
// Local utilities (macros and free-standing functions).
#ifndef LOCAL_UTILS_H_
#define LOCAL_UTILS_H_
#include <limits>
#include <type_traits>
// Converts the value SRC to a value of DST_TYPE, in the range of [MIN, MAX].
// Values less than MIN are clamped to MIN, and values greater than MAX are
// clamped to MAX. Conversions are safe in the sense that the range is checked
// to be valid for both SRC and DST_TYPE, at compile-time.
//
// As compared to static_cast<>, SAFELY_CLAMP is a) more explicit, b) more
// flexible, and c) less prone to surprising conversions (e.g. -1 becoming
// UINT_MAX).
#define SAFELY_CLAMP(SRC, DST_TYPE, MIN, MAX) \
local_utils::internal::SafelyClamp<decltype(SRC), DST_TYPE, MIN, MAX, MIN, \
MAX>(SRC)
// While attributes are standard in C++11, the nonnull attribute is not part of
// the standard. We use a macro to abstract the nonnull attribute, to allow
// the code to compile with compilers that don't recognize nonnull.
#if defined(__clang__)
#define NONNULL [[gnu::nonnull]] /* NOLINT(whitespace/braces) */
#else
#define NONNULL
#endif
namespace android {
namespace wifilogd {
namespace local_utils {
// Returns the maximal value representable by T. Generates a compile-time
// error if T is not an integral type.
template <typename T>
constexpr T GetMaxVal() {
// Give a useful error for non-numeric types, and avoid returning zero for
// pointers and C-style enums (http://stackoverflow.com/a/9201960).
static_assert(std::is_integral<T>::value,
"GetMaxVal requires an integral type");
return std::numeric_limits<T>::max();
}
// Returns the maximal value representable by |t_instance|. Generates a
// compile-time error if |t_instance| is not an instance of an integral type.
template <typename T>
constexpr T GetMaxVal(const T& /* t_instance */) {
return GetMaxVal<T>();
}
namespace internal {
// Implements the functionality documented for the SAFELY_CLAMP macro.
// This function should be used via the SAFELY_CLAMP macro.
template <typename SrcType, typename DstType, SrcType MinAsSrcType,
SrcType MaxAsSrcType, DstType MinAsDstType, DstType MaxAsDstType>
DstType SafelyClamp(SrcType input) {
static_assert(std::is_integral<SrcType>::value,
"source type must be integral");
static_assert(std::is_integral<DstType>::value,
"destination type must be integral");
static_assert(MinAsSrcType < MaxAsSrcType, "invalid source range");
static_assert(MinAsDstType < MaxAsDstType, "invalid destination range");
// Clients should use the SAFELY_CLAMP macro, in which case this should never
// happen. (When the SAFELY_CLAMP macro is used, the values can only be
// unequal if there was a narrowing conversion. But, in that case, the value
// should have failed to match the template, since narrowing-conversions are
// not allowed for non-type template arguments.
// http://stackoverflow.com/a/24346350)
//
// Anyway, these checks provide a fail-safe, in case clients use the template
// function directly, and pass in inconsistent values for the range
// definition.
static_assert(MinAsSrcType == MinAsDstType, "inconsistent range min");
static_assert(MaxAsSrcType == MaxAsDstType, "inconsistent range max");
if (input < MinAsSrcType) {
return MinAsDstType;
} else if (input > MaxAsSrcType) {
return MaxAsDstType;
} else {
// - Given that the template has matched, we know that MinAsSrcType,
// MaxAsSrcType, MinAsDstType, and MaxAsDstType are valid for their
// respective types. (See narrowing-conversion comment above.)
// - Given the static_assert()s above, we know that a) the ranges are
// well-formed, and that the b) source range is identical to the
// destination range.
// - Given the range checks above, we know that |input| is within the range.
//
// Hence, the value to be returned must be valid for DstType, and the
// expression below has the same value as |input|.
return static_cast<DstType>(input);
}
}
} // namespace internal
} // namespace local_utils
} // namespace wifilogd
} // namespace android
#endif // LOCAL_UTILS_H_