forked from ThalesGroup/kmip-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
enums.go
141 lines (127 loc) · 2.92 KB
/
enums.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package ttlv
import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"strconv"
"strings"
"sync"
"github.com/ansel1/merry"
)
var ErrInvalidHexString = errors.New("invalid hex string")
type EnumTypeDef struct {
Parse func(s string) (uint32, bool)
String func(v uint32) string
Typed func(v uint32) interface{}
}
func RegisterEnum(tag Tag, def EnumTypeDef) {
enumRegistry.Store(tag, enumDef{EnumTypeDef: def})
}
func RegisterBitMask(tag Tag, def EnumTypeDef) {
enumRegistry.Store(tag, enumDef{EnumTypeDef: def, isMask: true})
}
func IsEnumeration(tag Tag) bool {
v, _ := enumRegistry.Load(tag)
if v != nil {
return !v.(enumDef).isMask
}
return false
}
func IsBitMask(tag Tag) bool {
v, _ := enumRegistry.Load(tag)
if v != nil {
return v.(enumDef).isMask
}
return false
}
func ParseInteger(tag Tag, s string) (int32, error) {
if strings.IndexAny(s, "| ") < 0 {
return parseOneInteger(tag, s)
}
// split values, look up each, and recombine
s = strings.Replace(s, "|", " ", -1)
parts := strings.Split(s, " ")
var v int32
for _, part := range parts {
if len(part) == 0 {
continue
}
i, err := parseOneInteger(tag, part)
if err != nil {
return 0, err
}
v |= i
}
return v, nil
}
func ParseEnum(tag Tag, s string) (uint32, error) {
if strings.HasPrefix(s, "0x") {
b, err := hex.DecodeString(s[2:])
if err != nil {
return 0, merry.Here(ErrInvalidHexString).WithCause(err)
}
if len(b) != 4 {
return 0, merry.Here(ErrInvalidHexString).Append("must be 4 bytes (8 hex characters)")
}
return binary.BigEndian.Uint32(b), nil
}
u, err := strconv.ParseUint(s, 10, 32)
if err == nil {
// it was a raw number
return uint32(u), nil
}
v, _ := enumRegistry.Load(tag)
if v != nil {
if u, ok := v.(enumDef).Parse(s); ok {
return u, nil
}
}
return 0, merry.New("must be a number, hex string, or enum value name")
}
func EnumToTyped(tag Tag, i uint32) interface{} {
v, _ := enumRegistry.Load(tag)
if v != nil {
if v.(enumDef).Typed != nil {
return v.(enumDef).Typed(i)
}
}
return i
}
func EnumToString(tag Tag, i uint32) string {
v, _ := enumRegistry.Load(tag)
if v != nil {
s := v.(enumDef).String(i)
if s != "" {
return s
}
}
return fmt.Sprintf("%#08x", byte(i))
}
type enumDef struct {
EnumTypeDef
isMask bool
}
var enumRegistry = sync.Map{}
func parseOneInteger(tag Tag, s string) (int32, error) {
if strings.HasPrefix(s, "0x") {
b, err := hex.DecodeString(s[2:])
if err != nil {
return 0, merry.Here(ErrInvalidHexString).WithCause(err)
}
if len(b) != 4 {
return 0, merry.Here(ErrInvalidHexString).Append("must be 4 bytes (8 hex characters)")
}
return int32(binary.BigEndian.Uint32(b)), nil
}
i, err := strconv.ParseInt(s, 10, 32)
if err == nil {
return int32(i), nil
}
if v, ok := enumRegistry.Load(tag); ok {
if u, ok := v.(enumDef).Parse(s); ok {
return int32(u), nil
}
}
return 0, merry.New("must be number, hex string, or mask value name")
}