pax_global_header00006660000000000000000000000064145511743400014516gustar00rootroot0000000000000052 comment=52d3ac47440b52fd42bf4bbb5bacb358a85fccc3 easyproto-0.1.4/000077500000000000000000000000001455117434000135455ustar00rootroot00000000000000easyproto-0.1.4/LICENSE000066400000000000000000000247631455117434000145660ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2023-2024 VictoriaMetrics, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. easyproto-0.1.4/README.md000066400000000000000000000150601455117434000150260ustar00rootroot00000000000000[![GoDoc](https://godoc.org/github.com/VictoriaMetrics/easyproto?status.svg)](http://godoc.org/github.com/VictoriaMetrics/easyproto) # easyproto Package [github.com/VictoriaMetrics/easyproto](http://godoc.org/github.com/VictoriaMetrics/easyproto) provides simple building blocks for marshaling and unmarshaling of [protobuf](https://protobuf.dev/) messages with [proto3 encoding](https://protobuf.dev/programming-guides/encoding/). ## Features - There is no need in [protoc](https://grpc.io/docs/protoc-installation/) or [go generate](https://go.dev/blog/generate) - just write simple maintainable code for marshaling and unmarshaling protobuf messages. - `easyproto` doesn't increase your binary size by tens of megabytes unlike traditional `protoc`-combiled code may do. - `easyproto` allows writing zero-alloc code for marshaling and unmarshaling of arbitrary complex protobuf messages. See [examples](#examples). ## Restrictions - It supports only [proto3 encoding](https://protobuf.dev/programming-guides/encoding/), e.g. it doesn't support `proto2` encoding features such as [proto2 groups](https://protobuf.dev/programming-guides/proto2/#groups). - It doesn't provide helpers for marshaling and unmarshaling of [well-known types](https://protobuf.dev/reference/protobuf/google.protobuf/), since they aren't used too much in practice. ## Examples Suppose you need marshaling and unmarshaling of the following `timeseries` message: ```proto message timeseries { string name = 1; repeated sample samples = 2; } message sample { double value = 1; int64 timestamp = 2; } ``` At first let's create the corresponding data structures in Go: ```go type Timeseries struct { Name string Samples []Sample } type Sample struct { Value float64 Timestamp int64 } ``` Since you write the code on yourself without any `go generate` and `protoc` invocations, you are free to use arbitrary fields and methods in these structs. You can also specify the most suitable types for these fields. For example, the `Sample` struct may be written as the following if you need an ability to detect empty values and timestamps: ```go type Sample struct { Value *float64 Timestamp *int64 } ``` * [How to marshal `Timeseries` struct to protobuf message](#marshaling) * [How to unmarshal protobuf message to `Timeseries` struct](#unmarshaling) ### Marshaling The following code can be used for marshaling `Timeseries` struct to protobuf message: ```go import ( "github.com/VictoriaMetrics/easyproto" ) // MarshalProtobuf marshals ts into protobuf message, appends this message to dst and returns the result. // // This function doesn't allocate memory on repeated calls. func (ts *Timeseries) MarshalProtobuf(dst []byte) []byte { m := mp.Get() ts.marshalProtobuf(m.MessageMarshaler()) dst = m.Marshal(dst) mp.Put(m) return dst } func (ts *Timeseries) marshalProtobuf(mm *easyproto.MessageMarshaler) { mm.AppendString(1, ts.Name) for _, s := range ts.Samples { s.marshalProtobuf(mm.AppendMessage(2)) } } func (s *Sample) marshalProtobuf(mm *easyproto.MessageMarshaler) { mm.AppendDouble(1, s.Value) mm.AppendInt64(2, s.Timestamp) } var mp easyproto.MarshalerPool ``` Note that you are free to modify this code according to your needs, since you write and maintain it. For example, you can construct arbitrary protobuf messages on the fly without the need to prepare the source struct for marshaling: ```go func CreateProtobufMessageOnTheFly() []byte { // Dynamically construct timeseries message with 10 samples var m easyproto.Marshaler mm := m.MessageMarshaler() mm.AppendString(1, "foo") for i := 0; i < 10; i++ { mmSample := mm.AppendMessage(2) mmSample.AppendDouble(1, float64(i)/10) mmSample.AppendInt64(2, int64(i)*1000) } return m.Marshal(nil) } ``` This may be useful in tests. ### Unmarshaling The following code can be used for unmarshaling [`timeseries` message](#examples) into `Timeseries` struct: ```go // UnmarshalProtobuf unmarshals ts from protobuf message at src. func (ts *Timeseries) UnmarshalProtobuf(src []byte) (err error) { // Set default Timeseries values ts.Name = "" ts.Samples = ts.Samples[:0] // Parse Timeseries message at src var fc easyproto.FieldContext for len(src) > 0 { src, err = fc.NextField(src) if err != nil { return fmt.Errorf("cannot read next field in Timeseries message") } switch fc.FieldNum { case 1: name, ok := fc.String() if !ok { return fmt.Errorf("cannot read Timeseries name") } // name refers to src. This means that the name changes when src changes. // Make a copy with strings.Clone(name) if needed. ts.Name = name case 2: data, ok := fc.MessageData() if !ok { return fmt.Errorf("cannot read Timeseries sample data") } ts.Samples = append(ts.Samples, Sample{}) s := &ts.Samples[len(ts.Samples)-1] if err := s.UnmarshalProtobuf(data); err != nil { return fmt.Errorf("cannot unmarshal sample: %w", err) } } } return nil } // UnmarshalProtobuf unmarshals s from protobuf message at src. func (s *Sample) UnmarshalProtobuf(src []byte) (err error) { // Set default Sample values s.Value = 0 s.Timestamp = 0 // Parse Sample message at src var fc easyproto.FieldContext for len(src) > 0 { src, err = fc.NextField(src) if err != nil { return fmt.Errorf("cannot read next field in sample") } switch fc.FieldNum { case 1: value, ok := fc.Double() if !ok { return fmt.Errorf("cannot read sample value") } s.Value = value case 2: timestamp, ok := fc.Int64() if !ok { return fmt.Errorf("cannot read sample timestamp") } s.Timestamp = timestamp } } return nil } ``` You are free to modify this code according to your needs, since you wrote it and you maintain it. It is possible to extract the needed data from arbitrary protobuf messages without the need to create a destination struct. For example, the following code extracts `timeseries` name from protobuf message, while ignoring all the other fields: ```go func GetTimeseriesName(src []byte) (name string, err error) { var fc easyproto.FieldContext for len(src) > 0 { src, err = fc.NextField(src) if src != nil { return "", fmt.Errorf("cannot read the next field") } if fc.FieldNum == 1 { name, ok := fc.String() if !ok { return "", fmt.Errorf("cannot read timeseries name") } // Return a copy of name, since name refers to src. return strings.Clone(name), nil } } return "", fmt.Errorf("timeseries name isn't found in the message") } ``` ## Users `easyproto` is used in the following projects: - [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics) easyproto-0.1.4/doc.go000066400000000000000000000002711455117434000146410ustar00rootroot00000000000000// Package easyproto provides building blocks for marshaling and unmarshaling protobuf v3 messages // according to https://protobuf.dev/programming-guides/encoding/ . package easyproto easyproto-0.1.4/easyproto_example_test.go000066400000000000000000000070501455117434000206750ustar00rootroot00000000000000package easyproto_test import ( "fmt" "github.com/VictoriaMetrics/easyproto" ) // Timeseries is a named time series. // // It has the following protobuf v3 definition: // // message timeseries { // string name = 1; // repeated sample samples = 2; // } type Timeseries struct { Name string Samples []Sample } // Sample represents a sample for the named time series. // // It has the following protobuf v3 definition: // // message sample { // double value = 1; // int64 timestamp = 2; // } type Sample struct { Value float64 Timestamp int64 } // MarshalProtobuf marshals ts into protobuf message, appends this message to dst and returns the result. // // This function doesn't allocate memory on repeated calls. func (ts *Timeseries) MarshalProtobuf(dst []byte) []byte { m := mp.Get() ts.marshalProtobuf(m.MessageMarshaler()) dst = m.Marshal(dst) mp.Put(m) return dst } func (ts *Timeseries) marshalProtobuf(mm *easyproto.MessageMarshaler) { mm.AppendString(1, ts.Name) for _, s := range ts.Samples { s.marshalProtobuf(mm.AppendMessage(2)) } } func (s *Sample) marshalProtobuf(mm *easyproto.MessageMarshaler) { mm.AppendDouble(1, s.Value) mm.AppendInt64(2, s.Timestamp) } var mp easyproto.MarshalerPool // UnmarshalProtobuf unmarshals ts from protobuf message at src. func (ts *Timeseries) UnmarshalProtobuf(src []byte) (err error) { // Set default Timeseries values ts.Name = "" ts.Samples = ts.Samples[:0] // Parse Timeseries message at src var fc easyproto.FieldContext for len(src) > 0 { src, err = fc.NextField(src) if err != nil { return fmt.Errorf("cannot read next field in Timeseries message") } switch fc.FieldNum { case 1: name, ok := fc.String() if !ok { return fmt.Errorf("cannot read Timeseries name") } // name refers to src. This means that the name changes when src changes. // Make a copy with strings.Clone(name) if needed. ts.Name = name case 2: data, ok := fc.MessageData() if !ok { return fmt.Errorf("cannot read Timeseries sample data") } ts.Samples = append(ts.Samples, Sample{}) s := &ts.Samples[len(ts.Samples)-1] if err := s.UnmarshalProtobuf(data); err != nil { return fmt.Errorf("cannot unmarshal sample: %w", err) } } } return nil } // UnmarshalProtobuf unmarshals s from protobuf message at src. func (s *Sample) UnmarshalProtobuf(src []byte) (err error) { // Set default Sample values s.Value = 0 s.Timestamp = 0 // Parse Sample message at src var fc easyproto.FieldContext for len(src) > 0 { src, err = fc.NextField(src) if err != nil { return fmt.Errorf("cannot read next field in sample") } switch fc.FieldNum { case 1: value, ok := fc.Double() if !ok { return fmt.Errorf("cannot read sample value") } s.Value = value case 2: timestamp, ok := fc.Int64() if !ok { return fmt.Errorf("cannot read sample timestamp") } s.Timestamp = timestamp } } return nil } func Example() { ts := &Timeseries{ Name: "foo", Samples: []Sample{ { Value: 123, Timestamp: -453426, }, { Value: -234.344, Timestamp: 23328434, }, }, } data := ts.MarshalProtobuf(nil) var tsNew Timeseries if err := tsNew.UnmarshalProtobuf(data); err != nil { fmt.Printf("unexpected error: %s\n", err) return } fmt.Printf("name: %s\n", tsNew.Name) fmt.Printf("samples:\n") for _, s := range tsNew.Samples { fmt.Printf(" {value:%v, timestamp:%d}\n", s.Value, s.Timestamp) } // Output: // name: foo // samples: // {value:123, timestamp:-453426} // {value:-234.344, timestamp:23328434} } easyproto-0.1.4/easyproto_test.go000066400000000000000000001565021455117434000171710ustar00rootroot00000000000000package easyproto import ( "bytes" "fmt" "reflect" "testing" ) func TestZigZagInt32(t *testing.T) { f := func(i32 int32) { t.Helper() u32 := encodeZigZagInt32(i32) n := decodeZigZagInt32(u32) if n != i32 { t.Fatalf("unexpected value after zig-zag coding; got %d; want %d", n, i32) } } f(0) f(1) f(-1) f(2) f(-2) f(1 << 7) f(-1 << 7) f(1 << 8) f(-1 << 8) f(1<<31 - 1) f(-1 << 31) f(-1<<31 + 1) } func TestZigZagInt64(t *testing.T) { f := func(i64 int64) { t.Helper() u64 := encodeZigZagInt64(i64) n := decodeZigZagInt64(u64) if n != i64 { t.Fatalf("unexpected value after zig-zag coding; got %d; want %d", n, i64) } } f(0) f(1) f(-1) f(2) f(-2) f(1 << 7) f(-1 << 7) f(1 << 8) f(-1 << 8) f(1<<63 - 1) f(-1 << 63) f(-1<<63 + 1) } func TestMarshaler(t *testing.T) { m := mp.Get() mm1 := m.MessageMarshaler() mm2 := m.MessageMarshaler() if mm1 != mm2 { t.Fatalf("unexpected mm2=%p; must equal to mm1=%p", mm2, mm1) } mp.Put(m) } func TestMarshalUnmarshalMessageInt32(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []int32{0, -1, 1, 1<<31 - 1, -1 << 31, -1<<31 + 1} { // Marshal int32 m := mp.Get() mm := m.MessageMarshaler() mm.AppendInt32(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal int32 var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Int32() if !ok { t.Fatalf("unexpected error in Int32()") } if v != value { t.Fatalf("unexpected value; got %d; want %d", v, value) } } } } func TestMarshalUnmarshalMessageInt64(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []int64{0, -1, 1, 1<<63 - 1, -1 << 63, -1<<63 + 1} { // Marshal int64 m := mp.Get() mm := m.MessageMarshaler() mm.AppendInt64(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal int64 var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Int64() if !ok { t.Fatalf("unexpected error in Int64()") } if v != value { t.Fatalf("unexpected value; got %d; want %d", v, value) } } } } func TestMarshalUnmarshalMessageUint32(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []uint32{0, 1, 2, 1<<32 - 1, 1<<32 - 2} { // Marshal uint32 m := mp.Get() mm := m.MessageMarshaler() mm.AppendUint32(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal uint32 var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Uint32() if !ok { t.Fatalf("unexpected error in Uint32()") } if v != value { t.Fatalf("unexpected value; got %d; want %d", v, value) } } } } func TestMarshalUnmarshalMessageUint64(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []uint64{0, 1, 2, 1<<64 - 1, 1<<64 - 2} { // Marshal uint64 m := mp.Get() mm := m.MessageMarshaler() mm.AppendUint64(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal uint64 var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Uint64() if !ok { t.Fatalf("unexpected error in Uint64()") } if v != value { t.Fatalf("unexpected value; got %d; want %d", v, value) } } } } func TestMarshalUnmarshalMessageSint32(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []int32{0, -1, 1, 1<<31 - 1, -1 << 31, -1<<31 + 1} { // Marshal sint32 m := mp.Get() mm := m.MessageMarshaler() mm.AppendSint32(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal sint32 var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Sint32() if !ok { t.Fatalf("unexpected error in Sint32()") } if v != value { t.Fatalf("unexpected value; got %d; want %d", v, value) } } } } func TestMarshalUnmarshalMessageSint64(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []int64{0, -1, 1, 1<<63 - 1, -1 << 63, -1<<63 + 1} { // Marshal sint64 m := mp.Get() mm := m.MessageMarshaler() mm.AppendSint64(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal sint64 var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Sint64() if !ok { t.Fatalf("unexpected error in Sint64()") } if v != value { t.Fatalf("unexpected value; got %d; want %d", v, value) } } } } func TestMarshalUnmarshalMessageBool(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []bool{true, false} { // Marshal bool m := mp.Get() mm := m.MessageMarshaler() mm.AppendBool(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal bool var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Bool() if !ok { t.Fatalf("unexpected error in Bool()") } if v != value { t.Fatalf("unexpected value; got %v; want %v", v, value) } } } } func TestMarshalUnmarshalMessageFixed64(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []uint64{0, 1, 2, 1<<64 - 1, 1<<64 - 2} { // Marshal fixed64 m := mp.Get() mm := m.MessageMarshaler() mm.AppendFixed64(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal fixed64 var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Fixed64() if !ok { t.Fatalf("unexpected error in Fixed64()") } if v != value { t.Fatalf("unexpected value; got %d; want %d", v, value) } } } } func TestMarshalUnmarshalMessageSfixed64(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []int64{0, -1, 1, 1<<63 - 1, -1 << 63, -1<<63 + 1} { // Marshal sfixed64 m := mp.Get() mm := m.MessageMarshaler() mm.AppendSfixed64(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal sfixed64 var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Sfixed64() if !ok { t.Fatalf("unexpected error in Sfixed64()") } if v != value { t.Fatalf("unexpected value; got %d; want %d", v, value) } } } } func TestMarshalUnmarshalMessageDouble(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []float64{0, -1, 1, 1.0 / 3, -1.0 / 3, 1.234e43, -323.34387e-4} { // Marshal double m := mp.Get() mm := m.MessageMarshaler() mm.AppendDouble(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal double var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Double() if !ok { t.Fatalf("unexpected error in Double()") } if v != value { t.Fatalf("unexpected value; got %v; want %v", v, value) } } } } func TestMarshalUnmarshalMessageString(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []string{"", "foo", "foo bar baz"} { // Marshal string m := mp.Get() mm := m.MessageMarshaler() mm.AppendString(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal string var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.String() if !ok { t.Fatalf("cannot get string field") } if v != value { t.Fatalf("unexpected value; got %q; want %q", v, value) } } } } func TestMarshalUnmarshalMessageBytes(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []string{"", "foo", "foo bar baz"} { // Marshal bytes m := mp.Get() mm := m.MessageMarshaler() mm.AppendBytes(fieldNum, []byte(value)) data := m.Marshal(nil) mp.Put(m) // Unmarshal bytes var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Bytes() if !ok { t.Fatalf("cannot get bytes field") } if string(v) != value { t.Fatalf("unexpected value; got %q; want %q", v, value) } } } } func TestMarshalUnmarshalMessageFixed32(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []uint32{0, 1, 2, 1<<32 - 1, 1<<32 - 2} { // Marshal fixed32 m := mp.Get() mm := m.MessageMarshaler() mm.AppendFixed32(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal fixed32 var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Fixed32() if !ok { t.Fatalf("unexpected error in Fixed32()") } if v != value { t.Fatalf("unexpected value; got %d; want %d", v, value) } } } } func TestMarshalUnmarshalMessageSfixed32(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []int32{0, -1, 1, 1<<31 - 1, -1 << 31, -1<<31 + 1} { // Marshal sfixed32 m := mp.Get() mm := m.MessageMarshaler() mm.AppendSfixed32(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal sfixed32 var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Sfixed32() if !ok { t.Fatalf("unexpected error in Sfixed32()") } if v != value { t.Fatalf("unexpected value; got %d; want %d", v, value) } } } } func TestMarshalUnmarshalMessageFloat(t *testing.T) { for _, fieldNum := range []uint32{0, 1, 10, 1000, 1<<32 - 1} { for _, value := range []float32{0, -1, 1, 1.0 / 3, -1.0 / 3, 1.234e20, -323.34387e-4} { // Marshal float m := mp.Get() mm := m.MessageMarshaler() mm.AppendFloat(fieldNum, value) data := m.Marshal(nil) mp.Put(m) // Unmarshal float var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) > 0 { t.Fatalf("unexpected non-empty tail left with %d bytes", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } v, ok := fc.Float() if !ok { t.Fatalf("unexpected error in Float()") } if v != value { t.Fatalf("unexpected value; got %v; want %v", v, value) } } } } func TestMarshalUnmarshalEmptyInt32s(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendInt32s(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackInt32s(nil) if !ok { t.Fatalf("unexpected error in UnpackInt32s()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%d", vs) } } func TestMarshalUnmarshalEmptyInt64s(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendInt64s(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackInt64s(nil) if !ok { t.Fatalf("unexpected error in UnpackInt64s()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%d", vs) } } func TestMarshalUnmarshalEmptyUint32s(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendUint32s(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackUint32s(nil) if !ok { t.Fatalf("unexpected error in UnpackUint32s()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%d", vs) } } func TestMarshalUnmarshalEmptyUint64s(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendUint64s(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackUint64s(nil) if !ok { t.Fatalf("unexpected error in UnpackUint64s()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%d", vs) } } func TestMarshalUnmarshalEmptySint32s(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendSint32s(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackSint32s(nil) if !ok { t.Fatalf("unexpected error in UnpackSint32s()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%d", vs) } } func TestMarshalUnmarshalEmptySint64s(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendSint64s(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackSint64s(nil) if !ok { t.Fatalf("unexpected error in UnpackSint64s()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%d", vs) } } func TestMarshalUnmarshalEmptyBools(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendBools(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackBools(nil) if !ok { t.Fatalf("unexpected error in UnpackBools()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%v", vs) } } func TestMarshalUnmarshalEmptyFixed64s(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendFixed64s(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackFixed64s(nil) if !ok { t.Fatalf("unexpected error in UnpackFixed64s()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%d", vs) } } func TestMarshalUnmarshalEmptySfixed64s(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendSfixed64s(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackSfixed64s(nil) if !ok { t.Fatalf("unexpected error in UnpackSfixed64s()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%d", vs) } } func TestMarshalUnmarshalEmptyDoubles(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendDoubles(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackDoubles(nil) if !ok { t.Fatalf("unexpected error in UnpackDobules()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%v", vs) } } func TestMarshalUnmarshalEmptyFixed32s(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendFixed32s(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackFixed32s(nil) if !ok { t.Fatalf("unexpected error in UnpackFixed32s()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%d", vs) } } func TestMarshalUnmarshalEmptySfixed32s(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendSfixed32s(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackSfixed32s(nil) if !ok { t.Fatalf("unexpected error in UnpackSfixed32s()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%d", vs) } } func TestMarshalUnmarshalEmptyFloats(t *testing.T) { const fieldNum = 42 m := mp.Get() mm := m.MessageMarshaler() mm.AppendFloats(fieldNum, nil) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail with len(tail)=%d", len(tail)) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } vs, ok := fc.UnpackFloats(nil) if !ok { t.Fatalf("unexpected error in UnpackFloats()") } if len(vs) > 0 { t.Fatalf("unexpected non-empty vs=%v", vs) } } func TestMarshalUnmarshalInt32s(t *testing.T) { const fieldNum = 42 values := []int32{0, -1, 1, 1<<31 - 1, -1 << 31, -1<<31 + 1, 123} // marshal int32 values m := mp.Get() mm := m.MessageMarshaler() mm.AppendInt32s(fieldNum, values[:len(values)-1]) mm.AppendInt32(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal int32 values var vs []int32 var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackInt32s(vs) if !ok { t.Fatalf("unexpected error in UnpackInt32s()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %d; want %d", vs, values) } } func TestMarshalUnmarshalInt64s(t *testing.T) { const fieldNum = 42 values := []int64{0, -1, 1, 1<<63 - 1, -1 << 63, -1<<63 + 1, 123} // marshal int64 values m := mp.Get() mm := m.MessageMarshaler() mm.AppendInt64s(fieldNum, values[:len(values)-1]) mm.AppendInt64(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal int64 values var vs []int64 var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackInt64s(vs) if !ok { t.Fatalf("unexpected error in UnpackInt64s()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %d; want %d", vs, values) } } func TestMarshalUnmarshalUint32s(t *testing.T) { const fieldNum = 42 values := []uint32{0, 1, 1<<32 - 1, 1<<32 - 2, 123} // marshal uint32 values m := mp.Get() mm := m.MessageMarshaler() mm.AppendUint32s(fieldNum, values[:len(values)-1]) mm.AppendUint32(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal uint32 values var vs []uint32 var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackUint32s(vs) if !ok { t.Fatalf("unexpected error in UnpackUint32s()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %d; want %d", vs, values) } } func TestMarshalUnmarshalUint64s(t *testing.T) { const fieldNum = 42 values := []uint64{0, 1, 1<<64 - 1, 1<<64 - 2, 123} // marshal uint64 values m := mp.Get() mm := m.MessageMarshaler() mm.AppendUint64s(fieldNum, values[:len(values)-1]) mm.AppendUint64(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal uint64 values var vs []uint64 var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackUint64s(vs) if !ok { t.Fatalf("unexpected error in UnpackUint64s()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %d; want %d", vs, values) } } func TestMarshalUnmarshalSint32s(t *testing.T) { const fieldNum = 42 values := []int32{0, -1, 1, 1<<31 - 1, -1 << 31, -1<<31 + 1, 123} // marshal sint32 values m := mp.Get() mm := m.MessageMarshaler() mm.AppendSint32s(fieldNum, values[:len(values)-1]) mm.AppendSint32(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal sint32 values var vs []int32 var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackSint32s(vs) if !ok { t.Fatalf("unexpected error in UnpackSint32s()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %d; want %d", vs, values) } } func TestMarshalUnmarshalSint64s(t *testing.T) { const fieldNum = 42 values := []int64{0, -1, 1, 1<<63 - 1, -1 << 63, -1<<63 + 1, 123} // marshal sint64 values m := mp.Get() mm := m.MessageMarshaler() mm.AppendSint64s(fieldNum, values[:len(values)-1]) mm.AppendSint64(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal sint64 values var vs []int64 var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackSint64s(vs) if !ok { t.Fatalf("unexpected error in UnpackSint64s()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %d; want %d", vs, values) } } func TestMarshalUnmarshalBools(t *testing.T) { const fieldNum = 42 values := []bool{true, false, false, true, true} // marshal bool values m := mp.Get() mm := m.MessageMarshaler() mm.AppendBools(fieldNum, values[:len(values)-1]) mm.AppendBool(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal bool values var vs []bool var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackBools(vs) if !ok { t.Fatalf("unexpected error in UnpackBools()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %v; want %v", vs, values) } } func TestMarshalUnmarshalFixed64s(t *testing.T) { const fieldNum = 42 values := []uint64{0, 1, 1<<64 - 1, 1<<64 - 1, 123, 1 << 32, 1<<64 - 2} // marshal fixed64 values m := mp.Get() mm := m.MessageMarshaler() mm.AppendFixed64s(fieldNum, values[:len(values)-1]) mm.AppendFixed64(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal fixed64 values var vs []uint64 var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackFixed64s(vs) if !ok { t.Fatalf("unexpected error in UnpackFixed64s()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %d; want %d", vs, values) } } func TestMarshalUnmarshalSfixed64s(t *testing.T) { const fieldNum = 42 values := []int64{0, -1, 1, 1<<63 - 1, -1 << 63, -1<<63 + 1, 123} // marshal sfixed64 values m := mp.Get() mm := m.MessageMarshaler() mm.AppendSfixed64s(fieldNum, values[:len(values)-1]) mm.AppendSfixed64(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal sfixed64 values var vs []int64 var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackSfixed64s(vs) if !ok { t.Fatalf("unexpected error in UnpackSfixed64s()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %d; want %d", vs, values) } } func TestMarshalUnmarshalDoubles(t *testing.T) { const fieldNum = 42 values := []float64{0, -1, 1, 1<<63 - 1, -1 << 63, -1<<63 + 1, 123, 1.34, -34.34e2, 1.0 / 3, -1.0 / 3} // marshal double values m := mp.Get() mm := m.MessageMarshaler() mm.AppendDoubles(fieldNum, values[:len(values)-1]) mm.AppendDouble(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal double values var vs []float64 var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackDoubles(vs) if !ok { t.Fatalf("unexpected error in UnpackDoubles()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %v; want %v", vs, values) } } func TestMarshalUnmarshalFixed32s(t *testing.T) { const fieldNum = 42 values := []uint32{0, 1, 1<<32 - 1, 1<<32 - 1, 123, 1 << 16, 1<<32 - 2} // marshal fixed32 values m := mp.Get() mm := m.MessageMarshaler() mm.AppendFixed32s(fieldNum, values[:len(values)-1]) mm.AppendFixed32(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal fixed32 values var vs []uint32 var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackFixed32s(vs) if !ok { t.Fatalf("unexpected error in UnpackFixed32s()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %d; want %d", vs, values) } } func TestMarshalUnmarshalSfixed32s(t *testing.T) { const fieldNum = 42 values := []int32{0, 1, -1, 1<<31 - 1, -1 << 31, 123, 1 << 16, -1<<31 + 1} // marshal sfixed32 values m := mp.Get() mm := m.MessageMarshaler() mm.AppendSfixed32s(fieldNum, values[:len(values)-1]) mm.AppendSfixed32(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal sfixed32 values var vs []int32 var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackSfixed32s(vs) if !ok { t.Fatalf("unexpected error in UnpackSfixed32s()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %d; want %d", vs, values) } } func TestMarshalUnmarshalFloats(t *testing.T) { const fieldNum = 42 values := []float32{0, -1, 1, 1<<63 - 1, -1 << 63, -1<<63 + 1, 123, 1.34, -34.34e2, 1.0 / 3, -1.0 / 3} // marshal float values m := mp.Get() mm := m.MessageMarshaler() mm.AppendFloats(fieldNum, values[:len(values)-1]) mm.AppendFloat(fieldNum, values[len(values)-1]) data := m.Marshal(nil) mp.Put(m) // Unmarshal float values var vs []float32 var fc FieldContext for len(data) > 0 { tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error in NextField(): %s", err) } if fc.FieldNum != fieldNum { t.Fatalf("unexpected fieldNum; got %d; want %d", fc.FieldNum, fieldNum) } var ok bool vs, ok = fc.UnpackFloats(vs) if !ok { t.Fatalf("unexpected error in UnpackFloats()") } data = tail } if !reflect.DeepEqual(vs, values) { t.Fatalf("unexpected values; got %v; want %v", vs, values) } } func TestMarshalUnmarshalMessage(t *testing.T) { type label struct { Name string Value string } type sample struct { Value float64 Timestamp int64 } type timeseries struct { Labels []label Samples []sample } type writeRequest struct { Timeseries []timeseries } const timeseriesFieldNum = 1 const labelFieldNum = 1 const sampleFieldNum = 2 const labelNameFieldNum = 1 const labelValueFieldNum = 2 const sampleValueFieldNum = 1 const sampleTimestampFieldNum = 2 marshalWriteRequest := func(wr *writeRequest) []byte { m := mp.Get() mm := m.MessageMarshaler() for _, ts := range wr.Timeseries { tsm := mm.AppendMessage(timeseriesFieldNum) for _, label := range ts.Labels { lm := tsm.AppendMessage(labelFieldNum) lm.AppendString(labelNameFieldNum, label.Name) lm.AppendString(labelValueFieldNum, label.Value) } for _, sample := range ts.Samples { sm := tsm.AppendMessage(sampleFieldNum) sm.AppendDouble(sampleValueFieldNum, sample.Value) sm.AppendInt64(sampleTimestampFieldNum, sample.Timestamp) } } data := m.Marshal(nil) mp.Put(m) return data } unmarshalLabel := func(src []byte) (label, error) { var lbl label var fc FieldContext for len(src) > 0 { tail, err := fc.NextField(src) if err != nil { return lbl, fmt.Errorf("cannot obtain next field in label: %w", err) } switch fc.FieldNum { case labelNameFieldNum: name, ok := fc.String() if !ok { return lbl, fmt.Errorf("cannot unmarshal label name") } lbl.Name = name case labelValueFieldNum: value, ok := fc.String() if !ok { return lbl, fmt.Errorf("cannot unmarshal label value") } lbl.Value = value default: return lbl, fmt.Errorf("unexpected fieldNum=%d in label", fc.FieldNum) } src = tail } return lbl, nil } unmarshalSample := func(src []byte) (sample, error) { var smpl sample var fc FieldContext for len(src) > 0 { tail, err := fc.NextField(src) if err != nil { return smpl, fmt.Errorf("cannot obtain next field in sample: %w", err) } switch fc.FieldNum { case sampleValueFieldNum: value, ok := fc.Double() if !ok { return smpl, fmt.Errorf("cannot unmarshal sample value") } smpl.Value = value case sampleTimestampFieldNum: timestamp, ok := fc.Int64() if !ok { return smpl, fmt.Errorf("cannot unmarshal sample timestamp") } smpl.Timestamp = timestamp default: return smpl, fmt.Errorf("unexpectef fieldNum=%d in sample", fc.FieldNum) } src = tail } return smpl, nil } unmarshalTimeseries := func(src []byte) (timeseries, error) { var ts timeseries var fc FieldContext for len(src) > 0 { tail, err := fc.NextField(src) if err != nil { return ts, fmt.Errorf("cannot obtain next field in timeseries: %w", err) } switch fc.FieldNum { case labelFieldNum: data, ok := fc.MessageData() if !ok { return ts, fmt.Errorf("cannot obtain message data for label") } label, err := unmarshalLabel(data) if err != nil { return ts, fmt.Errorf("cannot unmarshal label: %w", err) } ts.Labels = append(ts.Labels, label) case sampleFieldNum: data, ok := fc.MessageData() if !ok { return ts, fmt.Errorf("cannot obtain message data for sample") } sample, err := unmarshalSample(data) if err != nil { return ts, fmt.Errorf("cannot unmarshal sample: %w", err) } ts.Samples = append(ts.Samples, sample) default: return ts, fmt.Errorf("unexpected fieldNum=%d in timeseries", fc.FieldNum) } src = tail } return ts, nil } unmarshalWriteRequest := func(src []byte) (*writeRequest, error) { var tss []timeseries var fc FieldContext for len(src) > 0 { tail, err := fc.NextField(src) if err != nil { return nil, fmt.Errorf("cannot obtain next field in writeRequest: %w", err) } switch fc.FieldNum { case timeseriesFieldNum: data, ok := fc.MessageData() if !ok { return nil, fmt.Errorf("cannot obtain message data for timeseries") } ts, err := unmarshalTimeseries(data) if err != nil { return nil, fmt.Errorf("cannot unmarshal timeseries: %w", err) } tss = append(tss, ts) default: return nil, fmt.Errorf("unexpected fieldNum=%d in writeRequest", fc.FieldNum) } src = tail } wr := &writeRequest{ Timeseries: tss, } return wr, nil } wr := &writeRequest{ Timeseries: []timeseries{ { Labels: []label{ { Name: "foo", Value: "bar", }, { Name: "abc", Value: "wwe", }, }, Samples: []sample{ { Value: 123.323, Timestamp: 883434, }, { Value: -1884.4329, Timestamp: -883432, }, }, }, }, } data := marshalWriteRequest(wr) result, err := unmarshalWriteRequest(data) if err != nil { t.Fatalf("unexpected error during unmarshaling: %s", err) } if !reflect.DeepEqual(wr, result) { t.Fatalf("unexpected result; got %v; want %v", result, wr) } } func TestMarshalUnmarshalEmptyMessage(t *testing.T) { const firstMessageFieldNum = 1 const secondMessageFieldNum = 1 m := mp.Get() mm := m.MessageMarshaler() msg := mm.AppendMessage(firstMessageFieldNum) _ = msg.AppendMessage(secondMessageFieldNum) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("cannot obtain the next field in the first message: %s", err) } if fc.FieldNum != firstMessageFieldNum { t.Fatalf("unexpected field num in the first message; got %d; want %d", fc.FieldNum, firstMessageFieldNum) } if len(tail) != 0 { t.Fatalf("unexpected tail left after the first message; len(tail)=%d", len(tail)) } data, ok := fc.MessageData() if !ok { t.Fatalf("unexpected error when obtaining the first message data") } tail, err = fc.NextField(data) if err != nil { t.Fatalf("cannot obtain the next field in the second message: %s", err) } if fc.FieldNum != secondMessageFieldNum { t.Fatalf("unexpected field num in the second message; got %d; want %d", fc.FieldNum, secondMessageFieldNum) } if len(tail) != 0 { t.Fatalf("unexpected tail left after the second message; len(tail)=%d", len(tail)) } data, ok = fc.MessageData() if !ok { t.Fatalf("unexpected error when obtaining the second message data") } if len(data) != 0 { t.Fatalf("unexpected length for the second message; got %d; want 0", len(data)) } } func TestMarshalUnmarshalEmptyMessageAndNextField(t *testing.T) { m := mp.Get() mm := m.MessageMarshaler() _ = mm.AppendMessage(1) mm.AppendString(2, "foo") data := m.Marshal(nil) mp.Put(m) var fc FieldContext // read empty message tail, err := fc.NextField(data) if err != nil { t.Fatalf("cannot read next field: %s", err) } if fc.FieldNum != 1 { t.Fatalf("unexpected fieldNum; got %d; want 1", fc.FieldNum) } data, ok := fc.MessageData() if !ok { t.Fatalf("cannot get message data") } if len(data) != 0 { t.Fatalf("unexpected non-empty message data: %X", data) } // read the next string tail, err = fc.NextField(tail) if err != nil { t.Fatalf("cannot read next field: %s", err) } if fc.FieldNum != 2 { t.Fatalf("unexpected fieldNum; got %d; want 2", fc.FieldNum) } s, ok := fc.String() if !ok { t.Fatalf("cannot get string") } if s != "foo" { t.Fatalf("unexpected string read; got %q; want %q", s, "foo") } // make sure there is an empty tail left if len(tail) != 0 { t.Fatalf("unexpected non-empty tail left: %X", tail) } } func TestMarshalInterleaved(t *testing.T) { m := mp.Get() mm := m.MessageMarshaler() mm.AppendInt64(1, 123) msg := mm.AppendMessage(42) mm.AppendDouble(3, 4.2) msg.AppendInt64(100, 1234) mm.AppendBool(2, true) data := m.Marshal(nil) mp.Put(m) var fc FieldContext var err error expectNextField := func(expectedFieldNum uint32) { t.Helper() data, err = fc.NextField(data) if err != nil { t.Fatalf("cannot get next field: %s", err) } if fc.FieldNum != expectedFieldNum { t.Fatalf("unexpected field num; got %d; want %d", fc.FieldNum, expectedFieldNum) } } expectNextField(1) i64, ok := fc.Int64() if !ok { t.Fatalf("cannot read int64") } if i64 != 123 { t.Fatalf("unexpected int64 value; got %d; want 123", i64) } expectNextField(42) msgData, ok := fc.MessageData() if !ok { t.Fatalf("cannot read message data") } msgData, err = fc.NextField(msgData) if err != nil { t.Fatalf("cannot get next field in embedded message: %s", err) } if fc.FieldNum != 100 { t.Fatalf("unexpected field num; got %d; want 100", fc.FieldNum) } i64, ok = fc.Int64() if !ok { t.Fatalf("cannot read int64") } if i64 != 1234 { t.Fatalf("unexpected int64 value; got %d; want 1234", i64) } if len(msgData) > 0 { t.Fatalf("unexpected data left in msgData=%X", msgData) } expectNextField(3) d, ok := fc.Double() if !ok { t.Fatalf("cannot read double: %s", err) } if d != 4.2 { t.Fatalf("unexpected double value; got %v; want 4.2", d) } expectNextField(2) v, ok := fc.Bool() if !ok { t.Fatalf("cannot read bool: %s", err) } if !v { t.Fatalf("unexpected bool value; got false; want true") } if len(data) > 0 { t.Fatalf("unexpected data left: %X", data) } } func TestNextFieldFailure(t *testing.T) { f := func(data []byte) { t.Helper() var fc FieldContext _, err := fc.NextField(data) if err == nil { t.Fatalf("expecting non-nil error") } } // empty message f(nil) // incorrectly encoded message tag f([]byte{0xff}) // too big fieldNum f([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}) // incorrectly encoded varint f([]byte{1 << 3, 0xff}) // too small i64 f([]byte{byte(1<<3 | wireTypeI64), 0xff}) // too small i32 f([]byte{byte(1<<3 | wireTypeI32), 0xff}) // incorrectly encoded len for wireTypeLen f([]byte{byte(1<<3 | wireTypeLen), 0xff}) // too small message for wireTypelen f([]byte{byte(1<<3 | wireTypeLen), 0x01}) f([]byte{0xff, byte(1<<3 | wireTypeLen), 0x01}) f([]byte{byte(1<<3 | wireTypeLen), 0xff, 0x7f}) // unknown wireType f([]byte{1<<3 | 7}) } func TestFieldContextWrongWireType(t *testing.T) { m := mp.Get() mm := m.MessageMarshaler() mm.AppendFixed32(42, 8932) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error: %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail; len(tail)=%d", len(tail)) } i32, ok := fc.Int32() if ok { t.Fatalf("expecting non-nil error") } if i32 != 0 { t.Fatalf("unexpected i32=%d; want 0", i32) } i64, ok := fc.Int64() if ok { t.Fatalf("expecting non-nil error") } if i64 != 0 { t.Fatalf("unexpected i64=%d; want 0", i64) } u32, ok := fc.Uint32() if ok { t.Fatalf("expecting non-nil error") } if u32 != 0 { t.Fatalf("unexpected u32=%d; want 0", u32) } u64, ok := fc.Uint64() if ok { t.Fatalf("expecting non-nil error") } if u64 != 0 { t.Fatalf("unexpected n=%d; want 0", u64) } i32, ok = fc.Sint32() if ok { t.Fatalf("expecting non-nil error") } if i32 != 0 { t.Fatalf("unexpected i32=%d; want 0", i32) } i64, ok = fc.Sint64() if ok { t.Fatalf("expecting non-nil error") } if i64 != 0 { t.Fatalf("unexpected n=%d; want 0", i64) } b, ok := fc.Bool() if ok { t.Fatalf("expecting non-nil error") } if b { t.Fatalf("unexpected b=true; want false") } u64, ok = fc.Fixed64() if ok { t.Fatalf("expecting non-nil error") } if u64 != 0 { t.Fatalf("unexpected u64=%d; want 0", u64) } i64, ok = fc.Sfixed64() if ok { t.Fatalf("expecting non-nil error") } if i64 != 0 { t.Fatalf("unexpected i64=%d; want 0", i64) } f64, ok := fc.Double() if ok { t.Fatalf("expecting non-nil error") } if f64 != 0 { t.Fatalf("unexpected f64=%v; want 0", f64) } s, ok := fc.String() if ok { t.Fatalf("expecting missing string") } if s != "" { t.Fatalf("unexpected s=%q; want %q", s, "") } bts, ok := fc.Bytes() if ok { t.Fatalf("expecting missing bytes") } if bts != nil { t.Fatalf("unexpected bts=%q; want nil", bts) } msgData, ok := fc.MessageData() if ok { t.Fatalf("expecting missing message data") } if msgData != nil { t.Fatalf("unexpected msgData=%q; want nil", msgData) } if _, ok := fc.UnpackInt32s(nil); ok { t.Fatalf("expecting non-nil error") } if _, ok := fc.UnpackInt64s(nil); ok { t.Fatalf("expecting non-nil error") } if _, ok := fc.UnpackUint32s(nil); ok { t.Fatalf("expecting non-nil error") } if _, ok := fc.UnpackUint64s(nil); ok { t.Fatalf("expecting non-nil error") } if _, ok := fc.UnpackSint32s(nil); ok { t.Fatalf("expecting non-nil error") } if _, ok := fc.UnpackSint64s(nil); ok { t.Fatalf("expecting non-nil error") } if _, ok := fc.UnpackBools(nil); ok { t.Fatalf("expecting non-nil error") } if _, ok := fc.UnpackFixed64s(nil); ok { t.Fatalf("expecting non-nil error") } if _, ok := fc.UnpackSfixed64s(nil); ok { t.Fatalf("expecting non-nil error") } if _, ok := fc.UnpackDoubles(nil); ok { t.Fatalf("expecting non-nil error") } m = mp.Get() mm = m.MessageMarshaler() mm.AppendFixed64(42, 8932) data = m.Marshal(data[:0]) mp.Put(m) tail, err = fc.NextField(data) if err != nil { t.Fatalf("unexpected error: %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail; len(tail)=%d", len(tail)) } u32, ok = fc.Fixed32() if ok { t.Fatalf("expecting non-nil error") } if u32 != 0 { t.Fatalf("unexpected u32=%d; want 0", u32) } i32, ok = fc.Sfixed32() if ok { t.Fatalf("expecting non-nil error") } if i32 != 0 { t.Fatalf("unexpected i32=%d; want 0", i32) } f32, ok := fc.Float() if ok { t.Fatalf("expecting non-nil error") } if f32 != 0 { t.Fatalf("unexpected f32=%v; want 0", f32) } if _, ok := fc.UnpackFixed32s(nil); ok { t.Fatalf("expecting non-nil error") } if _, ok := fc.UnpackSfixed32s(nil); ok { t.Fatalf("expecting non-nil error") } if _, ok := fc.UnpackFloats(nil); ok { t.Fatalf("expecting non-nil error") } } func TestDecodeIntOverflow(t *testing.T) { u64 := uint64(1<<64 - 1) m := mp.Get() mm := m.MessageMarshaler() mm.AppendUint64(1, u64) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error: %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail; len(tail)=%d", len(tail)) } i32, ok := fc.Int32() if ok { t.Fatalf("expecting non-nil error") } if i32 != 0 { t.Fatalf("unexpected i32=%d; want 0", i32) } u32, ok := fc.Uint32() if ok { t.Fatalf("expecting non-nil error") } if u32 != 0 { t.Fatalf("unexpected u32=%d; want 0", u32) } i32, ok = fc.Sint32() if ok { t.Fatalf("expecting non-nil error") } if i32 != 0 { t.Fatalf("unexpected i32=%d; want 0", i32) } b, ok := fc.Bool() if ok { t.Fatalf("expecting non-nil error") } if b { t.Fatalf("unexpected b=true; want false") } if _, ok := fc.UnpackInt32s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackUint32s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackSint32s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackBools(nil); ok { t.Fatalf("expecting non-nil err") } m = mp.Get() mm = m.MessageMarshaler() mm.AppendUint64s(1, []uint64{u64}) data = m.Marshal(nil) mp.Put(m) tail, err = fc.NextField(data) if err != nil { t.Fatalf("unexpected error: %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail; len(tail)=%d", len(tail)) } if _, ok := fc.UnpackInt32s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackUint32s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackSint32s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackBools(nil); ok { t.Fatalf("expecting non-nil err") } } func TestUnmarshalWithoutMessageMarshaler(t *testing.T) { m := mp.Get() data := m.Marshal(nil) mp.Put(m) if len(data) > 0 { t.Fatalf("unexpected non-empty marshaled data returned: %X", data) } } func TestDecodeArrayInvalidVarint(t *testing.T) { m := mp.Get() mm := m.MessageMarshaler() mm.AppendUint64s(1, []uint64{0}) data := m.Marshal(nil) mp.Put(m) data[len(data)-1] |= 0x80 var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error: %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail; len(tail)=%d", len(tail)) } if _, ok := fc.UnpackInt32s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackInt64s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackUint32s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackUint64s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackSint32s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackSint64s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackBools(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackFixed64s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackSfixed64s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackDoubles(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackFixed32s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackSfixed32s(nil); ok { t.Fatalf("expecting non-nil err") } if _, ok := fc.UnpackFloats(nil); ok { t.Fatalf("expecting non-nil err") } } func TestMarshalWithLen(t *testing.T) { m := mp.Get() // Verify marshaling of empty message data := m.MarshalWithLen(nil) if !bytes.Equal(data, []byte{0}) { t.Fatalf("unexpected data for empty message; got %X; want 00", data) } // Verify marshaling of complext message m.Reset() mm := m.MessageMarshaler() mm.AppendString(1, "foo") mm.AppendBool(2, true) mm.AppendDouble(3, 123.456) mm.AppendInt64(4, -1234) data = m.MarshalWithLen(nil) mp.Put(m) msgLen, tail, ok := UnmarshalMessageLen(data) if !ok { t.Fatalf("cannot read message length") } data = tail if msgLen != len(data) { t.Fatalf("unexpected message length read; got %d; want %d", msgLen, len(data)) } var fc FieldContext var err error data, err = fc.NextField(data) if err != nil { t.Fatalf("unexpected error: %s", err) } if fc.FieldNum != 1 { t.Fatalf("unexpected field num read; got %d; want 1", fc.FieldNum) } s, ok := fc.String() if !ok { t.Fatalf("cannot read string") } if s != "foo" { t.Fatalf("unexpected string read; got %q; want %q", s, "foo") } data, err = fc.NextField(data) if err != nil { t.Fatalf("unexpected error: %s", err) } if fc.FieldNum != 2 { t.Fatalf("unexpected field num read; got %d; want 2", fc.FieldNum) } v, ok := fc.Bool() if !ok { t.Fatalf("cannot read bool") } if !v { t.Fatalf("unexpected bool read; got false; want true") } data, err = fc.NextField(data) if err != nil { t.Fatalf("unexpected error: %s", err) } if fc.FieldNum != 3 { t.Fatalf("unexpected field num read; got %d; want 3", fc.FieldNum) } d, ok := fc.Double() if !ok { t.Fatalf("cannot read double") } if d != 123.456 { t.Fatalf("unexpected double value read; got %v; want %v", d, 123.456) } data, err = fc.NextField(data) if err != nil { t.Fatalf("unexpected error: %s", err) } if fc.FieldNum != 4 { t.Fatalf("unexpected field num read; got %d; want 4", fc.FieldNum) } i64, ok := fc.Int64() if !ok { t.Fatalf("cannot read int64") } if i64 != -1234 { t.Fatalf("unexpected int64 read; got %d; want %d", i64, -1234) } if len(data) != 0 { t.Fatalf("unexpected tail left: %X", data) } } func TestMarshalLongMessage(t *testing.T) { b := make([]byte, 1024) m := mp.Get() mm := m.MessageMarshaler() msg := mm.AppendMessage(1230) msg.AppendBytes(234, b) data := m.Marshal(nil) mp.Put(m) var fc FieldContext tail, err := fc.NextField(data) if err != nil { t.Fatalf("unexpected error: %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail: %X", tail) } if fc.FieldNum != 1230 { t.Fatalf("unexpected fieldNum; got %d; want 1230", fc.FieldNum) } msgData, ok := fc.MessageData() if !ok { t.Fatalf("cannot read message data") } tail, err = fc.NextField(msgData) if err != nil { t.Fatalf("cannot read messge field: %s", err) } if len(tail) != 0 { t.Fatalf("unexpected non-empty tail after message field: %X", tail) } if fc.FieldNum != 234 { t.Fatalf("unexpected fieldNum; got %d; want 234", fc.FieldNum) } b1, ok := fc.Bytes() if !ok { t.Fatalf("cannot read bytes") } if !bytes.Equal(b, b1) { t.Fatalf("unexpected bytes read\ngot\n%X\nwant\n%X", b1, b) } } func TestUnmarshalMessageLenFailure(t *testing.T) { f := func(data []byte) { t.Helper() _, _, ok := UnmarshalMessageLen(data) if ok { t.Fatalf("expecting failure") } } // Empty data f(nil) // Invalid encoding f([]byte{0xff}) // Too big message length f([]byte{0xff, 0xff, 0xff, 0xff, 0x08}) } func TestUnmarshalMessageLenSuccess(t *testing.T) { f := func(data []byte, msgLenExpected int, tailExpected []byte) { t.Helper() msgLen, tail, ok := UnmarshalMessageLen(data) if !ok { t.Fatalf("cannot unmarshal message length") } if msgLen != msgLenExpected { t.Fatalf("unexpected msgLen; got %d; want %d", msgLen, msgLenExpected) } if !bytes.Equal(tail, tailExpected) { t.Fatalf("unexpected tail; got %X; want %X", tail, tailExpected) } } f([]byte{0x7f}, 0x7f, nil) f([]byte{0x80, 0x00, 0xab}, 0, []byte{0xab}) f([]byte{0x80, 0x80, 0x00, 0xab}, 0, []byte{0xab}) f([]byte{0xff, 0x7f, 0x00, 0xab}, (1<<14)-1, []byte{0x00, 0xab}) f([]byte{0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0xab}, (1<<31)-1, []byte{0x00, 0xab}) } var mp MarshalerPool easyproto-0.1.4/easyproto_timing_test.go000066400000000000000000000117641455117434000205400ustar00rootroot00000000000000package easyproto import ( "fmt" "sync" "testing" ) func BenchmarkMarshalComplexMessage(b *testing.B) { const seriesCount = 1_000 b.ReportAllocs() b.SetBytes(seriesCount) b.RunParallel(func(pb *testing.PB) { var buf []byte for pb.Next() { buf = marshalComplexMessage(buf[:0], seriesCount) } }) } func BenchmarkUnmarshalComplexMessage(b *testing.B) { const seriesCount = 1_000 data := marshalComplexMessage(nil, seriesCount) b.ReportAllocs() b.SetBytes(seriesCount) b.RunParallel(func(pb *testing.PB) { for pb.Next() { unmarshalComplexMessage(data) } }) } func marshalComplexMessage(dst []byte, seriesCount int) []byte { m := mp.Get() mm := m.MessageMarshaler() for i := 0; i < seriesCount; i++ { mmTimeseries := mm.AppendMessage(1) for j := 0; j < 20; j++ { mmLabel := mmTimeseries.AppendMessage(1) mmLabel.AppendString(1, "instance") mmLabel.AppendString(2, "foo-bar-baz-aaa-bbb") } for j := 0; j < 1; j++ { mmSample := mmTimeseries.AppendMessage(2) mmSample.AppendDouble(1, float64(j)+1.23) mmSample.AppendInt64(2, int64(j)*73287) } } dst = m.Marshal(dst) mp.Put(m) return dst } func unmarshalComplexMessage(src []byte) { type label struct { name string value string } type sample struct { value float64 timestamp int64 } type timeseries struct { labels []label samples []sample } type writeRequest struct { tss []timeseries } resetTimeseries := func(ts *timeseries) { labels := ts.labels for i := range labels { labels[i] = label{} } ts.labels = labels[:0] samples := ts.samples for i := range samples { samples[i] = sample{} } ts.samples = samples[:0] } resetWriteRequest := func(wr *writeRequest) { tss := wr.tss for i := range tss { resetTimeseries(&tss[i]) } wr.tss = tss[:0] } unmarshalSample := func(smpl *sample, src []byte) error { var fc FieldContext for len(src) > 0 { tail, err := fc.NextField(src) if err != nil { return fmt.Errorf("cannot get next field: %w", err) } switch fc.FieldNum { case 1: value, ok := fc.Double() if !ok { return fmt.Errorf("cannot unmarshal sample value") } smpl.value = value case 2: timestamp, ok := fc.Int64() if !ok { return fmt.Errorf("cannot unmarshal timestamp") } smpl.timestamp = timestamp } src = tail } return nil } unmarshalLabel := func(lbl *label, src []byte) error { var fc FieldContext for len(src) > 0 { tail, err := fc.NextField(src) if err != nil { return fmt.Errorf("cannot get next field: %w", err) } switch fc.FieldNum { case 1: name, ok := fc.String() if !ok { return fmt.Errorf("cannot unmarshal label name") } lbl.name = name case 2: value, ok := fc.String() if !ok { return fmt.Errorf("cannot unmarshal label value") } lbl.value = value } src = tail } return nil } unmarshalTimeseries := func(ts *timeseries, src []byte) error { labels := ts.labels samples := ts.samples var fc FieldContext for len(src) > 0 { tail, err := fc.NextField(src) if err != nil { return fmt.Errorf("cannot get next field: %w", err) } switch fc.FieldNum { case 1: data, ok := fc.MessageData() if !ok { return fmt.Errorf("cannot get label data") } if len(labels) < cap(labels) { labels = labels[:len(labels)+1] } else { labels = append(labels, label{}) } label := &labels[len(labels)-1] if err := unmarshalLabel(label, data); err != nil { return fmt.Errorf("cannot unmarshal label: %w", err) } case 2: data, ok := fc.MessageData() if !ok { return fmt.Errorf("cannot get sample data") } if len(samples) < cap(samples) { samples = samples[:len(samples)+1] } else { samples = append(samples, sample{}) } sample := &samples[len(samples)-1] if err := unmarshalSample(sample, data); err != nil { return fmt.Errorf("cannot unmarshal sample: %w", err) } } src = tail } ts.labels = labels ts.samples = samples return nil } unmarshalWriteRequest := func(wr *writeRequest, src []byte) error { tss := wr.tss var fc FieldContext for len(src) > 0 { tail, err := fc.NextField(src) if err != nil { return fmt.Errorf("cannot get next field: %w", err) } data, ok := fc.MessageData() if !ok { return fmt.Errorf("cannot get message data") } switch fc.FieldNum { case 1: if len(tss) < cap(tss) { tss = tss[:len(tss)+1] } else { tss = append(tss, timeseries{}) } ts := &tss[len(tss)-1] if err := unmarshalTimeseries(ts, data); err != nil { return fmt.Errorf("cannot unmarshal timeseries: %w", err) } } src = tail } wr.tss = tss return nil } v := writeRequestPool.Get() if v == nil { v = &writeRequest{} } wr := v.(*writeRequest) if err := unmarshalWriteRequest(wr, src); err != nil { panic(fmt.Errorf("cannot unmarshal writeRequest: %w", err)) } resetWriteRequest(wr) writeRequestPool.Put(wr) } var writeRequestPool sync.Pool easyproto-0.1.4/go.mod000066400000000000000000000000651455117434000146540ustar00rootroot00000000000000module github.com/VictoriaMetrics/easyproto go 1.18 easyproto-0.1.4/reader.go000066400000000000000000000423311455117434000153410ustar00rootroot00000000000000package easyproto import ( "encoding/binary" "fmt" "math" "unsafe" ) // FieldContext represents a single protobuf-encoded field after NextField() call. type FieldContext struct { // FieldNum is the number of protobuf field read after NextField() call. FieldNum uint32 // wireType is the wire type for the given field wireType wireType // data is probobuf-encoded field data for wireType=wireTypeLen data []byte // intValue contains int value for wireType!=wireTypeLen intValue uint64 } // NextField reads the next field from protobuf-encoded src. // // It returns the tail left after reading the next field from src. // // It is unsafe modifying src while FieldContext is in use. func (fc *FieldContext) NextField(src []byte) ([]byte, error) { if len(src) >= 2 { n := uint16(src[0])<<8 | uint16(src[1]) if (n&0x8080 == 0) && (n&0x0700 == (uint16(wireTypeLen) << 8)) { // Fast path - read message with the length smaller than 0x80 bytes. msgLen := int(n & 0xff) src = src[2:] if len(src) < msgLen { return src, fmt.Errorf("cannot read field for from %d bytes; need at least %d bytes", len(src), msgLen) } fc.FieldNum = uint32(n >> (8 + 3)) fc.wireType = wireTypeLen fc.data = src[:msgLen] src = src[msgLen:] return src, nil } } // Read field tag. See https://protobuf.dev/programming-guides/encoding/#structure if len(src) == 0 { return src, fmt.Errorf("cannot unmarshal field from empty message") } var fieldNum uint64 tag := uint64(src[0]) if tag < 0x80 { src = src[1:] fieldNum = tag >> 3 } else { var offset int tag, offset = binary.Uvarint(src) if offset <= 0 { return src, fmt.Errorf("cannot unmarshal field tag from uvarint") } src = src[offset:] fieldNum = tag >> 3 if fieldNum > math.MaxUint32 { return src, fmt.Errorf("fieldNum=%d is bigger than uint32max=%d", fieldNum, uint64(math.MaxUint32)) } } wt := wireType(tag & 0x07) fc.FieldNum = uint32(fieldNum) fc.wireType = wt // Read the remaining data if wt == wireTypeLen { u64, offset := binary.Uvarint(src) if offset <= 0 { return src, fmt.Errorf("cannot read message length for field #%d", fieldNum) } src = src[offset:] if uint64(len(src)) < u64 { return src, fmt.Errorf("cannot read data for field #%d from %d bytes; need at least %d bytes", fieldNum, len(src), u64) } fc.data = src[:u64] src = src[u64:] return src, nil } if wt == wireTypeVarint { u64, offset := binary.Uvarint(src) if offset <= 0 { return src, fmt.Errorf("cannot read varint after field tag for field #%d", fieldNum) } src = src[offset:] fc.intValue = u64 return src, nil } if wt == wireTypeI64 { if len(src) < 8 { return src, fmt.Errorf("cannot read i64 for field #%d", fieldNum) } u64 := binary.LittleEndian.Uint64(src) src = src[8:] fc.intValue = u64 return src, nil } if wt == wireTypeI32 { if len(src) < 4 { return src, fmt.Errorf("cannot read i32 for field #%d", fieldNum) } u32 := binary.LittleEndian.Uint32(src) src = src[4:] fc.intValue = uint64(u32) return src, nil } return src, fmt.Errorf("unknown wireType=%d", wt) } // UnmarshalMessageLen unmarshals protobuf message length from src. // // It returns the tail left after unmarshaling message length from src. // // It is expected that src is marshaled with Marshaler.MarshalWithLen(). // // False is returned if message length cannot be unmarshaled from src. func UnmarshalMessageLen(src []byte) (int, []byte, bool) { u64, offset := binary.Uvarint(src) if offset <= 0 { return 0, src, false } src = src[offset:] if u64 > math.MaxInt32 { return 0, src, false } return int(u64), src, true } // wireType is the type of of protobuf-encoded field // // See https://protobuf.dev/programming-guides/encoding/#structure type wireType byte const ( // VARINT type - one of int32, int64, uint32, uint64, sint32, sint64, bool, enum wireTypeVarint = wireType(0) // I64 type wireTypeI64 = wireType(1) // Len type wireTypeLen = wireType(2) // I32 type wireTypeI32 = wireType(5) ) // Int32 returns int32 value for fc. // // False is returned if fc doesn't contain int32 value. func (fc *FieldContext) Int32() (int32, bool) { if fc.wireType != wireTypeVarint { return 0, false } return getInt32(fc.intValue) } // Int64 returns int64 value for fc. // // False is returned if fc doesn't contain int64 value. func (fc *FieldContext) Int64() (int64, bool) { if fc.wireType != wireTypeVarint { return 0, false } return int64(fc.intValue), true } // Uint32 returns uint32 value for fc. // // False is returned if fc doesn't contain uint32 value. func (fc *FieldContext) Uint32() (uint32, bool) { if fc.wireType != wireTypeVarint { return 0, false } return getUint32(fc.intValue) } // Uint64 returns uint64 value for fc. // // False is returned if fc doesn't contain uint64 value. func (fc *FieldContext) Uint64() (uint64, bool) { if fc.wireType != wireTypeVarint { return 0, false } return fc.intValue, true } // Sint32 returns sint32 value for fc. // // False is returned if fc doesn't contain sint32 value. func (fc *FieldContext) Sint32() (int32, bool) { if fc.wireType != wireTypeVarint { return 0, false } u32, ok := getUint32(fc.intValue) if !ok { return 0, false } i32 := decodeZigZagInt32(u32) return i32, true } // Sint64 returns sint64 value for fc. // // False is returned if fc doesn't contain sint64 value. func (fc *FieldContext) Sint64() (int64, bool) { if fc.wireType != wireTypeVarint { return 0, false } i64 := decodeZigZagInt64(fc.intValue) return i64, true } // Bool returns bool value for fc. // // False is returned in the second result if fc doesn't contain bool value. func (fc *FieldContext) Bool() (bool, bool) { if fc.wireType != wireTypeVarint { return false, false } return getBool(fc.intValue) } // Fixed64 returns fixed64 value for fc. // // False is returned if fc doesn't contain fixed64 value. func (fc *FieldContext) Fixed64() (uint64, bool) { if fc.wireType != wireTypeI64 { return 0, false } return fc.intValue, true } // Sfixed64 returns sfixed64 value for fc. // // False is returned if fc doesn't contain sfixed64 value. func (fc *FieldContext) Sfixed64() (int64, bool) { if fc.wireType != wireTypeI64 { return 0, false } return int64(fc.intValue), true } // Double returns dobule value for fc. // // False is returned if fc doesn't contain double value. func (fc *FieldContext) Double() (float64, bool) { if fc.wireType != wireTypeI64 { return 0, false } v := math.Float64frombits(fc.intValue) return v, true } // String returns string value for fc. // // The returned string is valid while the underlying buffer isn't changed. // // False is returned if fc doesn't contain string value. func (fc *FieldContext) String() (string, bool) { if fc.wireType != wireTypeLen { return "", false } s := unsafeBytesToString(fc.data) return s, true } // Bytes returns bytes value for fc. // // The returned byte slice is valid while the underlying buffer isn't changed. // // False is returned if fc doesn't contain bytes value. func (fc *FieldContext) Bytes() ([]byte, bool) { if fc.wireType != wireTypeLen { return nil, false } return fc.data, true } // MessageData returns protobuf message data for fc. // // False is returned if fc doesn't contain message data. func (fc *FieldContext) MessageData() ([]byte, bool) { if fc.wireType != wireTypeLen { return nil, false } return fc.data, true } // Fixed32 returns fixed32 value for fc. // // False is returned if fc doesn't contain fixed32 value. func (fc *FieldContext) Fixed32() (uint32, bool) { if fc.wireType != wireTypeI32 { return 0, false } u32 := mustGetUint32(fc.intValue) return u32, true } // Sfixed32 returns sfixed32 value for fc. // // False is returned if fc doesn't contain sfixed value. func (fc *FieldContext) Sfixed32() (int32, bool) { if fc.wireType != wireTypeI32 { return 0, false } i32 := mustGetInt32(fc.intValue) return i32, true } // Float returns float value for fc. // // False is returned if fc doesn't contain float value. func (fc *FieldContext) Float() (float32, bool) { if fc.wireType != wireTypeI32 { return 0, false } u32 := mustGetUint32(fc.intValue) v := math.Float32frombits(u32) return v, true } // UnpackInt32s unpacks int32 values from fc, appends them to dst and returns the result. // // False is returned if fc doesn't contain int32 values. func (fc *FieldContext) UnpackInt32s(dst []int32) ([]int32, bool) { if fc.wireType == wireTypeVarint { i32, ok := getInt32(fc.intValue) if !ok { return dst, false } dst = append(dst, i32) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { u64, offset := binary.Uvarint(src) if offset <= 0 { return dstOrig, false } src = src[offset:] i32, ok := getInt32(u64) if !ok { return dstOrig, false } dst = append(dst, i32) } return dst, true } // UnpackInt64s unpacks int64 values from fc, appends them to dst and returns the result. // // False is returned if fc doesn't contain int64 values. func (fc *FieldContext) UnpackInt64s(dst []int64) ([]int64, bool) { if fc.wireType == wireTypeVarint { dst = append(dst, int64(fc.intValue)) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { u64, offset := binary.Uvarint(src) if offset <= 0 { return dstOrig, false } src = src[offset:] dst = append(dst, int64(u64)) } return dst, true } // UnpackUint32s unpacks uint32 values from fc, appends them to dst and returns the result. // // False is returned if fc doesn't contain uint32 values. func (fc *FieldContext) UnpackUint32s(dst []uint32) ([]uint32, bool) { if fc.wireType == wireTypeVarint { u32, ok := getUint32(fc.intValue) if !ok { return dst, false } dst = append(dst, u32) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { u64, offset := binary.Uvarint(src) if offset <= 0 { return dstOrig, false } src = src[offset:] u32, ok := getUint32(u64) if !ok { return dstOrig, false } dst = append(dst, u32) } return dst, true } // UnpackUint64s unpacks uint64 values from fc, appends them to dst and returns the result. // // False is returned if fc doesn't contain uint64 values. func (fc *FieldContext) UnpackUint64s(dst []uint64) ([]uint64, bool) { if fc.wireType == wireTypeVarint { dst = append(dst, fc.intValue) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { u64, offset := binary.Uvarint(src) if offset <= 0 { return dstOrig, false } src = src[offset:] dst = append(dst, u64) } return dst, true } // UnpackSint32s unpacks sint32 values from fc, appends them to dst and returns the result. // // False is returned if fc doesn't contain sint32 values. func (fc *FieldContext) UnpackSint32s(dst []int32) ([]int32, bool) { if fc.wireType == wireTypeVarint { u32, ok := getUint32(fc.intValue) if !ok { return dst, false } i32 := decodeZigZagInt32(u32) dst = append(dst, i32) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { u64, offset := binary.Uvarint(src) if offset <= 0 { return dstOrig, false } src = src[offset:] u32, ok := getUint32(u64) if !ok { return dstOrig, false } i32 := decodeZigZagInt32(u32) dst = append(dst, i32) } return dst, true } // UnpackSint64s unpacks sint64 values from fc, appends them to dst and returns the result. // // False is returned if fc doesn't contain sint64 values. func (fc *FieldContext) UnpackSint64s(dst []int64) ([]int64, bool) { if fc.wireType == wireTypeVarint { i64 := decodeZigZagInt64(fc.intValue) dst = append(dst, i64) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { u64, offset := binary.Uvarint(src) if offset <= 0 { return dstOrig, false } src = src[offset:] i64 := decodeZigZagInt64(u64) dst = append(dst, i64) } return dst, true } // UnpackBools unpacks bool values from fc, appends them to dst and returns the result. // // False is returned in the second result if fc doesn't contain bool values. func (fc *FieldContext) UnpackBools(dst []bool) ([]bool, bool) { if fc.wireType == wireTypeVarint { v, ok := getBool(fc.intValue) if !ok { return dst, false } dst = append(dst, v) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { u64, offset := binary.Uvarint(src) if offset <= 0 { return dstOrig, false } src = src[offset:] v, ok := getBool(u64) if !ok { return dst, false } dst = append(dst, v) } return dst, true } // UnpackFixed64s unpacks fixed64 values from fc, appends them to dst and returns the result. // // False is returned if fc doesn't contain fixed64 values. func (fc *FieldContext) UnpackFixed64s(dst []uint64) ([]uint64, bool) { if fc.wireType == wireTypeI64 { u64 := fc.intValue dst = append(dst, u64) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { if len(src) < 8 { return dstOrig, false } u64 := binary.LittleEndian.Uint64(src) src = src[8:] dst = append(dst, u64) } return dst, true } // UnpackSfixed64s unpacks sfixed64 values from fc, appends them to dst and returns the result. // // False is returned if fc doesn't contain sfixed64 values. func (fc *FieldContext) UnpackSfixed64s(dst []int64) ([]int64, bool) { if fc.wireType == wireTypeI64 { u64 := fc.intValue dst = append(dst, int64(u64)) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { if len(src) < 8 { return dstOrig, false } u64 := binary.LittleEndian.Uint64(src) src = src[8:] dst = append(dst, int64(u64)) } return dst, true } // UnpackDoubles unpacks double values from fc, appends them to dst and returns the result. // // False is returned if fc doesn't contain double values. func (fc *FieldContext) UnpackDoubles(dst []float64) ([]float64, bool) { if fc.wireType == wireTypeI64 { v := math.Float64frombits(fc.intValue) dst = append(dst, v) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { if len(src) < 8 { return dstOrig, false } u64 := binary.LittleEndian.Uint64(src) src = src[8:] v := math.Float64frombits(u64) dst = append(dst, v) } return dst, true } // UnpackFixed32s unpacks fixed32 values from fc, appends them to dst and returns the result. // // False is returned if fc doesn't contain fixed32 values. func (fc *FieldContext) UnpackFixed32s(dst []uint32) ([]uint32, bool) { if fc.wireType == wireTypeI32 { u32 := mustGetUint32(fc.intValue) dst = append(dst, u32) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { if len(src) < 4 { return dstOrig, false } u32 := binary.LittleEndian.Uint32(src) src = src[4:] dst = append(dst, u32) } return dst, true } // UnpackSfixed32s unpacks sfixed32 values from fc, appends them to dst and returns the result. // // False is returned if fc doesn't contain sfixed32 values. func (fc *FieldContext) UnpackSfixed32s(dst []int32) ([]int32, bool) { if fc.wireType == wireTypeI32 { i32 := mustGetInt32(fc.intValue) dst = append(dst, i32) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { if len(src) < 4 { return dstOrig, false } u32 := binary.LittleEndian.Uint32(src) src = src[4:] dst = append(dst, int32(u32)) } return dst, true } // UnpackFloats unpacks float values from fc, appends them to dst and returns the result. // // False is returned if fc doesn't contain float values. func (fc *FieldContext) UnpackFloats(dst []float32) ([]float32, bool) { if fc.wireType == wireTypeI32 { u32 := mustGetUint32(fc.intValue) v := math.Float32frombits(u32) dst = append(dst, v) return dst, true } if fc.wireType != wireTypeLen { return dst, false } src := fc.data dstOrig := dst for len(src) > 0 { if len(src) < 4 { return dstOrig, false } u32 := binary.LittleEndian.Uint32(src) src = src[4:] v := math.Float32frombits(u32) dst = append(dst, v) } return dst, true } func decodeZigZagInt64(u64 uint64) int64 { return int64(u64>>1) ^ (int64(u64<<63) >> 63) } func decodeZigZagInt32(u32 uint32) int32 { return int32(u32>>1) ^ (int32(u32<<31) >> 31) } func unsafeBytesToString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } func getInt32(u64 uint64) (int32, bool) { u32, ok := getUint32(u64) if !ok { return 0, false } return int32(u32), true } func getUint32(u64 uint64) (uint32, bool) { if u64 > math.MaxUint32 { return 0, false } return uint32(u64), true } func mustGetInt32(u64 uint64) int32 { u32 := mustGetUint32(u64) return int32(u32) } func mustGetUint32(u64 uint64) uint32 { u32, ok := getUint32(u64) if !ok { panic(fmt.Errorf("BUG: cannot get uint32 from %d", u64)) } return u32 } func getBool(u64 uint64) (bool, bool) { if u64 == 0 { return false, true } if u64 == 1 { return true, true } return false, false } easyproto-0.1.4/writer.go000066400000000000000000000440471455117434000154210ustar00rootroot00000000000000package easyproto import ( "encoding/binary" "math" "math/bits" "sync" ) // MarshalerPool is a pool of Marshaler structs. type MarshalerPool struct { p sync.Pool } // Get obtains a Marshaler from the pool. // // The returned Marshaler can be returned to the pool via Put after it is no longer needed. func (mp *MarshalerPool) Get() *Marshaler { v := mp.p.Get() if v == nil { return &Marshaler{} } return v.(*Marshaler) } // Put returns the given m to the pool. // // m cannot be used after returning to the pool. func (mp *MarshalerPool) Put(m *Marshaler) { m.Reset() mp.p.Put(m) } // Marshaler helps marshaling arbitrary protobuf messages. // // Construct message with Append* functions at MessageMarshaler() and then call Marshal* for marshaling the constructed message. // // It is unsafe to use a single Marshaler instance from multiple concurrently running goroutines. // // It is recommended re-cycling Marshaler via MarshalerPool in order to reduce memory allocations. type Marshaler struct { // mm contains the root MessageMarshaler. mm *MessageMarshaler // buf contains temporary data needed for marshaling the protobuf message. buf []byte // fs contains fields for the currently marshaled message. fs []field // mms contains MessageMarshaler structs for the currently marshaled message. mms []MessageMarshaler } // MessageMarshaler helps constructing protobuf message for marshaling. // // MessageMarshaler must be obtained via Marshaler.MessageMarshaler(). type MessageMarshaler struct { // m is the parent Marshaler for the given MessageMarshaler. m *Marshaler // tag contains protobuf message tag for the given MessageMarshaler. tag uint64 // firstFieldIdx contains the index of the first field in the Marshaler.fs, which belongs to MessageMarshaler. firstFieldIdx int // lastFieldIdx is the index of the last field in the Marshaler.fs, which belongs to MessageMarshaler. lastFieldIdx int } func (mm *MessageMarshaler) reset() { mm.m = nil mm.tag = 0 mm.firstFieldIdx = -1 mm.lastFieldIdx = -1 } type field struct { // messageSize is the size of marshaled protobuf message for the given field. messageSize uint64 // dataStart is the start offset of field data at Marshaler.buf. dataStart int // dataEnd is the end offset of field data at Marshaler.buf. dataEnd int // nextFieldIdx contains an index of the next field in Marshaler.fs. nextFieldIdx int // childMessageMarshalerIdx contains an index of child MessageMarshaler in Marshaler.mms. childMessageMarshalerIdx int } func (f *field) reset() { f.messageSize = 0 f.dataStart = 0 f.dataEnd = 0 f.nextFieldIdx = -1 f.childMessageMarshalerIdx = -1 } // Reset resets m, so it can be re-used. func (m *Marshaler) Reset() { m.mm = nil m.buf = m.buf[:0] // There is no need in resetting individual fields, since they are reset in newFieldIndex() m.fs = m.fs[:0] // There is no need in resetting individual MessageMarshaler items, since they are reset in newMessageMarshalerIndex() m.mms = m.mms[:0] } // MarshalWithLen marshals m, appends its length together with the marshaled m to dst and returns the result. // // E.g. appends length-delimited protobuf message to dst. // The length of the resulting message can be read via UnmarshalMessageLen() function. // // See also Marshal. func (m *Marshaler) MarshalWithLen(dst []byte) []byte { if m.mm == nil { dst = marshalVarUint64(dst, 0) return dst } if firstFieldIdx := m.mm.firstFieldIdx; firstFieldIdx >= 0 { f := &m.fs[firstFieldIdx] messageSize := f.initMessageSize(m) if cap(dst) == 0 { dst = make([]byte, messageSize+10) dst = dst[:0] } dst = marshalVarUint64(dst, messageSize) dst = f.marshal(dst, m) } return dst } // Marshal appends marshaled protobuf m to dst and returns the result. // // The marshaled message can be read via FieldContext.NextField(). // // See also MarshalWithLen. func (m *Marshaler) Marshal(dst []byte) []byte { if m.mm == nil { // Nothing to marshal return dst } if firstFieldIdx := m.mm.firstFieldIdx; firstFieldIdx >= 0 { f := &m.fs[firstFieldIdx] messageSize := f.initMessageSize(m) if cap(dst) == 0 { dst = make([]byte, messageSize) dst = dst[:0] } dst = f.marshal(dst, m) } return dst } // MessageMarshaler returns message marshaler for the given m. func (m *Marshaler) MessageMarshaler() *MessageMarshaler { if mm := m.mm; mm != nil { return mm } idx := m.newMessageMarshalerIndex() mm := &m.mms[idx] m.mm = mm return mm } func (m *Marshaler) newMessageMarshalerIndex() int { mms := m.mms mmsLen := len(mms) if cap(mms) > mmsLen { mms = mms[:mmsLen+1] } else { mms = append(mms, MessageMarshaler{}) } m.mms = mms mm := &mms[mmsLen] mm.reset() mm.m = m return mmsLen } func (m *Marshaler) newFieldIndex() int { fs := m.fs fsLen := len(fs) if cap(fs) > fsLen { fs = fs[:fsLen+1] } else { fs = append(fs, field{}) } m.fs = fs fs[fsLen].reset() return fsLen } // AppendInt32 appends the given int32 value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendInt32(fieldNum uint32, i32 int32) { mm.AppendUint64(fieldNum, uint64(uint32(i32))) } // AppendInt64 appends the given int64 value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendInt64(fieldNum uint32, i64 int64) { mm.AppendUint64(fieldNum, uint64(i64)) } // AppendUint32 appends the given uint32 value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendUint32(fieldNum, u32 uint32) { mm.AppendUint64(fieldNum, uint64(u32)) } // AppendUint64 appends the given uint64 value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendUint64(fieldNum uint32, u64 uint64) { tag := makeTag(fieldNum, wireTypeVarint) m := mm.m dst := m.buf dstLen := len(dst) if tag < 0x80 { dst = append(dst, byte(tag)) } else { dst = marshalVarUint64(dst, tag) } dst = marshalVarUint64(dst, u64) m.buf = dst mm.appendField(m, dstLen, len(dst)) } // AppendSint32 appends the given sint32 value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendSint32(fieldNum uint32, i32 int32) { u64 := uint64(encodeZigZagInt32(i32)) mm.AppendUint64(fieldNum, u64) } // AppendSint64 appends the given sint64 value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendSint64(fieldNum uint32, i64 int64) { u64 := encodeZigZagInt64(i64) mm.AppendUint64(fieldNum, u64) } // AppendBool appends the given bool value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendBool(fieldNum uint32, v bool) { u64 := uint64(0) if v { u64 = 1 } mm.AppendUint64(fieldNum, u64) } // AppendFixed64 appends fixed64 value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendFixed64(fieldNum uint32, u64 uint64) { tag := makeTag(fieldNum, wireTypeI64) m := mm.m dst := m.buf dstLen := len(dst) if tag < 0x80 { dst = append(dst, byte(tag)) } else { dst = marshalVarUint64(dst, tag) } dst = marshalUint64(dst, u64) m.buf = dst mm.appendField(m, dstLen, len(dst)) } // AppendSfixed64 appends sfixed64 value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendSfixed64(fieldNum uint32, i64 int64) { mm.AppendFixed64(fieldNum, uint64(i64)) } // AppendDouble appends double value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendDouble(fieldNum uint32, f float64) { u64 := math.Float64bits(f) mm.AppendFixed64(fieldNum, u64) } // AppendString appends string value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendString(fieldNum uint32, s string) { tag := makeTag(fieldNum, wireTypeLen) m := mm.m dst := m.buf dstLen := len(dst) sLen := len(s) if tag < 0x80 && sLen < 0x80 { dst = append(dst, byte(tag), byte(sLen)) } else { dst = marshalVarUint64(dst, tag) dst = marshalVarUint64(dst, uint64(sLen)) } dst = append(dst, s...) m.buf = dst mm.appendField(m, dstLen, len(dst)) } // AppendBytes appends bytes value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendBytes(fieldNum uint32, b []byte) { s := unsafeBytesToString(b) mm.AppendString(fieldNum, s) } // AppendMessage appends protobuf message with the given fieldNum to m. // // The function returns the MessageMarshaler for constructing the appended message. func (mm *MessageMarshaler) AppendMessage(fieldNum uint32) *MessageMarshaler { tag := makeTag(fieldNum, wireTypeLen) f := mm.newField() m := mm.m f.childMessageMarshalerIdx = m.newMessageMarshalerIndex() mmChild := &m.mms[f.childMessageMarshalerIdx] mmChild.tag = tag return mmChild } // AppendFixed32 appends fixed32 value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendFixed32(fieldNum, u32 uint32) { tag := makeTag(fieldNum, wireTypeI32) m := mm.m dst := m.buf dstLen := len(dst) if tag < 0x80 { dst = append(dst, byte(tag)) } else { dst = marshalVarUint64(dst, tag) } dst = marshalUint32(dst, u32) m.buf = dst mm.appendField(m, dstLen, len(dst)) } // AppendSfixed32 appends sfixed32 value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendSfixed32(fieldNum uint32, i32 int32) { mm.AppendFixed32(fieldNum, uint32(i32)) } // AppendFloat appends float value under the given fieldNum to mm. func (mm *MessageMarshaler) AppendFloat(fieldNum uint32, f float32) { u32 := math.Float32bits(f) mm.AppendFixed32(fieldNum, u32) } // AppendInt32s appends the given int32 values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendInt32s(fieldNum uint32, i32s []int32) { child := mm.AppendMessage(fieldNum) child.appendInt32s(i32s) } // AppendInt64s appends the given int64 values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendInt64s(fieldNum uint32, i64s []int64) { child := mm.AppendMessage(fieldNum) child.appendInt64s(i64s) } // AppendUint32s appends the given uint32 values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendUint32s(fieldNum uint32, u32s []uint32) { child := mm.AppendMessage(fieldNum) child.appendUint32s(u32s) } // AppendUint64s appends the given uint64 values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendUint64s(fieldNum uint32, u64s []uint64) { child := mm.AppendMessage(fieldNum) child.appendUint64s(u64s) } // AppendSint32s appends the given sint32 values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendSint32s(fieldNum uint32, i32s []int32) { child := mm.AppendMessage(fieldNum) child.appendSint32s(i32s) } // AppendSint64s appends the given sint64 values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendSint64s(fieldNum uint32, i64s []int64) { child := mm.AppendMessage(fieldNum) child.appendSint64s(i64s) } // AppendBools appends the given bool values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendBools(fieldNum uint32, bs []bool) { child := mm.AppendMessage(fieldNum) child.appendBools(bs) } // AppendFixed64s appends the given fixed64 values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendFixed64s(fieldNum uint32, u64s []uint64) { child := mm.AppendMessage(fieldNum) child.appendFixed64s(u64s) } // AppendSfixed64s appends the given sfixed64 values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendSfixed64s(fieldNum uint32, i64s []int64) { child := mm.AppendMessage(fieldNum) child.appendSfixed64s(i64s) } // AppendDoubles appends the given double values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendDoubles(fieldNum uint32, fs []float64) { child := mm.AppendMessage(fieldNum) child.appendDoubles(fs) } // AppendFixed32s appends the given fixed32 values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendFixed32s(fieldNum uint32, u32s []uint32) { child := mm.AppendMessage(fieldNum) child.appendFixed32s(u32s) } // AppendSfixed32s appends the given sfixed32 values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendSfixed32s(fieldNum uint32, i32s []int32) { child := mm.AppendMessage(fieldNum) child.appendSfixed32s(i32s) } // AppendFloats appends the given float values under the given fieldNum to mm. func (mm *MessageMarshaler) AppendFloats(fieldNum uint32, fs []float32) { child := mm.AppendMessage(fieldNum) child.appendFloats(fs) } func (mm *MessageMarshaler) appendInt32s(i32s []int32) { m := mm.m dst := m.buf dstLen := len(dst) for _, i32 := range i32s { dst = marshalVarUint64(dst, uint64(uint32(i32))) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendUint32s(u32s []uint32) { m := mm.m dst := m.buf dstLen := len(dst) for _, u32 := range u32s { dst = marshalVarUint64(dst, uint64(u32)) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendSint32s(i32s []int32) { m := mm.m dst := m.buf dstLen := len(dst) for _, i32 := range i32s { u64 := uint64(encodeZigZagInt32(i32)) dst = marshalVarUint64(dst, u64) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendInt64s(i64s []int64) { m := mm.m dst := m.buf dstLen := len(dst) for _, i64 := range i64s { dst = marshalVarUint64(dst, uint64(i64)) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendUint64s(u64s []uint64) { m := mm.m dst := m.buf dstLen := len(dst) for _, u64 := range u64s { dst = marshalVarUint64(dst, u64) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendSint64s(i64s []int64) { m := mm.m dst := m.buf dstLen := len(dst) for _, i64 := range i64s { u64 := encodeZigZagInt64(i64) dst = marshalVarUint64(dst, u64) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendBools(bs []bool) { m := mm.m dst := m.buf dstLen := len(dst) for _, b := range bs { u64 := uint64(0) if b { u64 = 1 } dst = marshalVarUint64(dst, u64) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendFixed64s(u64s []uint64) { m := mm.m dst := m.buf dstLen := len(dst) for _, u64 := range u64s { dst = marshalUint64(dst, u64) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendSfixed64s(i64s []int64) { m := mm.m dst := m.buf dstLen := len(dst) for _, i64 := range i64s { dst = marshalUint64(dst, uint64(i64)) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendFixed32s(u32s []uint32) { m := mm.m dst := m.buf dstLen := len(dst) for _, u32 := range u32s { dst = marshalUint32(dst, u32) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendSfixed32s(i32s []int32) { m := mm.m dst := m.buf dstLen := len(dst) for _, i32 := range i32s { dst = marshalUint32(dst, uint32(i32)) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendDoubles(fs []float64) { m := mm.m dst := m.buf dstLen := len(dst) for _, f := range fs { u64 := math.Float64bits(f) dst = marshalUint64(dst, u64) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendFloats(fs []float32) { m := mm.m dst := m.buf dstLen := len(dst) for _, f := range fs { u32 := math.Float32bits(f) dst = marshalUint32(dst, u32) } m.buf = dst mm.appendField(m, dstLen, len(dst)) } func (mm *MessageMarshaler) appendField(m *Marshaler, dataStart, dataEnd int) { if lastFieldIdx := mm.lastFieldIdx; lastFieldIdx >= 0 { if f := &m.fs[lastFieldIdx]; f.childMessageMarshalerIdx == -1 && f.dataEnd == dataStart { f.dataEnd = dataEnd return } } f := mm.newField() f.dataStart = dataStart f.dataEnd = dataEnd } func (mm *MessageMarshaler) newField() *field { m := mm.m idx := m.newFieldIndex() f := &m.fs[idx] if lastFieldIdx := mm.lastFieldIdx; lastFieldIdx >= 0 { m.fs[lastFieldIdx].nextFieldIdx = idx } else { mm.firstFieldIdx = idx } mm.lastFieldIdx = idx return f } func (f *field) initMessageSize(m *Marshaler) uint64 { n := uint64(0) for { if childMessageMarshalerIdx := f.childMessageMarshalerIdx; childMessageMarshalerIdx < 0 { n += uint64(f.dataEnd - f.dataStart) } else { mmChild := m.mms[childMessageMarshalerIdx] if tag := mmChild.tag; tag < 0x80 { n++ } else { n += varuintLen(tag) } messageSize := uint64(0) if firstFieldIdx := mmChild.firstFieldIdx; firstFieldIdx >= 0 { messageSize = m.fs[firstFieldIdx].initMessageSize(m) } n += messageSize if messageSize < 0x80 { n++ } else { n += varuintLen(messageSize) } f.messageSize = messageSize } nextFieldIdx := f.nextFieldIdx if nextFieldIdx < 0 { return n } f = &m.fs[nextFieldIdx] } } func (f *field) marshal(dst []byte, m *Marshaler) []byte { for { if childMessageMarshalerIdx := f.childMessageMarshalerIdx; childMessageMarshalerIdx < 0 { data := m.buf[f.dataStart:f.dataEnd] dst = append(dst, data...) } else { mmChild := m.mms[childMessageMarshalerIdx] tag := mmChild.tag messageSize := f.messageSize if tag < 0x80 && messageSize < 0x80 { dst = append(dst, byte(tag), byte(messageSize)) } else { dst = marshalVarUint64(dst, mmChild.tag) dst = marshalVarUint64(dst, f.messageSize) } if firstFieldIdx := mmChild.firstFieldIdx; firstFieldIdx >= 0 { dst = m.fs[firstFieldIdx].marshal(dst, m) } } nextFieldIdx := f.nextFieldIdx if nextFieldIdx < 0 { return dst } f = &m.fs[nextFieldIdx] } } func marshalUint64(dst []byte, u64 uint64) []byte { return binary.LittleEndian.AppendUint64(dst, u64) } func marshalUint32(dst []byte, u32 uint32) []byte { return binary.LittleEndian.AppendUint32(dst, u32) } func marshalVarUint64(dst []byte, u64 uint64) []byte { if u64 < 0x80 { // Fast path dst = append(dst, byte(u64)) return dst } for u64 > 0x7f { dst = append(dst, 0x80|byte(u64)) u64 >>= 7 } dst = append(dst, byte(u64)) return dst } func encodeZigZagInt64(i64 int64) uint64 { return uint64((i64 << 1) ^ (i64 >> 63)) } func encodeZigZagInt32(i32 int32) uint32 { return uint32((i32 << 1) ^ (i32 >> 31)) } func makeTag(fieldNum uint32, wt wireType) uint64 { return (uint64(fieldNum) << 3) | uint64(wt) } // varuintLen returns the number of bytes needed for varuint-encoding of u64. // // Note that it returns 0 for u64=0, so this case must be handled separately. func varuintLen(u64 uint64) uint64 { return uint64(((byte(bits.Len64(u64))) + 6) / 7) }