profile
viewpoint
Joe Tsai dsnet @google Mountain View http://digital-static.net Free food critic.

google/go-cmp 1986

Package for comparing Go values in tests

google/safebrowsing 337

Safe Browsing API Go Client

dsnet/compress 312

Collection of compression related Go packages.

dsnet/udptunnel 95

Daemon for creating a simple VPN over UDP.

dsnet/motd-generator 38

Custom message-of-the-day (MOTD) generator intended to be informative about the system you are logging in to.

dsnet/sshtunnel 35

SSH daemon for creating forward and reverse tunnels.

dsnet/golib 14

Collection of mostly unrelated helper Go packages.

dsnet/termijack 14

TermiJack surreptitiously hijacks standard streams (stdin, stdout, and/or stderr) from an already running process.

dsnet/playground 13

Locally hosted Go playground for more advanced functionality.

dsnet/gotab 9

Simple bash tab completion for the go command.

issue commentgolang/go

reflect.DeepEqual on two empty slices returns false

I should further note that this is documented on DeepEqual as:

Slice values are deeply equal when all of the following are true: they are both nil or both non-nil

If you need a fuzzier comparison for slices, consider using the cmp.Equal with the cmpopts.EquateEmpty option:

import "github.com/google/go-cmp/cmp"
import "github.com/google/go-cmp/cmp/cmpopts"

cmp.Equal(x, y, cmpopts.EquateEmpty())
3noch

comment created time in 9 minutes

issue closedgolang/go

reflect.DeepEqual on two empty slices returns false

What did you expect to see?

reflect.DeepEqual on two slices with the same type and same length of 0 should be true.

What did you see instead?

See repro here: https://play.golang.org/p/YjCo7OcS7hv

closed time in 13 minutes

3noch

issue commentgolang/go

reflect.DeepEqual on two empty slices returns false

Oops, Ian posted at the same time.

3noch

comment created time in 13 minutes

IssuesEvent

issue commentgolang/go

reflect.DeepEqual on two empty slices returns false

DeepEqual distinguishes between a slice that is empty and non-nil, versus one that is nil. You are probably seeing that effect.

3noch

comment created time in 13 minutes

issue closedgolang/protobuf

Invalid reference of ProtoPackageIsVersion4 in v1.25.0

What version of protobuf and what language are you using? Language: GoLang Version:

protoc: libprotoc 3.13.0 protoc-gen-go: protoc-gen-go v1.25.0

What did you do? I followed the below steps. mkdir protoc-learning

go mod init proto-gen-hello

cat <<EOF >hello.proto
> syntax = "proto3";
> package hello;
> option go_package = ".;hello";
> message Greet {
>     string hello = 1;
> }
> EOF
cat <<EOF >main.go
> package main
> import "log"
> func main() {
>	log.Print("Hello protoc")
> }
go get google.golang.org/protobuf@v1.25.0

⚠️ This above step is resulting in the following known errors though https://github.com/golang/protobuf/issues/1033 https://github.com/golang/protobuf/issues/1224 The workaround I used is adding the module manually in the go.mod file.

mkdir out
 protoc --proto_path . -I=. hello.proto --hello_out=./out --go_out=./out

As expected, the above command creates the pb.go file. But it creates the file with proto "github.com/golang/protobuf/proto" dependency.

With this also, the file refers proto.ProtoPackageIsVersion4 constant.

What did you expect to see? I expect to get v1.25.0 dependency resolved and the pb.go file should be created with the corresponding constants/variables. Because the proto.ProtoPackageIsVersion4 is not available in the v1.25.0

What did you see instead?

I see the dependency as proto "github.com/golang/protobuf/proto" and referred const _ = proto.ProtoPackageIsVersion4 constant which is not available in v1.25.0 as I have added that in the go,mod.

Make sure you include information that can help us debug (full error message, exception listing, stack trace, logs).

Anything else we should know about your project / environment?

<details> <summary>Click to see go.mod file</summary> <pre> module protoc-gen-hello

go 1.15

require ( google.golang.org/protobuf v1.25.0 ) </pre> </details>

<details> <summary>Click to see proto file</summary> <pre> syntax = "proto3"; package hello; option go_package = ".;hello"; message Greet { string hello = 1; } </pre> </details>

<details> <summary>Click to see pb.go file</summary> <pre> // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.25.0 // protoc v3.13.0 // source: hello.proto

package hello

import ( proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" )

const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) )

// This is a compile-time assertion that a sufficiently up-to-date version // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4

type Greet struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields

Hello string `protobuf:"bytes,1,opt,name=hello,proto3" json:"hello,omitempty"`

}

func (x *Greet) Reset() { *x = Greet{} if protoimpl.UnsafeEnabled { mi := &file_hello_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } }

func (x *Greet) String() string { return protoimpl.X.MessageStringOf(x) }

func (*Greet) ProtoMessage() {}

func (x *Greet) ProtoReflect() protoreflect.Message { mi := &file_hello_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) }

// Deprecated: Use Greet.ProtoReflect.Descriptor instead. func (*Greet) Descriptor() ([]byte, []int) { return file_hello_proto_rawDescGZIP(), []int{0} }

func (x *Greet) GetHello() string { if x != nil { return x.Hello } return "" }

var File_hello_proto protoreflect.FileDescriptor

var file_hello_proto_rawDesc = []byte{ 0x0a, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x1d, 0x0a, 0x05, 0x47, 0x72, 0x65, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }

var ( file_hello_proto_rawDescOnce sync.Once file_hello_proto_rawDescData = file_hello_proto_rawDesc )

func file_hello_proto_rawDescGZIP() []byte { file_hello_proto_rawDescOnce.Do(func() { file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_proto_rawDescData) }) return file_hello_proto_rawDescData }

var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_hello_proto_goTypes = []interface{}{ (*Greet)(nil), // 0: hello.Greet } var file_hello_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name }

func init() { file_hello_proto_init() } func file_hello_proto_init() { if File_hello_proto != nil { return } if !protoimpl.UnsafeEnabled { file_hello_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Greet); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_hello_proto_rawDesc, NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_hello_proto_goTypes, DependencyIndexes: file_hello_proto_depIdxs, MessageInfos: file_hello_proto_msgTypes, }.Build() File_hello_proto = out.File file_hello_proto_rawDesc = nil file_hello_proto_goTypes = nil file_hello_proto_depIdxs = nil } </pre> </details>

Please let me know if I am missing anything obvious.

closed time in an hour

gkarthiks

issue commentgolang/protobuf

Invalid reference of ProtoPackageIsVersion4 in v1.25.0

Closing as working as intended.

gkarthiks

comment created time in an hour

issue commentgolang/protobuf

I'm getting this message when using go get- u github.com/protocolbuffers/protobuf-go

The official path for this module is google.golang.org/protobuf. There's also no Go package at the module root, so you can try go get -u google.golang.org/protobuf/proto instead.

danielberber

comment created time in an hour

issue closedgolang/go

proposal: reflect: add Type.QualifiedString

#22075 makes it such that NumMethod no longer counts unexported methods for interfaces (and by implication the inability to obtain information about unexported methods).

One use case this breaks is https://github.com/google/go-cmp/blob/566225a2554cf156c7af1006dc3a0940e1e02b09/cmp/internal/value/name.go#L133-L146, where the goal is to print fully-qualified type information about a Go type (i.e., reflect.Type.String is insufficiently detailed). With the NumMethod change, I can no longer print unnamed interfaces in a way where users can tell that they are different.

I propose we add a reflect.Type.QualifiedString method that ensures the following property:

t1.QualifiedString() == t2.QualifiedString() if and only if t1 == t2

I don't have any strong preferences for the content of the string other than it be a humanly readable representation of the type. If the output is Go syntax, perhaps the method can be called GoString.

\cc @mdempsky

closed time in 8 hours

dsnet

issue commentgolang/go

proposal: reflect: add Type.QualifiedString

I'm abandoning my proposal.

dsnet

comment created time in 8 hours

issue closedgolang/protobuf

protobuf-go: why not make EnumNumber as alias of int32

@dsnet // EnumNumber is the numeric value for an enum. type EnumNumber int32

https://github.com/protocolbuffers/protobuf-go/blob/d3470999428befce9bbefe77980ff65ac5a494c4/reflect/protoreflect/proto.go#L396

Why not make EnumNumber as alias of type int32. then we could call .Number() to compare with int32 as the old version does.

closed time in 21 hours

timestee

issue commentgolang/protobuf

protobuf-go: why not make EnumNumber as alias of int32

We need the ability to distinguish between int32 and enums in a type switch. Thus, they are distinctly different types. Regardless, such change can't be made anymore without breaking backwards compatibility.

timestee

comment created time in 21 hours

issue commentgolang/go

proposal: Go 2: Dash import packages

Don't necessarily interpret this proposal are related to my use case.

A proposal that is divorced from concrete use cases that it is intended to solve is hard to evaluate realistically.

Finally, your approach loses static type checking

True, but Go reflection is a approach that works today without the addition of an entire language feature with many subtleties involved.

pjebs

comment created time in a day

issue commentgolang/go

proposal: Go 2: Dash import packages

For this specific case, couldn't you use reflection to access the relevant data without a hard dependency on those error types?

_, err := db.QueryRow("query", id).Scan(&nt)
if err == nil {
    return  0
}
switch verr := reflect.Indirect(reflect.ValueOf(err)); {
case verr.PkgPath() == "github.com/go-sql-driver/mysql" && verr.Name() == "MySQLError":
    return v.FieldByName("Number").Interface()
case verr.PkgPath() == "github.com/lib/pq" && verr.Name() == "Error":
    return v.FieldByName("Code").Interface()
}
pjebs

comment created time in a day

issue commentgolang/protobuf

Invalid reference of ProtoPackageIsVersion4 in v1.25.0

the ProtoPackageIsVersion4 has nothing to do with the google.golang.org/protobuf. Therefore, it's expected that you won't find it in v1.25.0 (in fact you won't find it on version of that module).

The identifier is declared in the github.com/golang/protobuf module at v1.4.0 and above.

gkarthiks

comment created time in a day

push eventgoogle/go-cmp

Joe Tsai

commit sha ab46b8bd0abd4c4557cc4709ad7ae12d47570603

Adjust for reflect.Type.NumMethod change in Go1.16 (#240) In Go1.16, the reflect.Type.NumMethod method will no longer report unexported fields, matching the documented behavior on the method. This means that t.NumMethod() == 0 is no longer a reliable means to detect whether an interface type is the empty interface or not. Fix the code to check whether the empty interface itself implements the target type.

view details

Joe Tsai

commit sha c2cbac1196d857095b0013539534101a419ec9bd

Merge branch 'master' into doc

view details

push time in 3 days

issue closedgolang/protobuf

No alternative to jsonpb.UnmarshalNext in v1.25

What version of protobuf and what language are you using? Version: 1.25

What did you do? Code using v1.4 and earlier uses jsonpb.UnmarshalNext(). There is no equivalent function in v1.25.

I'd like to see a protojson.UnmarshalNext(), but there's only protojson.Unmarshal(). None of the code in protojson gives me access to the Decoder or lets me inject my own. As a result in order to reproduce the functionality of jsonpb.UnmarshalNext I'd have to make my own copy of the protojson module.

closed time in 5 days

mostrows2

issue commentgolang/protobuf

No alternative to jsonpb.UnmarshalNext in v1.25

The UnmarshalNext function was seldom used and would force the protojson package to have a hard dependency on encoding/json that can never be removed (should a v2 version of encoding/json one day come). There is no need to carry this helper function into the new module.

It's equivalent functionality can be accomplished with:

func UnmarshalNext(d *json.Decoder, m proto.Message) error {
	var b json.RawMessage
	if err := d.Decode(&b); err != nil {
		return err
	}
	return protojson.Unmarshal(b, m)
}
mostrows2

comment created time in 5 days

issue commentgolang/protobuf

protoc-gen-go-grpc: program not found or is not executable when using APIv2

If you already ran go install google.golang.org/protobuf/cmd/protoc-gen-go and protoc still can't find the command, then the issues likely due to the PATH environment variable not set up.

Are you certain that your PATH environment variable contains the directory where go install outputs the binary?

ctfrancia

comment created time in 5 days

issue commentgolang/protobuf

protoc-gen-go-grpc: program not found or is not executable when using APIv2

Executing go get google.golang.org/grpc/cmd/protoc-gen-go-grpc installs protoc-gen-go-grpc, not protoc-gen-go.

Perhaps you meant go get google.golang.org/protobuf/cmd/protoc-gen-go instead?

ctfrancia

comment created time in 5 days

issue commentgolang/go

compress/bzip2: Unexpected File Signature

Sounds like you learned what you need.

Is there anything actionable to do for this issue or can we close it?

varunravi98

comment created time in 5 days

issue commentgolang/go

compress/bzip2: Unexpected File Signature

I'm not sure what this issue is trying to get at. While the internal implementation doesn't treat h as part of the magic number, it does have an explicit check that the subsequent version flag is always h. Thus, even if h isn't functionally treated as part of the magic number in the implementation, it functionally still enforces it to be present.

varunravi98

comment created time in 5 days

issue commentgolang/protobuf

protoc-gen-go: fully generate marshaling and unmarshaling code

Wanted update this thread. @neild and I have talked on/off about this in the past. One of things we'd like to explore first before addressing this is to figure out whether to provide a streaming or scatter/gather API (see #609). One significant challenge with generated code is that the implementation easily grows stale unless users regenerate the code (which rarely occurs as has been observed).

Though it's not a hard ordering requirement, we would presumably want to know if we're going to go anywhere with streaming or scatter/gather APIs before doing generated serialization as the former would likely affect the internal APIs and implementation of the latter.

awalterschulze

comment created time in 6 days

issue commentgolang/protobuf

jsonpb/protojson: Empty slices are omitted during serialization in APIv2

This isn't an issue with Go spec. It's an issue with regard to how the protobuf data model (call it the protobuf spec if you will), which has no distinction between a nil repeated field and an empty one. The google.golang.org/protobuf/protojson implementation is correct, while the github.com/golang/protobuf/jsonpb implementation is wrong.

  1. Would you consider adding options to allow flipping the behaviour to match the Go spec with regards to JSON serialization?

It seems wrong to preserve what is fundamentally a bug in the old implementation into the new implementation, which aims for correctness as a first-class feature.

Furthermore, it is impossible for the new implementation to preserve the old behavior since the new implementation is written entirely in terms of protobuf reflection, which does not provide any ability to detect this distinction. So, even if we wanted to provide an option, it would not be possible to implement.

mfridman

comment created time in 6 days

issue commentgolang/protobuf

protoc-gen-go: support generating structs with non-pointer message fields

Others in other forums have raised the point that regardless of the allocation concerns, the reflection-based decoding is dramatically less efficient than code-generated decoding. I’ve become convinced that we need some benchmarks on the topic.

Several thoughts:

  • The current implementation only uses reflection initially to construct the (de)serialization tables up-front, and all subsequent operations use what we call "the table driven" implementation. It is inaccurate to say that it is "reflection-based decoding".

  • We are aware that full code generation can be more performant, but:

    1. Full code generation leads to significant code bloat which leads to certain programs not being buildable since they have restrictions regarding how the binary is built or where it is deployed. It cannot be the default mode.
    2. Full code generation unfairly favors micro-benchmarks more than realistic end-to-end benchmarks. Code generation highly benefits from the generated (de)serialization code being in the instruction cache. For a sufficiently large program, the benefits of full code generation are often not as great as seen on microbenchmarks. In some rare cases, we have actually seen it have the opposite effect of slowing down the program since the instruction cache starts to thrash heavily.
  • Let's keep any further discussion about code generation on #280. It's not something we're opposed to adding, but it is a non-trivial amount of work and there's only so much we can do.

    • That said, since the motivation for this proposal is performance, I think it's more practical if we invest effort into #280 first if we believe it will benefit users that want to opt-in to having it. This proposal is much harder to accept than #280 since it requires API-level changes, while #280 does not.
ajwerner

comment created time in 6 days

issue commentgolang/protobuf

types/known/fieldmaskpb: special parsing behaviour for struct.proto types

It sounds like you want a FieldMask-like message that is specific to JSON, which is an entirely reasonable use case. However, the FieldMask well-known type is clearly intended to operate on the semantics of the protobuf type system, not arbitrary JSON objects. It's not clear to me that conflating these two semantics is necessarily a good idea. Either way, I think this needs to be discussed with the main protobuf project since semantics like this would affect all the language implementations of protobufs, not just Go.

johanbrandhorst

comment created time in 6 days

issue closedgolang/protobuf

Can you please add documentation for self-describing message usage?

Hi Team,

Can you please add an example for self-describing message? i mean how to use this. I searched a lot but couldn't see any solid example of how to use it.

Regards, Sharath

closed time in 6 days

Sharathnasa

issue commentgolang/protobuf

Can you please add documentation for self-describing message usage?

Closing. For questions regarding usage, it is better to ask on Slack or someone other means where you can more readily get immediate help. Consider joining the #protobuf channel or the Gophers slack workspace: https://gophers.slack.com/messages/general/

I don't think we need to add documentation for this. The fact that C++ doesn't have a pre-defined type for SelfDescribingMessage indicates that this is a niche and relatively rare use case. I certainly believe we need more examples in our module, but we still need to be judicious about which to add. Too many examples for use-cases that are less common is still counter-productive.

Sharathnasa

comment created time in 6 days

issue closedgolang/protobuf

Protobuf with go modules

Hi,

as a aggravation I tried to read here and there about the topic, but could not get the solution.

I would like to use a this go package https://github.com/andreaaizza/sniffer, which is generating via its makefile 3 different protobuf files, inside a outer package, let's call the latter 'Outer'.

Outer is also using modules and its go.mod looks like:

module andreaaizza/outer

go 1.15

require (
        github.com/andreaaizza/sniffer v0.1.0
...
        google.golang.org/protobuf v1.25.0
)

When I try to go build it, I get the following error:

# github.com/andreaaizza/sniffer/logger
~/go/pkg/mod/github.com/andreaaizza/sniffer@v0.1.0/logger/logger.go:36:2: undefined: LoggerBuffer
~/go/pkg/mod/github.com/andreaaizza/sniffer@v0.1.0/logger/logger.go:37:19: undefined: DataUnit
...

obviously because the .proto files of github.com/andreaaizza/sniffer are not compiled.

Now, I tried multiple combinations including adding the following to Makefile in order to build .pb.go files inside vendor/... directory:

clean_vendor: 
        -rm -rf vendor

vendor: clean_vendor
        $(eval SNIFFER_PKG_DIR=$(shell go list -f "{{ .Dir }}" -m github.com/andreaaizza/sniffer))
        echo $(SNIFFER_PKG_DIR)
        go mod vendor
        make -f $(SNIFFER_PKG_DIR)/Makefile -C vendor/github.com/andreaaizza/sniffer proto

but build still fails with:

$ go build -mod vendor
vendor/github.com/andreaaizza/sniffer/logger/logger.pb.go:10:2: cannot find package "." in:
        ./vendor/github.com/golang/protobuf/proto

The only way I could make this working is by building .proto files inside readonly pkg directory:

pushd `go list -f "{{ .Dir }}" -m github.com/andreaaizza/sniffer`/..
chmod -R u+w sniffer@v0.1.0 && make -C sniffer@v0.1.0 proto
ake: Entering directory '~/go/pkg/mod/github.com/andreaaizza/sniffer@v0.1.0'
protoc --go_out=./ --go_opt=module=github.com/andreaaizza/sniffer logger/logger.proto
protoc --go_out=./ --go_opt=module=github.com/andreaaizza/sniffer dissector/dissector.proto
protoc --go_out=./ --go_opt=module=github.com/andreaaizza/sniffer sniffer.proto
make: Leaving directory '~/go/pkg/mod/github.com/andreaaizza/sniffer@v0.1.0'
popd
go build 

I belive this is a evident blunder, but I cannot find the right solution.

Can anybody help in getting me to the right path to handle protobuf with go modules?

Thanks in advance, Andrea

closed time in 6 days

andreaaizza

issue commentgolang/protobuf

Protobuf with go modules

Closing as working as intended. For questions regarding usage, it is better to ask on Slack or someone other means where you can more readily get immediate help. Consider joining the #protobuf channel or the Gophers slack workspace: https://gophers.slack.com/messages/general/

andreaaizza

comment created time in 6 days

issue commentgolang/protobuf

proto: annotate errors regarding invalid UTF-8 with the relevant field

Annotating errors with this information seems reasonable. If we do this or marshal, we presumably want to do it for unmarshal as well.

At least for unmarshal, we removed context from most errors since it was often misleading as to what the exact problem is. For example, unmarshaling random garbage would often produce an error saying that some specific field was the issue, when really the entire input was bad. This occurs because the protobuf wire format has no magic number. For invalid UTF-8, it seems unlikely to encounter this error due to random data since the data would first have to pass the length-prefix check.

Another concern I don't know the answer to is what the performance cost of this is. There's cost in allocating a new error value that contains the field information, and there may be cost passing the field information up/down the call stack.

\cc @neild.

nicolasparada

comment created time in 6 days

issue commentgolang/protobuf

types/dynamicpb: add NewTypes helper

As an implementation note, I'm waffling whether to add a FindExtensionByNumber method to protoregistry.Files so that NewTypes can simply be a thin wrapper over the Files-like object that is provided.

In fact, I'm wondering whether we should make the APIs of protoregistry.Files and protoregistry.Types more similar in all respects.

dsnet

comment created time in 6 days

issue commentgolang/protobuf

protoc-gen-go: support generating structs with non-pointer message fields

Most proposals to generate message fields as simply M instead of *M disregard protobuf correctness which requires that presence be preserved for these field. I appreciate that your proposal doesn't throw correctness out the window and attempts to preserve that behavior.

Several thoughts:

  • Regarding the representation of repeated message fields: the performance of []M or []*M is highly dependent on at least 3 factors: the size of the message, the number of slice elements, and whether the length is known beforehand. In the cases where the message sizes are on the larger side and the number of elements is non-trivial, it increasingly becomes more performant to use []*M over []M. The main reason why []M may be more performant is the ability to batch allocate the messages, but you can also do that with []*M by allocating a separate []M and addressing each element into the former. See this benchmark for example; try varying the message size and the number of elements.
  • Regarding the representation of map fields with messages: it is generally going to be less performant to use map[K]M over map[K]*M. The values in a map are not addressable. In the implementation of protobufs themselves, and also the wider ecosystem that deals with protobuf messages, it is very common for a concrete message to go through a proto.Message interface. Since map values can't be addressed, the value will have to end up on the heap each time it goes through a proto.Message interface.
  • Regarding "solution b" where we "[generate] a method for each message field ... to set its validity". Let's suppose we generate HasField and ClearField methods for a field named Field with go.nullable=false. What happens when there is another field that happens to be exactly named HasField or ClearField? Do we simply not generate the methods? Do we automatically mangle the method name to be unique? Do we generate invalid code to indicate to the user that the combination of options and field names is not permitted? (this is not a hypothetical issue, but a real issues that we continually encounter at scale).
ajwerner

comment created time in 6 days

issue commentgolang/go

compress/bzip2: Unexpected File Signature

See https://github.com/dsnet/compress/raw/master/doc/bzip2-format.pdf, which is a reverse engineered specification from the C source code. Section 2.2.2. describes the stream header, which indicates that "BZ" is the magic for the file, and "h" is technically the version flag.

varunravi98

comment created time in 6 days

Pull request review commentgrpc/grpc-go

protobuf: update all generated code to google.golang.org/protobuf

 echo "remove existing generated files" # intentionally generated by an older version of protoc-gen-go. rm -f $(find . -name '*.pb.go' | grep -v 'grpc_testingv3/testv3.pb.go') -echo "go install github.com/golang/protobuf/protoc-gen-go"-(cd test/tools && go install github.com/golang/protobuf/protoc-gen-go)+echo "go install google.golang.org/protobuf/cmd/protoc-gen-go"+(cd test/tools && go install google.golang.org/protobuf/cmd/protoc-gen-go)

See https://github.com/golang/protobuf/issues/1077 for why it originally existed. The dependency has since been removed, but won't be observable until we release v1.26.0.

dfawley

comment created time in 7 days

PullRequestReviewEvent

issue closedgolang/protobuf

jsonpb allows unmarshaling invalid date

What version of protobuf and what language are you using? protobuf v1.4.0 go 1.15.1

What did you do? Unmarshal a json request containing an invalide date into a proto.Message struct

example:

dest := &Message{} // Which has a UpdatedAt *timestamp.Timestamp attr
err := jsonpb.Unmarshal(strings.NewReader(`{"updated_at":"0000-12-31T23:45:00Z"}`), dest)
assert.Error(t, err)

What did you expect to see? A successful test, an error should be returned as the date is invalid according to validateTimestamp method.

What did you see instead? The test fails, there is no error returned.

closed time in 7 days

cedric-parisi

issue commentgolang/protobuf

jsonpb allows unmarshaling invalid date

The following snippet on the google.golang.org/protobuf module:

ts := new(timestamppb.Timestamp)
err := protojson.Unmarshal([]byte(`"0000-12-31T23:45:00Z"`), ts)
fmt.Println(ts, err)

correctly fails with:

proto: (line 1:1): google.protobuf.Timestamp value out of range: "0000-12-31T23:45:00Z"

The github.com/golang/protobuf module is frozen for any changes except for regression fixes. This issue is not seem to be a regression. Closing this as "wont fix".

cedric-parisi

comment created time in 7 days

issue commentgolang/protobuf

Can you please add documentation for self-describing message usage?

You can obtain a obtain a new anypb.Any representing a message with anypb.New.

There is not quick way to obtain a FileDescriptorSet for a generated message. You'll have to do something like the following:

// FileDescriptorSetForMessage returns the FileDescriptorSet needed to represent m.
func FileDescriptorSetForMessage(m proto.Message) *descriptorpb.FileDescriptorSet {
	fds := new(descriptorpb.FileDescriptorSet)
	seen := map[protoreflect.FileDescriptor]bool{}
	appendFileDescriptor(m.ProtoReflect().Descriptor().ParentFile(), fds, seen)
	return fds
}
func appendFileDescriptor(fd protoreflect.FileDescriptor, fds *descriptorpb.FileDescriptorSet, seen map[protoreflect.FileDescriptor]bool) {
	if seen[fd] {
		return
	}
	seen[fd] = true

	fds.File = append(fds.File, protodesc.ToFileDescriptorProto(fd))

	imps := fd.Imports()
	for i := 0; i < imps.Len(); i++ {
		appendFileDescriptor(imps.Get(i).FileDescriptor, fds, seen)
	}
}

The code above may panic if the messages have not been regenerated with google.golang.org/cmd/protoc-gen-go since it fundamentally depends on first-class support for protobuf reflection.

Sharathnasa

comment created time in 8 days

issue commentgolang/protobuf

Protobuf with go modules

So the "right way" would be to update https://github.com/andreaaizza/sniffer: make proto and commit also .pb.go files?

Correct.

andreaaizza

comment created time in 8 days

delete branch google/go-cmp

delete branch : empty-iface

delete time in 8 days

push eventgoogle/go-cmp

Joe Tsai

commit sha ab46b8bd0abd4c4557cc4709ad7ae12d47570603

Adjust for reflect.Type.NumMethod change in Go1.16 (#240) In Go1.16, the reflect.Type.NumMethod method will no longer report unexported fields, matching the documented behavior on the method. This means that t.NumMethod() == 0 is no longer a reliable means to detect whether an interface type is the empty interface or not. Fix the code to check whether the empty interface itself implements the target type.

view details

push time in 8 days

PR merged google/go-cmp

Adjust for reflect.Type.NumMethod change in Go1.16

In Go1.16, the reflect.Type.NumMethod method will no longer report unexported fields, matching the documented behavior on the method. This means that t.NumMethod() == 0 is no longer a reliable means to detect whether an interface type is the empty interface or not. Fix the code to check whether the empty interface itself implements the target type.

+55 -4

1 comment

4 changed files

dsnet

pr closed time in 8 days

pull request commentgoogle/go-cmp

Adjust for reflect.Type.NumMethod change in Go1.16

See https://github.com/golang/go/issues/22075 for details.

dsnet

comment created time in 8 days

PR opened google/go-cmp

Adjust for reflect.Type.NumMethod change in Go1.16

In Go1.16, the reflect.Type.NumMethod method will no longer report unexported fields, matching the documented behavior on the method. This means that t.NumMethod() == 0 is no longer a reliable means to detect whether an interface type is the empty interface or not. Fix the code to check whether the empty interface itself implements the target type.

+55 -4

0 comment

4 changed files

pr created time in 8 days

create barnchgoogle/go-cmp

branch : empty-iface

created branch time in 8 days

issue openedgolang/go

proposal: reflect: add QualifiedString

#22075 makes it such that NumMethod no longer counts unexported methods for interfaces (and by implication the inability to obtain information about unexported methods).

One use case this breaks is https://github.com/google/go-cmp/blob/566225a2554cf156c7af1006dc3a0940e1e02b09/cmp/internal/value/name.go#L133-L146, where the goal is to print fully-qualified type information about a Go type (i.e., reflect.Type.String is insufficiently detailed). With the NumMethod change, I can no longer print unnamed interfaces in a way where users can tell that they are different.

Should we add a reflect.Type.QualifiedString method that ensures the following property:

t1.QualifiedString() == t2.QualifiedString() if and only if t1 == t2

\cc @mdempsky

created time in 8 days

issue commentgolang/go

proposal: reflect: add StructField.IsExported and Method.IsExported

Another note: #22075 makes it such that NumMethod no longer counts unexported methods, which subsequently means that you can't retrieve unexported methods. If this is the case, we should mark Method.PkgPath as deprecated instead of adding a Method.IsUnexported method.

dsnet

comment created time in 8 days

issue closedgolang/protobuf

protoc-gen-go uses deprecated libraries

Hello,

I posted this on golang-nuts, but hope to reach the maintainers here.

I am coming back to a project after few months, and done upgrading to the latest libraries, but the generated code still points to deprecated versions. I read the blog post, and docs followed the instructions to install and use the library. Please see the below example. I was expecting github.com/golang/protobuf/proto -> google.golang.org/protobuf/proto github.com/golang/protobuf/ptypes/timestamp -> google.golang.org/protobuf/types/known/timestamppb

I see a recent commit to remove legacy proto import, but concerned about timestamp. My code uses google.golang.org/protobuf/types/known/timestamppb, but generated github.com/golang/protobuf/ptypes/timestamp. How can I change it to point to same.

Thanks bsr

proto: (source https://developers.google.com/protocol-buffers/docs/gotutorial)

syntax = "proto3";
package tutorial;

import "google/protobuf/timestamp.proto";

option go_package = "github.com/protocolbuffers/protobuf/examples/go/tutorialpb";

message Person {
  string name = 1;
  int32 id = 2;  // Unique ID number for this person.
  string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
  }

  repeated PhoneNumber phones = 4;

  google.protobuf.Timestamp last_updated = 5;
}

// Our address book file is just one of these.
message AddressBook {
  repeated Person people = 1;
}

command:

$ protoc-gen-go --version protoc-gen-go v1.25.0

protoc -I=. --go_out=. ./addressbook.proto

generated code:

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.25.0
// 	protoc        v3.13.0
// source: addressbook.proto

package tutorialpb

import (
	proto "github.com/golang/protobuf/proto"
	timestamp "github.com/golang/protobuf/ptypes/timestamp"
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	reflect "reflect"
	sync "sync"
)

const (
	// Verify that this generated code is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
	// Verify that runtime/protoimpl is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4

type Person_PhoneType int32

const (
	Person_MOBILE Person_PhoneType = 0
	Person_HOME   Person_PhoneType = 1
	Person_WORK   Person_PhoneType = 2
)

// Enum value maps for Person_PhoneType.
var (
	Person_PhoneType_name = map[int32]string{
		0: "MOBILE",
		1: "HOME",
		2: "WORK",
	}
	Person_PhoneType_value = map[string]int32{
		"MOBILE": 0,
		"HOME":   1,
		"WORK":   2,
	}
)

func (x Person_PhoneType) Enum() *Person_PhoneType {
	p := new(Person_PhoneType)
	*p = x
	return p
}

func (x Person_PhoneType) String() string {
	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}

func (Person_PhoneType) Descriptor() protoreflect.EnumDescriptor {
	return file_addressbook_proto_enumTypes[0].Descriptor()
}

func (Person_PhoneType) Type() protoreflect.EnumType {
	return &file_addressbook_proto_enumTypes[0]
}

func (x Person_PhoneType) Number() protoreflect.EnumNumber {
	return protoreflect.EnumNumber(x)
}

// Deprecated: Use Person_PhoneType.Descriptor instead.
func (Person_PhoneType) EnumDescriptor() ([]byte, []int) {
	return file_addressbook_proto_rawDescGZIP(), []int{0, 0}
}

type Person struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Name        string                `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	Id          int32                 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` // Unique ID number for this person.
	Email       string                `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
	Phones      []*Person_PhoneNumber `protobuf:"bytes,4,rep,name=phones,proto3" json:"phones,omitempty"`
	LastUpdated *timestamp.Timestamp  `protobuf:"bytes,5,opt,name=last_updated,json=lastUpdated,proto3" json:"last_updated,omitempty"`
}

func (x *Person) Reset() {
	*x = Person{}
	if protoimpl.UnsafeEnabled {
		mi := &file_addressbook_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Person) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*Person) ProtoMessage() {}

func (x *Person) ProtoReflect() protoreflect.Message {
	mi := &file_addressbook_proto_msgTypes[0]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Person.ProtoReflect.Descriptor instead.
func (*Person) Descriptor() ([]byte, []int) {
	return file_addressbook_proto_rawDescGZIP(), []int{0}
}

func (x *Person) GetName() string {
	if x != nil {
		return x.Name
	}
	return ""
}

func (x *Person) GetId() int32 {
	if x != nil {
		return x.Id
	}
	return 0
}

func (x *Person) GetEmail() string {
	if x != nil {
		return x.Email
	}
	return ""
}

func (x *Person) GetPhones() []*Person_PhoneNumber {
	if x != nil {
		return x.Phones
	}
	return nil
}

func (x *Person) GetLastUpdated() *timestamp.Timestamp {
	if x != nil {
		return x.LastUpdated
	}
	return nil
}

// Our address book file is just one of these.
type AddressBook struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	People []*Person `protobuf:"bytes,1,rep,name=people,proto3" json:"people,omitempty"`
}

func (x *AddressBook) Reset() {
	*x = AddressBook{}
	if protoimpl.UnsafeEnabled {
		mi := &file_addressbook_proto_msgTypes[1]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *AddressBook) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*AddressBook) ProtoMessage() {}

func (x *AddressBook) ProtoReflect() protoreflect.Message {
	mi := &file_addressbook_proto_msgTypes[1]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use AddressBook.ProtoReflect.Descriptor instead.
func (*AddressBook) Descriptor() ([]byte, []int) {
	return file_addressbook_proto_rawDescGZIP(), []int{1}
}

func (x *AddressBook) GetPeople() []*Person {
	if x != nil {
		return x.People
	}
	return nil
}

type Person_PhoneNumber struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Number string           `protobuf:"bytes,1,opt,name=number,proto3" json:"number,omitempty"`
	Type   Person_PhoneType `protobuf:"varint,2,opt,name=type,proto3,enum=tutorial.Person_PhoneType" json:"type,omitempty"`
}

func (x *Person_PhoneNumber) Reset() {
	*x = Person_PhoneNumber{}
	if protoimpl.UnsafeEnabled {
		mi := &file_addressbook_proto_msgTypes[2]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Person_PhoneNumber) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*Person_PhoneNumber) ProtoMessage() {}

func (x *Person_PhoneNumber) ProtoReflect() protoreflect.Message {
	mi := &file_addressbook_proto_msgTypes[2]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Person_PhoneNumber.ProtoReflect.Descriptor instead.
func (*Person_PhoneNumber) Descriptor() ([]byte, []int) {
	return file_addressbook_proto_rawDescGZIP(), []int{0, 0}
}

func (x *Person_PhoneNumber) GetNumber() string {
	if x != nil {
		return x.Number
	}
	return ""
}

func (x *Person_PhoneNumber) GetType() Person_PhoneType {
	if x != nil {
		return x.Type
	}
	return Person_MOBILE
}

var File_addressbook_proto protoreflect.FileDescriptor

var file_addressbook_proto_rawDesc = []byte{
	0x0a, 0x11, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x70, 0x72,
	0x6f, 0x74, 0x6f, 0x12, 0x08, 0x74, 0x75, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x1a, 0x1f, 0x67,
	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74,
	0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbb,
	0x02, 0x0a, 0x06, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a,
	0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a,
	0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d,
	0x61, 0x69, 0x6c, 0x12, 0x34, 0x0a, 0x06, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0x04, 0x20,
	0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x75, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x50,
	0x65, 0x72, 0x73, 0x6f, 0x6e, 0x2e, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65,
	0x72, 0x52, 0x06, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x0c, 0x6c, 0x61, 0x73,
	0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
	0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
	0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x6c, 0x61, 0x73,
	0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x1a, 0x55, 0x0a, 0x0b, 0x50, 0x68, 0x6f, 0x6e,
	0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65,
	0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12,
	0x2e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e,
	0x74, 0x75, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x2e,
	0x50, 0x68, 0x6f, 0x6e, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22,
	0x2b, 0x0a, 0x09, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06,
	0x4d, 0x4f, 0x42, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x4d, 0x45,
	0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x4f, 0x52, 0x4b, 0x10, 0x02, 0x22, 0x37, 0x0a, 0x0b,
	0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x6f, 0x6f, 0x6b, 0x12, 0x28, 0x0a, 0x06, 0x70,
	0x65, 0x6f, 0x70, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x75,
	0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x70,
	0x65, 0x6f, 0x70, 0x6c, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
	0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x62, 0x75, 0x66, 0x66,
	0x65, 0x72, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x78, 0x61,
	0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x75, 0x74, 0x6f, 0x72, 0x69, 0x61,
	0x6c, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}

var (
	file_addressbook_proto_rawDescOnce sync.Once
	file_addressbook_proto_rawDescData = file_addressbook_proto_rawDesc
)

func file_addressbook_proto_rawDescGZIP() []byte {
	file_addressbook_proto_rawDescOnce.Do(func() {
		file_addressbook_proto_rawDescData = protoimpl.X.CompressGZIP(file_addressbook_proto_rawDescData)
	})
	return file_addressbook_proto_rawDescData
}

var file_addressbook_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_addressbook_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_addressbook_proto_goTypes = []interface{}{
	(Person_PhoneType)(0),       // 0: tutorial.Person.PhoneType
	(*Person)(nil),              // 1: tutorial.Person
	(*AddressBook)(nil),         // 2: tutorial.AddressBook
	(*Person_PhoneNumber)(nil),  // 3: tutorial.Person.PhoneNumber
	(*timestamp.Timestamp)(nil), // 4: google.protobuf.Timestamp
}
var file_addressbook_proto_depIdxs = []int32{
	3, // 0: tutorial.Person.phones:type_name -> tutorial.Person.PhoneNumber
	4, // 1: tutorial.Person.last_updated:type_name -> google.protobuf.Timestamp
	1, // 2: tutorial.AddressBook.people:type_name -> tutorial.Person
	0, // 3: tutorial.Person.PhoneNumber.type:type_name -> tutorial.Person.PhoneType
	4, // [4:4] is the sub-list for method output_type
	4, // [4:4] is the sub-list for method input_type
	4, // [4:4] is the sub-list for extension type_name
	4, // [4:4] is the sub-list for extension extendee
	0, // [0:4] is the sub-list for field type_name
}

func init() { file_addressbook_proto_init() }
func file_addressbook_proto_init() {
	if File_addressbook_proto != nil {
		return
	}
	if !protoimpl.UnsafeEnabled {
		file_addressbook_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Person); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_addressbook_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*AddressBook); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_addressbook_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Person_PhoneNumber); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
	}
	type x struct{}
	out := protoimpl.TypeBuilder{
		File: protoimpl.DescBuilder{
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
			RawDescriptor: file_addressbook_proto_rawDesc,
			NumEnums:      1,
			NumMessages:   3,
			NumExtensions: 0,
			NumServices:   0,
		},
		GoTypes:           file_addressbook_proto_goTypes,
		DependencyIndexes: file_addressbook_proto_depIdxs,
		EnumInfos:         file_addressbook_proto_enumTypes,
		MessageInfos:      file_addressbook_proto_msgTypes,
	}.Build()
	File_addressbook_proto = out.File
	file_addressbook_proto_rawDesc = nil
	file_addressbook_proto_goTypes = nil
	file_addressbook_proto_depIdxs = nil
}

closed time in 9 days

bsr203

issue commentgolang/protobuf

protoc-gen-go uses deprecated libraries

Duplicate of #1033.

The generated import paths for the well-known types is not determined by our module, but rather by a release of the main protobuf toolchain itself.

The relevant changes have already been made to https://github.com/protocolbuffers/protobuf and is only blocked on a new release of the protobuf toolchain. There's nothing left that we can do on our end in this module.

bsr203

comment created time in 9 days

issue commentgolang/protobuf

encoding/protojson: DiscardUnknown doesn't work for unknown enum strings

@cybrcodr mailed out a change for this already. I agree that something should be changed, but it's not clear what should be changed. It's known that C++'s approach is problematic, and sometimes even considered a bug. It's not clear that we should emulate them in this behavior.

jjhuff

comment created time in 9 days

issue commentgolang/protobuf

Suppress warnings during tests

It's been about 6 months since the initial release of the google.golang.org/protobuf module. It's probably about time we turn these warnings into hard errors anyways.

adambabik

comment created time in 12 days

issue commentgolang/protobuf

Protobuf with go modules

The build failure here is entirely expected because the .pb.go files have not be generated. The go build command does not automatically invoke protoc.

andreaaizza

comment created time in 13 days

issue commentgolang/protobuf

Protobuf with go modules

Without a reproduction, I don't know how else to help.

andreaaizza

comment created time in 13 days

issue commentgolang/go

proposal: reflect: add StructField.IsExported

I should note that we probably want to add an IsExported method to the Method type as well since it has a PkgPath field with the same semantics.

dsnet

comment created time in 13 days

issue commentgolang/protobuf

Protobuf with go modules

I just ran the following:

$ git clone https://github.com/andreaaizza/sniffer
$ cd sniffer/
$ protoc --go_out=./ --go_opt=module=github.com/andreaaizza/sniffer logger/logger.proto
$ protoc --go_out=./ --go_opt=module=github.com/andreaaizza/sniffer dissector/dissector.proto
$ protoc --go_out=./ --go_opt=module=github.com/andreaaizza/sniffer sniffer.proto
$ go build ./...
$ go mod vendor
$ go build -mod vendor ./...
$ go version
go version go1.15.2 linux/amd64

Everything worked fine. I don't seem to be able to replicate any problems.

andreaaizza

comment created time in 13 days

issue commentgolang/protobuf

Protobuf with go modules

What about logger.proto? That's the one reporting the build failure with: cannot find package "."

andreaaizza

comment created time in 13 days

issue commentgolang/protobuf

Protobuf with go modules

I suspect that this has nothing to do with modules, but rather a missing or malformed go_package option in the .proto files. What do you have it specified to?

andreaaizza

comment created time in 13 days

issue openedgoogle/go-cmp

Incorrect slice diff output

Consider the following snippet:

got := "Forwarding service bread:foo-http-jean has a same-env shard undrained: aa:jean-shard000. The quick brown fox jumped over the lazy dog."
want := "Forwarding service bread:foo-api-scary has a same-env shard undrained: bb:scary-shard000. The quick brown fox jumped over the lazy dog."
fmt.Println(cmp.Diff(got, want))

Depending on the randomization involved, this sometimes prints:

  strings.Join({
  	"Fo",
- 	"rwar",
+ 	"rwar",
  	"ding service bread:foo-",
- 	"http-jean",
+ 	"api-scary",
  	" has a same-env shard undrained: ",
- 	"aa:jean",
+ 	"bb:scary",
  	"-shard000. The quick brown fox jumped over the lazy dog.",
  }, "")

when it should print:

  strings.Join({
  	"Forwarding service bread:foo-",
- 	"http-jean",
+ 	"api-scary",
  	" has a same-env shard undrained: ",
- 	"aa:jean",
+ 	"bb:scary",
  	"-shard000. The quick brown fox jumped over the lazy dog.",
  }, "")

Not that it incorrectly reports that "rwar" is different? That's wrong.

created time in 13 days

issue closedgolang/protobuf

github.com/protocolbuffers/protobuf: update canonical import paths of well-known types

What version of protobuf and what language are you using?

protoc-gen-go v1.19.0-devel
protoc        v3.11.3

What did you do? Generating google/rps/status.proto which contains import "google/protobuf/any.proto";

What did you expect to see? Go import of "google.golang.org/protobuf/types/known/anypb"

What did you see instead? Go import of "github.com/golang/protobuf/ptypes/any"

Anything else we should know about your project / environment? protoc -I=. --go_out=:. google/api/*

closed time in 13 days

emcfarlane

issue commentgolang/protobuf

github.com/protocolbuffers/protobuf: update canonical import paths of well-known types

Fixed in https://github.com/protocolbuffers/protobuf/commit/dfab275eca9481b5de31122db6fc91b31db3382a

emcfarlane

comment created time in 13 days

issue commentgolang/protobuf

types/dynamicpb: add NewTypes helper

(Note for consideration in the future) We need to decide whether this function keeps a reference to a protoregistry.Files-like object or not. The semantic difference is whether NewTypes iterates through all files up-front or if it simply holds a reference to it and dynamically queries it when each Find method is called. If the latter, I should note that FindExtensionByNumber currently can't be implemented by the lone FindDescriptorByName method on protoregistry.Files.

dsnet

comment created time in 14 days

created taggolang/protobuf

tagv1.4.3

Go support for Google's protocol buffers

created time in 14 days

release golang/protobuf

v1.4.3

released time in 14 days

PR opened google/go-cmp

Fix Diff documentation

The description inaccurately describes the operation of Diff, which is y - x, where a '+' prefix denotes elements added from y and a '-' prefix denotes elements removed from x.

For example: // Consider this call to Diff and its result. x y cmp.Diff({b:2, c:3}, {a:1, b:2}) => {+a:1, b:2, -c:3}

// Consider the same in mathematical notation.
         y - x
{a:1, b:2} - {b:2, c:3} = {+a:1, b:2, -c:3}
+2 -2

0 comment

1 changed file

pr created time in 14 days

create barnchgoogle/go-cmp

branch : doc

created branch time in 14 days

PullRequestReviewEvent

issue commentgolang/protobuf

jsonpb: unexpected output for small negative google.protobuf.Duration (again)

This is not a regression starting in v1.4.x, but one that has been there since v1.1.0. The bug was introduced in #492 over 2.5 years ago. If anything, we probably had the bug in the new module in the first place because we copied similar logic over.

AlekSi

comment created time in 15 days

issue closedgolang/protobuf

Type mismatch - unable to implement gRPC server.

What version of protobuf and what language are you using? libprotoc 3.6.1

What did you do?

  1. Generate grpc from .proto file,
  2. Implement server in another package,

What did you expect to see? Working server.

What did you see instead? Bunch of errors, described bellow.

Anything else we should know about your project / environment? I was following tutorial and everything worked fine, until today. I had to reinstall my environment, and there appeared error while generating code from Proto files:

$ protoc \
--proto_path=pkg/proto \
--go_out=plugins=grpc:. \
pkg/proto/*.proto
--go_out: protoc-gen-go: plugins are not supported; use 'protoc --go-grpc_out=...' to generate gRPC

So I changed it to:

protoc \
    --proto_path=pkg/proto \
    --go_out=. \
    --go-grpc_out=. \
    pkg/proto/*.proto

which generated code successfully.

But my server was implemented inside other package which now causes this error:

cmd/server/main.go:27:30: cannot use umServ (type *service.UserManagementServer) as type pb.UserServiceServer in argument to pb.RegisterUserServiceServer:
        *service.UserManagementServer does not implement pb.UserServiceServer (missing pb.mustEmbedUnimplementedUserServiceServer method)

closed time in 17 days

Shanduur

issue commentgolang/protobuf

Type mismatch - unable to implement gRPC server.

Hi, I recommend filing this at https://github.com/grpc/grpc-go/issues, as that repository is responsible for designing and maintaining the grpc framework.

Shanduur

comment created time in 17 days

issue commentgolang/protobuf

Can you please add documentation for self-describing message usage?

I should note that steps 2 to 4 could be simplified a little if we had #1216.

Sharathnasa

comment created time in 18 days

issue commentgolang/protobuf

Can you please add documentation for self-describing message usage?

You can do the following:

  1. Use protodesc.NewFiles to convert a descriptorpb.FileDescriptorSet into a protoregistry.Files.
  2. Use the anypb.Any.MessageName method to obtain the name of the underlying message in the Any and lookup a protoreflect.MessageDescriptor with protoregistry.Files.FindDescriptorByName. Since the method returns a protoreflect.Descriptor, you'll need to do a type-assertion.
  3. Use dynamicpb.NewMessage to create a new concrete instance of the message from the previously obtained protoreflect.MessageDescriptor.
  4. Use the anypb.Any.UnmarshalTo method to unmarshal from the Any message into the previously instantiated dynamicpb.Message.
Sharathnasa

comment created time in 18 days

PR opened golang/protobuf

ptypes: deprecate the package

Deprecate the ptypes package since all the equivalent functionality is now directly generated with the well-known types themselves.

+82 -5

0 comment

6 changed files

pr created time in 19 days

create barnchgolang/protobuf

branch : deprecate-ptypes

created branch time in 19 days

delete branch golang/protobuf

delete branch : ptypes-deprecate

delete time in 19 days

create barnchgolang/protobuf

branch : ptypes-deprecate

created branch time in 19 days

issue openedgolang/protobuf

types/dynamicpb: add NewTypes helper

The protodesc package provides the ability to go from a descriptorpb.FileDescriptorSet to a protoregistry.Files. However, the latter type is still not useful to use with most of the protobuf module since many APIs expect a interface that only protoregistry.Types implements (e.g., protojson.MarshalOptions.Resolver).

Should we provide helper functionality somewhere that takes a protoregistry.Files-like and creates a protoregistry.Types-like object from it?

The signature for this helper could possibly be something like:

// NewTypes constructs a new types resolver from a file descriptor ranger.
// It constructs protoreflect.MessageTypes and protoreflect.ExtensionTypes
// from descriptors using the dynamicpb package.
//
// The input is implemented by protoregistry.Files.
// The output provides methods similar to protoregistry.Types.
func NewTypes(interface {
	RangeFiles(func(protoreflect.FileDescriptor) bool)
}) interface {
	FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
	FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
	FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error)
	FindMessageByURL(url string) (protoreflect.MessageType, error)
} {
    ...
} 

(The final implementation probably wouldn't return an interface but a concrete type so that we have the freedom to add new methods).

I don't know where such help functionality should live. Reasonable packages could be protodesc, protoregistry, or dynamicpb or somewhere entirely new.

created time in 19 days

issue commentgolang/protobuf

Can you please add documentation for self-describing message usage?

I'm not sure what "an example for self-describing message" means. Does the documentation for the generated code help? See https://developers.google.com/protocol-buffers/docs/reference/go-generated

Sharathnasa

comment created time in 19 days

pull request commentgrpc/grpc-go

protobuf: update all generated code to google.golang.org/protobuf

The logic in grpc-go/encoding/proto/proto.go doesn't seem particularly well suited for the new module. I seem to recall that Damien looked at that in the past and had ideas for how to optimize it.

dfawley

comment created time in 20 days

issue commentgolang/protobuf

protoc-gen-go: remove generated dependency on github.com/golang/protobuf

Was this issue closed prematurely or am I missing something?

That line is an unused constant in the implementation of protoc-gen-go itself. Thanks for pointing that out, I'll remove it. The logic for actually emitting a generated dependency has been removed. See https://go-review.googlesource.com/c/protobuf/+/259901

kevinburkemeter

comment created time in 20 days

issue commentgolang/protobuf

protoc-gen-go: remove generated dependency on github.com/golang/protobuf

In https://github.com/golang/protobuf/issues/1077#issuecomment-617954633, I suggested that we remove this generated hard dependency in 6 months. It's been 7 months since the initial google.golang.org/protobuf module release. It's time to remove this.

kevinburkemeter

comment created time in 21 days

PR opened golang/protobuf

all: rely on protodesc.ToFileDescriptorProto

Use protodesc.ToFileDescriptorProto to retrieve the raw descriptors for legacy support instead of the undocumented ProtoLegacyRawDesc method that we expect v2 to provide.

This change will cause the legacy proto package to incur a dependency on the descriptorpb package.

+5 -16

0 comment

2 changed files

pr created time in 22 days

create barnchgolang/protobuf

branch : filedesc

created branch time in 22 days

issue closedgolang/protobuf

growslice issue

When using proto repeated type field, I encounter the growslice performance issue as call unmarshal function. Do we have a initilize method to pre allocate slice size for a repeated fields.

here is the schema

message User_Data_Record {
    message KVR {
    }
    message NPSegment {
        repeated KVR kv = 1; /// this filed encoutner the growslice performance issue
    }
    repeated NPSegment segments = 1;
   
}

closed time in 22 days

linchuan4028

issue commentgolang/protobuf

growslice issue

Closing as inactionable. The report does not concern the main protobuf implementation.

linchuan4028

comment created time in 22 days

issue closedgolang/protobuf

encoding/protojson: Allow for optional marshaling/unmarshaling of booleans as numeric 1/0

Is your feature request related to a problem? Please describe. It's not uncommon to see protocols over JSON that use numeric 1 or 0 to represent booleans. Although unfortunate, it happens. Notably what the OpenRTB spec defines and the widely used protocol buffers schema for it.

I'd like to submit a change to encoding/protojson that makes marshaling and unmarshaling optionally accept this behaviour.

Describe the solution you'd like For unmarshaling JSON, extend protojson::UnmarshalOptions adding a new option AcceptNumericBoolean such that if is set, the parser will accept numeric values as boolean fields, 1 for true and 0 for false as well as true and false. Any other value is considered an error

type UnmarshalOptions struct {
	...
        // If AcceptNumericBoolean is set, accept numeric values as booleans, 1 for true and 0 for false.
        // Any other value is considered an error
        AcceptNumericBoolean bool
        ...
}

and for marshaling to JSON, extend protojson::MarshalOptions adding a new option UseNumericBoolean such that if set, for boolean fields, the number 1 is emitted instead of true and the number 0 instead of false

type MarshalOptions struct {
	...
        // UseNumericBoolean emits boolean fields with 1 for true and 0 for false 
        UseNumericBoolean bool
        ...
}

This would also affect the behaviour of EmitUnpopulated

Describe alternatives you've considered I've considered other ways to inject encoders and decoders into protojson but that would be a more invasive change and perhaps negatively affect performance.

Additional context I've started working on these changes on a fork and would like to make it available to all if accepted.

closed time in 22 days

bignacio

issue commentgolang/protobuf

encoding/protojson: Allow for optional marshaling/unmarshaling of booleans as numeric 1/0

Closing this as inactionable. Leaving this open in this repo won't get this anywhere as it needs this needs to be brought up and approved at https://github.com/protocolbuffers/protobuf. If the main protobuf project adopts this proposal, we'll implement it here.

bignacio

comment created time in 22 days

issue commentgolang/go

proposal: archive/zip: add already compressed files

If we plan on adding a CreateHeaderRaw, I suspect that we'll need to pay close attention to #22520. We'll want to make sure we get the semantics right from the start, otherwise they will be very hard to change later on.

saracen

comment created time in 22 days

push eventgoogle/go-cmp

colinnewell

commit sha 566225a2554cf156c7af1006dc3a0940e1e02b09

Add an example for IgnoreFields (#205) Add an example for IgnoreFields. This resuses the test data from the example in the cmp package to provide consistency between examples.

view details

push time in 25 days

PR merged google/go-cmp

Create a simple example for the IgnoreFields

I was struggling to figure out how to use IgnoreFields initially so I figured an example would be helpful. I've essentially ripped off the example from the parent module with a proper copy paste job for now with a quick example.

If you can suggest a better way to do it, I'd be happy to clean up the code.

I suspect I could also make the example clearer, but I'd quite like to clean up the code before going too far.

+130 -0

4 comments

1 changed file

colinnewell

pr closed time in 25 days

Pull request review commentgoogle/go-cmp

Create a simple example for the IgnoreFields

+package cmpopts_test

I know people aren't fond of 2020, but that's the current year ;)

colinnewell

comment created time in 25 days

Pull request review commentgoogle/go-cmp

Create a simple example for the IgnoreFields

+// Copyright 2017, The Go Authors. All rights reserved.+// Use of this source code is governed by a BSD-style+// license that can be found in the LICENSE file.++package cmpopts_test++import (+	"fmt"+	"net"+	"time"++	"github.com/google/go-cmp/cmp"+	"github.com/google/go-cmp/cmp/cmpopts"+	"github.com/google/go-cmp/cmp/internal/flags"+)++func init() {+	flags.Deterministic = true+}++// Use IgnoreFields to ignore fields on a type when comparing.+// Provide an interface of the type and the field names to ignore.
// Use IgnoreFields to ignore fields on a struct type when comparing
// by providing a value of the type and the field names to ignore.
// Typically, a zero value of the type is used (e.g., foo.MyStruct{}).
colinnewell

comment created time in 25 days

Pull request review commentgoogle/go-cmp

Create a simple example for the IgnoreFields

+package cmpopts_test++import (+	"fmt"+	"net"+	"time"++	"github.com/google/go-cmp/cmp"+	"github.com/google/go-cmp/cmp/cmpopts"+	"github.com/google/go-cmp/cmp/internal/flags"+)++func init() {+	flags.Deterministic = true+}++// Use IgnoreFields to ignore fields on a type when comparing.+// Provide an interface of the type, and the field names to ignore.+func ExampleIgnoreFields_testing() {+	// Let got be the hypothetical value obtained from some logic under test+	// and want be the expected golden data.+	got, want := MakeGatewayInfo()++	// Note that the Diff will still potentially show ignored fields as different,

I think there's a crucial difference between "in the diff" as you just said versus "as different" as currently written in the example. The former denotes an issues with display representation, while the latter subtly suggests something about the semantics of how the comparison is performed.

Perhaps this is better worded as:

// While the specified fields will be semantically ignored for the comparison,
// the fields may be printed in the diff when displaying entire values
// that are already determined to be different.
colinnewell

comment created time in 25 days

PullRequestReviewEvent
PullRequestReviewEvent

issue commentgoogle/go-cmp

proposal: use generic less function when cmpopts.SortSlices argument is nil

Another subtle complication: it's possible today that cmp.Equal can break the transitive property that one would normally expect for equality.

That is, if a == b and b == c, then one would expect a == c. However, this property does not hold due to the use of approximate equality. For example, let a = 0.5s; b = 0.9s; c = 1.3s and we had some approximate comparison that treated two values as equal if they were within 0.5s of each other. In such a case, a != c since they differ by 0.8s. The fact that the transitive property is violated has significance on how two sequences of unordered elements are ordered in order to compare the sets.

rogpeppe

comment created time in 25 days

Pull request review commentgoogle/go-cmp

Create a simple example for the IgnoreFields

+package cmpopts_test++import (+	"fmt"+	"net"+	"time"++	"github.com/google/go-cmp/cmp"+	"github.com/google/go-cmp/cmp/cmpopts"+	"github.com/google/go-cmp/cmp/internal/flags"+)++func init() {+	flags.Deterministic = true+}++// Use IgnoreFields to ignore fields on a type when comparing.+// Provide an interface of the type, and the field names to ignore.+func ExampleIgnoreFields_testing() {+	// Let got be the hypothetical value obtained from some logic under test+	// and want be the expected golden data.+	got, want := MakeGatewayInfo()++	// Note that the Diff will still potentially show ignored fields as different,

https://github.com/google/go-cmp/blob/d713870ac17fdb9ee5e2ee48ff6562dfb1c0157b/cmp/report_compare.go#L258-L265

colinnewell

comment created time in 25 days

PullRequestReviewEvent

Pull request review commentgoogle/go-cmp

Create a simple example for the IgnoreFields

+package cmpopts_test

Please add license header.

colinnewell

comment created time in 25 days

Pull request review commentgoogle/go-cmp

Create a simple example for the IgnoreFields

+package cmpopts_test++import (+	"fmt"+	"net"+	"time"++	"github.com/google/go-cmp/cmp"+	"github.com/google/go-cmp/cmp/cmpopts"+	"github.com/google/go-cmp/cmp/internal/flags"+)++func init() {+	flags.Deterministic = true+}++// Use IgnoreFields to ignore fields on a type when comparing.+// Provide an interface of the type, and the field names to ignore.+func ExampleIgnoreFields_testing() {+	// Let got be the hypothetical value obtained from some logic under test+	// and want be the expected golden data.+	got, want := MakeGatewayInfo()++	// Note that the Diff will still potentially show ignored fields as different,+	// but only because they are adjacent to other fields that are also different.+	if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(Client{}, "IPAddress")); diff != "" {+		t.Errorf("MakeGatewayInfo() mismatch (-want +got):\n%s", diff)+	}++	// Output:+	// MakeGatewayInfo() mismatch (-want +got):+	//   cmpopts_test.Gateway{+	//   	SSID:      "CoffeeShopWiFi",+	// - 	IPAddress: s"192.168.0.2",+	// + 	IPAddress: s"192.168.0.1",+	//   	NetMask:   {0xff, 0xff, 0x00, 0x00},+	//   	Clients: []cmpopts_test.Client{+	//   		... // 3 identical elements+	//   		{Hostname: "espresso", ...},+	//   		{Hostname: "latte", LastSeen: s"2009-11-10 23:00:23 +0000 UTC", ...},+	// + 		{+	// + 			Hostname:  "americano",+	// + 			IPAddress: s"192.168.0.188",+	// + 			LastSeen:  s"2009-11-10 23:03:05 +0000 UTC",+	// + 		},+	//   	},+	//   }+

Drop blank line.

colinnewell

comment created time in 25 days

more