github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/third_party/labix.org/v2/mgo/bson/bson.go (about) 1 // BSON library for Go 2 // 3 // Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net> 4 // 5 // All rights reserved. 6 // 7 // Redistribution and use in source and binary forms, with or without 8 // modification, are permitted provided that the following conditions are met: 9 // 10 // 1. Redistributions of source code must retain the above copyright notice, this 11 // list of conditions and the following disclaimer. 12 // 2. Redistributions in binary form must reproduce the above copyright notice, 13 // this list of conditions and the following disclaimer in the documentation 14 // and/or other materials provided with the distribution. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27 // Package bson is an implementation of the BSON specification for Go: 28 // 29 // http://bsonspec.org 30 // 31 // It was created as part of the mgo MongoDB driver for Go, but is standalone 32 // and may be used on its own without the driver. 33 package bson 34 35 import ( 36 "crypto/md5" 37 "crypto/rand" 38 "encoding/binary" 39 "encoding/hex" 40 "errors" 41 "fmt" 42 "io" 43 "os" 44 "reflect" 45 "runtime" 46 "strings" 47 "sync" 48 "sync/atomic" 49 "time" 50 ) 51 52 // -------------------------------------------------------------------------- 53 // The public API. 54 55 // A value implementing the bson.Getter interface will have its GetBSON 56 // method called when the given value has to be marshalled, and the result 57 // of this method will be marshaled in place of the actual object. 58 // 59 // If GetBSON returns return a non-nil error, the marshalling procedure 60 // will stop and error out with the provided value. 61 type Getter interface { 62 GetBSON() (interface{}, error) 63 } 64 65 // A value implementing the bson.Setter interface will receive the BSON 66 // value via the SetBSON method during unmarshaling, and the object 67 // itself will not be changed as usual. 68 // 69 // If setting the value works, the method should return nil or alternatively 70 // bson.SetZero to set the respective field to its zero value (nil for 71 // pointer types). If SetBSON returns a value of type bson.TypeError, the 72 // BSON value will be omitted from a map or slice being decoded and the 73 // unmarshalling will continue. If it returns any other non-nil error, the 74 // unmarshalling procedure will stop and error out with the provided value. 75 // 76 // This interface is generally useful in pointer receivers, since the method 77 // will want to change the receiver. A type field that implements the Setter 78 // interface doesn't have to be a pointer, though. 79 // 80 // Unlike the usual behavior, unmarshalling onto a value that implements a 81 // Setter interface will NOT reset the value to its zero state. This allows 82 // the value to decide by itself how to be unmarshalled. 83 // 84 // For example: 85 // 86 // type MyString string 87 // 88 // func (s *MyString) SetBSON(raw bson.Raw) error { 89 // return raw.Unmarshal(s) 90 // } 91 // 92 type Setter interface { 93 SetBSON(raw Raw) error 94 } 95 96 // SetZero may be returned from a SetBSON method to have the value set to 97 // its respective zero value. When used in pointer values, this will set the 98 // field to nil rather than to the pre-allocated value. 99 var SetZero = errors.New("set to zero") 100 101 // M is a convenient alias for a map[string]interface{} map, useful for 102 // dealing with BSON in a native way. For instance: 103 // 104 // bson.M{"a": 1, "b": true} 105 // 106 // There's no special handling for this type in addition to what's done anyway 107 // for an equivalent map type. Elements in the map will be dumped in an 108 // undefined ordered. See also the bson.D type for an ordered alternative. 109 type M map[string]interface{} 110 111 // D represents a BSON document containing ordered elements. For example: 112 // 113 // bson.D{{"a", 1}, {"b", true}} 114 // 115 // In some situations, such as when creating indexes for MongoDB, the order in 116 // which the elements are defined is important. If the order is not important, 117 // using a map is generally more comfortable. See bson.M and bson.RawD. 118 type D []DocElem 119 120 // See the D type. 121 type DocElem struct { 122 Name string 123 Value interface{} 124 } 125 126 // Map returns a map out of the ordered element name/value pairs in d. 127 func (d D) Map() (m M) { 128 m = make(M, len(d)) 129 for _, item := range d { 130 m[item.Name] = item.Value 131 } 132 return m 133 } 134 135 // The Raw type represents raw unprocessed BSON documents and elements. 136 // Kind is the kind of element as defined per the BSON specification, and 137 // Data is the raw unprocessed data for the respective element. 138 // Using this type it is possible to unmarshal or marshal values partially. 139 // 140 // Relevant documentation: 141 // 142 // http://bsonspec.org/#/specification 143 // 144 type Raw struct { 145 Kind byte 146 Data []byte 147 } 148 149 // RawD represents a BSON document containing raw unprocessed elements. 150 // This low-level representation may be useful when lazily processing 151 // documents of uncertain content, or when manipulating the raw content 152 // documents in general. 153 type RawD []RawDocElem 154 155 // See the RawD type. 156 type RawDocElem struct { 157 Name string 158 Value Raw 159 } 160 161 // ObjectId is a unique ID identifying a BSON value. It must be exactly 12 bytes 162 // long. MongoDB objects by default have such a property set in their "_id" 163 // property. 164 // 165 // http://www.mongodb.org/display/DOCS/Object+IDs 166 type ObjectId string 167 168 // ObjectIdHex returns an ObjectId from the provided hex representation. 169 // Calling this function with an invalid hex representation will 170 // cause a runtime panic. See the IsObjectIdHex function. 171 func ObjectIdHex(s string) ObjectId { 172 d, err := hex.DecodeString(s) 173 if err != nil || len(d) != 12 { 174 panic(fmt.Sprintf("Invalid input to ObjectIdHex: %q", s)) 175 } 176 return ObjectId(d) 177 } 178 179 // IsObjectIdHex returns whether s is a valid hex representation of 180 // an ObjectId. See the ObjectIdHex function. 181 func IsObjectIdHex(s string) bool { 182 if len(s) != 24 { 183 return false 184 } 185 _, err := hex.DecodeString(s) 186 return err == nil 187 } 188 189 // objectIdCounter is atomically incremented when generating a new ObjectId 190 // using NewObjectId() function. It's used as a counter part of an id. 191 var objectIdCounter uint32 = 0 192 193 // machineId stores machine id generated once and used in subsequent calls 194 // to NewObjectId function. 195 var machineId = readMachineId() 196 197 // initMachineId generates machine id and puts it into the machineId global 198 // variable. If this function fails to get the hostname, it will cause 199 // a runtime error. 200 func readMachineId() []byte { 201 var sum [3]byte 202 id := sum[:] 203 hostname, err1 := os.Hostname() 204 if err1 != nil { 205 _, err2 := io.ReadFull(rand.Reader, id) 206 if err2 != nil { 207 panic(fmt.Errorf("cannot get hostname: %v; %v", err1, err2)) 208 } 209 return id 210 } 211 hw := md5.New() 212 hw.Write([]byte(hostname)) 213 copy(id, hw.Sum(nil)) 214 return id 215 } 216 217 // NewObjectId returns a new unique ObjectId. 218 // This function causes a runtime error if it fails to get the hostname 219 // of the current machine. 220 func NewObjectId() ObjectId { 221 var b [12]byte 222 // Timestamp, 4 bytes, big endian 223 binary.BigEndian.PutUint32(b[:], uint32(time.Now().Unix())) 224 // Machine, first 3 bytes of md5(hostname) 225 b[4] = machineId[0] 226 b[5] = machineId[1] 227 b[6] = machineId[2] 228 // Pid, 2 bytes, specs don't specify endianness, but we use big endian. 229 pid := os.Getpid() 230 b[7] = byte(pid >> 8) 231 b[8] = byte(pid) 232 // Increment, 3 bytes, big endian 233 i := atomic.AddUint32(&objectIdCounter, 1) 234 b[9] = byte(i >> 16) 235 b[10] = byte(i >> 8) 236 b[11] = byte(i) 237 return ObjectId(b[:]) 238 } 239 240 // NewObjectIdWithTime returns a dummy ObjectId with the timestamp part filled 241 // with the provided number of seconds from epoch UTC, and all other parts 242 // filled with zeroes. It's not safe to insert a document with an id generated 243 // by this method, it is useful only for queries to find documents with ids 244 // generated before or after the specified timestamp. 245 func NewObjectIdWithTime(t time.Time) ObjectId { 246 var b [12]byte 247 binary.BigEndian.PutUint32(b[:4], uint32(t.Unix())) 248 return ObjectId(string(b[:])) 249 } 250 251 // String returns a hex string representation of the id. 252 // Example: ObjectIdHex("4d88e15b60f486e428412dc9"). 253 func (id ObjectId) String() string { 254 return fmt.Sprintf(`ObjectIdHex("%x")`, string(id)) 255 } 256 257 // Hex returns a hex representation of the ObjectId. 258 func (id ObjectId) Hex() string { 259 return hex.EncodeToString([]byte(id)) 260 } 261 262 // MarshalJSON turns a bson.ObjectId into a json.Marshaller. 263 func (id ObjectId) MarshalJSON() ([]byte, error) { 264 return []byte(fmt.Sprintf(`"%x"`, string(id))), nil 265 } 266 267 // UnmarshalJSON turns *bson.ObjectId into a json.Unmarshaller. 268 func (id *ObjectId) UnmarshalJSON(data []byte) error { 269 if len(data) != 26 || data[0] != '"' || data[25] != '"' { 270 return errors.New(fmt.Sprintf("Invalid ObjectId in JSON: %s", string(data))) 271 } 272 var buf [12]byte 273 _, err := hex.Decode(buf[:], data[1:25]) 274 if err != nil { 275 return errors.New(fmt.Sprintf("Invalid ObjectId in JSON: %s (%s)", string(data), err)) 276 } 277 *id = ObjectId(string(buf[:])) 278 return nil 279 } 280 281 // Valid returns true if id is valid. A valid id must contain exactly 12 bytes. 282 func (id ObjectId) Valid() bool { 283 return len(id) == 12 284 } 285 286 // byteSlice returns byte slice of id from start to end. 287 // Calling this function with an invalid id will cause a runtime panic. 288 func (id ObjectId) byteSlice(start, end int) []byte { 289 if len(id) != 12 { 290 panic(fmt.Sprintf("Invalid ObjectId: %q", string(id))) 291 } 292 return []byte(string(id)[start:end]) 293 } 294 295 // Time returns the timestamp part of the id. 296 // It's a runtime error to call this method with an invalid id. 297 func (id ObjectId) Time() time.Time { 298 // First 4 bytes of ObjectId is 32-bit big-endian seconds from epoch. 299 secs := int64(binary.BigEndian.Uint32(id.byteSlice(0, 4))) 300 return time.Unix(secs, 0) 301 } 302 303 // Machine returns the 3-byte machine id part of the id. 304 // It's a runtime error to call this method with an invalid id. 305 func (id ObjectId) Machine() []byte { 306 return id.byteSlice(4, 7) 307 } 308 309 // Pid returns the process id part of the id. 310 // It's a runtime error to call this method with an invalid id. 311 func (id ObjectId) Pid() uint16 { 312 return binary.BigEndian.Uint16(id.byteSlice(7, 9)) 313 } 314 315 // Counter returns the incrementing value part of the id. 316 // It's a runtime error to call this method with an invalid id. 317 func (id ObjectId) Counter() int32 { 318 b := id.byteSlice(9, 12) 319 // Counter is stored as big-endian 3-byte value 320 return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])) 321 } 322 323 // The Symbol type is similar to a string and is used in languages with a 324 // distinct symbol type. 325 type Symbol string 326 327 // Now returns the current time with millisecond precision. MongoDB stores 328 // timestamps with the same precision, so a Time returned from this method 329 // will not change after a roundtrip to the database. That's the only reason 330 // why this function exists. Using the time.Now function also works fine 331 // otherwise. 332 func Now() time.Time { 333 return time.Unix(0, time.Now().UnixNano()/1e6*1e6) 334 } 335 336 // MongoTimestamp is a special internal type used by MongoDB that for some 337 // strange reason has its own datatype defined in BSON. 338 type MongoTimestamp int64 339 340 type orderKey int64 341 342 // MaxKey is a special value that compares higher than all other possible BSON 343 // values in a MongoDB database. 344 var MaxKey = orderKey(1<<63 - 1) 345 346 // MinKey is a special value that compares lower than all other possible BSON 347 // values in a MongoDB database. 348 var MinKey = orderKey(-1 << 63) 349 350 type undefined struct{} 351 352 // Undefined represents the undefined BSON value. 353 var Undefined undefined 354 355 // Binary is a representation for non-standard binary values. Any kind should 356 // work, but the following are known as of this writing: 357 // 358 // 0x00 - Generic. This is decoded as []byte(data), not Binary{0x00, data}. 359 // 0x01 - Function (!?) 360 // 0x02 - Obsolete generic. 361 // 0x03 - UUID 362 // 0x05 - MD5 363 // 0x80 - User defined. 364 // 365 type Binary struct { 366 Kind byte 367 Data []byte 368 } 369 370 // RegEx represents a regular expression. The Options field may contain 371 // individual characters defining the way in which the pattern should be 372 // applied, and must be sorted. Valid options as of this writing are 'i' for 373 // case insensitive matching, 'm' for multi-line matching, 'x' for verbose 374 // mode, 'l' to make \w, \W, and similar be locale-dependent, 's' for dot-all 375 // mode (a '.' matches everything), and 'u' to make \w, \W, and similar match 376 // unicode. The value of the Options parameter is not verified before being 377 // marshaled into the BSON format. 378 type RegEx struct { 379 Pattern string 380 Options string 381 } 382 383 // JavaScript is a type that holds JavaScript code. If Scope is non-nil, it 384 // will be marshaled as a mapping from identifiers to values that may be 385 // used when evaluating the provided Code. 386 type JavaScript struct { 387 Code string 388 Scope interface{} 389 } 390 391 const initialBufferSize = 64 392 393 func handleErr(err *error) { 394 if r := recover(); r != nil { 395 if _, ok := r.(runtime.Error); ok { 396 panic(r) 397 } else if _, ok := r.(externalPanic); ok { 398 panic(r) 399 } else if s, ok := r.(string); ok { 400 *err = errors.New(s) 401 } else if e, ok := r.(error); ok { 402 *err = e 403 } else { 404 panic(r) 405 } 406 } 407 } 408 409 // Marshal serializes the in value, which may be a map or a struct value. 410 // In the case of struct values, only exported fields will be serialized. 411 // The lowercased field name is used as the key for each exported field, 412 // but this behavior may be changed using the respective field tag. 413 // The tag may also contain flags to tweak the marshalling behavior for 414 // the field. The tag formats accepted are: 415 // 416 // "[<key>][,<flag1>[,<flag2>]]" 417 // 418 // `(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)` 419 // 420 // The following flags are currently supported: 421 // 422 // omitempty Only include the field if it's not set to the zero 423 // value for the type or to empty slices or maps. 424 // 425 // minsize Marshal an int64 value as an int32, if that's feasible 426 // while preserving the numeric value. 427 // 428 // inline Inline the field, which must be a struct or a map, 429 // causing all of its fields or keys to be processed as if 430 // they were part of the outer struct. For maps, keys must 431 // not conflict with the bson keys of other struct fields. 432 // 433 // Some examples: 434 // 435 // type T struct { 436 // A bool 437 // B int "myb" 438 // C string "myc,omitempty" 439 // D string `bson:",omitempty" json:"jsonkey"` 440 // E int64 ",minsize" 441 // F int64 "myf,omitempty,minsize" 442 // } 443 // 444 func Marshal(in interface{}) (out []byte, err error) { 445 defer handleErr(&err) 446 e := &encoder{make([]byte, 0, initialBufferSize)} 447 e.addDoc(reflect.ValueOf(in)) 448 return e.out, nil 449 } 450 451 // Unmarshal deserializes data from in into the out value. The out value 452 // must be a map, a pointer to a struct, or a pointer to a bson.D value. 453 // The lowercased field name is used as the key for each exported field, 454 // but this behavior may be changed using the respective field tag. 455 // The tag may also contain flags to tweak the marshalling behavior for 456 // the field. The tag formats accepted are: 457 // 458 // "[<key>][,<flag1>[,<flag2>]]" 459 // 460 // `(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)` 461 // 462 // The following flags are currently supported during unmarshal (see the 463 // Marshal method for other flags): 464 // 465 // inline Inline the field, which must be a struct or a map. 466 // Inlined structs are handled as if its fields were part 467 // of the outer struct. An inlined map causes keys that do 468 // not match any other struct field to be inserted in the 469 // map rather than being discarded as usual. 470 // 471 // The target field or element types of out may not necessarily match 472 // the BSON values of the provided data. The following conversions are 473 // made automatically: 474 // 475 // - Numeric types are converted if at least the integer part of the 476 // value would be preserved correctly 477 // - Bools are converted to numeric types as 1 or 0 478 // - Numeric types are converted to bools as true if not 0 or false otherwise 479 // - Binary and string BSON data is converted to a string, array or byte slice 480 // 481 // If the value would not fit the type and cannot be converted, it's 482 // silently skipped. 483 // 484 // Pointer values are initialized when necessary. 485 func Unmarshal(in []byte, out interface{}) (err error) { 486 defer handleErr(&err) 487 v := reflect.ValueOf(out) 488 switch v.Kind() { 489 case reflect.Map, reflect.Ptr: 490 d := newDecoder(in) 491 d.readDocTo(v) 492 case reflect.Struct: 493 return errors.New("Unmarshal can't deal with struct values. Use a pointer.") 494 default: 495 return errors.New("Unmarshal needs a map or a pointer to a struct.") 496 } 497 return nil 498 } 499 500 // Unmarshal deserializes raw into the out value. If the out value type 501 // is not compatible with raw, a *bson.TypeError is returned. 502 // 503 // See the Unmarshal function documentation for more details on the 504 // unmarshalling process. 505 func (raw Raw) Unmarshal(out interface{}) (err error) { 506 defer handleErr(&err) 507 v := reflect.ValueOf(out) 508 switch v.Kind() { 509 case reflect.Ptr: 510 v = v.Elem() 511 fallthrough 512 case reflect.Map: 513 d := newDecoder(raw.Data) 514 good := d.readElemTo(v, raw.Kind) 515 if !good { 516 return &TypeError{v.Type(), raw.Kind} 517 } 518 case reflect.Struct: 519 return errors.New("Raw Unmarshal can't deal with struct values. Use a pointer.") 520 default: 521 return errors.New("Raw Unmarshal needs a map or a valid pointer.") 522 } 523 return nil 524 } 525 526 type TypeError struct { 527 Type reflect.Type 528 Kind byte 529 } 530 531 func (e *TypeError) Error() string { 532 return fmt.Sprintf("BSON kind 0x%02x isn't compatible with type %s", e.Kind, e.Type.String()) 533 } 534 535 // -------------------------------------------------------------------------- 536 // Maintain a mapping of keys to structure field indexes 537 538 type structInfo struct { 539 FieldsMap map[string]fieldInfo 540 FieldsList []fieldInfo 541 InlineMap int 542 Zero reflect.Value 543 } 544 545 type fieldInfo struct { 546 Key string 547 Num int 548 OmitEmpty bool 549 MinSize bool 550 Inline []int 551 } 552 553 var structMap = make(map[reflect.Type]*structInfo) 554 var structMapMutex sync.RWMutex 555 556 type externalPanic string 557 558 func (e externalPanic) String() string { 559 return string(e) 560 } 561 562 func getStructInfo(st reflect.Type) (*structInfo, error) { 563 structMapMutex.RLock() 564 sinfo, found := structMap[st] 565 structMapMutex.RUnlock() 566 if found { 567 return sinfo, nil 568 } 569 n := st.NumField() 570 fieldsMap := make(map[string]fieldInfo) 571 fieldsList := make([]fieldInfo, 0, n) 572 inlineMap := -1 573 for i := 0; i != n; i++ { 574 field := st.Field(i) 575 if field.PkgPath != "" { 576 continue // Private field 577 } 578 579 info := fieldInfo{Num: i} 580 581 tag := field.Tag.Get("bson") 582 if tag == "" && strings.Index(string(field.Tag), ":") < 0 { 583 tag = string(field.Tag) 584 } 585 if tag == "-" { 586 continue 587 } 588 589 // XXX Drop this after a few releases. 590 if s := strings.Index(tag, "/"); s >= 0 { 591 recommend := tag[:s] 592 for _, c := range tag[s+1:] { 593 switch c { 594 case 'c': 595 recommend += ",omitempty" 596 case 's': 597 recommend += ",minsize" 598 default: 599 msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", string([]byte{uint8(c)}), tag, st) 600 panic(externalPanic(msg)) 601 } 602 } 603 msg := fmt.Sprintf("Replace tag %q in field %s of type %s by %q", tag, field.Name, st, recommend) 604 panic(externalPanic(msg)) 605 } 606 607 inline := false 608 fields := strings.Split(tag, ",") 609 if len(fields) > 1 { 610 for _, flag := range fields[1:] { 611 switch flag { 612 case "omitempty": 613 info.OmitEmpty = true 614 case "minsize": 615 info.MinSize = true 616 case "inline": 617 inline = true 618 default: 619 msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st) 620 panic(externalPanic(msg)) 621 } 622 } 623 tag = fields[0] 624 } 625 626 if inline { 627 switch field.Type.Kind() { 628 case reflect.Map: 629 if inlineMap >= 0 { 630 return nil, errors.New("Multiple ,inline maps in struct " + st.String()) 631 } 632 if field.Type.Key() != reflect.TypeOf("") { 633 return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) 634 } 635 inlineMap = info.Num 636 case reflect.Struct: 637 sinfo, err := getStructInfo(field.Type) 638 if err != nil { 639 return nil, err 640 } 641 for _, finfo := range sinfo.FieldsList { 642 if _, found := fieldsMap[finfo.Key]; found { 643 msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() 644 return nil, errors.New(msg) 645 } 646 if finfo.Inline == nil { 647 finfo.Inline = []int{i, finfo.Num} 648 } else { 649 finfo.Inline = append([]int{i}, finfo.Inline...) 650 } 651 fieldsMap[finfo.Key] = finfo 652 fieldsList = append(fieldsList, finfo) 653 } 654 default: 655 panic("Option ,inline needs a struct value or map field") 656 } 657 continue 658 } 659 660 if tag != "" { 661 info.Key = tag 662 } else { 663 info.Key = strings.ToLower(field.Name) 664 } 665 666 if _, found = fieldsMap[info.Key]; found { 667 msg := "Duplicated key '" + info.Key + "' in struct " + st.String() 668 return nil, errors.New(msg) 669 } 670 671 fieldsList = append(fieldsList, info) 672 fieldsMap[info.Key] = info 673 } 674 sinfo = &structInfo{ 675 fieldsMap, 676 fieldsList, 677 inlineMap, 678 reflect.New(st).Elem(), 679 } 680 structMapMutex.Lock() 681 structMap[st] = sinfo 682 structMapMutex.Unlock() 683 return sinfo, nil 684 }