2022-08-16 09:48:06 +00:00
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
2022-10-06 10:24:15 +00:00
//
2022-08-16 09:48:06 +00:00
// 2. If the value implements Marshaler, call that
2022-10-06 10:24:15 +00:00
//
2022-08-16 09:48:06 +00:00
// 3. If the struct field has an "omitempty" flag, and the value is
2022-10-06 10:24:15 +00:00
// zero, skip the field:
2022-08-16 09:48:06 +00:00
//
2022-10-06 10:24:15 +00:00
// type Foo struct {
// Comment string `ttlv:,omitempty`
// }
2022-08-16 09:48:06 +00:00
//
// 4. If the value is a slice (except []byte) or array, marshal all
2022-10-06 10:24:15 +00:00
// values concatenated
//
2022-08-16 09:48:06 +00:00
// 5. If a tag has not been inferred at this point, return *MarshalerError with
2022-10-06 10:24:15 +00:00
// cause ErrNoTag
//
2022-08-16 09:48:06 +00:00
// 6. If the Tag is registered as an enum, or has the "enum" struct tag flag, attempt
2022-10-06 10:24:15 +00:00
// 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.
//
2022-08-16 09:48:06 +00:00
// 7. If the Tag is registered as a bitmask, or has the "bitmask" struct tag flag, attempt
2022-10-06 10:24:15 +00:00
// to marshal to an Integer, following the same rules as for Enumerations. The ParseInt()
// function is used to parse string values.
//
2022-08-16 09:48:06 +00:00
// 9. time.Time marshals to DateTime. If the field has the "datetimeextended" struct flag,
2022-10-06 10:24:15 +00:00
// marshal as DateTimeExtended. Example:
2022-08-16 09:48:06 +00:00
//
2022-10-06 10:24:15 +00:00
// type Foo struct {
// ActivationDate time.Time `ttlv:",datetimeextended"`
// }
2022-08-16 09:48:06 +00:00
//
// 10. big.Int marshals to BigInteger
2022-10-06 10:24:15 +00:00
//
2022-08-16 09:48:06 +00:00
// 11. time.Duration marshals to Interval
2022-10-06 10:24:15 +00:00
//
2022-08-16 09:48:06 +00:00
// 12. string marshals to TextString
2022-10-06 10:24:15 +00:00
//
2022-08-16 09:48:06 +00:00
// 13. []byte marshals to ByteString
2022-10-06 10:24:15 +00:00
//
2022-08-16 09:48:06 +00:00
// 14. all int and uint variants except int64 and uint64 marshal to Integer. If the golang
2022-10-06 10:24:15 +00:00
// value overflows the KMIP value, *MarshalerError with cause ErrIntOverflow is returned
//
2022-08-16 09:48:06 +00:00
// 15. int64 and uint64 marshal to LongInteger
2022-10-06 10:24:15 +00:00
//
2022-08-16 09:48:06 +00:00
// 16. bool marshals to Boolean
2022-10-06 10:24:15 +00:00
//
2022-08-16 09:48:06 +00:00
// 17. structs marshal to Structure. Each field of the struct will be marshaled into the
2022-10-06 10:24:15 +00:00
// values of the Structure according to the above rules.
2022-08-16 09:48:06 +00:00
//
// 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<Type> 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
}