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

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

    9 gogoproto.testgen & gogoproto.testgen_all

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

    1. pb code:
    2. option (gogoproto.testgen_all) = true;
    3. option (gogoproto.benchgen_all) = true;
    4. message A {
    5. string msg = 1;
    6. }
    7. go code:
    8. package test
    9. import testing "testing"
    10. import math_rand "math/rand"
    11. import time "time"
    12. import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto"
    13. import encoding_json "encoding/json"
    14. func TestAProto(t *testing.T) {
    15. popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
    16. p := NewPopulatedA(popr, false)
    17. data, err := github_com_gogo_protobuf_proto.Marshal(p)
    18. if err != nil {
    19. panic(err)
    20. }
    21. msg := &A{}
    22. if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil {
    23. panic(err)
    24. }
    25. for i := range data {
    26. data[i] = byte(popr.Intn(256))
    27. }
    28. if !p.Equal(msg) {
    29. t.Fatalf("%#v !Proto %#v", msg, p)
    30. }
    31. }
    32. func BenchmarkAProtoMarshal(b *testing.B) {
    33. popr := math_rand.New(math_rand.NewSource(616))
    34. total := 0
    35. pops := make([]*A, 10000)
    36. for i := 0; i < 10000; i++ {
    37. pops[i] = NewPopulatedA(popr, false)
    38. }
    39. b.ResetTimer()
    40. for i := 0; i < b.N; i++ {
    41. data, err := github_com_gogo_protobuf_proto.Marshal(pops[i%10000])
    42. if err != nil {
    43. panic(err)
    44. }
    45. total += len(data)
    46. }
    47. b.SetBytes(int64(total / b.N))
    48. }
    49. func BenchmarkAProtoUnmarshal(b *testing.B) {
    50. popr := math_rand.New(math_rand.NewSource(616))
    51. total := 0
    52. datas := make([][]byte, 10000)
    53. for i := 0; i < 10000; i++ {
    54. data, err := github_com_gogo_protobuf_proto.Marshal(NewPopulatedA(popr, false))
    55. if err != nil {
    56. panic(err)
    57. }
    58. datas[i] = data
    59. }
    60. msg := &A{}
    61. b.ResetTimer()
    62. for i := 0; i < b.N; i++ {
    63. total += len(datas[i%10000])
    64. if err := github_com_gogo_protobuf_proto.Unmarshal(datas[i%10000], msg); err != nil {
    65. panic(err)
    66. }
    67. }
    68. b.SetBytes(int64(total / b.N))
    69. }
    70. func TestAJSON(t *testing.T) {
    71. popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
    72. p := NewPopulatedA(popr, true)
    73. jsondata, err := encoding_json.Marshal(p)
    74. if err != nil {
    75. panic(err)
    76. }
    77. msg := &A{}
    78. err = encoding_json.Unmarshal(jsondata, msg)
    79. if err != nil {
    80. panic(err)
    81. }
    82. if !p.Equal(msg) {
    83. t.Fatalf("%#v !Json Equal %#v", msg, p)
    84. }
    85. }
    86. func TestAProtoText(t *testing.T) {
    87. popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
    88. p := NewPopulatedA(popr, true)
    89. data := github_com_gogo_protobuf_proto.MarshalTextString(p)
    90. msg := &A{}
    91. if err := github_com_gogo_protobuf_proto.UnmarshalText(data, msg); err != nil {
    92. panic(err)
    93. }
    94. if !p.Equal(msg) {
    95. t.Fatalf("%#v !Proto %#v", msg, p)
    96. }
    97. }
    98. func TestAProtoCompactText(t *testing.T) {
    99. popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
    100. p := NewPopulatedA(popr, true)
    101. data := github_com_gogo_protobuf_proto.CompactTextString(p)
    102. msg := &A{}
    103. if err := github_com_gogo_protobuf_proto.UnmarshalText(data, msg); err != nil {
    104. panic(err)
    105. }
    106. if !p.Equal(msg) {
    107. t.Fatalf("%#v !Proto %#v", msg, p)
    108. }
    109. }

    10 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。

    1. pb code:
    2. option (gogoproto.marshaler_all) = true;
    3. option (gogoproto.sizer_all) = true;
    4. message A {
    5. string msg = 1;
    6. }
    7. go gode:
    8. type A struct {
    9. Msg *string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
    10. XXX_unrecognized []byte `json:"-"`
    11. }
    12. func (m *A) Size() (n int) {
    13. var l int
    14. _ = l
    15. if m.Msg != nil {
    16. l = len(*m.Msg)
    17. n += 1 + l + sovTest(uint64(l))
    18. }
    19. if m.XXX_unrecognized != nil {
    20. n += len(m.XXX_unrecognized)
    21. }
    22. return n
    23. }
    24. func sovTest(x uint64) (n int) {
    25. for {
    26. n++
    27. x >>= 7
    28. if x == 0 {
    29. break
    30. }
    31. }
    32. return n
    33. }
    34. func sozTest(x uint64) (n int) {
    35. return sovTest(uint64((x << 1) ^ uint64((int64(x) >> 63))))
    36. }
    37. func (m *A) Marshal() (data []byte, err error) {
    38. size := m.Size()
    39. data = make([]byte, size)
    40. n, err := m.MarshalTo(data)
    41. if err != nil {
    42. return nil, err
    43. }
    44. return data[:n], nil
    45. }
    46. func (m *A) MarshalTo(data []byte) (n int, err error) {
    47. var i int
    48. _ = i
    49. var l int
    50. _ = l
    51. if m.Msg != nil {
    52. data[i] = 0xa
    53. i++
    54. i = encodeVarintTest(data, i, uint64(len(*m.Msg)))
    55. i += copy(data[i:], *m.Msg)
    56. }
    57. if m.XXX_unrecognized != nil {
    58. i += copy(data[i:], m.XXX_unrecognized)
    59. }
    60. return i, nil
    61. }
    62. func encodeFixed64Test(data []byte, offset int, v uint64) int {
    63. data[offset] = uint8(v)
    64. data[offset+1] = uint8(v >> 8)
    65. data[offset+2] = uint8(v >> 16)
    66. data[offset+3] = uint8(v >> 24)
    67. data[offset+4] = uint8(v >> 32)
    68. data[offset+5] = uint8(v >> 40)
    69. data[offset+6] = uint8(v >> 48)
    70. data[offset+7] = uint8(v >> 56)
    71. return offset + 8
    72. }
    73. func encodeFixed32Test(data []byte, offset int, v uint32) int {
    74. data[offset] = uint8(v)
    75. data[offset+1] = uint8(v >> 8)
    76. data[offset+2] = uint8(v >> 16)
    77. data[offset+3] = uint8(v >> 24)
    78. return offset + 4
    79. }
    80. func encodeVarintTest(data []byte, offset int, v uint64) int {
    81. for v >= 1<<7 {
    82. data[offset] = uint8(v&0x7f | 0x80)
    83. v >>= 7
    84. offset++
    85. }
    86. data[offset] = uint8(v)
    87. return offset + 1
    88. }

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

    11 gogoprotobuf.unmarshaler & gogoprotobuf.unmarshaler_all

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

    1. pb code:
    2. option (gogoproto.unmarshaler_all) = true;
    3. message A {
    4. string msg = 1;
    5. }
    6. go code:
    7. type A struct {
    8. Msg *string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
    9. XXX_unrecognized []byte `json:"-"`
    10. }
    11. func (m *A) Unmarshal(data []byte) error {
    12. l := len(data)
    13. index := 0
    14. for index < l {
    15. var wire uint64
    16. for shift := uint(0); ; shift += 7 {
    17. if index >= l {
    18. return io.ErrUnexpectedEOF
    19. }
    20. b := data[index]
    21. index++
    22. wire |= (uint64(b) & 0x7F) << shift
    23. if b < 0x80 {
    24. break
    25. }
    26. }
    27. fieldNum := int32(wire >> 3)
    28. wireType := int(wire & 0x7)
    29. switch fieldNum {
    30. case 1:
    31. if wireType != 2 {
    32. return fmt.Errorf("proto: wrong wireType = %d for field Msg", wireType)
    33. }
    34. var stringLen uint64
    35. for shift := uint(0); ; shift += 7 {
    36. if index >= l {
    37. return io.ErrUnexpectedEOF
    38. }
    39. b := data[index]
    40. index++
    41. stringLen |= (uint64(b) & 0x7F) << shift
    42. if b < 0x80 {
    43. break
    44. }
    45. }
    46. postIndex := index + int(stringLen)
    47. if postIndex > l {
    48. return io.ErrUnexpectedEOF
    49. }
    50. s := string(data[index:postIndex])
    51. m.Msg = &s
    52. index = postIndex
    53. default:
    54. var sizeOfWire int
    55. for {
    56. sizeOfWire++
    57. wire >>= 7
    58. if wire == 0 {
    59. break
    60. }
    61. }
    62. index -= sizeOfWire
    63. skippy, err := github_com_gogo_protobuf_proto.Skip(data[index:])
    64. if err != nil {
    65. return err
    66. }
    67. if (index + skippy) > l {
    68. return io.ErrUnexpectedEOF
    69. }
    70. m.XXX_unrecognized = append(m.XXX_unrecognized, data[index:index+skippy]...)
    71. index += skippy
    72. }
    73. }
    74. return nil
    75. }

    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() 方法吧?

    12 equal & equal_all & verbose_equal & verbose_equal_all

    如果设定了 equal,则 gogo 为每个 message 生成一个 Equal method。若设定了 verbose_equal,则生成 VerboseEqual method。Equal 与 VerboseEqual 之间的差别在于,如果两个对象不等则 VerboseEqual 返回 error 不为 nil,这个 error 会详细说明两个对象差别在哪里,便于 debugging。

    如果设定了 testgen & testgen_all,则 gogo 会为这些 equal 函数生成测试函数。

    1. pb code:
    2. option (gogoproto.equal_all) = true;
    3. option (gogoproto.verbose_equal_all) = true;
    4. message B {
    5. optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
    6. repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false];
    7. }
    8. go code:
    9. func (this *B) VerboseEqual(that interface{}) error {
    10. if that == nil {
    11. if this == nil {
    12. return nil
    13. }
    14. return fmt2.Errorf("that == nil && this != nil")
    15. }
    16. that1, ok := that.(*B)
    17. if !ok {
    18. return fmt2.Errorf("that is not of type *B")
    19. }
    20. if that1 == nil {
    21. if this == nil {
    22. return nil
    23. }
    24. return fmt2.Errorf("that is type *B but is nil && this != nil")
    25. } else if this == nil {
    26. return fmt2.Errorf("that is type *B but is not nil && this == nil")
    27. }
    28. if !this.A.Equal(&that1.A) {
    29. return fmt2.Errorf("A this(%v) Not Equal that(%v)", this.A, that1.A)
    30. }
    31. if len(this.G) != len(that1.G) {
    32. return fmt2.Errorf("G this(%v) Not Equal that(%v)", len(this.G), len(that1.G))
    33. }
    34. for i := range this.G {
    35. if !this.G[i].Equal(that1.G[i]) {
    36. return fmt2.Errorf("G this[%v](%v) Not Equal that[%v](%v)", i, this.G[i], i, that1.G[i])
    37. }
    38. }
    39. if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) {
    40. return fmt2.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized)
    41. }
    42. return nil
    43. }
    44. func (this *B) Equal(that interface{}) bool {
    45. if that == nil {
    46. if this == nil {
    47. return true
    48. }
    49. return false
    50. }
    51. that1, ok := that.(*B)
    52. if !ok {
    53. return false
    54. }
    55. if that1 == nil {
    56. if this == nil {
    57. return true
    58. }
    59. return false
    60. } else if this == nil {
    61. return false
    62. }
    63. if !this.A.Equal(&that1.A) {
    64. return false
    65. }
    66. if len(this.G) != len(that1.G) {
    67. return false
    68. }
    69. for i := range this.G {
    70. if !this.G[i].Equal(that1.G[i]) {
    71. return false
    72. }
    73. }
    74. if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) {
    75. return false
    76. }
    77. return true
    78. }

    13 总结

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

    1. syntax = "proto3";
    2. package test;
    3. option (gogoproto.gostring_all) = true;
    4. option (gogoproto.equal_all) = true;
    5. option (gogoproto.verbose_equal_all) = true;
    6. // option (gogoproto.goproto_stringer_all) = false;
    7. // option (gogoproto.stringer_all) = true;
    8. // option (gogoproto.populate_all) = true;
    9. // option (gogoproto.testgen_all) = true;
    10. // option (gogoproto.benchgen_all) = true;
    11. option (gogoproto.marshaler_all) = true;
    12. option (gogoproto.sizer_all) = true;
    13. option (gogoproto.unmarshaler_all) = true;
    14. option (gogoproto.goproto_getters_all) = false;
    15. import "github.com/gogo/protobuf/gogoproto/gogo.proto";
    16. enum E {
    17. option (gogoproto.goproto_enum_prefix) = false;
    18. EA = 0;
    19. EB = 2;
    20. }
    21. message A {
    22. string msg = 1[(gogoproto.nullable) = false];
    23. }

    其编译命令为:

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

    写此文档的时候,得原公司 (写文档的时候还在公司) 同事陶春华兄大力相助, 在此致谢!2016/02/28。
    https://my.oschina.net/alexstocks/blog/387058