Created
August 6, 2014 09:35
-
-
Save joshlf/dcfd1eaf8ab4677d2ad8 to your computer and use it in GitHub Desktop.
Randomly generate instances of arbitrary types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
The *Rand type here is essentially equivalent to the standard library's | |
math/rand.Rand, albeit with a few additions for types (to randomly | |
generate strings, bools, etc). | |
*/ | |
package rand | |
import ( | |
"reflect" | |
"sync" | |
"unsafe" | |
) | |
// Type of a function which initializes val to a random value | |
type generator func(r *Rand, val reflect.Value) | |
var generatorCache struct { | |
sync.RWMutex | |
m map[reflect.Type]generator | |
} | |
func init() { generatorCache.m = make(map[reflect.Type]generator) } | |
func getGenerator(typ reflect.Type) generator { | |
generatorCache.RLock() | |
gen, ok := generatorCache.m[typ] | |
generatorCache.RUnlock() | |
if !ok { | |
gen = createGenerator(typ) | |
generatorCache.Lock() | |
generatorCache.m[typ] = gen | |
generatorCache.Unlock() | |
} | |
return gen | |
} | |
// NewInstance returns a randomly initialized instance | |
// of type typ. | |
func (r *Rand) NewInstance(typ reflect.Type) interface{} { | |
val := reflect.New(typ).Elem() | |
getGenerator(typ)(r, val) | |
return val.Interface() | |
} | |
// Initialize initializes val with a random value. If val is | |
// a pointer, the value it points it will be initialized | |
// with a random value. If val is a slice, each of its | |
// elements will be initialized with random values. If val | |
// has any other type, Initialize will panic. | |
func (r *Rand) Initialize(val interface{}) { | |
typ := reflect.TypeOf(val) | |
v := reflect.ValueOf(val) | |
switch typ.Kind() { | |
case reflect.Ptr: | |
typ = typ.Elem() | |
getGenerator(typ)(r, v.Elem()) | |
case reflect.Slice: | |
typ = typ.Elem() | |
gen := getGenerator(typ) | |
len := v.Len() | |
for i := 0; i < len; i++ { | |
elem := v.Index(i) | |
gen(r, elem) | |
} | |
default: | |
panic("rand.Rand.Initialize called on illegal type") | |
} | |
} | |
// typ is the type to generate. The generator | |
// will accept a settable value, so typ should | |
// be the type itself. If typ is a pointer type, | |
// the resultant generator will be a no-op. | |
func createGenerator(typ reflect.Type) generator { | |
switch { | |
case typ.Kind() <= reflect.Complex128: // bool, ints, uints, floats, complexes | |
return basicGenerators[typ.Kind()] | |
case typ.Kind() == reflect.Array: | |
return createArrayGenerator(typ) | |
case typ.Kind() == reflect.Map: | |
return createMapGenerator(typ) | |
case typ.Kind() == reflect.Slice: | |
return createSliceGenerator(typ) | |
case typ.Kind() == reflect.String: | |
return stringGenerator | |
case typ.Kind() == reflect.Struct: | |
return createStructGenerator(typ) | |
case typ.Kind() == reflect.UnsafePointer: | |
return unsafePointerGenerator | |
default: | |
return noOpGenerator | |
} | |
} | |
func createArrayGenerator(typ reflect.Type) generator { | |
len := typ.Len() | |
elemGenerator := createGenerator(typ.Elem()) | |
return func(r *Rand, val reflect.Value) { | |
for i := 0; i < len; i++ { | |
elemGenerator(r, val.Index(i)) | |
} | |
} | |
} | |
func createMapGenerator(typ reflect.Type) generator { | |
keyType := typ.Key() | |
valType := typ.Elem() | |
keyGenerator := createGenerator(keyType) | |
valGenerator := createGenerator(valType) | |
return func(r *Rand, val reflect.Value) { | |
n := int(r.ExpFloat64()) | |
for i := 0; i < n; i++ { | |
k, v := reflect.New(keyType).Elem(), reflect.New(valType).Elem() | |
keyGenerator(r, k) | |
valGenerator(r, v) | |
val.SetMapIndex(k, v) | |
} | |
} | |
} | |
func createSliceGenerator(typ reflect.Type) generator { | |
elemGenerator := createGenerator(typ.Elem()) | |
return func(r *Rand, val reflect.Value) { | |
len := int(r.ExpFloat64()) | |
slc := reflect.MakeSlice(typ, len, len) | |
for i := 0; i < len; i++ { | |
elem := slc.Index(i) | |
elemGenerator(r, elem) | |
} | |
val.Set(slc) | |
} | |
} | |
func stringGenerator(r *Rand, val reflect.Value) { | |
s := r.String() | |
val.SetString(s) | |
} | |
func createStructGenerator(typ reflect.Type) generator { | |
n := typ.NumField() | |
fieldGenerators := make([]generator, n) | |
// Contains indexes to generate; | |
// ignores unexported fields | |
fieldIndexes := make([]int, 0) | |
for i := 0; i < n; i++ { | |
if isExported(typ.Field(i)) { | |
fieldGenerators[i] = createGenerator(typ.Field(i).Type) | |
fieldIndexes = append(fieldIndexes, i) | |
} | |
} | |
return func(r *Rand, val reflect.Value) { | |
for _, i := range fieldIndexes { | |
fieldGenerators[i](r, val.Field(i)) | |
} | |
} | |
} | |
func unsafePointerGenerator(r *Rand, val reflect.Value) { | |
val.SetPointer(unsafe.Pointer(uintptr((r.Uint64())))) | |
} | |
func noOpGenerator(r *Rand, val reflect.Value) {} | |
var basicGenerators = map[reflect.Kind]generator{ | |
reflect.Bool: func(r *Rand, val reflect.Value) { | |
val.SetBool(r.Bool()) | |
}, | |
reflect.Int: func(r *Rand, val reflect.Value) { | |
val.SetInt(int64(r.Int())) | |
}, | |
reflect.Int8: func(r *Rand, val reflect.Value) { | |
val.SetInt(int64(r.Int8())) | |
}, | |
reflect.Int16: func(r *Rand, val reflect.Value) { | |
val.SetInt(int64(r.Int16())) | |
}, | |
reflect.Int32: func(r *Rand, val reflect.Value) { | |
val.SetInt(int64(r.Int32())) | |
}, | |
reflect.Int64: func(r *Rand, val reflect.Value) { | |
val.SetInt(int64(r.Int64())) | |
}, | |
reflect.Uint: func(r *Rand, val reflect.Value) { | |
val.SetUint(uint64(r.Uint())) | |
}, | |
reflect.Uint8: func(r *Rand, val reflect.Value) { | |
val.SetUint(uint64(r.Uint8())) | |
}, | |
reflect.Uint16: func(r *Rand, val reflect.Value) { | |
val.SetUint(uint64(r.Uint16())) | |
}, | |
reflect.Uint32: func(r *Rand, val reflect.Value) { | |
val.SetUint(uint64(r.Uint32())) | |
}, | |
reflect.Uint64: func(r *Rand, val reflect.Value) { | |
val.SetUint(uint64(r.Uint64())) | |
}, | |
reflect.Uintptr: func(r *Rand, val reflect.Value) { | |
val.SetUint(uint64(r.Uint())) | |
}, | |
reflect.Float32: func(r *Rand, val reflect.Value) { | |
val.SetFloat(float64(r.Float32())) | |
}, | |
reflect.Float64: func(r *Rand, val reflect.Value) { | |
val.SetFloat(float64(r.Float64())) | |
}, | |
reflect.Complex64: func(r *Rand, val reflect.Value) { | |
val.SetComplex(complex128(r.Complex64())) | |
}, | |
reflect.Complex128: func(r *Rand, val reflect.Value) { | |
val.SetComplex(complex128(r.Complex128())) | |
}, | |
} | |
func isExported(field reflect.StructField) bool { | |
// See http://golang.org/pkg/reflect/#StructField | |
return field.PkgPath == "" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment