package ttlv import ( "bytes" "encoding/binary" "errors" "fmt" "io" "math" "math/big" "reflect" "strings" "time" "github.com/ansel1/merry" ) const structFieldTag = "ttlv" var ( ErrIntOverflow = fmt.Errorf("value exceeds max int value %d", math.MaxInt32) ErrUnsupportedEnumTypeError = errors.New("unsupported type for enums, must be string, or int types") ErrUnsupportedTypeError = errors.New("marshaling/unmarshaling is not supported for this type") ErrNoTag = errors.New("unable to determine tag for field") ErrTagConflict = errors.New("tag conflict") ) // Marshal encodes a golang value into a KMIP value. // // An error will be returned if v is an invalid pointer. // // Currently, Marshal does not support anonymous fields. // Private fields are ignored. // // Marshal maps the golang value to a KMIP tag, type, and value // encoding. To determine the KMIP tag, Marshal uses the same rules // as Unmarshal. // // The appropriate type and encoding are inferred from the golang type // and from the inferred KMIP tag, according to these rules: // // 1. If the value is a TTLV, it is copied byte for byte // // 2. If the value implements Marshaler, call that // // 3. If the struct field has an "omitempty" flag, and the value is // zero, skip the field: // // type Foo struct { // Comment string `ttlv:,omitempty` // } // // 4. If the value is a slice (except []byte) or array, marshal all // values concatenated // // 5. If a tag has not been inferred at this point, return *MarshalerError with // cause ErrNoTag // // 6. If the Tag is registered as an enum, or has the "enum" struct tag flag, attempt // to marshal as an Enumeration. int, int8, int16, int32, and their uint counterparts // can be marshaled as an Enumeration. A string can be marshaled to an Enumeration // if the string contains a number, a 4 byte (8 char) hex string with the prefix "0x", // or the normalized name of an enum value registered to this tag. Examples: // // type Foo struct { // CancellationResult string // will encode as an Enumeration because // // the tag CancellationResult is registered // // as an enum. // C int `ttlv:"Comment,enum" // The tag Comment is not registered as an enum // // but the enum flag will force this to encode // // as an enumeration. // } // // If the string can't be interpreted as an enum value, it will be encoded as a TextString. If // the "enum" struct flag is set, the value *must* successfully encode to an Enumeration using // above rules, or an error is returned. // // 7. If the Tag is registered as a bitmask, or has the "bitmask" struct tag flag, attempt // to marshal to an Integer, following the same rules as for Enumerations. The ParseInt() // function is used to parse string values. // // 9. time.Time marshals to DateTime. If the field has the "datetimeextended" struct flag, // marshal as DateTimeExtended. Example: // // type Foo struct { // ActivationDate time.Time `ttlv:",datetimeextended"` // } // // 10. big.Int marshals to BigInteger // // 11. time.Duration marshals to Interval // // 12. string marshals to TextString // // 13. []byte marshals to ByteString // // 14. all int and uint variants except int64 and uint64 marshal to Integer. If the golang // value overflows the KMIP value, *MarshalerError with cause ErrIntOverflow is returned // // 15. int64 and uint64 marshal to LongInteger // // 16. bool marshals to Boolean // // 17. structs marshal to Structure. Each field of the struct will be marshaled into the // values of the Structure according to the above rules. // // Any other golang type will return *MarshalerError with cause ErrUnsupportedTypeError. func Marshal(v interface{}) (TTLV, error) { buf := bytes.NewBuffer(nil) err := NewEncoder(buf).Encode(v) if err != nil { return nil, err } return buf.Bytes(), nil } // Marshaler knows how to encode itself to TTLV. // The implementation should use the primitive methods of the encoder, // such as EncodeInteger(), etc. // // The tag inferred by the Encoder from the field or type information is // passed as an argument, but the implementation can choose to ignore it. type Marshaler interface { MarshalTTLV(e *Encoder, tag Tag) error } func NewEncoder(w io.Writer) *Encoder { return &Encoder{w: w} } // Encode a single value and flush to the writer. The tag will be inferred from // the value. If no tag can be inferred, an error is returned. // See Marshal for encoding rules. func (e *Encoder) Encode(v interface{}) error { return e.EncodeValue(TagNone, v) } // EncodeValue encodes a single value with the given tag and flushes it // to the writer. // See Marshal for encoding rules. func (e *Encoder) EncodeValue(tag Tag, v interface{}) error { err := e.encode(tag, reflect.ValueOf(v), nil) if err != nil { return err } return e.Flush() } // EncodeStructure encodes a Structure with the given tag to the writer. // The function argument should encode the enclosed values inside the Structure. // Call Flush() to write the data to the writer. func (e *Encoder) EncodeStructure(tag Tag, f func(e *Encoder) error) error { e.encodeDepth++ i := e.encBuf.begin(tag, TypeStructure) err := f(e) e.encBuf.end(i) e.encodeDepth-- return err } // EncodeEnumeration, along with the other Encode methods, encodes a // single KMIP value with the given tag to an internal buffer. These methods // do not flush the data to the writer: call Flush() to flush the buffer. func (e *Encoder) EncodeEnumeration(tag Tag, v uint32) { e.encBuf.encodeEnum(tag, v) } func (e *Encoder) EncodeInteger(tag Tag, v int32) { e.encBuf.encodeInt(tag, v) } func (e *Encoder) EncodeLongInteger(tag Tag, v int64) { e.encBuf.encodeLongInt(tag, v) } func (e *Encoder) EncodeInterval(tag Tag, v time.Duration) { e.encBuf.encodeInterval(tag, v) } func (e *Encoder) EncodeDateTime(tag Tag, v time.Time) { e.encBuf.encodeDateTime(tag, v) } func (e *Encoder) EncodeDateTimeExtended(tag Tag, v time.Time) { e.encBuf.encodeDateTimeExtended(tag, v) } func (e *Encoder) EncodeBigInteger(tag Tag, v *big.Int) { e.encBuf.encodeBigInt(tag, v) } func (e *Encoder) EncodeBoolean(tag Tag, v bool) { e.encBuf.encodeBool(tag, v) } func (e *Encoder) EncodeTextString(tag Tag, v string) { e.encBuf.encodeTextString(tag, v) } func (e *Encoder) EncodeByteString(tag Tag, v []byte) { e.encBuf.encodeByteString(tag, v) } // Flush flushes the internal encoding buffer to the writer. func (e *Encoder) Flush() error { if e.encodeDepth > 0 { return nil } _, err := e.encBuf.WriteTo(e.w) e.encBuf.Reset() return err } type MarshalerError struct { // Type is the golang type of the value being marshaled Type reflect.Type // Struct is the name of the enclosing struct if the marshaled value is a field. Struct string // Field is the name of the field being marshaled Field string Tag Tag } func (e *MarshalerError) Error() string { msg := "kmip: error marshaling value" if e.Type != nil { msg += " of type " + e.Type.String() } if e.Struct != "" { msg += " in struct field " + e.Struct + "." + e.Field } return msg } func (e *Encoder) marshalingError(tag Tag, t reflect.Type, cause error) merry.Error { err := &MarshalerError{ Type: t, Struct: e.currStruct, Field: e.currField, Tag: tag, } return merry.WrapSkipping(err, 1).WithCause(cause) } var ( byteType = reflect.TypeOf(byte(0)) marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem() timeType = reflect.TypeOf((*time.Time)(nil)).Elem() bigIntPtrType = reflect.TypeOf((*big.Int)(nil)) bigIntType = bigIntPtrType.Elem() durationType = reflect.TypeOf(time.Nanosecond) ttlvType = reflect.TypeOf((*TTLV)(nil)).Elem() tagType = reflect.TypeOf(Tag(0)) ) var invalidValue = reflect.Value{} // indirect dives into interfaces values, and one level deep into pointers // returns an invalid value if the resolved value is nil or invalid. func indirect(v reflect.Value) reflect.Value { if !v.IsValid() { return v } if v.Kind() == reflect.Interface { v = v.Elem() } if !v.IsValid() { return v } if v.Kind() == reflect.Ptr { v = v.Elem() } switch v.Kind() { case reflect.Func, reflect.Slice, reflect.Map, reflect.Chan, reflect.Ptr, reflect.Interface: if v.IsNil() { return invalidValue } default: } return v } var zeroBigInt = big.Int{} func isEmptyValue(v reflect.Value) bool { switch v.Kind() { case reflect.Array, reflect.Map, reflect.Slice, reflect.String: return v.Len() == 0 case reflect.Bool: return !v.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() default: } switch v.Type() { case timeType: return v.Interface().(time.Time).IsZero() //nolint:forcetypeassert case bigIntType: i := v.Interface().(big.Int) //nolint:forcetypeassert return zeroBigInt.Cmp(&i) == 0 } return false } func (e *Encoder) encode(tag Tag, v reflect.Value, fi *fieldInfo) error { // if pointer or interface v = indirect(v) if !v.IsValid() { return nil } typ := v.Type() if typ == ttlvType { // fast path: if the value is TTLV, we write it directly to the output buffer _, err := e.encBuf.Write(v.Bytes()) return err } typeInfo, err := getTypeInfo(typ) if err != nil { return err } if tag == TagNone { tag = tagForMarshal(v, typeInfo, fi) } var flags fieldFlags if fi != nil { flags = fi.flags } // check for Marshaler switch { case typ.Implements(marshalerType): if flags.omitEmpty() && isEmptyValue(v) { return nil } return v.Interface().(Marshaler).MarshalTTLV(e, tag) //nolint:forcetypeassert case v.CanAddr(): pv := v.Addr() pvtyp := pv.Type() if pvtyp.Implements(marshalerType) { if flags.omitEmpty() && isEmptyValue(v) { return nil } return pv.Interface().(Marshaler).MarshalTTLV(e, tag) //nolint:forcetypeassert } } // If the type doesn't implement Marshaler, then validate the value is a supported kind switch v.Kind() { case reflect.Chan, reflect.Map, reflect.Func, reflect.Ptr, reflect.UnsafePointer, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.Interface: return e.marshalingError(tag, v.Type(), ErrUnsupportedTypeError) default: } // skip if value is empty and tags include omitempty if flags.omitEmpty() && isEmptyValue(v) { return nil } // recurse to handle slices of values switch v.Kind() { case reflect.Slice: if typ.Elem() == byteType { // special case, encode as a ByteString, handled below break } fallthrough case reflect.Array: for i := 0; i < v.Len(); i++ { // turn off the omit empty flag. applies at the field level, // not to each member of the slice // TODO: is this true? var fi2 *fieldInfo if fi != nil { fi2 = &fieldInfo{} // make a copy. *fi2 = *fi fi2.flags &^= fOmitEmpty } err := e.encode(tag, v.Index(i), fi2) if err != nil { return err } } return nil default: } if tag == TagNone { return e.marshalingError(tag, v.Type(), ErrNoTag) } // handle enums and bitmasks // // If the field has the "enum" or "bitmask" flag, or the tag is registered as an enum or bitmask, // attempt to interpret the go value as such. // // If the field is explicitly flag, return an error if the value can't be interpreted. Otherwise // ignore errors and let processing fallthrough to the type-based encoding. enumMap := DefaultRegistry.EnumForTag(tag) if flags.enum() || flags.bitmask() || enumMap != nil { switch typ.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32: i := v.Int() if flags.bitmask() || (enumMap != nil && enumMap.Bitmask()) { e.encBuf.encodeInt(tag, int32(i)) } else { e.encBuf.encodeEnum(tag, uint32(i)) } return nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: i := v.Uint() if flags.bitmask() || (enumMap != nil && enumMap.Bitmask()) { e.encBuf.encodeInt(tag, int32(i)) } else { e.encBuf.encodeEnum(tag, uint32(i)) } return nil case reflect.String: s := v.String() if flags.bitmask() || (enumMap != nil && enumMap.Bitmask()) { i, err := ParseInt(s, enumMap) if err == nil { e.encBuf.encodeInt(tag, i) return nil } // only throw an error if the field is explicitly marked as a bitmask // otherwise just ignore it, and let it encode as a string later on. if flags.bitmask() { // if we couldn't parse the string as an enum value return e.marshalingError(tag, typ, err) } } else { i, err := ParseEnum(s, enumMap) if err == nil { e.encBuf.encodeEnum(tag, i) return nil } // only throw an error if the field is explicitly marked as an enum // otherwise just ignore it, and let it encode as a string later on. if flags.enum() { // if we couldn't parse the string as an enum value return e.marshalingError(tag, typ, err) } } default: if flags.enum() || flags.bitmask() { return e.marshalingError(tag, typ, ErrUnsupportedEnumTypeError).Append(typ.String()) } } } // handle special types switch typ { case timeType: if flags.dateTimeExt() { e.encBuf.encodeDateTimeExtended(tag, v.Interface().(time.Time)) //nolint:forcetypeassert } else { e.encBuf.encodeDateTime(tag, v.Interface().(time.Time)) //nolint:forcetypeassert } return nil case bigIntType: bi := v.Interface().(big.Int) //nolint:forcetypeassert e.encBuf.encodeBigInt(tag, &bi) return nil case bigIntPtrType: e.encBuf.encodeBigInt(tag, v.Interface().(*big.Int)) //nolint:forcetypeassert return nil case durationType: e.encBuf.encodeInterval(tag, time.Duration(v.Int())) return nil } // handle the rest of the kinds switch typ.Kind() { case reflect.Struct: // push current struct onto stack currStruct := e.currStruct e.currStruct = typ.Name() err = e.EncodeStructure(tag, func(e *Encoder) error { for _, field := range typeInfo.valueFields { fv := v.FieldByIndex(field.index) // note: we're staying in reflection world here instead of // converting back to an interface{} value and going through // the non-reflection path again. Calling Interface() // on the reflect value would make a potentially addressable value // into an unaddressable value, reducing the chances we can coerce // the value into a Marshalable. // // tl;dr // Consider a type which implements Marshaler with // a pointer receiver, and a struct with a non-pointer field of that type: // // type Wheel struct{} // func (*Wheel) MarshalTTLV(...) // // type Car struct{ // Wheel Wheel // } // // When traversing the Car struct, should the encoder invoke Wheel's // Marshaler method, or not? Technically, the type `Wheel` // doesn't implement the Marshaler interface. Only the type `*Wheel` // implements it. However, the other encoders in the SDK, like JSON // and XML, will try, if possible, to get a pointer to field values like this, in // order to invoke the Marshaler interface anyway. // // Encoders can only get a pointer to field values if the field // value is `addressable`. Addressability is explained in the docs for reflect.Value#CanAddr(). // Using reflection to turn a reflect.Value() back into an interface{} // can make a potentially addressable value (like the field of an addressable struct) // into an unaddressable value (reflect.Value#Interface{} always returns an unaddressable // copy). // push the currField currField := e.currField e.currField = field.name err := e.encode(TagNone, fv, &field) //nolint:gosec,scopelint // pop the currField e.currField = currField if err != nil { return err } } return nil }) // pop current struct e.currStruct = currStruct return err case reflect.String: e.encBuf.encodeTextString(tag, v.String()) case reflect.Slice: // special case, encode as a ByteString // all slices which aren't []byte should have been handled above // the call to v.Bytes() will panic if this assumption is wrong e.encBuf.encodeByteString(tag, v.Bytes()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32: i := v.Int() if i > math.MaxInt32 { return e.marshalingError(tag, typ, ErrIntOverflow) } e.encBuf.encodeInt(tag, int32(i)) return nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: u := v.Uint() if u > math.MaxInt32 { return e.marshalingError(tag, typ, ErrIntOverflow) } e.encBuf.encodeInt(tag, int32(u)) return nil case reflect.Uint64: u := v.Uint() e.encBuf.encodeLongInt(tag, int64(u)) return nil case reflect.Int64: e.encBuf.encodeLongInt(tag, v.Int()) return nil case reflect.Bool: e.encBuf.encodeBool(tag, v.Bool()) default: // all kinds should have been handled by now panic(errors.New("should never get here")) } return nil } func tagForMarshal(v reflect.Value, ti typeInfo, fi *fieldInfo) Tag { // the tag on the TTLVTag field if ti.tagField != nil && ti.tagField.explicitTag != TagNone { return ti.tagField.explicitTag } // the value of the TTLVTag field of type Tag if v.IsValid() && ti.tagField != nil && ti.tagField.ti.typ == tagType { tag := v.FieldByIndex(ti.tagField.index).Interface().(Tag) //nolint:forcetypeassert if tag != TagNone { return tag } } // if value is in a struct field, infer the tag from the field // else infer from the value's type name if fi != nil { return fi.tag } return ti.inferredTag } // encBuf encodes basic KMIP types into TTLV. type encBuf struct { bytes.Buffer } func (h *encBuf) begin(tag Tag, typ Type) int { _ = h.WriteByte(byte(tag >> 16)) _ = h.WriteByte(byte(tag >> 8)) _ = h.WriteByte(byte(tag)) _ = h.WriteByte(byte(typ)) _, _ = h.Write(zeros[:4]) return h.Len() } func (h *encBuf) end(i int) { n := h.Len() - i if m := n % 8; m > 0 { _, _ = h.Write(zeros[:8-m]) } binary.BigEndian.PutUint32(h.Bytes()[i-4:], uint32(n)) } func (h *encBuf) writeLongIntVal(tag Tag, typ Type, i int64) { s := h.begin(tag, typ) ll := h.Len() _, _ = h.Write(zeros[:8]) binary.BigEndian.PutUint64(h.Bytes()[ll:], uint64(i)) h.end(s) } func (h *encBuf) writeIntVal(tag Tag, typ Type, val uint32) { s := h.begin(tag, typ) ll := h.Len() _, _ = h.Write(zeros[:4]) binary.BigEndian.PutUint32(h.Bytes()[ll:], val) h.end(s) } var ( ones = [8]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} zeros = [8]byte{} ) func (h *encBuf) encodeBigInt(tag Tag, i *big.Int) { if i == nil { return } ii := h.begin(tag, TypeBigInteger) switch i.Sign() { case 0: _, _ = h.Write(zeros[:8]) case 1: b := i.Bytes() l := len(b) // if n is positive, but the first bit is a 1, it will look like // a negative in 2's complement, so prepend zeroes in front if b[0]&0x80 > 0 { _ = h.WriteByte(byte(0)) l++ } // pad front with zeros to multiple of 8 if m := l % 8; m > 0 { _, _ = h.Write(zeros[:8-m]) } _, _ = h.Write(b) case -1: length := uint(i.BitLen()/8+1) * 8 j := new(big.Int).Lsh(one, length) b := j.Add(i, j).Bytes() // When the most significant bit is on a byte // boundary, we can get some extra significant // bits, so strip them off when that happens. if len(b) >= 2 && b[0] == 0xff && b[1]&0x80 != 0 { b = b[1:] } l := len(b) // pad front with ones to multiple of 8 if m := l % 8; m > 0 { _, _ = h.Write(ones[:8-m]) } _, _ = h.Write(b) } h.end(ii) } func (h *encBuf) encodeInt(tag Tag, i int32) { h.writeIntVal(tag, TypeInteger, uint32(i)) } func (h *encBuf) encodeBool(tag Tag, b bool) { if b { h.writeLongIntVal(tag, TypeBoolean, 1) } else { h.writeLongIntVal(tag, TypeBoolean, 0) } } func (h *encBuf) encodeLongInt(tag Tag, i int64) { h.writeLongIntVal(tag, TypeLongInteger, i) } func (h *encBuf) encodeDateTime(tag Tag, t time.Time) { h.writeLongIntVal(tag, TypeDateTime, t.Unix()) } func (h *encBuf) encodeDateTimeExtended(tag Tag, t time.Time) { // take unix seconds, times a million, to get microseconds, then // add nanoseconds remainder/1000 // // this gives us a larger ranger of possible values than just t.UnixNano() / 1000. // see UnixNano() docs for its limits. // // this is limited to max(int64) *microseconds* from epoch, rather than // max(int64) nanoseconds like UnixNano(). m := (t.Unix() * 1000000) + int64(t.Nanosecond()/1000) h.writeLongIntVal(tag, TypeDateTimeExtended, m) } func (h *encBuf) encodeInterval(tag Tag, d time.Duration) { h.writeIntVal(tag, TypeInterval, uint32(d/time.Second)) } func (h *encBuf) encodeEnum(tag Tag, i uint32) { h.writeIntVal(tag, TypeEnumeration, i) } func (h *encBuf) encodeTextString(tag Tag, s string) { i := h.begin(tag, TypeTextString) _, _ = h.WriteString(s) h.end(i) } func (h *encBuf) encodeByteString(tag Tag, b []byte) { if b == nil { return } i := h.begin(tag, TypeByteString) _, _ = h.Write(b) h.end(i) } func getTypeInfo(typ reflect.Type) (ti typeInfo, err error) { ti.inferredTag, _ = DefaultRegistry.ParseTag(typ.Name()) ti.typ = typ err = ti.getFieldsInfo() return ti, err } var errSkip = errors.New("skip") func getFieldInfo(typ reflect.Type, sf reflect.StructField) (fieldInfo, error) { var fi fieldInfo // skip anonymous and unexported fields if sf.Anonymous || /*unexported:*/ sf.PkgPath != "" { return fi, errSkip } fi.name = sf.Name fi.structType = typ fi.index = sf.Index var anyField bool // handle field tags parts := strings.Split(sf.Tag.Get(structFieldTag), ",") for i, value := range parts { if i == 0 { switch value { case "-": // skip return fi, errSkip case "": default: var err error fi.explicitTag, err = DefaultRegistry.ParseTag(value) if err != nil { return fi, err } } } else { switch strings.ToLower(value) { case "enum": fi.flags |= fEnum case "omitempty": fi.flags |= fOmitEmpty case "datetimeextended": fi.flags |= fDateTimeExtended case "bitmask": fi.flags |= fBitBask case "any": anyField = true fi.flags |= fAny } } } if anyField && fi.explicitTag != TagNone { return fi, merry.Here(ErrTagConflict).Appendf(`field %s.%s may not specify a TTLV tag and the "any" flag`, fi.structType.Name(), fi.name) } // extract type info for the field. The KMIP tag // for this field is derived from either the field name, // the field tags, or the field type. var err error fi.ti, err = getTypeInfo(sf.Type) if err != nil { return fi, err } if fi.ti.tagField != nil && fi.ti.tagField.explicitTag != TagNone { fi.tag = fi.ti.tagField.explicitTag if fi.explicitTag != TagNone && fi.explicitTag != fi.tag { // if there was a tag on the struct field containing this value, it must // agree with the value's intrinsic tag return fi, merry.Here(ErrTagConflict).Appendf(`TTLV tag "%s" in tag of %s.%s conflicts with TTLV tag "%s" in %s.%s`, fi.explicitTag, fi.structType.Name(), fi.name, fi.ti.tagField.explicitTag, fi.ti.typ.Name(), fi.ti.tagField.name) } } // pre-calculate the tag for this field. This intentional duplicates // some of tagForMarshaling(). The value is primarily used in unmarshaling // where the dynamic value of the field is not needed. if fi.tag == TagNone { fi.tag = fi.explicitTag } if fi.tag == TagNone { fi.tag, _ = DefaultRegistry.ParseTag(fi.name) } return fi, nil } func (ti *typeInfo) getFieldsInfo() error { if ti.typ.Kind() != reflect.Struct { return nil } for i := 0; i < ti.typ.NumField(); i++ { fi, err := getFieldInfo(ti.typ, ti.typ.Field(i)) switch { case err == errSkip: //nolint:errorlint // skip case err != nil: return err case fi.name == "TTLVTag": ti.tagField = &fi default: ti.valueFields = append(ti.valueFields, fi) } } // verify that multiple fields don't have the same tag names := map[Tag]string{} for _, f := range ti.valueFields { if f.flags.any() { // ignore any fields continue } tag := f.tag if tag != TagNone { if fname, ok := names[tag]; ok { return merry.Here(ErrTagConflict).Appendf("field resolves to the same tag (%s) as other field (%s)", tag, fname) } names[tag] = f.name } } return nil } type typeInfo struct { typ reflect.Type inferredTag Tag tagField *fieldInfo valueFields []fieldInfo } const ( fOmitEmpty fieldFlags = 1 << iota fEnum fDateTimeExtended fAny fBitBask ) type fieldFlags int func (f fieldFlags) omitEmpty() bool { return f&fOmitEmpty != 0 } func (f fieldFlags) any() bool { return f&fAny != 0 } func (f fieldFlags) dateTimeExt() bool { return f&fDateTimeExtended != 0 } func (f fieldFlags) enum() bool { return f&fEnum != 0 } func (f fieldFlags) bitmask() bool { return f&fBitBask != 0 } type fieldInfo struct { structType reflect.Type explicitTag, tag Tag name string index []int flags fieldFlags ti typeInfo }