Skip to content

Instantly share code, notes, and snippets.

@joshlf
Created August 6, 2014 09:35
Show Gist options
  • Save joshlf/dcfd1eaf8ab4677d2ad8 to your computer and use it in GitHub Desktop.
Save joshlf/dcfd1eaf8ab4677d2ad8 to your computer and use it in GitHub Desktop.
Randomly generate instances of arbitrary types
/*
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