-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathencoding.go
129 lines (107 loc) · 2.37 KB
/
encoding.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
package milter
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"reflect"
)
func decode(data []byte, dest interface{}) error {
d := &decoder{data}
return d.decode(dest)
}
// A decoder decodes data from the milter protocol's binary wire format.
type decoder struct {
data []byte
}
var errNotEnoughData = errors.New("not enough data")
func (d *decoder) decode(val interface{}) error {
var v reflect.Value
switch val := val.(type) {
case reflect.Value:
v = val
default:
v = reflect.ValueOf(val)
}
switch v.Kind() {
case reflect.Ptr, reflect.Interface:
return d.decode(v.Elem())
case reflect.Uint8:
if len(d.data) < 1 {
return errNotEnoughData
}
v.SetUint(uint64(d.data[0]))
d.data = d.data[1:]
return nil
case reflect.Uint16:
if len(d.data) < 2 {
return errNotEnoughData
}
v.SetUint(uint64(binary.BigEndian.Uint16(d.data)))
d.data = d.data[2:]
return nil
case reflect.Uint32:
if len(d.data) < 4 {
return errNotEnoughData
}
v.SetUint(uint64(binary.BigEndian.Uint32(d.data)))
d.data = d.data[4:]
return nil
case reflect.String:
i := bytes.IndexByte(d.data, 0)
if i == -1 {
return errors.New("unterminated C string")
}
v.SetString(string(d.data[:i]))
d.data = d.data[i+1:]
return nil
case reflect.Struct:
n := v.NumField()
for i := 0; i < n; i++ {
if err := d.decode(v.Field(i)); err != nil {
return err
}
}
return nil
default:
panic(fmt.Errorf("decode: unsupported type: %T", v.Interface()))
}
}
func encode(val interface{}) []byte {
e := &encoder{new(bytes.Buffer)}
e.encode(val)
return e.Bytes()
}
// An encoder encodes data into the milter protocol's binary wire format.
type encoder struct {
*bytes.Buffer
}
func (e *encoder) encode(val interface{}) {
var v reflect.Value
switch val := val.(type) {
case reflect.Value:
v = val
default:
v = reflect.ValueOf(val)
}
switch v.Kind() {
case reflect.Ptr, reflect.Interface:
e.encode(v.Elem())
case reflect.Uint8:
e.WriteByte(byte(v.Uint()))
case reflect.Uint16:
binary.Write(e, binary.BigEndian, uint16(v.Uint()))
case reflect.Uint32:
binary.Write(e, binary.BigEndian, uint32(v.Uint()))
case reflect.String:
e.WriteString(v.String())
e.WriteByte(0)
case reflect.Struct:
n := v.NumField()
for i := 0; i < n; i++ {
e.encode(v.Field(i))
}
default:
panic(fmt.Errorf("encode: unsupported type: %T", v.Interface()))
}
}