diff --git a/types/checked_ints.go b/types/checked_ints.go new file mode 100644 index 000000000..7f61d648b --- /dev/null +++ b/types/checked_ints.go @@ -0,0 +1,33 @@ +package types + +import ( + "errors" + "math" +) + +var ErrOverflowInt = errors.New("integer overflow") + +type CheckedInt32 int32 +type CheckedUint32 uint32 + +type CheckedInt64 int64 +type CheckedUint64 int64 + +func (i32 CheckedInt32) CheckedAdd(otherI32 CheckedInt32) (CheckedInt32, error) { + if otherI32 > 0 && (i32 > math.MaxInt32-otherI32) { + return 0, ErrOverflowInt + } else if otherI32 < 0 && (i32 < math.MinInt32-otherI32) { + return 0, ErrOverflowInt + } + return i32 + otherI32, nil +} + +func (i32 CheckedInt32) CheckedSub(otherI32 CheckedInt32) (CheckedInt32, error) { + if otherI32 > 0 && (i32 < math.MinInt32+otherI32) { + return 0, ErrOverflowInt + } else if otherI32 < 0 && (i32 > math.MaxInt32+otherI32) { + return 0, ErrOverflowInt + } + return i32 - otherI32, nil + +} diff --git a/types/checked_ints_test.go b/types/checked_ints_test.go new file mode 100644 index 000000000..68912506a --- /dev/null +++ b/types/checked_ints_test.go @@ -0,0 +1,61 @@ +package types + +import ( + "math" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCheckedInt32_CheckedAdd(t *testing.T) { + tcs := []struct { + val1 int32 + val2 int32 + sum int32 + wantErr bool + }{ + 0: {math.MaxInt32, 1, 0, true}, + 1: {math.MinInt32, 1, math.MinInt32 + 1, false}, + 2: {math.MaxInt32, math.MaxInt32, 0, true}, + 3: {0, math.MaxInt32, math.MaxInt32, false}, + 4: {0, 1, 1, false}, + 5: {1, 1, 2, false}, + } + for i, tc := range tcs { + v1 := CheckedInt32(tc.val1) + v2 := CheckedInt32(tc.val2) + sum, err := v1.CheckedAdd(v2) + if tc.wantErr { + assert.Error(t, err, "Should fail: %v", i) + assert.Zero(t, sum, "Got invalid sum for case %v", i) + } else { + assert.EqualValues(t, tc.sum, sum) + } + } +} + +func TestCheckedInt32_CheckedSub(t *testing.T) { + tcs := []struct { + val1 int32 + val2 int32 + sum int32 + wantErr bool + }{ + 0: {math.MaxInt32, math.MaxInt32, 0, false}, + 1: {math.MinInt32, 1, 0, true}, + 2: {math.MinInt32 + 1, 1, math.MinInt32, false}, + 3: {1, 1, 0, false}, + 4: {1, 2, -1, false}, + } + for i, tc := range tcs { + v1 := CheckedInt32(tc.val1) + v2 := CheckedInt32(tc.val2) + sum, err := v1.CheckedSub(v2) + if tc.wantErr { + assert.Error(t, err, "Should fail: %v", i) + assert.Zero(t, sum, "Got invalid sum for case %v", i) + } else { + assert.EqualValues(t, tc.sum, sum, "failed: %v", i) + } + } +}