Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 54 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,60 +65,61 @@ Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usa

Benchmarks
------
###### Run on i5-7600 16 GB DDR4-2400 using Go version go1.8 linux/amd64
###### Run on Dell XPS 15 i7-7700HQ 32GB Go version go1.8.3 linux/amd64
```go
BenchmarkFieldSuccess-4 20000000 74.3 ns/op 0 B/op 0 allocs/op
BenchmarkFieldSuccessParallel-4 50000000 31.5 ns/op 0 B/op 0 allocs/op
BenchmarkFieldFailure-4 3000000 556 ns/op 208 B/op 4 allocs/op
BenchmarkFieldFailureParallel-4 20000000 88.7 ns/op 208 B/op 4 allocs/op
BenchmarkFieldDiveSuccess-4 2000000 630 ns/op 201 B/op 11 allocs/op
BenchmarkFieldDiveSuccessParallel-4 10000000 173 ns/op 201 B/op 11 allocs/op
BenchmarkFieldDiveFailure-4 1000000 1350 ns/op 412 B/op 16 allocs/op
BenchmarkFieldDiveFailureParallel-4 5000000 250 ns/op 412 B/op 16 allocs/op
BenchmarkFieldCustomTypeSuccess-4 10000000 202 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeSuccessParallel-4 20000000 63.5 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailure-4 5000000 568 ns/op 208 B/op 4 allocs/op
BenchmarkFieldCustomTypeFailureParallel-4 20000000 87.5 ns/op 208 B/op 4 allocs/op
BenchmarkFieldOrTagSuccess-4 2000000 703 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTagSuccessParallel-4 3000000 447 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTagFailure-4 3000000 604 ns/op 224 B/op 5 allocs/op
BenchmarkFieldOrTagFailureParallel-4 5000000 353 ns/op 224 B/op 5 allocs/op
BenchmarkStructLevelValidationSuccess-4 10000000 190 ns/op 32 B/op 2 allocs/op
BenchmarkStructLevelValidationSuccessParallel-4 30000000 59.9 ns/op 32 B/op 2 allocs/op
BenchmarkStructLevelValidationFailure-4 2000000 705 ns/op 304 B/op 8 allocs/op
BenchmarkStructLevelValidationFailureParallel-4 10000000 146 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4 5000000 361 ns/op 32 B/op 2 allocs/op
BenchmarkStructSimpleCustomTypeSuccessParallel-4 20000000 101 ns/op 32 B/op 2 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4 1000000 1210 ns/op 424 B/op 9 allocs/op
BenchmarkStructSimpleCustomTypeFailureParallel-4 10000000 196 ns/op 440 B/op 10 allocs/op
BenchmarkStructFilteredSuccess-4 2000000 757 ns/op 288 B/op 9 allocs/op
BenchmarkStructFilteredSuccessParallel-4 10000000 167 ns/op 288 B/op 9 allocs/op
BenchmarkStructFilteredFailure-4 3000000 619 ns/op 256 B/op 7 allocs/op
BenchmarkStructFilteredFailureParallel-4 10000000 134 ns/op 256 B/op 7 allocs/op
BenchmarkStructPartialSuccess-4 2000000 687 ns/op 256 B/op 6 allocs/op
BenchmarkStructPartialSuccessParallel-4 10000000 159 ns/op 256 B/op 6 allocs/op
BenchmarkStructPartialFailure-4 1000000 1281 ns/op 480 B/op 11 allocs/op
BenchmarkStructPartialFailureParallel-4 10000000 218 ns/op 480 B/op 11 allocs/op
BenchmarkStructExceptSuccess-4 1000000 1041 ns/op 496 B/op 12 allocs/op
BenchmarkStructExceptSuccessParallel-4 10000000 140 ns/op 240 B/op 5 allocs/op
BenchmarkStructExceptFailure-4 1000000 1014 ns/op 464 B/op 10 allocs/op
BenchmarkStructExceptFailureParallel-4 10000000 201 ns/op 464 B/op 10 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4 5000000 364 ns/op 72 B/op 3 allocs/op
BenchmarkStructSimpleCrossFieldSuccessParallel-4 20000000 103 ns/op 72 B/op 3 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4 2000000 789 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCrossFieldFailureParallel-4 10000000 174 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 3000000 522 ns/op 80 B/op 4 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-4 10000000 146 ns/op 80 B/op 4 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4 2000000 879 ns/op 320 B/op 9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-4 10000000 225 ns/op 320 B/op 9 allocs/op
BenchmarkStructSimpleSuccess-4 10000000 223 ns/op 0 B/op 0 allocs/op
BenchmarkStructSimpleSuccessParallel-4 20000000 63.3 ns/op 0 B/op 0 allocs/op
BenchmarkStructSimpleFailure-4 2000000 1097 ns/op 424 B/op 9 allocs/op
BenchmarkStructSimpleFailureParallel-4 10000000 182 ns/op 424 B/op 9 allocs/op
BenchmarkStructComplexSuccess-4 1000000 1362 ns/op 128 B/op 8 allocs/op
BenchmarkStructComplexSuccessParallel-4 5000000 359 ns/op 128 B/op 8 allocs/op
BenchmarkStructComplexFailure-4 300000 6446 ns/op 3040 B/op 53 allocs/op
BenchmarkStructComplexFailureParallel-4 1000000 1203 ns/op 3040 B/op 53 allocs/op
go test -run=XXX -bench=. -benchmem=true
BenchmarkFieldSuccess-8 20000000 88.3 ns/op 0 B/op 0 allocs/op
BenchmarkFieldSuccessParallel-8 50000000 30.4 ns/op 0 B/op 0 allocs/op
BenchmarkFieldFailure-8 3000000 428 ns/op 208 B/op 4 allocs/op
BenchmarkFieldFailureParallel-8 20000000 96.0 ns/op 208 B/op 4 allocs/op
BenchmarkFieldDiveSuccess-8 2000000 695 ns/op 201 B/op 11 allocs/op
BenchmarkFieldDiveSuccessParallel-8 10000000 205 ns/op 201 B/op 11 allocs/op
BenchmarkFieldDiveFailure-8 1000000 1083 ns/op 412 B/op 16 allocs/op
BenchmarkFieldDiveFailureParallel-8 5000000 278 ns/op 413 B/op 16 allocs/op
BenchmarkFieldCustomTypeSuccess-8 10000000 229 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 72.4 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailure-8 5000000 377 ns/op 208 B/op 4 allocs/op
BenchmarkFieldCustomTypeFailureParallel-8 20000000 93.0 ns/op 208 B/op 4 allocs/op
BenchmarkFieldOrTagSuccess-8 2000000 767 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTagSuccessParallel-8 3000000 425 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTagFailure-8 2000000 548 ns/op 224 B/op 5 allocs/op
BenchmarkFieldOrTagFailureParallel-8 3000000 411 ns/op 224 B/op 5 allocs/op
BenchmarkStructLevelValidationSuccess-8 10000000 219 ns/op 32 B/op 2 allocs/op
BenchmarkStructLevelValidationSuccessParallel-8 20000000 69.2 ns/op 32 B/op 2 allocs/op
BenchmarkStructLevelValidationFailure-8 2000000 628 ns/op 304 B/op 8 allocs/op
BenchmarkStructLevelValidationFailureParallel-8 10000000 165 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-8 3000000 411 ns/op 32 B/op 2 allocs/op
BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 122 ns/op 32 B/op 2 allocs/op
BenchmarkStructSimpleCustomTypeFailure-8 1000000 1022 ns/op 424 B/op 9 allocs/op
BenchmarkStructSimpleCustomTypeFailureParallel-8 10000000 228 ns/op 440 B/op 10 allocs/op
BenchmarkStructFilteredSuccess-8 2000000 737 ns/op 288 B/op 9 allocs/op
BenchmarkStructFilteredSuccessParallel-8 10000000 192 ns/op 288 B/op 9 allocs/op
BenchmarkStructFilteredFailure-8 3000000 583 ns/op 256 B/op 7 allocs/op
BenchmarkStructFilteredFailureParallel-8 10000000 152 ns/op 256 B/op 7 allocs/op
BenchmarkStructPartialSuccess-8 2000000 731 ns/op 256 B/op 6 allocs/op
BenchmarkStructPartialSuccessParallel-8 10000000 173 ns/op 256 B/op 6 allocs/op
BenchmarkStructPartialFailure-8 1000000 1164 ns/op 480 B/op 11 allocs/op
BenchmarkStructPartialFailureParallel-8 5000000 253 ns/op 480 B/op 11 allocs/op
BenchmarkStructExceptSuccess-8 1000000 1337 ns/op 496 B/op 12 allocs/op
BenchmarkStructExceptSuccessParallel-8 10000000 153 ns/op 240 B/op 5 allocs/op
BenchmarkStructExceptFailure-8 2000000 954 ns/op 464 B/op 10 allocs/op
BenchmarkStructExceptFailureParallel-8 5000000 234 ns/op 464 B/op 10 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-8 3000000 420 ns/op 72 B/op 3 allocs/op
BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 125 ns/op 72 B/op 3 allocs/op
BenchmarkStructSimpleCrossFieldFailure-8 2000000 790 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 205 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 611 ns/op 80 B/op 4 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 172 ns/op 80 B/op 4 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 1112 ns/op 320 B/op 9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 258 ns/op 320 B/op 9 allocs/op
BenchmarkStructSimpleSuccess-8 5000000 263 ns/op 0 B/op 0 allocs/op
BenchmarkStructSimpleSuccessParallel-8 20000000 83.1 ns/op 0 B/op 0 allocs/op
BenchmarkStructSimpleFailure-8 2000000 964 ns/op 424 B/op 9 allocs/op
BenchmarkStructSimpleFailureParallel-8 10000000 212 ns/op 424 B/op 9 allocs/op
BenchmarkStructComplexSuccess-8 1000000 1504 ns/op 128 B/op 8 allocs/op
BenchmarkStructComplexSuccessParallel-8 3000000 427 ns/op 128 B/op 8 allocs/op
BenchmarkStructComplexFailure-8 300000 7585 ns/op 3041 B/op 53 allocs/op
BenchmarkStructComplexFailureParallel-8 1000000 1387 ns/op 3041 B/op 53 allocs/op
```

Complementary Software
Expand Down
20 changes: 15 additions & 5 deletions baked_in.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package validator

import (
"context"
"fmt"
"net"
"net/url"
Expand All @@ -10,13 +11,22 @@ import (
"unicode/utf8"
)

// Func accepts all values needed for file and cross field validation
// fl = FieldLevel validation helper
// field = field value for validation
// fieldType = fields
// param = parameter used in validation i.e. gt=0 param would be 0
// Func accepts a FieldLevel interface for all validation needs
type Func func(fl FieldLevel) bool

// FuncCtx accepts a context.Context and FieldLevel interface for all validation needs
type FuncCtx func(ctx context.Context, fl FieldLevel) bool

// wrapFunc wraps noramal Func makes it compatible with FuncCtx
func wrapFunc(fn Func) FuncCtx {
if fn == nil {
return nil // be sure not to wrap a bad function.
}
return func(ctx context.Context, fl FieldLevel) bool {
return fn(fl)
}
}

var (
restrictedTags = map[string]struct{}{
diveTag: {},
Expand Down
4 changes: 2 additions & 2 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (tc *tagCache) Set(key string, value *cTag) {
type cStruct struct {
name string
fields []*cField
fn StructLevelFunc
fn StructLevelFuncCtx
}

type cField struct {
Expand All @@ -90,7 +90,7 @@ type cTag struct {
hasAlias bool
typeof tagType
hasTag bool
fn Func
fn FuncCtx
next *cTag
}

Expand Down
18 changes: 15 additions & 3 deletions struct_level.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
package validator

import "reflect"
import (
"context"
"reflect"
)

// StructLevelFunc accepts all values needed for struct level validation
type StructLevelFunc func(sl StructLevel)

// StructLevelFuncCtx accepts all values needed for struct level validation
// but also allows passing of contextual validation information vi context.Context.
type StructLevelFuncCtx func(ctx context.Context, sl StructLevel)

// wrapStructLevelFunc wraps noramal StructLevelFunc makes it compatible with StructLevelFuncCtx
func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx {
return func(ctx context.Context, sl StructLevel) {
fn(sl)
}
}

// StructLevel contains all the information and helper functions
// to validate a struct
type StructLevel interface {
Expand All @@ -21,8 +35,6 @@ type StructLevel interface {
Parent() reflect.Value

// returns the current struct.
// this is not needed when implementing 'Validatable' interface,
// only when a StructLevel is registered
Current() reflect.Value

// ExtractType gets the actual underlying type of field value.
Expand Down
19 changes: 10 additions & 9 deletions validator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package validator

import (
"context"
"fmt"
"reflect"
"strconv"
Expand Down Expand Up @@ -34,7 +35,7 @@ type validate struct {
}

// parent and current will be the same the first run of validateStruct
func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {

cs, ok := v.v.structCache.Get(typ)
if !ok {
Expand Down Expand Up @@ -78,7 +79,7 @@ func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, t
}
}

v.traverseField(parent, current.Field(f.idx), ns, structNs, f, f.cTags)
v.traverseField(ctx, parent, current.Field(f.idx), ns, structNs, f, f.cTags)
}
}

Expand All @@ -92,12 +93,12 @@ func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, t
v.ns = ns
v.actualNs = structNs

cs.fn(v)
cs.fn(ctx, v)
}
}

// traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {
func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {

var typ reflect.Type
var kind reflect.Kind
Expand Down Expand Up @@ -192,7 +193,7 @@ func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns
structNs = append(append(structNs, cf.name...), '.')
}

v.validateStruct(current, current, typ, ns, structNs, ct)
v.validateStruct(ctx, current, current, typ, ns, structNs, ct)
return
}
}
Expand Down Expand Up @@ -261,7 +262,7 @@ OUTER:
reusableCF.altName = string(v.misc)
}

v.traverseField(parent, current.Index(i), ns, structNs, reusableCF, ct)
v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct)
}

case reflect.Map:
Expand Down Expand Up @@ -291,7 +292,7 @@ OUTER:
reusableCF.altName = string(v.misc)
}

v.traverseField(parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
}

default:
Expand All @@ -314,7 +315,7 @@ OUTER:
v.cf = cf
v.ct = ct

if ct.fn(v) {
if ct.fn(ctx, v) {

// drain rest of the 'or' values, then continue or leave
for {
Expand Down Expand Up @@ -407,7 +408,7 @@ OUTER:
// v.ns = ns
// v.actualNs = structNs

if !ct.fn(v) {
if !ct.fn(ctx, v) {

v.str1 = string(append(ns, cf.altName...))

Expand Down
Loading