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.
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.
14 #ifndef NATIVE_CLIENT_SRC_INCLUDE_CHECKED_CAST_H_
15 #define NATIVE_CLIENT_SRC_INCLUDE_CHECKED_CAST_H_ 1
17 // Windows defines std::min and std::max in a different header
18 // than gcc prior to Visual Studio 2013.
26 // TODO(ilewis): remove reference to base as soon as we can get COMPILE_ASSERT
27 // from another source.
28 #include "native_client/src/shared/platform/nacl_log.h"
33 // Function to determine whether an information-preserving cast
34 // is possible. In other words, if this function returns true, then
35 // the input value has an exact representation in the target type.
37 template<typename target_t, typename source_t>
38 bool can_cast(const source_t& input);
41 // Function to safely cast from one type to another, with a customizable
42 // policy determining what happens if the cast cannot complete without
43 // losing information.
48 template<typename, typename> class trunc_policy>
49 target_t checked_cast(const source_t& input);
52 // Convenience wrappers for specializations of checked_cast with
53 // different truncation policies
55 template<typename T, typename S>
56 T assert_cast(const S& input);
58 template<typename T, typename S>
59 T saturate_cast(const S& input);
63 // Helper function prototypes
66 namespace CheckedCast {
68 // OnTruncate* functions: policy functions that define
69 // what to do if a checked cast can't be made without
70 // truncation that loses data.
72 template <typename target_t, typename source_t>
73 struct TruncationPolicySaturate {
74 static target_t OnTruncate(const source_t& input);
77 template <typename target_t, typename source_t>
78 struct TruncationPolicyAbort {
79 static target_t OnTruncate(const source_t& input);
83 //-----------------------------------------------------------------------------
86 // Ugly nested templates--necessary because GCC is ever vigilant about type
87 // safety, and has no way to temporarily disable certain warnings
89 //-----------------------------------------------------------------------------
92 // TrivialityChecker template
93 // Makes decisions about whether a cast is trivial (does not require
94 // the value of the input to be checked).
96 // The specializations are necessary to avoid running comparisons that
97 // are either invalid or always true, since these comparisons trigger
100 template<typename tlimits, typename slimits, bool IsCompileTimeTrivial>
101 struct TrivialityChecker {
102 static bool IsRuntimeTrivial() { return false; }
105 template<typename tlimits, typename slimits>
106 struct TrivialityChecker<tlimits, slimits, true> {
107 static bool IsRuntimeTrivial() {
108 // Split into variables to bypass const value warning
109 bool tlimits_lequ_min_slimits = tlimits::min() <= slimits::min();
110 bool tlimits_gequ_max_slimits = tlimits::max() >= slimits::max();
111 bool trivial = tlimits_lequ_min_slimits && tlimits_gequ_max_slimits;
117 // Casting versions of std::min and std::max.
118 // These should only be called from code that has checked to make
119 // sure the cast is valid, otherwise compiler warnings will be
122 template<typename A, typename B>
123 static B cast_min(const A& a, const B& b) {
124 return (static_cast<B>(a) < b) ? static_cast<B>(a) : b;
127 template<typename A, typename B>
128 static B cast_max(const A& a, const B& b) {
129 return (static_cast<B>(a) > b) ? static_cast<B>(a) : b;
133 // Template specializations for determining valid ranges for
134 // any given typecast. Specialized for different combinations
135 // of signed/unsigned types.
137 template<typename target_t,
139 bool SignednessDiffers,
142 typedef std::numeric_limits<target_t> tlimits;
143 typedef std::numeric_limits<source_t> slimits;
145 static source_t OverlapMin() {
146 return cast_max(tlimits::min(), slimits::min());
148 static source_t OverlapMax() {
149 return cast_min(tlimits::max(), slimits::max());
153 // signed target / unsigned source
154 template<typename target_t, typename source_t>
155 struct RangeDetail<target_t, source_t, true, true> {
156 typedef std::numeric_limits<target_t> tlimits;
157 typedef std::numeric_limits<source_t> slimits;
159 static source_t OverlapMin() {
160 if (tlimits::min() >= 0) {
161 return cast_max(tlimits::min(), slimits::min());
163 return slimits::min();
166 static source_t OverlapMax() {
167 if (tlimits::max() >= 0) {
168 return cast_min(tlimits::max(), slimits::max());
170 return slimits::min();
175 // unsigned target / signed source
176 template<typename target_t, typename source_t>
177 struct RangeDetail<target_t, source_t, true, false> {
178 typedef std::numeric_limits<target_t> tlimits;
179 typedef std::numeric_limits<source_t> slimits;
181 static source_t OverlapMin() {
182 if (slimits::min() >= 0) {
183 return cast_max(tlimits::min(), slimits::min());
184 } else if (slimits::max() >= 0) {
185 return cast_min(tlimits::min(), slimits::max());
187 return slimits::min();
190 static source_t OverlapMax() {
191 if (slimits::max() >= 0) {
192 return cast_min(tlimits::max(), slimits::max());
194 return slimits::min();
200 // Wrapper for RangeDetail. Prevents RangeDetail objects
201 // from being instantiated for unsupported types.
203 template<typename target_t, typename source_t, bool IsSupported>
205 typedef std::numeric_limits<target_t> tlimits;
206 typedef std::numeric_limits<source_t> slimits;
208 static bool OverlapExists() { return false; }
210 // The return values from OverlapMin() and OverlapMax() are
211 // arbitrary if OverlapExists() returns false, so we'll
212 // just return the minimum source value for both.
213 static source_t OverlapMin() { return slimits::min(); }
214 static source_t OverlapMax() { return slimits::min(); }
217 template<typename target_t, typename source_t>
218 struct RangeHelper<target_t, source_t, true> {
219 typedef std::numeric_limits<target_t> tlimits;
220 typedef std::numeric_limits<source_t> slimits;
222 typedef RangeDetail<target_t,
224 (tlimits::is_signed != slimits::is_signed),
225 tlimits::is_signed> detail_t;
227 static bool OverlapExists() {
228 return detail_t::OverlapMin() < detail_t::OverlapMax();
230 static source_t OverlapMin() { return detail_t::OverlapMin(); }
231 static source_t OverlapMax() { return detail_t::OverlapMax(); }
237 // Maintains information about how to cast between
240 template<typename target_t, typename source_t>
242 typedef std::numeric_limits<target_t> tlimits;
243 typedef std::numeric_limits<source_t> slimits;
245 static const bool kSupported = tlimits::is_specialized
246 && slimits::is_specialized
247 && tlimits::is_integer
248 && slimits::is_integer
249 && tlimits::is_bounded
250 && slimits::is_bounded;
252 static const bool kIdenticalSignedness =
253 (tlimits::is_signed == slimits::is_signed);
255 // "digits" in numeric_limits refers to binary
256 // digits. (Decimal digits are stored in a field
258 static const bool kSufficientBits =
259 (tlimits::digits >= slimits::digits);
261 static const bool kCompileTimeTrivial = kSupported
262 && kIdenticalSignedness
265 typedef TrivialityChecker<tlimits,
267 kCompileTimeTrivial> trivial_t;
268 typedef RangeHelper<target_t, source_t, kSupported> range_t;
270 // Can the cast be done without runtime checks--i.e. are
271 // all possible input values also valid output values?
272 static bool RuntimeTrivial() {
273 return trivial_t::IsRuntimeTrivial();
276 // Are there any valid input values for which a cast would succeed?
277 static bool RuntimePossible() {
278 return range_t::OverlapExists();
281 // Is the given source value a valid target value?
282 static bool RuntimeRangeCheck(const source_t& src) {
283 return (range_t::OverlapExists()
284 && src <= range_t::OverlapMax()
285 && src >= range_t::OverlapMin());
288 // Range of source values which are also valid target values.
289 static source_t RuntimeRangeMin() { return range_t::OverlapMin(); }
290 static source_t RuntimeRangeMax() { return range_t::OverlapMax(); }
292 } // namespace detail
293 } // namespace CheckedCast
297 //-----------------------------------------------------------------------------
301 //-----------------------------------------------------------------------------
303 //-----------------------------------------------------------------------------
304 // checked_cast(const source_t& input)
305 // An augmented replacement for static_cast. Does range checking,
306 // overflow validation. Includes a policy which allows the caller to
307 // specify what action to take if it's determined that the cast
310 // Template parameters:
311 // target_t: the type on the left hand side of the cast
312 // source_t: the type on the right hand side of the cast
313 // trunc_policy: type of a policy object that will be called
314 // if the cast would result in a loss of data. The
315 // type must implement a method named OnTruncate which
316 // is compatible with the following signature:
317 // target_t (target_t& output, const source_t& input).
318 // It is the responsibility of this function to
319 // convert the value of 'input' and store the result
320 // of the conversion in out parameter 'output'. It is
321 // permissible for the function to abort or log warnings
322 // if that is the desired behavior.
324 // Function parameters:
325 // input: the value to convert.
328 // // naive truncation handler for sample purposes ONLY!!
329 // template <typename T, typename S>
330 // void naive_trunc(T& o, const S& i) {
331 // // this only gets called if checked_cast can't do a safe automatic
332 // // conversion. In real code you'd want to do something cool like
333 // // clamp the incoming value or abort the program. For this sample
334 // // we just return a c-style cast, which makes the outcome roughly
335 // // equivalent to static_cast<>.
340 // uint64_t foo = 0xffff0000ffff0000;
341 // uint32_t bar = checked_cast<uint32_t>(foo, naive_trunc);
347 template<typename, typename> class trunc_policy>
348 target_t nacl::checked_cast(const source_t& input) {
352 // Runtime checks--these should compile out for all basic types
354 if (nacl::can_cast<target_t>(input)) {
355 output = static_cast<target_t>(input);
357 output = trunc_policy<target_t, source_t>::OnTruncate(input);
363 //-----------------------------------------------------------------------------
364 // can_cast(const source_t& input)
365 // Returns true if checked_cast will return without invoking trunc_policy.
366 //-----------------------------------------------------------------------------
367 template <typename target_t, typename source_t>
368 bool nacl::can_cast(const source_t& input) {
369 typedef CheckedCast::detail::CastInfo<target_t, source_t> info;
374 // Runtime checks--these should compile out for all basic types
376 result = info::RuntimeTrivial()
377 || (info::RuntimePossible()
378 && info::RuntimeRangeCheck(input));
385 // Convenience wrappers for specializations of checked_cast
388 //-----------------------------------------------------------------------------
389 // checked_cast_fatal(const S& input)
390 // Calls checked_cast; on truncation, log error and abort
391 //-----------------------------------------------------------------------------
392 template<typename T, typename S>
393 T nacl::assert_cast(const S& input) {
394 return checked_cast<T, S, CheckedCast::TruncationPolicyAbort>(input);
396 //-----------------------------------------------------------------------------
397 // saturate_cast(const S& input)
398 // Calls checked_cast; on truncation, saturates the input to the minimum
399 // or maximum of the output.
400 //-----------------------------------------------------------------------------
401 template<typename T, typename S>
402 T nacl::saturate_cast(const S& input) {
404 checked_cast<T, S, CheckedCast::TruncationPolicySaturate>(input);
407 //-----------------------------------------------------------------------------
408 // CheckedCastDetail::OnTruncationSaturate
409 // Implements the Saturate truncation policy.
410 //-----------------------------------------------------------------------------
411 template <typename target_t, typename source_t>
414 ::TruncationPolicySaturate<target_t, source_t>
415 ::OnTruncate(const source_t& input) {
416 typedef detail::CastInfo<target_t, source_t> info;
418 source_t clamped = input;
419 bool valid = info::RuntimePossible();
422 NaClLog(LOG_FATAL, "Checked cast: type ranges do not overlap");
425 clamped = std::max(clamped, info::RuntimeRangeMin());
426 clamped = std::min(clamped, info::RuntimeRangeMax());
428 target_t output = static_cast<target_t>(clamped);
435 //-----------------------------------------------------------------------------
436 // CheckedCastDetail::OnTruncationAbort
437 // Implements the Abort truncation policy.
438 //-----------------------------------------------------------------------------
439 template <typename target_t, typename source_t>
442 ::TruncationPolicyAbort<target_t, source_t>
443 ::OnTruncate(const source_t&) {
444 NaClLog(LOG_FATAL, "Arithmetic overflow");
446 // Unreachable, assuming that LOG_FATAL really is fatal
451 #endif /* NATIVE_CLIENT_SRC_INCLUDE_CHECKED_CAST_H_ */