aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go')
-rw-r--r--vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go296
1 files changed, 296 insertions, 0 deletions
diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go
new file mode 100644
index 0000000..864fb67
--- /dev/null
+++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go
@@ -0,0 +1,296 @@
1// Package jsonutil provides JSON serialization of AWS requests and responses.
2package jsonutil
3
4import (
5 "bytes"
6 "encoding/base64"
7 "encoding/json"
8 "fmt"
9 "math"
10 "reflect"
11 "sort"
12 "strconv"
13 "time"
14
15 "github.com/aws/aws-sdk-go/aws"
16 "github.com/aws/aws-sdk-go/private/protocol"
17)
18
19var timeType = reflect.ValueOf(time.Time{}).Type()
20var byteSliceType = reflect.ValueOf([]byte{}).Type()
21
22// BuildJSON builds a JSON string for a given object v.
23func BuildJSON(v interface{}) ([]byte, error) {
24 var buf bytes.Buffer
25
26 err := buildAny(reflect.ValueOf(v), &buf, "")
27 return buf.Bytes(), err
28}
29
30func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
31 origVal := value
32 value = reflect.Indirect(value)
33 if !value.IsValid() {
34 return nil
35 }
36
37 vtype := value.Type()
38
39 t := tag.Get("type")
40 if t == "" {
41 switch vtype.Kind() {
42 case reflect.Struct:
43 // also it can't be a time object
44 if value.Type() != timeType {
45 t = "structure"
46 }
47 case reflect.Slice:
48 // also it can't be a byte slice
49 if _, ok := value.Interface().([]byte); !ok {
50 t = "list"
51 }
52 case reflect.Map:
53 // cannot be a JSONValue map
54 if _, ok := value.Interface().(aws.JSONValue); !ok {
55 t = "map"
56 }
57 }
58 }
59
60 switch t {
61 case "structure":
62 if field, ok := vtype.FieldByName("_"); ok {
63 tag = field.Tag
64 }
65 return buildStruct(value, buf, tag)
66 case "list":
67 return buildList(value, buf, tag)
68 case "map":
69 return buildMap(value, buf, tag)
70 default:
71 return buildScalar(origVal, buf, tag)
72 }
73}
74
75func buildStruct(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
76 if !value.IsValid() {
77 return nil
78 }
79
80 // unwrap payloads
81 if payload := tag.Get("payload"); payload != "" {
82 field, _ := value.Type().FieldByName(payload)
83 tag = field.Tag
84 value = elemOf(value.FieldByName(payload))
85
86 if !value.IsValid() {
87 return nil
88 }
89 }
90
91 buf.WriteByte('{')
92
93 t := value.Type()
94 first := true
95 for i := 0; i < t.NumField(); i++ {
96 member := value.Field(i)
97
98 // This allocates the most memory.
99 // Additionally, we cannot skip nil fields due to
100 // idempotency auto filling.
101 field := t.Field(i)
102
103 if field.PkgPath != "" {
104 continue // ignore unexported fields
105 }
106 if field.Tag.Get("json") == "-" {
107 continue
108 }
109 if field.Tag.Get("location") != "" {
110 continue // ignore non-body elements
111 }
112 if field.Tag.Get("ignore") != "" {
113 continue
114 }
115
116 if protocol.CanSetIdempotencyToken(member, field) {
117 token := protocol.GetIdempotencyToken()
118 member = reflect.ValueOf(&token)
119 }
120
121 if (member.Kind() == reflect.Ptr || member.Kind() == reflect.Slice || member.Kind() == reflect.Map) && member.IsNil() {
122 continue // ignore unset fields
123 }
124
125 if first {
126 first = false
127 } else {
128 buf.WriteByte(',')
129 }
130
131 // figure out what this field is called
132 name := field.Name
133 if locName := field.Tag.Get("locationName"); locName != "" {
134 name = locName
135 }
136
137 writeString(name, buf)
138 buf.WriteString(`:`)
139
140 err := buildAny(member, buf, field.Tag)
141 if err != nil {
142 return err
143 }
144
145 }
146
147 buf.WriteString("}")
148
149 return nil
150}
151
152func buildList(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
153 buf.WriteString("[")
154
155 for i := 0; i < value.Len(); i++ {
156 buildAny(value.Index(i), buf, "")
157
158 if i < value.Len()-1 {
159 buf.WriteString(",")
160 }
161 }
162
163 buf.WriteString("]")
164
165 return nil
166}
167
168type sortedValues []reflect.Value
169
170func (sv sortedValues) Len() int { return len(sv) }
171func (sv sortedValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
172func (sv sortedValues) Less(i, j int) bool { return sv[i].String() < sv[j].String() }
173
174func buildMap(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
175 buf.WriteString("{")
176
177 sv := sortedValues(value.MapKeys())
178 sort.Sort(sv)
179
180 for i, k := range sv {
181 if i > 0 {
182 buf.WriteByte(',')
183 }
184
185 writeString(k.String(), buf)
186 buf.WriteString(`:`)
187
188 buildAny(value.MapIndex(k), buf, "")
189 }
190
191 buf.WriteString("}")
192
193 return nil
194}
195
196func buildScalar(v reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
197 // prevents allocation on the heap.
198 scratch := [64]byte{}
199 switch value := reflect.Indirect(v); value.Kind() {
200 case reflect.String:
201 writeString(value.String(), buf)
202 case reflect.Bool:
203 if value.Bool() {
204 buf.WriteString("true")
205 } else {
206 buf.WriteString("false")
207 }
208 case reflect.Int64:
209 buf.Write(strconv.AppendInt(scratch[:0], value.Int(), 10))
210 case reflect.Float64:
211 f := value.Float()
212 if math.IsInf(f, 0) || math.IsNaN(f) {
213 return &json.UnsupportedValueError{Value: v, Str: strconv.FormatFloat(f, 'f', -1, 64)}
214 }
215 buf.Write(strconv.AppendFloat(scratch[:0], f, 'f', -1, 64))
216 default:
217 switch converted := value.Interface().(type) {
218 case time.Time:
219 format := tag.Get("timestampFormat")
220 if len(format) == 0 {
221 format = protocol.UnixTimeFormatName
222 }
223
224 ts := protocol.FormatTime(format, converted)
225 if format != protocol.UnixTimeFormatName {
226 ts = `"` + ts + `"`
227 }
228
229 buf.WriteString(ts)
230 case []byte:
231 if !value.IsNil() {
232 buf.WriteByte('"')
233 if len(converted) < 1024 {
234 // for small buffers, using Encode directly is much faster.
235 dst := make([]byte, base64.StdEncoding.EncodedLen(len(converted)))
236 base64.StdEncoding.Encode(dst, converted)
237 buf.Write(dst)
238 } else {
239 // for large buffers, avoid unnecessary extra temporary
240 // buffer space.
241 enc := base64.NewEncoder(base64.StdEncoding, buf)
242 enc.Write(converted)
243 enc.Close()
244 }
245 buf.WriteByte('"')
246 }
247 case aws.JSONValue:
248 str, err := protocol.EncodeJSONValue(converted, protocol.QuotedEscape)
249 if err != nil {
250 return fmt.Errorf("unable to encode JSONValue, %v", err)
251 }
252 buf.WriteString(str)
253 default:
254 return fmt.Errorf("unsupported JSON value %v (%s)", value.Interface(), value.Type())
255 }
256 }
257 return nil
258}
259
260var hex = "0123456789abcdef"
261
262func writeString(s string, buf *bytes.Buffer) {
263 buf.WriteByte('"')
264 for i := 0; i < len(s); i++ {
265 if s[i] == '"' {
266 buf.WriteString(`\"`)
267 } else if s[i] == '\\' {
268 buf.WriteString(`\\`)
269 } else if s[i] == '\b' {
270 buf.WriteString(`\b`)
271 } else if s[i] == '\f' {
272 buf.WriteString(`\f`)
273 } else if s[i] == '\r' {
274 buf.WriteString(`\r`)
275 } else if s[i] == '\t' {
276 buf.WriteString(`\t`)
277 } else if s[i] == '\n' {
278 buf.WriteString(`\n`)
279 } else if s[i] < 32 {
280 buf.WriteString("\\u00")
281 buf.WriteByte(hex[s[i]>>4])
282 buf.WriteByte(hex[s[i]&0xF])
283 } else {
284 buf.WriteByte(s[i])
285 }
286 }
287 buf.WriteByte('"')
288}
289
290// Returns the reflection element of a value, if it is a pointer.
291func elemOf(value reflect.Value) reflect.Value {
292 for value.Kind() == reflect.Ptr {
293 value = value.Elem()
294 }
295 return value
296}