Blame vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go

Packit 63bb0d
// Package jsonutil provides JSON serialization of AWS requests and responses.
Packit 63bb0d
package jsonutil
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"bytes"
Packit 63bb0d
	"encoding/base64"
Packit 63bb0d
	"encoding/json"
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"math"
Packit 63bb0d
	"reflect"
Packit 63bb0d
	"sort"
Packit 63bb0d
	"strconv"
Packit 63bb0d
	"time"
Packit 63bb0d
Packit 63bb0d
	"github.com/aws/aws-sdk-go/aws"
Packit 63bb0d
	"github.com/aws/aws-sdk-go/private/protocol"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
var timeType = reflect.ValueOf(time.Time{}).Type()
Packit 63bb0d
var byteSliceType = reflect.ValueOf([]byte{}).Type()
Packit 63bb0d
Packit 63bb0d
// BuildJSON builds a JSON string for a given object v.
Packit 63bb0d
func BuildJSON(v interface{}) ([]byte, error) {
Packit 63bb0d
	var buf bytes.Buffer
Packit 63bb0d
Packit 63bb0d
	err := buildAny(reflect.ValueOf(v), &buf, "")
Packit 63bb0d
	return buf.Bytes(), err
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
Packit 63bb0d
	origVal := value
Packit 63bb0d
	value = reflect.Indirect(value)
Packit 63bb0d
	if !value.IsValid() {
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	vtype := value.Type()
Packit 63bb0d
Packit 63bb0d
	t := tag.Get("type")
Packit 63bb0d
	if t == "" {
Packit 63bb0d
		switch vtype.Kind() {
Packit 63bb0d
		case reflect.Struct:
Packit 63bb0d
			// also it can't be a time object
Packit 63bb0d
			if value.Type() != timeType {
Packit 63bb0d
				t = "structure"
Packit 63bb0d
			}
Packit 63bb0d
		case reflect.Slice:
Packit 63bb0d
			// also it can't be a byte slice
Packit 63bb0d
			if _, ok := value.Interface().([]byte); !ok {
Packit 63bb0d
				t = "list"
Packit 63bb0d
			}
Packit 63bb0d
		case reflect.Map:
Packit 63bb0d
			// cannot be a JSONValue map
Packit 63bb0d
			if _, ok := value.Interface().(aws.JSONValue); !ok {
Packit 63bb0d
				t = "map"
Packit 63bb0d
			}
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	switch t {
Packit 63bb0d
	case "structure":
Packit 63bb0d
		if field, ok := vtype.FieldByName("_"); ok {
Packit 63bb0d
			tag = field.Tag
Packit 63bb0d
		}
Packit 63bb0d
		return buildStruct(value, buf, tag)
Packit 63bb0d
	case "list":
Packit 63bb0d
		return buildList(value, buf, tag)
Packit 63bb0d
	case "map":
Packit 63bb0d
		return buildMap(value, buf, tag)
Packit 63bb0d
	default:
Packit 63bb0d
		return buildScalar(origVal, buf, tag)
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func buildStruct(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
Packit 63bb0d
	if !value.IsValid() {
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	// unwrap payloads
Packit 63bb0d
	if payload := tag.Get("payload"); payload != "" {
Packit 63bb0d
		field, _ := value.Type().FieldByName(payload)
Packit 63bb0d
		tag = field.Tag
Packit 63bb0d
		value = elemOf(value.FieldByName(payload))
Packit 63bb0d
Packit 63bb0d
		if !value.IsValid() {
Packit 63bb0d
			return nil
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	buf.WriteByte('{')
Packit 63bb0d
Packit 63bb0d
	t := value.Type()
Packit 63bb0d
	first := true
Packit 63bb0d
	for i := 0; i < t.NumField(); i++ {
Packit 63bb0d
		member := value.Field(i)
Packit 63bb0d
Packit 63bb0d
		// This allocates the most memory.
Packit 63bb0d
		// Additionally, we cannot skip nil fields due to
Packit 63bb0d
		// idempotency auto filling.
Packit 63bb0d
		field := t.Field(i)
Packit 63bb0d
Packit 63bb0d
		if field.PkgPath != "" {
Packit 63bb0d
			continue // ignore unexported fields
Packit 63bb0d
		}
Packit 63bb0d
		if field.Tag.Get("json") == "-" {
Packit 63bb0d
			continue
Packit 63bb0d
		}
Packit 63bb0d
		if field.Tag.Get("location") != "" {
Packit 63bb0d
			continue // ignore non-body elements
Packit 63bb0d
		}
Packit 63bb0d
		if field.Tag.Get("ignore") != "" {
Packit 63bb0d
			continue
Packit 63bb0d
		}
Packit 63bb0d
Packit 63bb0d
		if protocol.CanSetIdempotencyToken(member, field) {
Packit 63bb0d
			token := protocol.GetIdempotencyToken()
Packit 63bb0d
			member = reflect.ValueOf(&token)
Packit 63bb0d
		}
Packit 63bb0d
Packit 63bb0d
		if (member.Kind() == reflect.Ptr || member.Kind() == reflect.Slice || member.Kind() == reflect.Map) && member.IsNil() {
Packit 63bb0d
			continue // ignore unset fields
Packit 63bb0d
		}
Packit 63bb0d
Packit 63bb0d
		if first {
Packit 63bb0d
			first = false
Packit 63bb0d
		} else {
Packit 63bb0d
			buf.WriteByte(',')
Packit 63bb0d
		}
Packit 63bb0d
Packit 63bb0d
		// figure out what this field is called
Packit 63bb0d
		name := field.Name
Packit 63bb0d
		if locName := field.Tag.Get("locationName"); locName != "" {
Packit 63bb0d
			name = locName
Packit 63bb0d
		}
Packit 63bb0d
Packit 63bb0d
		writeString(name, buf)
Packit 63bb0d
		buf.WriteString(`:`)
Packit 63bb0d
Packit 63bb0d
		err := buildAny(member, buf, field.Tag)
Packit 63bb0d
		if err != nil {
Packit 63bb0d
			return err
Packit 63bb0d
		}
Packit 63bb0d
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	buf.WriteString("}")
Packit 63bb0d
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func buildList(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
Packit 63bb0d
	buf.WriteString("[")
Packit 63bb0d
Packit 63bb0d
	for i := 0; i < value.Len(); i++ {
Packit 63bb0d
		buildAny(value.Index(i), buf, "")
Packit 63bb0d
Packit 63bb0d
		if i < value.Len()-1 {
Packit 63bb0d
			buf.WriteString(",")
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	buf.WriteString("]")
Packit 63bb0d
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
type sortedValues []reflect.Value
Packit 63bb0d
Packit 63bb0d
func (sv sortedValues) Len() int           { return len(sv) }
Packit 63bb0d
func (sv sortedValues) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }
Packit 63bb0d
func (sv sortedValues) Less(i, j int) bool { return sv[i].String() < sv[j].String() }
Packit 63bb0d
Packit 63bb0d
func buildMap(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
Packit 63bb0d
	buf.WriteString("{")
Packit 63bb0d
Packit 63bb0d
	sv := sortedValues(value.MapKeys())
Packit 63bb0d
	sort.Sort(sv)
Packit 63bb0d
Packit 63bb0d
	for i, k := range sv {
Packit 63bb0d
		if i > 0 {
Packit 63bb0d
			buf.WriteByte(',')
Packit 63bb0d
		}
Packit 63bb0d
Packit 63bb0d
		writeString(k.String(), buf)
Packit 63bb0d
		buf.WriteString(`:`)
Packit 63bb0d
Packit 63bb0d
		buildAny(value.MapIndex(k), buf, "")
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	buf.WriteString("}")
Packit 63bb0d
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func buildScalar(v reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
Packit 63bb0d
	// prevents allocation on the heap.
Packit 63bb0d
	scratch := [64]byte{}
Packit 63bb0d
	switch value := reflect.Indirect(v); value.Kind() {
Packit 63bb0d
	case reflect.String:
Packit 63bb0d
		writeString(value.String(), buf)
Packit 63bb0d
	case reflect.Bool:
Packit 63bb0d
		if value.Bool() {
Packit 63bb0d
			buf.WriteString("true")
Packit 63bb0d
		} else {
Packit 63bb0d
			buf.WriteString("false")
Packit 63bb0d
		}
Packit 63bb0d
	case reflect.Int64:
Packit 63bb0d
		buf.Write(strconv.AppendInt(scratch[:0], value.Int(), 10))
Packit 63bb0d
	case reflect.Float64:
Packit 63bb0d
		f := value.Float()
Packit 63bb0d
		if math.IsInf(f, 0) || math.IsNaN(f) {
Packit 63bb0d
			return &json.UnsupportedValueError{Value: v, Str: strconv.FormatFloat(f, 'f', -1, 64)}
Packit 63bb0d
		}
Packit 63bb0d
		buf.Write(strconv.AppendFloat(scratch[:0], f, 'f', -1, 64))
Packit 63bb0d
	default:
Packit 63bb0d
		switch converted := value.Interface().(type) {
Packit 63bb0d
		case time.Time:
Packit 63bb0d
			format := tag.Get("timestampFormat")
Packit 63bb0d
			if len(format) == 0 {
Packit 63bb0d
				format = protocol.UnixTimeFormatName
Packit 63bb0d
			}
Packit 63bb0d
Packit 63bb0d
			ts := protocol.FormatTime(format, converted)
Packit 63bb0d
			if format != protocol.UnixTimeFormatName {
Packit 63bb0d
				ts = `"` + ts + `"`
Packit 63bb0d
			}
Packit 63bb0d
Packit 63bb0d
			buf.WriteString(ts)
Packit 63bb0d
		case []byte:
Packit 63bb0d
			if !value.IsNil() {
Packit 63bb0d
				buf.WriteByte('"')
Packit 63bb0d
				if len(converted) < 1024 {
Packit 63bb0d
					// for small buffers, using Encode directly is much faster.
Packit 63bb0d
					dst := make([]byte, base64.StdEncoding.EncodedLen(len(converted)))
Packit 63bb0d
					base64.StdEncoding.Encode(dst, converted)
Packit 63bb0d
					buf.Write(dst)
Packit 63bb0d
				} else {
Packit 63bb0d
					// for large buffers, avoid unnecessary extra temporary
Packit 63bb0d
					// buffer space.
Packit 63bb0d
					enc := base64.NewEncoder(base64.StdEncoding, buf)
Packit 63bb0d
					enc.Write(converted)
Packit 63bb0d
					enc.Close()
Packit 63bb0d
				}
Packit 63bb0d
				buf.WriteByte('"')
Packit 63bb0d
			}
Packit 63bb0d
		case aws.JSONValue:
Packit 63bb0d
			str, err := protocol.EncodeJSONValue(converted, protocol.QuotedEscape)
Packit 63bb0d
			if err != nil {
Packit 63bb0d
				return fmt.Errorf("unable to encode JSONValue, %v", err)
Packit 63bb0d
			}
Packit 63bb0d
			buf.WriteString(str)
Packit 63bb0d
		default:
Packit 63bb0d
			return fmt.Errorf("unsupported JSON value %v (%s)", value.Interface(), value.Type())
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
var hex = "0123456789abcdef"
Packit 63bb0d
Packit 63bb0d
func writeString(s string, buf *bytes.Buffer) {
Packit 63bb0d
	buf.WriteByte('"')
Packit 63bb0d
	for i := 0; i < len(s); i++ {
Packit 63bb0d
		if s[i] == '"' {
Packit 63bb0d
			buf.WriteString(`\"`)
Packit 63bb0d
		} else if s[i] == '\\' {
Packit 63bb0d
			buf.WriteString(`\\`)
Packit 63bb0d
		} else if s[i] == '\b' {
Packit 63bb0d
			buf.WriteString(`\b`)
Packit 63bb0d
		} else if s[i] == '\f' {
Packit 63bb0d
			buf.WriteString(`\f`)
Packit 63bb0d
		} else if s[i] == '\r' {
Packit 63bb0d
			buf.WriteString(`\r`)
Packit 63bb0d
		} else if s[i] == '\t' {
Packit 63bb0d
			buf.WriteString(`\t`)
Packit 63bb0d
		} else if s[i] == '\n' {
Packit 63bb0d
			buf.WriteString(`\n`)
Packit 63bb0d
		} else if s[i] < 32 {
Packit 63bb0d
			buf.WriteString("\\u00")
Packit 63bb0d
			buf.WriteByte(hex[s[i]>>4])
Packit 63bb0d
			buf.WriteByte(hex[s[i]&0xF])
Packit 63bb0d
		} else {
Packit 63bb0d
			buf.WriteByte(s[i])
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	buf.WriteByte('"')
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Returns the reflection element of a value, if it is a pointer.
Packit 63bb0d
func elemOf(value reflect.Value) reflect.Value {
Packit 63bb0d
	for value.Kind() == reflect.Ptr {
Packit 63bb0d
		value = value.Elem()
Packit 63bb0d
	}
Packit 63bb0d
	return value
Packit 63bb0d
}