github.com/philpearl/plenc@v0.0.15/plenccore/wire.go (about) 1 // Package plenccore describes the wire protocol used by plenc. You don't need to 2 // look at this unless you're implementing your own plenc CODEC 3 package plenccore 4 5 import "fmt" 6 7 // WireType represents a protobuf wire type. It's really all about how you can 8 // skip over fields in encoded data that aren't recognised because the field no 9 // longer exists in the struct. 10 type WireType int8 11 12 const ( 13 // WTVarInt signals a variable-length encoded integer. Signed integers are 14 // encoded with zig-zag encoding first. 15 WTVarInt WireType = iota 16 // WT64 signals a 64 bit value. Used for float64 17 WT64 18 // WTLength signals length-value data. Length is encoded as a varint and is 19 // a byte count. This is used for structs and strings, and for slices of 20 // types encoded using WTVarInt, WT64 or WT32 21 WTLength 22 // WTSlice re-uses the code point used for the deprecated 'StartGroup' wire 23 // type. It is used for slices of types implemented with WTLength. It is 24 // followed by a count of items in the slice encoded as a VarUint. Each 25 // entry is then encoded starting with its length encoded as a VarUint. 26 WTSlice 27 wtEndGroupDeprecated 28 // WT32 signals a 32 bit value. Used for float32 29 WT32 30 ) 31 32 // ReadTag reads the wire type and field index from data 33 func ReadTag(data []byte) (wt WireType, index, n int) { 34 v, n := ReadVarUint(data) 35 wt = WireType(v & 0x7) 36 index = int(v >> 3) 37 38 // fmt.Println("tag", wt, index, n) 39 return wt, index, n 40 } 41 42 // SizeTag determines the space needed to encode a tag 43 func SizeTag(wt WireType, index int) int { 44 tag := uint64(index<<3) | uint64(wt) 45 return SizeVarUint(tag) 46 } 47 48 // AppendTag encodes the tag and appends it to data 49 func AppendTag(data []byte, wt WireType, index int) []byte { 50 tag := uint64(index<<3) | uint64(wt) 51 return AppendVarUint(data, tag) 52 } 53 54 // Skip returns the size of a data item in the encoded data 55 func Skip(data []byte, wt WireType) (int, error) { 56 switch wt { 57 case WTVarInt: 58 for i, v := range data { 59 if v&0x80 == 0 { 60 return i + 1, nil 61 } 62 if i > 9 { 63 return 0, fmt.Errorf("VarInt does not terminate") 64 } 65 } 66 return 0, fmt.Errorf("unexpected end of data. %X", data) 67 case WT64: 68 return 8, nil 69 case WTLength: 70 l, n := ReadVarUint(data) 71 if n < 0 { 72 return 0, fmt.Errorf("corrupt data for WTLength tag") 73 } 74 return int(l) + n, nil 75 case WTSlice: 76 count, n := ReadVarUint(data) 77 if n < 0 { 78 return 0, fmt.Errorf("corrupt data for WTSkip tag") 79 } 80 // We now expect count length-value encoded items 81 offset := n 82 for i := uint64(0); i < count; i++ { 83 if offset >= len(data) { 84 return 0, fmt.Errorf("start of entry %d of WTSlice overruns data", i) 85 } 86 l, n := ReadVarUint(data[offset:]) 87 if n < 0 { 88 return 0, fmt.Errorf("corrupt length for entry %d of WTSlice", i) 89 } 90 offset += int(l) + n 91 } 92 return offset, nil 93 case WT32: 94 return 4, nil 95 } 96 return 0, fmt.Errorf("unsupported wire type %v", wt) 97 }