improve sliceValidateError.Error performance using switch and strings.Builder (#2765)
fix missing nil pointer check use simpler switch case add missing tests use for-loop instead of range add benchmark test codes
This commit is contained in:
		@ -20,15 +20,27 @@ type defaultValidator struct {
 | 
			
		||||
 | 
			
		||||
type sliceValidateError []error
 | 
			
		||||
 | 
			
		||||
// Error concatenates all error elements in sliceValidateError into a single string separated by \n.
 | 
			
		||||
func (err sliceValidateError) Error() string {
 | 
			
		||||
	var errMsgs []string
 | 
			
		||||
	for i, e := range err {
 | 
			
		||||
		if e == nil {
 | 
			
		||||
			continue
 | 
			
		||||
	n := len(err)
 | 
			
		||||
	switch n {
 | 
			
		||||
	case 0:
 | 
			
		||||
		return ""
 | 
			
		||||
	default:
 | 
			
		||||
		var b strings.Builder
 | 
			
		||||
		if err[0] != nil {
 | 
			
		||||
			fmt.Fprintf(&b, "[%d]: %s", 0, err[0].Error())
 | 
			
		||||
		}
 | 
			
		||||
		errMsgs = append(errMsgs, fmt.Sprintf("[%d]: %s", i, e.Error()))
 | 
			
		||||
		if n > 1 {
 | 
			
		||||
			for i := 1; i < n; i++ {
 | 
			
		||||
				if err[i] != nil {
 | 
			
		||||
					b.WriteString("\n")
 | 
			
		||||
					fmt.Fprintf(&b, "[%d]: %s", i, err[i].Error())
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return b.String()
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(errMsgs, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ StructValidator = &defaultValidator{}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										20
									
								
								binding/default_validator_benchmark_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								binding/default_validator_benchmark_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
package binding
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func BenchmarkSliceValidateError(b *testing.B) {
 | 
			
		||||
	const size int = 100
 | 
			
		||||
	for i := 0; i < b.N; i++ {
 | 
			
		||||
		e := make(sliceValidateError, size)
 | 
			
		||||
		for j := 0; j < size; j++ {
 | 
			
		||||
			e[j] = errors.New(strconv.Itoa(j))
 | 
			
		||||
		}
 | 
			
		||||
		if len(e.Error()) == 0 {
 | 
			
		||||
			b.Errorf("error")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -16,6 +16,26 @@ func TestSliceValidateError(t *testing.T) {
 | 
			
		||||
		want string
 | 
			
		||||
	}{
 | 
			
		||||
		{"has nil elements", sliceValidateError{errors.New("test error"), nil}, "[0]: test error"},
 | 
			
		||||
		{"has zero elements", sliceValidateError{}, ""},
 | 
			
		||||
		{"has one element", sliceValidateError{errors.New("test one error")}, "[0]: test one error"},
 | 
			
		||||
		{"has two elements",
 | 
			
		||||
			sliceValidateError{
 | 
			
		||||
				errors.New("first error"),
 | 
			
		||||
				errors.New("second error"),
 | 
			
		||||
			},
 | 
			
		||||
			"[0]: first error\n[1]: second error",
 | 
			
		||||
		},
 | 
			
		||||
		{"has many elements",
 | 
			
		||||
			sliceValidateError{
 | 
			
		||||
				errors.New("first error"),
 | 
			
		||||
				errors.New("second error"),
 | 
			
		||||
				nil,
 | 
			
		||||
				nil,
 | 
			
		||||
				nil,
 | 
			
		||||
				errors.New("last error"),
 | 
			
		||||
			},
 | 
			
		||||
			"[0]: first error\n[1]: second error\n[5]: last error",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user