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

Packet switch to receivers, packet modification #92

Merged
merged 20 commits into from
Aug 14, 2018

Conversation

alextarasov1
Copy link
Contributor

@alextarasov1 alextarasov1 commented Jul 10, 2018

Gots has the ability to read packets, but its ability to create or modify packets is very limited. Packet creation is an essential feature for writing unit tests.

Packet type can now use receivers. These changes will not break the existing library interfaces and will remain compatible with old versions of the library. This will be more consistent with the rest of the library (EBP, PSI, PES, SCTE35 all use receivers). Packet creation and modification functionality is added. Almost all fields of the packet can now be read, modified, and created. Error checking was added that checks the packet so it is not corrupted, or in an invalid state.

Old way to create packet:

pkt := SetCC(
	Create(
		uint16(13),
		WithHasPayloadFlag,
		WithPUSI),
	7)

New way to create packet:

pkt := NewPacket()
pkt.SetPID(13)
pkt.SetAdaptationFieldControl(PayloadFlag)
pkt.SetPayloadUnitStartIndicator(true)
pkt.SetContinuityCounter(7)


// invalid returns true if the size of the slice (AdaptationField) is invalid,
// or the adaptation field doesnt exist
func (af AdaptationField) invalid() bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be valid. Calling !invalid() is awkward.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or isValid. Functions/methods that return boolean values should be name interrogatively.

"000000000000000000000000000000000"
)

func createPacketEmptyBody(t *testing.T, header string) (p Packet) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does body mean? If it means "payload" let's call it payload. Body has no specific meaning in MPEGTS.

return
}

func assertPacket(t *testing.T, target Packet, generated Packet) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This swallows all error context. How do we know what failed?

oldValue := af.getBit(index, mask)
if value != oldValue {
if value {
return 1 // growing
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of documenting the return values in-line, write a GoDoc comment for this function that has the return value in it.

@kortschak
Copy link
Contributor

@alextarasov1 It looks like you could also remove adaptationfield/create.go and fix #82 here.

// transportPrivateDataLength returns the length of the transport private data,
// if there is no transport private data then its length is zero.
func (af AdaptationField) transportPrivateDataLength() int {
if af.hasTransportPrivateData() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invert this condition to return early and reduce the mass of text to the right (Go idiom).

// adaptationExtensionLength returns the length of the adaptation field extension,
// if there is no adaptation field extension then its length is zero
func (af AdaptationField) adaptationExtensionLength() int {
if af.hasAdaptationFieldExtension() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

// 0 is returned if the bit is unchanged.
// this can be used to find if a field is growing or shrinking.
func (af AdaptationField) bitDelta(index int, mask byte, value bool) int {
if value != af.getBit(index, mask) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here.

@alextarasov1
Copy link
Contributor Author

@kortschak I won't remove adaptationfield/create.go in this PR (that way the PR is limited to new features only), but it is certainly a possibility in the future.

kortschak added a commit to kortschak/gots that referenced this pull request Aug 7, 2018
It is incorrect and untested and will be replaced by methods in Comcast#92.
Copy link
Collaborator

@WillGunther WillGunther left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing all this work! I appreciate all the attention to detail, and comments. This is really close to being ready for merging, but I would like to have input from some of the other maintainers and the open source community on #98. Many of these functions provide duplicate functionality. While I like the receiver functions, there needs to be a discussion on how to treat the older functions in the packet package.

SOFTWARE.
*/

package packet
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see why this file was not put in packet/adapatationfield as that would cause an import cycle, but it does bother me that now two packages have identical functionality with different functions. This definitely means we have to determine the best course of action for #98

// index is the byte in the packet where the bit is located
// mask is the bitmask that has that bit set to one and the rest of the bits set to zero
// value is the value that the bit will be set to
func (af AdaptationField) setBit(index int, mask byte, value bool) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also feels a bit redundant, but I suppose this makes sense since packet is type [188]byte and adaptationfield is []byte

// parseAdaptationField parses the adaptation field that is present in a packet.
// this function is only used by packet functions.
func parseAdaptationField(p *Packet) AdaptationField {
return AdaptationField(p[:])
Copy link
Collaborator

@WillGunther WillGunther Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems superfluous as it is only used in one spot and is just a type change.

}

// RandomAccess returns the value of the random access field in the packet.
func (af AdaptationField) RandomAccess() (bool, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to get some test coverage on RandomAccess and StreamPriority? Splice point and splice countdown also look like they don't get touched.


// Length returns the length of the adaptation field
func (af AdaptationField) Length() (int, error) {
if len(af) != PacketSize {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to make type AdaptationField [188]byte as well? That way we ditch the error checking? Or I would recommend thinking about making the AdaptationField a slice with variable length that actually represents the length of the field.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just thinking a similar thing. If AdaptationField is type AdaptationField Packet, you get all the conversions for free and the type itself becomes a namespacing tool. (Though see overall comment coming soon).

// FromBytes creates a ts packet from a slice of bytes 188 in length.
// If the bytes provided have errors or the slice is not 188 in length,
// then an error vill be returned along with a nill slice.
func FromBytes(bytes []byte) (pkt Packet, err error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit vague. Perhaps PacketFromBytes or NewPacketFromBytes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will read as packet.FromBytes(b) in user code. This is good API.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. The above comment can be ignored.

@kortschak
Copy link
Contributor

While I like the receiver functions, there needs to be a discussion on how to treat the older functions in the packet package.

My thought, FWIW.

One of them should go away, since being able to do things in two trivially similar ways leads to confusion and style wars. The question is about which and why.

Apologies for the Go101, but it helps explain the reasoning.

In Go methods are used for a variety of reasons:

  1. to satisfy interfaces
  2. to namespace functions
  3. to group documentation
  4. to provide fluent API
  5. style and others

In this case the first is not relevant, since there is only one type, the second has merit, as does the third. In many cases for '*Packet the fourth could be an option if the method returned *Packet, but not for *AdaptationField since many of the methods are errorable (there are ways around this, but not without either adding error state to the packet and adaptation field types, or adding additional types that perform these mutations that hold that sate). The fifth is taste.

Functions are used in the place of generics (when taking interface arguments), or for simple stateless mutations (or world mutations).

Separation of functions into distinct packages is often overdone - here it seems that separating packet and adaptation field is one of those times. The benefits that are gained by doing it are achieved by the method namespacing.

Given the number of functions being provided, ISTM that providing the functions as methods is worthwhile for 2 and 3 above and the para above. It did also seem like the "adaptationfield" name was long for a postentially commonly used package.

Copy link
Collaborator

@WillGunther WillGunther left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. Thanks for all your hard work! I'm going to try to get another approval, and if not we merge early next week anyway.

@WillGunther WillGunther dismissed guygrigsby’s stale review August 14, 2018 15:11

Feedback has been addressed

@WillGunther WillGunther merged commit 4c3c478 into Comcast:master Aug 14, 2018
@WillGunther
Copy link
Collaborator

This is a big change! Thanks for the hard work.

kortschak added a commit to kortschak/gots that referenced this pull request Sep 27, 2018
It is incorrect and untested and will be replaced by methods in Comcast#92.
kortschak added a commit to kortschak/gots that referenced this pull request Nov 10, 2018
It is incorrect and untested and will be replaced by methods in Comcast#92.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants