~juju-qa/ubuntu/yakkety/juju/2.0-rc3-again

« back to all changes in this revision

Viewing changes to src/labix.org/v2/mgo/bson/encode.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-04-24 22:34:47 UTC
  • Revision ID: package-import@ubuntu.com-20130424223447-f0qdji7ubnyo0s71
Tags: upstream-1.10.0.1
ImportĀ upstreamĀ versionĀ 1.10.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// BSON library for Go
 
2
// 
 
3
// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
 
4
// 
 
5
// All rights reserved.
 
6
//
 
7
// Redistribution and use in source and binary forms, with or without
 
8
// modification, are permitted provided that the following conditions are met: 
 
9
// 
 
10
// 1. Redistributions of source code must retain the above copyright notice, this
 
11
//    list of conditions and the following disclaimer. 
 
12
// 2. Redistributions in binary form must reproduce the above copyright notice,
 
13
//    this list of conditions and the following disclaimer in the documentation
 
14
//    and/or other materials provided with the distribution. 
 
15
// 
 
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 
17
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 
18
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
19
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 
20
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
21
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
22
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 
23
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
24
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 
25
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
26
// gobson - BSON library for Go.
 
27
 
 
28
package bson
 
29
 
 
30
import (
 
31
        "fmt"
 
32
        "math"
 
33
        "net/url"
 
34
        "reflect"
 
35
        "strconv"
 
36
        "time"
 
37
)
 
38
 
 
39
// --------------------------------------------------------------------------
 
40
// Some internal infrastructure.
 
41
 
 
42
var (
 
43
        typeBinary         = reflect.TypeOf(Binary{})
 
44
        typeObjectId       = reflect.TypeOf(ObjectId(""))
 
45
        typeSymbol         = reflect.TypeOf(Symbol(""))
 
46
        typeMongoTimestamp = reflect.TypeOf(MongoTimestamp(0))
 
47
        typeOrderKey       = reflect.TypeOf(MinKey)
 
48
        typeDocElem        = reflect.TypeOf(DocElem{})
 
49
        typeRaw            = reflect.TypeOf(Raw{})
 
50
        typeURL            = reflect.TypeOf(url.URL{})
 
51
        typeTime           = reflect.TypeOf(time.Time{})
 
52
)
 
53
 
 
54
const itoaCacheSize = 32
 
55
 
 
56
var itoaCache []string
 
57
 
 
58
func init() {
 
59
        itoaCache = make([]string, itoaCacheSize)
 
60
        for i := 0; i != itoaCacheSize; i++ {
 
61
                itoaCache[i] = strconv.Itoa(i)
 
62
        }
 
63
}
 
64
 
 
65
func itoa(i int) string {
 
66
        if i < itoaCacheSize {
 
67
                return itoaCache[i]
 
68
        }
 
69
        return strconv.Itoa(i)
 
70
}
 
71
 
 
72
// --------------------------------------------------------------------------
 
73
// Marshaling of the document value itself.
 
74
 
 
75
type encoder struct {
 
76
        out []byte
 
77
}
 
78
 
 
79
func (e *encoder) addDoc(v reflect.Value) {
 
80
        for {
 
81
                if vi, ok := v.Interface().(Getter); ok {
 
82
                        getv, err := vi.GetBSON()
 
83
                        if err != nil {
 
84
                                panic(err)
 
85
                        }
 
86
                        v = reflect.ValueOf(getv)
 
87
                        continue
 
88
                }
 
89
                if v.Kind() == reflect.Ptr {
 
90
                        v = v.Elem()
 
91
                        continue
 
92
                }
 
93
                break
 
94
        }
 
95
 
 
96
        if v.Type() == typeRaw {
 
97
                raw := v.Interface().(Raw)
 
98
                if raw.Kind != 0x03 && raw.Kind != 0x00 {
 
99
                        panic("Attempted to unmarshal Raw kind " + strconv.Itoa(int(raw.Kind)) + " as a document")
 
100
                }
 
101
                e.addBytes(raw.Data...)
 
102
                return
 
103
        }
 
104
 
 
105
        start := e.reserveInt32()
 
106
 
 
107
        switch v.Kind() {
 
108
        case reflect.Map:
 
109
                e.addMap(v)
 
110
        case reflect.Struct:
 
111
                e.addStruct(v)
 
112
        case reflect.Array, reflect.Slice:
 
113
                e.addSlice(v)
 
114
        default:
 
115
                panic("Can't marshal " + v.Type().String() + " as a BSON document")
 
116
        }
 
117
 
 
118
        e.addBytes(0)
 
119
        e.setInt32(start, int32(len(e.out)-start))
 
120
}
 
121
 
 
122
func (e *encoder) addMap(v reflect.Value) {
 
123
        for _, k := range v.MapKeys() {
 
124
                e.addElem(k.String(), v.MapIndex(k), false)
 
125
        }
 
126
}
 
127
 
 
128
func (e *encoder) addStruct(v reflect.Value) {
 
129
        sinfo, err := getStructInfo(v.Type())
 
130
        if err != nil {
 
131
                panic(err)
 
132
        }
 
133
        var value reflect.Value
 
134
        if sinfo.InlineMap >= 0 {
 
135
                m := v.Field(sinfo.InlineMap)
 
136
                if m.Len() > 0 {
 
137
                        for _, k := range m.MapKeys() {
 
138
                                ks := k.String()
 
139
                                if _, found := sinfo.FieldsMap[ks]; found {
 
140
                                        panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", ks))
 
141
                                }
 
142
                                e.addElem(ks, m.MapIndex(k), false)
 
143
                        }
 
144
                }
 
145
        }
 
146
        for _, info := range sinfo.FieldsList {
 
147
                if info.Inline == nil {
 
148
                        value = v.Field(info.Num)
 
149
                } else {
 
150
                        value = v.FieldByIndex(info.Inline)
 
151
                }
 
152
                if info.OmitEmpty && isZero(value) {
 
153
                        continue
 
154
                }
 
155
                e.addElem(info.Key, value, info.MinSize)
 
156
        }
 
157
}
 
158
 
 
159
func isZero(v reflect.Value) bool {
 
160
        switch v.Kind() {
 
161
        case reflect.String:
 
162
                return len(v.String()) == 0
 
163
        case reflect.Ptr, reflect.Interface:
 
164
                return v.IsNil()
 
165
        case reflect.Slice:
 
166
                return v.Len() == 0
 
167
        case reflect.Map:
 
168
                return v.Len() == 0
 
169
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 
170
                return v.Int() == 0
 
171
        case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 
172
                return v.Uint() == 0
 
173
        case reflect.Float32, reflect.Float64:
 
174
                return v.Float() == 0
 
175
        case reflect.Bool:
 
176
                return !v.Bool()
 
177
        case reflect.Struct:
 
178
                if v.Type() == typeTime {
 
179
                        return v.Interface().(time.Time).IsZero()
 
180
                }
 
181
        }
 
182
        return false
 
183
}
 
184
 
 
185
func (e *encoder) addSlice(v reflect.Value) {
 
186
        if d, ok := v.Interface().(D); ok {
 
187
                for _, elem := range d {
 
188
                        e.addElem(elem.Name, reflect.ValueOf(elem.Value), false)
 
189
                }
 
190
        } else if v.Type().Elem() == typeDocElem {
 
191
                l := v.Len()
 
192
                for i := 0; i < l; i++ {
 
193
                        elem := v.Index(i).Interface().(DocElem)
 
194
                        e.addElem(elem.Name, reflect.ValueOf(elem.Value), false)
 
195
                }
 
196
        } else {
 
197
                l := v.Len()
 
198
                for i := 0; i < l; i++ {
 
199
                        e.addElem(itoa(i), v.Index(i), false)
 
200
                }
 
201
        }
 
202
}
 
203
 
 
204
// --------------------------------------------------------------------------
 
205
// Marshaling of elements in a document.
 
206
 
 
207
func (e *encoder) addElemName(kind byte, name string) {
 
208
        e.addBytes(kind)
 
209
        e.addBytes([]byte(name)...)
 
210
        e.addBytes(0)
 
211
}
 
212
 
 
213
func (e *encoder) addElem(name string, v reflect.Value, minSize bool) {
 
214
 
 
215
        if !v.IsValid() {
 
216
                e.addElemName('\x0A', name)
 
217
                return
 
218
        }
 
219
 
 
220
        if getter, ok := v.Interface().(Getter); ok {
 
221
                getv, err := getter.GetBSON()
 
222
                if err != nil {
 
223
                        panic(err)
 
224
                }
 
225
                e.addElem(name, reflect.ValueOf(getv), minSize)
 
226
                return
 
227
        }
 
228
 
 
229
        switch v.Kind() {
 
230
 
 
231
        case reflect.Interface:
 
232
                e.addElem(name, v.Elem(), minSize)
 
233
 
 
234
        case reflect.Ptr:
 
235
                e.addElem(name, v.Elem(), minSize)
 
236
 
 
237
        case reflect.String:
 
238
                s := v.String()
 
239
                switch v.Type() {
 
240
                case typeObjectId:
 
241
                        if len(s) != 12 {
 
242
                                panic("ObjectIDs must be exactly 12 bytes long (got " +
 
243
                                        strconv.Itoa(len(s)) + ")")
 
244
                        }
 
245
                        e.addElemName('\x07', name)
 
246
                        e.addBytes([]byte(s)...)
 
247
                case typeSymbol:
 
248
                        e.addElemName('\x0E', name)
 
249
                        e.addStr(s)
 
250
                default:
 
251
                        e.addElemName('\x02', name)
 
252
                        e.addStr(s)
 
253
                }
 
254
 
 
255
        case reflect.Float32, reflect.Float64:
 
256
                e.addElemName('\x01', name)
 
257
                e.addInt64(int64(math.Float64bits(v.Float())))
 
258
 
 
259
        case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 
260
                u := v.Uint()
 
261
                if int64(u) < 0 {
 
262
                        panic("BSON has no uint64 type, and value is too large to fit correctly in an int64")
 
263
                } else if u <= math.MaxInt32 && (minSize || v.Kind() <= reflect.Uint32) {
 
264
                        e.addElemName('\x10', name)
 
265
                        e.addInt32(int32(u))
 
266
                } else {
 
267
                        e.addElemName('\x12', name)
 
268
                        e.addInt64(int64(u))
 
269
                }
 
270
 
 
271
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 
272
                if v.Type().Kind() <= reflect.Int32 {
 
273
                        e.addElemName('\x10', name)
 
274
                        e.addInt32(int32(v.Int()))
 
275
                } else {
 
276
                        switch v.Type() {
 
277
                        case typeMongoTimestamp:
 
278
                                e.addElemName('\x11', name)
 
279
                                e.addInt64(v.Int())
 
280
 
 
281
                        case typeOrderKey:
 
282
                                if v.Int() == int64(MaxKey) {
 
283
                                        e.addElemName('\x7F', name)
 
284
                                } else {
 
285
                                        e.addElemName('\xFF', name)
 
286
                                }
 
287
 
 
288
                        default:
 
289
                                i := v.Int()
 
290
                                if minSize && i >= math.MinInt32 && i <= math.MaxInt32 {
 
291
                                        // It fits into an int32, encode as such.
 
292
                                        e.addElemName('\x10', name)
 
293
                                        e.addInt32(int32(i))
 
294
                                } else {
 
295
                                        e.addElemName('\x12', name)
 
296
                                        e.addInt64(i)
 
297
                                }
 
298
                        }
 
299
                }
 
300
 
 
301
        case reflect.Bool:
 
302
                e.addElemName('\x08', name)
 
303
                if v.Bool() {
 
304
                        e.addBytes(1)
 
305
                } else {
 
306
                        e.addBytes(0)
 
307
                }
 
308
 
 
309
        case reflect.Map:
 
310
                e.addElemName('\x03', name)
 
311
                e.addDoc(v)
 
312
 
 
313
        case reflect.Slice:
 
314
                vt := v.Type()
 
315
                et := vt.Elem()
 
316
                if et.Kind() == reflect.Uint8 {
 
317
                        e.addElemName('\x05', name)
 
318
                        e.addBinary('\x00', v.Bytes())
 
319
                } else if et == typeDocElem {
 
320
                        e.addElemName('\x03', name)
 
321
                        e.addDoc(v)
 
322
                } else {
 
323
                        e.addElemName('\x04', name)
 
324
                        e.addDoc(v)
 
325
                }
 
326
 
 
327
        case reflect.Array:
 
328
                et := v.Type().Elem()
 
329
                if et.Kind() == reflect.Uint8 {
 
330
                        e.addElemName('\x05', name)
 
331
                        e.addBinary('\x00', v.Slice(0, v.Len()).Interface().([]byte))
 
332
                } else {
 
333
                        e.addElemName('\x04', name)
 
334
                        e.addDoc(v)
 
335
                }
 
336
 
 
337
        case reflect.Struct:
 
338
                switch s := v.Interface().(type) {
 
339
 
 
340
                case Raw:
 
341
                        kind := s.Kind
 
342
                        if kind == 0x00 {
 
343
                                kind = 0x03
 
344
                        }
 
345
                        e.addElemName(kind, name)
 
346
                        e.addBytes(s.Data...)
 
347
 
 
348
                case Binary:
 
349
                        e.addElemName('\x05', name)
 
350
                        e.addBinary(s.Kind, s.Data)
 
351
 
 
352
                case RegEx:
 
353
                        e.addElemName('\x0B', name)
 
354
                        e.addCStr(s.Pattern)
 
355
                        e.addCStr(s.Options)
 
356
 
 
357
                case JavaScript:
 
358
                        if s.Scope == nil {
 
359
                                e.addElemName('\x0D', name)
 
360
                                e.addStr(s.Code)
 
361
                        } else {
 
362
                                e.addElemName('\x0F', name)
 
363
                                start := e.reserveInt32()
 
364
                                e.addStr(s.Code)
 
365
                                e.addDoc(reflect.ValueOf(s.Scope))
 
366
                                e.setInt32(start, int32(len(e.out)-start))
 
367
                        }
 
368
 
 
369
                case time.Time:
 
370
                        // MongoDB handles timestamps as milliseconds.
 
371
                        e.addElemName('\x09', name)
 
372
                        e.addInt64(s.Unix() * 1000 + int64(s.Nanosecond() / 1e6))
 
373
 
 
374
                case url.URL:
 
375
                        e.addElemName('\x02', name)
 
376
                        e.addStr(s.String())
 
377
 
 
378
                case undefined:
 
379
                        e.addElemName('\x06', name)
 
380
 
 
381
                default:
 
382
                        e.addElemName('\x03', name)
 
383
                        e.addDoc(v)
 
384
                }
 
385
 
 
386
        default:
 
387
                panic("Can't marshal " + v.Type().String() + " in a BSON document")
 
388
        }
 
389
}
 
390
 
 
391
// --------------------------------------------------------------------------
 
392
// Marshaling of base types.
 
393
 
 
394
func (e *encoder) addBinary(subtype byte, v []byte) {
 
395
        if subtype == 0x02 {
 
396
                // Wonder how that brilliant idea came to life. Obsolete, luckily.
 
397
                e.addInt32(int32(len(v) + 4))
 
398
                e.addBytes(subtype)
 
399
                e.addInt32(int32(len(v)))
 
400
        } else {
 
401
                e.addInt32(int32(len(v)))
 
402
                e.addBytes(subtype)
 
403
        }
 
404
        e.addBytes(v...)
 
405
}
 
406
 
 
407
func (e *encoder) addStr(v string) {
 
408
        e.addInt32(int32(len(v) + 1))
 
409
        e.addCStr(v)
 
410
}
 
411
 
 
412
func (e *encoder) addCStr(v string) {
 
413
        e.addBytes([]byte(v)...)
 
414
        e.addBytes(0)
 
415
}
 
416
 
 
417
func (e *encoder) reserveInt32() (pos int) {
 
418
        pos = len(e.out)
 
419
        e.addBytes(0, 0, 0, 0)
 
420
        return pos
 
421
}
 
422
 
 
423
func (e *encoder) setInt32(pos int, v int32) {
 
424
        e.out[pos+0] = byte(v)
 
425
        e.out[pos+1] = byte(v >> 8)
 
426
        e.out[pos+2] = byte(v >> 16)
 
427
        e.out[pos+3] = byte(v >> 24)
 
428
}
 
429
 
 
430
func (e *encoder) addInt32(v int32) {
 
431
        u := uint32(v)
 
432
        e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24))
 
433
}
 
434
 
 
435
func (e *encoder) addInt64(v int64) {
 
436
        u := uint64(v)
 
437
        e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24),
 
438
                byte(u>>32), byte(u>>40), byte(u>>48), byte(u>>56))
 
439
}
 
440
 
 
441
func (e *encoder) addBytes(v ...byte) {
 
442
        e.out = append(e.out, v...)
 
443
}