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