diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..3f37f2e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 John C. Griffin, + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 418e427..2a9ed03 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,35 @@ [![Build Status](https://travis-ci.org/JohnCGriffin/overflow.png)](https://travis-ci.org/JohnCGriffin/overflow) # overflow -Check for int/int8/int16/int64/int32 integer overflow in Golang arithmetic. +Check for integer overflow in Golang arithmetic and type conversion. + ### Install -``` + +```sh go get github.com/johncgriffin/overflow ``` -Note that because Go has no template types, the majority of repetitive code is -generated by overflow_template.sh. If you have to change an -algorithm, change it there and regenerate the Go code via: -``` + +In order to be compatible with the old code and keep the code simple and readable, the new code still does not use generics, but uses templates to generate code. So the majority of repetitive code is generated by `overflow_template.sh`. + +If you have to change an algorithm, change it there and regenerate the Go code via: +```sh go generate ``` ### Synopsis -``` -package main - -import "fmt" -import "math" +#### Arithmetic overflow detection +```go import "github.com/JohnCGriffin/overflow" func main() { - - addend := math.MaxInt64 - 5 - - for i := 0; i < 10; i++ { - sum, ok := overflow.Add(addend, i) - fmt.Printf("%v+%v -> (%v,%v)\n", - addend, i, sum, ok) - } - + addend := math.MaxInt64 - 5 + for i := 0; i < 10; i++ { + sum, ok := overflow.Add(addend, i) + fmt.Printf("%v+%v -> (%v,%v)\n", addend, i, sum, ok) + } } ``` yields the output -``` +```go 9223372036854775802+0 -> (9223372036854775802,true) 9223372036854775802+1 -> (9223372036854775803,true) 9223372036854775802+2 -> (9223372036854775804,true) @@ -45,40 +41,83 @@ yields the output 9223372036854775802+8 -> (0,false) 9223372036854775802+9 -> (0,false) ``` +For (u)int types, provide (U)Add, (U)Sub, (U)Mul, (U)Div, (U)Quotient, etc. -For int, int64, and int32 types, provide Add, Add32, Add64, Sub, Sub32, Sub64, etc. -Unsigned types not covered at the moment, but such additions are welcome. +#### Type conversion overflow detection +```go +func main() { + var i uint + for i = math.MaxInt - 5; i <= math.MaxInt+5; i++ { + ret, ok := overflow.UintToInt(i) + fmt.Printf("%v -> (%v,%v)\n", i, ret, ok) + } +} +``` +yields the output +```go +9223372036854775802 -> (9223372036854775802,true) +9223372036854775803 -> (9223372036854775803,true) +9223372036854775804 -> (9223372036854775804,true) +9223372036854775805 -> (9223372036854775805,true) +9223372036854775806 -> (9223372036854775806,true) +9223372036854775807 -> (9223372036854775807,true) +9223372036854775808 -> (-9223372036854775808,false) +9223372036854775809 -> (-9223372036854775807,false) +9223372036854775810 -> (-9223372036854775806,false) +9223372036854775811 -> (-9223372036854775805,false) +9223372036854775812 -> (-9223372036854775804,false) +``` +Provide UintToInt, IntToUint, Uint64ToInt32, Int32ToUint64, etc. -### Stay calm and panic +#### Get absolute value +```go +func main() { + normalAbs := func(x int64) int64 { + if x < 0 { + x = -x + } + return x + } + var i1, j1, k1 int64 = -9007199254740993, -9007199254740993, -9007199254740993 + fmt.Println(int64(math.Abs(float64(i1)))) + fmt.Println(normalAbs(j1)) + fmt.Println(overflow.Abs64(k1)) + + var i2, j2, k2 int64 = math.MinInt64, math.MinInt64, math.MinInt64 + fmt.Println(int64(math.Abs(float64(i2)))) + fmt.Println(normalAbs(j2)) + fmt.Println(overflow.Abs64(k2)) +} +``` +yields the output +```go +9007199254740992 // Mantissa overflow, precision lost +9007199254740993 +9007199254740993 true +-9223372036854775808 +-9223372036854775808 +-9223372036854775808 false // Overflow detected +``` + +For int, provides an absolute value including overflow detection. -There's a good case to be made that a panic is an unidiomatic but proper response. Iff you -believe that there's no valid way to continue your program after math goes wayward, you can -use the easier Addp, Mulp, Subp, and Divp versions which return the normal result or panic. +### Stay calm and panic +There's a good case to be made that a panic is an unidiomatic but proper response. If you believe that there's no valid way to continue your program after math goes wayward, you can use the easier Addp, Mulp, Subp, Divp, IntToUintp, Absp etc, which return the normal result or panic. -- - - -MIT License +### Performance considerations -Copyright (c) 2017 John C. Griffin, +Compared to integer safe libraries in other languages (e.g. C++), this library uses some seemingly slow operations, such as division. But that doesn't mean these methods will be slow. In fact, because of Go's automatic inlining strategy for short functions and compiler optimizations, these operations are actually very fast in benchmark tests, lightning fast. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +In addition, Go defines the overflow behavior of signed integers, making the detection of signed integer overflow simple and efficient. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +Note that using `//go:noinline` in your business function will not affect the inlining of the library function. Only disabling global inlining through `-gcflags="-l"` will affect the inlining of this library function. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +### Basis and dependencies +The library is developed based on Go's official compiler implementation (gc) and language specifications. +### License +[MIT LICENSE](./LICENSE.md) diff --git a/overflow.go b/overflow.go index 17e2417..e5d581e 100644 --- a/overflow.go +++ b/overflow.go @@ -1,4 +1,5 @@ -/*Package overflow offers overflow-checked integer arithmetic operations +/* +Package overflow offers overflow-checked integer arithmetic operations for int, int32, and int64. Each of the operations returns a result,bool combination. This was prompted by the need to know when to flow into higher precision types from the math.big library. @@ -16,7 +17,8 @@ overflow.Add(math.MaxInt64,1) -> (0, false) Add, Sub, Mul, Div are for int. Add64, Add32, etc. are specifically sized. If anybody wishes an unsigned version, submit a pull request for code -and new tests. */ +and new tests. +*/ package overflow //go:generate ./overflow_template.sh @@ -40,7 +42,17 @@ So, FEEL FREE to carefully review the code visually. // Unspecified size, i.e. normal signed int -// Add sums two ints, returning the result and a boolean status. +// Abs get absolute value of an int, returning the result and a ok result indicating whether the operation is safe. +func Abs(x int) (int, bool) { + if _is64Bit() { + r64, ok := Abs64(int64(x)) + return int(r64), ok + } + r32, ok := Abs32(int32(x)) + return int(r32), ok +} + +// Add sums two ints, returning the result and a ok result indicating whether the operation is safe. func Add(a, b int) (int, bool) { if _is64Bit() { r64, ok := Add64(int64(a), int64(b)) @@ -50,7 +62,17 @@ func Add(a, b int) (int, bool) { return int(r32), ok } -// Sub returns the difference of two ints and a boolean status. +// UAdd sums two uints, returning the result and a ok result indicating whether the operation is safe. +func UAdd(a, b uint) (uint, bool) { + if _is64Bit() { + r64, ok := UAdd64(uint64(a), uint64(b)) + return uint(r64), ok + } + r32, ok := UAdd32(uint32(a), uint32(b)) + return uint(r32), ok +} + +// Sub returns the difference of two ints and a ok result indicating whether the operation is safe. func Sub(a, b int) (int, bool) { if _is64Bit() { r64, ok := Sub64(int64(a), int64(b)) @@ -60,7 +82,17 @@ func Sub(a, b int) (int, bool) { return int(r32), ok } -// Mul returns the product of two ints and a boolean status. +// USub returns the difference of two uints and a ok result indicating whether the operation is safe. +func USub(a, b uint) (uint, bool) { + if _is64Bit() { + r64, ok := USub64(uint64(a), uint64(b)) + return uint(r64), ok + } + r32, ok := USub32(uint32(a), uint32(b)) + return uint(r32), ok +} + +// Mul returns the product of two ints and a ok result indicating whether the operation is safe. func Mul(a, b int) (int, bool) { if _is64Bit() { r64, ok := Mul64(int64(a), int64(b)) @@ -70,7 +102,17 @@ func Mul(a, b int) (int, bool) { return int(r32), ok } -// Div returns the quotient of two ints and a boolean status +// UMul returns the product of two uints and a ok result indicating whether the operation is safe. +func UMul(a, b uint) (uint, bool) { + if _is64Bit() { + r64, ok := UMul64(uint64(a), uint64(b)) + return uint(r64), ok + } + r32, ok := UMul32(uint32(a), uint32(b)) + return uint(r32), ok +} + +// Div returns the quotient of two ints and a ok result indicating whether the operation is safe. func Div(a, b int) (int, bool) { if _is64Bit() { r64, ok := Div64(int64(a), int64(b)) @@ -80,7 +122,17 @@ func Div(a, b int) (int, bool) { return int(r32), ok } -// Quotient returns the quotient, remainder and status of two ints +// UDiv returns the quotient of two uints and a ok result indicating whether the operation is safe. +func UDiv(a, b uint) (uint, bool) { + if _is64Bit() { + r64, ok := UDiv64(uint64(a), uint64(b)) + return uint(r64), ok + } + r32, ok := UDiv32(uint32(a), uint32(b)) + return uint(r32), ok +} + +// Quotient returns the quotient, remainder and ok result indicating whether the operation is safe. func Quotient(a, b int) (int, int, bool) { if _is64Bit() { q64, r64, ok := Quotient64(int64(a), int64(b)) @@ -90,8 +142,49 @@ func Quotient(a, b int) (int, int, bool) { return int(q32), int(r32), ok } +// UQuotient returns the quotient, remainder and ok result indicating whether the operation is safe. +func UQuotient(a, b uint) (uint, uint, bool) { + if _is64Bit() { + uq64, ur64, ok := UQuotient64(uint64(a), uint64(b)) + return uint(uq64), uint(ur64), ok + } + uq32, ur32, ok := UQuotient32(uint32(a), uint32(b)) + return uint(uq32), uint(ur32), ok +} + +// IntToUint converts an int value to uint. +// returning a converted value and a bool value indicating whether the operation is safe. +func IntToUint(x int) (uint, bool) { + if _is64Bit() { + y64, ok := Int64ToUint64(int64(x)) + return uint(y64), ok + } + y32, ok := Int32ToUint32(int32(x)) + return uint(y32), ok +} + +// UintToInt converts an uint value to int. +// returning a converted value and a bool value indicating whether the operation is safe. +func UintToInt(x uint) (int, bool) { + if _is64Bit() { + y64, ok := Uint64ToInt64(uint64(x)) + return int(y64), ok + } + y32, ok := Uint32ToInt32(uint32(x)) + return int(y32), ok +} + /************* Panic versions for int ****************/ +// Absp returns the absolute value, panicking on overflow +func Absp(x int) int { + r, ok := Abs(x) + if !ok { + panic("absolute value overflow") + } + return r +} + // Addp returns the sum of two ints, panicking on overflow func Addp(a, b int) int { r, ok := Add(a, b) @@ -101,6 +194,15 @@ func Addp(a, b int) int { return r } +// UAddp returns the sum of two uints, panicking on overflow +func UAddp(a, b uint) uint { + r, ok := UAdd(a, b) + if !ok { + panic("addition overflow") + } + return r +} + // Subp returns the difference of two ints, panicking on overflow. func Subp(a, b int) int { r, ok := Sub(a, b) @@ -110,6 +212,15 @@ func Subp(a, b int) int { return r } +// USubp returns the difference of two uints, panicking on overflow. +func USubp(a, b uint) uint { + r, ok := USub(a, b) + if !ok { + panic("subtraction overflow") + } + return r +} + // Mulp returns the product of two ints, panicking on overflow. func Mulp(a, b int) int { r, ok := Mul(a, b) @@ -119,6 +230,15 @@ func Mulp(a, b int) int { return r } +// UMulp returns the product of two uints, panicking on overflow. +func UMulp(a, b uint) uint { + r, ok := UMul(a, b) + if !ok { + panic("multiplication overflow") + } + return r +} + // Divp returns the quotient of two ints, panicking on overflow. func Divp(a, b int) int { r, ok := Div(a, b) @@ -127,3 +247,30 @@ func Divp(a, b int) int { } return r } + +// UDivp returns the quotient of two uints, panicking on overflow. +func UDivp(a, b uint) uint { + r, ok := UDiv(a, b) + if !ok { + panic("division failure") + } + return r +} + +// IntToUintp converts an uint value to int, panicking on overflow. +func IntToUintp(x int) uint { + r, ok := IntToUint(x) + if !ok { + panic("conversion failure") + } + return r +} + +// UintToIntp converts an uint value to int, panicking on overflow. +func UintToIntp(x uint) int { + r, ok := UintToInt(x) + if !ok { + panic("conversion failure") + } + return r +} diff --git a/overflow_impl.go b/overflow_impl.go index d1cbe86..fb7eaae 100644 --- a/overflow_impl.go +++ b/overflow_impl.go @@ -1,13 +1,27 @@ package overflow +import "math" + // This is generated code, created by overflow_template.sh executed // by "go generate" +// Abs8 performs absolute value operation on an int8 operand +// returning a result and a ok result indicating whether the operation is safe. +func Abs8(x int8) (int8, bool) { + if x == math.MinInt8 { + return x, false + } + if x >= 0 { + return x, true + } + return -x, true +} + // Add8 performs + operation on two int8 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Add8(a, b int8) (int8, bool) { c := a + b if (c > a) == (b > 0) { @@ -25,9 +39,28 @@ func Add8p(a, b int8) int8 { return r } +// UAdd8 performs + operation on two uint8 operands +// returning a result and a ok result indicating whether the operation is safe. +func UAdd8(a, b uint8) (uint8, bool) { + c := a + b + if c >= a { + return c, true + } + return c, false +} + +// UAdd8p is the unchecked panicing version of UAdd8 +func UAdd8p(a, b uint8) uint8 { + r, ok := UAdd8(a, b) + if !ok { + panic("addition overflow") + } + return r +} + // Sub8 performs - operation on two int8 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Sub8(a, b int8) (int8, bool) { c := a - b if (c < a) == (b > 0) { @@ -45,9 +78,28 @@ func Sub8p(a, b int8) int8 { return r } +// USub8 performs - operation on two uint8 operands +// returning a result and a ok result indicating whether the operation is safe. +func USub8(a, b uint8) (uint8, bool) { + c := a - b + if a >= b { + return c, true + } + return c, false +} + +// USub8p is the unchecked panicing version of USub8 +func USub8p(a, b uint8) uint8 { + r, ok := USub8(a, b) + if !ok { + panic("subtraction overflow") + } + return r +} + // Mul8 performs * operation on two int8 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Mul8(a, b int8) (int8, bool) { if a == 0 || b == 0 { return 0, true @@ -70,10 +122,31 @@ func Mul8p(a, b int8) int8 { return r } +// UMul8 performs * operation on two uint8 operands +// returning a result and a ok result indicating whether the operation is safe. +func UMul8(a, b uint8) (uint8, bool) { + if a == 0 || b == 0 { + return 0, true + } + c := a * b + if c/b == a { + return c, true + } + return c, false +} + +// UMul8p is the unchecked panicing version of UMul8 +func UMul8p(a, b uint8) uint8 { + r, ok := UMul8(a, b) + if !ok { + panic("multiplication overflow") + } + return r +} // Div8 performs / operation on two int8 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Div8(a, b int8) (int8, bool) { q, _, ok := Quotient8(a, b) return q, ok @@ -89,7 +162,7 @@ func Div8p(a, b int8) int8 { } // Quotient8 performs + operation on two int8 operands -// returning a quotient, a remainder and status +// returning a quotient, a remainder and a ok result indicating whether the operation is safe. func Quotient8(a, b int8) (int8, int8, bool) { if b == 0 { return 0, 0, false @@ -99,10 +172,48 @@ func Quotient8(a, b int8) (int8, int8, bool) { return c, a % b, status } +// UDiv8 performs / operation on two uint8 operands +// returning a result and a ok result indicating whether the operation is safe. +func UDiv8(a, b uint8) (uint8, bool) { + q, _, ok := UQuotient8(a, b) + return q, ok +} + +// UDiv8p is the unchecked panicing version of UDiv8 +func UDiv8p(a, b uint8) uint8 { + r, ok := UDiv8(a, b) + if !ok { + panic("division failure") + } + return r +} + +// UQuotient8 performs + operation on two uint8 operands +// returning a quotient, a remainder and a ok result indicating whether the operation is safe. +func UQuotient8(a, b uint8) (uint8, uint8, bool) { + if b == 0 { + return 0, 0, false + } + c := a / b + return c, a % b, true +} + +// Abs16 performs absolute value operation on an int16 operand +// returning a result and a ok result indicating whether the operation is safe. +func Abs16(x int16) (int16, bool) { + if x == math.MinInt16 { + return x, false + } + if x >= 0 { + return x, true + } + return -x, true +} + // Add16 performs + operation on two int16 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Add16(a, b int16) (int16, bool) { c := a + b if (c > a) == (b > 0) { @@ -120,9 +231,28 @@ func Add16p(a, b int16) int16 { return r } +// UAdd16 performs + operation on two uint16 operands +// returning a result and a ok result indicating whether the operation is safe. +func UAdd16(a, b uint16) (uint16, bool) { + c := a + b + if c >= a { + return c, true + } + return c, false +} + +// UAdd16p is the unchecked panicing version of UAdd16 +func UAdd16p(a, b uint16) uint16 { + r, ok := UAdd16(a, b) + if !ok { + panic("addition overflow") + } + return r +} + // Sub16 performs - operation on two int16 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Sub16(a, b int16) (int16, bool) { c := a - b if (c < a) == (b > 0) { @@ -140,9 +270,28 @@ func Sub16p(a, b int16) int16 { return r } +// USub16 performs - operation on two uint16 operands +// returning a result and a ok result indicating whether the operation is safe. +func USub16(a, b uint16) (uint16, bool) { + c := a - b + if a >= b { + return c, true + } + return c, false +} + +// USub16p is the unchecked panicing version of USub16 +func USub16p(a, b uint16) uint16 { + r, ok := USub16(a, b) + if !ok { + panic("subtraction overflow") + } + return r +} + // Mul16 performs * operation on two int16 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Mul16(a, b int16) (int16, bool) { if a == 0 || b == 0 { return 0, true @@ -165,10 +314,31 @@ func Mul16p(a, b int16) int16 { return r } +// UMul16 performs * operation on two uint16 operands +// returning a result and a ok result indicating whether the operation is safe. +func UMul16(a, b uint16) (uint16, bool) { + if a == 0 || b == 0 { + return 0, true + } + c := a * b + if c/b == a { + return c, true + } + return c, false +} + +// UMul16p is the unchecked panicing version of UMul16 +func UMul16p(a, b uint16) uint16 { + r, ok := UMul16(a, b) + if !ok { + panic("multiplication overflow") + } + return r +} // Div16 performs / operation on two int16 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Div16(a, b int16) (int16, bool) { q, _, ok := Quotient16(a, b) return q, ok @@ -184,7 +354,7 @@ func Div16p(a, b int16) int16 { } // Quotient16 performs + operation on two int16 operands -// returning a quotient, a remainder and status +// returning a quotient, a remainder and a ok result indicating whether the operation is safe. func Quotient16(a, b int16) (int16, int16, bool) { if b == 0 { return 0, 0, false @@ -194,10 +364,48 @@ func Quotient16(a, b int16) (int16, int16, bool) { return c, a % b, status } +// UDiv16 performs / operation on two uint16 operands +// returning a result and a ok result indicating whether the operation is safe. +func UDiv16(a, b uint16) (uint16, bool) { + q, _, ok := UQuotient16(a, b) + return q, ok +} + +// UDiv16p is the unchecked panicing version of UDiv16 +func UDiv16p(a, b uint16) uint16 { + r, ok := UDiv16(a, b) + if !ok { + panic("division failure") + } + return r +} + +// UQuotient16 performs + operation on two uint16 operands +// returning a quotient, a remainder and a ok result indicating whether the operation is safe. +func UQuotient16(a, b uint16) (uint16, uint16, bool) { + if b == 0 { + return 0, 0, false + } + c := a / b + return c, a % b, true +} + +// Abs32 performs absolute value operation on an int32 operand +// returning a result and a ok result indicating whether the operation is safe. +func Abs32(x int32) (int32, bool) { + if x == math.MinInt32 { + return x, false + } + if x >= 0 { + return x, true + } + return -x, true +} + // Add32 performs + operation on two int32 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Add32(a, b int32) (int32, bool) { c := a + b if (c > a) == (b > 0) { @@ -215,9 +423,28 @@ func Add32p(a, b int32) int32 { return r } +// UAdd32 performs + operation on two uint32 operands +// returning a result and a ok result indicating whether the operation is safe. +func UAdd32(a, b uint32) (uint32, bool) { + c := a + b + if c >= a { + return c, true + } + return c, false +} + +// UAdd32p is the unchecked panicing version of UAdd32 +func UAdd32p(a, b uint32) uint32 { + r, ok := UAdd32(a, b) + if !ok { + panic("addition overflow") + } + return r +} + // Sub32 performs - operation on two int32 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Sub32(a, b int32) (int32, bool) { c := a - b if (c < a) == (b > 0) { @@ -235,9 +462,28 @@ func Sub32p(a, b int32) int32 { return r } +// USub32 performs - operation on two uint32 operands +// returning a result and a ok result indicating whether the operation is safe. +func USub32(a, b uint32) (uint32, bool) { + c := a - b + if a >= b { + return c, true + } + return c, false +} + +// USub32p is the unchecked panicing version of USub32 +func USub32p(a, b uint32) uint32 { + r, ok := USub32(a, b) + if !ok { + panic("subtraction overflow") + } + return r +} + // Mul32 performs * operation on two int32 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Mul32(a, b int32) (int32, bool) { if a == 0 || b == 0 { return 0, true @@ -260,10 +506,31 @@ func Mul32p(a, b int32) int32 { return r } +// UMul32 performs * operation on two uint32 operands +// returning a result and a ok result indicating whether the operation is safe. +func UMul32(a, b uint32) (uint32, bool) { + if a == 0 || b == 0 { + return 0, true + } + c := a * b + if c/b == a { + return c, true + } + return c, false +} + +// UMul32p is the unchecked panicing version of UMul32 +func UMul32p(a, b uint32) uint32 { + r, ok := UMul32(a, b) + if !ok { + panic("multiplication overflow") + } + return r +} // Div32 performs / operation on two int32 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Div32(a, b int32) (int32, bool) { q, _, ok := Quotient32(a, b) return q, ok @@ -279,7 +546,7 @@ func Div32p(a, b int32) int32 { } // Quotient32 performs + operation on two int32 operands -// returning a quotient, a remainder and status +// returning a quotient, a remainder and a ok result indicating whether the operation is safe. func Quotient32(a, b int32) (int32, int32, bool) { if b == 0 { return 0, 0, false @@ -289,10 +556,48 @@ func Quotient32(a, b int32) (int32, int32, bool) { return c, a % b, status } +// UDiv32 performs / operation on two uint32 operands +// returning a result and a ok result indicating whether the operation is safe. +func UDiv32(a, b uint32) (uint32, bool) { + q, _, ok := UQuotient32(a, b) + return q, ok +} + +// UDiv32p is the unchecked panicing version of UDiv32 +func UDiv32p(a, b uint32) uint32 { + r, ok := UDiv32(a, b) + if !ok { + panic("division failure") + } + return r +} + +// UQuotient32 performs + operation on two uint32 operands +// returning a quotient, a remainder and a ok result indicating whether the operation is safe. +func UQuotient32(a, b uint32) (uint32, uint32, bool) { + if b == 0 { + return 0, 0, false + } + c := a / b + return c, a % b, true +} + +// Abs64 performs absolute value operation on an int64 operand +// returning a result and a ok result indicating whether the operation is safe. +func Abs64(x int64) (int64, bool) { + if x == math.MinInt64 { + return x, false + } + if x >= 0 { + return x, true + } + return -x, true +} + // Add64 performs + operation on two int64 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Add64(a, b int64) (int64, bool) { c := a + b if (c > a) == (b > 0) { @@ -310,9 +615,28 @@ func Add64p(a, b int64) int64 { return r } +// UAdd64 performs + operation on two uint64 operands +// returning a result and a ok result indicating whether the operation is safe. +func UAdd64(a, b uint64) (uint64, bool) { + c := a + b + if c >= a { + return c, true + } + return c, false +} + +// UAdd64p is the unchecked panicing version of UAdd64 +func UAdd64p(a, b uint64) uint64 { + r, ok := UAdd64(a, b) + if !ok { + panic("addition overflow") + } + return r +} + // Sub64 performs - operation on two int64 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Sub64(a, b int64) (int64, bool) { c := a - b if (c < a) == (b > 0) { @@ -330,9 +654,28 @@ func Sub64p(a, b int64) int64 { return r } +// USub64 performs - operation on two uint64 operands +// returning a result and a ok result indicating whether the operation is safe. +func USub64(a, b uint64) (uint64, bool) { + c := a - b + if a >= b { + return c, true + } + return c, false +} + +// USub64p is the unchecked panicing version of USub64 +func USub64p(a, b uint64) uint64 { + r, ok := USub64(a, b) + if !ok { + panic("subtraction overflow") + } + return r +} + // Mul64 performs * operation on two int64 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Mul64(a, b int64) (int64, bool) { if a == 0 || b == 0 { return 0, true @@ -355,10 +698,31 @@ func Mul64p(a, b int64) int64 { return r } +// UMul64 performs * operation on two uint64 operands +// returning a result and a ok result indicating whether the operation is safe. +func UMul64(a, b uint64) (uint64, bool) { + if a == 0 || b == 0 { + return 0, true + } + c := a * b + if c/b == a { + return c, true + } + return c, false +} + +// UMul64p is the unchecked panicing version of UMul64 +func UMul64p(a, b uint64) uint64 { + r, ok := UMul64(a, b) + if !ok { + panic("multiplication overflow") + } + return r +} // Div64 performs / operation on two int64 operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Div64(a, b int64) (int64, bool) { q, _, ok := Quotient64(a, b) return q, ok @@ -374,7 +738,7 @@ func Div64p(a, b int64) int64 { } // Quotient64 performs + operation on two int64 operands -// returning a quotient, a remainder and status +// returning a quotient, a remainder and a ok result indicating whether the operation is safe. func Quotient64(a, b int64) (int64, int64, bool) { if b == 0 { return 0, 0, false @@ -384,3 +748,523 @@ func Quotient64(a, b int64) (int64, int64, bool) { return c, a % b, status } +// UDiv64 performs / operation on two uint64 operands +// returning a result and a ok result indicating whether the operation is safe. +func UDiv64(a, b uint64) (uint64, bool) { + q, _, ok := UQuotient64(a, b) + return q, ok +} + +// UDiv64p is the unchecked panicing version of UDiv64 +func UDiv64p(a, b uint64) uint64 { + r, ok := UDiv64(a, b) + if !ok { + panic("division failure") + } + return r +} + +// UQuotient64 performs + operation on two uint64 operands +// returning a quotient, a remainder and a ok result indicating whether the operation is safe. +func UQuotient64(a, b uint64) (uint64, uint64, bool) { + if b == 0 { + return 0, 0, false + } + c := a / b + return c, a % b, true +} + + + +// Int64ToInt32 converts an int64 value to int32. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int64ToInt32(x int64) (int32, bool) { + y := int32(x) + if math.MinInt32 <= x && x <= math.MaxInt32 { + return y, true + } + return y, false +} + + + + +// Int64ToInt16 converts an int64 value to int16. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int64ToInt16(x int64) (int16, bool) { + y := int16(x) + if math.MinInt16 <= x && x <= math.MaxInt16 { + return y, true + } + return y, false +} + + + + +// Int64ToInt8 converts an int64 value to int8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int64ToInt8(x int64) (int8, bool) { + y := int8(x) + if math.MinInt8 <= x && x <= math.MaxInt8 { + return y, true + } + return y, false +} + + + + +// Int32ToInt16 converts an int32 value to int16. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int32ToInt16(x int32) (int16, bool) { + y := int16(x) + if math.MinInt16 <= x && x <= math.MaxInt16 { + return y, true + } + return y, false +} + + + + +// Int32ToInt8 converts an int32 value to int8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int32ToInt8(x int32) (int8, bool) { + y := int8(x) + if math.MinInt8 <= x && x <= math.MaxInt8 { + return y, true + } + return y, false +} + + + + +// Int16ToInt8 converts an int16 value to int8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int16ToInt8(x int16) (int8, bool) { + y := int8(x) + if math.MinInt8 <= x && x <= math.MaxInt8 { + return y, true + } + return y, false +} + + + + +// Uint64ToUint32 converts an uint64 value to uint32. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint64ToUint32(x uint64) (uint32, bool) { + y := uint32(x) + if x <= math.MaxUint32 { + return y, true + } + return y, false +} + + + + +// Uint64ToUint16 converts an uint64 value to uint16. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint64ToUint16(x uint64) (uint16, bool) { + y := uint16(x) + if x <= math.MaxUint16 { + return y, true + } + return y, false +} + + + + +// Uint64ToUint8 converts an uint64 value to uint8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint64ToUint8(x uint64) (uint8, bool) { + y := uint8(x) + if x <= math.MaxUint8 { + return y, true + } + return y, false +} + + + + +// Uint32ToUint16 converts an uint32 value to uint16. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint32ToUint16(x uint32) (uint16, bool) { + y := uint16(x) + if x <= math.MaxUint16 { + return y, true + } + return y, false +} + + + + +// Uint32ToUint8 converts an uint32 value to uint8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint32ToUint8(x uint32) (uint8, bool) { + y := uint8(x) + if x <= math.MaxUint8 { + return y, true + } + return y, false +} + + + + +// Uint16ToUint8 converts an uint16 value to uint8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint16ToUint8(x uint16) (uint8, bool) { + y := uint8(x) + if x <= math.MaxUint8 { + return y, true + } + return y, false +} + + + + +// Uint64ToInt64 converts an uint64 value to int64. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint64ToInt64(x uint64) (int64, bool) { + y := int64(x) + if x <= math.MaxInt64 { + return y, true + } + return y, false +} + + + + +// Uint64ToInt32 converts an uint64 value to int32. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint64ToInt32(x uint64) (int32, bool) { + y := int32(x) + if x <= math.MaxInt32 { + return y, true + } + return y, false +} + + + + +// Uint64ToInt16 converts an uint64 value to int16. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint64ToInt16(x uint64) (int16, bool) { + y := int16(x) + if x <= math.MaxInt16 { + return y, true + } + return y, false +} + + + + +// Uint64ToInt8 converts an uint64 value to int8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint64ToInt8(x uint64) (int8, bool) { + y := int8(x) + if x <= math.MaxInt8 { + return y, true + } + return y, false +} + + + + +// Uint32ToInt32 converts an uint32 value to int32. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint32ToInt32(x uint32) (int32, bool) { + y := int32(x) + if x <= math.MaxInt32 { + return y, true + } + return y, false +} + + + + +// Uint32ToInt16 converts an uint32 value to int16. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint32ToInt16(x uint32) (int16, bool) { + y := int16(x) + if x <= math.MaxInt16 { + return y, true + } + return y, false +} + + + + +// Uint32ToInt8 converts an uint32 value to int8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint32ToInt8(x uint32) (int8, bool) { + y := int8(x) + if x <= math.MaxInt8 { + return y, true + } + return y, false +} + + + + +// Uint16ToInt16 converts an uint16 value to int16. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint16ToInt16(x uint16) (int16, bool) { + y := int16(x) + if x <= math.MaxInt16 { + return y, true + } + return y, false +} + + + + +// Uint16ToInt8 converts an uint16 value to int8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint16ToInt8(x uint16) (int8, bool) { + y := int8(x) + if x <= math.MaxInt8 { + return y, true + } + return y, false +} + + + + +// Uint8ToInt8 converts an uint8 value to int8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint8ToInt8(x uint8) (int8, bool) { + y := int8(x) + if x <= math.MaxInt8 { + return y, true + } + return y, false +} + + + + +// Int64ToUint64 converts an int64 value to uint64. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int64ToUint64(x int64) (uint64, bool) { + y := uint64(x) + if 0 <= x { + return y, true + } + return y, false +} + + + + +// Int64ToUint32 converts an int64 value to uint32. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int64ToUint32(x int64) (uint32, bool) { + y := uint32(x) + if 0 <= x && x <= math.MaxUint32 { + return y, true + } + return y, false +} + + + + +// Int64ToUint16 converts an int64 value to uint16. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int64ToUint16(x int64) (uint16, bool) { + y := uint16(x) + if 0 <= x && x <= math.MaxUint16 { + return y, true + } + return y, false +} + + + + +// Int64ToUint8 converts an int64 value to uint8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int64ToUint8(x int64) (uint8, bool) { + y := uint8(x) + if 0 <= x && x <= math.MaxUint8 { + return y, true + } + return y, false +} + + + + +// Int32ToUint64 converts an int32 value to uint64. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int32ToUint64(x int32) (uint64, bool) { + y := uint64(x) + if 0 <= x { + return y, true + } + return y, false +} + + + + +// Int32ToUint32 converts an int32 value to uint32. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int32ToUint32(x int32) (uint32, bool) { + y := uint32(x) + if 0 <= x { + return y, true + } + return y, false +} + + + + +// Int32ToUint16 converts an int32 value to uint16. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int32ToUint16(x int32) (uint16, bool) { + y := uint16(x) + if 0 <= x && x <= math.MaxUint16 { + return y, true + } + return y, false +} + + + + +// Int32ToUint8 converts an int32 value to uint8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int32ToUint8(x int32) (uint8, bool) { + y := uint8(x) + if 0 <= x && x <= math.MaxUint8 { + return y, true + } + return y, false +} + + + + +// Int16ToUint64 converts an int16 value to uint64. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int16ToUint64(x int16) (uint64, bool) { + y := uint64(x) + if 0 <= x { + return y, true + } + return y, false +} + + + + +// Int16ToUint32 converts an int16 value to uint32. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int16ToUint32(x int16) (uint32, bool) { + y := uint32(x) + if 0 <= x { + return y, true + } + return y, false +} + + + + +// Int16ToUint16 converts an int16 value to uint16. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int16ToUint16(x int16) (uint16, bool) { + y := uint16(x) + if 0 <= x { + return y, true + } + return y, false +} + + + + +// Int16ToUint8 converts an int16 value to uint8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int16ToUint8(x int16) (uint8, bool) { + y := uint8(x) + if 0 <= x && x <= math.MaxUint8 { + return y, true + } + return y, false +} + + + + +// Int8ToUint64 converts an int8 value to uint64. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int8ToUint64(x int8) (uint64, bool) { + y := uint64(x) + if 0 <= x { + return y, true + } + return y, false +} + + + + +// Int8ToUint32 converts an int8 value to uint32. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int8ToUint32(x int8) (uint32, bool) { + y := uint32(x) + if 0 <= x { + return y, true + } + return y, false +} + + + + +// Int8ToUint16 converts an int8 value to uint16. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int8ToUint16(x int8) (uint16, bool) { + y := uint16(x) + if 0 <= x { + return y, true + } + return y, false +} + + + + +// Int8ToUint8 converts an int8 value to uint8. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int8ToUint8(x int8) (uint8, bool) { + y := uint8(x) + if 0 <= x { + return y, true + } + return y, false +} + + diff --git a/overflow_template.sh b/overflow_template.sh index b21fb04..96faae7 100755 --- a/overflow_template.sh +++ b/overflow_template.sh @@ -1,9 +1,11 @@ -#!/bin/sh +#!/bin/bash exec > overflow_impl.go echo "package overflow +import \"math\" + // This is generated code, created by overflow_template.sh executed // by \"go generate\" @@ -14,8 +16,20 @@ for SIZE in 8 16 32 64 do echo " +// Abs${SIZE} performs absolute value operation on an int${SIZE} operand +// returning a result and a ok result indicating whether the operation is safe. +func Abs${SIZE}(x int${SIZE}) (int${SIZE}, bool) { + if x == math.MinInt${SIZE} { + return x, false + } + if x >= 0 { + return x, true + } + return -x, true +} + // Add${SIZE} performs + operation on two int${SIZE} operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Add${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) { c := a + b if (c > a) == (b > 0) { @@ -33,9 +47,28 @@ func Add${SIZE}p(a, b int${SIZE}) int${SIZE} { return r } +// UAdd${SIZE} performs + operation on two uint${SIZE} operands +// returning a result and a ok result indicating whether the operation is safe. +func UAdd${SIZE}(a, b uint${SIZE}) (uint${SIZE}, bool) { + c := a + b + if c >= a { + return c, true + } + return c, false +} + +// UAdd${SIZE}p is the unchecked panicing version of UAdd${SIZE} +func UAdd${SIZE}p(a, b uint${SIZE}) uint${SIZE} { + r, ok := UAdd${SIZE}(a, b) + if !ok { + panic(\"addition overflow\") + } + return r +} + // Sub${SIZE} performs - operation on two int${SIZE} operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Sub${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) { c := a - b if (c < a) == (b > 0) { @@ -53,9 +86,28 @@ func Sub${SIZE}p(a, b int${SIZE}) int${SIZE} { return r } +// USub${SIZE} performs - operation on two uint${SIZE} operands +// returning a result and a ok result indicating whether the operation is safe. +func USub${SIZE}(a, b uint${SIZE}) (uint${SIZE}, bool) { + c := a - b + if a >= b { + return c, true + } + return c, false +} + +// USub${SIZE}p is the unchecked panicing version of USub${SIZE} +func USub${SIZE}p(a, b uint${SIZE}) uint${SIZE} { + r, ok := USub${SIZE}(a, b) + if !ok { + panic(\"subtraction overflow\") + } + return r +} + // Mul${SIZE} performs * operation on two int${SIZE} operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Mul${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) { if a == 0 || b == 0 { return 0, true @@ -78,10 +130,31 @@ func Mul${SIZE}p(a, b int${SIZE}) int${SIZE} { return r } +// UMul${SIZE} performs * operation on two uint${SIZE} operands +// returning a result and a ok result indicating whether the operation is safe. +func UMul${SIZE}(a, b uint${SIZE}) (uint${SIZE}, bool) { + if a == 0 || b == 0 { + return 0, true + } + c := a * b + if c/b == a { + return c, true + } + return c, false +} + +// UMul${SIZE}p is the unchecked panicing version of UMul${SIZE} +func UMul${SIZE}p(a, b uint${SIZE}) uint${SIZE} { + r, ok := UMul${SIZE}(a, b) + if !ok { + panic(\"multiplication overflow\") + } + return r +} // Div${SIZE} performs / operation on two int${SIZE} operands -// returning a result and status +// returning a result and a ok result indicating whether the operation is safe. func Div${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) { q, _, ok := Quotient${SIZE}(a, b) return q, ok @@ -97,7 +170,7 @@ func Div${SIZE}p(a, b int${SIZE}) int${SIZE} { } // Quotient${SIZE} performs + operation on two int${SIZE} operands -// returning a quotient, a remainder and status +// returning a quotient, a remainder and a ok result indicating whether the operation is safe. func Quotient${SIZE}(a, b int${SIZE}) (int${SIZE}, int${SIZE}, bool) { if b == 0 { return 0, 0, false @@ -106,5 +179,117 @@ func Quotient${SIZE}(a, b int${SIZE}) (int${SIZE}, int${SIZE}, bool) { status := (c < 0) == ((a < 0) != (b < 0)) return c, a % b, status } + +// UDiv${SIZE} performs / operation on two uint${SIZE} operands +// returning a result and a ok result indicating whether the operation is safe. +func UDiv${SIZE}(a, b uint${SIZE}) (uint${SIZE}, bool) { + q, _, ok := UQuotient${SIZE}(a, b) + return q, ok +} + +// UDiv${SIZE}p is the unchecked panicing version of UDiv${SIZE} +func UDiv${SIZE}p(a, b uint${SIZE}) uint${SIZE} { + r, ok := UDiv${SIZE}(a, b) + if !ok { + panic(\"division failure\") + } + return r +} + +// UQuotient${SIZE} performs + operation on two uint${SIZE} operands +// returning a quotient, a remainder and a ok result indicating whether the operation is safe. +func UQuotient${SIZE}(a, b uint${SIZE}) (uint${SIZE}, uint${SIZE}, bool) { + if b == 0 { + return 0, 0, false + } + c := a / b + return c, a % b, true +} +" +done + +SIZE=(64 32 16 8) + +# Int -> Int +for FROM in {0..3}; do +for TO in $(seq $((FROM + 1)) 3); do +echo " + +// Int${SIZE[FROM]}ToInt${SIZE[TO]} converts an int${SIZE[FROM]} value to int${SIZE[TO]}. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int${SIZE[FROM]}ToInt${SIZE[TO]}(x int${SIZE[FROM]}) (int${SIZE[TO]}, bool) { + y := int${SIZE[TO]}(x) + if math.MinInt${SIZE[TO]} <= x && x <= math.MaxInt${SIZE[TO]} { + return y, true + } + return y, false +} + +" +done +done + +# Uint -> Uint +for FROM in {0..3}; do +for TO in $(seq $((FROM + 1)) 3); do +echo " + +// Uint${SIZE[FROM]}ToUint${SIZE[TO]} converts an uint${SIZE[FROM]} value to uint${SIZE[TO]}. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint${SIZE[FROM]}ToUint${SIZE[TO]}(x uint${SIZE[FROM]}) (uint${SIZE[TO]}, bool) { + y := uint${SIZE[TO]}(x) + if x <= math.MaxUint${SIZE[TO]} { + return y, true + } + return y, false +} + +" +done +done + +# Uint -> Int +for FROM in {0..3}; do +for TO in $(seq $((FROM)) 3); do +echo " + +// Uint${SIZE[FROM]}ToInt${SIZE[TO]} converts an uint${SIZE[FROM]} value to int${SIZE[TO]}. +// returning a converted value and a ok result indicating whether the operation is safe. +func Uint${SIZE[FROM]}ToInt${SIZE[TO]}(x uint${SIZE[FROM]}) (int${SIZE[TO]}, bool) { + y := int${SIZE[TO]}(x) + if x <= math.MaxInt${SIZE[TO]} { + return y, true + } + return y, false +} + +" +done +done + + +# Int -> Uint +for FROM in {0..3}; do +for TO in {0..3}; do +echo " + +// Int${SIZE[FROM]}ToUint${SIZE[TO]} converts an int${SIZE[FROM]} value to uint${SIZE[TO]}. +// returning a converted value and a ok result indicating whether the operation is safe. +func Int${SIZE[FROM]}ToUint${SIZE[TO]}(x int${SIZE[FROM]}) (uint${SIZE[TO]}, bool) { + y := uint${SIZE[TO]}(x)" + if [ "$((SIZE[FROM]))" -gt "$((SIZE[TO]))" ]; then + echo "\ + if 0 <= x && x <= math.MaxUint${SIZE[TO]} {" + else + echo "\ + if 0 <= x {" + fi +echo "\ + return y, true + } + return y, false +} + " done +done diff --git a/overflow_test.go b/overflow_test.go index 419191d..c11464a 100644 --- a/overflow_test.go +++ b/overflow_test.go @@ -1,15 +1,15 @@ package overflow import ( + "fmt" "math" "testing" ) -import "fmt" // sample all possibilities of 8 bit numbers // by checking against 64 bit numbers -func TestAlgorithms(t *testing.T) { +func TestAlgorithmsInt(t *testing.T) { errors := 0 @@ -95,6 +95,92 @@ func TestAlgorithms(t *testing.T) { } +func TestAlgorithmsUint(t *testing.T) { + + errors := 0 + + for a64 := uint64(0); a64 <= uint64(math.MaxUint8); a64++ { + + for b64 := uint64(0); b64 <= uint64(math.MaxUint8) && errors < 10; b64++ { + + a8 := uint8(a64) + b8 := uint8(b64) + + if uint64(a8) != a64 || uint64(b8) != b64 { + t.Fatal("LOGIC FAILURE IN TEST") + } + + // ADDITION + { + r64 := a64 + b64 + + // now the verification + result, ok := UAdd8(a8, b8) + if ok && uint64(result) != r64 { + t.Errorf("failed to fail on %v + %v = %v instead of %v\n", + a8, b8, result, r64) + errors++ + } + if !ok && uint64(result) == r64 { + t.Fail() + errors++ + } + } + + // SUBTRACTION + { + r64 := a64 - b64 + + // now the verification + result, ok := USub8(a8, b8) + if ok && uint64(result) != r64 { + t.Errorf("failed to fail on %v - %v = %v instead of %v\n", + a8, b8, result, r64) + } + if !ok && uint64(result) == r64 { + t.Fail() + errors++ + } + } + + // MULTIPLICATION + { + r64 := a64 * b64 + + // now the verification + result, ok := UMul8(a8, b8) + if ok && uint64(result) != r64 { + t.Errorf("failed to fail on %v * %v = %v instead of %v\n", + a8, b8, result, r64) + errors++ + } + if !ok && uint64(result) == r64 { + t.Fail() + errors++ + } + } + + // DIVISION + if b8 != 0 { + r64 := a64 / b64 + + // now the verifiggcation + result, _, ok := UQuotient8(a8, b8) + if ok && uint64(result) != r64 { + t.Errorf("failed to fail on %v / %v = %v instead of %v\n", + a8, b8, result, r64) + errors++ + } + if !ok && result != 0 && uint64(result) == r64 { + t.Fail() + errors++ + } + } + } + } + +} + func TestQuotient(t *testing.T) { q, r, ok := Quotient(100, 3) if r != 1 || q != 33 || !ok { @@ -103,6 +189,34 @@ func TestQuotient(t *testing.T) { if _, _, ok = Quotient(1, 0); ok { t.Error("unexpected lack of failure") } + if _, _, ok = Quotient(math.MinInt, -1); ok { + t.Error("unexpected lack of failure") + } + uq, ur, ok := UQuotient(100, 3) + if ur != 1 || uq != 33 || !ok { + t.Errorf("expected 100/3 => 33, r=1") + } + if _, _, ok = UQuotient(1, 0); ok { + t.Error("unexpected lack of failure") + } +} + +func TestAbs(t *testing.T) { + if _, ok := Abs(math.MinInt); ok { + t.Error("unexpected ok, absolute value overflow") + } + if _, ok := Abs64(math.MinInt64); ok { + t.Error("unexpected ok, absolute value overflow") + } + if _, ok := Abs32(math.MinInt32); ok { + t.Error("unexpected ok, absolute value overflow") + } + if _, ok := Abs16(math.MinInt16); ok { + t.Error("unexpected ok, absolute value overflow") + } + if _, ok := Abs8(math.MinInt8); ok { + t.Error("unexpected ok, absolute value overflow") + } } //func TestAdditionInt(t *testing.T) { diff --git a/overflow_type_conversion_test.go b/overflow_type_conversion_test.go new file mode 100644 index 0000000..d58d80d --- /dev/null +++ b/overflow_type_conversion_test.go @@ -0,0 +1,392 @@ +package overflow + +import ( + "testing" +) + +/* + +NOTE: +For the convenience of editing, please turn off automatic formatting when modifying this file +Please ignore any meaningless comments, which are residual comments generated automatically and are not easy to clear. + +NOTE: +The following test cases are generated by the following bash script, +and all compiler error lines are deleted, and then the expected OK lines are manually commented. + +```bash +sizes=(64 32 16 8) +signs=("Int" "Uint") +for from_sign in "${signs[@]}"; do +for from_size in "${sizes[@]}"; do +for to_sign in "${signs[@]}"; do +for to_size in "${sizes[@]}"; do +code=" +// ${from_sign}${from_size} -> ${to_sign}${to_size} +if r,ok:=${from_sign}${from_size}To${to_sign}${to_size}(Max${from_sign}${from_size});ok{t.Error(UNEXPECTED_OK,r)} +if r,ok:=${from_sign}${from_size}To${to_sign}${to_size}(Min${from_sign}${from_size});ok{t.Error(UNEXPECTED_OK,r)} +if r,ok:=${from_sign}${from_size}To${to_sign}${to_size}(Max${to_sign}${to_size}+1);ok{t.Error(UNEXPECTED_OK,r)} +if r,ok:=${from_sign}${from_size}To${to_sign}${to_size}(Min${to_sign}${to_size}-1);ok{t.Error(UNEXPECTED_OK,r)} +" +echo "$code" +done +done +done +done +``` +*/ + +// Integer limit values. +const ( + intSize = 32 << (^uint(0) >> 63) // 32 or 64 + + MaxInt = 1<<(intSize-1) - 1 // MaxInt32 or MaxInt64 depending on intSize. + MinInt = -1 << (intSize - 1) // MinInt32 or MinInt64 depending on intSize. + MaxInt8 = 1<<7 - 1 // 127 + MinInt8 = -1 << 7 // -128 + MaxInt16 = 1<<15 - 1 // 32767 + MinInt16 = -1 << 15 // -32768 + MaxInt32 = 1<<31 - 1 // 2147483647 + MinInt32 = -1 << 31 // -2147483648 + MaxInt64 = 1<<63 - 1 // 9223372036854775807 + MinInt64 = -1 << 63 // -9223372036854775808 + MaxUint = 1< Int64 + + + // Int64 -> Int32 + if r,ok:=Int64ToInt32(MaxInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToInt32(MinInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToInt32(MaxInt32+1);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToInt32(MinInt32-1);ok{t.Error(UnexpectedOk,r)} + + + // Int64 -> Int16 + if r,ok:=Int64ToInt16(MaxInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToInt16(MinInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToInt16(MaxInt16+1);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToInt16(MinInt16-1);ok{t.Error(UnexpectedOk,r)} + + + // Int64 -> Int8 + if r,ok:=Int64ToInt8(MaxInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToInt8(MinInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToInt8(MaxInt8+1);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToInt8(MinInt8-1);ok{t.Error(UnexpectedOk,r)} + + + // Int64 -> Uint64 + //if r,ok:=Int64ToUint64(MaxInt64);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Int64ToUint64(MinInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToUint64(MinUint64-1);ok{t.Error(UnexpectedOk,r)} + + + // Int64 -> Uint32 + if r,ok:=Int64ToUint32(MaxInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToUint32(MinInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToUint32(MaxUint32+1);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToUint32(MinUint32-1);ok{t.Error(UnexpectedOk,r)} + + + // Int64 -> Uint16 + if r,ok:=Int64ToUint16(MaxInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToUint16(MinInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToUint16(MaxUint16+1);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToUint16(MinUint16-1);ok{t.Error(UnexpectedOk,r)} + + + // Int64 -> Uint8 + if r,ok:=Int64ToUint8(MaxInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToUint8(MinInt64);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToUint8(MaxUint8+1);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int64ToUint8(MinUint8-1);ok{t.Error(UnexpectedOk,r)} + + + // Int32 -> Int64 + + + // Int32 -> Int32 + + + // Int32 -> Int16 + if r,ok:=Int32ToInt16(MaxInt32);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToInt16(MinInt32);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToInt16(MaxInt16+1);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToInt16(MinInt16-1);ok{t.Error(UnexpectedOk,r)} + + + // Int32 -> Int8 + if r,ok:=Int32ToInt8(MaxInt32);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToInt8(MinInt32);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToInt8(MaxInt8+1);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToInt8(MinInt8-1);ok{t.Error(UnexpectedOk,r)} + + + // Int32 -> Uint64 + //if r,ok:=Int32ToUint64(MaxInt32);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Int32ToUint64(MinInt32);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToUint64(MinUint64-1);ok{t.Error(UnexpectedOk,r)} + + + // Int32 -> Uint32 + //if r,ok:=Int32ToUint32(MaxInt32);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Int32ToUint32(MinInt32);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToUint32(MinUint32-1);ok{t.Error(UnexpectedOk,r)} + + + // Int32 -> Uint16 + if r,ok:=Int32ToUint16(MaxInt32);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToUint16(MinInt32);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToUint16(MaxUint16+1);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToUint16(MinUint16-1);ok{t.Error(UnexpectedOk,r)} + + + // Int32 -> Uint8 + if r,ok:=Int32ToUint8(MaxInt32);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToUint8(MinInt32);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToUint8(MaxUint8+1);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int32ToUint8(MinUint8-1);ok{t.Error(UnexpectedOk,r)} + + + // Int16 -> Int64 + + + // Int16 -> Int32 + + + // Int16 -> Int16 + + + // Int16 -> Int8 + if r,ok:=Int16ToInt8(MaxInt16);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int16ToInt8(MinInt16);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int16ToInt8(MaxInt8+1);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int16ToInt8(MinInt8-1);ok{t.Error(UnexpectedOk,r)} + + + // Int16 -> Uint64 + //if r,ok:=Int16ToUint64(MaxInt16);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Int16ToUint64(MinInt16);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int16ToUint64(MinUint64-1);ok{t.Error(UnexpectedOk,r)} + + + // Int16 -> Uint32 + //if r,ok:=Int16ToUint32(MaxInt16);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Int16ToUint32(MinInt16);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int16ToUint32(MinUint32-1);ok{t.Error(UnexpectedOk,r)} + + + // Int16 -> Uint16 + //if r,ok:=Int16ToUint16(MaxInt16);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Int16ToUint16(MinInt16);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int16ToUint16(MinUint16-1);ok{t.Error(UnexpectedOk,r)} + + + // Int16 -> Uint8 + if r,ok:=Int16ToUint8(MaxInt16);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int16ToUint8(MinInt16);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int16ToUint8(MaxUint8+1);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int16ToUint8(MinUint8-1);ok{t.Error(UnexpectedOk,r)} + + + // Int8 -> Int64 + + + // Int8 -> Int32 + + + // Int8 -> Int16 + + + // Int8 -> Int8 + + + // Int8 -> Uint64 + //if r,ok:=Int8ToUint64(MaxInt8);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Int8ToUint64(MinInt8);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int8ToUint64(MinUint64-1);ok{t.Error(UnexpectedOk,r)} + + + // Int8 -> Uint32 + //if r,ok:=Int8ToUint32(MaxInt8);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Int8ToUint32(MinInt8);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int8ToUint32(MinUint32-1);ok{t.Error(UnexpectedOk,r)} + + + // Int8 -> Uint16 + //if r,ok:=Int8ToUint16(MaxInt8);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Int8ToUint16(MinInt8);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int8ToUint16(MinUint16-1);ok{t.Error(UnexpectedOk,r)} + + + // Int8 -> Uint8 + //if r,ok:=Int8ToUint8(MaxInt8);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Int8ToUint8(MinInt8);ok{t.Error(UnexpectedOk,r)} + if r,ok:=Int8ToUint8(MinUint8-1);ok{t.Error(UnexpectedOk,r)} + + + // Uint64 -> Int64 + if r,ok:=Uint64ToInt64(MaxUint64);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint64ToInt64(MinUint64);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint64ToInt64(MaxInt64+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint64 -> Int32 + if r,ok:=Uint64ToInt32(MaxUint64);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint64ToInt32(MinUint64);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint64ToInt32(MaxInt32+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint64 -> Int16 + if r,ok:=Uint64ToInt16(MaxUint64);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint64ToInt16(MinUint64);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint64ToInt16(MaxInt16+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint64 -> Int8 + if r,ok:=Uint64ToInt8(MaxUint64);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint64ToInt8(MinUint64);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint64ToInt8(MaxInt8+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint64 -> Uint64 + + + // Uint64 -> Uint32 + if r,ok:=Uint64ToUint32(MaxUint64);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint64ToUint32(MinUint64);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint64ToUint32(MaxUint32+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint64 -> Uint16 + if r,ok:=Uint64ToUint16(MaxUint64);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint64ToUint16(MinUint64);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint64ToUint16(MaxUint16+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint64 -> Uint8 + if r,ok:=Uint64ToUint8(MaxUint64);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint64ToUint8(MinUint64);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint64ToUint8(MaxUint8+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint32 -> Int64 + + + // Uint32 -> Int32 + if r,ok:=Uint32ToInt32(MaxUint32);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint32ToInt32(MinUint32);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint32ToInt32(MaxInt32+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint32 -> Int16 + if r,ok:=Uint32ToInt16(MaxUint32);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint32ToInt16(MinUint32);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint32ToInt16(MaxInt16+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint32 -> Int8 + if r,ok:=Uint32ToInt8(MaxUint32);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint32ToInt8(MinUint32);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint32ToInt8(MaxInt8+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint32 -> Uint64 + + + // Uint32 -> Uint32 + + + // Uint32 -> Uint16 + if r,ok:=Uint32ToUint16(MaxUint32);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint32ToUint16(MinUint32);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint32ToUint16(MaxUint16+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint32 -> Uint8 + if r,ok:=Uint32ToUint8(MaxUint32);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint32ToUint8(MinUint32);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint32ToUint8(MaxUint8+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint16 -> Int64 + + + // Uint16 -> Int32 + + + // Uint16 -> Int16 + if r,ok:=Uint16ToInt16(MaxUint16);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint16ToInt16(MinUint16);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint16ToInt16(MaxInt16+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint16 -> Int8 + if r,ok:=Uint16ToInt8(MaxUint16);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint16ToInt8(MinUint16);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint16ToInt8(MaxInt8+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint16 -> Uint64 + + + // Uint16 -> Uint32 + + + // Uint16 -> Uint16 + + + // Uint16 -> Uint8 + if r,ok:=Uint16ToUint8(MaxUint16);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint16ToUint8(MinUint16);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint16ToUint8(MaxUint8+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint8 -> Int64 + + + // Uint8 -> Int32 + + + // Uint8 -> Int16 + + + // Uint8 -> Int8 + if r,ok:=Uint8ToInt8(MaxUint8);ok{t.Error(UnexpectedOk,r)} + //if r,ok:=Uint8ToInt8(MinUint8);ok{t.Error(UNEXPECTED_OK,r)} + if r,ok:=Uint8ToInt8(MaxInt8+1);ok{t.Error(UnexpectedOk,r)} + + + // Uint8 -> Uint64 + + + // Uint8 -> Uint32 + + + // Uint8 -> Uint16 + + + // Uint8 -> Uint8 + +}