forked from vapourismo/knx-go
/
unpack.go
138 lines (106 loc) · 2.71 KB
/
unpack.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
// Copyright 2017 Ole Krüger.
// Licensed under the MIT license which can be found in the LICENSE file.
package util
import (
"bytes"
"fmt"
"io"
)
var (
stringDecoder = stringCharmap.NewDecoder()
)
// Unpackable is implemented by types that can be initialized by reading from a byte slice.
type Unpackable interface {
Unpack(data []byte) (uint, error)
}
func unpackUInt16(data []byte, output *uint16) (uint, error) {
if len(data) < 2 {
return 0, io.ErrUnexpectedEOF
}
*output = uint16(data[1]) | uint16(data[0])<<8
return 2, nil
}
func unpackUInt32(data []byte, output *uint32) (uint, error) {
if len(data) < 4 {
return 0, io.ErrUnexpectedEOF
}
*output = uint32(data[3]) | uint32(data[2])<<8 | uint32(data[1])<<16 | uint32(data[0])<<24
return 4, nil
}
func unpackUInt64(data []byte, output *uint64) (uint, error) {
if len(data) < 8 {
return 0, io.ErrUnexpectedEOF
}
*output = uint64(data[7]) | uint64(data[6])<<8 | uint64(data[5])<<16 | uint64(data[4])<<24 |
uint64(data[3])<<32 | uint64(data[2])<<40 | uint64(data[1])<<48 | uint64(data[0])<<56
return 8, nil
}
// Unpack the byte slice to the given value.
func Unpack(data []byte, output interface{}) (uint, error) {
switch output := output.(type) {
case *uint8:
if len(data) < 1 {
return 0, io.ErrUnexpectedEOF
}
*output = data[0]
return 1, nil
case *int8:
if len(data) < 1 {
return 0, io.ErrUnexpectedEOF
}
*output = int8(data[0])
return 1, nil
case *uint16:
return unpackUInt16(data, output)
case *int16:
var u uint16
n, err := unpackUInt16(data, &u)
*output = int16(u)
return n, err
case *uint32:
return unpackUInt32(data, output)
case *int32:
var u uint32
n, err := unpackUInt32(data, &u)
*output = int32(u)
return n, err
case *uint64:
return unpackUInt64(data, output)
case *int64:
var u uint64
n, err := unpackUInt64(data, &u)
*output = int64(u)
return n, err
case []byte:
if len(output) > len(data) {
return 0, io.ErrUnexpectedEOF
}
return uint(copy(output, data)), nil
case Unpackable:
return output.Unpack(data)
}
return 0, fmt.Errorf("can't unpack type %T", output)
}
// UnpackSome unpacks multiple values.
func UnpackSome(data []byte, outputs ...interface{}) (uint, error) {
var n uint
for _, output := range outputs {
m, err := Unpack(data[n:], output)
n += m
if err != nil {
return n, err
}
}
return n, nil
}
// UnpackString unpacks a string
func UnpackString(buffer []byte, len uint, output *string) (uint, error) {
buffer = buffer[:len]
buffer = bytes.TrimRight(buffer, string(byte(0x0)))
buffer, err := stringDecoder.Bytes(buffer)
if err != nil {
return 0, fmt.Errorf("unable to decode string: %s", err)
}
*output = string(buffer)
return len, nil
}