mgo

Merge lp:~miki-tebeka/mgo/string-compile into lp:~niemeyer/mgo/trunk

Proposed by tebeka
Status: Needs review
Proposed branch: lp:~miki-tebeka/mgo/string-compile
Merge into: lp:~niemeyer/mgo/trunk
Diff against target: 2677 lines (+2646/-0) (has conflicts)
6 files modified
LICENSE (+29/-0)
Makefile (+22/-0)
decode.go (+596/-0)
encode.go (+420/-0)
gobson.go (+483/-0)
gobson_test.go (+1096/-0)
Conflict adding file LICENSE.  Moved existing file to LICENSE.moved.
Conflict adding file Makefile.  Moved existing file to Makefile.moved.
To merge this branch: bzr merge lp:~miki-tebeka/mgo/string-compile
Reviewer Review Type Date Requested Status
Gustavo Niemeyer Pending
Review via email: mp+69689@code.launchpad.net

Description of the change

Fix compilation error with current go head (e3cd47e9748d)

To post a comment you must log in.
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Please check out the diff from this branch (below in the merge proposal). Looks like quite a few things have changed unintendedly.

Also, please note that mgo will follow the stable release of Go. Once these changes make into the stable release, mgo will be immediately changed accordingly.

We'll likely need a way to enable people to use mgo with the head, though. I've been thinking about some convention around that. Please pop up in the mailing list to talk about this if you have ideas.

Revision history for this message
tebeka (miki-tebeka) wrote :

Greetings,

> Please check out the diff from this branch (below in the merge proposal). Looks like quite a few things have changed unintendedly.
This it a patch to gobson, I did a mistake when creating the branch.
Attached the right patch.

> Also, please note that mgo will follow the stable release of Go.  Once these changes make into the stable release, mgo will be immediately changed accordingly.
I guessed so and it's the right thing to do. I run from tip since I mostly play.

All the best,
--
Miki

0-----------------------------------------------------------
1revno: 290revno: 29
2committer: Miki Tebeka <miki.tebeka@gmail.com>1committer: Miki Tebeka <miki.tebeka@gmail.com>
3branch nick: gobson2branch nick: gobson
4timestamp: Thu 2011-07-28 10:06:14 -07003timestamp: Thu 2011-07-28 10:06:14 -0700
5message:4message:
6 Compile with go head5 Compile with go head
diff:
=== modified file 'gobson.go'
--- gobson.go 2011-06-24 23:48:23 +0000
+++ gobson.go 2011-07-28 17:06:14 +0000
@@ -442,7 +442,7 @@
442442
443 info := fieldInfo{Num: i}443 info := fieldInfo{Num: i}
444444
445 if s := strings.LastIndex(field.Tag, "/"); s != -1 {445 if s := strings.LastIndex(string(field.Tag), "/"); s != -1 {
446 for _, c := range field.Tag[s+1:] {446 for _, c := range field.Tag[s+1:] {
447 switch c {447 switch c {
448 case int('c'):448 case int('c'):
@@ -457,7 +457,7 @@
457 }457 }
458458
459 if field.Tag != "" {459 if field.Tag != "" {
460 info.Key = field.Tag460 info.Key = string(field.Tag)
461 } else {461 } else {
462 info.Key = strings.ToLower(field.Name)462 info.Key = strings.ToLower(field.Name)
463 }463 }

Unmerged revisions

29. By tebeka

Compile with go head

28. By Gustavo Niemeyer

Some inlining and tweaks for decoding performance.

27. By Gustavo Niemeyer

os.ErrorString => os.NewError

26. By Gustavo Niemeyer

Raw.Unmarshal and "/s" support.

25. By Gustavo Niemeyer

Updated to current weekly and binary array support.

24. By Gustavo Niemeyer

Better handling of pointers.

23. By Gustavo Niemeyer

Added Raw type.

22. By Gustavo Niemeyer

Encode (u)int64 as int32 if the value fits.

21. By Gustavo Niemeyer

s/Milliseconds/Timestamp/ and use ns. Added Now().

20. By Gustavo Niemeyer

Ported to current weekly.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'LICENSE'
--- LICENSE 1970-01-01 00:00:00 +0000
+++ LICENSE 2011-07-28 17:08:30 +0000
@@ -0,0 +1,29 @@
1gobson - BSON library for Go.
2
3Copyright (c) 2010-2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
4
5All rights reserved.
6
7Redistribution and use in source and binary forms, with or without
8modification, are permitted provided that the following conditions are met:
9
10 * Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 * 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 * Neither the name of the copyright holder nor the names of its
16 contributors may be used to endorse or promote products derived from
17 this software without specific prior written permission.
18
19THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030
=== renamed file 'LICENSE' => 'LICENSE.moved'
=== added file 'Makefile'
--- Makefile 1970-01-01 00:00:00 +0000
+++ Makefile 2011-07-28 17:08:30 +0000
@@ -0,0 +1,22 @@
1include $(GOROOT)/src/Make.inc
2
3TARG=launchpad.net/gobson/bson
4
5GOFILES=\
6 gobson.go\
7 encode.go\
8 decode.go\
9
10include $(GOROOT)/src/Make.pkg
11
12GOFMT=gofmt
13BADFMT=$(shell $(GOFMT) -l $(GOFILES) $(wildcard *_test.go) 2> /dev/null)
14
15gofmt: $(BADFMT)
16 @for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done
17
18ifneq ($(BADFMT),)
19ifneq ($(MAKECMDGOALS),gofmt)
20$(warning WARNING: make gofmt: $(BADFMT))
21endif
22endif
023
=== renamed file 'Makefile' => 'Makefile.moved'
=== added file 'decode.go'
--- decode.go 1970-01-01 00:00:00 +0000
+++ decode.go 2011-07-28 17:08:30 +0000
@@ -0,0 +1,596 @@
1// gobson - BSON library for Go.
2//
3// Copyright (c) 2010-2011 - 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// * Redistributions of source code must retain the above copyright notice,
11// this list of conditions and the following disclaimer.
12// * 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// * Neither the name of the copyright holder nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31package bson
32
33import (
34 "reflect"
35 "math"
36 "fmt"
37)
38
39type decoder struct {
40 in []byte
41 i int
42}
43
44// --------------------------------------------------------------------------
45// Some helper functions.
46
47func corrupted() {
48 panic("Document is corrupted")
49}
50
51func zeroNilPtr(v reflect.Value) (changed bool) {
52 if v.Kind() == reflect.Ptr && v.IsNil() {
53 v.Set(reflect.New(v.Type().Elem()))
54 return true
55 }
56 return false
57}
58
59func settableValueOf(i interface{}) reflect.Value {
60 v := reflect.ValueOf(i)
61 sv := reflect.New(v.Type()).Elem()
62 sv.Set(v)
63 return sv
64}
65
66// --------------------------------------------------------------------------
67// Unmarshaling of documents.
68
69func (d *decoder) readDocTo(out reflect.Value) {
70 var elemType reflect.Type
71 outt := out.Type()
72 outk := outt.Kind()
73
74 for {
75 if outk == reflect.Ptr && out.IsNil() {
76 out.Set(reflect.New(outt.Elem()))
77 }
78 if setter, ok := out.Interface().(Setter); ok {
79 setter.SetBSON(d.readDocD())
80 return
81 }
82 if outk == reflect.Ptr {
83 out = out.Elem()
84 outt = out.Type()
85 outk = out.Kind()
86 continue
87 }
88 break
89 }
90
91 var fieldsMap map[string]fieldInfo
92 start := d.i
93
94 switch outk {
95 case reflect.Interface:
96 if !out.IsNil() {
97 panic("Found non-nil interface. Please contact the developers.")
98 }
99 mv := reflect.ValueOf(make(M))
100 out.Set(mv)
101 out = mv
102 outt = out.Type()
103 outk = outt.Kind()
104 fallthrough
105 case reflect.Map:
106 if outt.Key().Kind() != reflect.String {
107 panic("BSON map must have string keys. Got: " + outt.String())
108 }
109 elemType = outt.Elem()
110 if out.IsNil() {
111 out.Set(reflect.MakeMap(out.Type()))
112 }
113 case reflect.Struct:
114 if outt != typeRaw {
115 fields, err := getStructFields(out.Type())
116 if err != nil {
117 panic(err)
118 }
119 fieldsMap = fields.Map
120 }
121 default:
122 panic("TESTME:" + reflect.ValueOf(out).Type().String())
123 }
124
125 end := d.i - 4 + int(d.readInt32())
126 if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
127 corrupted()
128 }
129 for d.in[d.i] != '\x00' {
130 kind := d.readByte()
131 name := d.readCStr()
132 if d.i >= end {
133 corrupted()
134 }
135
136 switch outk {
137 case reflect.Map:
138 e := reflect.New(elemType).Elem()
139 if d.readElemTo(e, kind) {
140 out.SetMapIndex(reflect.ValueOf(name), e)
141 }
142 case reflect.Struct:
143 if outt == typeRaw {
144 d.readElemTo(blackHole, kind)
145 } else {
146 if info, ok := fieldsMap[name]; ok {
147 d.readElemTo(out.Field(info.Num), kind)
148 } else {
149 d.dropElem(kind)
150 }
151 }
152 }
153
154 if d.i >= end {
155 corrupted()
156 }
157 }
158 d.i++ // '\x00'
159 if d.i != end {
160 corrupted()
161 }
162
163 switch outk {
164 case reflect.Struct:
165 if outt == typeRaw {
166 out.Set(reflect.ValueOf(Raw{0x03, d.in[start:d.i]}))
167 }
168 }
169}
170
171func (d *decoder) readArrayDoc(t reflect.Type) interface{} {
172 tmp := make([]reflect.Value, 0, 8)
173 elemType := t.Elem()
174
175 end := d.i - 4 + int(d.readInt32())
176 if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
177 corrupted()
178 }
179 for d.in[d.i] != '\x00' {
180 kind := d.readByte()
181 for d.i < end && d.in[d.i] != '\x00' {
182 d.i++
183 }
184 if d.i >= end {
185 corrupted()
186 }
187 d.i++
188 e := reflect.New(elemType).Elem()
189 if d.readElemTo(e, kind) {
190 tmp = append(tmp, e)
191 }
192 if d.i >= end {
193 corrupted()
194 }
195 }
196 d.i++ // '\x00'
197 if d.i != end {
198 corrupted()
199 }
200
201 n := len(tmp)
202 slice := reflect.MakeSlice(t, n, n)
203 for i := 0; i != n; i++ {
204 slice.Index(i).Set(tmp[i])
205 }
206 return slice.Interface()
207}
208
209var typeD = reflect.TypeOf(D{})
210var typeSlice = reflect.TypeOf([]interface{}{})
211
212func (d *decoder) readDocD() interface{} {
213 slice := make(D, 0, 8)
214 d.readDocWith(func(kind byte, name string) {
215 e := DocElem{Name: name}
216 v := reflect.ValueOf(&e.Value)
217 if d.readElemTo(v.Elem(), kind) {
218 slice = append(slice, e)
219 }
220 })
221 return slice
222}
223
224func (d *decoder) readDocWith(f func(kind byte, name string)) {
225 end := d.i - 4 + int(d.readInt32())
226 if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
227 corrupted()
228 }
229 for d.in[d.i] != '\x00' {
230 kind := d.readByte()
231 name := d.readCStr()
232 if d.i >= end {
233 corrupted()
234 }
235 f(kind, name)
236 if d.i >= end {
237 corrupted()
238 }
239 }
240 d.i++ // '\x00'
241 if d.i != end {
242 corrupted()
243 }
244}
245
246// --------------------------------------------------------------------------
247// Unmarshaling of individual elements within a document.
248
249var blackHole = settableValueOf(struct{}{})
250
251func (d *decoder) dropElem(kind byte) {
252 d.readElemTo(blackHole, kind)
253}
254
255// Attempt to decode an element from the document and put it into out.
256// If the types are not compatible, the returned ok value will be
257// false and out will be unchanged.
258func (d *decoder) readElemTo(out reflect.Value, kind byte) (good bool) {
259
260 start := d.i
261
262 if kind == '\x03' {
263 // Special case for documents. Delegate to readDocTo().
264 switch out.Kind() {
265 case reflect.Interface, reflect.Ptr, reflect.Struct, reflect.Map:
266 d.readDocTo(out)
267 default:
268 if _, ok := out.Interface().(D); ok {
269 out.Set(reflect.ValueOf(d.readDocD()))
270 } else {
271 d.readDocTo(blackHole)
272 }
273 }
274 return true
275 }
276
277 var in interface{}
278
279 switch kind {
280 case '\x01': // Float64
281 in = d.readFloat64()
282 case '\x02': // UTF-8 string
283 in = d.readStr()
284 case '\x03': // Document
285 panic("Can't happen. Handled above.")
286 case '\x04': // Array
287 outt := out.Type()
288 for outt.Kind() == reflect.Ptr {
289 outt = outt.Elem()
290 }
291 switch outt.Kind() {
292 case reflect.Slice:
293 in = d.readArrayDoc(outt)
294 default:
295 in = d.readArrayDoc(typeSlice)
296 }
297 case '\x05': // Binary
298 b := d.readBinary()
299 if b.Kind == 0x00 {
300 in = b.Data
301 } else {
302 in = b
303 }
304 case '\x06': // Undefined (obsolete, but still seen in the wild)
305 in = Undefined
306 case '\x07': // ObjectId
307 in = ObjectId(d.readBytes(12))
308 case '\x08': // Bool
309 in = d.readBool()
310 case '\x09': // Timestamp
311 // MongoDB wants timestamps as milliseconds.
312 // Go likes nanoseconds. Convert them.
313 in = Timestamp(d.readInt64() * 1e6)
314 case '\x0A': // Nil
315 in = nil
316 case '\x0B': // RegEx
317 in = d.readRegEx()
318 case '\x0D': // JavaScript without scope
319 in = JS{Code: d.readStr()}
320 case '\x0E': // Symbol
321 in = Symbol(d.readStr())
322 case '\x0F': // JavaScript with scope
323 d.i += 4 // Skip length
324 js := JS{d.readStr(), make(M)}
325 d.readDocTo(reflect.ValueOf(js.Scope))
326 in = js
327 case '\x10': // Int32
328 in = int(d.readInt32())
329 case '\x11': // Mongo-specific timestamp
330 in = MongoTimestamp(d.readInt64())
331 case '\x12': // Int64
332 in = d.readInt64()
333 case '\x7F': // Max key
334 in = MaxKey
335 case '\xFF': // Min key
336 in = MinKey
337 default:
338 panic(fmt.Sprintf("Unknown element kind (0x%02X)", kind))
339 }
340
341 outt := out.Type()
342
343 if outt == typeRaw {
344 out.Set(reflect.ValueOf(Raw{kind, d.in[start:d.i]}))
345 return true
346 }
347
348 if setter, ok := out.Interface().(Setter); ok {
349 if zeroNilPtr(out) {
350 setter = out.Interface().(Setter)
351 }
352 return setter.SetBSON(in)
353 }
354
355 if in == nil {
356 out.Set(reflect.Zero(outt))
357 return true
358 }
359
360 outk := outt.Kind()
361
362 // Dereference and initialize pointer if necessary.
363 first := true
364 for outk == reflect.Ptr {
365 if !out.IsNil() {
366 out = out.Elem()
367 } else {
368 elem := reflect.New(outt.Elem())
369 if first {
370 // Only set if value is compatible.
371 first = false
372 defer func(out, elem reflect.Value) {
373 if good {
374 out.Set(elem)
375 }
376 }(out, elem)
377 } else {
378 out.Set(elem)
379 }
380 out = elem
381 }
382 outt = out.Type()
383 outk = outt.Kind()
384 }
385
386 inv := reflect.ValueOf(in)
387 if outt == inv.Type() {
388 out.Set(inv)
389 return true
390 }
391
392 switch outk {
393 case reflect.Interface:
394 out.Set(inv)
395 return true
396 case reflect.String:
397 switch inv.Kind() {
398 case reflect.String:
399 out.SetString(inv.String())
400 return true
401 case reflect.Slice:
402 if b, ok := in.([]byte); ok {
403 out.SetString(string(b))
404 return true
405 }
406 }
407 case reflect.Slice, reflect.Array:
408 // Remember, array (0x04) slices are built with the correct element
409 // type. If we are here, must be a cross BSON kind conversion.
410 if outt.Elem().Kind() == reflect.Uint8 {
411 switch inv.Kind() {
412 case reflect.String:
413 slice := []byte(inv.String())
414 out.Set(reflect.ValueOf(slice))
415 return true
416 case reflect.Slice:
417 // out must be an array. A slice would trigger
418 // inv.Type() == out.Type() above.
419 reflect.Copy(out, inv)
420 return true
421 }
422 }
423 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
424 switch inv.Kind() {
425 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
426 // MongoDB wants timestamps as milliseconds.
427 // Go likes nanoseconds. Convert them.
428 // out.Type() == inv.Type() has been handled above.
429 if outt == typeTimestamp {
430 out.SetInt(inv.Int() * 1e6)
431 } else if inv.Type() == typeTimestamp {
432 out.SetInt(inv.Int() / 1e6)
433 } else {
434 out.SetInt(inv.Int())
435 }
436 return true
437 case reflect.Float32, reflect.Float64:
438 out.SetInt(int64(inv.Float()))
439 return true
440 case reflect.Bool:
441 if inv.Bool() {
442 out.SetInt(1)
443 } else {
444 out.SetInt(0)
445 }
446 return true
447 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
448 panic("Can't happen. No uint types in BSON?")
449 }
450 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
451 switch inv.Kind() {
452 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
453 out.SetUint(uint64(inv.Int()))
454 return true
455 case reflect.Float32, reflect.Float64:
456 out.SetUint(uint64(inv.Float()))
457 return true
458 case reflect.Bool:
459 if inv.Bool() {
460 out.SetUint(1)
461 } else {
462 out.SetUint(0)
463 }
464 return true
465 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
466 panic("Can't happen. No uint types in BSON?")
467 }
468 case reflect.Float32, reflect.Float64:
469 switch inv.Kind() {
470 case reflect.Float32, reflect.Float64:
471 out.SetFloat(inv.Float())
472 return true
473 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
474 out.SetFloat(float64(inv.Int()))
475 return true
476 case reflect.Bool:
477 if inv.Bool() {
478 out.SetFloat(1)
479 } else {
480 out.SetFloat(0)
481 }
482 return true
483 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
484 panic("Can't happen. No uint types in BSON?")
485 }
486 case reflect.Bool:
487 switch inv.Kind() {
488 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
489 out.SetBool(inv.Int() != 0)
490 return true
491 case reflect.Float32, reflect.Float64:
492 out.SetBool(inv.Float() != 0)
493 return true
494 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
495 panic("Can't happen. No uint types in BSON?")
496 }
497 }
498
499 return false
500}
501
502// --------------------------------------------------------------------------
503// Parsers of basic types.
504
505func (d *decoder) readRegEx() RegEx {
506 re := RegEx{}
507 re.Pattern = d.readCStr()
508 re.Options = d.readCStr()
509 return re
510}
511
512func (d *decoder) readBinary() Binary {
513 l := d.readInt32()
514 b := Binary{}
515 b.Kind = d.readByte()
516 b.Data = d.readBytes(l)
517 if b.Kind == 0x02 {
518 // Weird obsolete format with redundant length.
519 b.Data = b.Data[4:]
520 }
521 return b
522}
523
524func (d *decoder) readStr() string {
525 l := d.readInt32()
526 b := d.readBytes(l - 1)
527 if d.readByte() != '\x00' {
528 corrupted()
529 }
530 return string(b)
531}
532
533func (d *decoder) readCStr() string {
534 start := d.i
535 end := start
536 l := len(d.in)
537 for ; end != l; end++ {
538 if d.in[end] == '\x00' {
539 break
540 }
541 }
542 d.i = end + 1
543 if d.i > l {
544 corrupted()
545 }
546 return string(d.in[start:end])
547}
548
549func (d *decoder) readBool() bool {
550 if d.readByte() == 1 {
551 return true
552 }
553 return false
554}
555
556func (d *decoder) readFloat64() float64 {
557 return math.Float64frombits(uint64(d.readInt64()))
558}
559
560func (d *decoder) readInt32() int32 {
561 b := d.readBytes(4)
562 return int32((uint32(b[0]) << 0) |
563 (uint32(b[1]) << 8) |
564 (uint32(b[2]) << 16) |
565 (uint32(b[3]) << 24))
566}
567
568func (d *decoder) readInt64() int64 {
569 b := d.readBytes(8)
570 return int64((uint64(b[0]) << 0) |
571 (uint64(b[1]) << 8) |
572 (uint64(b[2]) << 16) |
573 (uint64(b[3]) << 24) |
574 (uint64(b[4]) << 32) |
575 (uint64(b[5]) << 40) |
576 (uint64(b[6]) << 48) |
577 (uint64(b[7]) << 56))
578}
579
580func (d *decoder) readByte() byte {
581 i := d.i
582 d.i++
583 if d.i > len(d.in) {
584 corrupted()
585 }
586 return d.in[i]
587}
588
589func (d *decoder) readBytes(length int32) []byte {
590 start := d.i
591 d.i += int(length)
592 if d.i > len(d.in) {
593 corrupted()
594 }
595 return d.in[start : start+int(length)]
596}
0597
=== added file 'encode.go'
--- encode.go 1970-01-01 00:00:00 +0000
+++ encode.go 2011-07-28 17:08:30 +0000
@@ -0,0 +1,420 @@
1// gobson - BSON library for Go.
2//
3// Copyright (c) 2010-2011 - 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// * Redistributions of source code must retain the above copyright notice,
11// this list of conditions and the following disclaimer.
12// * 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// * Neither the name of the copyright holder nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31package bson
32
33import (
34 "strconv"
35 "reflect"
36 "math"
37)
38
39// --------------------------------------------------------------------------
40// Some internal infrastructure.
41
42var (
43 typeRegEx reflect.Type
44 typeBinary reflect.Type
45 typeObjectId reflect.Type
46 typeSymbol reflect.Type
47 typeTimestamp reflect.Type
48 typeMongoTimestamp reflect.Type
49 typeOrderKey reflect.Type
50 typeDocElem reflect.Type
51 typeRaw reflect.Type
52)
53
54const itoaCacheSize = 32
55
56var itoaCache []string
57
58func init() {
59 typeBinary = reflect.TypeOf(Binary{})
60 typeObjectId = reflect.TypeOf(ObjectId(""))
61 typeSymbol = reflect.TypeOf(Symbol(""))
62 typeTimestamp = reflect.TypeOf(Timestamp(0))
63 typeMongoTimestamp = reflect.TypeOf(MongoTimestamp(0))
64 typeOrderKey = reflect.TypeOf(MinKey)
65 typeDocElem = reflect.TypeOf(DocElem{})
66 typeRaw = reflect.TypeOf(Raw{})
67
68 itoaCache = make([]string, itoaCacheSize)
69 for i := 0; i != itoaCacheSize; i++ {
70 itoaCache[i] = strconv.Itoa(i)
71 }
72}
73
74func itoa(i int) string {
75 if i < itoaCacheSize {
76 return itoaCache[i]
77 }
78 return strconv.Itoa(i)
79}
80
81
82// --------------------------------------------------------------------------
83// Marshaling of the document value itself.
84
85type encoder struct {
86 out []byte
87}
88
89func (e *encoder) addDoc(v reflect.Value) {
90 for {
91 if vi, ok := v.Interface().(Getter); ok {
92 v = reflect.ValueOf(vi.GetBSON())
93 continue
94 }
95 if v.Kind() == reflect.Ptr {
96 v = v.Elem()
97 continue
98 }
99 break
100 }
101
102 if v.Type() == typeRaw {
103 raw := v.Interface().(Raw)
104 if raw.Kind != 0x03 && raw.Kind != 0x00 {
105 panic("Attempted to unmarshal Raw kind " + strconv.Itoa(int(raw.Kind)) + " as a document")
106 }
107 e.addBytes(raw.Data...)
108 return
109 }
110
111 start := e.reserveInt32()
112
113 switch v.Kind() {
114 case reflect.Map:
115 e.addMap(v)
116 case reflect.Struct:
117 e.addStruct(v)
118 case reflect.Array, reflect.Slice:
119 e.addSlice(v)
120 default:
121 panic("Can't marshal " + v.Type().String() + " as a BSON document")
122 }
123
124 e.addBytes(0)
125 e.setInt32(start, int32(len(e.out)-start))
126}
127
128func (e *encoder) addMap(v reflect.Value) {
129 for _, k := range v.MapKeys() {
130 e.addElem(k.String(), v.MapIndex(k), false)
131 }
132}
133
134func (e *encoder) addStruct(v reflect.Value) {
135 fields, err := getStructFields(v.Type())
136 if err != nil {
137 panic(err)
138 }
139 for i, info := range fields.List {
140 value := v.Field(i)
141 if info.Conditional && isZero(value) {
142 continue
143 }
144 e.addElem(info.Key, value, info.Short)
145 }
146}
147
148func isZero(v reflect.Value) bool {
149 switch v.Kind() {
150 case reflect.String:
151 return len(v.String()) == 0
152 case reflect.Ptr, reflect.Interface:
153 return v.IsNil()
154 case reflect.Slice:
155 return v.Len() == 0
156 case reflect.Map:
157 return v.Len() == 0
158 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
159 return v.Int() == 0
160 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
161 return v.Uint() == 0
162 case reflect.Bool:
163 return !v.Bool()
164 }
165 return false
166}
167
168func (e *encoder) addSlice(v reflect.Value) {
169 if d, ok := v.Interface().(D); ok {
170 for _, elem := range d {
171 e.addElem(elem.Name, reflect.ValueOf(elem.Value), false)
172 }
173 } else {
174 for i := 0; i != v.Len(); i++ {
175 e.addElem(itoa(i), v.Index(i), false)
176 }
177 }
178}
179
180
181// --------------------------------------------------------------------------
182// Marshaling of elements in a document.
183
184func (e *encoder) addElemName(kind byte, name string) {
185 e.addBytes(kind)
186 e.addBytes([]byte(name)...)
187 e.addBytes(0)
188}
189
190func (e *encoder) addElem(name string, v reflect.Value, short bool) {
191
192 if !v.IsValid() {
193 e.addElemName('\x0A', name)
194 return
195 }
196
197 if getter, ok := v.Interface().(Getter); ok {
198 e.addElem(name, reflect.ValueOf(getter.GetBSON()), short)
199 return
200 }
201
202 switch v.Kind() {
203
204 case reflect.Interface:
205 e.addElem(name, v.Elem(), short)
206
207 case reflect.Ptr:
208 e.addElem(name, v.Elem(), short)
209
210 case reflect.String:
211 s := v.String()
212
213 switch v.Type() {
214
215 case typeObjectId:
216 if len(s) != 12 {
217 panic("ObjectIDs must be exactly 12 bytes long (got " +
218 strconv.Itoa(len(s)) + ")")
219 }
220 e.addElemName('\x07', name)
221 e.addBytes([]byte(s)...)
222
223 case typeSymbol:
224 e.addElemName('\x0E', name)
225 e.addStr(s)
226
227 default:
228 e.addElemName('\x02', name)
229 e.addStr(s)
230 }
231
232 case reflect.Float32, reflect.Float64:
233 e.addElemName('\x01', name)
234 e.addInt64(int64(math.Float64bits(v.Float())))
235
236 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
237 u := v.Uint()
238 if int64(u) < 0 {
239 panic("BSON has no uint64 type, and value is too large to fit correctly in an int64")
240 } else if u <= math.MaxInt32 && (short || v.Kind() <= reflect.Uint32) {
241 e.addElemName('\x10', name)
242 e.addInt32(int32(u))
243 } else {
244 e.addElemName('\x12', name)
245 e.addInt64(int64(u))
246 }
247
248 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
249 if v.Type().Kind() <= reflect.Int32 {
250 e.addElemName('\x10', name)
251 e.addInt32(int32(v.Int()))
252 } else {
253 switch v.Type() {
254
255 case typeTimestamp:
256 // MongoDB wants timestamps as milliseconds.
257 // Go likes nanoseconds. Convert them.
258 e.addElemName('\x09', name)
259 e.addInt64(v.Int() / 1e6)
260
261 case typeMongoTimestamp:
262 e.addElemName('\x11', name)
263 e.addInt64(v.Int())
264
265 case typeOrderKey:
266 if v.Int() == int64(MaxKey) {
267 e.addElemName('\x7F', name)
268 } else {
269 e.addElemName('\xFF', name)
270 }
271
272 default:
273 i := v.Int()
274 if short && i >= math.MinInt32 && i <= math.MaxInt32 {
275 // It fits into an int32, encode as such.
276 e.addElemName('\x10', name)
277 e.addInt32(int32(i))
278 } else {
279 e.addElemName('\x12', name)
280 e.addInt64(i)
281 }
282 }
283 }
284
285 case reflect.Bool:
286 e.addElemName('\x08', name)
287 if v.Bool() {
288 e.addBytes(1)
289 } else {
290 e.addBytes(0)
291 }
292
293 case reflect.Map:
294 e.addElemName('\x03', name)
295 e.addDoc(v)
296
297 case reflect.Slice:
298 vt := v.Type()
299 et := vt.Elem()
300 if et.Kind() == reflect.Uint8 {
301 // FIXME: This breaks down with custom types based on []byte
302 e.addElemName('\x05', name)
303 e.addBinary('\x00', v.Interface().([]byte))
304 } else if et == typeDocElem {
305 e.addElemName('\x03', name)
306 e.addDoc(v)
307 } else {
308 e.addElemName('\x04', name)
309 e.addDoc(v)
310 }
311
312 case reflect.Array:
313 et := v.Type().Elem()
314 if et.Kind() == reflect.Uint8 {
315 e.addElemName('\x05', name)
316 e.addBinary('\x00', v.Slice(0, v.Len()).Interface().([]byte))
317 } else {
318 e.addElemName('\x04', name)
319 e.addDoc(v)
320 }
321
322 case reflect.Struct:
323 switch s := v.Interface().(type) {
324
325 case Raw:
326 kind := s.Kind
327 if kind == 0x00 {
328 kind = 0x03
329 }
330 e.addElemName(kind, name)
331 e.addBytes(s.Data...)
332
333 case Binary:
334 e.addElemName('\x05', name)
335 e.addBinary(s.Kind, s.Data)
336
337 case RegEx:
338 e.addElemName('\x0B', name)
339 e.addCStr(s.Pattern)
340 e.addCStr(s.Options)
341
342 case JS:
343 if s.Scope == nil {
344 e.addElemName('\x0D', name)
345 e.addStr(s.Code)
346 } else {
347 e.addElemName('\x0F', name)
348 start := e.reserveInt32()
349 e.addStr(s.Code)
350 e.addDoc(reflect.ValueOf(s.Scope))
351 e.setInt32(start, int32(len(e.out)-start))
352 }
353
354 case undefined:
355 e.addElemName('\x06', name)
356
357 default:
358 e.addElemName('\x03', name)
359 e.addDoc(v)
360 }
361
362 default:
363 panic("Can't marshal " + v.Type().String() + " in a BSON document")
364 }
365}
366
367
368// --------------------------------------------------------------------------
369// Marshaling of base types.
370
371func (e *encoder) addBinary(subtype byte, v []byte) {
372 if subtype == 0x02 {
373 // Wonder how that brilliant idea came to life. Obsolete, luckily.
374 e.addInt32(int32(len(v) + 4))
375 e.addBytes(subtype)
376 e.addInt32(int32(len(v)))
377 } else {
378 e.addInt32(int32(len(v)))
379 e.addBytes(subtype)
380 }
381 e.addBytes(v...)
382}
383
384func (e *encoder) addStr(v string) {
385 e.addInt32(int32(len(v) + 1))
386 e.addCStr(v)
387}
388
389func (e *encoder) addCStr(v string) {
390 e.addBytes([]byte(v)...)
391 e.addBytes(0)
392}
393
394func (e *encoder) reserveInt32() (pos int) {
395 pos = len(e.out)
396 e.addBytes(0, 0, 0, 0)
397 return pos
398}
399
400func (e *encoder) setInt32(pos int, v int32) {
401 e.out[pos+0] = byte(v)
402 e.out[pos+1] = byte(v >> 8)
403 e.out[pos+2] = byte(v >> 16)
404 e.out[pos+3] = byte(v >> 24)
405}
406
407func (e *encoder) addInt32(v int32) {
408 u := uint32(v)
409 e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24))
410}
411
412func (e *encoder) addInt64(v int64) {
413 u := uint64(v)
414 e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24),
415 byte(u>>32), byte(u>>40), byte(u>>48), byte(u>>56))
416}
417
418func (e *encoder) addBytes(v ...byte) {
419 e.out = append(e.out, v...)
420}
0421
=== added file 'gobson.go'
--- gobson.go 1970-01-01 00:00:00 +0000
+++ gobson.go 2011-07-28 17:08:30 +0000
@@ -0,0 +1,483 @@
1// gobson - BSON library for Go.
2//
3// Copyright (c) 2010-2011 - 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// * Redistributions of source code must retain the above copyright notice,
11// this list of conditions and the following disclaimer.
12// * 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// * Neither the name of the copyright holder nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31package bson
32
33import (
34 "encoding/binary"
35 "encoding/hex"
36 "crypto/md5"
37 "runtime"
38 "reflect"
39 "strings"
40 "sync/atomic"
41 "sync"
42 "time"
43 "fmt"
44 "os"
45)
46
47// --------------------------------------------------------------------------
48// The public API.
49
50// Objects implementing the bson.Getter interface will get the GetBSON()
51// method called when the given value has to be marshalled, and the result
52// of this method will be marshaled in place of the actual object.
53type Getter interface {
54 GetBSON() interface{}
55}
56
57// Objects implementing the bson.Setter interface will receive the BSON
58// value via the SetBSON method during unmarshaling, and will not be
59// changed as usual. If setting the value works, the method should
60// return true. If it returns false, the given value will be omitted
61// from maps and slices.
62type Setter interface {
63 SetBSON(v interface{}) (ok bool)
64}
65
66// Handy alias for a map[string]interface{} map, useful for dealing with BSON
67// in a native way. For instance:
68//
69// bson.M{"a": 1, "b": true}
70//
71// There's no special handling for this type in addition to what's done anyway
72// for an equivalent map type. Elements in the map will be dumped in an
73// undefined ordered. See also the bson.D type for an ordered alternative.
74type M map[string]interface{}
75
76// Type for dealing with documents containing ordered elements in a native
77// fashion. For instance:
78//
79// bson.D{{"a", 1}, {"b", true}}
80//
81// In some situations, such as when creating indexes for MongoDB, the order in
82// which the elements are defined is important. If the order is not important,
83// using a map is generally more comfortable (see the bson.M type and the
84// Map() method for D).
85type D []DocElem
86
87// See the bson.D type.
88type DocElem struct {
89 Name string
90 Value interface{}
91}
92
93// Raw may be used to work with raw unprocessed BSON documents and elements,
94// if necessary in advanced cases. Kind is the kind of element as defined
95// per the BSON specification, and Data is the raw unprocessed data for
96// the respective element.
97//
98// Relevant documentation:
99//
100// http://bsonspec.org/#/specification
101//
102type Raw struct {
103 Kind byte
104 Data []byte
105}
106
107// Build a map[string]interface{} out of the ordered element name/value pairs.
108func (d D) Map() (m M) {
109 m = make(M, len(d))
110 for _, item := range d {
111 m[item.Name] = item.Value
112 }
113 return m
114}
115
116// Unique ID identifying the BSON object. Must be exactly 12 bytes long.
117// MongoDB objects by default have such a property set in their "_id"
118// property.
119//
120// http://www.mongodb.org/display/DOCS/Object+IDs
121type ObjectId string
122
123// ObjectIdHex returns an ObjectId from the provided hex representation.
124// Calling this function with an invalid hex representation will
125// cause a runtime panic.
126func ObjectIdHex(s string) ObjectId {
127 d, err := hex.DecodeString(s)
128 if err != nil || len(d) != 12 {
129 panic(fmt.Sprintf("Invalid input to ObjectIdHex: %q", s))
130 }
131 return ObjectId(d)
132}
133
134// objectIdCounter is atomically incremented when generating a new ObjectId
135// using NewObjectId() function. It's used as a counter part of an id.
136var objectIdCounter uint32 = 0
137
138// machineId stores machine id generated once and used in subsequent calls
139// to NewObjectId function.
140var machineId []byte
141
142// initMachineId generates machine id and puts it into the machineId global
143// variable. If this function fails to get the hostname, it will cause
144// a runtime error.
145func initMachineId() {
146 var sum [3]byte
147 hostname, err := os.Hostname()
148 if err != nil {
149 panic("Failed to get hostname: " + err.String())
150 }
151 hw := md5.New()
152 hw.Write([]byte(hostname))
153 copy(sum[:3], hw.Sum())
154 machineId = sum[:]
155}
156
157// NewObjectId generates and returns a new unique ObjectId.
158// This function causes a runtime error if it fails to get the hostname
159// of the current machine.
160func NewObjectId() ObjectId {
161 b := make([]byte, 12)
162 // Timestamp, 4 bytes, big endian
163 binary.BigEndian.PutUint32(b, uint32(time.Seconds()))
164 // Machine, first 3 bytes of md5(hostname)
165 if machineId == nil {
166 initMachineId()
167 }
168 b[4] = machineId[0]
169 b[5] = machineId[1]
170 b[6] = machineId[2]
171 // Pid, 2 bytes, specs don't specify endianness, but we use big endian.
172 pid := os.Getpid()
173 b[7] = byte(pid >> 8)
174 b[8] = byte(pid)
175 // Increment, 3 bytes, big endian
176 i := atomic.AddUint32(&objectIdCounter, 1)
177 b[9] = byte(i >> 16)
178 b[10] = byte(i >> 8)
179 b[11] = byte(i)
180 return ObjectId(b)
181}
182
183// NewObjectIdSeconds returns a dummy ObjectId with the timestamp part filled
184// with the provided number of seconds from epoch UTC, and all other parts
185// filled with zeroes. It's not safe to insert a document with an id generated
186// by this method, it is useful only for queries to find documents with ids
187// generated before or after the specified timestamp.
188func NewObjectIdSeconds(sec int32) ObjectId {
189 var b [12]byte
190 binary.BigEndian.PutUint32(b[:4], uint32(sec))
191 return ObjectId(string(b[:]))
192}
193
194// String returns a hex string representation of the id.
195// Example: ObjectIdHex("4d88e15b60f486e428412dc9").
196func (id ObjectId) String() string {
197 return `ObjectIdHex("` + hex.EncodeToString([]byte(string(id))) + `")`
198}
199
200// Valid returns true if the id is valid (contains exactly 12 bytes)
201func (id ObjectId) Valid() bool {
202 return len(id) == 12
203}
204
205// byteSlice returns byte slice of id from start to end.
206// Calling this function with an invalid id will cause a runtime panic.
207func (id ObjectId) byteSlice(start, end int) []byte {
208 if len(id) != 12 {
209 panic(fmt.Sprintf("Invalid ObjectId: %q", string(id)))
210 }
211 return []byte(string(id)[start:end])
212}
213
214// Timestamp returns the timestamp part of the id (the number of seconds
215// from epoch in UTC).
216// It's a runtime error to call this method with an invalid id.
217func (id ObjectId) Timestamp() int32 {
218 // First 4 bytes of ObjectId is 32-bit big-endian timestamp
219 return int32(binary.BigEndian.Uint32(id.byteSlice(0, 4)))
220}
221
222// Machine returns the 3-byte machine id part of the id.
223// It's a runtime error to call this method with an invalid id.
224func (id ObjectId) Machine() []byte {
225 return id.byteSlice(4, 7)
226}
227
228// Pid returns the process id part of the id.
229// It's a runtime error to call this method with an invalid id.
230func (id ObjectId) Pid() uint16 {
231 return binary.BigEndian.Uint16(id.byteSlice(7, 9))
232}
233
234// Counter returns the incrementing value part of the id.
235// It's a runtime error to call this method with an invalid id.
236func (id ObjectId) Counter() int32 {
237 b := id.byteSlice(9, 12)
238 // Counter is stored as big-endian 3-byte value
239 return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2]))
240}
241
242// Similar to a string, but used in languages with a distinct symbol type. This
243// is an alias to a string type, so it can be used in string contexts and
244// string(symbol) will work correctly.
245type Symbol string
246
247// UTC timestamp defined as nanoseconds since the traditional epoch time. The
248// internal MongoDB representation stores this value as milliseconds, so some
249// precision will be lost when sending a Go value to MongoDB, but given that
250// Go most commonly uses nanoseconds in time-related operations, this conversion
251// is convenient.
252type Timestamp int64
253
254// Now returns a Timestamp value with the current time in nanoseconds.
255func Now() Timestamp {
256 // The value is stored in MongoDB as milliseconds, so truncate the value
257 // ahead of time to avoid surprises after a roundtrip.
258 return Timestamp(time.Nanoseconds() / 1e6 * 1e6)
259}
260
261// Special internal type used by MongoDB which for some strange reason has its
262// own datatype defined in BSON.
263type MongoTimestamp int64
264
265type orderKey int64
266
267// Special value which compares higher than all other possible BSON values.
268var MaxKey = orderKey(1<<63 - 1)
269
270// Special value which compares lower than all other possible BSON values.
271var MinKey = orderKey(-1 << 63)
272
273type undefined struct{}
274
275var Undefined undefined
276
277// Representation for non-standard binary values. Any kind should work,
278// but the following are known as of this writing:
279//
280// 0x00 - Generic. This is decoded as []byte(data), not Binary{0x00, data}.
281// 0x01 - Function (!?)
282// 0x02 - Obsolete generic.
283// 0x03 - UUID
284// 0x05 - MD5
285// 0x80 - User defined.
286//
287type Binary struct {
288 Kind byte
289 Data []byte
290}
291
292// A special type for regular expressions. The Options field should contain
293// individual characters defining the way in which the pattern should be
294// applied, and must be sorted. Valid options as of this writing are 'i' for
295// case insensitive matching, 'm' for multi-line matching, 'x' for verbose
296// mode, 'l' to make \w, \W, and similar be locale-dependent, 's' for dot-all
297// mode (a '.' matches everything), and 'u' to make \w, \W, and similar match
298// unicode. The value of the Options parameter is not verified before being
299// marshaled into the BSON format.
300type RegEx struct {
301 Pattern string
302 Options string
303}
304
305// Special type for JavaScript code. If Scope is non-nil, it will be marshaled
306// as a mapping from identifiers to values which should be used when evaluating
307// the provided Code.
308type JS struct {
309 Code string
310 Scope interface{}
311}
312
313
314const initialBufferSize = 64
315
316func handleErr(err *os.Error) {
317 if r := recover(); r != nil {
318 if _, ok := r.(runtime.Error); ok {
319 panic(r)
320 } else if s, ok := r.(string); ok {
321 *err = os.NewError(s)
322 } else if e, ok := r.(os.Error); ok {
323 *err = e
324 } else {
325 panic(r)
326 }
327 }
328}
329
330
331// Marshal serializes the in document, which may be a map or a struct value.
332// In the case of struct values, only exported fields will be serialized.
333// These fields may optionally have tags to define the serialization key for
334// the respective fields. Without a tag, the lowercased field name is used
335// as the key for each field. If a field tag ends in "/c", that field will
336// only be serialized if it's not set to the zero value for the field type.
337// If a field tag ends with the "/s" suffix, an int64 value in the given
338// field will be serialized as an int32 if possible.
339func Marshal(in interface{}) (out []byte, err os.Error) {
340 defer handleErr(&err)
341 e := &encoder{make([]byte, 0, initialBufferSize)}
342 e.addDoc(reflect.ValueOf(in))
343 return e.out, nil
344}
345
346// Unmarshal deserializes data from in into the out value. The out value
347// must be a map or a pointer to a struct (or a pointer to a struct pointer).
348// In the case of struct values, field names are mapped to the struct using
349// the field tag as the key. If the field has no tag, its lowercased name
350// will be used as the default key. Nil values are properly initialized
351// when necessary.
352//
353// The target field types of out may not necessarily match the BSON values
354// of the provided data. If there is a sensible way to unmarshal the values
355// into the Go types, they will be converted. Otherwise, the incompatible
356// values will be silently skipped.
357func Unmarshal(in []byte, out interface{}) (err os.Error) {
358 defer handleErr(&err)
359 v := reflect.ValueOf(out)
360 switch v.Kind() {
361 case reflect.Map, reflect.Ptr:
362 d := &decoder{in: in}
363 d.readDocTo(v)
364 case reflect.Struct:
365 return os.NewError("Unmarshal can't deal with struct values. Use a pointer.")
366 default:
367 return os.NewError("Unmarshal needs a map or a pointer to a struct.")
368 }
369 return nil
370}
371
372// Unmarshal deserializes raw into the out value. In addition to whole
373// documents, Raw's Unmarshal may also be used to unmarshal the data for
374// individual elements within a partially unmarshalled document. This
375// enables parts of a document to be lazily and conditionally deserialized.
376//
377// If the out value type is not compatible with raw, a *bson.TypeError
378// is returned.
379func (raw Raw) Unmarshal(out interface{}) (err os.Error) {
380 defer handleErr(&err)
381 v := reflect.ValueOf(out)
382 switch v.Kind() {
383 case reflect.Map, reflect.Ptr:
384 d := &decoder{in: raw.Data}
385 good := d.readElemTo(v, raw.Kind)
386 if !good {
387 return &TypeError{v.Type(), raw.Kind}
388 }
389 default:
390 return os.NewError("Raw Unmarshal needs a map or a valid pointer.")
391 }
392 return nil
393}
394
395type TypeError struct {
396 Type reflect.Type
397 Kind byte
398}
399
400func (e *TypeError) String() string {
401 return fmt.Sprintf("BSON kind 0x%02x isn't compatible with type %s", e.Kind, e.Type.String())
402}
403
404// --------------------------------------------------------------------------
405// Maintain a mapping of keys to structure field indexes
406
407type structFields struct {
408 Map map[string]fieldInfo
409 List []fieldInfo
410}
411
412type fieldInfo struct {
413 Key string
414 Num int
415 Conditional bool
416 Short bool
417}
418
419var fieldMap = make(map[string]*structFields)
420var fieldMapMutex sync.RWMutex
421
422func getStructFields(st reflect.Type) (*structFields, os.Error) {
423 path := st.PkgPath()
424 name := st.Name()
425
426 fullName := path + "." + name
427 fieldMapMutex.RLock()
428 fields, found := fieldMap[fullName]
429 fieldMapMutex.RUnlock()
430 if found {
431 return fields, nil
432 }
433
434 n := st.NumField()
435 fieldsMap := make(map[string]fieldInfo)
436 fieldsList := make([]fieldInfo, n)
437 for i := 0; i != n; i++ {
438 field := st.Field(i)
439 if field.PkgPath != "" {
440 continue // Private field
441 }
442
443 info := fieldInfo{Num: i}
444
445 if s := strings.LastIndex(string(field.Tag), "/"); s != -1 {
446 for _, c := range field.Tag[s+1:] {
447 switch c {
448 case int('c'):
449 info.Conditional = true
450 case int('s'):
451 info.Short = true
452 default:
453 panic("Unsupported field flag: " + string([]int{c}))
454 }
455 }
456 field.Tag = field.Tag[:s]
457 }
458
459 if field.Tag != "" {
460 info.Key = string(field.Tag)
461 } else {
462 info.Key = strings.ToLower(field.Name)
463 }
464
465 if _, found = fieldsMap[info.Key]; found {
466 msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
467 return nil, os.NewError(msg)
468 }
469
470 fieldsList[len(fieldsMap)] = info
471 fieldsMap[info.Key] = info
472 }
473
474 fields = &structFields{fieldsMap, fieldsList[:len(fieldsMap)]}
475
476 if fullName != "." {
477 fieldMapMutex.Lock()
478 fieldMap[fullName] = fields
479 fieldMapMutex.Unlock()
480 }
481
482 return fields, nil
483}
0484
=== added file 'gobson_test.go'
--- gobson_test.go 1970-01-01 00:00:00 +0000
+++ gobson_test.go 2011-07-28 17:08:30 +0000
@@ -0,0 +1,1096 @@
1// gobson - BSON library for Go.
2//
3// Copyright (c) 2010-2011 - 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// * Redistributions of source code must retain the above copyright notice,
11// this list of conditions and the following disclaimer.
12// * 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// * Neither the name of the copyright holder nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31package bson_test
32
33import (
34 . "launchpad.net/gocheck"
35 "encoding/binary"
36 "testing"
37 "reflect"
38 "time"
39 "launchpad.net/gobson/bson"
40)
41
42
43func TestAll(t *testing.T) {
44 TestingT(t)
45}
46
47type S struct{}
48
49var _ = Suite(&S{})
50
51
52// Wrap up the document elements contained in data, prepending the int32
53// length of the data, and appending the '\x00' value closing the document.
54func wrapInDoc(data string) string {
55 result := make([]byte, len(data)+5)
56 binary.LittleEndian.PutUint32(result, uint32(len(result)))
57 copy(result[4:], []byte(data))
58 return string(result)
59}
60
61func makeZeroDoc(value interface{}) (zero interface{}) {
62 v := reflect.ValueOf(value)
63 t := v.Type()
64 if t.Kind() == reflect.Map {
65 mv := reflect.MakeMap(t)
66 zero = mv.Interface()
67 } else {
68 pv := reflect.New(v.Type().Elem())
69 zero = pv.Interface()
70 }
71 return zero
72}
73
74func testUnmarshal(c *C, data string, obj interface{}) {
75 zero := makeZeroDoc(obj)
76 err := bson.Unmarshal([]byte(data), zero)
77 c.Assert(err, IsNil)
78 c.Assert(zero, Equals, obj)
79}
80
81
82type testItemType struct {
83 obj interface{}
84 data string
85}
86
87// --------------------------------------------------------------------------
88// Samples from bsonspec.org:
89
90var sampleItems = []testItemType{
91 {bson.M{"hello": "world"},
92 "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00"},
93 {bson.M{"BSON": []interface{}{"awesome", float64(5.05), 1986}},
94 "1\x00\x00\x00\x04BSON\x00&\x00\x00\x00\x020\x00\x08\x00\x00\x00" +
95 "awesome\x00\x011\x00333333\x14@\x102\x00\xc2\x07\x00\x00\x00\x00"},
96}
97
98func (s *S) TestMarshalSampleItems(c *C) {
99 for i, item := range sampleItems {
100 data, err := bson.Marshal(item.obj)
101 c.Assert(err, IsNil)
102 c.Assert(string(data), Equals, item.data,
103 Bug("Failed on item %d", i))
104 }
105}
106
107func (s *S) TestUnmarshalSampleItems(c *C) {
108 for i, item := range sampleItems {
109 value := bson.M{}
110 err := bson.Unmarshal([]byte(item.data), value)
111 c.Assert(err, IsNil)
112 c.Assert(value, Equals, item.obj,
113 Bug("Failed on item %d", i))
114 }
115}
116
117// --------------------------------------------------------------------------
118// Every type, ordered by the type flag. These are not wrapped with the
119// length and last \x00 from the document. wrapInDoc() computes them.
120// Note that all of them should be supported as two-way conversions.
121
122var allItems = []testItemType{
123 {bson.M{},
124 ""},
125 {bson.M{"_": float64(5.05)},
126 "\x01_\x00333333\x14@"},
127 {bson.M{"_": "yo"},
128 "\x02_\x00\x03\x00\x00\x00yo\x00"},
129 {bson.M{"_": bson.M{"a": true}},
130 "\x03_\x00\x09\x00\x00\x00\x08a\x00\x01\x00"},
131 {bson.M{"_": []interface{}{true, false}},
132 "\x04_\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"},
133 {bson.M{"_": []byte("yo")},
134 "\x05_\x00\x02\x00\x00\x00\x00yo"},
135 {bson.M{"_": bson.Binary{0x02, []byte("old")}},
136 "\x05_\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"},
137 {bson.M{"_": bson.Binary{0x80, []byte("udef")}},
138 "\x05_\x00\x04\x00\x00\x00\x80udef"},
139 {bson.M{"_": bson.Undefined}, // Obsolete, but still seen in the wild.
140 "\x06_\x00"},
141 {bson.M{"_": bson.ObjectId("0123456789ab")},
142 "\x07_\x000123456789ab"},
143 {bson.M{"_": false},
144 "\x08_\x00\x00"},
145 {bson.M{"_": true},
146 "\x08_\x00\x01"},
147 {bson.M{"_": bson.Timestamp(258e6)}, // Note the NS <=> MS conversion.
148 "\x09_\x00\x02\x01\x00\x00\x00\x00\x00\x00"},
149 {bson.M{"_": nil},
150 "\x0A_\x00"},
151 {bson.M{"_": bson.RegEx{"ab", "cd"}},
152 "\x0B_\x00ab\x00cd\x00"},
153 {bson.M{"_": bson.JS{"code", nil}},
154 "\x0D_\x00\x05\x00\x00\x00code\x00"},
155 {bson.M{"_": bson.Symbol("sym")},
156 "\x0E_\x00\x04\x00\x00\x00sym\x00"},
157 {bson.M{"_": bson.JS{"code", bson.M{"": nil}}},
158 "\x0F_\x00\x14\x00\x00\x00\x05\x00\x00\x00code\x00" +
159 "\x07\x00\x00\x00\x0A\x00\x00"},
160 {bson.M{"_": 258},
161 "\x10_\x00\x02\x01\x00\x00"},
162 {bson.M{"_": bson.MongoTimestamp(258)},
163 "\x11_\x00\x02\x01\x00\x00\x00\x00\x00\x00"},
164 {bson.M{"_": int64(258)},
165 "\x12_\x00\x02\x01\x00\x00\x00\x00\x00\x00"},
166 {bson.M{"_": int64(258 << 32)},
167 "\x12_\x00\x00\x00\x00\x00\x02\x01\x00\x00"},
168 {bson.M{"_": bson.MaxKey},
169 "\x7F_\x00"},
170 {bson.M{"_": bson.MinKey},
171 "\xFF_\x00"},
172}
173
174func (s *S) TestMarshalAllItems(c *C) {
175 for i, item := range allItems {
176 data, err := bson.Marshal(item.obj)
177 c.Assert(err, IsNil)
178 c.Assert(string(data), Equals, wrapInDoc(item.data), Bug("Failed on item %d: %#v", i, item))
179 }
180}
181
182func (s *S) TestUnmarshalAllItems(c *C) {
183 for i, item := range allItems {
184 value := bson.M{}
185 err := bson.Unmarshal([]byte(wrapInDoc(item.data)), value)
186 c.Assert(err, IsNil)
187 c.Assert(value, Equals, item.obj, Bug("Failed on item %d: %#v", i, item))
188 }
189}
190
191func (s *S) TestUnmarshalRawAllItems(c *C) {
192 for i, item := range allItems {
193 if len(item.data) == 0 {
194 continue
195 }
196 value := item.obj.(bson.M)["_"]
197 if value == nil {
198 continue
199 }
200 pv := reflect.New(reflect.ValueOf(value).Type())
201 raw := bson.Raw{item.data[0], []byte(item.data[3:])}
202 c.Logf("Unmarshal raw: %#v, %#v", raw, pv.Interface())
203 err := raw.Unmarshal(pv.Interface())
204 c.Assert(err, IsNil)
205 c.Assert(pv.Elem().Interface(), Equals, value, Bug("Failed on item %d: %#v", i, item))
206 }
207}
208
209func (s *S) TestUnmarshalRawIncompatible(c *C) {
210 raw := bson.Raw{0x08, []byte{0x01}} // true
211 err := raw.Unmarshal(&struct{}{})
212 c.Assert(err, Matches, `BSON kind 0x08 isn't compatible with type \*struct { }`)
213}
214
215// --------------------------------------------------------------------------
216// Some one way marshaling operations which would unmarshal differently.
217
218var oneWayMarshalItems = []testItemType{
219 // These are being passed as pointers, and will unmarshal as values.
220 {bson.M{"": &bson.Binary{0x02, []byte("old")}},
221 "\x05\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"},
222 {bson.M{"": &bson.Binary{0x80, []byte("udef")}},
223 "\x05\x00\x04\x00\x00\x00\x80udef"},
224 {bson.M{"": &bson.RegEx{"ab", "cd"}},
225 "\x0B\x00ab\x00cd\x00"},
226 {bson.M{"": &bson.JS{"code", nil}},
227 "\x0D\x00\x05\x00\x00\x00code\x00"},
228 {bson.M{"": &bson.JS{"code", bson.M{"": nil}}},
229 "\x0F\x00\x14\x00\x00\x00\x05\x00\x00\x00code\x00" +
230 "\x07\x00\x00\x00\x0A\x00\x00"},
231
232 // There's no float32 type in BSON. Will encode as a float64.
233 {bson.M{"": float32(5.05)},
234 "\x01\x00\x00\x00\x00@33\x14@"},
235
236 // The array will be unmarshaled as a slice instead.
237 {bson.M{"": [2]bool{true, false}},
238 "\x04\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"},
239
240 // The typed slice will be unmarshaled as []interface{}.
241 {bson.M{"": []bool{true, false}},
242 "\x04\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"},
243
244 // Will unmarshal as a []byte.
245 {bson.M{"": bson.Binary{0x00, []byte("yo")}},
246 "\x05\x00\x02\x00\x00\x00\x00yo"},
247
248 // No way to preserve the type information here. We might encode as a zero
249 // value, but this would mean that pointer values in structs wouldn't be
250 // able to correctly distinguish between unset and set to the zero value.
251 {bson.M{"": (*byte)(nil)},
252 "\x0A\x00"},
253
254 // No int types smaller than int32 in BSON. Could encode this as a char,
255 // but it would still be ambiguous, take more, and be awkward in Go when
256 // loaded without typing information.
257 {bson.M{"": byte(8)},
258 "\x10\x00\x08\x00\x00\x00"},
259
260 // There are no unsigned types in BSON. Will unmarshal as int32 or int64.
261 {bson.M{"": uint32(258)},
262 "\x10\x00\x02\x01\x00\x00"},
263 {bson.M{"": uint64(258)},
264 "\x12\x00\x02\x01\x00\x00\x00\x00\x00\x00"},
265 {bson.M{"": uint64(258 << 32)},
266 "\x12\x00\x00\x00\x00\x00\x02\x01\x00\x00"},
267
268 // This will unmarshal as int.
269 {bson.M{"": int32(258)},
270 "\x10\x00\x02\x01\x00\x00"},
271
272 // That's a special case. The unsigned value is too large for an int32,
273 // so an int64 is used instead.
274 {bson.M{"": uint32(1<<32 - 1)},
275 "\x12\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"},
276 {bson.M{"": uint(1<<32 - 1)},
277 "\x12\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"},
278}
279
280func (s *S) TestOneWayMarshalItems(c *C) {
281 for i, item := range oneWayMarshalItems {
282 data, err := bson.Marshal(item.obj)
283 c.Assert(err, IsNil)
284 c.Assert(string(data), Equals, wrapInDoc(item.data),
285 Bug("Failed on item %d", i))
286 }
287}
288
289
290// --------------------------------------------------------------------------
291// Two-way tests for user-defined structures using the samples
292// from bsonspec.org.
293
294type specSample1 struct {
295 Hello string
296}
297
298type specSample2 struct {
299 BSON []interface{} "BSON"
300}
301
302var structSampleItems = []testItemType{
303 {&specSample1{"world"},
304 "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00"},
305 {&specSample2{[]interface{}{"awesome", float64(5.05), 1986}},
306 "1\x00\x00\x00\x04BSON\x00&\x00\x00\x00\x020\x00\x08\x00\x00\x00" +
307 "awesome\x00\x011\x00333333\x14@\x102\x00\xc2\x07\x00\x00\x00\x00"},
308}
309
310
311func (s *S) TestMarshalStructSampleItems(c *C) {
312 for i, item := range structSampleItems {
313 data, err := bson.Marshal(item.obj)
314 c.Assert(err, IsNil)
315 c.Assert(string(data), Equals, item.data,
316 Bug("Failed on item %d", i))
317 }
318}
319
320func (s *S) TestUnmarshalStructSampleItems(c *C) {
321 for _, item := range structSampleItems {
322 testUnmarshal(c, item.data, item.obj)
323 }
324}
325
326
327// --------------------------------------------------------------------------
328// Generic two-way struct marshaling tests.
329
330var bytevar = byte(8)
331var byteptr = &bytevar
332
333var structItems = []testItemType{
334 {&struct{ Ptr *byte }{nil},
335 "\x0Aptr\x00"},
336 {&struct{ Ptr *byte }{&bytevar},
337 "\x10ptr\x00\x08\x00\x00\x00"},
338 {&struct{ Ptr **byte }{&byteptr},
339 "\x10ptr\x00\x08\x00\x00\x00"},
340 {&struct{ Byte byte }{8},
341 "\x10byte\x00\x08\x00\x00\x00"},
342 {&struct{ Byte byte }{0},
343 "\x10byte\x00\x00\x00\x00\x00"},
344 {&struct {
345 V byte "Tag"
346 }{8},
347 "\x10Tag\x00\x08\x00\x00\x00"},
348 {&struct {
349 V *struct {
350 Byte byte
351 }
352 }{&struct{ Byte byte }{8}},
353 "\x03v\x00" + "\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00"},
354 {&struct{ priv byte }{}, ""},
355
356 // The order of the dumped fields should be the same in the struct.
357 {&struct{ A, C, B, D, F, E *byte }{},
358 "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x0Ae\x00"},
359
360 {&struct{ V bson.Raw }{bson.Raw{0x03, []byte("\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00")}},
361 "\x03v\x00" + "\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00"},
362 {&struct{ V bson.Raw }{bson.Raw{0x10, []byte("\x00\x00\x00\x00")}},
363 "\x10v\x00" + "\x00\x00\x00\x00"},
364
365 // Byte arrays.
366 {&struct{ V [2]byte }{[2]byte{'y', 'o'}},
367 "\x05v\x00\x02\x00\x00\x00\x00yo"},
368}
369
370
371func (s *S) TestMarshalStructItems(c *C) {
372 for i, item := range structItems {
373 data, err := bson.Marshal(item.obj)
374 c.Assert(err, IsNil)
375 c.Assert(string(data), Equals, wrapInDoc(item.data),
376 Bug("Failed on item %d", i))
377 }
378}
379
380func (s *S) TestUnmarshalStructItems(c *C) {
381 for _, item := range structItems {
382 testUnmarshal(c, wrapInDoc(item.data), item.obj)
383 }
384}
385
386
387// --------------------------------------------------------------------------
388// One-way marshaling tests.
389
390type dOnIface struct {
391 D interface{}
392}
393
394var marshalItems = []testItemType{
395 // Ordered document dump. Will unmarshal as a dictionary by default.
396 {bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", nil}, {"f", nil}, {"e", true}},
397 "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x08e\x00\x01"},
398 {&dOnIface{bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", true}}},
399 "\x03d\x00" + wrapInDoc("\x0Aa\x00\x0Ac\x00\x0Ab\x00\x08d\x00\x01")},
400
401 // Marshalling a Raw document does nothing.
402 {bson.Raw{0x03, []byte(wrapInDoc("anything"))},
403 "anything"},
404 {bson.Raw{Data: []byte(wrapInDoc("anything"))},
405 "anything"},
406}
407
408func (s *S) TestMarshalOneWayItems(c *C) {
409 for _, item := range marshalItems {
410 data, err := bson.Marshal(item.obj)
411 c.Assert(err, IsNil)
412 c.Assert(string(data), Equals, wrapInDoc(item.data))
413 }
414}
415
416// --------------------------------------------------------------------------
417// One-way unmarshaling tests.
418
419var unmarshalItems = []testItemType{
420 // Field is private. Should not attempt to unmarshal it.
421 {&struct{ priv byte }{},
422 "\x10priv\x00\x08\x00\x00\x00"},
423
424 // Wrong casing. Field names are lowercased.
425 {&struct{ Byte byte }{},
426 "\x10Byte\x00\x08\x00\x00\x00"},
427
428 // Ignore non-existing field.
429 {&struct{ Byte byte }{9},
430 "\x10boot\x00\x08\x00\x00\x00" + "\x10byte\x00\x09\x00\x00\x00"},
431
432 // Ignore unsuitable types silently.
433 {map[string]string{"str": "s"},
434 "\x02str\x00\x02\x00\x00\x00s\x00" + "\x10int\x00\x01\x00\x00\x00"},
435 {map[string][]int{"array": []int{5, 9}},
436 "\x04array\x00" +
437 wrapInDoc("\x100\x00\x05\x00\x00\x00"+
438 "\x021\x00\x02\x00\x00\x00s\x00"+
439 "\x102\x00\x09\x00\x00\x00")},
440
441 // Wrong type. Shouldn't init pointer.
442 {&struct{ Str *byte }{},
443 "\x02str\x00\x02\x00\x00\x00s\x00"},
444 {&struct{ Str *struct{ Str string } }{},
445 "\x02str\x00\x02\x00\x00\x00s\x00"},
446
447 // Ordered document.
448 {&struct{ bson.D }{bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", true}}},
449 "\x03d\x00" + wrapInDoc("\x0Aa\x00\x0Ac\x00\x0Ab\x00\x08d\x00\x01")},
450
451 // Raw document.
452 {&bson.Raw{0x03, []byte(wrapInDoc("\x10byte\x00\x08\x00\x00\x00"))},
453 "\x10byte\x00\x08\x00\x00\x00"},
454}
455
456
457func (s *S) TestUnmarshalOneWayItems(c *C) {
458 for _, item := range unmarshalItems {
459 testUnmarshal(c, wrapInDoc(item.data), item.obj)
460 }
461}
462
463func (s *S) TestUnmarshalNilInStruct(c *C) {
464 // Nil is the default value, so we need to ensure it's indeed being set.
465 b := byte(1)
466 v := &struct{ Ptr *byte }{&b}
467 err := bson.Unmarshal([]byte(wrapInDoc("\x0Aptr\x00")), v)
468 c.Assert(err, IsNil)
469 c.Assert(v, Equals, &struct{ Ptr *byte }{nil})
470}
471
472// --------------------------------------------------------------------------
473// Marshalling error cases.
474
475type structWithDupKeys struct {
476 Name byte
477 Other byte "name" // Tag should precede.
478}
479
480var marshalErrorItems = []testItemType{
481 {bson.M{"": uint64(1 << 63)},
482 "BSON has no uint64 type, and value is too large to fit correctly in an int64"},
483 {bson.M{"": bson.ObjectId("tooshort")},
484 "ObjectIDs must be exactly 12 bytes long \\(got 8\\)"},
485 {int64(123),
486 "Can't marshal int64 as a BSON document"},
487 {bson.M{"": 1i},
488 "Can't marshal complex128 in a BSON document"},
489 {&structWithDupKeys{},
490 "Duplicated key 'name' in struct bson_test.structWithDupKeys"},
491 {bson.Raw{0x0A, []byte{}},
492 "Attempted to unmarshal Raw kind 10 as a document"},
493}
494
495func (s *S) TestMarshalErrorItems(c *C) {
496 for _, item := range marshalErrorItems {
497 data, err := bson.Marshal(item.obj)
498 c.Assert(err, Matches, item.data)
499 c.Assert(data, IsNil)
500 }
501}
502
503// --------------------------------------------------------------------------
504// Unmarshalling error cases.
505
506type unmarshalErrorType struct {
507 obj interface{}
508 data string
509 error string
510}
511
512var unmarshalErrorItems = []unmarshalErrorType{
513 // Tag name conflicts with existing parameter.
514 {&structWithDupKeys{},
515 "\x10name\x00\x08\x00\x00\x00",
516 "Duplicated key 'name' in struct bson_test.structWithDupKeys"},
517
518 // Non-string map key.
519 {map[int]interface{}{},
520 "\x10name\x00\x08\x00\x00\x00",
521 "BSON map must have string keys. Got: map\\[int\\] interface \\{ \\}"},
522
523 {nil,
524 "\xEEname\x00",
525 "Unknown element kind \\(0xEE\\)"},
526
527 {struct{ Name bool }{},
528 "\x10name\x00\x08\x00\x00\x00",
529 "Unmarshal can't deal with struct values. Use a pointer."},
530
531 {123,
532 "\x10name\x00\x08\x00\x00\x00",
533 "Unmarshal needs a map or a pointer to a struct."},
534}
535
536
537func (s *S) TestUnmarshalErrorItems(c *C) {
538 for _, item := range unmarshalErrorItems {
539 data := []byte(wrapInDoc(item.data))
540 var value interface{}
541 switch reflect.ValueOf(item.obj).Kind() {
542 case reflect.Map, reflect.Ptr:
543 value = makeZeroDoc(item.obj)
544 case reflect.Invalid:
545 value = bson.M{}
546 default:
547 value = item.obj
548 }
549 err := bson.Unmarshal(data, value)
550 c.Assert(err, Matches, item.error)
551 }
552}
553
554
555type unmarshalRawErrorType struct {
556 obj interface{}
557 raw bson.Raw
558 error string
559}
560
561var unmarshalRawErrorItems = []unmarshalRawErrorType{
562 // Tag name conflicts with existing parameter.
563 {&structWithDupKeys{},
564 bson.Raw{0x03, []byte("\x10byte\x00\x08\x00\x00\x00")},
565 "Duplicated key 'name' in struct bson_test.structWithDupKeys"},
566
567 {&struct{}{},
568 bson.Raw{0xEE, []byte{}},
569 "Unknown element kind \\(0xEE\\)"},
570
571 {struct{ Name bool }{},
572 bson.Raw{0x10, []byte("\x08\x00\x00\x00")},
573 "Raw Unmarshal needs a map or a valid pointer."},
574
575 {123,
576 bson.Raw{0x10, []byte("\x08\x00\x00\x00")},
577 "Raw Unmarshal needs a map or a valid pointer."},
578}
579
580func (s *S) TestUnmarshalRawErrorItems(c *C) {
581 for i, item := range unmarshalRawErrorItems {
582 err := item.raw.Unmarshal(item.obj)
583 c.Assert(err, Matches, item.error, Bug("Failed on item %d: %#v\n", i, item))
584 }
585}
586
587var corruptedData = []string{
588 "\x04\x00\x00\x00\x00", // Shorter than minimum
589 "\x06\x00\x00\x00\x00", // Not enough data
590 "\x05\x00\x00", // Broken length
591 "\x05\x00\x00\x00\xff", // Corrupted termination
592 "\x0A\x00\x00\x00\x0Aooop\x00", // Unfinished C string
593
594 // Array end past end of string (s[2]=0x07 is correct)
595 wrapInDoc("\x04\x00\x09\x00\x00\x00\x0A\x00\x00"),
596
597 // Array end within string, but past acceptable.
598 wrapInDoc("\x04\x00\x08\x00\x00\x00\x0A\x00\x00"),
599
600 // Document end within string, but past acceptable.
601 wrapInDoc("\x03\x00\x08\x00\x00\x00\x0A\x00\x00"),
602
603 // String with corrupted end.
604 wrapInDoc("\x02\x00\x03\x00\x00\x00yo\xFF"),
605}
606
607
608func (s *S) TestUnmarshalMapDocumentTooShort(c *C) {
609 for _, data := range corruptedData {
610 err := bson.Unmarshal([]byte(data), bson.M{})
611 c.Assert(err, Matches, "Document is corrupted")
612
613 err = bson.Unmarshal([]byte(data), &struct{}{})
614 c.Assert(err, Matches, "Document is corrupted")
615 }
616}
617
618
619// --------------------------------------------------------------------------
620// Setter test cases.
621
622var setterResult = map[string]bool{}
623
624type typeWithSetter struct {
625 received interface{}
626}
627
628func (o *typeWithSetter) SetBSON(value interface{}) (ok bool) {
629 o.received = value
630 if s, ok := value.(string); ok {
631 if result, ok := setterResult[s]; ok {
632 return result
633 }
634 }
635 return true
636}
637
638type docWithSetterField struct {
639 Field *typeWithSetter "_"
640}
641
642func (s *S) TestUnmarshalAllItemsWithSetter(c *C) {
643 for _, item := range allItems {
644 obj := &docWithSetterField{}
645 err := bson.Unmarshal([]byte(wrapInDoc(item.data)), obj)
646 c.Assert(err, IsNil)
647
648 if item.data == "" {
649 // Nothing to unmarshal. Should be untouched.
650 c.Assert(obj.Field, IsNil)
651 } else {
652 expected := item.obj.(bson.M)["_"]
653
654 if m, ok := expected.(bson.M); ok {
655 // Setter works with a bson.D slice rather than bson.M.
656 slice := make(bson.D, 0, len(m))
657 for key, value := range m {
658 slice = append(slice, bson.DocElem{key, value})
659 }
660 expected = slice
661 }
662
663 c.Assert(obj.Field, NotNil,
664 Bug("Pointer not initialized (%#v)", expected))
665 c.Assert(obj.Field.received, Equals, expected)
666 }
667 }
668}
669
670func (s *S) TestUnmarshalWholeDocumentWithSetter(c *C) {
671 obj := &typeWithSetter{}
672 err := bson.Unmarshal([]byte(sampleItems[0].data), obj)
673 c.Assert(err, IsNil)
674 c.Assert(obj.received, Equals, bson.D{{"hello", "world"}})
675}
676
677func (s *S) TestUnmarshalWithFalseSetterIgnoresValue(c *C) {
678 setterResult["2"] = false
679 setterResult["4"] = false
680 defer func() {
681 setterResult["2"] = false, false
682 setterResult["4"] = false, false
683 }()
684
685 m := map[string]*typeWithSetter{}
686 data := wrapInDoc("\x02abc\x00\x02\x00\x00\x001\x00" +
687 "\x02def\x00\x02\x00\x00\x002\x00" +
688 "\x02ghi\x00\x02\x00\x00\x003\x00" +
689 "\x02jkl\x00\x02\x00\x00\x004\x00")
690 err := bson.Unmarshal([]byte(data), m)
691 c.Assert(err, IsNil)
692 c.Assert(m["abc"], NotNil)
693 c.Assert(m["def"], IsNil)
694 c.Assert(m["ghi"], NotNil)
695 c.Assert(m["jkl"], IsNil)
696
697 c.Assert(m["abc"].received, Equals, "1")
698 c.Assert(m["ghi"].received, Equals, "3")
699}
700
701func (s *S) TestDMap(c *C) {
702 d := bson.D{{"a", 1}, {"b", 2}}
703 c.Assert(d.Map(), Equals, bson.M{"a": 1, "b": 2})
704}
705
706
707// --------------------------------------------------------------------------
708// Getter test cases.
709
710type typeWithGetter struct {
711 result interface{}
712}
713
714func (t *typeWithGetter) GetBSON() interface{} {
715 return t.result
716}
717
718type docWithGetterField struct {
719 Field *typeWithGetter "_"
720}
721
722func (s *S) TestMarshalAllItemsWithGetter(c *C) {
723 for i, item := range allItems {
724 if item.data == "" {
725 continue
726 }
727 obj := &docWithGetterField{}
728 obj.Field = &typeWithGetter{item.obj.(bson.M)["_"]}
729 data, err := bson.Marshal(obj)
730 c.Assert(err, IsNil)
731 c.Assert(string(data), Equals, wrapInDoc(item.data),
732 Bug("Failed on item #%d", i))
733 }
734}
735
736func (s *S) TestMarshalWholeDocumentWithGetter(c *C) {
737 obj := &typeWithGetter{sampleItems[0].obj}
738 data, err := bson.Marshal(obj)
739 c.Assert(err, IsNil)
740 c.Assert(string(data), Equals, sampleItems[0].data)
741}
742
743type intGetter int64
744
745func (t intGetter) GetBSON() interface{} {
746 return int64(t)
747}
748
749type typeWithIntGetter struct {
750 V intGetter "/s"
751}
752
753func (s *S) TestMarshalShortWithGetter(c *C) {
754 obj := typeWithIntGetter{42}
755 data, err := bson.Marshal(obj)
756 c.Assert(err, IsNil)
757 m := bson.M{}
758 err = bson.Unmarshal(data, m)
759 c.Assert(m["v"], Equals, 42)
760}
761
762// --------------------------------------------------------------------------
763// Cross-type conversion tests.
764
765type crossTypeItem struct {
766 obj1 interface{}
767 obj2 interface{}
768}
769
770type condStr struct {
771 V string "/c"
772}
773type condBool struct {
774 V bool "/c"
775}
776type condInt struct {
777 V int "/c"
778}
779type condUInt struct {
780 V uint "/c"
781}
782type condIface struct {
783 V interface{} "/c"
784}
785type condPtr struct {
786 V *bool "/c"
787}
788type condSlice struct {
789 V []string "/c"
790}
791type condMap struct {
792 V map[string]int "/c"
793}
794type namedCondStr struct {
795 V string "myv/c"
796}
797
798type shortInt struct {
799 V int64 "/s"
800}
801type shortUint struct {
802 V uint64 "/s"
803}
804type shortIface struct {
805 V interface{} "/s"
806}
807type shortPtr struct {
808 V *int64 "/s"
809}
810
811type slashedName struct {
812 V string "a/b/"
813}
814
815var truevar = true
816var falsevar = false
817
818var int64var = int64(42)
819var int64ptr = &int64var
820var intvar = int(42)
821var intptr = &intvar
822
823// That's a pretty fun test. It will dump the first item, generate a zero
824// value equivalent to the second one, load the dumped data onto it, and then
825// verify that the resulting value is deep-equal to the untouched second value.
826// Then, it will do the same in the *opposite* direction!
827var twoWayCrossItems = []crossTypeItem{
828 // int<=>int
829 {&struct{ I int }{42}, &struct{ I int8 }{42}},
830 {&struct{ I int }{42}, &struct{ I int32 }{42}},
831 {&struct{ I int }{42}, &struct{ I int64 }{42}},
832 {&struct{ I int8 }{42}, &struct{ I int32 }{42}},
833 {&struct{ I int8 }{42}, &struct{ I int64 }{42}},
834 {&struct{ I int32 }{42}, &struct{ I int64 }{42}},
835
836 // uint<=>uint
837 {&struct{ I uint }{42}, &struct{ I uint8 }{42}},
838 {&struct{ I uint }{42}, &struct{ I uint32 }{42}},
839 {&struct{ I uint }{42}, &struct{ I uint64 }{42}},
840 {&struct{ I uint8 }{42}, &struct{ I uint32 }{42}},
841 {&struct{ I uint8 }{42}, &struct{ I uint64 }{42}},
842 {&struct{ I uint32 }{42}, &struct{ I uint64 }{42}},
843
844 // float32<=>float64
845 {&struct{ I float32 }{42}, &struct{ I float64 }{42}},
846
847 // int<=>uint
848 {&struct{ I uint }{42}, &struct{ I int }{42}},
849 {&struct{ I uint }{42}, &struct{ I int8 }{42}},
850 {&struct{ I uint }{42}, &struct{ I int32 }{42}},
851 {&struct{ I uint }{42}, &struct{ I int64 }{42}},
852 {&struct{ I uint8 }{42}, &struct{ I int }{42}},
853 {&struct{ I uint8 }{42}, &struct{ I int8 }{42}},
854 {&struct{ I uint8 }{42}, &struct{ I int32 }{42}},
855 {&struct{ I uint8 }{42}, &struct{ I int64 }{42}},
856 {&struct{ I uint32 }{42}, &struct{ I int }{42}},
857 {&struct{ I uint32 }{42}, &struct{ I int8 }{42}},
858 {&struct{ I uint32 }{42}, &struct{ I int32 }{42}},
859 {&struct{ I uint32 }{42}, &struct{ I int64 }{42}},
860 {&struct{ I uint64 }{42}, &struct{ I int }{42}},
861 {&struct{ I uint64 }{42}, &struct{ I int8 }{42}},
862 {&struct{ I uint64 }{42}, &struct{ I int32 }{42}},
863 {&struct{ I uint64 }{42}, &struct{ I int64 }{42}},
864
865 // int <=> timestamp. Note the NS <=> MS conversion.
866 {&struct{ I bson.Timestamp }{42e6}, &struct{ I int64 }{42}},
867 {&struct{ I bson.Timestamp }{42e6}, &struct{ I int32 }{42}},
868 {&struct{ I bson.Timestamp }{42e6}, &struct{ I int }{42}},
869
870 // int <=> float
871 {&struct{ I int }{42}, &struct{ I float64 }{42}},
872
873 // int <=> bool
874 {&struct{ I int }{1}, &struct{ I bool }{true}},
875 {&struct{ I int }{0}, &struct{ I bool }{false}},
876
877 // uint <=> float64
878 {&struct{ I uint }{42}, &struct{ I float64 }{42}},
879
880 // uint <=> bool
881 {&struct{ I uint }{1}, &struct{ I bool }{true}},
882 {&struct{ I uint }{0}, &struct{ I bool }{false}},
883
884 // float64 <=> bool
885 {&struct{ I float64 }{1}, &struct{ I bool }{true}},
886 {&struct{ I float64 }{0}, &struct{ I bool }{false}},
887
888 // string <=> string and string <=> []byte
889 {&struct{ S []byte }{[]byte("abc")}, &struct{ S string }{"abc"}},
890 {&struct{ S []byte }{[]byte("def")}, &struct{ S bson.Symbol }{"def"}},
891 {&struct{ S string }{"ghi"}, &struct{ S bson.Symbol }{"ghi"}},
892
893 // map <=> struct
894 {&struct {
895 A struct {
896 B, C int
897 }
898 }{struct{ B, C int }{1, 2}},
899 map[string]map[string]int{"a": map[string]int{"b": 1, "c": 2}}},
900
901 {&struct{ A bson.Symbol }{"abc"}, map[string]string{"a": "abc"}},
902 {&struct{ A bson.Symbol }{"abc"}, map[string][]byte{"a": []byte("abc")}},
903 {&struct{ A []byte }{[]byte("abc")}, map[string]string{"a": "abc"}},
904 {&struct{ A uint }{42}, map[string]int{"a": 42}},
905 {&struct{ A uint }{42}, map[string]float64{"a": 42}},
906 {&struct{ A uint }{1}, map[string]bool{"a": true}},
907 {&struct{ A int }{42}, map[string]uint{"a": 42}},
908 {&struct{ A int }{42}, map[string]float64{"a": 42}},
909 {&struct{ A int }{1}, map[string]bool{"a": true}},
910 {&struct{ A float64 }{42}, map[string]float32{"a": 42}},
911 {&struct{ A float64 }{42}, map[string]int{"a": 42}},
912 {&struct{ A float64 }{42}, map[string]uint{"a": 42}},
913 {&struct{ A float64 }{1}, map[string]bool{"a": true}},
914 {&struct{ A bool }{true}, map[string]int{"a": 1}},
915 {&struct{ A bool }{true}, map[string]uint{"a": 1}},
916 {&struct{ A bool }{true}, map[string]float64{"a": 1}},
917 {&struct{ A **byte }{&byteptr}, map[string]byte{"a": 8}},
918
919 // Slices
920 {&struct{ S []int }{[]int{1, 2, 3}}, map[string][]int{"s": []int{1, 2, 3}}},
921 {&struct{ S *[]int }{&[]int{1, 2, 3}}, map[string][]int{"s": []int{1, 2, 3}}},
922
923 // Conditionals
924 {&condBool{true}, map[string]bool{"v": true}},
925 {&condBool{}, map[string]bool{}},
926 {&condInt{1}, map[string]int{"v": 1}},
927 {&condInt{}, map[string]int{}},
928 {&condUInt{1}, map[string]uint{"v": 1}},
929 {&condUInt{}, map[string]uint{}},
930 {&condStr{"yo"}, map[string]string{"v": "yo"}},
931 {&condStr{}, map[string]string{}},
932 {&condSlice{[]string{"yo"}}, map[string][]string{"v": []string{"yo"}}},
933 {&condSlice{}, map[string][]string{}},
934 {&condMap{map[string]int{"k": 1}}, bson.M{"v": bson.M{"k": 1}}},
935 {&condMap{map[string]int{}}, map[string][]string{}},
936 {&condMap{}, map[string][]string{}},
937 {&condIface{"yo"}, map[string]string{"v": "yo"}},
938 {&condIface{""}, map[string]string{"v": ""}},
939 {&condIface{}, map[string]string{}},
940 {&condPtr{&truevar}, map[string]bool{"v": true}},
941 {&condPtr{&falsevar}, map[string]bool{"v": false}},
942 {&condPtr{}, map[string]string{}},
943
944 {&namedCondStr{"yo"}, map[string]string{"myv": "yo"}},
945 {&namedCondStr{}, map[string]string{}},
946
947 {&shortInt{1}, map[string]interface{}{"v": 1}},
948 {&shortInt{1 << 30}, map[string]interface{}{"v": 1 << 30}},
949 {&shortInt{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}},
950 {&shortUint{1 << 30}, map[string]interface{}{"v": 1 << 30}},
951 {&shortUint{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}},
952 {&shortIface{int64(1) << 31}, map[string]interface{}{"v": int64(1 << 31)}},
953 {&shortPtr{int64ptr}, map[string]interface{}{"v": intvar}},
954
955 {&slashedName{"yo"}, map[string]string{"a/b": "yo"}},
956}
957
958// Same thing, but only one way (obj1 => obj2).
959var oneWayCrossItems = []crossTypeItem{
960 // map <=> struct
961 {map[string]interface{}{"a": 1, "b": "2", "c": 3},
962 map[string]int{"a": 1, "c": 3}},
963
964 // Can't decode int into struct.
965 {bson.M{"a": bson.M{"b": 2}}, &struct{ A bool }{}},
966
967 // Would get decoded into a int32 too in the opposite direction.
968 {&shortIface{int64(1) << 30}, map[string]interface{}{"v": 1 << 30}},
969}
970
971func testCrossPair(c *C, dump interface{}, load interface{}, bug interface{}) {
972 //c.Logf("")
973 zero := makeZeroDoc(load)
974 data, err := bson.Marshal(dump)
975 c.Assert(err, IsNil, bug)
976 c.Logf("Data: %#v", string(data))
977 err = bson.Unmarshal(data, zero)
978 c.Assert(err, IsNil, bug)
979 c.Assert(zero, Equals, load, bug)
980}
981
982func (s *S) TestTwoWayCrossPairs(c *C) {
983 for i, item := range twoWayCrossItems {
984 testCrossPair(c, item.obj1, item.obj2, Bug("#%d obj1 => obj2", i))
985 testCrossPair(c, item.obj2, item.obj1, Bug("#%d obj1 <= obj2", i))
986 }
987}
988
989func (s *S) TestOneWayCrossPairs(c *C) {
990 for i, item := range oneWayCrossItems {
991 testCrossPair(c, item.obj1, item.obj2, Bug("#%d obj1 => obj2", i))
992 }
993}
994
995// --------------------------------------------------------------------------
996// ObjectId hex representation test.
997
998func (s *S) TestObjectIdHex(c *C) {
999 id := bson.ObjectIdHex("4d88e15b60f486e428412dc9")
1000 str := "ObjectIdHex(\"4d88e15b60f486e428412dc9\")"
1001 c.Assert(str, Equals, id.String())
1002}
1003
1004// --------------------------------------------------------------------------
1005// ObjectId parts extraction tests.
1006
1007type objectIdParts struct {
1008 id bson.ObjectId
1009 timestamp int32
1010 machine []byte
1011 pid uint16
1012 counter int32
1013}
1014
1015var objectIds = []objectIdParts{
1016 objectIdParts{
1017 bson.ObjectIdHex("4d88e15b60f486e428412dc9"),
1018 1300816219,
1019 []byte{0x60, 0xf4, 0x86},
1020 0xe428,
1021 4271561,
1022 },
1023 objectIdParts{
1024 bson.ObjectIdHex("000000000000000000000000"),
1025 0,
1026 []byte{0x00, 0x00, 0x00},
1027 0x0000,
1028 0,
1029 },
1030 objectIdParts{
1031 bson.ObjectIdHex("00000000aabbccddee000001"),
1032 0,
1033 []byte{0xaa, 0xbb, 0xcc},
1034 0xddee,
1035 1,
1036 },
1037}
1038
1039func (s *S) TestObjectIdPartsExtraction(c *C) {
1040 for i, v := range objectIds {
1041 c.Assert(v.id.Timestamp(), Equals, v.timestamp, Bug("#%d Wrong timestamp value", i))
1042 c.Assert(v.id.Machine(), Equals, v.machine, Bug("#%d Wrong machine id value", i))
1043 c.Assert(v.id.Pid(), Equals, v.pid, Bug("#%d Wrong pid value", i))
1044 c.Assert(v.id.Counter(), Equals, v.counter, Bug("#%d Wrong counter value", i))
1045 }
1046}
1047
1048func (s *S) TestNow(c *C) {
1049 before := time.Nanoseconds()
1050 time.Sleep(1e6)
1051 now := bson.Now()
1052 time.Sleep(1e6)
1053 after := time.Nanoseconds()
1054 c.Assert(reflect.TypeOf(now), Equals, reflect.TypeOf(bson.Timestamp(00)))
1055 c.Assert(int64(now) > before && int64(now) < after, Equals, true, Bug("now=%d, before=%d, after=%d", now, before, after))
1056}
1057
1058// --------------------------------------------------------------------------
1059// ObjectId generation tests.
1060
1061func (s *S) TestNewObjectId(c *C) {
1062 // Generate 10 ids
1063 ids := make([]bson.ObjectId, 10)
1064 for i := 0; i < 10; i++ {
1065 ids[i] = bson.NewObjectId()
1066 }
1067 for i := 1; i < 10; i++ {
1068 prevId := ids[i-1]
1069 id := ids[i]
1070 // Test for uniqueness among all other 9 generated ids
1071 for j, tid := range ids {
1072 if j != i {
1073 c.Assert(id, Not(Equals), tid, Bug("Generated ObjectId is not unique"))
1074 }
1075 }
1076 // Check that timestamp was incremented and is within 30 seconds of the previous one
1077 td := id.Timestamp() - prevId.Timestamp()
1078 c.Assert((td >= 0 && td <= 30), Equals, true, Bug("Wrong timestamp in generated ObjectId"))
1079 // Check that machine ids are the same
1080 c.Assert(id.Machine(), Equals, prevId.Machine())
1081 // Check that pids are the same
1082 c.Assert(id.Pid(), Equals, prevId.Pid())
1083 // Test for proper increment
1084 delta := int(id.Counter() - prevId.Counter())
1085 c.Assert(delta, Equals, 1, Bug("Wrong increment in generated ObjectId"))
1086 }
1087}
1088
1089func (s *S) TestNewObjectIdSeconds(c *C) {
1090 sec := int32(time.Seconds())
1091 id := bson.NewObjectIdSeconds(sec)
1092 c.Assert(id.Timestamp(), Equals, sec)
1093 c.Assert(id.Machine(), Equals, []byte{0x00, 0x00, 0x00})
1094 c.Assert(int(id.Pid()), Equals, 0)
1095 c.Assert(int(id.Counter()), Equals, 0)
1096}

Subscribers

People subscribed via source and target branches

to all changes: