mirror of
https://github.com/wader/fq.git
synced 2024-12-18 10:52:44 +03:00
152 lines
3.8 KiB
Go
152 lines
3.8 KiB
Go
package protobuf
|
|
|
|
import (
|
|
"github.com/wader/fq/format"
|
|
"github.com/wader/fq/format/registry"
|
|
"github.com/wader/fq/internal/num"
|
|
"github.com/wader/fq/pkg/decode"
|
|
)
|
|
|
|
func init() {
|
|
registry.MustRegister(&decode.Format{
|
|
Name: format.PROTOBUF,
|
|
Description: "Protobuf",
|
|
DecodeFn: protobufDecode,
|
|
})
|
|
}
|
|
|
|
const (
|
|
wireTypeVarint = 0
|
|
wireType64Bit = 1
|
|
wireTypeLengthDelimited = 2
|
|
wireType32Bit = 5
|
|
)
|
|
|
|
var wireTypeNames = map[uint64]string{
|
|
0: "Varint",
|
|
1: "64-bit",
|
|
2: "Length-delimited",
|
|
5: "32-bit",
|
|
}
|
|
|
|
func varInt(d *decode.D) uint64 {
|
|
var n uint64
|
|
for i := 0; ; i++ {
|
|
b := d.U8()
|
|
n = n | (b&0x7f)<<(7*i)
|
|
if b&0x80 == 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
func fieldVarInt(d *decode.D, name string) uint64 {
|
|
return d.FieldUFn(name, func() (uint64, decode.DisplayFormat, string) {
|
|
return varInt(d), decode.NumberDecimal, ""
|
|
})
|
|
}
|
|
|
|
func protobufDecodeField(d *decode.D, pbm *format.ProtoBufMessage) {
|
|
d.FieldStructFn("field", func(d *decode.D) {
|
|
keyN := fieldVarInt(d, "key_n")
|
|
fieldNumber := keyN >> 3
|
|
wireType := keyN & 0x7
|
|
d.FieldValueU("field_number", fieldNumber, "")
|
|
d.FieldValueU("wire_type", wireType, wireTypeNames[wireType])
|
|
|
|
var value uint64
|
|
var length uint64
|
|
var valueStart int64
|
|
switch wireType {
|
|
case wireTypeVarint:
|
|
value = fieldVarInt(d, "wire_value")
|
|
case wireType64Bit:
|
|
value = d.FieldU64("wire_value")
|
|
case wireTypeLengthDelimited:
|
|
length = fieldVarInt(d, "length")
|
|
valueStart = d.Pos()
|
|
d.FieldBitBufLen("wire_value", int64(length)*8)
|
|
case wireType32Bit:
|
|
value = d.FieldU32("wire_value")
|
|
}
|
|
|
|
if pbm != nil {
|
|
if pbf, ok := (*pbm)[int(fieldNumber)]; ok {
|
|
d.FieldValueStr("name", pbf.Name, "")
|
|
d.FieldValueStr("type", format.ProtoBufTypeNames[uint64(pbf.Type)], "")
|
|
|
|
switch pbf.Type {
|
|
case format.ProtoBufTypeInt32, format.ProtoBufTypeInt64:
|
|
v := num.ZigZag(value)
|
|
d.FieldValueS("value", v, "")
|
|
if len(pbf.Enums) > 0 {
|
|
d.FieldValueStr("enum", pbf.Enums[uint64(v)], "")
|
|
}
|
|
case format.ProtoBufTypeUInt32, format.ProtoBufTypeUInt64:
|
|
d.FieldValueU("value", value, "")
|
|
if len(pbf.Enums) > 0 {
|
|
d.FieldValueStr("enum", pbf.Enums[value], "")
|
|
}
|
|
case format.ProtoBufTypeSInt32, format.ProtoBufTypeSInt64:
|
|
// TODO: correct? 32 different?
|
|
v := num.TwosComplement(64, value)
|
|
d.FieldValueS("value", v, "")
|
|
if len(pbf.Enums) > 0 {
|
|
d.FieldValueStr("enum", pbf.Enums[uint64(v)], "")
|
|
}
|
|
case format.ProtoBufTypeBool:
|
|
d.FieldValueBool("value", value != 0, "")
|
|
case format.ProtoBufTypeEnum:
|
|
d.FieldValueStr("enum", pbf.Enums[value], "")
|
|
case format.ProtoBufTypeFixed64:
|
|
// TODO:
|
|
case format.ProtoBufTypeSFixed64:
|
|
// TODO:
|
|
case format.ProtoBufTypeDouble:
|
|
// TODO:
|
|
case format.ProtoBufTypeString:
|
|
d.FieldValueStr("value", string(d.BytesRange(valueStart, int(length))), "")
|
|
case format.ProtoBufTypeBytes:
|
|
d.FieldValueBytes("value", d.BytesRange(valueStart, int(length)), "")
|
|
case format.ProtoBufTypeMessage:
|
|
// TODO: test
|
|
d.DecodeLenFn(int64(length)*8, func(d *decode.D) {
|
|
protobufDecodeFields(d, &pbf.Message)
|
|
})
|
|
case format.ProtoBufTypePackedRepeated:
|
|
// TODO:
|
|
case format.ProtoBufTypeFixed32:
|
|
// TODO:
|
|
case format.ProtoBufTypeSFixed32:
|
|
// TODO:
|
|
case format.ProtoBufTypeFloat:
|
|
// TODO:
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func protobufDecodeFields(d *decode.D, pbm *format.ProtoBufMessage) {
|
|
d.FieldArrayFn("fields", func(d *decode.D) {
|
|
for d.BitsLeft() > 0 {
|
|
protobufDecodeField(d, pbm)
|
|
//break
|
|
}
|
|
})
|
|
}
|
|
|
|
func protobufDecode(d *decode.D, in interface{}) interface{} {
|
|
var pbm *format.ProtoBufMessage
|
|
pbi, ok := in.(format.ProtoBufIn)
|
|
if ok {
|
|
pbm = &pbi.Message
|
|
}
|
|
|
|
protobufDecodeFields(d, pbm)
|
|
|
|
return nil
|
|
}
|