Merge lp:~miki-tebeka/mgo/string-compile into lp:~niemeyer/mgo/trunk
- string-compile
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gustavo Niemeyer | Pending | ||
Review via email: mp+69689@code.launchpad.net |
Commit message
Description of the change
Fix compilation error with current go head (e3cd47e9748d)
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
1 | ------------------------------------------------------------ |
2 | revno: 29 |
3 | committer: Miki Tebeka <miki.tebeka@gmail.com> |
4 | branch nick: gobson |
5 | timestamp: Thu 2011-07-28 10:06:14 -0700 |
6 | message: |
7 | Compile with go head |
8 | diff: |
9 | === modified file 'gobson.go' |
10 | --- gobson.go 2011-06-24 23:48:23 +0000 |
11 | +++ gobson.go 2011-07-28 17:06:14 +0000 |
12 | @@ -442,7 +442,7 @@ |
13 | |
14 | info := fieldInfo{Num: i} |
15 | |
16 | - if s := strings.LastIndex(field.Tag, "/"); s != -1 { |
17 | + if s := strings.LastIndex(string(field.Tag), "/"); s != -1 { |
18 | for _, c := range field.Tag[s+1:] { |
19 | switch c { |
20 | case int('c'): |
21 | @@ -457,7 +457,7 @@ |
22 | } |
23 | |
24 | if field.Tag != "" { |
25 | - info.Key = field.Tag |
26 | + info.Key = string(field.Tag) |
27 | } else { |
28 | info.Key = strings.ToLower(field.Name) |
29 | } |
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
1 | === added file 'LICENSE' |
2 | --- LICENSE 1970-01-01 00:00:00 +0000 |
3 | +++ LICENSE 2011-07-28 17:08:30 +0000 |
4 | @@ -0,0 +1,29 @@ |
5 | +gobson - BSON library for Go. |
6 | + |
7 | +Copyright (c) 2010-2011 - Gustavo Niemeyer <gustavo@niemeyer.net> |
8 | + |
9 | +All rights reserved. |
10 | + |
11 | +Redistribution and use in source and binary forms, with or without |
12 | +modification, are permitted provided that the following conditions are met: |
13 | + |
14 | + * Redistributions of source code must retain the above copyright notice, |
15 | + this list of conditions and the following disclaimer. |
16 | + * Redistributions in binary form must reproduce the above copyright notice, |
17 | + this list of conditions and the following disclaimer in the documentation |
18 | + and/or other materials provided with the distribution. |
19 | + * Neither the name of the copyright holder nor the names of its |
20 | + contributors may be used to endorse or promote products derived from |
21 | + this software without specific prior written permission. |
22 | + |
23 | +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
24 | +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
25 | +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
26 | +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
27 | +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
28 | +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
29 | +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
30 | +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
31 | +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
32 | +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
33 | +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
34 | |
35 | === renamed file 'LICENSE' => 'LICENSE.moved' |
36 | === added file 'Makefile' |
37 | --- Makefile 1970-01-01 00:00:00 +0000 |
38 | +++ Makefile 2011-07-28 17:08:30 +0000 |
39 | @@ -0,0 +1,22 @@ |
40 | +include $(GOROOT)/src/Make.inc |
41 | + |
42 | +TARG=launchpad.net/gobson/bson |
43 | + |
44 | +GOFILES=\ |
45 | + gobson.go\ |
46 | + encode.go\ |
47 | + decode.go\ |
48 | + |
49 | +include $(GOROOT)/src/Make.pkg |
50 | + |
51 | +GOFMT=gofmt |
52 | +BADFMT=$(shell $(GOFMT) -l $(GOFILES) $(wildcard *_test.go) 2> /dev/null) |
53 | + |
54 | +gofmt: $(BADFMT) |
55 | + @for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done |
56 | + |
57 | +ifneq ($(BADFMT),) |
58 | +ifneq ($(MAKECMDGOALS),gofmt) |
59 | +$(warning WARNING: make gofmt: $(BADFMT)) |
60 | +endif |
61 | +endif |
62 | |
63 | === renamed file 'Makefile' => 'Makefile.moved' |
64 | === added file 'decode.go' |
65 | --- decode.go 1970-01-01 00:00:00 +0000 |
66 | +++ decode.go 2011-07-28 17:08:30 +0000 |
67 | @@ -0,0 +1,596 @@ |
68 | +// gobson - BSON library for Go. |
69 | +// |
70 | +// Copyright (c) 2010-2011 - Gustavo Niemeyer <gustavo@niemeyer.net> |
71 | +// |
72 | +// All rights reserved. |
73 | +// |
74 | +// Redistribution and use in source and binary forms, with or without |
75 | +// modification, are permitted provided that the following conditions are met: |
76 | +// |
77 | +// * Redistributions of source code must retain the above copyright notice, |
78 | +// this list of conditions and the following disclaimer. |
79 | +// * Redistributions in binary form must reproduce the above copyright notice, |
80 | +// this list of conditions and the following disclaimer in the documentation |
81 | +// and/or other materials provided with the distribution. |
82 | +// * Neither the name of the copyright holder nor the names of its |
83 | +// contributors may be used to endorse or promote products derived from |
84 | +// this software without specific prior written permission. |
85 | +// |
86 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
87 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
88 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
89 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
90 | +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
91 | +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
92 | +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
93 | +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
94 | +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
95 | +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
96 | +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
97 | + |
98 | +package bson |
99 | + |
100 | +import ( |
101 | + "reflect" |
102 | + "math" |
103 | + "fmt" |
104 | +) |
105 | + |
106 | +type decoder struct { |
107 | + in []byte |
108 | + i int |
109 | +} |
110 | + |
111 | +// -------------------------------------------------------------------------- |
112 | +// Some helper functions. |
113 | + |
114 | +func corrupted() { |
115 | + panic("Document is corrupted") |
116 | +} |
117 | + |
118 | +func zeroNilPtr(v reflect.Value) (changed bool) { |
119 | + if v.Kind() == reflect.Ptr && v.IsNil() { |
120 | + v.Set(reflect.New(v.Type().Elem())) |
121 | + return true |
122 | + } |
123 | + return false |
124 | +} |
125 | + |
126 | +func settableValueOf(i interface{}) reflect.Value { |
127 | + v := reflect.ValueOf(i) |
128 | + sv := reflect.New(v.Type()).Elem() |
129 | + sv.Set(v) |
130 | + return sv |
131 | +} |
132 | + |
133 | +// -------------------------------------------------------------------------- |
134 | +// Unmarshaling of documents. |
135 | + |
136 | +func (d *decoder) readDocTo(out reflect.Value) { |
137 | + var elemType reflect.Type |
138 | + outt := out.Type() |
139 | + outk := outt.Kind() |
140 | + |
141 | + for { |
142 | + if outk == reflect.Ptr && out.IsNil() { |
143 | + out.Set(reflect.New(outt.Elem())) |
144 | + } |
145 | + if setter, ok := out.Interface().(Setter); ok { |
146 | + setter.SetBSON(d.readDocD()) |
147 | + return |
148 | + } |
149 | + if outk == reflect.Ptr { |
150 | + out = out.Elem() |
151 | + outt = out.Type() |
152 | + outk = out.Kind() |
153 | + continue |
154 | + } |
155 | + break |
156 | + } |
157 | + |
158 | + var fieldsMap map[string]fieldInfo |
159 | + start := d.i |
160 | + |
161 | + switch outk { |
162 | + case reflect.Interface: |
163 | + if !out.IsNil() { |
164 | + panic("Found non-nil interface. Please contact the developers.") |
165 | + } |
166 | + mv := reflect.ValueOf(make(M)) |
167 | + out.Set(mv) |
168 | + out = mv |
169 | + outt = out.Type() |
170 | + outk = outt.Kind() |
171 | + fallthrough |
172 | + case reflect.Map: |
173 | + if outt.Key().Kind() != reflect.String { |
174 | + panic("BSON map must have string keys. Got: " + outt.String()) |
175 | + } |
176 | + elemType = outt.Elem() |
177 | + if out.IsNil() { |
178 | + out.Set(reflect.MakeMap(out.Type())) |
179 | + } |
180 | + case reflect.Struct: |
181 | + if outt != typeRaw { |
182 | + fields, err := getStructFields(out.Type()) |
183 | + if err != nil { |
184 | + panic(err) |
185 | + } |
186 | + fieldsMap = fields.Map |
187 | + } |
188 | + default: |
189 | + panic("TESTME:" + reflect.ValueOf(out).Type().String()) |
190 | + } |
191 | + |
192 | + end := d.i - 4 + int(d.readInt32()) |
193 | + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { |
194 | + corrupted() |
195 | + } |
196 | + for d.in[d.i] != '\x00' { |
197 | + kind := d.readByte() |
198 | + name := d.readCStr() |
199 | + if d.i >= end { |
200 | + corrupted() |
201 | + } |
202 | + |
203 | + switch outk { |
204 | + case reflect.Map: |
205 | + e := reflect.New(elemType).Elem() |
206 | + if d.readElemTo(e, kind) { |
207 | + out.SetMapIndex(reflect.ValueOf(name), e) |
208 | + } |
209 | + case reflect.Struct: |
210 | + if outt == typeRaw { |
211 | + d.readElemTo(blackHole, kind) |
212 | + } else { |
213 | + if info, ok := fieldsMap[name]; ok { |
214 | + d.readElemTo(out.Field(info.Num), kind) |
215 | + } else { |
216 | + d.dropElem(kind) |
217 | + } |
218 | + } |
219 | + } |
220 | + |
221 | + if d.i >= end { |
222 | + corrupted() |
223 | + } |
224 | + } |
225 | + d.i++ // '\x00' |
226 | + if d.i != end { |
227 | + corrupted() |
228 | + } |
229 | + |
230 | + switch outk { |
231 | + case reflect.Struct: |
232 | + if outt == typeRaw { |
233 | + out.Set(reflect.ValueOf(Raw{0x03, d.in[start:d.i]})) |
234 | + } |
235 | + } |
236 | +} |
237 | + |
238 | +func (d *decoder) readArrayDoc(t reflect.Type) interface{} { |
239 | + tmp := make([]reflect.Value, 0, 8) |
240 | + elemType := t.Elem() |
241 | + |
242 | + end := d.i - 4 + int(d.readInt32()) |
243 | + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { |
244 | + corrupted() |
245 | + } |
246 | + for d.in[d.i] != '\x00' { |
247 | + kind := d.readByte() |
248 | + for d.i < end && d.in[d.i] != '\x00' { |
249 | + d.i++ |
250 | + } |
251 | + if d.i >= end { |
252 | + corrupted() |
253 | + } |
254 | + d.i++ |
255 | + e := reflect.New(elemType).Elem() |
256 | + if d.readElemTo(e, kind) { |
257 | + tmp = append(tmp, e) |
258 | + } |
259 | + if d.i >= end { |
260 | + corrupted() |
261 | + } |
262 | + } |
263 | + d.i++ // '\x00' |
264 | + if d.i != end { |
265 | + corrupted() |
266 | + } |
267 | + |
268 | + n := len(tmp) |
269 | + slice := reflect.MakeSlice(t, n, n) |
270 | + for i := 0; i != n; i++ { |
271 | + slice.Index(i).Set(tmp[i]) |
272 | + } |
273 | + return slice.Interface() |
274 | +} |
275 | + |
276 | +var typeD = reflect.TypeOf(D{}) |
277 | +var typeSlice = reflect.TypeOf([]interface{}{}) |
278 | + |
279 | +func (d *decoder) readDocD() interface{} { |
280 | + slice := make(D, 0, 8) |
281 | + d.readDocWith(func(kind byte, name string) { |
282 | + e := DocElem{Name: name} |
283 | + v := reflect.ValueOf(&e.Value) |
284 | + if d.readElemTo(v.Elem(), kind) { |
285 | + slice = append(slice, e) |
286 | + } |
287 | + }) |
288 | + return slice |
289 | +} |
290 | + |
291 | +func (d *decoder) readDocWith(f func(kind byte, name string)) { |
292 | + end := d.i - 4 + int(d.readInt32()) |
293 | + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { |
294 | + corrupted() |
295 | + } |
296 | + for d.in[d.i] != '\x00' { |
297 | + kind := d.readByte() |
298 | + name := d.readCStr() |
299 | + if d.i >= end { |
300 | + corrupted() |
301 | + } |
302 | + f(kind, name) |
303 | + if d.i >= end { |
304 | + corrupted() |
305 | + } |
306 | + } |
307 | + d.i++ // '\x00' |
308 | + if d.i != end { |
309 | + corrupted() |
310 | + } |
311 | +} |
312 | + |
313 | +// -------------------------------------------------------------------------- |
314 | +// Unmarshaling of individual elements within a document. |
315 | + |
316 | +var blackHole = settableValueOf(struct{}{}) |
317 | + |
318 | +func (d *decoder) dropElem(kind byte) { |
319 | + d.readElemTo(blackHole, kind) |
320 | +} |
321 | + |
322 | +// Attempt to decode an element from the document and put it into out. |
323 | +// If the types are not compatible, the returned ok value will be |
324 | +// false and out will be unchanged. |
325 | +func (d *decoder) readElemTo(out reflect.Value, kind byte) (good bool) { |
326 | + |
327 | + start := d.i |
328 | + |
329 | + if kind == '\x03' { |
330 | + // Special case for documents. Delegate to readDocTo(). |
331 | + switch out.Kind() { |
332 | + case reflect.Interface, reflect.Ptr, reflect.Struct, reflect.Map: |
333 | + d.readDocTo(out) |
334 | + default: |
335 | + if _, ok := out.Interface().(D); ok { |
336 | + out.Set(reflect.ValueOf(d.readDocD())) |
337 | + } else { |
338 | + d.readDocTo(blackHole) |
339 | + } |
340 | + } |
341 | + return true |
342 | + } |
343 | + |
344 | + var in interface{} |
345 | + |
346 | + switch kind { |
347 | + case '\x01': // Float64 |
348 | + in = d.readFloat64() |
349 | + case '\x02': // UTF-8 string |
350 | + in = d.readStr() |
351 | + case '\x03': // Document |
352 | + panic("Can't happen. Handled above.") |
353 | + case '\x04': // Array |
354 | + outt := out.Type() |
355 | + for outt.Kind() == reflect.Ptr { |
356 | + outt = outt.Elem() |
357 | + } |
358 | + switch outt.Kind() { |
359 | + case reflect.Slice: |
360 | + in = d.readArrayDoc(outt) |
361 | + default: |
362 | + in = d.readArrayDoc(typeSlice) |
363 | + } |
364 | + case '\x05': // Binary |
365 | + b := d.readBinary() |
366 | + if b.Kind == 0x00 { |
367 | + in = b.Data |
368 | + } else { |
369 | + in = b |
370 | + } |
371 | + case '\x06': // Undefined (obsolete, but still seen in the wild) |
372 | + in = Undefined |
373 | + case '\x07': // ObjectId |
374 | + in = ObjectId(d.readBytes(12)) |
375 | + case '\x08': // Bool |
376 | + in = d.readBool() |
377 | + case '\x09': // Timestamp |
378 | + // MongoDB wants timestamps as milliseconds. |
379 | + // Go likes nanoseconds. Convert them. |
380 | + in = Timestamp(d.readInt64() * 1e6) |
381 | + case '\x0A': // Nil |
382 | + in = nil |
383 | + case '\x0B': // RegEx |
384 | + in = d.readRegEx() |
385 | + case '\x0D': // JavaScript without scope |
386 | + in = JS{Code: d.readStr()} |
387 | + case '\x0E': // Symbol |
388 | + in = Symbol(d.readStr()) |
389 | + case '\x0F': // JavaScript with scope |
390 | + d.i += 4 // Skip length |
391 | + js := JS{d.readStr(), make(M)} |
392 | + d.readDocTo(reflect.ValueOf(js.Scope)) |
393 | + in = js |
394 | + case '\x10': // Int32 |
395 | + in = int(d.readInt32()) |
396 | + case '\x11': // Mongo-specific timestamp |
397 | + in = MongoTimestamp(d.readInt64()) |
398 | + case '\x12': // Int64 |
399 | + in = d.readInt64() |
400 | + case '\x7F': // Max key |
401 | + in = MaxKey |
402 | + case '\xFF': // Min key |
403 | + in = MinKey |
404 | + default: |
405 | + panic(fmt.Sprintf("Unknown element kind (0x%02X)", kind)) |
406 | + } |
407 | + |
408 | + outt := out.Type() |
409 | + |
410 | + if outt == typeRaw { |
411 | + out.Set(reflect.ValueOf(Raw{kind, d.in[start:d.i]})) |
412 | + return true |
413 | + } |
414 | + |
415 | + if setter, ok := out.Interface().(Setter); ok { |
416 | + if zeroNilPtr(out) { |
417 | + setter = out.Interface().(Setter) |
418 | + } |
419 | + return setter.SetBSON(in) |
420 | + } |
421 | + |
422 | + if in == nil { |
423 | + out.Set(reflect.Zero(outt)) |
424 | + return true |
425 | + } |
426 | + |
427 | + outk := outt.Kind() |
428 | + |
429 | + // Dereference and initialize pointer if necessary. |
430 | + first := true |
431 | + for outk == reflect.Ptr { |
432 | + if !out.IsNil() { |
433 | + out = out.Elem() |
434 | + } else { |
435 | + elem := reflect.New(outt.Elem()) |
436 | + if first { |
437 | + // Only set if value is compatible. |
438 | + first = false |
439 | + defer func(out, elem reflect.Value) { |
440 | + if good { |
441 | + out.Set(elem) |
442 | + } |
443 | + }(out, elem) |
444 | + } else { |
445 | + out.Set(elem) |
446 | + } |
447 | + out = elem |
448 | + } |
449 | + outt = out.Type() |
450 | + outk = outt.Kind() |
451 | + } |
452 | + |
453 | + inv := reflect.ValueOf(in) |
454 | + if outt == inv.Type() { |
455 | + out.Set(inv) |
456 | + return true |
457 | + } |
458 | + |
459 | + switch outk { |
460 | + case reflect.Interface: |
461 | + out.Set(inv) |
462 | + return true |
463 | + case reflect.String: |
464 | + switch inv.Kind() { |
465 | + case reflect.String: |
466 | + out.SetString(inv.String()) |
467 | + return true |
468 | + case reflect.Slice: |
469 | + if b, ok := in.([]byte); ok { |
470 | + out.SetString(string(b)) |
471 | + return true |
472 | + } |
473 | + } |
474 | + case reflect.Slice, reflect.Array: |
475 | + // Remember, array (0x04) slices are built with the correct element |
476 | + // type. If we are here, must be a cross BSON kind conversion. |
477 | + if outt.Elem().Kind() == reflect.Uint8 { |
478 | + switch inv.Kind() { |
479 | + case reflect.String: |
480 | + slice := []byte(inv.String()) |
481 | + out.Set(reflect.ValueOf(slice)) |
482 | + return true |
483 | + case reflect.Slice: |
484 | + // out must be an array. A slice would trigger |
485 | + // inv.Type() == out.Type() above. |
486 | + reflect.Copy(out, inv) |
487 | + return true |
488 | + } |
489 | + } |
490 | + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
491 | + switch inv.Kind() { |
492 | + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
493 | + // MongoDB wants timestamps as milliseconds. |
494 | + // Go likes nanoseconds. Convert them. |
495 | + // out.Type() == inv.Type() has been handled above. |
496 | + if outt == typeTimestamp { |
497 | + out.SetInt(inv.Int() * 1e6) |
498 | + } else if inv.Type() == typeTimestamp { |
499 | + out.SetInt(inv.Int() / 1e6) |
500 | + } else { |
501 | + out.SetInt(inv.Int()) |
502 | + } |
503 | + return true |
504 | + case reflect.Float32, reflect.Float64: |
505 | + out.SetInt(int64(inv.Float())) |
506 | + return true |
507 | + case reflect.Bool: |
508 | + if inv.Bool() { |
509 | + out.SetInt(1) |
510 | + } else { |
511 | + out.SetInt(0) |
512 | + } |
513 | + return true |
514 | + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
515 | + panic("Can't happen. No uint types in BSON?") |
516 | + } |
517 | + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
518 | + switch inv.Kind() { |
519 | + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
520 | + out.SetUint(uint64(inv.Int())) |
521 | + return true |
522 | + case reflect.Float32, reflect.Float64: |
523 | + out.SetUint(uint64(inv.Float())) |
524 | + return true |
525 | + case reflect.Bool: |
526 | + if inv.Bool() { |
527 | + out.SetUint(1) |
528 | + } else { |
529 | + out.SetUint(0) |
530 | + } |
531 | + return true |
532 | + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
533 | + panic("Can't happen. No uint types in BSON?") |
534 | + } |
535 | + case reflect.Float32, reflect.Float64: |
536 | + switch inv.Kind() { |
537 | + case reflect.Float32, reflect.Float64: |
538 | + out.SetFloat(inv.Float()) |
539 | + return true |
540 | + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
541 | + out.SetFloat(float64(inv.Int())) |
542 | + return true |
543 | + case reflect.Bool: |
544 | + if inv.Bool() { |
545 | + out.SetFloat(1) |
546 | + } else { |
547 | + out.SetFloat(0) |
548 | + } |
549 | + return true |
550 | + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
551 | + panic("Can't happen. No uint types in BSON?") |
552 | + } |
553 | + case reflect.Bool: |
554 | + switch inv.Kind() { |
555 | + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
556 | + out.SetBool(inv.Int() != 0) |
557 | + return true |
558 | + case reflect.Float32, reflect.Float64: |
559 | + out.SetBool(inv.Float() != 0) |
560 | + return true |
561 | + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
562 | + panic("Can't happen. No uint types in BSON?") |
563 | + } |
564 | + } |
565 | + |
566 | + return false |
567 | +} |
568 | + |
569 | +// -------------------------------------------------------------------------- |
570 | +// Parsers of basic types. |
571 | + |
572 | +func (d *decoder) readRegEx() RegEx { |
573 | + re := RegEx{} |
574 | + re.Pattern = d.readCStr() |
575 | + re.Options = d.readCStr() |
576 | + return re |
577 | +} |
578 | + |
579 | +func (d *decoder) readBinary() Binary { |
580 | + l := d.readInt32() |
581 | + b := Binary{} |
582 | + b.Kind = d.readByte() |
583 | + b.Data = d.readBytes(l) |
584 | + if b.Kind == 0x02 { |
585 | + // Weird obsolete format with redundant length. |
586 | + b.Data = b.Data[4:] |
587 | + } |
588 | + return b |
589 | +} |
590 | + |
591 | +func (d *decoder) readStr() string { |
592 | + l := d.readInt32() |
593 | + b := d.readBytes(l - 1) |
594 | + if d.readByte() != '\x00' { |
595 | + corrupted() |
596 | + } |
597 | + return string(b) |
598 | +} |
599 | + |
600 | +func (d *decoder) readCStr() string { |
601 | + start := d.i |
602 | + end := start |
603 | + l := len(d.in) |
604 | + for ; end != l; end++ { |
605 | + if d.in[end] == '\x00' { |
606 | + break |
607 | + } |
608 | + } |
609 | + d.i = end + 1 |
610 | + if d.i > l { |
611 | + corrupted() |
612 | + } |
613 | + return string(d.in[start:end]) |
614 | +} |
615 | + |
616 | +func (d *decoder) readBool() bool { |
617 | + if d.readByte() == 1 { |
618 | + return true |
619 | + } |
620 | + return false |
621 | +} |
622 | + |
623 | +func (d *decoder) readFloat64() float64 { |
624 | + return math.Float64frombits(uint64(d.readInt64())) |
625 | +} |
626 | + |
627 | +func (d *decoder) readInt32() int32 { |
628 | + b := d.readBytes(4) |
629 | + return int32((uint32(b[0]) << 0) | |
630 | + (uint32(b[1]) << 8) | |
631 | + (uint32(b[2]) << 16) | |
632 | + (uint32(b[3]) << 24)) |
633 | +} |
634 | + |
635 | +func (d *decoder) readInt64() int64 { |
636 | + b := d.readBytes(8) |
637 | + return int64((uint64(b[0]) << 0) | |
638 | + (uint64(b[1]) << 8) | |
639 | + (uint64(b[2]) << 16) | |
640 | + (uint64(b[3]) << 24) | |
641 | + (uint64(b[4]) << 32) | |
642 | + (uint64(b[5]) << 40) | |
643 | + (uint64(b[6]) << 48) | |
644 | + (uint64(b[7]) << 56)) |
645 | +} |
646 | + |
647 | +func (d *decoder) readByte() byte { |
648 | + i := d.i |
649 | + d.i++ |
650 | + if d.i > len(d.in) { |
651 | + corrupted() |
652 | + } |
653 | + return d.in[i] |
654 | +} |
655 | + |
656 | +func (d *decoder) readBytes(length int32) []byte { |
657 | + start := d.i |
658 | + d.i += int(length) |
659 | + if d.i > len(d.in) { |
660 | + corrupted() |
661 | + } |
662 | + return d.in[start : start+int(length)] |
663 | +} |
664 | |
665 | === added file 'encode.go' |
666 | --- encode.go 1970-01-01 00:00:00 +0000 |
667 | +++ encode.go 2011-07-28 17:08:30 +0000 |
668 | @@ -0,0 +1,420 @@ |
669 | +// gobson - BSON library for Go. |
670 | +// |
671 | +// Copyright (c) 2010-2011 - Gustavo Niemeyer <gustavo@niemeyer.net> |
672 | +// |
673 | +// All rights reserved. |
674 | +// |
675 | +// Redistribution and use in source and binary forms, with or without |
676 | +// modification, are permitted provided that the following conditions are met: |
677 | +// |
678 | +// * Redistributions of source code must retain the above copyright notice, |
679 | +// this list of conditions and the following disclaimer. |
680 | +// * Redistributions in binary form must reproduce the above copyright notice, |
681 | +// this list of conditions and the following disclaimer in the documentation |
682 | +// and/or other materials provided with the distribution. |
683 | +// * Neither the name of the copyright holder nor the names of its |
684 | +// contributors may be used to endorse or promote products derived from |
685 | +// this software without specific prior written permission. |
686 | +// |
687 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
688 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
689 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
690 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
691 | +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
692 | +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
693 | +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
694 | +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
695 | +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
696 | +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
697 | +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
698 | + |
699 | +package bson |
700 | + |
701 | +import ( |
702 | + "strconv" |
703 | + "reflect" |
704 | + "math" |
705 | +) |
706 | + |
707 | +// -------------------------------------------------------------------------- |
708 | +// Some internal infrastructure. |
709 | + |
710 | +var ( |
711 | + typeRegEx reflect.Type |
712 | + typeBinary reflect.Type |
713 | + typeObjectId reflect.Type |
714 | + typeSymbol reflect.Type |
715 | + typeTimestamp reflect.Type |
716 | + typeMongoTimestamp reflect.Type |
717 | + typeOrderKey reflect.Type |
718 | + typeDocElem reflect.Type |
719 | + typeRaw reflect.Type |
720 | +) |
721 | + |
722 | +const itoaCacheSize = 32 |
723 | + |
724 | +var itoaCache []string |
725 | + |
726 | +func init() { |
727 | + typeBinary = reflect.TypeOf(Binary{}) |
728 | + typeObjectId = reflect.TypeOf(ObjectId("")) |
729 | + typeSymbol = reflect.TypeOf(Symbol("")) |
730 | + typeTimestamp = reflect.TypeOf(Timestamp(0)) |
731 | + typeMongoTimestamp = reflect.TypeOf(MongoTimestamp(0)) |
732 | + typeOrderKey = reflect.TypeOf(MinKey) |
733 | + typeDocElem = reflect.TypeOf(DocElem{}) |
734 | + typeRaw = reflect.TypeOf(Raw{}) |
735 | + |
736 | + itoaCache = make([]string, itoaCacheSize) |
737 | + for i := 0; i != itoaCacheSize; i++ { |
738 | + itoaCache[i] = strconv.Itoa(i) |
739 | + } |
740 | +} |
741 | + |
742 | +func itoa(i int) string { |
743 | + if i < itoaCacheSize { |
744 | + return itoaCache[i] |
745 | + } |
746 | + return strconv.Itoa(i) |
747 | +} |
748 | + |
749 | + |
750 | +// -------------------------------------------------------------------------- |
751 | +// Marshaling of the document value itself. |
752 | + |
753 | +type encoder struct { |
754 | + out []byte |
755 | +} |
756 | + |
757 | +func (e *encoder) addDoc(v reflect.Value) { |
758 | + for { |
759 | + if vi, ok := v.Interface().(Getter); ok { |
760 | + v = reflect.ValueOf(vi.GetBSON()) |
761 | + continue |
762 | + } |
763 | + if v.Kind() == reflect.Ptr { |
764 | + v = v.Elem() |
765 | + continue |
766 | + } |
767 | + break |
768 | + } |
769 | + |
770 | + if v.Type() == typeRaw { |
771 | + raw := v.Interface().(Raw) |
772 | + if raw.Kind != 0x03 && raw.Kind != 0x00 { |
773 | + panic("Attempted to unmarshal Raw kind " + strconv.Itoa(int(raw.Kind)) + " as a document") |
774 | + } |
775 | + e.addBytes(raw.Data...) |
776 | + return |
777 | + } |
778 | + |
779 | + start := e.reserveInt32() |
780 | + |
781 | + switch v.Kind() { |
782 | + case reflect.Map: |
783 | + e.addMap(v) |
784 | + case reflect.Struct: |
785 | + e.addStruct(v) |
786 | + case reflect.Array, reflect.Slice: |
787 | + e.addSlice(v) |
788 | + default: |
789 | + panic("Can't marshal " + v.Type().String() + " as a BSON document") |
790 | + } |
791 | + |
792 | + e.addBytes(0) |
793 | + e.setInt32(start, int32(len(e.out)-start)) |
794 | +} |
795 | + |
796 | +func (e *encoder) addMap(v reflect.Value) { |
797 | + for _, k := range v.MapKeys() { |
798 | + e.addElem(k.String(), v.MapIndex(k), false) |
799 | + } |
800 | +} |
801 | + |
802 | +func (e *encoder) addStruct(v reflect.Value) { |
803 | + fields, err := getStructFields(v.Type()) |
804 | + if err != nil { |
805 | + panic(err) |
806 | + } |
807 | + for i, info := range fields.List { |
808 | + value := v.Field(i) |
809 | + if info.Conditional && isZero(value) { |
810 | + continue |
811 | + } |
812 | + e.addElem(info.Key, value, info.Short) |
813 | + } |
814 | +} |
815 | + |
816 | +func isZero(v reflect.Value) bool { |
817 | + switch v.Kind() { |
818 | + case reflect.String: |
819 | + return len(v.String()) == 0 |
820 | + case reflect.Ptr, reflect.Interface: |
821 | + return v.IsNil() |
822 | + case reflect.Slice: |
823 | + return v.Len() == 0 |
824 | + case reflect.Map: |
825 | + return v.Len() == 0 |
826 | + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
827 | + return v.Int() == 0 |
828 | + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
829 | + return v.Uint() == 0 |
830 | + case reflect.Bool: |
831 | + return !v.Bool() |
832 | + } |
833 | + return false |
834 | +} |
835 | + |
836 | +func (e *encoder) addSlice(v reflect.Value) { |
837 | + if d, ok := v.Interface().(D); ok { |
838 | + for _, elem := range d { |
839 | + e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) |
840 | + } |
841 | + } else { |
842 | + for i := 0; i != v.Len(); i++ { |
843 | + e.addElem(itoa(i), v.Index(i), false) |
844 | + } |
845 | + } |
846 | +} |
847 | + |
848 | + |
849 | +// -------------------------------------------------------------------------- |
850 | +// Marshaling of elements in a document. |
851 | + |
852 | +func (e *encoder) addElemName(kind byte, name string) { |
853 | + e.addBytes(kind) |
854 | + e.addBytes([]byte(name)...) |
855 | + e.addBytes(0) |
856 | +} |
857 | + |
858 | +func (e *encoder) addElem(name string, v reflect.Value, short bool) { |
859 | + |
860 | + if !v.IsValid() { |
861 | + e.addElemName('\x0A', name) |
862 | + return |
863 | + } |
864 | + |
865 | + if getter, ok := v.Interface().(Getter); ok { |
866 | + e.addElem(name, reflect.ValueOf(getter.GetBSON()), short) |
867 | + return |
868 | + } |
869 | + |
870 | + switch v.Kind() { |
871 | + |
872 | + case reflect.Interface: |
873 | + e.addElem(name, v.Elem(), short) |
874 | + |
875 | + case reflect.Ptr: |
876 | + e.addElem(name, v.Elem(), short) |
877 | + |
878 | + case reflect.String: |
879 | + s := v.String() |
880 | + |
881 | + switch v.Type() { |
882 | + |
883 | + case typeObjectId: |
884 | + if len(s) != 12 { |
885 | + panic("ObjectIDs must be exactly 12 bytes long (got " + |
886 | + strconv.Itoa(len(s)) + ")") |
887 | + } |
888 | + e.addElemName('\x07', name) |
889 | + e.addBytes([]byte(s)...) |
890 | + |
891 | + case typeSymbol: |
892 | + e.addElemName('\x0E', name) |
893 | + e.addStr(s) |
894 | + |
895 | + default: |
896 | + e.addElemName('\x02', name) |
897 | + e.addStr(s) |
898 | + } |
899 | + |
900 | + case reflect.Float32, reflect.Float64: |
901 | + e.addElemName('\x01', name) |
902 | + e.addInt64(int64(math.Float64bits(v.Float()))) |
903 | + |
904 | + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
905 | + u := v.Uint() |
906 | + if int64(u) < 0 { |
907 | + panic("BSON has no uint64 type, and value is too large to fit correctly in an int64") |
908 | + } else if u <= math.MaxInt32 && (short || v.Kind() <= reflect.Uint32) { |
909 | + e.addElemName('\x10', name) |
910 | + e.addInt32(int32(u)) |
911 | + } else { |
912 | + e.addElemName('\x12', name) |
913 | + e.addInt64(int64(u)) |
914 | + } |
915 | + |
916 | + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
917 | + if v.Type().Kind() <= reflect.Int32 { |
918 | + e.addElemName('\x10', name) |
919 | + e.addInt32(int32(v.Int())) |
920 | + } else { |
921 | + switch v.Type() { |
922 | + |
923 | + case typeTimestamp: |
924 | + // MongoDB wants timestamps as milliseconds. |
925 | + // Go likes nanoseconds. Convert them. |
926 | + e.addElemName('\x09', name) |
927 | + e.addInt64(v.Int() / 1e6) |
928 | + |
929 | + case typeMongoTimestamp: |
930 | + e.addElemName('\x11', name) |
931 | + e.addInt64(v.Int()) |
932 | + |
933 | + case typeOrderKey: |
934 | + if v.Int() == int64(MaxKey) { |
935 | + e.addElemName('\x7F', name) |
936 | + } else { |
937 | + e.addElemName('\xFF', name) |
938 | + } |
939 | + |
940 | + default: |
941 | + i := v.Int() |
942 | + if short && i >= math.MinInt32 && i <= math.MaxInt32 { |
943 | + // It fits into an int32, encode as such. |
944 | + e.addElemName('\x10', name) |
945 | + e.addInt32(int32(i)) |
946 | + } else { |
947 | + e.addElemName('\x12', name) |
948 | + e.addInt64(i) |
949 | + } |
950 | + } |
951 | + } |
952 | + |
953 | + case reflect.Bool: |
954 | + e.addElemName('\x08', name) |
955 | + if v.Bool() { |
956 | + e.addBytes(1) |
957 | + } else { |
958 | + e.addBytes(0) |
959 | + } |
960 | + |
961 | + case reflect.Map: |
962 | + e.addElemName('\x03', name) |
963 | + e.addDoc(v) |
964 | + |
965 | + case reflect.Slice: |
966 | + vt := v.Type() |
967 | + et := vt.Elem() |
968 | + if et.Kind() == reflect.Uint8 { |
969 | + // FIXME: This breaks down with custom types based on []byte |
970 | + e.addElemName('\x05', name) |
971 | + e.addBinary('\x00', v.Interface().([]byte)) |
972 | + } else if et == typeDocElem { |
973 | + e.addElemName('\x03', name) |
974 | + e.addDoc(v) |
975 | + } else { |
976 | + e.addElemName('\x04', name) |
977 | + e.addDoc(v) |
978 | + } |
979 | + |
980 | + case reflect.Array: |
981 | + et := v.Type().Elem() |
982 | + if et.Kind() == reflect.Uint8 { |
983 | + e.addElemName('\x05', name) |
984 | + e.addBinary('\x00', v.Slice(0, v.Len()).Interface().([]byte)) |
985 | + } else { |
986 | + e.addElemName('\x04', name) |
987 | + e.addDoc(v) |
988 | + } |
989 | + |
990 | + case reflect.Struct: |
991 | + switch s := v.Interface().(type) { |
992 | + |
993 | + case Raw: |
994 | + kind := s.Kind |
995 | + if kind == 0x00 { |
996 | + kind = 0x03 |
997 | + } |
998 | + e.addElemName(kind, name) |
999 | + e.addBytes(s.Data...) |
1000 | + |
1001 | + case Binary: |
1002 | + e.addElemName('\x05', name) |
1003 | + e.addBinary(s.Kind, s.Data) |
1004 | + |
1005 | + case RegEx: |
1006 | + e.addElemName('\x0B', name) |
1007 | + e.addCStr(s.Pattern) |
1008 | + e.addCStr(s.Options) |
1009 | + |
1010 | + case JS: |
1011 | + if s.Scope == nil { |
1012 | + e.addElemName('\x0D', name) |
1013 | + e.addStr(s.Code) |
1014 | + } else { |
1015 | + e.addElemName('\x0F', name) |
1016 | + start := e.reserveInt32() |
1017 | + e.addStr(s.Code) |
1018 | + e.addDoc(reflect.ValueOf(s.Scope)) |
1019 | + e.setInt32(start, int32(len(e.out)-start)) |
1020 | + } |
1021 | + |
1022 | + case undefined: |
1023 | + e.addElemName('\x06', name) |
1024 | + |
1025 | + default: |
1026 | + e.addElemName('\x03', name) |
1027 | + e.addDoc(v) |
1028 | + } |
1029 | + |
1030 | + default: |
1031 | + panic("Can't marshal " + v.Type().String() + " in a BSON document") |
1032 | + } |
1033 | +} |
1034 | + |
1035 | + |
1036 | +// -------------------------------------------------------------------------- |
1037 | +// Marshaling of base types. |
1038 | + |
1039 | +func (e *encoder) addBinary(subtype byte, v []byte) { |
1040 | + if subtype == 0x02 { |
1041 | + // Wonder how that brilliant idea came to life. Obsolete, luckily. |
1042 | + e.addInt32(int32(len(v) + 4)) |
1043 | + e.addBytes(subtype) |
1044 | + e.addInt32(int32(len(v))) |
1045 | + } else { |
1046 | + e.addInt32(int32(len(v))) |
1047 | + e.addBytes(subtype) |
1048 | + } |
1049 | + e.addBytes(v...) |
1050 | +} |
1051 | + |
1052 | +func (e *encoder) addStr(v string) { |
1053 | + e.addInt32(int32(len(v) + 1)) |
1054 | + e.addCStr(v) |
1055 | +} |
1056 | + |
1057 | +func (e *encoder) addCStr(v string) { |
1058 | + e.addBytes([]byte(v)...) |
1059 | + e.addBytes(0) |
1060 | +} |
1061 | + |
1062 | +func (e *encoder) reserveInt32() (pos int) { |
1063 | + pos = len(e.out) |
1064 | + e.addBytes(0, 0, 0, 0) |
1065 | + return pos |
1066 | +} |
1067 | + |
1068 | +func (e *encoder) setInt32(pos int, v int32) { |
1069 | + e.out[pos+0] = byte(v) |
1070 | + e.out[pos+1] = byte(v >> 8) |
1071 | + e.out[pos+2] = byte(v >> 16) |
1072 | + e.out[pos+3] = byte(v >> 24) |
1073 | +} |
1074 | + |
1075 | +func (e *encoder) addInt32(v int32) { |
1076 | + u := uint32(v) |
1077 | + e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24)) |
1078 | +} |
1079 | + |
1080 | +func (e *encoder) addInt64(v int64) { |
1081 | + u := uint64(v) |
1082 | + e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24), |
1083 | + byte(u>>32), byte(u>>40), byte(u>>48), byte(u>>56)) |
1084 | +} |
1085 | + |
1086 | +func (e *encoder) addBytes(v ...byte) { |
1087 | + e.out = append(e.out, v...) |
1088 | +} |
1089 | |
1090 | === added file 'gobson.go' |
1091 | --- gobson.go 1970-01-01 00:00:00 +0000 |
1092 | +++ gobson.go 2011-07-28 17:08:30 +0000 |
1093 | @@ -0,0 +1,483 @@ |
1094 | +// gobson - BSON library for Go. |
1095 | +// |
1096 | +// Copyright (c) 2010-2011 - Gustavo Niemeyer <gustavo@niemeyer.net> |
1097 | +// |
1098 | +// All rights reserved. |
1099 | +// |
1100 | +// Redistribution and use in source and binary forms, with or without |
1101 | +// modification, are permitted provided that the following conditions are met: |
1102 | +// |
1103 | +// * Redistributions of source code must retain the above copyright notice, |
1104 | +// this list of conditions and the following disclaimer. |
1105 | +// * Redistributions in binary form must reproduce the above copyright notice, |
1106 | +// this list of conditions and the following disclaimer in the documentation |
1107 | +// and/or other materials provided with the distribution. |
1108 | +// * Neither the name of the copyright holder nor the names of its |
1109 | +// contributors may be used to endorse or promote products derived from |
1110 | +// this software without specific prior written permission. |
1111 | +// |
1112 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
1113 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
1114 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
1115 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
1116 | +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
1117 | +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
1118 | +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
1119 | +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
1120 | +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
1121 | +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
1122 | +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
1123 | + |
1124 | +package bson |
1125 | + |
1126 | +import ( |
1127 | + "encoding/binary" |
1128 | + "encoding/hex" |
1129 | + "crypto/md5" |
1130 | + "runtime" |
1131 | + "reflect" |
1132 | + "strings" |
1133 | + "sync/atomic" |
1134 | + "sync" |
1135 | + "time" |
1136 | + "fmt" |
1137 | + "os" |
1138 | +) |
1139 | + |
1140 | +// -------------------------------------------------------------------------- |
1141 | +// The public API. |
1142 | + |
1143 | +// Objects implementing the bson.Getter interface will get the GetBSON() |
1144 | +// method called when the given value has to be marshalled, and the result |
1145 | +// of this method will be marshaled in place of the actual object. |
1146 | +type Getter interface { |
1147 | + GetBSON() interface{} |
1148 | +} |
1149 | + |
1150 | +// Objects implementing the bson.Setter interface will receive the BSON |
1151 | +// value via the SetBSON method during unmarshaling, and will not be |
1152 | +// changed as usual. If setting the value works, the method should |
1153 | +// return true. If it returns false, the given value will be omitted |
1154 | +// from maps and slices. |
1155 | +type Setter interface { |
1156 | + SetBSON(v interface{}) (ok bool) |
1157 | +} |
1158 | + |
1159 | +// Handy alias for a map[string]interface{} map, useful for dealing with BSON |
1160 | +// in a native way. For instance: |
1161 | +// |
1162 | +// bson.M{"a": 1, "b": true} |
1163 | +// |
1164 | +// There's no special handling for this type in addition to what's done anyway |
1165 | +// for an equivalent map type. Elements in the map will be dumped in an |
1166 | +// undefined ordered. See also the bson.D type for an ordered alternative. |
1167 | +type M map[string]interface{} |
1168 | + |
1169 | +// Type for dealing with documents containing ordered elements in a native |
1170 | +// fashion. For instance: |
1171 | +// |
1172 | +// bson.D{{"a", 1}, {"b", true}} |
1173 | +// |
1174 | +// In some situations, such as when creating indexes for MongoDB, the order in |
1175 | +// which the elements are defined is important. If the order is not important, |
1176 | +// using a map is generally more comfortable (see the bson.M type and the |
1177 | +// Map() method for D). |
1178 | +type D []DocElem |
1179 | + |
1180 | +// See the bson.D type. |
1181 | +type DocElem struct { |
1182 | + Name string |
1183 | + Value interface{} |
1184 | +} |
1185 | + |
1186 | +// Raw may be used to work with raw unprocessed BSON documents and elements, |
1187 | +// if necessary in advanced cases. Kind is the kind of element as defined |
1188 | +// per the BSON specification, and Data is the raw unprocessed data for |
1189 | +// the respective element. |
1190 | +// |
1191 | +// Relevant documentation: |
1192 | +// |
1193 | +// http://bsonspec.org/#/specification |
1194 | +// |
1195 | +type Raw struct { |
1196 | + Kind byte |
1197 | + Data []byte |
1198 | +} |
1199 | + |
1200 | +// Build a map[string]interface{} out of the ordered element name/value pairs. |
1201 | +func (d D) Map() (m M) { |
1202 | + m = make(M, len(d)) |
1203 | + for _, item := range d { |
1204 | + m[item.Name] = item.Value |
1205 | + } |
1206 | + return m |
1207 | +} |
1208 | + |
1209 | +// Unique ID identifying the BSON object. Must be exactly 12 bytes long. |
1210 | +// MongoDB objects by default have such a property set in their "_id" |
1211 | +// property. |
1212 | +// |
1213 | +// http://www.mongodb.org/display/DOCS/Object+IDs |
1214 | +type ObjectId string |
1215 | + |
1216 | +// ObjectIdHex returns an ObjectId from the provided hex representation. |
1217 | +// Calling this function with an invalid hex representation will |
1218 | +// cause a runtime panic. |
1219 | +func ObjectIdHex(s string) ObjectId { |
1220 | + d, err := hex.DecodeString(s) |
1221 | + if err != nil || len(d) != 12 { |
1222 | + panic(fmt.Sprintf("Invalid input to ObjectIdHex: %q", s)) |
1223 | + } |
1224 | + return ObjectId(d) |
1225 | +} |
1226 | + |
1227 | +// objectIdCounter is atomically incremented when generating a new ObjectId |
1228 | +// using NewObjectId() function. It's used as a counter part of an id. |
1229 | +var objectIdCounter uint32 = 0 |
1230 | + |
1231 | +// machineId stores machine id generated once and used in subsequent calls |
1232 | +// to NewObjectId function. |
1233 | +var machineId []byte |
1234 | + |
1235 | +// initMachineId generates machine id and puts it into the machineId global |
1236 | +// variable. If this function fails to get the hostname, it will cause |
1237 | +// a runtime error. |
1238 | +func initMachineId() { |
1239 | + var sum [3]byte |
1240 | + hostname, err := os.Hostname() |
1241 | + if err != nil { |
1242 | + panic("Failed to get hostname: " + err.String()) |
1243 | + } |
1244 | + hw := md5.New() |
1245 | + hw.Write([]byte(hostname)) |
1246 | + copy(sum[:3], hw.Sum()) |
1247 | + machineId = sum[:] |
1248 | +} |
1249 | + |
1250 | +// NewObjectId generates and returns a new unique ObjectId. |
1251 | +// This function causes a runtime error if it fails to get the hostname |
1252 | +// of the current machine. |
1253 | +func NewObjectId() ObjectId { |
1254 | + b := make([]byte, 12) |
1255 | + // Timestamp, 4 bytes, big endian |
1256 | + binary.BigEndian.PutUint32(b, uint32(time.Seconds())) |
1257 | + // Machine, first 3 bytes of md5(hostname) |
1258 | + if machineId == nil { |
1259 | + initMachineId() |
1260 | + } |
1261 | + b[4] = machineId[0] |
1262 | + b[5] = machineId[1] |
1263 | + b[6] = machineId[2] |
1264 | + // Pid, 2 bytes, specs don't specify endianness, but we use big endian. |
1265 | + pid := os.Getpid() |
1266 | + b[7] = byte(pid >> 8) |
1267 | + b[8] = byte(pid) |
1268 | + // Increment, 3 bytes, big endian |
1269 | + i := atomic.AddUint32(&objectIdCounter, 1) |
1270 | + b[9] = byte(i >> 16) |
1271 | + b[10] = byte(i >> 8) |
1272 | + b[11] = byte(i) |
1273 | + return ObjectId(b) |
1274 | +} |
1275 | + |
1276 | +// NewObjectIdSeconds returns a dummy ObjectId with the timestamp part filled |
1277 | +// with the provided number of seconds from epoch UTC, and all other parts |
1278 | +// filled with zeroes. It's not safe to insert a document with an id generated |
1279 | +// by this method, it is useful only for queries to find documents with ids |
1280 | +// generated before or after the specified timestamp. |
1281 | +func NewObjectIdSeconds(sec int32) ObjectId { |
1282 | + var b [12]byte |
1283 | + binary.BigEndian.PutUint32(b[:4], uint32(sec)) |
1284 | + return ObjectId(string(b[:])) |
1285 | +} |
1286 | + |
1287 | +// String returns a hex string representation of the id. |
1288 | +// Example: ObjectIdHex("4d88e15b60f486e428412dc9"). |
1289 | +func (id ObjectId) String() string { |
1290 | + return `ObjectIdHex("` + hex.EncodeToString([]byte(string(id))) + `")` |
1291 | +} |
1292 | + |
1293 | +// Valid returns true if the id is valid (contains exactly 12 bytes) |
1294 | +func (id ObjectId) Valid() bool { |
1295 | + return len(id) == 12 |
1296 | +} |
1297 | + |
1298 | +// byteSlice returns byte slice of id from start to end. |
1299 | +// Calling this function with an invalid id will cause a runtime panic. |
1300 | +func (id ObjectId) byteSlice(start, end int) []byte { |
1301 | + if len(id) != 12 { |
1302 | + panic(fmt.Sprintf("Invalid ObjectId: %q", string(id))) |
1303 | + } |
1304 | + return []byte(string(id)[start:end]) |
1305 | +} |
1306 | + |
1307 | +// Timestamp returns the timestamp part of the id (the number of seconds |
1308 | +// from epoch in UTC). |
1309 | +// It's a runtime error to call this method with an invalid id. |
1310 | +func (id ObjectId) Timestamp() int32 { |
1311 | + // First 4 bytes of ObjectId is 32-bit big-endian timestamp |
1312 | + return int32(binary.BigEndian.Uint32(id.byteSlice(0, 4))) |
1313 | +} |
1314 | + |
1315 | +// Machine returns the 3-byte machine id part of the id. |
1316 | +// It's a runtime error to call this method with an invalid id. |
1317 | +func (id ObjectId) Machine() []byte { |
1318 | + return id.byteSlice(4, 7) |
1319 | +} |
1320 | + |
1321 | +// Pid returns the process id part of the id. |
1322 | +// It's a runtime error to call this method with an invalid id. |
1323 | +func (id ObjectId) Pid() uint16 { |
1324 | + return binary.BigEndian.Uint16(id.byteSlice(7, 9)) |
1325 | +} |
1326 | + |
1327 | +// Counter returns the incrementing value part of the id. |
1328 | +// It's a runtime error to call this method with an invalid id. |
1329 | +func (id ObjectId) Counter() int32 { |
1330 | + b := id.byteSlice(9, 12) |
1331 | + // Counter is stored as big-endian 3-byte value |
1332 | + return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])) |
1333 | +} |
1334 | + |
1335 | +// Similar to a string, but used in languages with a distinct symbol type. This |
1336 | +// is an alias to a string type, so it can be used in string contexts and |
1337 | +// string(symbol) will work correctly. |
1338 | +type Symbol string |
1339 | + |
1340 | +// UTC timestamp defined as nanoseconds since the traditional epoch time. The |
1341 | +// internal MongoDB representation stores this value as milliseconds, so some |
1342 | +// precision will be lost when sending a Go value to MongoDB, but given that |
1343 | +// Go most commonly uses nanoseconds in time-related operations, this conversion |
1344 | +// is convenient. |
1345 | +type Timestamp int64 |
1346 | + |
1347 | +// Now returns a Timestamp value with the current time in nanoseconds. |
1348 | +func Now() Timestamp { |
1349 | + // The value is stored in MongoDB as milliseconds, so truncate the value |
1350 | + // ahead of time to avoid surprises after a roundtrip. |
1351 | + return Timestamp(time.Nanoseconds() / 1e6 * 1e6) |
1352 | +} |
1353 | + |
1354 | +// Special internal type used by MongoDB which for some strange reason has its |
1355 | +// own datatype defined in BSON. |
1356 | +type MongoTimestamp int64 |
1357 | + |
1358 | +type orderKey int64 |
1359 | + |
1360 | +// Special value which compares higher than all other possible BSON values. |
1361 | +var MaxKey = orderKey(1<<63 - 1) |
1362 | + |
1363 | +// Special value which compares lower than all other possible BSON values. |
1364 | +var MinKey = orderKey(-1 << 63) |
1365 | + |
1366 | +type undefined struct{} |
1367 | + |
1368 | +var Undefined undefined |
1369 | + |
1370 | +// Representation for non-standard binary values. Any kind should work, |
1371 | +// but the following are known as of this writing: |
1372 | +// |
1373 | +// 0x00 - Generic. This is decoded as []byte(data), not Binary{0x00, data}. |
1374 | +// 0x01 - Function (!?) |
1375 | +// 0x02 - Obsolete generic. |
1376 | +// 0x03 - UUID |
1377 | +// 0x05 - MD5 |
1378 | +// 0x80 - User defined. |
1379 | +// |
1380 | +type Binary struct { |
1381 | + Kind byte |
1382 | + Data []byte |
1383 | +} |
1384 | + |
1385 | +// A special type for regular expressions. The Options field should contain |
1386 | +// individual characters defining the way in which the pattern should be |
1387 | +// applied, and must be sorted. Valid options as of this writing are 'i' for |
1388 | +// case insensitive matching, 'm' for multi-line matching, 'x' for verbose |
1389 | +// mode, 'l' to make \w, \W, and similar be locale-dependent, 's' for dot-all |
1390 | +// mode (a '.' matches everything), and 'u' to make \w, \W, and similar match |
1391 | +// unicode. The value of the Options parameter is not verified before being |
1392 | +// marshaled into the BSON format. |
1393 | +type RegEx struct { |
1394 | + Pattern string |
1395 | + Options string |
1396 | +} |
1397 | + |
1398 | +// Special type for JavaScript code. If Scope is non-nil, it will be marshaled |
1399 | +// as a mapping from identifiers to values which should be used when evaluating |
1400 | +// the provided Code. |
1401 | +type JS struct { |
1402 | + Code string |
1403 | + Scope interface{} |
1404 | +} |
1405 | + |
1406 | + |
1407 | +const initialBufferSize = 64 |
1408 | + |
1409 | +func handleErr(err *os.Error) { |
1410 | + if r := recover(); r != nil { |
1411 | + if _, ok := r.(runtime.Error); ok { |
1412 | + panic(r) |
1413 | + } else if s, ok := r.(string); ok { |
1414 | + *err = os.NewError(s) |
1415 | + } else if e, ok := r.(os.Error); ok { |
1416 | + *err = e |
1417 | + } else { |
1418 | + panic(r) |
1419 | + } |
1420 | + } |
1421 | +} |
1422 | + |
1423 | + |
1424 | +// Marshal serializes the in document, which may be a map or a struct value. |
1425 | +// In the case of struct values, only exported fields will be serialized. |
1426 | +// These fields may optionally have tags to define the serialization key for |
1427 | +// the respective fields. Without a tag, the lowercased field name is used |
1428 | +// as the key for each field. If a field tag ends in "/c", that field will |
1429 | +// only be serialized if it's not set to the zero value for the field type. |
1430 | +// If a field tag ends with the "/s" suffix, an int64 value in the given |
1431 | +// field will be serialized as an int32 if possible. |
1432 | +func Marshal(in interface{}) (out []byte, err os.Error) { |
1433 | + defer handleErr(&err) |
1434 | + e := &encoder{make([]byte, 0, initialBufferSize)} |
1435 | + e.addDoc(reflect.ValueOf(in)) |
1436 | + return e.out, nil |
1437 | +} |
1438 | + |
1439 | +// Unmarshal deserializes data from in into the out value. The out value |
1440 | +// must be a map or a pointer to a struct (or a pointer to a struct pointer). |
1441 | +// In the case of struct values, field names are mapped to the struct using |
1442 | +// the field tag as the key. If the field has no tag, its lowercased name |
1443 | +// will be used as the default key. Nil values are properly initialized |
1444 | +// when necessary. |
1445 | +// |
1446 | +// The target field types of out may not necessarily match the BSON values |
1447 | +// of the provided data. If there is a sensible way to unmarshal the values |
1448 | +// into the Go types, they will be converted. Otherwise, the incompatible |
1449 | +// values will be silently skipped. |
1450 | +func Unmarshal(in []byte, out interface{}) (err os.Error) { |
1451 | + defer handleErr(&err) |
1452 | + v := reflect.ValueOf(out) |
1453 | + switch v.Kind() { |
1454 | + case reflect.Map, reflect.Ptr: |
1455 | + d := &decoder{in: in} |
1456 | + d.readDocTo(v) |
1457 | + case reflect.Struct: |
1458 | + return os.NewError("Unmarshal can't deal with struct values. Use a pointer.") |
1459 | + default: |
1460 | + return os.NewError("Unmarshal needs a map or a pointer to a struct.") |
1461 | + } |
1462 | + return nil |
1463 | +} |
1464 | + |
1465 | +// Unmarshal deserializes raw into the out value. In addition to whole |
1466 | +// documents, Raw's Unmarshal may also be used to unmarshal the data for |
1467 | +// individual elements within a partially unmarshalled document. This |
1468 | +// enables parts of a document to be lazily and conditionally deserialized. |
1469 | +// |
1470 | +// If the out value type is not compatible with raw, a *bson.TypeError |
1471 | +// is returned. |
1472 | +func (raw Raw) Unmarshal(out interface{}) (err os.Error) { |
1473 | + defer handleErr(&err) |
1474 | + v := reflect.ValueOf(out) |
1475 | + switch v.Kind() { |
1476 | + case reflect.Map, reflect.Ptr: |
1477 | + d := &decoder{in: raw.Data} |
1478 | + good := d.readElemTo(v, raw.Kind) |
1479 | + if !good { |
1480 | + return &TypeError{v.Type(), raw.Kind} |
1481 | + } |
1482 | + default: |
1483 | + return os.NewError("Raw Unmarshal needs a map or a valid pointer.") |
1484 | + } |
1485 | + return nil |
1486 | +} |
1487 | + |
1488 | +type TypeError struct { |
1489 | + Type reflect.Type |
1490 | + Kind byte |
1491 | +} |
1492 | + |
1493 | +func (e *TypeError) String() string { |
1494 | + return fmt.Sprintf("BSON kind 0x%02x isn't compatible with type %s", e.Kind, e.Type.String()) |
1495 | +} |
1496 | + |
1497 | +// -------------------------------------------------------------------------- |
1498 | +// Maintain a mapping of keys to structure field indexes |
1499 | + |
1500 | +type structFields struct { |
1501 | + Map map[string]fieldInfo |
1502 | + List []fieldInfo |
1503 | +} |
1504 | + |
1505 | +type fieldInfo struct { |
1506 | + Key string |
1507 | + Num int |
1508 | + Conditional bool |
1509 | + Short bool |
1510 | +} |
1511 | + |
1512 | +var fieldMap = make(map[string]*structFields) |
1513 | +var fieldMapMutex sync.RWMutex |
1514 | + |
1515 | +func getStructFields(st reflect.Type) (*structFields, os.Error) { |
1516 | + path := st.PkgPath() |
1517 | + name := st.Name() |
1518 | + |
1519 | + fullName := path + "." + name |
1520 | + fieldMapMutex.RLock() |
1521 | + fields, found := fieldMap[fullName] |
1522 | + fieldMapMutex.RUnlock() |
1523 | + if found { |
1524 | + return fields, nil |
1525 | + } |
1526 | + |
1527 | + n := st.NumField() |
1528 | + fieldsMap := make(map[string]fieldInfo) |
1529 | + fieldsList := make([]fieldInfo, n) |
1530 | + for i := 0; i != n; i++ { |
1531 | + field := st.Field(i) |
1532 | + if field.PkgPath != "" { |
1533 | + continue // Private field |
1534 | + } |
1535 | + |
1536 | + info := fieldInfo{Num: i} |
1537 | + |
1538 | + if s := strings.LastIndex(string(field.Tag), "/"); s != -1 { |
1539 | + for _, c := range field.Tag[s+1:] { |
1540 | + switch c { |
1541 | + case int('c'): |
1542 | + info.Conditional = true |
1543 | + case int('s'): |
1544 | + info.Short = true |
1545 | + default: |
1546 | + panic("Unsupported field flag: " + string([]int{c})) |
1547 | + } |
1548 | + } |
1549 | + field.Tag = field.Tag[:s] |
1550 | + } |
1551 | + |
1552 | + if field.Tag != "" { |
1553 | + info.Key = string(field.Tag) |
1554 | + } else { |
1555 | + info.Key = strings.ToLower(field.Name) |
1556 | + } |
1557 | + |
1558 | + if _, found = fieldsMap[info.Key]; found { |
1559 | + msg := "Duplicated key '" + info.Key + "' in struct " + st.String() |
1560 | + return nil, os.NewError(msg) |
1561 | + } |
1562 | + |
1563 | + fieldsList[len(fieldsMap)] = info |
1564 | + fieldsMap[info.Key] = info |
1565 | + } |
1566 | + |
1567 | + fields = &structFields{fieldsMap, fieldsList[:len(fieldsMap)]} |
1568 | + |
1569 | + if fullName != "." { |
1570 | + fieldMapMutex.Lock() |
1571 | + fieldMap[fullName] = fields |
1572 | + fieldMapMutex.Unlock() |
1573 | + } |
1574 | + |
1575 | + return fields, nil |
1576 | +} |
1577 | |
1578 | === added file 'gobson_test.go' |
1579 | --- gobson_test.go 1970-01-01 00:00:00 +0000 |
1580 | +++ gobson_test.go 2011-07-28 17:08:30 +0000 |
1581 | @@ -0,0 +1,1096 @@ |
1582 | +// gobson - BSON library for Go. |
1583 | +// |
1584 | +// Copyright (c) 2010-2011 - Gustavo Niemeyer <gustavo@niemeyer.net> |
1585 | +// |
1586 | +// All rights reserved. |
1587 | +// |
1588 | +// Redistribution and use in source and binary forms, with or without |
1589 | +// modification, are permitted provided that the following conditions are met: |
1590 | +// |
1591 | +// * Redistributions of source code must retain the above copyright notice, |
1592 | +// this list of conditions and the following disclaimer. |
1593 | +// * Redistributions in binary form must reproduce the above copyright notice, |
1594 | +// this list of conditions and the following disclaimer in the documentation |
1595 | +// and/or other materials provided with the distribution. |
1596 | +// * Neither the name of the copyright holder nor the names of its |
1597 | +// contributors may be used to endorse or promote products derived from |
1598 | +// this software without specific prior written permission. |
1599 | +// |
1600 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
1601 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
1602 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
1603 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
1604 | +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
1605 | +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
1606 | +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
1607 | +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
1608 | +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
1609 | +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
1610 | +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
1611 | + |
1612 | +package bson_test |
1613 | + |
1614 | +import ( |
1615 | + . "launchpad.net/gocheck" |
1616 | + "encoding/binary" |
1617 | + "testing" |
1618 | + "reflect" |
1619 | + "time" |
1620 | + "launchpad.net/gobson/bson" |
1621 | +) |
1622 | + |
1623 | + |
1624 | +func TestAll(t *testing.T) { |
1625 | + TestingT(t) |
1626 | +} |
1627 | + |
1628 | +type S struct{} |
1629 | + |
1630 | +var _ = Suite(&S{}) |
1631 | + |
1632 | + |
1633 | +// Wrap up the document elements contained in data, prepending the int32 |
1634 | +// length of the data, and appending the '\x00' value closing the document. |
1635 | +func wrapInDoc(data string) string { |
1636 | + result := make([]byte, len(data)+5) |
1637 | + binary.LittleEndian.PutUint32(result, uint32(len(result))) |
1638 | + copy(result[4:], []byte(data)) |
1639 | + return string(result) |
1640 | +} |
1641 | + |
1642 | +func makeZeroDoc(value interface{}) (zero interface{}) { |
1643 | + v := reflect.ValueOf(value) |
1644 | + t := v.Type() |
1645 | + if t.Kind() == reflect.Map { |
1646 | + mv := reflect.MakeMap(t) |
1647 | + zero = mv.Interface() |
1648 | + } else { |
1649 | + pv := reflect.New(v.Type().Elem()) |
1650 | + zero = pv.Interface() |
1651 | + } |
1652 | + return zero |
1653 | +} |
1654 | + |
1655 | +func testUnmarshal(c *C, data string, obj interface{}) { |
1656 | + zero := makeZeroDoc(obj) |
1657 | + err := bson.Unmarshal([]byte(data), zero) |
1658 | + c.Assert(err, IsNil) |
1659 | + c.Assert(zero, Equals, obj) |
1660 | +} |
1661 | + |
1662 | + |
1663 | +type testItemType struct { |
1664 | + obj interface{} |
1665 | + data string |
1666 | +} |
1667 | + |
1668 | +// -------------------------------------------------------------------------- |
1669 | +// Samples from bsonspec.org: |
1670 | + |
1671 | +var sampleItems = []testItemType{ |
1672 | + {bson.M{"hello": "world"}, |
1673 | + "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00"}, |
1674 | + {bson.M{"BSON": []interface{}{"awesome", float64(5.05), 1986}}, |
1675 | + "1\x00\x00\x00\x04BSON\x00&\x00\x00\x00\x020\x00\x08\x00\x00\x00" + |
1676 | + "awesome\x00\x011\x00333333\x14@\x102\x00\xc2\x07\x00\x00\x00\x00"}, |
1677 | +} |
1678 | + |
1679 | +func (s *S) TestMarshalSampleItems(c *C) { |
1680 | + for i, item := range sampleItems { |
1681 | + data, err := bson.Marshal(item.obj) |
1682 | + c.Assert(err, IsNil) |
1683 | + c.Assert(string(data), Equals, item.data, |
1684 | + Bug("Failed on item %d", i)) |
1685 | + } |
1686 | +} |
1687 | + |
1688 | +func (s *S) TestUnmarshalSampleItems(c *C) { |
1689 | + for i, item := range sampleItems { |
1690 | + value := bson.M{} |
1691 | + err := bson.Unmarshal([]byte(item.data), value) |
1692 | + c.Assert(err, IsNil) |
1693 | + c.Assert(value, Equals, item.obj, |
1694 | + Bug("Failed on item %d", i)) |
1695 | + } |
1696 | +} |
1697 | + |
1698 | +// -------------------------------------------------------------------------- |
1699 | +// Every type, ordered by the type flag. These are not wrapped with the |
1700 | +// length and last \x00 from the document. wrapInDoc() computes them. |
1701 | +// Note that all of them should be supported as two-way conversions. |
1702 | + |
1703 | +var allItems = []testItemType{ |
1704 | + {bson.M{}, |
1705 | + ""}, |
1706 | + {bson.M{"_": float64(5.05)}, |
1707 | + "\x01_\x00333333\x14@"}, |
1708 | + {bson.M{"_": "yo"}, |
1709 | + "\x02_\x00\x03\x00\x00\x00yo\x00"}, |
1710 | + {bson.M{"_": bson.M{"a": true}}, |
1711 | + "\x03_\x00\x09\x00\x00\x00\x08a\x00\x01\x00"}, |
1712 | + {bson.M{"_": []interface{}{true, false}}, |
1713 | + "\x04_\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"}, |
1714 | + {bson.M{"_": []byte("yo")}, |
1715 | + "\x05_\x00\x02\x00\x00\x00\x00yo"}, |
1716 | + {bson.M{"_": bson.Binary{0x02, []byte("old")}}, |
1717 | + "\x05_\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"}, |
1718 | + {bson.M{"_": bson.Binary{0x80, []byte("udef")}}, |
1719 | + "\x05_\x00\x04\x00\x00\x00\x80udef"}, |
1720 | + {bson.M{"_": bson.Undefined}, // Obsolete, but still seen in the wild. |
1721 | + "\x06_\x00"}, |
1722 | + {bson.M{"_": bson.ObjectId("0123456789ab")}, |
1723 | + "\x07_\x000123456789ab"}, |
1724 | + {bson.M{"_": false}, |
1725 | + "\x08_\x00\x00"}, |
1726 | + {bson.M{"_": true}, |
1727 | + "\x08_\x00\x01"}, |
1728 | + {bson.M{"_": bson.Timestamp(258e6)}, // Note the NS <=> MS conversion. |
1729 | + "\x09_\x00\x02\x01\x00\x00\x00\x00\x00\x00"}, |
1730 | + {bson.M{"_": nil}, |
1731 | + "\x0A_\x00"}, |
1732 | + {bson.M{"_": bson.RegEx{"ab", "cd"}}, |
1733 | + "\x0B_\x00ab\x00cd\x00"}, |
1734 | + {bson.M{"_": bson.JS{"code", nil}}, |
1735 | + "\x0D_\x00\x05\x00\x00\x00code\x00"}, |
1736 | + {bson.M{"_": bson.Symbol("sym")}, |
1737 | + "\x0E_\x00\x04\x00\x00\x00sym\x00"}, |
1738 | + {bson.M{"_": bson.JS{"code", bson.M{"": nil}}}, |
1739 | + "\x0F_\x00\x14\x00\x00\x00\x05\x00\x00\x00code\x00" + |
1740 | + "\x07\x00\x00\x00\x0A\x00\x00"}, |
1741 | + {bson.M{"_": 258}, |
1742 | + "\x10_\x00\x02\x01\x00\x00"}, |
1743 | + {bson.M{"_": bson.MongoTimestamp(258)}, |
1744 | + "\x11_\x00\x02\x01\x00\x00\x00\x00\x00\x00"}, |
1745 | + {bson.M{"_": int64(258)}, |
1746 | + "\x12_\x00\x02\x01\x00\x00\x00\x00\x00\x00"}, |
1747 | + {bson.M{"_": int64(258 << 32)}, |
1748 | + "\x12_\x00\x00\x00\x00\x00\x02\x01\x00\x00"}, |
1749 | + {bson.M{"_": bson.MaxKey}, |
1750 | + "\x7F_\x00"}, |
1751 | + {bson.M{"_": bson.MinKey}, |
1752 | + "\xFF_\x00"}, |
1753 | +} |
1754 | + |
1755 | +func (s *S) TestMarshalAllItems(c *C) { |
1756 | + for i, item := range allItems { |
1757 | + data, err := bson.Marshal(item.obj) |
1758 | + c.Assert(err, IsNil) |
1759 | + c.Assert(string(data), Equals, wrapInDoc(item.data), Bug("Failed on item %d: %#v", i, item)) |
1760 | + } |
1761 | +} |
1762 | + |
1763 | +func (s *S) TestUnmarshalAllItems(c *C) { |
1764 | + for i, item := range allItems { |
1765 | + value := bson.M{} |
1766 | + err := bson.Unmarshal([]byte(wrapInDoc(item.data)), value) |
1767 | + c.Assert(err, IsNil) |
1768 | + c.Assert(value, Equals, item.obj, Bug("Failed on item %d: %#v", i, item)) |
1769 | + } |
1770 | +} |
1771 | + |
1772 | +func (s *S) TestUnmarshalRawAllItems(c *C) { |
1773 | + for i, item := range allItems { |
1774 | + if len(item.data) == 0 { |
1775 | + continue |
1776 | + } |
1777 | + value := item.obj.(bson.M)["_"] |
1778 | + if value == nil { |
1779 | + continue |
1780 | + } |
1781 | + pv := reflect.New(reflect.ValueOf(value).Type()) |
1782 | + raw := bson.Raw{item.data[0], []byte(item.data[3:])} |
1783 | + c.Logf("Unmarshal raw: %#v, %#v", raw, pv.Interface()) |
1784 | + err := raw.Unmarshal(pv.Interface()) |
1785 | + c.Assert(err, IsNil) |
1786 | + c.Assert(pv.Elem().Interface(), Equals, value, Bug("Failed on item %d: %#v", i, item)) |
1787 | + } |
1788 | +} |
1789 | + |
1790 | +func (s *S) TestUnmarshalRawIncompatible(c *C) { |
1791 | + raw := bson.Raw{0x08, []byte{0x01}} // true |
1792 | + err := raw.Unmarshal(&struct{}{}) |
1793 | + c.Assert(err, Matches, `BSON kind 0x08 isn't compatible with type \*struct { }`) |
1794 | +} |
1795 | + |
1796 | +// -------------------------------------------------------------------------- |
1797 | +// Some one way marshaling operations which would unmarshal differently. |
1798 | + |
1799 | +var oneWayMarshalItems = []testItemType{ |
1800 | + // These are being passed as pointers, and will unmarshal as values. |
1801 | + {bson.M{"": &bson.Binary{0x02, []byte("old")}}, |
1802 | + "\x05\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"}, |
1803 | + {bson.M{"": &bson.Binary{0x80, []byte("udef")}}, |
1804 | + "\x05\x00\x04\x00\x00\x00\x80udef"}, |
1805 | + {bson.M{"": &bson.RegEx{"ab", "cd"}}, |
1806 | + "\x0B\x00ab\x00cd\x00"}, |
1807 | + {bson.M{"": &bson.JS{"code", nil}}, |
1808 | + "\x0D\x00\x05\x00\x00\x00code\x00"}, |
1809 | + {bson.M{"": &bson.JS{"code", bson.M{"": nil}}}, |
1810 | + "\x0F\x00\x14\x00\x00\x00\x05\x00\x00\x00code\x00" + |
1811 | + "\x07\x00\x00\x00\x0A\x00\x00"}, |
1812 | + |
1813 | + // There's no float32 type in BSON. Will encode as a float64. |
1814 | + {bson.M{"": float32(5.05)}, |
1815 | + "\x01\x00\x00\x00\x00@33\x14@"}, |
1816 | + |
1817 | + // The array will be unmarshaled as a slice instead. |
1818 | + {bson.M{"": [2]bool{true, false}}, |
1819 | + "\x04\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"}, |
1820 | + |
1821 | + // The typed slice will be unmarshaled as []interface{}. |
1822 | + {bson.M{"": []bool{true, false}}, |
1823 | + "\x04\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"}, |
1824 | + |
1825 | + // Will unmarshal as a []byte. |
1826 | + {bson.M{"": bson.Binary{0x00, []byte("yo")}}, |
1827 | + "\x05\x00\x02\x00\x00\x00\x00yo"}, |
1828 | + |
1829 | + // No way to preserve the type information here. We might encode as a zero |
1830 | + // value, but this would mean that pointer values in structs wouldn't be |
1831 | + // able to correctly distinguish between unset and set to the zero value. |
1832 | + {bson.M{"": (*byte)(nil)}, |
1833 | + "\x0A\x00"}, |
1834 | + |
1835 | + // No int types smaller than int32 in BSON. Could encode this as a char, |
1836 | + // but it would still be ambiguous, take more, and be awkward in Go when |
1837 | + // loaded without typing information. |
1838 | + {bson.M{"": byte(8)}, |
1839 | + "\x10\x00\x08\x00\x00\x00"}, |
1840 | + |
1841 | + // There are no unsigned types in BSON. Will unmarshal as int32 or int64. |
1842 | + {bson.M{"": uint32(258)}, |
1843 | + "\x10\x00\x02\x01\x00\x00"}, |
1844 | + {bson.M{"": uint64(258)}, |
1845 | + "\x12\x00\x02\x01\x00\x00\x00\x00\x00\x00"}, |
1846 | + {bson.M{"": uint64(258 << 32)}, |
1847 | + "\x12\x00\x00\x00\x00\x00\x02\x01\x00\x00"}, |
1848 | + |
1849 | + // This will unmarshal as int. |
1850 | + {bson.M{"": int32(258)}, |
1851 | + "\x10\x00\x02\x01\x00\x00"}, |
1852 | + |
1853 | + // That's a special case. The unsigned value is too large for an int32, |
1854 | + // so an int64 is used instead. |
1855 | + {bson.M{"": uint32(1<<32 - 1)}, |
1856 | + "\x12\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"}, |
1857 | + {bson.M{"": uint(1<<32 - 1)}, |
1858 | + "\x12\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"}, |
1859 | +} |
1860 | + |
1861 | +func (s *S) TestOneWayMarshalItems(c *C) { |
1862 | + for i, item := range oneWayMarshalItems { |
1863 | + data, err := bson.Marshal(item.obj) |
1864 | + c.Assert(err, IsNil) |
1865 | + c.Assert(string(data), Equals, wrapInDoc(item.data), |
1866 | + Bug("Failed on item %d", i)) |
1867 | + } |
1868 | +} |
1869 | + |
1870 | + |
1871 | +// -------------------------------------------------------------------------- |
1872 | +// Two-way tests for user-defined structures using the samples |
1873 | +// from bsonspec.org. |
1874 | + |
1875 | +type specSample1 struct { |
1876 | + Hello string |
1877 | +} |
1878 | + |
1879 | +type specSample2 struct { |
1880 | + BSON []interface{} "BSON" |
1881 | +} |
1882 | + |
1883 | +var structSampleItems = []testItemType{ |
1884 | + {&specSample1{"world"}, |
1885 | + "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00"}, |
1886 | + {&specSample2{[]interface{}{"awesome", float64(5.05), 1986}}, |
1887 | + "1\x00\x00\x00\x04BSON\x00&\x00\x00\x00\x020\x00\x08\x00\x00\x00" + |
1888 | + "awesome\x00\x011\x00333333\x14@\x102\x00\xc2\x07\x00\x00\x00\x00"}, |
1889 | +} |
1890 | + |
1891 | + |
1892 | +func (s *S) TestMarshalStructSampleItems(c *C) { |
1893 | + for i, item := range structSampleItems { |
1894 | + data, err := bson.Marshal(item.obj) |
1895 | + c.Assert(err, IsNil) |
1896 | + c.Assert(string(data), Equals, item.data, |
1897 | + Bug("Failed on item %d", i)) |
1898 | + } |
1899 | +} |
1900 | + |
1901 | +func (s *S) TestUnmarshalStructSampleItems(c *C) { |
1902 | + for _, item := range structSampleItems { |
1903 | + testUnmarshal(c, item.data, item.obj) |
1904 | + } |
1905 | +} |
1906 | + |
1907 | + |
1908 | +// -------------------------------------------------------------------------- |
1909 | +// Generic two-way struct marshaling tests. |
1910 | + |
1911 | +var bytevar = byte(8) |
1912 | +var byteptr = &bytevar |
1913 | + |
1914 | +var structItems = []testItemType{ |
1915 | + {&struct{ Ptr *byte }{nil}, |
1916 | + "\x0Aptr\x00"}, |
1917 | + {&struct{ Ptr *byte }{&bytevar}, |
1918 | + "\x10ptr\x00\x08\x00\x00\x00"}, |
1919 | + {&struct{ Ptr **byte }{&byteptr}, |
1920 | + "\x10ptr\x00\x08\x00\x00\x00"}, |
1921 | + {&struct{ Byte byte }{8}, |
1922 | + "\x10byte\x00\x08\x00\x00\x00"}, |
1923 | + {&struct{ Byte byte }{0}, |
1924 | + "\x10byte\x00\x00\x00\x00\x00"}, |
1925 | + {&struct { |
1926 | + V byte "Tag" |
1927 | + }{8}, |
1928 | + "\x10Tag\x00\x08\x00\x00\x00"}, |
1929 | + {&struct { |
1930 | + V *struct { |
1931 | + Byte byte |
1932 | + } |
1933 | + }{&struct{ Byte byte }{8}}, |
1934 | + "\x03v\x00" + "\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00"}, |
1935 | + {&struct{ priv byte }{}, ""}, |
1936 | + |
1937 | + // The order of the dumped fields should be the same in the struct. |
1938 | + {&struct{ A, C, B, D, F, E *byte }{}, |
1939 | + "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x0Ae\x00"}, |
1940 | + |
1941 | + {&struct{ V bson.Raw }{bson.Raw{0x03, []byte("\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00")}}, |
1942 | + "\x03v\x00" + "\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00"}, |
1943 | + {&struct{ V bson.Raw }{bson.Raw{0x10, []byte("\x00\x00\x00\x00")}}, |
1944 | + "\x10v\x00" + "\x00\x00\x00\x00"}, |
1945 | + |
1946 | + // Byte arrays. |
1947 | + {&struct{ V [2]byte }{[2]byte{'y', 'o'}}, |
1948 | + "\x05v\x00\x02\x00\x00\x00\x00yo"}, |
1949 | +} |
1950 | + |
1951 | + |
1952 | +func (s *S) TestMarshalStructItems(c *C) { |
1953 | + for i, item := range structItems { |
1954 | + data, err := bson.Marshal(item.obj) |
1955 | + c.Assert(err, IsNil) |
1956 | + c.Assert(string(data), Equals, wrapInDoc(item.data), |
1957 | + Bug("Failed on item %d", i)) |
1958 | + } |
1959 | +} |
1960 | + |
1961 | +func (s *S) TestUnmarshalStructItems(c *C) { |
1962 | + for _, item := range structItems { |
1963 | + testUnmarshal(c, wrapInDoc(item.data), item.obj) |
1964 | + } |
1965 | +} |
1966 | + |
1967 | + |
1968 | +// -------------------------------------------------------------------------- |
1969 | +// One-way marshaling tests. |
1970 | + |
1971 | +type dOnIface struct { |
1972 | + D interface{} |
1973 | +} |
1974 | + |
1975 | +var marshalItems = []testItemType{ |
1976 | + // Ordered document dump. Will unmarshal as a dictionary by default. |
1977 | + {bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", nil}, {"f", nil}, {"e", true}}, |
1978 | + "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x08e\x00\x01"}, |
1979 | + {&dOnIface{bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", true}}}, |
1980 | + "\x03d\x00" + wrapInDoc("\x0Aa\x00\x0Ac\x00\x0Ab\x00\x08d\x00\x01")}, |
1981 | + |
1982 | + // Marshalling a Raw document does nothing. |
1983 | + {bson.Raw{0x03, []byte(wrapInDoc("anything"))}, |
1984 | + "anything"}, |
1985 | + {bson.Raw{Data: []byte(wrapInDoc("anything"))}, |
1986 | + "anything"}, |
1987 | +} |
1988 | + |
1989 | +func (s *S) TestMarshalOneWayItems(c *C) { |
1990 | + for _, item := range marshalItems { |
1991 | + data, err := bson.Marshal(item.obj) |
1992 | + c.Assert(err, IsNil) |
1993 | + c.Assert(string(data), Equals, wrapInDoc(item.data)) |
1994 | + } |
1995 | +} |
1996 | + |
1997 | +// -------------------------------------------------------------------------- |
1998 | +// One-way unmarshaling tests. |
1999 | + |
2000 | +var unmarshalItems = []testItemType{ |
2001 | + // Field is private. Should not attempt to unmarshal it. |
2002 | + {&struct{ priv byte }{}, |
2003 | + "\x10priv\x00\x08\x00\x00\x00"}, |
2004 | + |
2005 | + // Wrong casing. Field names are lowercased. |
2006 | + {&struct{ Byte byte }{}, |
2007 | + "\x10Byte\x00\x08\x00\x00\x00"}, |
2008 | + |
2009 | + // Ignore non-existing field. |
2010 | + {&struct{ Byte byte }{9}, |
2011 | + "\x10boot\x00\x08\x00\x00\x00" + "\x10byte\x00\x09\x00\x00\x00"}, |
2012 | + |
2013 | + // Ignore unsuitable types silently. |
2014 | + {map[string]string{"str": "s"}, |
2015 | + "\x02str\x00\x02\x00\x00\x00s\x00" + "\x10int\x00\x01\x00\x00\x00"}, |
2016 | + {map[string][]int{"array": []int{5, 9}}, |
2017 | + "\x04array\x00" + |
2018 | + wrapInDoc("\x100\x00\x05\x00\x00\x00"+ |
2019 | + "\x021\x00\x02\x00\x00\x00s\x00"+ |
2020 | + "\x102\x00\x09\x00\x00\x00")}, |
2021 | + |
2022 | + // Wrong type. Shouldn't init pointer. |
2023 | + {&struct{ Str *byte }{}, |
2024 | + "\x02str\x00\x02\x00\x00\x00s\x00"}, |
2025 | + {&struct{ Str *struct{ Str string } }{}, |
2026 | + "\x02str\x00\x02\x00\x00\x00s\x00"}, |
2027 | + |
2028 | + // Ordered document. |
2029 | + {&struct{ bson.D }{bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", true}}}, |
2030 | + "\x03d\x00" + wrapInDoc("\x0Aa\x00\x0Ac\x00\x0Ab\x00\x08d\x00\x01")}, |
2031 | + |
2032 | + // Raw document. |
2033 | + {&bson.Raw{0x03, []byte(wrapInDoc("\x10byte\x00\x08\x00\x00\x00"))}, |
2034 | + "\x10byte\x00\x08\x00\x00\x00"}, |
2035 | +} |
2036 | + |
2037 | + |
2038 | +func (s *S) TestUnmarshalOneWayItems(c *C) { |
2039 | + for _, item := range unmarshalItems { |
2040 | + testUnmarshal(c, wrapInDoc(item.data), item.obj) |
2041 | + } |
2042 | +} |
2043 | + |
2044 | +func (s *S) TestUnmarshalNilInStruct(c *C) { |
2045 | + // Nil is the default value, so we need to ensure it's indeed being set. |
2046 | + b := byte(1) |
2047 | + v := &struct{ Ptr *byte }{&b} |
2048 | + err := bson.Unmarshal([]byte(wrapInDoc("\x0Aptr\x00")), v) |
2049 | + c.Assert(err, IsNil) |
2050 | + c.Assert(v, Equals, &struct{ Ptr *byte }{nil}) |
2051 | +} |
2052 | + |
2053 | +// -------------------------------------------------------------------------- |
2054 | +// Marshalling error cases. |
2055 | + |
2056 | +type structWithDupKeys struct { |
2057 | + Name byte |
2058 | + Other byte "name" // Tag should precede. |
2059 | +} |
2060 | + |
2061 | +var marshalErrorItems = []testItemType{ |
2062 | + {bson.M{"": uint64(1 << 63)}, |
2063 | + "BSON has no uint64 type, and value is too large to fit correctly in an int64"}, |
2064 | + {bson.M{"": bson.ObjectId("tooshort")}, |
2065 | + "ObjectIDs must be exactly 12 bytes long \\(got 8\\)"}, |
2066 | + {int64(123), |
2067 | + "Can't marshal int64 as a BSON document"}, |
2068 | + {bson.M{"": 1i}, |
2069 | + "Can't marshal complex128 in a BSON document"}, |
2070 | + {&structWithDupKeys{}, |
2071 | + "Duplicated key 'name' in struct bson_test.structWithDupKeys"}, |
2072 | + {bson.Raw{0x0A, []byte{}}, |
2073 | + "Attempted to unmarshal Raw kind 10 as a document"}, |
2074 | +} |
2075 | + |
2076 | +func (s *S) TestMarshalErrorItems(c *C) { |
2077 | + for _, item := range marshalErrorItems { |
2078 | + data, err := bson.Marshal(item.obj) |
2079 | + c.Assert(err, Matches, item.data) |
2080 | + c.Assert(data, IsNil) |
2081 | + } |
2082 | +} |
2083 | + |
2084 | +// -------------------------------------------------------------------------- |
2085 | +// Unmarshalling error cases. |
2086 | + |
2087 | +type unmarshalErrorType struct { |
2088 | + obj interface{} |
2089 | + data string |
2090 | + error string |
2091 | +} |
2092 | + |
2093 | +var unmarshalErrorItems = []unmarshalErrorType{ |
2094 | + // Tag name conflicts with existing parameter. |
2095 | + {&structWithDupKeys{}, |
2096 | + "\x10name\x00\x08\x00\x00\x00", |
2097 | + "Duplicated key 'name' in struct bson_test.structWithDupKeys"}, |
2098 | + |
2099 | + // Non-string map key. |
2100 | + {map[int]interface{}{}, |
2101 | + "\x10name\x00\x08\x00\x00\x00", |
2102 | + "BSON map must have string keys. Got: map\\[int\\] interface \\{ \\}"}, |
2103 | + |
2104 | + {nil, |
2105 | + "\xEEname\x00", |
2106 | + "Unknown element kind \\(0xEE\\)"}, |
2107 | + |
2108 | + {struct{ Name bool }{}, |
2109 | + "\x10name\x00\x08\x00\x00\x00", |
2110 | + "Unmarshal can't deal with struct values. Use a pointer."}, |
2111 | + |
2112 | + {123, |
2113 | + "\x10name\x00\x08\x00\x00\x00", |
2114 | + "Unmarshal needs a map or a pointer to a struct."}, |
2115 | +} |
2116 | + |
2117 | + |
2118 | +func (s *S) TestUnmarshalErrorItems(c *C) { |
2119 | + for _, item := range unmarshalErrorItems { |
2120 | + data := []byte(wrapInDoc(item.data)) |
2121 | + var value interface{} |
2122 | + switch reflect.ValueOf(item.obj).Kind() { |
2123 | + case reflect.Map, reflect.Ptr: |
2124 | + value = makeZeroDoc(item.obj) |
2125 | + case reflect.Invalid: |
2126 | + value = bson.M{} |
2127 | + default: |
2128 | + value = item.obj |
2129 | + } |
2130 | + err := bson.Unmarshal(data, value) |
2131 | + c.Assert(err, Matches, item.error) |
2132 | + } |
2133 | +} |
2134 | + |
2135 | + |
2136 | +type unmarshalRawErrorType struct { |
2137 | + obj interface{} |
2138 | + raw bson.Raw |
2139 | + error string |
2140 | +} |
2141 | + |
2142 | +var unmarshalRawErrorItems = []unmarshalRawErrorType{ |
2143 | + // Tag name conflicts with existing parameter. |
2144 | + {&structWithDupKeys{}, |
2145 | + bson.Raw{0x03, []byte("\x10byte\x00\x08\x00\x00\x00")}, |
2146 | + "Duplicated key 'name' in struct bson_test.structWithDupKeys"}, |
2147 | + |
2148 | + {&struct{}{}, |
2149 | + bson.Raw{0xEE, []byte{}}, |
2150 | + "Unknown element kind \\(0xEE\\)"}, |
2151 | + |
2152 | + {struct{ Name bool }{}, |
2153 | + bson.Raw{0x10, []byte("\x08\x00\x00\x00")}, |
2154 | + "Raw Unmarshal needs a map or a valid pointer."}, |
2155 | + |
2156 | + {123, |
2157 | + bson.Raw{0x10, []byte("\x08\x00\x00\x00")}, |
2158 | + "Raw Unmarshal needs a map or a valid pointer."}, |
2159 | +} |
2160 | + |
2161 | +func (s *S) TestUnmarshalRawErrorItems(c *C) { |
2162 | + for i, item := range unmarshalRawErrorItems { |
2163 | + err := item.raw.Unmarshal(item.obj) |
2164 | + c.Assert(err, Matches, item.error, Bug("Failed on item %d: %#v\n", i, item)) |
2165 | + } |
2166 | +} |
2167 | + |
2168 | +var corruptedData = []string{ |
2169 | + "\x04\x00\x00\x00\x00", // Shorter than minimum |
2170 | + "\x06\x00\x00\x00\x00", // Not enough data |
2171 | + "\x05\x00\x00", // Broken length |
2172 | + "\x05\x00\x00\x00\xff", // Corrupted termination |
2173 | + "\x0A\x00\x00\x00\x0Aooop\x00", // Unfinished C string |
2174 | + |
2175 | + // Array end past end of string (s[2]=0x07 is correct) |
2176 | + wrapInDoc("\x04\x00\x09\x00\x00\x00\x0A\x00\x00"), |
2177 | + |
2178 | + // Array end within string, but past acceptable. |
2179 | + wrapInDoc("\x04\x00\x08\x00\x00\x00\x0A\x00\x00"), |
2180 | + |
2181 | + // Document end within string, but past acceptable. |
2182 | + wrapInDoc("\x03\x00\x08\x00\x00\x00\x0A\x00\x00"), |
2183 | + |
2184 | + // String with corrupted end. |
2185 | + wrapInDoc("\x02\x00\x03\x00\x00\x00yo\xFF"), |
2186 | +} |
2187 | + |
2188 | + |
2189 | +func (s *S) TestUnmarshalMapDocumentTooShort(c *C) { |
2190 | + for _, data := range corruptedData { |
2191 | + err := bson.Unmarshal([]byte(data), bson.M{}) |
2192 | + c.Assert(err, Matches, "Document is corrupted") |
2193 | + |
2194 | + err = bson.Unmarshal([]byte(data), &struct{}{}) |
2195 | + c.Assert(err, Matches, "Document is corrupted") |
2196 | + } |
2197 | +} |
2198 | + |
2199 | + |
2200 | +// -------------------------------------------------------------------------- |
2201 | +// Setter test cases. |
2202 | + |
2203 | +var setterResult = map[string]bool{} |
2204 | + |
2205 | +type typeWithSetter struct { |
2206 | + received interface{} |
2207 | +} |
2208 | + |
2209 | +func (o *typeWithSetter) SetBSON(value interface{}) (ok bool) { |
2210 | + o.received = value |
2211 | + if s, ok := value.(string); ok { |
2212 | + if result, ok := setterResult[s]; ok { |
2213 | + return result |
2214 | + } |
2215 | + } |
2216 | + return true |
2217 | +} |
2218 | + |
2219 | +type docWithSetterField struct { |
2220 | + Field *typeWithSetter "_" |
2221 | +} |
2222 | + |
2223 | +func (s *S) TestUnmarshalAllItemsWithSetter(c *C) { |
2224 | + for _, item := range allItems { |
2225 | + obj := &docWithSetterField{} |
2226 | + err := bson.Unmarshal([]byte(wrapInDoc(item.data)), obj) |
2227 | + c.Assert(err, IsNil) |
2228 | + |
2229 | + if item.data == "" { |
2230 | + // Nothing to unmarshal. Should be untouched. |
2231 | + c.Assert(obj.Field, IsNil) |
2232 | + } else { |
2233 | + expected := item.obj.(bson.M)["_"] |
2234 | + |
2235 | + if m, ok := expected.(bson.M); ok { |
2236 | + // Setter works with a bson.D slice rather than bson.M. |
2237 | + slice := make(bson.D, 0, len(m)) |
2238 | + for key, value := range m { |
2239 | + slice = append(slice, bson.DocElem{key, value}) |
2240 | + } |
2241 | + expected = slice |
2242 | + } |
2243 | + |
2244 | + c.Assert(obj.Field, NotNil, |
2245 | + Bug("Pointer not initialized (%#v)", expected)) |
2246 | + c.Assert(obj.Field.received, Equals, expected) |
2247 | + } |
2248 | + } |
2249 | +} |
2250 | + |
2251 | +func (s *S) TestUnmarshalWholeDocumentWithSetter(c *C) { |
2252 | + obj := &typeWithSetter{} |
2253 | + err := bson.Unmarshal([]byte(sampleItems[0].data), obj) |
2254 | + c.Assert(err, IsNil) |
2255 | + c.Assert(obj.received, Equals, bson.D{{"hello", "world"}}) |
2256 | +} |
2257 | + |
2258 | +func (s *S) TestUnmarshalWithFalseSetterIgnoresValue(c *C) { |
2259 | + setterResult["2"] = false |
2260 | + setterResult["4"] = false |
2261 | + defer func() { |
2262 | + setterResult["2"] = false, false |
2263 | + setterResult["4"] = false, false |
2264 | + }() |
2265 | + |
2266 | + m := map[string]*typeWithSetter{} |
2267 | + data := wrapInDoc("\x02abc\x00\x02\x00\x00\x001\x00" + |
2268 | + "\x02def\x00\x02\x00\x00\x002\x00" + |
2269 | + "\x02ghi\x00\x02\x00\x00\x003\x00" + |
2270 | + "\x02jkl\x00\x02\x00\x00\x004\x00") |
2271 | + err := bson.Unmarshal([]byte(data), m) |
2272 | + c.Assert(err, IsNil) |
2273 | + c.Assert(m["abc"], NotNil) |
2274 | + c.Assert(m["def"], IsNil) |
2275 | + c.Assert(m["ghi"], NotNil) |
2276 | + c.Assert(m["jkl"], IsNil) |
2277 | + |
2278 | + c.Assert(m["abc"].received, Equals, "1") |
2279 | + c.Assert(m["ghi"].received, Equals, "3") |
2280 | +} |
2281 | + |
2282 | +func (s *S) TestDMap(c *C) { |
2283 | + d := bson.D{{"a", 1}, {"b", 2}} |
2284 | + c.Assert(d.Map(), Equals, bson.M{"a": 1, "b": 2}) |
2285 | +} |
2286 | + |
2287 | + |
2288 | +// -------------------------------------------------------------------------- |
2289 | +// Getter test cases. |
2290 | + |
2291 | +type typeWithGetter struct { |
2292 | + result interface{} |
2293 | +} |
2294 | + |
2295 | +func (t *typeWithGetter) GetBSON() interface{} { |
2296 | + return t.result |
2297 | +} |
2298 | + |
2299 | +type docWithGetterField struct { |
2300 | + Field *typeWithGetter "_" |
2301 | +} |
2302 | + |
2303 | +func (s *S) TestMarshalAllItemsWithGetter(c *C) { |
2304 | + for i, item := range allItems { |
2305 | + if item.data == "" { |
2306 | + continue |
2307 | + } |
2308 | + obj := &docWithGetterField{} |
2309 | + obj.Field = &typeWithGetter{item.obj.(bson.M)["_"]} |
2310 | + data, err := bson.Marshal(obj) |
2311 | + c.Assert(err, IsNil) |
2312 | + c.Assert(string(data), Equals, wrapInDoc(item.data), |
2313 | + Bug("Failed on item #%d", i)) |
2314 | + } |
2315 | +} |
2316 | + |
2317 | +func (s *S) TestMarshalWholeDocumentWithGetter(c *C) { |
2318 | + obj := &typeWithGetter{sampleItems[0].obj} |
2319 | + data, err := bson.Marshal(obj) |
2320 | + c.Assert(err, IsNil) |
2321 | + c.Assert(string(data), Equals, sampleItems[0].data) |
2322 | +} |
2323 | + |
2324 | +type intGetter int64 |
2325 | + |
2326 | +func (t intGetter) GetBSON() interface{} { |
2327 | + return int64(t) |
2328 | +} |
2329 | + |
2330 | +type typeWithIntGetter struct { |
2331 | + V intGetter "/s" |
2332 | +} |
2333 | + |
2334 | +func (s *S) TestMarshalShortWithGetter(c *C) { |
2335 | + obj := typeWithIntGetter{42} |
2336 | + data, err := bson.Marshal(obj) |
2337 | + c.Assert(err, IsNil) |
2338 | + m := bson.M{} |
2339 | + err = bson.Unmarshal(data, m) |
2340 | + c.Assert(m["v"], Equals, 42) |
2341 | +} |
2342 | + |
2343 | +// -------------------------------------------------------------------------- |
2344 | +// Cross-type conversion tests. |
2345 | + |
2346 | +type crossTypeItem struct { |
2347 | + obj1 interface{} |
2348 | + obj2 interface{} |
2349 | +} |
2350 | + |
2351 | +type condStr struct { |
2352 | + V string "/c" |
2353 | +} |
2354 | +type condBool struct { |
2355 | + V bool "/c" |
2356 | +} |
2357 | +type condInt struct { |
2358 | + V int "/c" |
2359 | +} |
2360 | +type condUInt struct { |
2361 | + V uint "/c" |
2362 | +} |
2363 | +type condIface struct { |
2364 | + V interface{} "/c" |
2365 | +} |
2366 | +type condPtr struct { |
2367 | + V *bool "/c" |
2368 | +} |
2369 | +type condSlice struct { |
2370 | + V []string "/c" |
2371 | +} |
2372 | +type condMap struct { |
2373 | + V map[string]int "/c" |
2374 | +} |
2375 | +type namedCondStr struct { |
2376 | + V string "myv/c" |
2377 | +} |
2378 | + |
2379 | +type shortInt struct { |
2380 | + V int64 "/s" |
2381 | +} |
2382 | +type shortUint struct { |
2383 | + V uint64 "/s" |
2384 | +} |
2385 | +type shortIface struct { |
2386 | + V interface{} "/s" |
2387 | +} |
2388 | +type shortPtr struct { |
2389 | + V *int64 "/s" |
2390 | +} |
2391 | + |
2392 | +type slashedName struct { |
2393 | + V string "a/b/" |
2394 | +} |
2395 | + |
2396 | +var truevar = true |
2397 | +var falsevar = false |
2398 | + |
2399 | +var int64var = int64(42) |
2400 | +var int64ptr = &int64var |
2401 | +var intvar = int(42) |
2402 | +var intptr = &intvar |
2403 | + |
2404 | +// That's a pretty fun test. It will dump the first item, generate a zero |
2405 | +// value equivalent to the second one, load the dumped data onto it, and then |
2406 | +// verify that the resulting value is deep-equal to the untouched second value. |
2407 | +// Then, it will do the same in the *opposite* direction! |
2408 | +var twoWayCrossItems = []crossTypeItem{ |
2409 | + // int<=>int |
2410 | + {&struct{ I int }{42}, &struct{ I int8 }{42}}, |
2411 | + {&struct{ I int }{42}, &struct{ I int32 }{42}}, |
2412 | + {&struct{ I int }{42}, &struct{ I int64 }{42}}, |
2413 | + {&struct{ I int8 }{42}, &struct{ I int32 }{42}}, |
2414 | + {&struct{ I int8 }{42}, &struct{ I int64 }{42}}, |
2415 | + {&struct{ I int32 }{42}, &struct{ I int64 }{42}}, |
2416 | + |
2417 | + // uint<=>uint |
2418 | + {&struct{ I uint }{42}, &struct{ I uint8 }{42}}, |
2419 | + {&struct{ I uint }{42}, &struct{ I uint32 }{42}}, |
2420 | + {&struct{ I uint }{42}, &struct{ I uint64 }{42}}, |
2421 | + {&struct{ I uint8 }{42}, &struct{ I uint32 }{42}}, |
2422 | + {&struct{ I uint8 }{42}, &struct{ I uint64 }{42}}, |
2423 | + {&struct{ I uint32 }{42}, &struct{ I uint64 }{42}}, |
2424 | + |
2425 | + // float32<=>float64 |
2426 | + {&struct{ I float32 }{42}, &struct{ I float64 }{42}}, |
2427 | + |
2428 | + // int<=>uint |
2429 | + {&struct{ I uint }{42}, &struct{ I int }{42}}, |
2430 | + {&struct{ I uint }{42}, &struct{ I int8 }{42}}, |
2431 | + {&struct{ I uint }{42}, &struct{ I int32 }{42}}, |
2432 | + {&struct{ I uint }{42}, &struct{ I int64 }{42}}, |
2433 | + {&struct{ I uint8 }{42}, &struct{ I int }{42}}, |
2434 | + {&struct{ I uint8 }{42}, &struct{ I int8 }{42}}, |
2435 | + {&struct{ I uint8 }{42}, &struct{ I int32 }{42}}, |
2436 | + {&struct{ I uint8 }{42}, &struct{ I int64 }{42}}, |
2437 | + {&struct{ I uint32 }{42}, &struct{ I int }{42}}, |
2438 | + {&struct{ I uint32 }{42}, &struct{ I int8 }{42}}, |
2439 | + {&struct{ I uint32 }{42}, &struct{ I int32 }{42}}, |
2440 | + {&struct{ I uint32 }{42}, &struct{ I int64 }{42}}, |
2441 | + {&struct{ I uint64 }{42}, &struct{ I int }{42}}, |
2442 | + {&struct{ I uint64 }{42}, &struct{ I int8 }{42}}, |
2443 | + {&struct{ I uint64 }{42}, &struct{ I int32 }{42}}, |
2444 | + {&struct{ I uint64 }{42}, &struct{ I int64 }{42}}, |
2445 | + |
2446 | + // int <=> timestamp. Note the NS <=> MS conversion. |
2447 | + {&struct{ I bson.Timestamp }{42e6}, &struct{ I int64 }{42}}, |
2448 | + {&struct{ I bson.Timestamp }{42e6}, &struct{ I int32 }{42}}, |
2449 | + {&struct{ I bson.Timestamp }{42e6}, &struct{ I int }{42}}, |
2450 | + |
2451 | + // int <=> float |
2452 | + {&struct{ I int }{42}, &struct{ I float64 }{42}}, |
2453 | + |
2454 | + // int <=> bool |
2455 | + {&struct{ I int }{1}, &struct{ I bool }{true}}, |
2456 | + {&struct{ I int }{0}, &struct{ I bool }{false}}, |
2457 | + |
2458 | + // uint <=> float64 |
2459 | + {&struct{ I uint }{42}, &struct{ I float64 }{42}}, |
2460 | + |
2461 | + // uint <=> bool |
2462 | + {&struct{ I uint }{1}, &struct{ I bool }{true}}, |
2463 | + {&struct{ I uint }{0}, &struct{ I bool }{false}}, |
2464 | + |
2465 | + // float64 <=> bool |
2466 | + {&struct{ I float64 }{1}, &struct{ I bool }{true}}, |
2467 | + {&struct{ I float64 }{0}, &struct{ I bool }{false}}, |
2468 | + |
2469 | + // string <=> string and string <=> []byte |
2470 | + {&struct{ S []byte }{[]byte("abc")}, &struct{ S string }{"abc"}}, |
2471 | + {&struct{ S []byte }{[]byte("def")}, &struct{ S bson.Symbol }{"def"}}, |
2472 | + {&struct{ S string }{"ghi"}, &struct{ S bson.Symbol }{"ghi"}}, |
2473 | + |
2474 | + // map <=> struct |
2475 | + {&struct { |
2476 | + A struct { |
2477 | + B, C int |
2478 | + } |
2479 | + }{struct{ B, C int }{1, 2}}, |
2480 | + map[string]map[string]int{"a": map[string]int{"b": 1, "c": 2}}}, |
2481 | + |
2482 | + {&struct{ A bson.Symbol }{"abc"}, map[string]string{"a": "abc"}}, |
2483 | + {&struct{ A bson.Symbol }{"abc"}, map[string][]byte{"a": []byte("abc")}}, |
2484 | + {&struct{ A []byte }{[]byte("abc")}, map[string]string{"a": "abc"}}, |
2485 | + {&struct{ A uint }{42}, map[string]int{"a": 42}}, |
2486 | + {&struct{ A uint }{42}, map[string]float64{"a": 42}}, |
2487 | + {&struct{ A uint }{1}, map[string]bool{"a": true}}, |
2488 | + {&struct{ A int }{42}, map[string]uint{"a": 42}}, |
2489 | + {&struct{ A int }{42}, map[string]float64{"a": 42}}, |
2490 | + {&struct{ A int }{1}, map[string]bool{"a": true}}, |
2491 | + {&struct{ A float64 }{42}, map[string]float32{"a": 42}}, |
2492 | + {&struct{ A float64 }{42}, map[string]int{"a": 42}}, |
2493 | + {&struct{ A float64 }{42}, map[string]uint{"a": 42}}, |
2494 | + {&struct{ A float64 }{1}, map[string]bool{"a": true}}, |
2495 | + {&struct{ A bool }{true}, map[string]int{"a": 1}}, |
2496 | + {&struct{ A bool }{true}, map[string]uint{"a": 1}}, |
2497 | + {&struct{ A bool }{true}, map[string]float64{"a": 1}}, |
2498 | + {&struct{ A **byte }{&byteptr}, map[string]byte{"a": 8}}, |
2499 | + |
2500 | + // Slices |
2501 | + {&struct{ S []int }{[]int{1, 2, 3}}, map[string][]int{"s": []int{1, 2, 3}}}, |
2502 | + {&struct{ S *[]int }{&[]int{1, 2, 3}}, map[string][]int{"s": []int{1, 2, 3}}}, |
2503 | + |
2504 | + // Conditionals |
2505 | + {&condBool{true}, map[string]bool{"v": true}}, |
2506 | + {&condBool{}, map[string]bool{}}, |
2507 | + {&condInt{1}, map[string]int{"v": 1}}, |
2508 | + {&condInt{}, map[string]int{}}, |
2509 | + {&condUInt{1}, map[string]uint{"v": 1}}, |
2510 | + {&condUInt{}, map[string]uint{}}, |
2511 | + {&condStr{"yo"}, map[string]string{"v": "yo"}}, |
2512 | + {&condStr{}, map[string]string{}}, |
2513 | + {&condSlice{[]string{"yo"}}, map[string][]string{"v": []string{"yo"}}}, |
2514 | + {&condSlice{}, map[string][]string{}}, |
2515 | + {&condMap{map[string]int{"k": 1}}, bson.M{"v": bson.M{"k": 1}}}, |
2516 | + {&condMap{map[string]int{}}, map[string][]string{}}, |
2517 | + {&condMap{}, map[string][]string{}}, |
2518 | + {&condIface{"yo"}, map[string]string{"v": "yo"}}, |
2519 | + {&condIface{""}, map[string]string{"v": ""}}, |
2520 | + {&condIface{}, map[string]string{}}, |
2521 | + {&condPtr{&truevar}, map[string]bool{"v": true}}, |
2522 | + {&condPtr{&falsevar}, map[string]bool{"v": false}}, |
2523 | + {&condPtr{}, map[string]string{}}, |
2524 | + |
2525 | + {&namedCondStr{"yo"}, map[string]string{"myv": "yo"}}, |
2526 | + {&namedCondStr{}, map[string]string{}}, |
2527 | + |
2528 | + {&shortInt{1}, map[string]interface{}{"v": 1}}, |
2529 | + {&shortInt{1 << 30}, map[string]interface{}{"v": 1 << 30}}, |
2530 | + {&shortInt{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}}, |
2531 | + {&shortUint{1 << 30}, map[string]interface{}{"v": 1 << 30}}, |
2532 | + {&shortUint{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}}, |
2533 | + {&shortIface{int64(1) << 31}, map[string]interface{}{"v": int64(1 << 31)}}, |
2534 | + {&shortPtr{int64ptr}, map[string]interface{}{"v": intvar}}, |
2535 | + |
2536 | + {&slashedName{"yo"}, map[string]string{"a/b": "yo"}}, |
2537 | +} |
2538 | + |
2539 | +// Same thing, but only one way (obj1 => obj2). |
2540 | +var oneWayCrossItems = []crossTypeItem{ |
2541 | + // map <=> struct |
2542 | + {map[string]interface{}{"a": 1, "b": "2", "c": 3}, |
2543 | + map[string]int{"a": 1, "c": 3}}, |
2544 | + |
2545 | + // Can't decode int into struct. |
2546 | + {bson.M{"a": bson.M{"b": 2}}, &struct{ A bool }{}}, |
2547 | + |
2548 | + // Would get decoded into a int32 too in the opposite direction. |
2549 | + {&shortIface{int64(1) << 30}, map[string]interface{}{"v": 1 << 30}}, |
2550 | +} |
2551 | + |
2552 | +func testCrossPair(c *C, dump interface{}, load interface{}, bug interface{}) { |
2553 | + //c.Logf("") |
2554 | + zero := makeZeroDoc(load) |
2555 | + data, err := bson.Marshal(dump) |
2556 | + c.Assert(err, IsNil, bug) |
2557 | + c.Logf("Data: %#v", string(data)) |
2558 | + err = bson.Unmarshal(data, zero) |
2559 | + c.Assert(err, IsNil, bug) |
2560 | + c.Assert(zero, Equals, load, bug) |
2561 | +} |
2562 | + |
2563 | +func (s *S) TestTwoWayCrossPairs(c *C) { |
2564 | + for i, item := range twoWayCrossItems { |
2565 | + testCrossPair(c, item.obj1, item.obj2, Bug("#%d obj1 => obj2", i)) |
2566 | + testCrossPair(c, item.obj2, item.obj1, Bug("#%d obj1 <= obj2", i)) |
2567 | + } |
2568 | +} |
2569 | + |
2570 | +func (s *S) TestOneWayCrossPairs(c *C) { |
2571 | + for i, item := range oneWayCrossItems { |
2572 | + testCrossPair(c, item.obj1, item.obj2, Bug("#%d obj1 => obj2", i)) |
2573 | + } |
2574 | +} |
2575 | + |
2576 | +// -------------------------------------------------------------------------- |
2577 | +// ObjectId hex representation test. |
2578 | + |
2579 | +func (s *S) TestObjectIdHex(c *C) { |
2580 | + id := bson.ObjectIdHex("4d88e15b60f486e428412dc9") |
2581 | + str := "ObjectIdHex(\"4d88e15b60f486e428412dc9\")" |
2582 | + c.Assert(str, Equals, id.String()) |
2583 | +} |
2584 | + |
2585 | +// -------------------------------------------------------------------------- |
2586 | +// ObjectId parts extraction tests. |
2587 | + |
2588 | +type objectIdParts struct { |
2589 | + id bson.ObjectId |
2590 | + timestamp int32 |
2591 | + machine []byte |
2592 | + pid uint16 |
2593 | + counter int32 |
2594 | +} |
2595 | + |
2596 | +var objectIds = []objectIdParts{ |
2597 | + objectIdParts{ |
2598 | + bson.ObjectIdHex("4d88e15b60f486e428412dc9"), |
2599 | + 1300816219, |
2600 | + []byte{0x60, 0xf4, 0x86}, |
2601 | + 0xe428, |
2602 | + 4271561, |
2603 | + }, |
2604 | + objectIdParts{ |
2605 | + bson.ObjectIdHex("000000000000000000000000"), |
2606 | + 0, |
2607 | + []byte{0x00, 0x00, 0x00}, |
2608 | + 0x0000, |
2609 | + 0, |
2610 | + }, |
2611 | + objectIdParts{ |
2612 | + bson.ObjectIdHex("00000000aabbccddee000001"), |
2613 | + 0, |
2614 | + []byte{0xaa, 0xbb, 0xcc}, |
2615 | + 0xddee, |
2616 | + 1, |
2617 | + }, |
2618 | +} |
2619 | + |
2620 | +func (s *S) TestObjectIdPartsExtraction(c *C) { |
2621 | + for i, v := range objectIds { |
2622 | + c.Assert(v.id.Timestamp(), Equals, v.timestamp, Bug("#%d Wrong timestamp value", i)) |
2623 | + c.Assert(v.id.Machine(), Equals, v.machine, Bug("#%d Wrong machine id value", i)) |
2624 | + c.Assert(v.id.Pid(), Equals, v.pid, Bug("#%d Wrong pid value", i)) |
2625 | + c.Assert(v.id.Counter(), Equals, v.counter, Bug("#%d Wrong counter value", i)) |
2626 | + } |
2627 | +} |
2628 | + |
2629 | +func (s *S) TestNow(c *C) { |
2630 | + before := time.Nanoseconds() |
2631 | + time.Sleep(1e6) |
2632 | + now := bson.Now() |
2633 | + time.Sleep(1e6) |
2634 | + after := time.Nanoseconds() |
2635 | + c.Assert(reflect.TypeOf(now), Equals, reflect.TypeOf(bson.Timestamp(00))) |
2636 | + c.Assert(int64(now) > before && int64(now) < after, Equals, true, Bug("now=%d, before=%d, after=%d", now, before, after)) |
2637 | +} |
2638 | + |
2639 | +// -------------------------------------------------------------------------- |
2640 | +// ObjectId generation tests. |
2641 | + |
2642 | +func (s *S) TestNewObjectId(c *C) { |
2643 | + // Generate 10 ids |
2644 | + ids := make([]bson.ObjectId, 10) |
2645 | + for i := 0; i < 10; i++ { |
2646 | + ids[i] = bson.NewObjectId() |
2647 | + } |
2648 | + for i := 1; i < 10; i++ { |
2649 | + prevId := ids[i-1] |
2650 | + id := ids[i] |
2651 | + // Test for uniqueness among all other 9 generated ids |
2652 | + for j, tid := range ids { |
2653 | + if j != i { |
2654 | + c.Assert(id, Not(Equals), tid, Bug("Generated ObjectId is not unique")) |
2655 | + } |
2656 | + } |
2657 | + // Check that timestamp was incremented and is within 30 seconds of the previous one |
2658 | + td := id.Timestamp() - prevId.Timestamp() |
2659 | + c.Assert((td >= 0 && td <= 30), Equals, true, Bug("Wrong timestamp in generated ObjectId")) |
2660 | + // Check that machine ids are the same |
2661 | + c.Assert(id.Machine(), Equals, prevId.Machine()) |
2662 | + // Check that pids are the same |
2663 | + c.Assert(id.Pid(), Equals, prevId.Pid()) |
2664 | + // Test for proper increment |
2665 | + delta := int(id.Counter() - prevId.Counter()) |
2666 | + c.Assert(delta, Equals, 1, Bug("Wrong increment in generated ObjectId")) |
2667 | + } |
2668 | +} |
2669 | + |
2670 | +func (s *S) TestNewObjectIdSeconds(c *C) { |
2671 | + sec := int32(time.Seconds()) |
2672 | + id := bson.NewObjectIdSeconds(sec) |
2673 | + c.Assert(id.Timestamp(), Equals, sec) |
2674 | + c.Assert(id.Machine(), Equals, []byte{0x00, 0x00, 0x00}) |
2675 | + c.Assert(int(id.Pid()), Equals, 0) |
2676 | + c.Assert(int(id.Counter()), Equals, 0) |
2677 | +} |
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.