gogoprotobuf使用(下)

alexstocks · · 8924 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

声明:版权所有,谢绝转载。

   

承接上文《gogoprotobuf使用(上)》,继续说明gogoprotobuf的各个option。

8 gogoproto.testgen & gogoproto.testgen_all

 testgen选项为true,则gogo会为相应的message生成一个测试用例与性能测试用例。testgen_all则为相应的package level的option。

pb code:
option (gogoproto.testgen_all) = true;
option (gogoproto.benchgen_all) = true;

message A {
	string msg = 1;
}

go code:
package test

import testing "testing"
import math_rand "math/rand"
import time "time"
import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto"
import encoding_json "encoding/json"

func TestAProto(t *testing.T) {
	popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
	p := NewPopulatedA(popr, false)
	data, err := github_com_gogo_protobuf_proto.Marshal(p)
	if err != nil {
		panic(err)
	}
	msg := &A{}
	if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil {
		panic(err)
	}
	for i := range data {
		data[i] = byte(popr.Intn(256))
	}
	if !p.Equal(msg) {
		t.Fatalf("%#v !Proto %#v", msg, p)
	}
}

func BenchmarkAProtoMarshal(b *testing.B) {
	popr := math_rand.New(math_rand.NewSource(616))
	total := 0
	pops := make([]*A, 10000)
	for i := 0; i < 10000; i++ {
		pops[i] = NewPopulatedA(popr, false)
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		data, err := github_com_gogo_protobuf_proto.Marshal(pops[i%10000])
		if err != nil {
			panic(err)
		}
		total += len(data)
	}
	b.SetBytes(int64(total / b.N))
}

func BenchmarkAProtoUnmarshal(b *testing.B) {
	popr := math_rand.New(math_rand.NewSource(616))
	total := 0
	datas := make([][]byte, 10000)
	for i := 0; i < 10000; i++ {
		data, err := github_com_gogo_protobuf_proto.Marshal(NewPopulatedA(popr, false))
		if err != nil {
			panic(err)
		}
		datas[i] = data
	}
	msg := &A{}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		total += len(datas[i%10000])
		if err := github_com_gogo_protobuf_proto.Unmarshal(datas[i%10000], msg); err != nil {
			panic(err)
		}
	}
	b.SetBytes(int64(total / b.N))
}

func TestAJSON(t *testing.T) {
	popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
	p := NewPopulatedA(popr, true)
	jsondata, err := encoding_json.Marshal(p)
	if err != nil {
		panic(err)
	}
	msg := &A{}
	err = encoding_json.Unmarshal(jsondata, msg)
	if err != nil {
		panic(err)
	}
	if !p.Equal(msg) {
		t.Fatalf("%#v !Json Equal %#v", msg, p)
	}
}
func TestAProtoText(t *testing.T) {
	popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
	p := NewPopulatedA(popr, true)
	data := github_com_gogo_protobuf_proto.MarshalTextString(p)
	msg := &A{}
	if err := github_com_gogo_protobuf_proto.UnmarshalText(data, msg); err != nil {
		panic(err)
	}
	if !p.Equal(msg) {
		t.Fatalf("%#v !Proto %#v", msg, p)
	}
}

func TestAProtoCompactText(t *testing.T) {
	popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
	p := NewPopulatedA(popr, true)
	data := github_com_gogo_protobuf_proto.CompactTextString(p)
	msg := &A{}
	if err := github_com_gogo_protobuf_proto.UnmarshalText(data, msg); err != nil {
		panic(err)
	}
	if !p.Equal(msg) {
		t.Fatalf("%#v !Proto %#v", msg, p)
	}
}

9 gogoproto.marshaler & gogoproto.sizer  gogoproto.marshaler_all & gogoproto.sizer_all

   sizer选项为true的时候,gogo会为相应的message生成method:"func Size() int";当marshaler为true的时候,gogo会为相应的method生成method:func Marshal()([] byte, int),这个method会调用Size(),所以marshaler为true的时候,sizer也必须为true。

pb code:
option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;

message A {
	string msg = 1;
}

go gode:
type A struct {
	Msg              *string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
	XXX_unrecognized []byte  `json:"-"`
}

func (m *A) Size() (n int) {
	var l int
	_ = l
	if m.Msg != nil {
		l = len(*m.Msg)
		n += 1 + l + sovTest(uint64(l))
	}
	if m.XXX_unrecognized != nil {
		n += len(m.XXX_unrecognized)
	}
	return n
}

func sovTest(x uint64) (n int) {
	for {
		n++
		x >>= 7
		if x == 0 {
			break
		}
	}
	return n
}
func sozTest(x uint64) (n int) {
	return sovTest(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *A) Marshal() (data []byte, err error) {
	size := m.Size()
	data = make([]byte, size)
	n, err := m.MarshalTo(data)
	if err != nil {
		return nil, err
	}
	return data[:n], nil
}

func (m *A) MarshalTo(data []byte) (n int, err error) {
	var i int
	_ = i
	var l int
	_ = l
	if m.Msg != nil {
		data[i] = 0xa
		i++
		i = encodeVarintTest(data, i, uint64(len(*m.Msg)))
		i += copy(data[i:], *m.Msg)
	}
	if m.XXX_unrecognized != nil {
		i += copy(data[i:], m.XXX_unrecognized)
	}
	return i, nil
}

func encodeFixed64Test(data []byte, offset int, v uint64) int {
	data[offset] = uint8(v)
	data[offset+1] = uint8(v >> 8)
	data[offset+2] = uint8(v >> 16)
	data[offset+3] = uint8(v >> 24)
	data[offset+4] = uint8(v >> 32)
	data[offset+5] = uint8(v >> 40)
	data[offset+6] = uint8(v >> 48)
	data[offset+7] = uint8(v >> 56)
	return offset + 8
}
func encodeFixed32Test(data []byte, offset int, v uint32) int {
	data[offset] = uint8(v)
	data[offset+1] = uint8(v >> 8)
	data[offset+2] = uint8(v >> 16)
	data[offset+3] = uint8(v >> 24)
	return offset + 4
}
func encodeVarintTest(data []byte, offset int, v uint64) int {
	for v >= 1<<7 {
		data[offset] = uint8(v&0x7f | 0x80)
		v >>= 7
		offset++
	}
	data[offset] = uint8(v)
	return offset + 1
}

新的marshal函数要比goprotobuf的proto.Marshal()函数效率高,因为它没用用reflect接口。当

10 gogoprotobuf.unmarshaler & gogoprotobuf.unmarshaler_all

     当unmarshaler为true的时候,gogo会为相应的message生成method:func Unmarshal(data []byte) error,用以代替goprotobuf的proto.Unmarshal([]byte, *struct)函数。

pb code:

option (gogoproto.unmarshaler_all) = true;

message A {
	string msg = 1;
}

go code:

type A struct {
	Msg              *string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
	XXX_unrecognized []byte  `json:"-"`
}

func (m *A) Unmarshal(data []byte) error {
	l := len(data)
	index := 0
	for index < l {
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if index >= l {
				return io.ErrUnexpectedEOF
			}
			b := data[index]
			index++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		fieldNum := int32(wire >> 3)
		wireType := int(wire & 0x7)
		switch fieldNum {
		case 1:
			if wireType != 2 {
				return fmt.Errorf("proto: wrong wireType = %d for field Msg", wireType)
			}
			var stringLen uint64
			for shift := uint(0); ; shift += 7 {
				if index >= l {
					return io.ErrUnexpectedEOF
				}
				b := data[index]
				index++
				stringLen |= (uint64(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			postIndex := index + int(stringLen)
			if postIndex > l {
				return io.ErrUnexpectedEOF
			}
			s := string(data[index:postIndex])
			m.Msg = &s
			index = postIndex
		default:
			var sizeOfWire int
			for {
				sizeOfWire++
				wire >>= 7
				if wire == 0 {
					break
				}
			}
			index -= sizeOfWire
			skippy, err := github_com_gogo_protobuf_proto.Skip(data[index:])
			if err != nil {
				return err
			}
			if (index + skippy) > l {
				return io.ErrUnexpectedEOF
			}
			m.XXX_unrecognized = append(m.XXX_unrecognized, data[index:index+skippy]...)
			index += skippy
		}
	}
	return nil
}

gogo官方关于这个函数有如下说明:

Remember when using this code to call proto.Unmarshal. This will call m.Reset and invoke the generated Unmarshal method for you. If you call m.Unmarshal without m.Reset you could be merging protocol buffers.

             from:  https://godoc.org/code.google.com/p/gogoprotobuf/plugin/unmarshal#NewUnmarshal

其意思很明显,就是m (*A)调用m.Unmarshal方法之前,请先调用m.Reset()函数对其内容进行清空。否则,如果m连续调用了两次Unmarshal函数,则第二次反序列化出来的内容会被合并到第一次反序列化后的内容之后。

不过,一般情况下,很少会有人连续两次调用m.Unmarshal()方法吧?

11 总结

最后给出一个个人的gogoprotobuf文件样例:

syntax = "proto3";

package test;

option (gogoproto.gostring_all) = true;
option (gogoproto.equal_all) = true;
option (gogoproto.verbose_equal_all) = true;
// option (gogoproto.goproto_stringer_all) = false;
// option (gogoproto.stringer_all) =  true;
// option (gogoproto.populate_all) = true;
// option (gogoproto.testgen_all) = true;
// option (gogoproto.benchgen_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.goproto_getters_all) = false;

import "github.com/gogo/protobuf/gogoproto/gogo.proto";

enum E {
    option (gogoproto.goproto_enum_prefix) = false;
    EA = 0;
    EB = 2;
}

message A {
	string msg = 1[(gogoproto.nullable) = false];
}

其编译命令为:

#!/bin/sh
# proto.sh
gopath=$HOME/bin/go/src
gogopath=${gopath}/github.com/gogo/protobuf/protobuf
protoc --proto_path=$gopath:$gogopath:./ --gogo_out=Mcommon.proto=protocol/common:$1 $2



有疑问加站长微信联系(非本文作者)

本文来自:开源中国博客

感谢作者:alexstocks

查看原文:gogoprotobuf使用(下)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:701969077

8924 次点击  
加入收藏 微博
上一篇:gogoprotobuf使用
下一篇:errors _ golang
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传