Home | History | Annotate | Download | only in wifilogd
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 // Local utilities (macros and free-standing functions).
     18 
     19 #ifndef LOCAL_UTILS_H_
     20 #define LOCAL_UTILS_H_
     21 
     22 #include <limits>
     23 #include <type_traits>
     24 
     25 #include "android-base/logging.h"
     26 
     27 // Converts the value SRC to a value of DST_TYPE, in the range of [MIN, MAX].
     28 // Values less than MIN are clamped to MIN, and values greater than MAX are
     29 // clamped to MAX. Conversions are safe in the sense that the range is checked
     30 // to be valid for both SRC and DST_TYPE, at compile-time.
     31 //
     32 // As compared to static_cast<>, SAFELY_CLAMP is a) more explicit, b) more
     33 // flexible, and c) less prone to surprising conversions (e.g. -1 becoming
     34 // UINT_MAX).
     35 #define SAFELY_CLAMP(SRC, DST_TYPE, MIN, MAX)                                \
     36   local_utils::internal::SafelyClamp<decltype(SRC), DST_TYPE, MIN, MAX, MIN, \
     37                                      MAX>(SRC)
     38 
     39 // While attributes are standard in C++11, these attributes are not part of
     40 // the standard. We use macros to abstract these attributes, to allow
     41 // the code to compile with compilers that don't recognize these attributes.
     42 #if defined(__clang__)
     43 #define NONNULL [[gnu::nonnull]]                 /* NOLINT(whitespace/braces) */
     44 #define RETURNS_NONNULL [[gnu::returns_nonnull]] /* NOLINT ... */
     45 #else
     46 #define NONNULL
     47 #define RETURNS_NONNULL
     48 #endif
     49 
     50 namespace android {
     51 namespace wifilogd {
     52 namespace local_utils {
     53 
     54 // Returns the value in |enum_value|, as the integral type underlying the
     55 // enum. (E.g. uint8_t, int32_t, etc.)
     56 template <typename T>
     57 constexpr auto CastEnumToInteger(T enum_value) {
     58   static_assert(std::is_enum<T>::value, "argument must be of an enum type");
     59   return static_cast<typename std::underlying_type<T>::type>(enum_value);
     60 }
     61 
     62 // Copies a |T| out of |buf|, aborting if |buf| is too short to hold a |T|.
     63 //
     64 // As compared to accessing the underlying data using reinterpret_cast<>,
     65 // CopyFromBufferOrDie() provides three benefits:
     66 // 1. Guarantees that the returned header is properly aligned. While
     67 //    many processors support some unaligned reads, there are some
     68 //    exceptions. E.g, a 64-bit unaligned read on 32-bit ARM may cause
     69 //    a program to abort.
     70 // 2. Removes the potential for bugs due to compiler optimizations based
     71 //    on type-based alias analysis. (These are the kinds of bugs that
     72 //    "strict-aliasing" warnings try to call out.)
     73 // 3. Verifies that the source buffer is large enough to contain the
     74 //    data we're trying to read out.
     75 template <typename T>
     76 T CopyFromBufferOrDie(NONNULL const void* buf, size_t buf_len) {
     77   static_assert(std::is_trivially_copyable<T>::value,
     78                 "CopyFromBufferOrDie can only copy trivially copyable types");
     79   T out;
     80   CHECK(buf_len >= sizeof(out));
     81   std::memcpy(&out, buf, sizeof(out));
     82   return out;
     83 }
     84 
     85 // Returns the maximal value representable by T. Generates a compile-time
     86 // error if T is not an integral type.
     87 template <typename T>
     88 constexpr T GetMaxVal() {
     89   // Give a useful error for non-numeric types, and avoid returning zero for
     90   // pointers and C-style enums (http://stackoverflow.com/a/9201960).
     91   static_assert(std::is_integral<T>::value,
     92                 "GetMaxVal requires an integral type");
     93   return std::numeric_limits<T>::max();
     94 }
     95 
     96 // Returns the maximal value representable by |t_instance|. Generates a
     97 // compile-time error if |t_instance| is not an instance of an integral type.
     98 template <typename T>
     99 constexpr T GetMaxVal(const T& /* t_instance */) {
    100   return GetMaxVal<T>();
    101 }
    102 
    103 // Returns true if |c| is a printable character, for ASCII data.
    104 inline bool IsAsciiPrintable(uint8_t c) {
    105   return (c == '\t' || c == '\n' || (c >= ' ' && c <= '~'));
    106 }
    107 
    108 namespace internal {
    109 
    110 // Implements the functionality documented for the SAFELY_CLAMP macro.
    111 // This function should be used via the SAFELY_CLAMP macro.
    112 template <typename SrcType, typename DstType, SrcType MinAsSrcType,
    113           SrcType MaxAsSrcType, DstType MinAsDstType, DstType MaxAsDstType>
    114 DstType SafelyClamp(SrcType input) {
    115   static_assert(std::is_integral<SrcType>::value,
    116                 "source type must be integral");
    117   static_assert(std::is_integral<DstType>::value,
    118                 "destination type must be integral");
    119   static_assert(MinAsSrcType < MaxAsSrcType, "invalid source range");
    120   static_assert(MinAsDstType < MaxAsDstType, "invalid destination range");
    121   // Clients should use the SAFELY_CLAMP macro, in which case this should never
    122   // happen. (When the SAFELY_CLAMP macro is used, the values can only be
    123   // unequal if there was a narrowing conversion. But, in that case, the value
    124   // should have failed to match the template, since narrowing-conversions are
    125   // not allowed for non-type template arguments.
    126   // http://stackoverflow.com/a/24346350)
    127   //
    128   // Anyway, these checks provide a fail-safe, in case clients use the template
    129   // function directly, and pass in inconsistent values for the range
    130   // definition.
    131   static_assert(MinAsSrcType == MinAsDstType, "inconsistent range min");
    132   static_assert(MaxAsSrcType == MaxAsDstType, "inconsistent range max");
    133 
    134   if (input < MinAsSrcType) {
    135     return MinAsDstType;
    136   } else if (input > MaxAsSrcType) {
    137     return MaxAsDstType;
    138   } else {
    139     // - Given that the template has matched, we know that MinAsSrcType,
    140     //   MaxAsSrcType, MinAsDstType, and MaxAsDstType are valid for their
    141     //   respective types. (See narrowing-conversion comment above.)
    142     // - Given the static_assert()s above, we know that a) the ranges are
    143     //   well-formed, and that the b) source range is identical to the
    144     //   destination range.
    145     // - Given the range checks above, we know that |input| is within the range.
    146     //
    147     // Hence, the value to be returned must be valid for DstType, and the
    148     // expression below has the same value as |input|.
    149     return static_cast<DstType>(input);
    150   }
    151 }
    152 
    153 }  // namespace internal
    154 
    155 }  // namespace local_utils
    156 }  // namespace wifilogd
    157 }  // namespace android
    158 
    159 #endif  // LOCAL_UTILS_H_
    160