mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-14 10:20:20 +00:00
963 lines
25 KiB
Go
963 lines
25 KiB
Go
|
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<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
|
||
|
}
|