forked from dcleblanc/SafeInt
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSafeInt.hpp
More file actions
6962 lines (5965 loc) · 235 KB
/
SafeInt.hpp
File metadata and controls
6962 lines (5965 loc) · 235 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/*-----------------------------------------------------------------------------------------------------------
SafeInt.hpp
Version 3.0.28p
This header implements an integer handling class designed to catch
unsafe integer operations
This header compiles properly at Wall on Visual Studio, -Wall on gcc, and -Weverything on clang.
Tested most recently on clang 3.8.0, gcc 7.3.1, and both Visual Studio 2015 and 2017.
Please read helpfile.md before using the class.
---------------------------------------------------------------*/
#ifndef SAFEINT_HPP
#define SAFEINT_HPP
// It is a bit tricky to sort out what compiler we are actually using,
// do this once here, and avoid cluttering the code
#define VISUAL_STUDIO_COMPILER 0
#define CLANG_COMPILER 1
#define GCC_COMPILER 2
#define UNKNOWN_COMPILER -1
// Clang will sometimes pretend to be Visual Studio
// and does pretend to be gcc. Check it first, as nothing else pretends to be clang
#if defined __clang__
#define SAFEINT_COMPILER CLANG_COMPILER
#elif defined __GNUC__
#define SAFEINT_COMPILER GCC_COMPILER
#elif defined _MSC_VER
#define SAFEINT_COMPILER VISUAL_STUDIO_COMPILER
#else
#define SAFEINT_COMPILER UNKNOWN_COMPILER
#endif
#define CPLUSPLUS_98 0
#define CPLUSPLUS_11 1
#define CPLUSPLUS_14 2
#define CPLUSPLUS_17 3
// Determine C++ support level
#if SAFEINT_COMPILER == CLANG_COMPILER || SAFEINT_COMPILER == GCC_COMPILER
#if __cplusplus < 201103L
#define CPLUSPLUS_STD CPLUSPLUS_98
#elif __cplusplus < 201402L
#define CPLUSPLUS_STD CPLUSPLUS_11
#elif __cplusplus < 201703L
#define CPLUSPLUS_STD CPLUSPLUS_14
#else
#define CPLUSPLUS_STD CPLUSPLUS_17
#endif
#elif SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
// This needs additional testing to get more versions of _MSCVER
#if _MSC_VER < 1900 // Prior to VS 2015, need more testing to determine support
#define CPLUSPLUS_STD CPLUSPLUS_98
#elif _MSC_VER < 1910 // VS 2015
#define CPLUSPLUS_STD CPLUSPLUS_11
#else // VS 2017 or later
// Note - there is a __cpp_constexpr test now, but everything prior to VS 2017 reports incorrect values
// and this version always supports at least the CPLUSPLUS_14 approach
#define CPLUSPLUS_STD CPLUSPLUS_14
#endif
#else
// Unknown compiler, assume C++ 98
#define CPLUSPLUS_STD CPLUSPLUS_98
#endif // Determine C++ support level
#if !defined SAFEINT_USE_CPLUSCPLUS_98
#if (SAFEINT_COMPILER == CLANG_COMPILER || SAFEINT_COMPILER == GCC_COMPILER) && CPLUSPLUS_STD < CPLUSPLUS_11
#error Must compile with --std=c++11, preferably --std=c++14 to use constexpr improvements
#endif
#endif
#define CONSTEXPR_NONE 0
#define CONSTEXPR_CPP11 1
#define CONSTEXPR_CPP14 2
// Let's try to use the new standard to determine feature compliance
// If the user has an unknown compiler, or just for testing, allow forcing this setting
#if !defined CONSTEXPR_SUPPORT
#if defined __cpp_constexpr
// If it is gcc or clang, at least recent versions, then we have -std=c++11 or -std=c++14
// This won't be set otherwise, but the headers won't compile, either
#if __cpp_constexpr >= 201304L
#define CONSTEXPR_SUPPORT CONSTEXPR_CPP14 // Clang, gcc, Visual Studio 2017 or later
#elif __cpp_constexpr >= 200704L
#define CONSTEXPR_SUPPORT CONSTEXPR_CPP11 // Clang, gcc with -std=c++11, Visual Studio 2015
#else
#define CONSTEXPR_SUPPORT CONSTEXPR_NONE
#endif
#else // !defined __cpp_constexpr
// Visual Studio is somehow not playing nice. shows __cpp_constexpr visually as defined, but won't compile
#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
#if CPLUSPLUS_STD == CPLUSPLUS_14
#define CONSTEXPR_SUPPORT CONSTEXPR_CPP14
#elif CPLUSPLUS_STD == CPLUSPLUS_11
#define CONSTEXPR_SUPPORT CONSTEXPR_CPP11
#else
#define CONSTEXPR_SUPPORT CONSTEXPR_NONE
#endif
#else
#define CONSTEXPR_SUPPORT CONSTEXPR_NONE
#endif
#endif // defined __cpp_constexpr
#endif // !defined CONSTEXPR_SUPPORT
#if CONSTEXPR_SUPPORT == CONSTEXPR_NONE
#define SAFEINT_CONSTEXPR11
#define SAFEINT_CONSTEXPR14
#elif CONSTEXPR_SUPPORT == CONSTEXPR_CPP11
#define SAFEINT_CONSTEXPR11 constexpr
#define SAFEINT_CONSTEXPR14
#elif CPLUSPLUS_STD >= CPLUSPLUS_14
#define SAFEINT_CONSTEXPR11 constexpr
#define SAFEINT_CONSTEXPR14 constexpr
#else
#error "Unexpected value of CPLUSPLUS_STD"
#endif
// Determine whether exceptions are enabled by the compiler
// Also, allow the user to force this, in case the compiler
// doesn't support the __cpp_exceptions feature
#if !defined SAFE_INT_HAS_EXCEPTIONS
#if __cpp_exceptions >= 199711L
#define SAFE_INT_HAS_EXCEPTIONS 1
#else
#define SAFE_INT_HAS_EXCEPTIONS 0
#endif
#endif
/* Microsoft's compiler continues to misrepresent the language version
// and to make everything more confusing, there's _Check_return_
// in MSVC, and __attribute__((warn_unused_result)) in clang/gcc
We can check for these with:
#if defined(__GNUC__) && (__GNUC__ >= 4)
#define CHECK_RESULT __attribute__ ((warn_unused_result))
#elif defined(_MSC_VER) && (_MSC_VER >= 1700)
#define CHECK_RESULT _Check_return_
#else
#define CHECK_RESULT
#endif
*/
// Begin with the standard way of testing for attributes
// Can use downlevel approaches if there's demand for it
#if defined __has_cpp_attribute && __has_cpp_attribute(nodiscard) >= 201603L
#define SAFE_INT_HAS_NODISCARD 1
#else
#define SAFE_INT_HAS_NODISCARD 0
#endif
#if SAFE_INT_HAS_NODISCARD
#define SAFE_INT_NODISCARD [[nodiscard]]
#else
#define SAFE_INT_NODISCARD
#endif
// Enable compiling with /Wall under VC
#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
// Off by default - unreferenced inline function has been removed
// Note - this intentionally leaks from the header, doesn't quench the warnings otherwise
// Also disable Spectre mitigation warning
#pragma warning( disable: 4514 5045 )
#pragma warning( push )
// Disable warnings coming from headers
#pragma warning( disable:4987 4820 4987 4820 )
#endif
// More defines to accomodate compiler differences
#if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER
#define SAFEINT_NORETURN __attribute__((noreturn))
#define SAFEINT_STDCALL
#define SAFEINT_VISIBLE __attribute__ ((__visibility__("default")))
#define SAFEINT_WEAK __attribute__ ((weak))
#else
#define SAFEINT_NORETURN __declspec(noreturn)
#define SAFEINT_STDCALL __stdcall
#define SAFEINT_VISIBLE
#define SAFEINT_WEAK
#endif
// On the Microsoft compiler, violating a throw() annotation is a silent error.
// Other compilers might turn these into exceptions, and some users may want to not have throw() enabled.
// In addition, some error handlers may not throw C++ exceptions, which makes everything no throw.
// noexcept requires C++11
#if defined SAFEINT_REMOVE_NOTHROW || CPLUSPLUS_STD == CPLUSPLUS_98
#define SAFEINT_NOTHROW
#else
#define SAFEINT_NOTHROW noexcept
#endif
#include <cstdint>
#include <limits>
#include <type_traits> // This is now required
// Need this for ptrdiff_t on some compilers
#include <cstddef>
#include <cmath> // Needed for floating point implementation
// Figure out if we should use intrinsics
// If the user has already decided, let that override
// Note - intrinsics and constexpr are mutually exclusive
// If it is important to get constexpr for multiplication, then define SAFEINT_USE_INTRINSICS 0
// However, intrinsics will result in much smaller code, and should have better perf
// We might have 128-bit int support, check for that, as it should work best
#if !defined SAFEINT_HAS_INT128
#if defined __SIZEOF_INT128__ && __SIZEOF_INT128__ == 16
#define SAFEINT_HAS_INT128 1
#else
#define SAFEINT_HAS_INT128 0
#endif
#endif
#if SAFEINT_HAS_INT128
#define SAFEINT_USE_INTRINSICS 0
#endif
#if !defined SAFEINT_USE_INTRINSICS
// If it is the Visual Studio compiler, then it has to be 64-bit, and not ARM64EC
#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
#if defined _M_AMD64 && !defined _M_ARM64EC
#include <intrin.h>
#define SAFEINT_USE_INTRINSICS 1
#else
#define SAFEINT_USE_INTRINSICS 0
#endif
#else
// Else for gcc and clang, we can use builtin functions
#if SAFEINT_COMPILER == CLANG_COMPILER || SAFEINT_COMPILER == GCC_COMPILER
#define SAFEINT_USE_INTRINSICS 1
#else
#define SAFEINT_USE_INTRINSICS 0
#endif
#endif
#endif
// The gcc and clang builtin functions are constexpr, but not the Microsoft intrinsics
#if SAFEINT_USE_INTRINSICS && SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
#define SAFEINT_CONSTEXPR14_MULTIPLY
#else
#define SAFEINT_CONSTEXPR14_MULTIPLY SAFEINT_CONSTEXPR14
#endif
// If you would like to use your own custom assert
// Define SAFEINT_ASSERT
#if !defined SAFEINT_ASSERT
#include <assert.h>
#define SAFEINT_ASSERT(x) assert(x)
#endif
#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER
#pragma warning( pop )
#endif
// Let's test some assumptions
// We're assuming two's complement negative numbers
static_assert( -1 == static_cast<int>(0xffffffff), "Two's complement signed numbers are required" );
// For current documentation, see helpfile.md
/* Revision history:
*
* Oct 12, 2003 - Created
* Author - David LeBlanc - dleblanc@microsoft.com (no longer valid)
* Current email is dcl@dleblanc.net
*
* Oct 27, 2003 - fixed numerous items pointed out by michmarc and bdawson
* Dec 28, 2003 - 1.0
* added support for mixed-type operations
* thanks to vikramh
* also fixed broken std::int64_t multiplication section
* added extended support for mixed-type operations where possible
* Jan 28, 2004 - 1.0.1
* changed WCHAR to wchar_t
* fixed a construct in two mixed-type assignment overloads that was
* not compiling on some compilers
* Also changed name of private method to comply with standards on
* reserved names
* Thanks to Niels Dekker for the input
* Feb 12, 2004 - 1.0.2
* Minor changes to remove dependency on Windows headers
* Consistently used std::int16_t, std::int32_t and std::int64_t to ensure
* portability
* May 10, 2004 - 1.0.3
* Corrected bug in one case of GreaterThan
* July 22, 2004 - 1.0.4
* Tightened logic in addition check (saving 2 instructions)
* Pulled error handler out into function to enable user-defined replacement
* Made internal type of SafeIntException an enum (as per Niels' suggestion)
* Added casts for base integer types (as per Scott Meyers' suggestion)
* Updated usage information - see important new perf notes.
* Cleaned up several const issues (more thanks to Niels)
*
* Oct 1, 2004 - 1.0.5
* Added support for SEH exceptions instead of C++ exceptions - Win32 only
* Made handlers for DIV0 and overflows individually overridable
* Commented out the destructor - major perf gains here
* Added cast operator for type long, since long != std::int32_t
* Corrected a couple of missing const modifiers
* Fixed broken >= and <= operators for type U op SafeInt< T, E >
* Nov 5, 2004 - 1.0.6
* Implemented new logic in binary operators to resolve issues with
* implicit casts
* Fixed casting operator because char != signed char
* Defined std::int32_t as int instead of long
* Removed unsafe SafeInt::Value method
* Re-implemented casting operator as a result of removing Value method
* Dec 1, 2004 - 1.0.7
* Implemented specialized operators for pointer arithmetic
* Created overloads for cases of U op= SafeInt. What you do with U
* after that may be dangerous.
* Fixed bug in corner case of MixedSizeModulus
* Fixed bug in MixedSizeMultiply and MixedSizeDivision with input of 0
* Added throw() decorations
*
* Apr 12, 2005 - 2.0
* Extensive revisions to leverage template specialization.
* April, 2007 Extensive revisions for version 3.0
* Nov 22, 2009 Forked from MS internal code
* Changes needed to support gcc compiler - many thanks to Niels Dekker
* for determining not just the issues, but also suggesting fixes.
* Also updating some of the header internals to be the same as the upcoming Visual Studio version.
*
* Jan 16, 2010 64-bit gcc has long == std::int64_t, which means that many of the existing 64-bit
* templates are over-specialized. This forces a redefinition of all the 64-bit
* multiplication routines to use pointers instead of references for return
* values. Also, let's use some intrinsics for x64 Microsoft compiler to
* reduce code size, and hopefully improve efficiency.
*
* June 21, 2014 Better support for clang, higher warning levels supported for all 3 primary supported
compilers (Visual Studio, clang, gcc).
Also started to converge the code base such that the public CodePlex version will
be a drop-in replacement for the Visual Studio version.
* Feb 12, 2018 Fixed floating point bug
* Fix to allow initialization by an enum
* Add support for static_assert, make it default to fix compiler warnings from C_ASSERT on gcc, clang
* Changed throw() to noexcept
* March, 2018 Introduced support for constexpr, both the C++11 and C++14 flavors work. The C++14 standard
allows for much more thorough usage, and should be preferred.
* August, 2022 Added support for nodiscard
* Note about code style - throughout this class, casts will be written using C-style (T),
* not C++ style static_cast< T >. This is because the class is nearly always dealing with integer
* types, and in this case static_cast and a C cast are equivalent. Given the large number of casts,
* the code is a little more readable this way. In the event a cast is needed where static_cast couldn't
* be substituted, we'll use the new templatized cast to make it explicit what the operation is doing.
*
************************************************************************************************************
*/
enum SafeIntError
{
SafeIntNoError = 0,
SafeIntArithmeticOverflow,
SafeIntDivideByZero
};
/*
Exception options -
1) You have your own exception handler
2) You want to use the built-in SafeIntException class
2a) TBD, support for std::exception of some sort would be nice to have
3) You don't want C++ exceptions
3a) Use abort()
3b) Use failfast
4) Use Win32 API structured exceptions (legacy, not recommened)
*/
// If the user has already defined an exception handler, we don't need built-in exception approaches
#if defined SafeIntDefaultExceptionHandler
// If the user has defined a custom exception handler, assume it is a C++
// exception handler, unless user tell us it isn't, or we already know
// we can't have exceptions
// Note - SAFEINT_EXCEPTION_HANDLER_CPP == 1
// implies that the handler can throw, and
// adjusts SAFE_INT_CPP_THROW to match
// If that isn't what you want, define it
// and SafeInt will use what you prefer.
#if !defined SAFEINT_EXCEPTION_HANDLER_CPP
#if SAFE_INT_HAS_EXCEPTIONS
#define SAFEINT_EXCEPTION_HANDLER_CPP 1
#else
#define SAFEINT_EXCEPTION_HANDLER_CPP 0
#endif
#endif
#else // Use one of the built-in exception approaches
// If you'd like to prevent platform-specific code,
// define SAFE_INT_USE_STDLIB
// Now we need to define an exception handler
// Internally defined exception handlers might assert
#if defined SAFEINT_ASSERT_ON_EXCEPTION
static inline void SafeIntExceptionAssert() SAFEINT_NOTHROW { SAFEINT_ASSERT(false); }
#else
static inline void SafeIntExceptionAssert() SAFEINT_NOTHROW {}
#endif
#define SAFEINT_EXCEPTION_WIN32 0
#define SAFEINT_EXCEPTION_ABORT 1
#define SAFEINT_EXCEPTION_CPP 2
#if defined SAFEINT_RAISE_EXCEPTION && !defined SAFE_INT_USE_STDLIB // Win32 structured exceptions
#define SAFEINT_EXCEPTION_METHOD SAFEINT_EXCEPTION_WIN32
#elif defined SAFEINT_FAILFAST // Call __failfast or abort, depending on platform
#define SAFEINT_EXCEPTION_METHOD SAFEINT_EXCEPTION_ABORT
#else
#if SAFE_INT_HAS_EXCEPTIONS // Use the built-in C++ exceptions
#define SAFEINT_EXCEPTION_METHOD SAFEINT_EXCEPTION_CPP
#else // Must use same as SAFEINT_FAILFAST
#define SAFEINT_EXCEPTION_METHOD SAFEINT_EXCEPTION_ABORT
#endif
#endif
#if SAFEINT_EXCEPTION_METHOD == SAFEINT_EXCEPTION_CPP
class SAFEINT_VISIBLE SafeIntException
{
public:
SAFEINT_CONSTEXPR11 SafeIntException(SafeIntError code = SafeIntNoError) SAFEINT_NOTHROW : m_code(code)
{
}
SafeIntError m_code;
};
// Note - removed weak annotation on class due to gcc complaints
// This was the only place in the file that used it, need to better understand
// whether it was put there correctly in the first place
namespace safeint_exception_handlers
{
template < typename E > class SafeIntExceptionHandler;
// Some users may have applications that do not use C++ exceptions
// and cannot compile the following class. If that is the case,
// either SafeInt_InvalidParameter must be defined as the default,
// or a custom, user-supplied exception handler must be provided.
template <> class SafeIntExceptionHandler < SafeIntException >
{
public:
static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow()
{
SafeIntExceptionAssert();
throw SafeIntException(SafeIntArithmeticOverflow);
}
static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero()
{
SafeIntExceptionAssert();
throw SafeIntException(SafeIntDivideByZero);
}
};
typedef SafeIntExceptionHandler < SafeIntException > CPlusPlusExceptionHandler;
}
#define SafeIntDefaultExceptionHandler safeint_exception_handlers::CPlusPlusExceptionHandler
#define SAFEINT_EXCEPTION_HANDLER_CPP 1
#endif // SAFEINT_EXCEPTION_CPP
#if SAFEINT_EXCEPTION_METHOD == SAFEINT_EXCEPTION_WIN32
// Must have Windows to use this
#if !defined _WINDOWS_
#error Include windows.h in order to use Win32 exceptions
#endif
namespace safeint_exception_handlers
{
class SafeIntWin32ExceptionHandler
{
public:
static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() SAFEINT_NOTHROW
{
SafeIntExceptionAssert();
RaiseException(static_cast<DWORD>(EXCEPTION_INT_OVERFLOW), EXCEPTION_NONCONTINUABLE, 0, 0);
}
static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() SAFEINT_NOTHROW
{
SafeIntExceptionAssert();
RaiseException(static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), EXCEPTION_NONCONTINUABLE, 0, 0);
}
};
}
typedef safeint_exception_handlers::SafeIntWin32ExceptionHandler Win32ExceptionHandler;
#define SafeIntDefaultExceptionHandler Win32ExceptionHandler
#define SAFEINT_EXCEPTION_HANDLER_CPP 0
#endif // SAFEINT_EXCEPTION_WIN32
#if SAFEINT_EXCEPTION_METHOD == SAFEINT_EXCEPTION_ABORT
// This has two possible implementations - one is failfast, the other is abort
#if defined _CRT_SECURE_INVALID_PARAMETER && !defined SAFE_INT_USE_STDLIB
#define SAFE_INT_ABORT(msg) _CRT_SECURE_INVALID_PARAMETER(msg)
#else
// Calling fail fast is somewhat more robust than calling abort,
// but abort is the closest we can manage without Visual Studio support
// Need the header for abort()
#include <stdlib.h>
#define SAFE_INT_ABORT(msg) abort()
#endif
namespace safeint_exception_handlers
{
class SafeInt_InvalidParameter
{
public:
static SAFEINT_NORETURN void SafeIntOnOverflow() SAFEINT_NOTHROW
{
SafeIntExceptionAssert();
SAFE_INT_ABORT("SafeInt Arithmetic Overflow");
}
static SAFEINT_NORETURN void SafeIntOnDivZero() SAFEINT_NOTHROW
{
SafeIntExceptionAssert();
SAFE_INT_ABORT("SafeInt Divide By Zero");
}
};
}
typedef safeint_exception_handlers::SafeInt_InvalidParameter InvalidParameterExceptionHandler;
#define SafeIntDefaultExceptionHandler InvalidParameterExceptionHandler
#define SAFEINT_EXCEPTION_HANDLER_CPP 0
#endif
#endif // defined SafeIntDefaultExceptionHandler
// If an error handler is chosen other than C++ exceptions, such as Win32 exceptions, fail fast,
// or abort, then all methods become no throw. Some teams track throw() annotations closely,
// and the following option provides for this.
// If someone has defined their own exception handler,
// it is at least possible they might have also defined
// the throw annotation.
#if !defined SAFEINT_CPP_THROW
#if SAFEINT_EXCEPTION_HANDLER_CPP
#define SAFEINT_CPP_THROW
#else
#define SAFEINT_CPP_THROW SAFEINT_NOTHROW
#endif
#endif // SAFEINT_CPP_THROW
namespace safeint_internal
{
// If we have support for std<typetraits>, then we can do this easily, and detect enums as well
template < typename T > class numeric_type;
// Continue to special case bool
template <> class numeric_type<bool> { public: enum { isBool = true, isInt = false }; };
template < typename T > class numeric_type
{
public:
enum
{
isBool = false, // We specialized out a bool
// If it is an enum, then consider it an int type
// This does allow someone to make a SafeInt from an enum type, which is not recommended,
// but it also allows someone to add an enum value to a SafeInt, which is handy.
isInt = std::is_integral<T>::value || std::is_enum<T>::value,
isEnum = std::is_enum<T>::value
};
};
template < typename T > class int_traits
{
public:
static_assert(safeint_internal::numeric_type< T >::isInt, "Integer type required");
enum
{
is64Bit = (sizeof(T) == 8),
is32Bit = (sizeof(T) == 4),
is16Bit = (sizeof(T) == 2),
is8Bit = (sizeof(T) == 1),
isLT32Bit = (sizeof(T) < 4),
isLT64Bit = (sizeof(T) < 8),
isInt8 = (sizeof(T) == 1 && std::numeric_limits<T>::is_signed),
isUint8 = (sizeof(T) == 1 && !std::numeric_limits<T>::is_signed),
isInt16 = (sizeof(T) == 2 && std::numeric_limits<T>::is_signed),
isUint16 = (sizeof(T) == 2 && !std::numeric_limits<T>::is_signed),
isInt32 = (sizeof(T) == 4 && std::numeric_limits<T>::is_signed),
isUint32 = (sizeof(T) == 4 && !std::numeric_limits<T>::is_signed),
isInt64 = (sizeof(T) == 8 && std::numeric_limits<T>::is_signed),
isUint64 = (sizeof(T) == 8 && !std::numeric_limits<T>::is_signed),
bitCount = (sizeof(T) * 8),
isBool = ((T)2 == (T)1)
};
};
template < typename T, typename U > class type_compare
{
public:
enum
{
isBothSigned = (std::numeric_limits< T >::is_signed && std::numeric_limits< U >::is_signed),
isBothUnsigned = (!std::numeric_limits< T >::is_signed && !std::numeric_limits< U >::is_signed),
isLikeSigned = ((bool)(std::numeric_limits< T >::is_signed) == (bool)(std::numeric_limits< U >::is_signed)),
isCastOK = ((isLikeSigned && sizeof(T) >= sizeof(U)) ||
(std::numeric_limits< T >::is_signed && sizeof(T) > sizeof(U))),
isBothLT32Bit = (safeint_internal::int_traits< T >::isLT32Bit && safeint_internal::int_traits< U >::isLT32Bit),
isBothLT64Bit = (safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::isLT64Bit)
};
};
}
//all of the arithmetic operators can be solved by the same code within
//each of these regions without resorting to compile-time constant conditionals
//most operators collapse the problem into less than the 22 zones, but this is used
//as the first cut
//using this also helps ensure that we handle all of the possible cases correctly
template < typename T, typename U > class IntRegion
{
public:
enum
{
//unsigned-unsigned zone
IntZone_UintLT32_UintLT32 = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::type_compare< T,U >::isBothLT32Bit,
IntZone_Uint32_UintLT64 = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::int_traits< T >::is32Bit && safeint_internal::int_traits< U >::isLT64Bit,
IntZone_UintLT32_Uint32 = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::int_traits< T >::isLT32Bit && safeint_internal::int_traits< U >::is32Bit,
IntZone_Uint64_Uint = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::int_traits< T >::is64Bit,
IntZone_UintLT64_Uint64 = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::is64Bit,
//unsigned-signed
IntZone_UintLT32_IntLT32 = !std::numeric_limits< T >::is_signed && std::numeric_limits< U >::is_signed && safeint_internal::type_compare< T,U >::isBothLT32Bit,
IntZone_Uint32_IntLT64 = safeint_internal::int_traits< T >::isUint32 && std::numeric_limits< U >::is_signed && safeint_internal::int_traits< U >::isLT64Bit,
IntZone_UintLT32_Int32 = !std::numeric_limits< T >::is_signed && safeint_internal::int_traits< T >::isLT32Bit && safeint_internal::int_traits< U >::isInt32,
IntZone_Uint64_Int = safeint_internal::int_traits< T >::isUint64 && std::numeric_limits< U >::is_signed && safeint_internal::int_traits< U >::isLT64Bit,
IntZone_UintLT64_Int64 = !std::numeric_limits< T >::is_signed && safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::isInt64,
IntZone_Uint64_Int64 = safeint_internal::int_traits< T >::isUint64 && safeint_internal::int_traits< U >::isInt64,
//signed-signed
IntZone_IntLT32_IntLT32 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::type_compare< T, U >::isBothLT32Bit,
IntZone_Int32_IntLT64 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::is32Bit && safeint_internal::int_traits< U >::isLT64Bit,
IntZone_IntLT32_Int32 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::isLT32Bit && safeint_internal::int_traits< U >::is32Bit,
IntZone_Int64_Int64 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isInt64,
IntZone_Int64_Int = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::is64Bit && safeint_internal::int_traits< U >::isLT64Bit,
IntZone_IntLT64_Int64 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::is64Bit,
//signed-unsigned
IntZone_IntLT32_UintLT32 = std::numeric_limits< T >::is_signed && !std::numeric_limits< U >::is_signed && safeint_internal::type_compare< T,U >::isBothLT32Bit,
IntZone_Int32_UintLT32 = safeint_internal::int_traits< T >::isInt32 && !std::numeric_limits< U >::is_signed && safeint_internal::int_traits< U >::isLT32Bit,
IntZone_IntLT64_Uint32 = std::numeric_limits< T >::is_signed && safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::isUint32,
IntZone_Int64_UintLT64 = safeint_internal::int_traits< T >::isInt64 && !std::numeric_limits< U >::is_signed && safeint_internal::int_traits< U >::isLT64Bit,
IntZone_Int_Uint64 = std::numeric_limits< T >::is_signed && safeint_internal::int_traits< U >::isUint64 && safeint_internal::int_traits< T >::isLT64Bit,
IntZone_Int64_Uint64 = safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isUint64
};
};
// In all of the following functions, we have two versions
// One for SafeInt, which throws C++ (or possibly SEH) exceptions
// The non-throwing versions are for use by the helper functions that return success and failure.
// Some of the non-throwing functions are not used, but are maintained for completeness.
// There's no real alternative to duplicating logic, but keeping the two versions
// immediately next to one another will help reduce problems
// useful function to help with getting the magnitude of a negative number
enum AbsMethod
{
AbsMethodInt,
AbsMethodInt64,
AbsMethodNoop
};
template < typename T >
class GetAbsMethod
{
public:
enum
{
method = safeint_internal::int_traits< T >::isLT64Bit && std::numeric_limits< T >::is_signed ? AbsMethodInt :
safeint_internal::int_traits< T >::isInt64 ? AbsMethodInt64 : AbsMethodNoop
};
};
// let's go ahead and hard-code a dependency on the
// representation of negative numbers to keep compilers from getting overly
// happy with optimizing away things like -MIN_INT.
template < typename T, int > class AbsValueHelper;
template < typename T > class AbsValueHelper < T, AbsMethodInt>
{
public:
SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 static std::uint32_t Abs( T t ) SAFEINT_NOTHROW
{
SAFEINT_ASSERT( t < 0 );
return ~(std::uint32_t)t + 1;
}
};
template < typename T > class AbsValueHelper < T, AbsMethodInt64 >
{
public:
SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 static std::uint64_t Abs( T t ) SAFEINT_NOTHROW
{
SAFEINT_ASSERT( t < 0 );
return ~(std::uint64_t)t + 1;
}
};
template < typename T > class AbsValueHelper < T, AbsMethodNoop >
{
public:
SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 static T Abs( T t ) SAFEINT_NOTHROW
{
// Why are you calling Abs on an unsigned number ???
SAFEINT_ASSERT( false );
return t;
}
};
// Helper classes to work keep compilers from
// optimizing away negation
template < typename T > class SignedNegation;
template <>
class SignedNegation <std::int32_t>
{
public:
SAFEINT_CONSTEXPR11 static std::int32_t Value(std::uint64_t in) SAFEINT_NOTHROW
{
return (std::int32_t)(~(std::uint32_t)in + 1);
}
SAFEINT_CONSTEXPR11 static std::int32_t Value(std::uint32_t in) SAFEINT_NOTHROW
{
return (std::int32_t)(~in + 1);
}
};
template <>
class SignedNegation <std::int64_t>
{
public:
SAFEINT_CONSTEXPR11 static std::int64_t Value(std::uint64_t in) SAFEINT_NOTHROW
{
return (std::int64_t)(~in + 1);
}
};
template < typename T, bool > class NegationHelper;
// Previous versions had an assert that the type being negated was 32-bit or higher
// In retrospect, this seems like something to just document
// Negation will normally upcast to int
// For example -(unsigned short)0xffff == (int)0xffff0001
// This class will retain the type, and will truncate, which may not be what
// you wanted
// If you want normal operator casting behavior, do this:
// SafeInt<unsigned short> ss = 0xffff;
// then:
// -(SafeInt<int>(ss))
// will then emit a signed int with the correct value and bitfield
// Note, unlike all of the other helper classes, the non-throwing negation
// doesn't make sense, isn't exposed or tested, so omit it
template < typename T > class NegationHelper <T, true> // Signed
{
public:
template <typename E>
SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 static T NegativeThrow( T t ) SAFEINT_CPP_THROW
{
// corner case
if( t != std::numeric_limits<T>::min() )
{
// cast prevents unneeded checks in the case of small ints
return -t;
}
E::SafeIntOnOverflow();
}
SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 static bool Negative(T t, T& out)
{
// corner case
if (t != std::numeric_limits<T>::min())
{
out = -t;
return true;
}
return false;
}
};
template < typename T > class NegationHelper <T, false> // unsigned
{
public:
template <typename E>
SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 static T NegativeThrow( T t ) SAFEINT_CPP_THROW
{
#if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION
static_assert( sizeof(T) == 0, "Unsigned negation is unsupported" );
#endif
// This may not be the most efficient approach, but you shouldn't be doing this
return (T)SignedNegation<std::int64_t>::Value(t);
}
SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 static bool Negative(T , T& /*out*/)
{
// This will only be used by the SafeNegation function
return false;
}
};
//core logic to determine casting behavior
enum CastMethod
{
CastOK = 0,
CastCheckLTZero,
CastCheckGTMax,
CastCheckSafeIntMinMaxUnsigned,
CastCheckSafeIntMinMaxSigned,
CastToFloat,
CastFromFloat,
CastToBool,
CastFromBool,
CastFromEnum
};
template < typename ToType, typename FromType >
class GetCastMethod
{
public:
enum
{
method = ( safeint_internal::numeric_type<FromType>::isEnum ) ? CastFromEnum :
( safeint_internal::int_traits< FromType >::isBool &&
!safeint_internal::int_traits< ToType >::isBool ) ? CastFromBool :
( !safeint_internal::int_traits< FromType >::isBool &&
safeint_internal::int_traits< ToType >::isBool ) ? CastToBool :
( safeint_internal::type_compare< ToType, FromType >::isCastOK ) ? CastOK :
( ( std::numeric_limits< ToType >::is_signed &&
!std::numeric_limits< FromType >::is_signed &&
sizeof( FromType ) >= sizeof( ToType ) ) ||
( safeint_internal::type_compare< ToType, FromType >::isBothUnsigned &&
sizeof( FromType ) > sizeof( ToType ) ) ) ? CastCheckGTMax :
( !std::numeric_limits< ToType >::is_signed &&
std::numeric_limits< FromType >::is_signed &&
sizeof( ToType ) >= sizeof( FromType ) ) ? CastCheckLTZero :
( !std::numeric_limits< ToType >::is_signed ) ? CastCheckSafeIntMinMaxUnsigned
: CastCheckSafeIntMinMaxSigned
};
};
template < typename FromType > class GetCastMethod < float, FromType >
{
public:
enum{ method = CastOK };
};
template < typename FromType > class GetCastMethod < double, FromType >
{
public:
enum{ method = CastOK };
};
template < typename FromType > class GetCastMethod < long double, FromType >
{
public:
enum{ method = CastOK };
};
template < typename ToType > class GetCastMethod < ToType, float >
{
public:
enum{ method = CastFromFloat };
};
template < typename ToType > class GetCastMethod < ToType, double >
{
public:
enum{ method = CastFromFloat };
};
template < typename ToType > class GetCastMethod < ToType, long double >
{
public:
enum{ method = CastFromFloat };
};
template < typename T, typename U, int > class SafeCastHelper;
template < typename T, typename U > class SafeCastHelper < T, U, CastOK >
{
public:
SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 static bool Cast( U u, T& t ) SAFEINT_NOTHROW
{
t = (T)u;
return true;
}
template < typename E >
SAFEINT_CONSTEXPR14 static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW
{
t = (T)u;
}
};
template <typename T, bool> class float_cast_helper;
template <typename T> class float_cast_helper <T, true> // Unsigned case
{
public:
SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 static bool Test(double d)
{
const std::uint64_t signifDouble = 0x1fffffffffffff;
// Anything larger than this either is larger than 2^64-1, or cannot be represented by a double
const std::uint64_t maxUnsignedDouble = signifDouble << 11;
// There is the possibility of both negative and positive zero,
// but we'll allow either, since (-0.0 < 0) == false
// if we wanted to change that, then use the signbit() macro
if (d < 0 || d > static_cast<double>(maxUnsignedDouble))
return false;
// The input can now safely be cast to an unsigned long long
if (static_cast<std::uint64_t>(d) > std::numeric_limits<T>::max())
return false;
return true;
}
};
template <typename T> class float_cast_helper <T, false> // Signed case
{
public:
SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 static bool Test(double d)
{
const std::uint64_t signifDouble = 0x1fffffffffffff;
// This has to fit in 2^63-1
const std::uint64_t maxSignedDouble = signifDouble << 10;
// The smallest signed long long is easier
const std::int64_t minSignedDouble = static_cast<std::int64_t>(0x8000000000000000);
if (d < static_cast<double>(minSignedDouble) || d > static_cast<double>(maxSignedDouble))
return false;
// And now cast to long long, and check against min and max for this type
std::int64_t test = static_cast<std::int64_t>(d);
if ((std::int64_t)test < (std::int64_t)std::numeric_limits<T>::min() || (std::int64_t)test >(std::int64_t)std::numeric_limits<T>::max())
return false;
return true;
}
};
// special case floats and doubles
template < typename T, typename U > class SafeCastHelper < T, U, CastFromFloat >
{
public:
SAFE_INT_NODISCARD static bool CheckFloatingPointCast(double d)
{
// A double can hold at most 53 bits of the value
// 53 bits is:
bool fValid = false;
switch (std::fpclassify(d))
{
case FP_NORMAL: // A positive or negative normalized non - zero value
case FP_SUBNORMAL: // A positive or negative denormalized value
case FP_ZERO: // A positive or negative zero value