-
Notifications
You must be signed in to change notification settings - Fork 1
/
protoid.go
140 lines (122 loc) · 3.68 KB
/
protoid.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
package protoid
import (
"errors"
"fmt"
"unicode/utf8"
)
var (
// ErrUnexpectedEndOfInput indicates that the input data is shorter than expected.
ErrUnexpectedEndOfInput = errors.New("unexpected end of input")
// ErrNotImplemented indicates that a message contained somethings that protoid cannot currently decode.
ErrNotImplemented = errors.New("groups not implemented")
// ErrNumberTooLarge inducates that a number found in a protocol buffers message cannot be represented as a 64 bit value.
ErrNumberTooLarge = errors.New("number too large for 64 bit value")
)
type valueApplier interface {
mapType0(propnum int, value uint64) error
mapType1(propnum int, value uint64) error
mapType2(propnum int, value []byte) error
mapType5(propnum int, value uint32) error
}
type genericMapValueApplier struct {
m map[int]interface{}
}
func (va *genericMapValueApplier) mapType0(propnum int, value uint64) error {
va.m[propnum] = value
return nil
}
func (va *genericMapValueApplier) mapType1(propnum int, value uint64) error {
va.m[propnum] = value
return nil
}
func (va *genericMapValueApplier) mapType2(propnum int, data []byte) error {
var value interface{}
// try to guess the type of data
// first try to decode as embedded value
emb, err := Decode(data)
if err == nil {
value = emb
} else {
if utf8.Valid(data) {
// string
value = string(data)
} else {
// assume bytes
value = copyBytes(data)
}
}
if va.m[propnum] != nil {
// we already have a value here, so this must be a repeated value.
slice, ok := va.m[propnum].([]interface{})
if ok {
// already a slice, simply append.
slice = append(slice, value)
} else {
// single value currently, change to a slice.
slice = append(slice, va.m[propnum])
slice = append(slice, value)
}
va.m[propnum] = slice
} else {
va.m[propnum] = value
}
return nil
}
func (va *genericMapValueApplier) mapType5(propnum int, value uint32) error {
va.m[propnum] = value
return nil
}
// Decode decodes an arbitrary protocol buffers message into a map of field number to field value. It makes a best-effort attempt to use the most appropriate type for the values. Embedded structs, strings, integers and more are often decoded correctly. However due to the nature of protocol buffers, it is not always possible to do this perfectly.
func Decode(input []byte) (map[int]interface{}, error) {
m := make(map[int]interface{})
va := &genericMapValueApplier{m}
if err := decode(input, va); err != nil {
return nil, err
}
return m, nil
}
func decode(input []byte, va valueApplier) error {
r := &reader{buf: input}
for !r.done() {
val := r.decodeVarint()
wiretype := val & 0x07
k := int(val >> 3)
switch wiretype {
case 0: // varint value (int32, int64, uint32, uint64, sint32, sint64, bool, enum)
v := r.decodeVarint()
if err := va.mapType0(k, v); err != nil {
return err
}
case 1: // 64 bit value (fixed64, sfixed64, double)
v := r.readLeUint64()
if err := va.mapType1(k, v); err != nil {
return err
}
case 2: // length-delimited value (string, bytes, embedded messages, packed repeated fields)
v := r.readLenDelimValue()
if err := va.mapType2(k, v); err != nil {
return err
}
case 3: // Start group (groups are deprecated)
return ErrNotImplemented
case 4: // End group (groups are deprecated)
return ErrNotImplemented
case 5: // 32-bit value (fixed32, sfixed32, float)
v := r.readLeUint32()
if err := va.mapType5(k, v); err != nil {
return err
}
default:
return fmt.Errorf("unsupported wire type : %v", wiretype)
}
}
if r.err != nil {
return r.err
}
return nil
}
func copyBytes(in []byte) []byte {
out := make([]byte, len(in))
copy(out, in)
return out
}