-
Notifications
You must be signed in to change notification settings - Fork 3
/
field.go
159 lines (139 loc) · 3.81 KB
/
field.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package beacon
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"strconv"
"strings"
)
// A Field stores a beacon ID or data field.
type Field []byte
// Fields is a slice of Field structs
type Fields []Field
// String converts a beacon Field into a human readable format.
func (f Field) String() string {
switch len(f) {
case 1:
return strconv.Itoa(int(f.Int8()))
case 2:
return strconv.Itoa(int(f.Uint16()))
default:
return f.Hex()
}
}
// MarshalJSON outputs a hex string for most fields, or an integer
// for fields that are two bytes in length.
func (f Field) MarshalJSON() (text []byte, err error) {
if len(f) == 2 {
text = []byte(f.String())
} else {
text = []byte(fmt.Sprintf("\"%v\"", f.Hex()))
}
err = nil
return
}
// UnmarshalJSON unmarshals a json string into a Field. If the text starts with
// a quote, it assumes it is a hex string. Otherwise it will unmarshal it as a
// uint16 field, if it looks like an integer.
func (f *Field) UnmarshalJSON(text []byte) error {
str := string(text)
if str[0] == '"' {
*f = FieldFromHex(str[1 : len(str)-1])
} else if i, err := strconv.Atoi(str); err == nil && strconv.Itoa(i) == str {
*f = FieldFromUint16(uint16(i))
} else {
return errors.New("Error unmarshaling text - invalid value")
}
return nil
}
// Int8 returns the field as an int8 value
func (f *Field) Int8() int8 {
return int8((*f)[0])
}
// Uint8 returns the field as a uint8 value
func (f *Field) Uint8() uint8 {
return uint8((*f)[0])
}
// Uint16 returns the field as a uint16 value. This is especially helpful for
// ibeacon or altbeaon major and minor fields.
func (f *Field) Uint16() uint16 {
return binary.BigEndian.Uint16((*f)[:])
}
// Hex returns the field as a hex string
func (f *Field) Hex() string {
return hex.EncodeToString(*f)
}
// FieldFromUint16 converts a uint16 value into a Field
func FieldFromUint16(n uint16) Field {
var field Field = make([]byte, 2)
binary.BigEndian.PutUint16(field, n)
return field
}
// FieldFromInt8 converts an int8 value into a Field struct
func FieldFromInt8(n int8) Field {
var field Field = []byte{byte(n)}
return field
}
// FieldFromHex converts a hex string into a Field struct
func FieldFromHex(s string) Field {
var field Field
// remove dashes in case we were given a UUID
s = strings.Replace(s, "-", "", -1)
field, _ = hex.DecodeString(s)
return field
}
// Equal tests if two field structs have the same bytes
func (a Field) Equal(b Field) bool {
return bytes.Equal(a, b)
}
// Equal tests if two Field slices contain matching Field structs
func (a Fields) Equal(b Fields) bool {
for i := range a {
if !bytes.Equal(a[i], b[i]) {
return false
}
}
return true
}
// UUIDMajorMinorFields returns Fields suitable for an altBeacon or iBeacon
func UUIDMajorMinorFields(uuid string, major uint16, minor uint16) Fields {
return Fields{
FieldFromHex(uuid),
FieldFromUint16(major),
FieldFromUint16(minor),
}
}
// EddystoneUIDFields returns Fields suitable for an Eddystone UID beacon
func EddystoneUIDFields(namespace, instance string) Fields {
return Fields{
FieldFromHex(namespace),
FieldFromHex(instance),
}
}
// Hex returns hex strings for each Field separated by a space
func (f *Fields) Hex() string {
strFields := make([]string, len(*f))
for i, field := range *f {
strFields[i] = field.Hex()
}
return strings.Join(strFields, " ")
}
// String returns a human readable representation of Fields
func (f *Fields) String() string {
strFields := make([]string, len(*f))
for i, field := range *f {
strFields[i] = field.String()
}
return strings.Join(strFields, " ")
}
// Key returns a value which can be used as a map key to uniquely
// represent this set of fields
func (f *Fields) Key() string {
data := make([][]byte, len(*f))
for i, field := range *f {
data[i] = field
}
return string(bytes.Join(data, []byte{}))
}