-
Notifications
You must be signed in to change notification settings - Fork 88
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
packet: use [188]byte as Packet type #84
Conversation
@@ -4,13 +4,13 @@ import "github.com/Comcast/gots/packet" | |||
|
|||
func SetPrivateData(pkt *packet.Packet, af []byte) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the potential for failure here suggests that SetPrivateData
should return an error.
A quick review of this really brings to light some good stuff. I am going to need some time to take a proper look and do some testing. |
Yeah, no worries. I'm happy to discuss my thinking in this. Even if there is not a desire to move to the statically sized type, there are a lot of changes here in style that you should probably make. |
While I'm thinking about this, there's a slight increase in buffer copying here which may or may not have a performance impact that you care about, so benchmarking is worthwhile (there are likely wins that may make up for it). There is a possibility of doing zero-copy conversion using unsafe, but that brings with it a risk of some memory over use if clients are using the cap-slicing operator. I think that in general, the copying is probably the better approach even ignoring that issue, but for posterity:
This probably should not be used, and if it is, only for converting |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, I think that there is a lot of value in the compile time checks and the elimination of the length check for every Packet
operation. The real problem is that it breaks all dependent projects. I would like to get opinions from those projects. @paulboschert @mikereedell @jessejlt @ianeckart @davemt @tmm1 @stilldavid Can any of you offer an opinion?
emptyByteArray []byte | ||
) | ||
|
||
type doneFunc func([]byte) (bool, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why eliminate this type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It gives you very little. The only thing it saves you from is another named function type, but this is unexported to the surface is minuscule and it causes readability and documentation to suffer.
To expand on this, your NewAccumulator
takes a doneFunc
, but what is this? A users needs to read the source because it's unexported and so doesn't show up in the godoc. And because it's unexported they have to use a func([]byte) (bool, error)
anyway which is assignable to a doneFunc
but is not the same type. If it were exported there would be a marginal benefit, but still it would impact on readability since "What is a packet.DoneFunc anyway?.... [navigates to definition... Ah, OK." and still most people will use an unnamed type closure or function value anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is some value in the label doneFunc
as it makes it easier to understand what is happening. That label can be used in accumulator
and NewAccumulator
instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmmm. What I'd do would be NewAccumulator(func(data []byte) (done bool, err error)) Accumumulator
, which explains far more than what currently exists.
BTW @guygrigsby Why is accumulator
hidden behind an Accumulator
? Is there a plan for alternative implementations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no plan for any additional implementations. IIRC, the possibility existed when we created it, but obviously that has not come to be. It's possible that doneFunc
makes sense to me because I am used to starting at it. I am a big fan of self documenting code and I think that func
types are a great way to do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WRT doneFunc
, it actually reduced code documentation except for people reading the source, and moves the identity of the type away from the use, to the definition of the type. To demonstrate, read the godoc for packet.NewAccumulator
. Now, as a person who wants to use packet.NewAccumulator
, but has not read the source and is new to gots (there are many Go users who don't read source of packages they use - I don't understand why, but it's a thing), exactly what is a doneFunc
type? The documentation is completely absent.because the type not exported, so you would need to navigate to the package.NewAccumulator
definition in the source in the hope that type doneFunc
is defined near near it, which in this case it is-ish. If you look at the case in the stdlib that take func types, generally the types are only named types when they have methods on them, or they are usually not user-facing types http.HandlerFunc
is an obvious case of user-facing named func type, but it has a method.
For the use of interfaces here, I wouldn't. Again this use reduced the visibility of documentation because you lose links from the godoc to method implementations. You can see this by navigating to packet.Accumulator
. If it were a concrete type, you would be able to follow the links to source. This doesn't help those mentioned above who don't read the source, but is a boon for people who do when the documentation is not helpful enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW There is no handling of the case of a nil parameter here. Is there a sensible default? If not, it should probably be guarded and NewAccumulator
should return an error.
Also, I'm wondering if it's not more sensible to just have the packets
and f
fields exported. Is there anything you are particularly protecting against there?
@@ -38,7 +38,7 @@ func ExamplePacketAccumulator() { | |||
|
|||
secondPacket, _ := hex.DecodeString("47006411f0002b59bc22ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") | |||
|
|||
packets := []Packet{firstPacket, secondPacket} | |||
packets := [][]byte{firstPacket, secondPacket} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a few changes like this. Changing to []byte
from Packet
. What's the benefit? We lose the compile time checking, which is why the change to [188]byte
would be so beneficial, IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it's not a packet, it's a []byte
, having been returned by a stdlib function that returns []byte
. This change is in tests.
} | ||
|
||
// WithHasPayloadFlag is an option function for creating a packet with a payload flag | ||
func WithHasPayloadFlag(pkt *Packet) { | ||
(*pkt)[3] = byte((*pkt)[3] | 0x10) | ||
pkt[3] |= 0x10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't even know this was a thing. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Array pointers are indexable - this is very useful for range
where otherwise you'd copy.
b[3] = byte(pts >> 7 & 0x7f) | ||
b[2] = byte(pts >> 14 & 0xff) | ||
b[1] = byte(pts >> 22 & 0x4f) | ||
b[0] = byte(pts >> 29 & 0x07) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do we gain by this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Elision of subsequent bounds checks.
} | ||
pkt := new(Packet) | ||
if copy(pkt[:], b) != PacketSize { | ||
panic("bad test (wrong length): " + h) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is only a test, but I would prefer not to panic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are two levels of failure in a test, failure of the code to perform the tests correctly and failure of the tests to do what they are believed to do. This checks for the latter and should shout at you. The input to this is static compile time (authoring time actually) entity., which is exactly what panics were designed for.
I think this is worth the API breakage, but I am a relatively new user and don't have a lot of code using the library. |
ping |
@jessejlt Thoughts? |
So I think there is some consensus that these are good changes. We are waiting to get a review from one of the main internal consumers of this library, and then if they sign off we will merge it. @kortschak if you get a chance could you update the branch? |
Thanks. Done. Let me know when you are ready. |
Is there a timeline for a response here? I have fixed the conflicts, but now I need to do that again and given the existence of #92, I'm concerned that I will have to do that again, with a significant amount of work the result. |
This obviates a collection of runtime testing (moving it to compile time), removes a few test cases that can no longer fail, likely improves performance by eliding bounds checks in constant index look-ups, reduces space very slightly and allows additional uses of Packet that were not previously available (though possibly not useful: e.g. using Packet as a map key). A number of other unrelated changes were made that simplify code and reduce line count.
Right, sorry about the delay. We are still waiting on that one review. Holding off on all merges until that review comes through. We have gotten a commitment that this will be reviewed by the end of the week. I've also talked to the author of #92 and he is willing to make the changes on his side for the 188 bytes. |
@@ -33,6 +33,18 @@ import ( | |||
"github.com/Comcast/gots/packet" | |||
) | |||
|
|||
func parseHexString(h string) *packet.Packet { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like how this function is declared in three separate places, but that's something that can be addressed later.
Alright! Finally merging. Thanks for your patience @kortschak |
Thanks for the review. |
NOT FOR SUBMISSION WITHOUT DISCUSSION
See #83 (comment)
This obviates a collection of runtime testing (moving it to compile time), removes a few test cases that can no longer fail, likely improves performance by eliding bounds checks in constant index look-ups, reduces space very slightly and allows additional uses of Packet that were not previously available (though possibly not useful: e.g. using Packet as a map key).
A number of other unrelated changes were made that simplify code and reduce line count.