Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proto: document whether custom proto.Unmarshaler should Reset or not #424

Closed
awalterschulze opened this issue Sep 12, 2017 · 5 comments
Closed

Comments

@awalterschulze
Copy link

The proto.Unmarshaler interface expects the generated Unmarshal method to call Reset, as stated in the documentation here https://github.com/golang/protobuf/blob/master/proto/decode.go#L380-L387

// Unmarshaler is the interface representing objects that can
// unmarshal themselves.  The method should reset the receiver before
// decoding starts.  The argument points to data that may be
// overwritten, so implementations should not keep references to the
// buffer.
type Unmarshaler interface {
	Unmarshal([]byte) error
}

If we look at the code at
https://github.com/golang/protobuf/blob/master/proto/decode.go#L407-L422

func Unmarshal(buf []byte, pb Message) error {
	pb.Reset()
	return UnmarshalMerge(buf, pb)
}
func UnmarshalMerge(buf []byte, pb Message) error {
	// If the object can unmarshal itself, let it.
	if u, ok := pb.(Unmarshaler); ok {
		return u.Unmarshal(buf)
	}
	return NewBuffer(buf).Unmarshal(pb)
}
  1. We see that this will cause Reset to be called twice, which I suspect will result in two allocations, instead of one. When you are generating an Unmarshal method, this type of thing really impacts performance.

  2. We can also see from this code that this will mess up the implementation of UnmarshalMerge, as the results will not be unmarshaling will not be merged if Reset is called in the generated Unmarshal method.

I propose we update the comment to not expect the generated Unmarshal method to call Reset.

@bcmills
Copy link
Contributor

bcmills commented Sep 12, 2017

I propose we update the comment to not expect the generated Unmarshal method to call Reset.

Yep, the current comment is just plain wrong. (I think @randall77 has a patch that updates the comment as part of a much larger refactoring. He or @cherrymui may have more information on when that's likely to land.)

@cherrymui
Copy link
Member

Yes, @randall77's patch fixes the comment. @dsnet is working on the landing.

@dsnet
Copy link
Member

dsnet commented Sep 14, 2017

I'm trying to get this work landing in the git repo in the near term, next week or two. but it will land in a separate branch, and will have to sit there for some time.

The new implementation causes much chaos in tests because of brittle tests using reflect.DeepEqual to compare protos. We wrote the cmp package to help perform comparisons in a more correct manner, but it will take time for people to fix tests.

@awalterschulze
Copy link
Author

Thank you for keeping us in the loop :)

It would be great if we could not pull in another dependency though.

@dsnet dsnet changed the title double Reset and broken UnmarshalMerge for generated Unmarshalers. proto: document whether custom proto.Unmarshaler should Reset or not Feb 14, 2018
@dsnet
Copy link
Member

dsnet commented Feb 27, 2018

The dev branch already has the updated comments.

It's unfortunate that we are changing the semantics, but the only sane behavior is for messages not to reset themselves, otherwise it is impossible for UnmarshalMerge to work properly.

@dsnet dsnet closed this as completed Feb 27, 2018
@golang golang locked and limited conversation to collaborators Jun 26, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants