Passed
Push — master ( c32edb...17f9f7 )
by Nikita
02:16
created

encode.marshalerEncoder   A

Complexity

Conditions 5

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 5.9256

Importance

Changes 0
Metric Value
cc 5
eloc 10
dl 0
loc 14
ccs 6
cts 9
cp 0.6667
crap 5.9256
rs 9.3333
c 0
b 0
f 0
nop 1
1
package encode
2
3
import (
4
	"encoding"
5
	"reflect"
6
	"sync"
7
8
	"github.com/et-nik/binngo/binn"
9
)
10
11
type encoderFunc func(v reflect.Value) ([]byte, error)
12
13
var encoderCache sync.Map // map[reflect.Type]encoderFunc
14
15
var (
16
	marshalerType     = reflect.TypeOf((*Marshaler)(nil)).Elem()
17
	textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
18
)
19
20
type Marshaler interface {
21
	MarshalBINN() ([]byte, error)
22
}
23
24
func marshal(v interface{}) ([]byte, error) {
25 1
	rv := reflect.ValueOf(v)
26
27 1
	if !rv.IsValid() {
28
		return nil, ErrInvalidValue
29
	}
30
31 1
	if !rv.IsValid() {
32
		return nil, ErrInvalidValue
33
	}
34
35 1
	enc := loadEncodeFunc(rv.Type())
36
37 1
	return enc(rv)
38
}
39
40
func loadEncodeFunc(t reflect.Type) encoderFunc {
41 1
	if fi, ok := encoderCache.Load(t); ok {
42 1
		return fi.(encoderFunc)
43
	}
44
45 1
	var (
46
		wg sync.WaitGroup
47
		f  encoderFunc
48
	)
49 1
	wg.Add(1)
50 1
	fi, loaded := encoderCache.LoadOrStore(t, encoderFunc(func(v reflect.Value) ([]byte, error) {
51
		wg.Wait()
52
		return f(v)
53
	}))
54 1
	if loaded {
55
		return fi.(encoderFunc)
56
	}
57
58 1
	f = newTypeEncoder(t)
59 1
	wg.Done()
60 1
	encoderCache.Store(t, f)
61 1
	return f
62
}
63
64
func newTypeEncoder(t reflect.Type) encoderFunc {
65 1
	if t.Implements(marshalerType) {
66 1
		return marshalerEncoder
67
	}
68 1
	if t.Implements(textMarshalerType) {
69
		return textMarshalerEncoder
70
	}
71
72 1
	switch t.Kind() {
73
	case reflect.Bool:
74
		return func(v reflect.Value) ([]byte, error) {
75
			if v.Bool() {
76
				return []byte{binn.True}, nil
77
			}
78
			return []byte{binn.False}, nil
79
		}
80
	case reflect.Struct:
81 1
		return newStructEncoder(t)
82
	case reflect.Map:
83 1
		return newMapEncoder(t)
84
	case reflect.Interface:
85 1
		return func(v reflect.Value) ([]byte, error) {
86 1
			if v.IsNil() {
87
				return []byte{binn.Null}, nil
88
			}
89
90 1
			return loadEncodeFunc(v.Elem().Type())(v.Elem())
91
		}
92
	case reflect.String:
93 1
		return func(v reflect.Value) ([]byte, error) {
94 1
			var bytes []byte
95
96 1
			bytes = append(bytes, Uint8(binn.StringType)...)
97 1
			bytes = append(bytes, String(v.String())...)
98 1
			bytes = append(bytes, 0x00)
99
100 1
			return bytes, nil
101
		}
102
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
103 1
		return func(v reflect.Value) ([]byte, error) {
104 1
			var bytes []byte
105
106 1
			bytes = append(bytes, Uint8(uint8(detectIntType(int(v.Int()))))...)
107 1
			bytes = append(bytes, Int(int(v.Int()))...)
108
109 1
			return bytes, nil
110
		}
111
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
112
		return func(v reflect.Value) ([]byte, error) {
113
			var bytes []byte
114
115
			bytes = append(bytes, Uint8(uint8(detectUintType(uint(v.Uint()))))...)
116
			bytes = append(bytes, Uint(uint(v.Uint()))...)
117
118
			return bytes, nil
119
		}
120
	case reflect.Float32:
121
		return func(v reflect.Value) ([]byte, error) {
122
			var bytes []byte
123
124
			bytes = append(bytes, Uint8(binn.Float32Type)...)
125
			bytes = append(bytes, Float32(float32(v.Float()))...)
126
127
			return bytes, nil
128
		}
129
	case reflect.Float64:
130
		return func(v reflect.Value) ([]byte, error) {
131
			var bytes []byte
132
133
			bytes = append(bytes, Uint8(binn.Float64Type)...)
134
			bytes = append(bytes, Float64(v.Float())...)
135
136
			return bytes, nil
137
		}
138
	case reflect.Slice, reflect.Array:
139 1
		return newArrayEncoder(t)
140
	case reflect.Ptr:
141
		return newPtrEncoder(t)
142
	}
143
144
	return func(v reflect.Value) ([]byte, error) {
145
		return nil, &UnsupportedTypeError{t}
146
	}
147
}
148
149
func marshalerEncoder(v reflect.Value) ([]byte, error) {
150 1
	if v.Kind() == reflect.Ptr && v.IsNil() {
151
		return []byte{0x00}, nil
152
	}
153 1
	m, ok := v.Interface().(Marshaler)
154 1
	if !ok {
155
		return []byte{0x00}, nil
156
	}
157 1
	b, err := m.MarshalBINN()
158 1
	if err != nil {
159
		return nil, &MarshalerError{v.Type(), err, "MarshalBINN"}
160
	}
161
162 1
	return b, nil
163
}
164