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
}
Beispiel #2
0
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)
}
Beispiel #3
0
// 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
}
Beispiel #4
0
// 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
}
Beispiel #5
0
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("")
}
Beispiel #6
0
func arrayOf(count int, elem reflect.Type) reflect.Type {
	return reflect.ArrayOf(count, elem)
}
Beispiel #7
0
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
}
Beispiel #8
0
// 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
}
Beispiel #9
0
func reflectArrayOf(rvn reflect.Value) (rvn2 reflect.Value) {
	rvn2 = reflect.New(reflect.ArrayOf(rvn.Len(), intfTyp)).Elem()
	reflect.Copy(rvn2, rvn)
	return
}
Beispiel #10
0
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
}
Beispiel #11
0
// 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
}
Beispiel #12
0
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
}
Beispiel #13
-1
// 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()
}