-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalues.go
More file actions
192 lines (152 loc) · 4.16 KB
/
Copy pathvalues.go
File metadata and controls
192 lines (152 loc) · 4.16 KB
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
package sml
import (
"bytes"
"errors"
"fmt"
"io"
"3e8.eu/go/sml/tlv"
)
const (
tlEndOfSmlMsg = 0b00000000
tlMaskContinue = 0b10000000
tlMaskType = 0b01110000
tlMaskLength = 0b00001111
)
const (
tlTypeOctetString = 0b00000000
tlTypeBoolean = 0b01000000
tlTypeInteger = 0b01010000
tlTypeUnsigned = 0b01100000
tlTypeList = 0b01110000
tlTypeContinue = 0b00000000
)
// ParseValues parses the content of an SML file into its raw TLV values. It returns a list of TLV
// values (which should each correspond to an SML message) or an error.
func ParseValues(data []byte) ([]tlv.Value, error) {
messages := []tlv.Value{}
r := bytes.NewReader(data)
for r.Len() > 0 {
msg, err := parseNextValue(r)
if err != nil {
index := r.Size() - int64(r.Len())
return []tlv.Value{}, fmt.Errorf("error at index %d: %w", index, err)
}
messages = append(messages, msg)
}
return messages, nil
}
func parseNextValue(r io.ByteReader) (tlv.Value, error) {
val, err := r.ReadByte()
if err != nil {
return tlv.Value{}, err
}
if val == tlEndOfSmlMsg {
out, _ := tlv.MakeValue(tlv.EndOfSmlMsg, 0, nil)
return out, nil
}
switch val & tlMaskType {
case tlTypeOctetString, tlTypeBoolean, tlTypeInteger, tlTypeUnsigned, tlTypeList:
default:
return tlv.Value{}, fmt.Errorf("unexpected TL field type: %02x", val)
}
length := int(val & tlMaskLength)
tlCount := 1
if (val & tlMaskContinue) != 0 {
count, err := parseContinuedLength(&length, r)
if err != nil {
return tlv.Value{}, err
}
tlCount += count
}
out, err := parseActualValue(val, length, tlCount, r)
if err != nil {
return tlv.Value{}, err
}
return out, nil
}
func parseContinuedLength(length *int, r io.ByteReader) (int, error) {
maxCount := 3
for i := 0; i <= maxCount; i++ {
val, err := r.ReadByte()
if err != nil {
return i, err
}
if (val & tlMaskType) != tlTypeContinue {
return i, fmt.Errorf("unexpected continued TL field: %02x", val)
}
addLength := int(val & tlMaskLength)
*length = *length<<4 | addLength
if (val & tlMaskContinue) == 0 {
return i + 1, nil
}
}
return maxCount, errors.New("unreasonable continuation of TL field")
}
func parseActualValue(firstVal byte, length, tlCount int, r io.ByteReader) (tlv.Value, error) {
valLength := length - tlCount
switch firstVal & tlMaskType {
case tlTypeOctetString:
if valLength < 0 {
return tlv.Value{}, fmt.Errorf("OctetString field with invalid value length %d", valLength)
}
buf := make([]byte, valLength)
for i := range valLength {
val, err := r.ReadByte()
if err != nil {
return tlv.Value{}, err
}
buf[i] = val
}
return tlv.MakeValue(tlv.OctetString, length, buf)
case tlTypeBoolean:
if length != 2 || valLength != 1 {
return tlv.Value{}, fmt.Errorf("Boolean field with invalid length %d", length)
}
val, err := r.ReadByte()
if err != nil {
return tlv.Value{}, err
}
return tlv.MakeValue(tlv.OctetString, length, val != 0)
case tlTypeInteger:
if valLength > 8 {
return tlv.Value{}, fmt.Errorf("Integer with unsupported value length %d", valLength)
}
var intVal int64
for _ = range valLength {
val, err := r.ReadByte()
if err != nil {
return tlv.Value{}, err
}
intVal = intVal<<8 | int64(val)
}
return tlv.MakeValue(tlv.Integer, length, intVal)
case tlTypeUnsigned:
if valLength > 8 {
return tlv.Value{}, fmt.Errorf("Unsigned with unsupported value length %d", valLength)
}
var uintVal uint64
for _ = range valLength {
val, err := r.ReadByte()
if err != nil {
return tlv.Value{}, err
}
uintVal = uintVal<<8 | uint64(val)
}
return tlv.MakeValue(tlv.Unsigned, length, uintVal)
case tlTypeList:
list := make([]tlv.Value, length)
for i := range length {
val, err := parseNextValue(r)
if err != nil {
return tlv.Value{}, err
}
list[i] = val
}
return tlv.MakeValue(tlv.List, length, list)
}
// This should never happen
return tlv.Value{}, errors.New("unexpected TLV type")
}