func (upckr *unpacker) unpackBlob(count int, isMapKey bool) (interface{}, error) { theType := upckr.buffer[upckr.offset] & 0xff upckr.offset++ count-- var val interface{} switch theType { case ParticleType.STRING: val = string(upckr.buffer[upckr.offset : upckr.offset+count]) case ParticleType.BLOB: if isMapKey { b := reflect.Indirect(reflect.New(reflect.ArrayOf(count, reflect.TypeOf(byte(0))))) reflect.Copy(b, reflect.ValueOf(upckr.buffer[upckr.offset:upckr.offset+count])) val = b.Interface() } else { b := make([]byte, count) copy(b, upckr.buffer[upckr.offset:upckr.offset+count]) val = b } case ParticleType.GEOJSON: val = NewGeoJSONValue(string(upckr.buffer[upckr.offset : upckr.offset+count])) default: panic(NewAerospikeError(SERIALIZE_ERROR, fmt.Sprintf("Error while unpacking BLOB. Type-header with code `%d` not recognized.", theType))) } upckr.offset += count return val, nil }
func main() { t := reflect.TypeOf(3) arr := reflect.ArrayOf(10, t) inst := reflect.New(arr).Interface().(*[10]int) for i := 0; i < 10; i++ { inst[i] = i } fmt.Println(inst) }
// generate fixed size array func ArrayOfFunction(env *Glisp, name string, args []Sexp) (Sexp, error) { if len(args) != 2 { return SexpNull, fmt.Errorf("insufficient arguments to ([size] regtype) array constructor. use: " + "([size...] a-regtype)\n") } sz := 0 Q("args = %#v in ArrayOfFunction", args) switch ar := args[1].(type) { case *SexpArray: if len(ar.Val) == 0 { return SexpNull, fmt.Errorf("at least one size must be specified in array constructor; e.g. ([size ...] regtype)") } asInt, isInt := ar.Val[0].(*SexpInt) if !isInt { return SexpNull, fmt.Errorf("size must be an int (not %T) in array constructor; e.g. ([size ...] regtype)", ar.Val[0]) } sz = int(asInt.Val) // TODO: implement multiple dimensional arrays (matrixes etc). default: return SexpNull, fmt.Errorf("at least one size must be specified in array constructor; e.g. ([size ...] regtype)") } var rt *RegisteredType switch arg := args[0].(type) { case *RegisteredType: rt = arg case *SexpHash: rt = arg.GoStructFactory default: return SexpNull, fmt.Errorf("argument tx in (%s x) was not regtype, "+ "instead type %T displaying as '%v' ", name, arg, arg.SexpString(nil)) } //Q("arrayOf arg = '%s' with type %T", args[0].SexpString(nil), args[0]) derivedType := reflect.ArrayOf(sz, rt.TypeCache) arrayRt := NewRegisteredType(func(env *Glisp) (interface{}, error) { return reflect.New(derivedType), nil }) arrayRt.DisplayAs = fmt.Sprintf("(%s %s)", name, rt.DisplayAs) arrayName := "arrayOf" + rt.RegisteredName GoStructRegistry.RegisterUserdef(arrayName, arrayRt, false) return arrayRt, nil }
// Parses str into an array of typeOfElem. // Returns a pointer to an array populated with comma separated values parsed from str. // Implements tags.ArrayMaker func (valueMaker) MakeArray(str string, typeOfElem reflect.Type) (bool, uintptr, error) { elements := strings.Split(str, ",") array := reflect.New(reflect.ArrayOf(len(elements), typeOfElem)) errs := make([]string, 0, 0) for i, str := range elements { elem := reflect.New(typeOfElem) if _, err := Injector.Inject(elem, str); err != nil { errs = append(errs, fmt.Sprintf("element %d - %s", i, err.Error())) } else { // Note that if Inject did not set the value of elem then it will be zero valued. array.Index(i).Set(elem) } } if len(errs) > 0 { return false, 0, errors.New(fmt.Sprintf("failed to parse list: %s", strings.Join(errs, "; "))) } return true, array.Pointer(), nil }
func reflectType(t types.Type) reflect.Type { switch t := t.(type) { case *types.Tuple: // TODO case *types.Basic: return reflectBasic(t.Kind()) case *types.Pointer: return reflect.PtrTo(reflectType(t.Elem())) case *types.Slice: return reflect.SliceOf(reflectType(t.Elem())) case *types.Array: return reflect.ArrayOf(int(t.Len()), reflectType(t.Elem())) case *types.Named: if st, ok := simdInfo(t); ok { return st.t } if sse2, ok := sse2Info(t); ok { return sse2.t } } ice(fmt.Sprintf("error unknown type:\"%v\"", t)) panic("") }
func arrayOf(count int, elem reflect.Type) reflect.Type { return reflect.ArrayOf(count, elem) }
func encode(buf *bytes.Buffer, v reflect.Value) error { switch v.Kind() { case reflect.Invalid: buf.WriteString("nil") case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: fmt.Fprintf(buf, "%d", v.Int()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: fmt.Fprintf(buf, "%d", v.Uint()) case reflect.Float32, reflect.Float64: fmt.Fprintf(buf, "%v", v.Float()) case reflect.Complex64, reflect.Complex128: fmt.Fprintf(buf, "#C(%v %v)", real(v.Complex()), imag(v.Complex())) case reflect.Bool: if v.Bool() { fmt.Fprintf(buf, "t") } else { fmt.Fprintf(buf, "nil") } case reflect.String: fmt.Fprintf(buf, "%q", v.String()) case reflect.Ptr: return encode(buf, v.Elem()) case reflect.Array, reflect.Slice: // (value ...) buf.WriteByte('(') lines := bytes.Split(buf.Bytes(), []byte("\n")) bcounts := len(lines[len(lines)-1]) - 1 arrayBuf := bytes.NewBuffer([]byte{}) for i := 0; i < v.Len(); i++ { if i > 0 { arrayBuf.WriteByte(' ') } if err := encode(arrayBuf, v.Index(i)); err != nil { return err } if v.Type() == reflect.SliceOf(reflect.TypeOf("")) || v.Type() == reflect.ArrayOf(v.Len(), reflect.TypeOf("")) { fmt.Fprintf(arrayBuf, "\n%s", strings.Repeat(" ", bcounts)) } } trim := bytes.TrimSpace(arrayBuf.Bytes()) arrayBuf = bytes.NewBuffer(trim) fmt.Fprintf(buf, "%s)", arrayBuf) case reflect.Struct: // ((name value) ...) buf.WriteByte('(') for i := 0; i < v.NumField(); i++ { if i > 0 { buf.WriteByte(' ') } fmt.Fprintf(buf, "(%s ", v.Type().Field(i).Name) if err := encode(buf, v.Field(i)); err != nil { return err } fmt.Fprintf(buf, ")\n") } buf = bytes.NewBuffer(bytes.TrimSpace(buf.Bytes())) fmt.Fprintf(buf, ")") case reflect.Map: // ((key value) ...) buf.WriteByte('(') lines := bytes.Split(buf.Bytes(), []byte("\n")) bcounts := len(lines[len(lines)-1]) - 1 mapBuf := bytes.NewBuffer([]byte{}) for i, key := range v.MapKeys() { if i > 0 { mapBuf.WriteByte(' ') } mapBuf.WriteByte('(') if err := encode(mapBuf, key); err != nil { return err } mapBuf.WriteByte(' ') if err := encode(mapBuf, v.MapIndex(key)); err != nil { return err } fmt.Fprintf(mapBuf, ")\n%s", strings.Repeat(" ", bcounts)) } trim := bytes.TrimSpace(mapBuf.Bytes()) fmt.Fprintf(buf, "%s)", trim) case reflect.Interface: if v.IsNil() { fmt.Fprintf(buf, "nil") } else { fmt.Fprintf(buf, "(\"%v\" ", v.Elem().Type()) if err := encode(buf, v.Elem()); err != nil { return err } fmt.Fprint(buf, ")") } default: // func, interface return fmt.Errorf("unsupported type: %s", v.Type()) } return nil }
// typeFromForm returns a FITS Type corresponding to a FITS TFORM string func typeFromForm(form string, htype HDUType) (Type, error) { var err error var typ Type switch htype { case BINARY_TBL: j := strings.IndexAny(form, "PQABCDEIJKLMX") if j < 0 { return typ, fmt.Errorf("fitsio: invalid TFORM format (%s)", form) } repeat := 1 if j > 0 { r, err := strconv.ParseInt(form[:j], 10, 32) if err != nil { return typ, fmt.Errorf("fitsio: invalid TFORM format (%s)", form) } repeat = int(r) } slice := false dsize := 0 hsize := 0 switch form[j] { case 'P': j += 1 slice = true dsize = 2 * 4 case 'Q': j += 1 slice = true dsize = 2 * 8 } tc, ok := g_fits2tc[BINARY_TBL][form[j]] if !ok { return typ, fmt.Errorf("fitsio: invalid TFORM format (%s) (no typecode found)", form) } rt, ok := g_fits2go[BINARY_TBL][form[j]] if !ok { return typ, fmt.Errorf("fitsio: invalid TFORM format (%s) (no Type found)", form) } elemsz := 0 switch form[j] { case 'A': elemsz = repeat repeat = 1 case 'X': elemsz = 1 const nbits = 8 sz := repeat + (nbits-(repeat%nbits))%nbits repeat = sz / nbits case 'L', 'B': elemsz = 1 case 'I': elemsz = 2 case 'J', 'E': elemsz = 4 case 'K', 'D', 'C': elemsz = 8 case 'M': elemsz = 16 } switch slice { case true: hsize = elemsz typ = Type{ tc: -tc, len: repeat, dsize: dsize, hsize: hsize, gotype: reflect.SliceOf(rt), } case false: dsize = elemsz if repeat > 1 { typ = Type{ tc: tc, len: repeat, dsize: dsize, hsize: hsize, gotype: reflect.ArrayOf(repeat, rt), } } else { typ = Type{ tc: tc, len: repeat, dsize: dsize, hsize: hsize, gotype: rt, } } } if typ.dsize*typ.len == 0 { if form != "0A" { return typ, fmt.Errorf("fitsio: invalid dtype! form=%q typ=%#v\n", form, typ) } } case ASCII_TBL: // fmt.Printf("### form %q\n", form) j := strings.IndexAny(form, "ADEFI") if j < 0 { return typ, fmt.Errorf("fitsio: invalid TFORM format (%s)", form) } j = strings.Index(form, ".") if j == -1 { j = len(form) } repeat := 1 r, err := strconv.ParseInt(form[1:j], 10, 32) if err != nil { return typ, fmt.Errorf("fitsio: invalid TFORM format (%s)", form) } repeat = int(r) tc, ok := g_fits2tc[ASCII_TBL][form[0]] if !ok { return typ, fmt.Errorf("fitsio: invalid TFORM format (%s) (no typecode found)", form) } rt, ok := g_fits2go[ASCII_TBL][form[0]] if !ok { return typ, fmt.Errorf("fitsio: invalid TFORM format (%s) (no Type found)", form) } dsize := 0 hsize := 0 switch form[0] { case 'A': dsize = repeat repeat = 1 case 'I': dsize = repeat repeat = 1 case 'D', 'E': dsize = repeat repeat = 1 case 'F': dsize = repeat repeat = 1 } typ = Type{ tc: tc, len: repeat, dsize: dsize, hsize: hsize, gotype: rt, } // fmt.Printf(">>> %#v (%v)\n", typ, typ.gotype.Name()) if typ.dsize*typ.len == 0 { if form != "0A" { return typ, fmt.Errorf("fitsio: invalid dtype! form=%q typ=%#v\n", form, typ) } } } return typ, err }
func reflectArrayOf(rvn reflect.Value) (rvn2 reflect.Value) { rvn2 = reflect.New(reflect.ArrayOf(rvn.Len(), intfTyp)).Elem() reflect.Copy(rvn2, rvn) return }
func (e XDREncoder) item(name string, dtype dataType, value interface{}) error { p := pair{ Name: name, NElements: 1, Type: dtype, data: value, } vbuf := &bytes.Buffer{} switch p.Type { case _BOOLEAN: p.NElements = 0 case _BYTE: value = int8(value.(uint8)) case _UINT8: value = int(int8(value.(uint8))) case _BYTE_ARRAY: p.NElements = uint32(len(value.([]byte))) n := int(p.NElements) arrType := reflect.ArrayOf(n, reflect.TypeOf(byte(0))) arr := reflect.New(arrType).Elem() for i, b := range value.([]byte) { arr.Index(i).SetUint(uint64(b)) } value = arr.Interface() case _BOOLEAN_ARRAY: p.NElements = uint32(len(value.([]bool))) case _INT8_ARRAY: p.NElements = uint32(len(value.([]int8))) case _INT16_ARRAY: p.NElements = uint32(len(value.([]int16))) case _INT32_ARRAY: p.NElements = uint32(len(value.([]int32))) case _INT64_ARRAY: p.NElements = uint32(len(value.([]int64))) case _UINT8_ARRAY: // this one is weird since UINT8s are encoded as char // aka int32s... :( p.NElements = uint32(len(value.([]uint8))) n := int(p.NElements) sliceType := reflect.SliceOf(reflect.TypeOf(int32(0))) slice := reflect.MakeSlice(sliceType, n, n) for i, b := range value.([]uint8) { slice.Index(i).SetInt(int64(int8(b))) } value = slice.Interface() case _UINT16_ARRAY: p.NElements = uint32(len(value.([]uint16))) case _UINT32_ARRAY: p.NElements = uint32(len(value.([]uint32))) case _UINT64_ARRAY: p.NElements = uint32(len(value.([]uint64))) case _STRING_ARRAY: p.NElements = uint32(len(value.([]string))) arrType := reflect.ArrayOf(int(p.NElements), reflect.TypeOf("")) arr := reflect.New(arrType).Elem() for i, b := range value.([]string) { arr.Index(i).SetString(b) } value = arr.Interface() case _NVLIST: enc := NewXDREncoder(vbuf) if err := encodeList(enc, reflect.ValueOf(value)); err != nil { return err } p.data = vbuf.Bytes() case _NVLIST_ARRAY: p.NElements = uint32(len(value.([]map[string]interface{}))) for _, l := range value.([]map[string]interface{}) { enc := NewXDREncoder(vbuf) if err := encodeList(enc, reflect.ValueOf(l)); err != nil { return err } } p.data = vbuf.Bytes() } if vbuf.Len() == 0 && p.Type != _BOOLEAN { _, err := xdr.NewEncoder(vbuf).Encode(value) if err != nil { return err } } p.EncodedSize = uint32(p.encodedSize()) p.DecodedSize = uint32(p.decodedSize()) pbuf := &bytes.Buffer{} _, err := xdr.NewEncoder(pbuf).Encode(p) if err != nil { return err } _, err = pbuf.WriteTo(e.w) if err != nil { return err } _, err = vbuf.WriteTo(e.w) if err != nil { return err } return nil }
// Decode an sflow packet read from 'r' into the struct given by 's' - The structs datatypes have to match the binary representation in the bytestream exactly func decodeInto(r io.Reader, s interface{}) (int, error) { var err error var bytesRead int // If the provided datastructure has a static size we can decode it directly if size := binary.Size(s); size != -1 { err = binary.Read(r, binary.BigEndian, s) return size, err } structure := reflect.TypeOf(s) data := reflect.ValueOf(s) if structure.Kind() == reflect.Interface || structure.Kind() == reflect.Ptr { structure = structure.Elem() } if data.Kind() == reflect.Interface || data.Kind() == reflect.Ptr { data = data.Elem() } //fmt.Printf("Decoding into %T - %+#v\n", s, s) for i := 0; i < structure.NumField(); i++ { field := data.Field(i) // Do not decode fields marked with "ignoreOnMarshal" Tags if ignoreField := structure.Field(i).Tag.Get("ignoreOnMarshal"); ignoreField == "true" { continue } //fmt.Printf("Kind: %s - %s\n", field.Kind(), field.CanSet()) //fmt.Printf("State: %s\n", s) if field.CanSet() { switch field.Kind() { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: // We can decode these kinds directly field.Set(reflect.New(field.Type()).Elem()) if err = binary.Read(r, binary.BigEndian, field.Addr().Interface()); err != nil { return bytesRead, err } bytesRead += binary.Size(field.Addr().Interface()) case reflect.Array: // For Arrays we have to create the correct structure first but can then decode directly into them buf := reflect.ArrayOf(field.Len(), field.Type().Elem()) field.Set(reflect.New(buf).Elem()) if err = binary.Read(r, binary.BigEndian, field.Addr().Interface()); err != nil { return bytesRead, err } bytesRead += binary.Size(field.Addr().Interface()) case reflect.Slice: // For slices we need to determine the length somehow switch field.Type() { // Some types (IP/HardwareAddr) are handled specifically case reflect.TypeOf(net.IP{}): var bufferSize uint32 ipVersion := structure.Field(i).Tag.Get("ipVersion") switch ipVersion { case "4": bufferSize = 4 case "6": bufferSize = 16 default: lookupField := structure.Field(i).Tag.Get("ipVersionLookUp") switch lookupField { default: ipType := reflect.Indirect(data).FieldByName(lookupField).Uint() switch ipType { case 1: bufferSize = 4 case 2: bufferSize = 16 default: return bytesRead, fmt.Errorf("Invalid Value found in ipVersionLookUp Type Field. Expected 1 or 2 and got: %d", ipType) } case "": return bytesRead, fmt.Errorf("Unable to determine which IP Version to read for field %s\n", structure.Field(i).Name) } } buffer := make([]byte, bufferSize) if err = binary.Read(r, binary.BigEndian, &buffer); err != nil { return bytesRead, err } bytesRead += binary.Size(&buffer) field.SetBytes(buffer) case reflect.TypeOf(HardwareAddr{}): buffer := make([]byte, 6) if err = binary.Read(r, binary.BigEndian, &buffer); err != nil { return bytesRead, err } bytesRead += binary.Size(&buffer) field.SetBytes(buffer) default: // Look up the slices length via the lengthLookUp Tag Field lengthField := structure.Field(i).Tag.Get("lengthLookUp") if lengthField == "" { return bytesRead, fmt.Errorf("Variable length slice (%s) without a defined lengthLookUp. Please specify length lookup field via struct tag: `lengthLookUp:\"fieldname\"`", structure.Field(i).Name) } bufferSize := reflect.Indirect(data).FieldByName(lengthField).Uint() if bufferSize > 0 { switch field.Type().Elem().Kind() { case reflect.Struct, reflect.Slice, reflect.Array: // For slices of unspecified types we call Decode revursively for every element field.Set(reflect.MakeSlice(field.Type(), int(bufferSize), int(bufferSize))) for x := 0; x < int(bufferSize); x++ { decodeInto(r, field.Index(x).Addr().Interface()) } default: //Apply padding size := bufferSize + (4-(bufferSize%4))%4 // For slices of defined length types we can look up the length and decode directly field.Set(reflect.MakeSlice(field.Type(), int(size), int(size))) // Read directly from io if err = binary.Read(r, binary.BigEndian, field.Addr().Interface()); err != nil { return bytesRead, err } bytesRead += binary.Size(field.Addr().Interface()) } } } case reflect.Struct: // For structs we call Decode revursively field.Set(reflect.Zero(field.Type())) decodeInto(r, field.Addr().Interface()) default: return bytesRead, fmt.Errorf("Unhandled Field Kind: %s", field.Kind()) } } } return bytesRead, nil }
func inject(srcValue, dstValue reflect.Value, tagKey string) (err error) { defer func() { if r := recover(); r != nil { switch rt := r.(type) { case runtime.Error: panic(r) case error: err = rt case string: err = errors.New(rt) default: panic(r) } } }() dstValue = reflect.Indirect(dstValue) srcValue = reflect.Indirect(reflect.ValueOf(srcValue.Interface())) srcKind := srcValue.Kind() dstKind := dstValue.Kind() switch dstKind { case reflect.Slice: dstType := dstValue.Type() dstTypeElem := dstType.Elem() dstTypeElemKind := dstTypeElem.Kind() if srcKind == reflect.Slice { srcLen := srcValue.Len() dstValue.Set(reflect.MakeSlice(dstType, srcLen, srcValue.Cap())) for i := 0; i < srcLen; i++ { if err := inject(srcValue.Index(i), dstValue.Index(i), tagKey); err != nil { return err } } return nil } if dstTypeElemKind == reflect.Interface || srcKind == dstTypeElemKind { dstValue.Set(reflect.MakeSlice(dstType, 1, 1)) return inject(srcValue, dstValue.Index(0), tagKey) } return &InvalidTypeError{ TypeSrc: srcValue.Type(), TypeDst: dstType, } case reflect.Map: dstType := dstValue.Type() dstTypeElem := dstType.Elem() switch srcKind { case reflect.Map: dstTypeKey := dstType.Key() dstValue.Set(reflect.MakeMap(dstType)) for _, srcKey := range srcValue.MapKeys() { dstKeyValue := reflect.New(dstTypeElem) if err := inject(srcValue.MapIndex(srcKey), dstKeyValue, tagKey); err != nil { return err } dstKey := reflect.New(dstTypeKey).Elem() dstKey.Set(reflect.ValueOf(srcKey.Interface())) dstValue.SetMapIndex(dstKey, reflect.Indirect(dstKeyValue)) } case reflect.Struct: dstValue.Set(reflect.MakeMap(dstType)) for i := 0; i < srcValue.NumField(); i++ { dstKeyValue := reflect.New(dstTypeElem) if err := inject(srcValue.Field(i), dstKeyValue, tagKey); err != nil { return err } srcFieldType := srcValue.Type().Field(i) keyName := keyNameFromTag(srcFieldType.Tag, tagKey) if keyName == "-" { continue } if keyName == "" { keyName = srcFieldType.Name } dstKey := reflect.New(reflect.TypeOf(keyName)).Elem() dstKey.SetString(keyName) dstValue.SetMapIndex(dstKey, reflect.Indirect(dstKeyValue)) } default: return &InvalidTypeError{ TypeSrc: srcValue.Type(), TypeDst: dstType, } } case reflect.Struct: switch srcKind { case reflect.Map: for i := 0; i < dstValue.NumField(); i++ { dstKeyValue := reflect.New(dstValue.Field(i).Type()) dstFieldType := dstValue.Type().Field(i) keyName := keyNameFromTag(dstFieldType.Tag, tagKey) if keyName == "" { keyName = dstFieldType.Name } if keyName == "-" { continue } srcKey := reflect.New(reflect.TypeOf(keyName)).Elem() srcKey.SetString(keyName) srcMapValue := srcValue.MapIndex(srcKey) if !srcMapValue.IsValid() { if tagContains(dstFieldType.Tag, tagKey, "required") { return &FieldRequiredError{ FieldName: keyName, } } continue } if err := inject(srcMapValue, dstKeyValue, tagKey); err != nil { return err } dstValue.Field(i).Set(reflect.Indirect(dstKeyValue)) } case reflect.Struct: for i := 0; i < dstValue.NumField(); i++ { dstKeyValue := reflect.New(dstValue.Field(i).Type()) dstFieldType := dstValue.Type().Field(i) fieldName := dstFieldType.Name srcStructValue := srcValue.FieldByName(fieldName) if !srcStructValue.IsValid() { if tagContains(dstFieldType.Tag, tagKey, "required") { return &FieldRequiredError{ FieldName: fieldName, } } continue } if err := inject(srcStructValue, dstKeyValue, tagKey); err != nil { return err } dstValue.Field(i).Set(reflect.Indirect(srcStructValue)) } default: return &InvalidTypeError{ TypeSrc: srcValue.Type(), TypeDst: dstValue.Type(), } } case reflect.Array: dstTypeElem := dstValue.Type().Elem() if srcValue.Type().Elem().Kind() == dstTypeElem.Kind() { dstValue.Set(srcValue) return } srcLen := srcValue.Len() dstValue.Set(reflect.New(reflect.ArrayOf(srcLen, dstTypeElem)).Elem()) for i := 0; i < srcLen; i++ { if err := inject(srcValue.Index(i), dstValue.Index(i), tagKey); err != nil { return err } } default: if dstKind != srcKind && dstKind != reflect.Interface { return &InvalidTypeError{ TypeSrc: srcValue.Type(), TypeDst: dstValue.Type(), } } dstValue.Set(srcValue) } return nil }
// Memoize takes a function and returns a function of the same type. The // returned function remembers the return value(s) of the function call. // Any pointer values will be used as an address, so functions that modify // their arguments or programs that modify returned values will not work. // // The returned function is safe to call from multiple goroutines if the // original function is. Panics are handled, so calling panic from a function // will call panic with the same value on future invocations with the same // arguments. // // The arguments to the function must be of comparable types. Slices, maps, // functions, and structs or arrays that contain slices, maps, or functions // cause a runtime panic if they are arguments to a memoized function. // See also: https://golang.org/ref/spec#Comparison_operators // // As a special case, variadic functions (func(x, y, ...z)) are allowed. func Memoize(fn interface{}) interface{} { v := reflect.ValueOf(fn) t := v.Type() keyType := reflect.ArrayOf(t.NumIn(), interfaceType) cache := reflect.MakeMap(reflect.MapOf(keyType, valueType)) var mtx sync.Mutex return reflect.MakeFunc(t, func(args []reflect.Value) (results []reflect.Value) { key := reflect.New(keyType).Elem() for i, v := range args { if i == len(args)-1 && t.IsVariadic() { a := reflect.New(reflect.ArrayOf(v.Len(), v.Type().Elem())).Elem() for j, l := 0, v.Len(); j < l; j++ { a.Index(j).Set(v.Index(j)) } v = a } vi := v.Interface() key.Index(i).Set(reflect.ValueOf(&vi).Elem()) } mtx.Lock() val := cache.MapIndex(key) if val.IsValid() { mtx.Unlock() c := val.Interface().(*call) <-c.wait if c.panicked.IsValid() { panic(c.panicked.Interface()) } return c.results } w := make(chan struct{}) c := &call{wait: w} cache.SetMapIndex(key, reflect.ValueOf(c)) mtx.Unlock() panicked := true defer func() { if panicked { p := recover() c.panicked = reflect.ValueOf(p) close(w) panic(p) } }() if t.IsVariadic() { results = v.CallSlice(args) } else { results = v.Call(args) } panicked = false c.results = results close(w) return }).Interface() }