LCOV - code coverage report
Current view: directory - src/include - checked_cast.h (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 69 60 87.0 %
Date: 2012-02-16 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright 2010 The Native Client Authors.  All rights reserved.
       3                 :  * Use of this source code is governed by a BSD-style license that can
       4                 :  * be found in the LICENSE file.
       5                 :  */
       6                 : 
       7                 : //
       8                 : // checked_cast.h
       9                 : //
      10                 : // A template system, intended to be a drop-in replacement for static_cast<>,
      11                 : // which performs compile-time and (if necessary) runtime evaluation of
      12                 : // integral type casts to detect and handle arithmetic overflow errors.
      13                 : //
      14                 : #ifndef NATIVE_CLIENT_SRC_INCLUDE_CHECKED_CAST_H_
      15                 : #define NATIVE_CLIENT_SRC_INCLUDE_CHECKED_CAST_H_ 1
      16                 : 
      17                 : // Windows defines std::min and std::max in a different header
      18                 : // than gcc.
      19                 : #if NACL_WINDOWS
      20                 : #include <xutility>
      21                 : #endif
      22                 : 
      23                 : #include <limits>
      24                 : 
      25                 : #if !NACL_WINDOWS
      26                 : // this is where std::min/max SHOULD live. It's included here (rather than
      27                 : // in an else block along with the Windows include above) to avoid breaking
      28                 : // google cpplint's header file inclusion order rules.
      29                 : #include <algorithm>
      30                 : #endif
      31                 : 
      32                 : // TODO(ilewis): remove reference to base as soon as we can get COMPILE_ASSERT
      33                 : //                from another source.
      34                 : #include "native_client/src/shared/platform/nacl_log.h"
      35                 : 
      36                 : namespace nacl {
      37                 : 
      38                 :   //
      39                 :   // Function to determine whether an information-preserving cast
      40                 :   // is possible. In other words, if this function returns true, then
      41                 :   // the input value has an exact representation in the target type.
      42                 :   //
      43                 :   template<typename target_t, typename source_t>
      44                 :   bool can_cast(const source_t& input);
      45                 : 
      46                 :   //
      47                 :   // Function to safely cast from one type to another, with a customizable
      48                 :   // policy determining what happens if the cast cannot complete without
      49                 :   // losing information.
      50                 :   //
      51                 :   template <
      52                 :     typename target_t,
      53                 :     typename source_t,
      54                 :     template<typename, typename> class trunc_policy>
      55                 :   target_t checked_cast(const source_t& input);
      56                 : 
      57                 :   //
      58                 :   // Convenience wrappers for specializations of checked_cast with
      59                 :   // different truncation policies
      60                 :   //
      61                 :   template<typename T, typename S>
      62                 :   T assert_cast(const S& input);
      63                 : 
      64                 :   template<typename T, typename S>
      65                 :   T saturate_cast(const S& input);
      66                 : 
      67                 : 
      68                 :   //
      69                 :   // Helper function prototypes
      70                 :   //
      71                 :   //
      72                 :   namespace CheckedCast {
      73                 :     //
      74                 :     // OnTruncate* functions: policy functions that define
      75                 :     // what to do if a checked cast can't be made without
      76                 :     // truncation that loses data.
      77                 :     //
      78                 :     template <typename target_t, typename source_t>
      79                 :     struct TruncationPolicySaturate {
      80                 :       static target_t OnTruncate(const source_t& input);
      81                 :     };
      82                 : 
      83                 :     template <typename target_t, typename source_t>
      84                 :     struct TruncationPolicyAbort {
      85                 :       static target_t OnTruncate(const source_t& input);
      86                 :     };
      87                 : 
      88                 :     namespace detail {
      89                 : //-----------------------------------------------------------------------------
      90                 : // RuntimeHelpers
      91                 : //
      92                 : // Ugly nested templates--necessary because GCC is ever vigilant about type
      93                 : // safety, and has no way to temporarily disable certain warnings
      94                 : //
      95                 : //-----------------------------------------------------------------------------
      96                 : 
      97                 :       //
      98                 :       // TrivialityChecker template
      99                 :       // Makes decisions about whether a cast is trivial (does not require
     100                 :       // the value of the input to be checked).
     101                 :       //
     102                 :       // The specializations are necessary to avoid running comparisons that
     103                 :       // are either invalid or always true, since these comparisons trigger
     104                 :       // warnings.
     105                 :       //
     106                 :       template<typename tlimits, typename slimits, bool IsCompileTimeTrivial>
     107                 :       struct TrivialityChecker {
     108             214 :         static bool IsRuntimeTrivial() { return false; }
     109                 :       };
     110                 : 
     111                 :       template<typename tlimits, typename slimits>
     112                 :       struct TrivialityChecker<tlimits, slimits, true> {
     113               3 :         static bool IsRuntimeTrivial() {
     114                 :           // Split into variables to bypass const value warning
     115               3 :           bool tlimits_lequ_min_slimits = tlimits::min() <= slimits::min();
     116               3 :           bool tlimits_gequ_max_slimits = tlimits::max() >= slimits::max();
     117               3 :           bool trivial = tlimits_lequ_min_slimits && tlimits_gequ_max_slimits;
     118               3 :           return trivial;
     119                 :         }
     120                 :       };
     121                 : 
     122                 :       //
     123                 :       // Casting versions of std::min and std::max.
     124                 :       // These should only be called from code that has checked to make
     125                 :       // sure the cast is valid, otherwise compiler warnings will be
     126                 :       // triggered.
     127                 :       //
     128                 :       template<typename A, typename B>
     129             668 :       static B cast_min(const A& a, const B& b) {
     130             668 :         return (static_cast<B>(a) < b) ? static_cast<B>(a) : b;
     131                 :       }
     132                 : 
     133                 :       template<typename A, typename B>
     134              21 :       static B cast_max(const A& a, const B& b) {
     135              21 :         return (static_cast<B>(a) > b) ? static_cast<B>(a) : b;
     136                 :       }
     137                 : 
     138                 :       //
     139                 :       // Template specializations for determining valid ranges for
     140                 :       // any given typecast. Specialized for different combinations
     141                 :       // of signed/unsigned types.
     142                 :       //
     143                 :       template<typename target_t,
     144                 :                typename source_t,
     145                 :                bool SignednessDiffers,
     146                 :                bool TargetIsSigned>
     147                 :       struct RangeDetail {
     148                 :         typedef std::numeric_limits<target_t> tlimits;
     149                 :         typedef std::numeric_limits<source_t> slimits;
     150                 : 
     151              21 :         static source_t OverlapMin() {
     152              21 :           return cast_max(tlimits::min(), slimits::min());
     153                 :         }
     154              24 :         static source_t OverlapMax() {
     155              24 :           return cast_min(tlimits::max(), slimits::max());
     156                 :         }
     157                 :       };
     158                 : 
     159                 :       // signed target / unsigned source
     160                 :       template<typename target_t, typename source_t>
     161                 :       struct RangeDetail<target_t, source_t, true, true> {
     162                 :         typedef std::numeric_limits<target_t> tlimits;
     163                 :         typedef std::numeric_limits<source_t> slimits;
     164                 : 
     165             625 :         static source_t OverlapMin() {
     166             625 :           if (tlimits::min() >= 0) {
     167               0 :             return cast_max(tlimits::min(), slimits::min());
     168                 :           } else {
     169             625 :             return slimits::min();
     170                 :           }
     171                 :         }
     172             628 :         static source_t OverlapMax() {
     173             628 :           if (tlimits::max() >= 0) {
     174             628 :             return cast_min(tlimits::max(), slimits::max());
     175                 :           } else {
     176               0 :             return slimits::min();
     177                 :           }
     178                 :         }
     179                 :       };
     180                 : 
     181                 :       // unsigned target / signed source
     182                 :       template<typename target_t, typename source_t>
     183                 :       struct RangeDetail<target_t, source_t, true, false> {
     184                 :         typedef std::numeric_limits<target_t> tlimits;
     185                 :         typedef std::numeric_limits<source_t> slimits;
     186                 : 
     187               8 :         static source_t OverlapMin() {
     188               8 :           if (slimits::min() >= 0) {
     189               0 :             return cast_max(tlimits::min(), slimits::min());
     190               8 :           } else if (slimits::max() >= 0) {
     191               8 :             return cast_min(tlimits::min(), slimits::max());
     192                 :           } else {
     193               0 :             return slimits::min();
     194                 :           }
     195                 :         }
     196               8 :         static source_t OverlapMax() {
     197               8 :           if (slimits::max() >= 0) {
     198               8 :             return cast_min(tlimits::max(), slimits::max());
     199                 :           } else {
     200               0 :             return slimits::min();
     201                 :           }
     202                 :         }
     203                 :       };
     204                 : 
     205                 :       //
     206                 :       // Wrapper for RangeDetail. Prevents RangeDetail objects
     207                 :       // from being instantiated for unsupported types.
     208                 :       //
     209                 :       template<typename target_t, typename source_t, bool IsSupported>
     210                 :       struct RangeHelper {
     211                 :         typedef std::numeric_limits<target_t> tlimits;
     212                 :         typedef std::numeric_limits<source_t> slimits;
     213                 : 
     214                 :         static bool OverlapExists() { return false; }
     215                 : 
     216                 :         // The return values from OverlapMin() and OverlapMax() are
     217                 :         // arbitrary if OverlapExists() returns false, so we'll
     218                 :         // just return the minimum source value for both.
     219                 :         static source_t OverlapMin() { return slimits::min(); }
     220                 :         static source_t OverlapMax() { return slimits::min(); }
     221                 :       };
     222                 : 
     223                 :       template<typename target_t, typename source_t>
     224                 :       struct RangeHelper<target_t, source_t, true> {
     225                 :         typedef std::numeric_limits<target_t> tlimits;
     226                 :         typedef std::numeric_limits<source_t> slimits;
     227                 : 
     228                 :         typedef RangeDetail<target_t,
     229                 :           source_t,
     230                 :           (tlimits::is_signed != slimits::is_signed),
     231                 :           tlimits::is_signed> detail_t;
     232                 : 
     233             438 :         static bool OverlapExists() {
     234             438 :           return detail_t::OverlapMin() < detail_t::OverlapMax();
     235                 :         }
     236             216 :         static source_t OverlapMin() { return detail_t::OverlapMin(); }
     237             222 :         static source_t OverlapMax() { return detail_t::OverlapMax(); }
     238                 :       };
     239                 : 
     240                 :       //
     241                 :       // CastInfo
     242                 :       //
     243                 :       // Maintains information about how to cast between
     244                 :       // two types.
     245                 :       //
     246                 :       template<typename target_t, typename source_t>
     247                 :       struct CastInfo {
     248                 :         typedef std::numeric_limits<target_t> tlimits;
     249                 :         typedef std::numeric_limits<source_t> slimits;
     250                 : 
     251                 :         static const bool kSupported = tlimits::is_specialized
     252                 :           && slimits::is_specialized
     253                 :           && tlimits::is_integer
     254                 :           && slimits::is_integer
     255                 :           && tlimits::is_bounded
     256                 :           && slimits::is_bounded;
     257                 : 
     258                 :         static const bool kIdenticalSignedness =
     259                 :           (tlimits::is_signed == slimits::is_signed);
     260                 : 
     261                 :         // "digits" in numeric_limits refers to binary
     262                 :         // digits. (Decimal digits are stored in a field
     263                 :         // called digits10.)
     264                 :         static const bool kSufficientBits =
     265                 :           (tlimits::digits >= slimits::digits);
     266                 : 
     267                 :         static const bool kCompileTimeTrivial = kSupported
     268                 :           && kIdenticalSignedness
     269                 :           && kSufficientBits;
     270                 : 
     271                 :         typedef TrivialityChecker<tlimits,
     272                 :                                   slimits,
     273                 :                                   kCompileTimeTrivial> trivial_t;
     274                 :         typedef RangeHelper<target_t, source_t, kSupported> range_t;
     275                 : 
     276                 :         // Can the cast be done without runtime checks--i.e. are
     277                 :         // all possible input values also valid output values?
     278             217 :         static bool RuntimeTrivial() {
     279             217 :           return trivial_t::IsRuntimeTrivial();
     280                 :         }
     281                 : 
     282                 :         // Are there any valid input values for which a cast would succeed?
     283             222 :         static bool RuntimePossible() {
     284             222 :           return range_t::OverlapExists();
     285                 :         }
     286                 : 
     287                 :         // Is the given source value a valid target value?
     288             216 :         static bool RuntimeRangeCheck(const source_t& src) {
     289                 :           return (range_t::OverlapExists()
     290                 :                   && src <= range_t::OverlapMax()
     291             216 :                   && src >= range_t::OverlapMin());
     292                 :         }
     293                 : 
     294                 :         // Range of source values which are also valid target values.
     295               6 :         static source_t RuntimeRangeMin() { return range_t::OverlapMin(); }
     296               6 :         static source_t RuntimeRangeMax() { return range_t::OverlapMax(); }
     297                 :       };
     298                 :     }  // namespace detail
     299                 :   }  // namespace CheckedCast
     300                 : }  // namespace nacl
     301                 : 
     302                 : 
     303                 : //-----------------------------------------------------------------------------
     304                 : //
     305                 : // Implementation
     306                 : //
     307                 : //-----------------------------------------------------------------------------
     308                 : 
     309                 : //-----------------------------------------------------------------------------
     310                 : // checked_cast(const source_t& input)
     311                 : //    An augmented replacement for static_cast. Does range checking,
     312                 : //    overflow validation. Includes a policy which allows the caller to
     313                 : //    specify what action to take if it's determined that the cast
     314                 : //    would lose data.
     315                 : //
     316                 : // Template parameters:
     317                 : //    target_t:     the type on the left hand side of the cast
     318                 : //    source_t:     the type on the right hand side of the cast
     319                 : //    trunc_policy: type of a policy object that will be called
     320                 : //                if the cast would result in a loss of data. The
     321                 : //                type must implement a method named OnTruncate which
     322                 : //                is compatible with the following signature:
     323                 : //                target_t (target_t& output, const source_t& input).
     324                 : //                It is the responsibility of this function to
     325                 : //                convert the value of 'input' and store the result
     326                 : //                of the conversion in out parameter 'output'. It is
     327                 : //                permissible for the function to abort or log warnings
     328                 : //                if that is the desired behavior.
     329                 : //
     330                 : // Function parameters:
     331                 : //    input:    the value to convert.
     332                 : //
     333                 : // usage:
     334                 : //    // naive truncation handler for sample purposes ONLY!!
     335                 : //    template <typename T, typename S>
     336                 : //    void naive_trunc(T& o, const S& i) {
     337                 : //      // this only gets called if checked_cast can't do a safe automatic
     338                 : //      // conversion. In real code you'd want to do something cool like
     339                 : //      // clamp the incoming value or abort the program. For this sample
     340                 : //      // we just return a c-style cast, which makes the outcome roughly
     341                 : //      // equivalent to static_cast<>.
     342                 : //      o = (T)i;
     343                 : //    }
     344                 : //
     345                 : //    void main() {
     346                 : //      uint64_t foo = 0xffff0000ffff0000;
     347                 : //      uint32_t bar = checked_cast<uint32_t>(foo, naive_trunc);
     348                 : //    }
     349                 : //
     350                 : template <
     351                 :     typename target_t,
     352                 :     typename source_t,
     353                 :     template<typename, typename> class trunc_policy>
     354             211 : target_t nacl::checked_cast(const source_t& input) {
     355                 :   typedef CheckedCast::detail::CastInfo<target_t, source_t> info;
     356                 : 
     357               2 :   target_t output;
     358                 : 
     359                 :   //
     360                 :   // Runtime checks--these should compile out for all basic types
     361                 :   //
     362             211 :   if (nacl::can_cast<target_t>(input)) {
     363             205 :     output = static_cast<target_t>(input);
     364                 :   } else {
     365               6 :     output = trunc_policy<target_t, source_t>::OnTruncate(input);
     366                 :   }
     367                 : 
     368             211 :   return output;
     369                 : }
     370                 : 
     371                 : //-----------------------------------------------------------------------------
     372                 : // can_cast(const source_t& input)
     373                 : //  Returns true if checked_cast will return without invoking trunc_policy.
     374                 : //-----------------------------------------------------------------------------
     375                 : template <typename target_t, typename source_t>
     376             217 : bool nacl::can_cast(const source_t& input) {
     377                 :   typedef CheckedCast::detail::CastInfo<target_t, source_t> info;
     378                 : 
     379                 :   bool result;
     380                 : 
     381                 :   //
     382                 :   // Runtime checks--these should compile out for all basic types
     383                 :   //
     384             217 :   result = info::RuntimeTrivial()
     385                 :     || (info::RuntimePossible()
     386                 :     && info::RuntimeRangeCheck(input));
     387                 : 
     388             217 :   return result;
     389                 : }
     390                 : 
     391                 : 
     392                 : //
     393                 : // Convenience wrappers for specializations of checked_cast
     394                 : //
     395                 : 
     396                 : //-----------------------------------------------------------------------------
     397                 : // checked_cast_fatal(const S& input)
     398                 : // Calls checked_cast; on truncation, log error and abort
     399                 : //-----------------------------------------------------------------------------
     400                 : template<typename T, typename S>
     401             205 : T nacl::assert_cast(const S& input) {
     402             205 :   return checked_cast<T, S, CheckedCast::TruncationPolicyAbort>(input);
     403                 : }
     404                 : //-----------------------------------------------------------------------------
     405                 : // saturate_cast(const S& input)
     406                 : // Calls checked_cast; on truncation, saturates the input to the minimum
     407                 : // or maximum of the output.
     408                 : //-----------------------------------------------------------------------------
     409                 : template<typename T, typename S>
     410               6 : T nacl::saturate_cast(const S& input) {
     411                 :   return
     412               6 :     checked_cast<T, S, CheckedCast::TruncationPolicySaturate>(input);
     413                 : }
     414                 : 
     415                 : //-----------------------------------------------------------------------------
     416                 : // CheckedCastDetail::OnTruncationSaturate
     417                 : // Implements the Saturate truncation policy.
     418                 : //-----------------------------------------------------------------------------
     419                 : template <typename target_t, typename source_t>
     420                 : target_t nacl
     421                 :     ::CheckedCast
     422                 :     ::TruncationPolicySaturate<target_t, source_t>
     423               6 :     ::OnTruncate(const source_t& input) {
     424                 :   typedef detail::CastInfo<target_t, source_t> info;
     425                 : 
     426               6 :   source_t clamped = input;
     427               6 :   bool valid = info::RuntimePossible();
     428                 : 
     429               6 :   if (!valid) {
     430               0 :     NaClLog(LOG_FATAL, "Checked cast: type ranges do not overlap");
     431                 :   }
     432                 : 
     433               6 :   clamped = std::max(clamped, info::RuntimeRangeMin());
     434               6 :   clamped = std::min(clamped, info::RuntimeRangeMax());
     435                 : 
     436               6 :   target_t output = static_cast<target_t>(clamped);
     437                 : 
     438               6 :   return output;
     439                 : }
     440                 : 
     441                 : 
     442                 : 
     443                 : //-----------------------------------------------------------------------------
     444                 : // CheckedCastDetail::OnTruncationAbort
     445                 : // Implements the Abort truncation policy.
     446                 : //-----------------------------------------------------------------------------
     447                 : template <typename target_t, typename source_t>
     448                 : target_t nacl
     449                 :     ::CheckedCast
     450                 :     ::TruncationPolicyAbort<target_t, source_t>
     451               0 :     ::OnTruncate(const source_t&) {
     452               0 :   NaClLog(LOG_FATAL, "Arithmetic overflow");
     453                 : 
     454                 :   // Unreachable, assuming that LOG_FATAL really is fatal
     455               0 :   return 0;
     456                 : }
     457                 : 
     458                 : 
     459                 : #endif  /* NATIVE_CLIENT_SRC_INCLUDE_CHECKED_CAST_H_ */

Generated by: LCOV version 1.7