Skip to content

Commit

Permalink
fix(utils): IsZeroValue align to conventions
Browse files Browse the repository at this point in the history
  • Loading branch information
sixcolors committed Dec 12, 2023
1 parent cdb2885 commit c228dbd
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 197 deletions.
47 changes: 1 addition & 46 deletions utils/zero.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,60 +51,15 @@ func IsZeroValue(x interface{}) bool {
return v == ""
case error:
return v == nil
case []bool:
return len(v) == 0
case []int:
return len(v) == 0
case []int8:
return len(v) == 0
case []int16:
return len(v) == 0
case []int32:
return len(v) == 0
case []int64:
return len(v) == 0
case []uint:
return len(v) == 0
case []uint8:
return len(v) == 0
case []uint16:
return len(v) == 0
case []uint32:
return len(v) == 0
case []uint64:
return len(v) == 0
case []uintptr:
return len(v) == 0
case []float32:
return len(v) == 0
case []float64:
return len(v) == 0
case []complex64:
return len(v) == 0
case []complex128:
return len(v) == 0
case []string:
return len(v) == 0
case []error:
return len(v) == 0
default:
// Slow path using reflection
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Slice, reflect.Map, reflect.Array:
return rv.Len() == 0
case reflect.Struct:
for i := 0; i < rv.NumField(); i++ {
if !IsZeroValue(rv.Field(i).Interface()) {
return false
}
}
return true
case reflect.Ptr:
if rv.IsNil() {
return true
}
return IsZeroValue(rv.Elem().Interface())
return reflect.DeepEqual(v, reflect.Zero(reflect.TypeOf(v)).Interface())
default:
return reflect.DeepEqual(v, reflect.Zero(reflect.TypeOf(v)).Interface())
}
Expand Down
273 changes: 122 additions & 151 deletions utils/zero_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package utils

import (
"errors"
"testing"
)

Expand Down Expand Up @@ -53,49 +52,6 @@ func TestIsZeroValue(t *testing.T) {
var zeroCustomSlice []customInt
var zeroCustomMap map[customInt]customInt

nonZeroInt := 1
nonZeroInt8 := int8(1)
nonZeroInt16 := int16(1)
nonZeroInt32 := int32(1)
nonZeroInt64 := int64(1)
nonZeroUint := uint(1)
nonZeroUint8 := uint8(1)
nonZeroUint16 := uint16(1)
nonZeroUint32 := uint32(1)
nonZeroUint64 := uint64(1)
nonZeroUintptr := uintptr(1)
nonZeroString := "1"
nonZeroBool := true
nonZeroFloat32 := float32(1)
nonZeroFloat64 := float64(1)
nonZeroComplex64 := complex64(1 + 0i)
nonZeroComplex128 := complex128(1 + 0i)
nonZeroSliceInt := []int{1}
nonZeroSliceInt8 := []int8{1}
nonZeroSliceInt16 := []int16{1}
nonZeroSliceInt32 := []int32{1}
nonZeroSliceInt64 := []int64{1}
nonZeroSliceUint := []uint{1}
nonZeroSliceUint8 := []uint8{1}
nonZeroSliceUint16 := []uint16{1}
nonZeroSliceUint32 := []uint32{1}
nonZeroSliceUint64 := []uint64{1}
nonZeroSliceUintptr := []uintptr{1}
nonZeroSliceString := []string{"1"}
nonZeroSliceBool := []bool{true}
nonZeroSliceFloat32 := []float32{1}
nonZeroSliceFloat64 := []float64{1}
nonZeroSliceComplex64 := []complex64{1 + 0i}
nonZeroSliceComplex128 := []complex128{1 + 0i}
nonZeroStruct := struct{ A int }{A: 1}
nonZeroPtr := &nonZeroInt
nonZeroCustomInt := customInt(1)
nonZeroMap := map[string]int{"1": 1}
nonZeroCustomStruct := customStruct{A: 1}
nonZeroError := errors.New("1")
nonZeroCustomSlice := []customInt{1}
nonZeroCustomMap := map[customInt]customInt{1: 1}

testCases := []struct {
description string
in interface{}
Expand Down Expand Up @@ -146,103 +102,110 @@ func TestIsZeroValue(t *testing.T) {
{"customMap", zeroCustomMap, true},
// Zero values, var initialized
{"nil", nil, true},
{"zeroInt", 0, true},
{"zeroInt8", int8(0), true},
{"zeroInt16", int16(0), true},
{"zeroInt32", int32(0), true},
{"zeroInt64", int64(0), true},
{"zeroUint", uint(0), true},
{"zeroUint8", uint8(0), true},
{"zeroUint16", uint16(0), true},
{"zeroUint32", uint32(0), true},
{"zeroUint64", uint64(0), true},
{"zeroUintptr", uintptr(0), true},
{"zeroString", "", true},
{"zeroBool", false, true},
{"zeroFloat32", float32(0), true},
{"zeroFloat64", float64(0), true},
{"zeroComplex64", complex64(0 + 0i), true},
{"zeroComplex128", complex128(0 + 0i), true},
{"zeroSliceInt", []int{}, true},
{"zeroSliceInt8", []int8{}, true},
{"zeroSliceInt16", []int16{}, true},
{"zeroSliceInt32", []int32{}, true},
{"zeroSliceInt64", []int64{}, true},
{"zeroSliceUint", []uint{}, true},
{"zeroSliceUint8", []uint8{}, true},
{"zeroSliceUint16", []uint16{}, true},
{"zeroSliceUint32", []uint32{}, true},
{"zeroSliceUint64", []uint64{}, true},
{"zeroSliceUintptr", []uintptr{}, true},
{"zeroSliceFloat32", []float32{}, true},
{"zeroSliceFloat64", []float64{}, true},
{"zeroSliceComplex64", []complex64{}, true},
{"zeroSliceComplex128", []complex128{}, true},
{"zeroSliceString", []string{}, true},
{"zeroSliceBool", []bool{}, true},
{"zeroStruct", struct{}{}, true},
{"zeroPtr", (*int)(nil), true},
{"zeroCustomInt", customInt(0), true},
{"zeroMap", map[string]int{}, true},
{"zeroCustomStruct", customStruct{}, true},
{"zeroError", error(nil), true},
{"zeroCustomSlice", []customInt{}, true},
{"zeroCustomMap", map[customInt]customInt{}, true},
{"zeroIntInited", 0, true},
{"zeroInt8Inited", int8(0), true},
{"zeroInt16Inited", int16(0), true},
{"zeroInt32Inited", int32(0), true},
{"zeroInt64Inited", int64(0), true},
{"zeroUintInited", uint(0), true},
{"zeroUint8Inited", uint8(0), true},
{"zeroUint16Inited", uint16(0), true},
{"zeroUint32Inited", uint32(0), true},
{"zeroUint64Inited", uint64(0), true},
{"zeroUintptrInited", uintptr(0), true},
{"zeroStringInited", "", true},
{"zeroBoolInited", false, true},
{"zeroFloat32Inited", float32(0), true},
{"zeroFloat64Inited", float64(0), true},
{"zeroComplex64Inited", complex64(0 + 0i), true},
{"zeroComplex128Inited", complex128(0 + 0i), true},
{"zeroStructInited", struct{}{}, true},
{"zeroPtrInited", (*int)(nil), true},
{"zeroCustomIntInited", customInt(0), true},
{"emptyMap", map[string]int{}, false},
{"emptyCustomStruct", customStruct{}, true},
{"zeroErrorInited", error(nil), true},
// Not zero values
{"nonZeroInt", nonZeroInt, false},
{"nonZeroInt8", nonZeroInt8, false},
{"nonZeroInt16", nonZeroInt16, false},
{"nonZeroInt32", nonZeroInt32, false},
{"nonZeroInt64", nonZeroInt64, false},
{"nonZeroUint", nonZeroUint, false},
{"nonZeroUint8", nonZeroUint8, false},
{"nonZeroUint16", nonZeroUint16, false},
{"nonZeroUint32", nonZeroUint32, false},
{"nonZeroUint64", nonZeroUint64, false},
{"nonZeroUintptr", nonZeroUintptr, false},
{"nonZeroString", nonZeroString, false},
{"nonZeroBool", nonZeroBool, false},
{"nonZeroFloat32", nonZeroFloat32, false},
{"nonZeroFloat64", nonZeroFloat64, false},
{"nonZeroComplex64", nonZeroComplex64, false},
{"nonZeroComplex128", nonZeroComplex128, false},
{"nonZeroSliceInt", nonZeroSliceInt, false},
{"nonZeroSliceInt8", nonZeroSliceInt8, false},
{"nonZeroSliceInt16", nonZeroSliceInt16, false},
{"nonZeroSliceInt32", nonZeroSliceInt32, false},
{"nonZeroSliceInt64", nonZeroSliceInt64, false},
{"nonZeroSliceUint", nonZeroSliceUint, false},
{"nonZeroSliceUint8", nonZeroSliceUint8, false},
{"nonZeroSliceUint16", nonZeroSliceUint16, false},
{"nonZeroSliceUint32", nonZeroSliceUint32, false},
{"nonZeroSliceUint64", nonZeroSliceUint64, false},
{"nonZeroSliceUintptr", nonZeroSliceUintptr, false},
{"nonZeroSliceString", nonZeroSliceString, false},
{"nonZeroSliceBool", nonZeroSliceBool, false},
{"nonZeroSliceFloat32", nonZeroSliceFloat32, false},
{"nonZeroSliceFloat64", nonZeroSliceFloat64, false},
{"nonZeroSliceComplex64", nonZeroSliceComplex64, false},
{"nonZeroSliceComplex128", nonZeroSliceComplex128, false},
{"nonZeroStruct", nonZeroStruct, false},
{"nonZeroPtr", nonZeroPtr, false},
{"nonZeroCustomInt", nonZeroCustomInt, false},
{"nonZeroMap", nonZeroMap, false},
{"nonZeroCustomStruct", nonZeroCustomStruct, false},
{"nonZeroError", nonZeroError, false},
{"nonZeroCustomSlice", nonZeroCustomSlice, false},
{"nonZeroCustomMap", nonZeroCustomMap, false},
// Initialized Zero value custom slice and map
{"zeroCustomSlice", []customInt{}, true},
{"zeroCustomMap", map[customInt]customInt{}, true},
// Initialized Not Zero value custom slice and map
{"notZeroCustomSlice", []customInt{1}, false},
{"notZeroCustomMap", map[customInt]customInt{1: 1}, false},
// Initialized Zero value custom slice and map pointers
{"zeroPtrCustomSlice", &[]customInt{}, true},
{"zeroPtrCustomMap", &map[customInt]customInt{}, true},
// Initialized Not Zero value custom slice and map pointers
{"notZeroPtrCustomSlice", &[]customInt{1}, false},
{"notZeroPtrCustomMap", &map[customInt]customInt{1: 1}, false},
{"notZeroInt", 1, false},
{"notZeroInt8", int8(1), false},
{"notZeroInt16", int16(1), false},
{"notZeroInt32", int32(1), false},
{"notZeroInt64", int64(1), false},
{"notZeroUint", uint(1), false},
{"notZeroUint8", uint8(1), false},
{"notZeroUint16", uint16(1), false},
{"notZeroUint32", uint32(1), false},
{"notZeroUint64", uint64(1), false},
{"notZeroUintptr", uintptr(1), false},
{"notZeroString", "1", false},
{"notZeroBool", true, false},
{"notZeroFloat32", float32(1), false},
{"notZeroFloat64", float64(1), false},
{"notZeroComplex64", complex64(1 + 0i), false},
{"notZeroComplex128", complex128(1 + 0i), false},
{"notZeroSliceBool", []bool{true}, false},
{"notZeroSliceInt", []int{1}, false},
{"notZeroSliceInt8", []int8{1}, false},
{"notZeroSliceInt16", []int16{1}, false},
{"notZeroSliceInt32", []int32{1}, false},
{"notZeroSliceInt64", []int64{1}, false},
{"notZeroSliceUint", []uint{1}, false},
{"notZeroSliceUint8", []uint8{1}, false},
{"notZeroSliceUint16", []uint16{1}, false},
{"notZeroSliceUint32", []uint32{1}, false},
{"notZeroSliceUint64", []uint64{1}, false},
{"notZeroSliceUintptr", []uintptr{1}, false},
{"notZeroSliceFloat32", []float32{1}, false},
{"notZeroSliceFloat64", []float64{1}, false},
{"notZeroSliceComplex64", []complex64{1 + 0i}, false},
{"notZeroSliceComplex128", []complex128{1 + 0i}, false},
{"notZeroSliceString", []string{"1"}, false},
{"notZeroSliceStruct", []struct{}{{}}, false},
{"notZeroSlicePtr", []*int{&zeroInt}, false},
{"notZeroSliceCustomInt", []customInt{1}, false},
{"notZeroSliceMap", []map[string]int{{"1": 1}}, false},
{"notZeroSliceCustomStruct", []customStruct{{1}}, false},
{"notZeroSliceError", []error{nil}, false},
{"notZeroSliceCustomSlice", [][]customInt{{1}}, false},
{"notZeroSliceCustomMap", []map[customInt]customInt{{1: 1}}, false},
// Empty slices
{"emptySliceBool", []bool{}, false},
{"emptySliceInt", []int{}, false},
{"emptySliceInt8", []int8{}, false},
{"emptySliceInt16", []int16{}, false},
{"emptySliceInt32", []int32{}, false},
{"emptySliceInt64", []int64{}, false},
{"emptySliceUint", []uint{}, false},
{"emptySliceUint8", []uint8{}, false},
{"emptySliceUint16", []uint16{}, false},
{"emptySliceUint32", []uint32{}, false},
{"emptySliceUint64", []uint64{}, false},
{"emptySliceUintptr", []uintptr{}, false},
{"emptySliceFloat32", []float32{}, false},
{"emptySliceFloat64", []float64{}, false},
{"emptySliceComplex64", []complex64{}, false},
{"emptySliceComplex128", []complex128{}, false},
{"emptySliceString", []string{}, false},
{"emptySliceStruct", []struct{}{}, false},
{"emptySlicePtr", []*int{}, false},
{"emptySliceCustomInt", []customInt{}, false},
{"emptySliceMap", []map[string]int{}, false},
{"emptySliceCustomStruct", []customStruct{}, false},
{"emptySliceError", []error{}, false},
{"emptySliceCustomSlice", [][]customInt{}, false},
{"emptySliceCustomMap", []map[customInt]customInt{}, false},
// Empty maps
{"emptyMap", map[string]int{}, false},
{"emptyCustomMap", map[customInt]customInt{}, false},
// Not empty maps
{"notEmptyMap", map[string]int{"1": 1}, false},
{"notEmptyCustomMap", map[customInt]customInt{1: 1}, false},
// Empty structs
{"emptyStruct", struct{}{}, true},
{"emptyCustomStruct", customStruct{}, true},
// Not empty structs
{"notEmptyStruct", struct{ A int }{1}, false},
{"notEmptyCustomStruct", customStruct{1}, false},
}

for _, tc := range testCases {
Expand All @@ -254,6 +217,10 @@ func TestIsZeroValue(t *testing.T) {

// go test -v -run=^$ -bench=Benchmark_Utils_IsVeroValue -benchmem -count=4
func Benchmark_Utils_IsVeroValue(b *testing.B) {
var zeroSlice []int
var zeroMap map[string]int
var zeroStruct struct{}

type customInt int
type customStruct struct {
A int
Expand All @@ -267,18 +234,22 @@ func Benchmark_Utils_IsVeroValue(b *testing.B) {
}{
{"nil", nil, true},
{"basic(int)", 0, true},
{"slice", []int{}, true},
{"map", map[string]int{}, true},
{"struct", struct{}{}, true},
{"zeroSlice", zeroSlice, true},
{"emptySlice", []int{}, true},
{"zeroMap", zeroMap, true},
{"emptyMap", map[string]int{}, true},
{"emptyMap", map[string]int{}, true},
{"zeroStruct", zeroStruct, true},
{"emptyStruct", struct{}{}, true},
{"ptr", (*int)(nil), true},
{"custom type", customInt(0), true},
{"custom struct", customStruct{}, true},
{"custom slice", customSlice{}, true},
{"custom map", customMap{}, true},
{"custom ptr slice", &customSlice{}, true},
{"custom ptr map", &customMap{}, true},
{"custom ptr slice with value", &customSlice{1}, false},
{"custom ptr map with value", &customMap{1: 1}, false},
{"customType", customInt(0), true},
{"customStruct", customStruct{}, true},
{"customSlice", customSlice{}, true},
{"customMap", customMap{}, true},
{"customPtrSlice", &customSlice{}, true},
{"customPtrMap", &customMap{}, true},
{"customPtrSliceWithValue", &customSlice{1}, false},
{"customPtrMapWithValue", &customMap{1: 1}, false},
}

for _, tc := range testCases {
Expand Down

0 comments on commit c228dbd

Please sign in to comment.