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  }