8000 Add routines to compare measurements against reference by jraman567 · Pull Request #167 · veraison/corim · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add routines to compare measurements against reference #167

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

Merged
merged 7 commits into from
Feb 26, 2025

Conversation

jraman567
Copy link
Collaborator

Adds a compare routine to some of the measurement types.

@jraman567
Copy link
Collaborator Author

I can't figure out the lint error; I'm unsure how to fix it.

10000

@thomas-fossati
Copy link
Contributor

I can't figure out the lint error; I'm unsure how to fix it.

I suggest giving a go at:

goimports -local "github.com/veraison/corim" -w .

@jraman567
Copy link
Collaborator Author

I suggest giving a go at:

goimports -local "github.com/veraison/corim" -w .

Thanks, @thomas-fossati; that helped to fix it. As Dionna suggested in the meeting, the fix needed a new line between "fmt" and the following line.

I'm working on the test coverage and will post a new revision of the changes shortly. Thank you!

Copy link
Contributor
@setrofim setrofim left a comment

Choose a reason for hiding this comment

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

As a general note, it's weird for Compare() method to raise an error. Two values not being equal is not automatically considered an error condition. Also, I would expect a method called "Compare" to do a relative comparison, rather than equality test.

I would rename the method to Equal and have it return a bool instead.

(Alternatively, you could keep it as "Compare" and have it return an int, with 1 indicating o > r, -1 indicating r > o, and 0 indicating o == r; however, that would require chainging the implementations as well, and would require for all types in question to have partial ordering, which I don't think we want.)

@thomas-fossati
Copy link
Contributor

Thanks, @thomas-fossati; that helped to fix it. As Dionna suggested in the meeting, the fix needed a new line between "fmt" and the following line.

Do you use vim or VS Code? If so, the popular go plugins (e.g., vim-go) should automatically update the import statements order transparently.

Copy link
Contributor
@thomas-fossati thomas-fossati left a comment

Choose a reason for hiding this comment

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

I agree with @setrofim high-level comment.

I think we should have:

  • a Compare method for types for which ordering makes sense, e.g., SVNs -- and it should return -1,0,1 rather than error;
  • an Equal method for types for which we are only concerned about strict equality (e.g., digests) -- and this could return a bool.

@setrofim
Copy link
Contributor
setrofim commented Feb 12, 2025

an Equal method for types for which we are only concerned about strict equality (e.g., digests)

I think we should have Equal for the other types as well (even if it's just implemented as o.Compare(r) == 0), since that would allow you to handle of values unin 8000 formly as interface{ Equal() bool } in situatations where you only care about exact values.

@thomas-fossati
Copy link
Contributor

an Equal method for types for which we are only concerned about strict equality (e.g., digests)

I think we should have Equal for the other types as well (even if it's just implemented as o.Compare(r) == 0), since that would allow you to handle of values uninformly as interface{ Equal() bool } in situatations where you only care about exact values.

WFM

@jraman567
Copy link
Collaborator Author

As a general note, it's weird for Compare() method to raise an error. Two values not being equal is not automatically considered an error condition. Also, I would expect a method called "Compare" to do a relative comparison, rather than equality test.

I also prefer a boolean return value. If we cannot perform the comparison due to an error, should we log it and return false for a result?

I would rename the method to Equal and have it return a bool instead.

It's not always equality; the type governs the semantics of comparison.

In the SVN type, for example, the comparison should be equality for the ExactValueType and greater-than-or-equal for MinValueType.

@jraman567
Copy link
Collaborator Author

OK, I'll push a PR with recommended changes. Thanks @thomas-fossati & @setrofim for the review.

@jraman567 jraman567 force-pushed the measurement-compare branch 2 times, most recently from 1695f74 to 4b374fc Compare February 12, 2025 16:58
@jraman567
Copy link
Collaborator Author

@thomas-fossati @setrofim could you please confirm if the PR looks good? Thanks!

Copy link
Contributor
@thomas-fossati thomas-fossati left a comment

Choose a reason for hiding this comment

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

Looks good, thanks @jraman567 !

There's one point to discuss about what to do with RawValue.

@jraman567
Copy link
Collaborator Author
jraman567 commented Feb 20, 2025

@thomas-fossati and @setrofim, I have addressed your comments. Additionally, I made the following changes:

  • Added two methods for each measurement type: Equal & Compare.
    • Equal compares an object with another object, it usually confirms if one object is an identical copy of another. This is something Sergei wanted
    • Compare method (note @setrofim , you wanted to name it as Check, but I'm going with Compare as it matches with the spec. Please confirm if that's OK) compares an object with a reference per the CoRIM spec
  • The latest version adds Compare methods for SVN, Digests, RawValue & IntegrityRegisters. I appreciate your review of these types once again
  • In some cases (Version, for example), both Equal and Compare do the same function. But I'm defining both for consistency.

Also, the lint error that CI reports (Implicit memory aliasing in for loop), is a false positive. It doesn't apply for golang versions 1.22 and newer.

Thank you for your review!

@setrofim
Copy link
Contributor
setrofim commented Feb 21, 2025

Compare method (note @setrofim , you wanted to name it as Check, but I'm going with Compare as it matches with the spec. Please confirm if that's OK) compares an object with a reference per the CoRIM spec

"Compare" as an operation has fairly well established semantics, which your methods do not implement. For example, in Go standard library:

https://pkg.go.dev/strings#Compare
https://pkg.go.dev/bytes#Compare
https://pkg.go.dev/cmp#Compare

In other libraries:

https://pkg.go.dev/github.com/open-policy-agent/opa/ast#Compare
https://pkg.go.dev/golang.org/x/mod/semver#Compare
https://pkg.go.dev/github.com/prometheus/prometheus/model/labels#Compare

And in other languages, stretching all the way back to C (in the form of strcmp and memcmp). Introducing a "compare" with different semantics is needlessly confusing and is begging to be misused.

Please also note that the spec specifically talks about "Comparing a condition ECT against the ACS", and not about comparing two values of those types in general. The defined operation is not a generic comparison, and so the method name should not imply that it is. If you wish to stay closer to the spec, perhaps something like CompareAgainstReference or CompareToCondition would work.

Please rename.

@jraman567
Copy link
Collaborator Author

Sounds reasonable. I'll change the name to CompareAgainstReference.

- refactor Version type into a separate file
- add equality and comparison methods

Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
- Add methods to test the equality of TaggedSVN & TaggedMinSVN types
- Add comparison methods for TaggedSVN as outlined in the spec

Signed-off-by: Sergei Trofimov <sergei.trofimov@arm.com>
Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
comid/digests.go Outdated
@@ -41,6 +43,54 @@ func (o Digests) Valid() error {
return nil
}

func (o *Digests) Equal(r *Digests) bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we're not modifying o, should this method have an object, rather than pointer, receiver (ditto for all other methods)?

At one point, we had the convetion that for methods that do not modify the internal state should have an object receiver to indicate that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

My personal opinion is to avoid mixing receiver types; we should pick a pointer or value type and stay consistent for the whole object. If an object is immutable, it should have a value type; it can take a pointer type otherwise.

I’m obviously unaware of your convention. Please confirm if you want to stick to a single type for an object or change the receiver type depending on whether the method modifies the object. Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have a strong opinion on this. By using pointer receivers you avoid copying ponetially large values (here, Digests is already a slice, which is tiny, so we don't win much in this case); by using a value receiver you do not risk modifying state from a method that is not intended to do so, and clearly communicate that (lack of) intent to the reader.

I don't see the advantage of avoiding switching receiver type between methods, depending on what the method needs. Consitently using pointer receiver in methods that don't need them just because other methods modify state helpes neither readability nor usability.

However, I don't see this as a major issue. So if the others are ok with it, I'm fine with code going in as-is.

Copy link
Contributor
@thomas-fossati thomas-fossati Feb 24, 2025

Choose a reason for hiding this comment

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

I don't have a strong opinion on this. By using pointer receivers you avoid copying ponetially large values (here, Digests is already a slice, which is tiny, so we don't win much in this case); by using a value receiver you do not risk modifying state from a method that is not intended to do so, and clearly communicate that (lack of) intent to the reader.

I don't see the advantage of avoiding switching receiver type between methods, depending on what the method needs. Consitently using pointer receiver in methods that don't need them just because other methods modify state helpes neither readability nor usability.

However, I don't see this as a major issue. So if the others are ok with it, I'm fine with code going in as-is.

I don't see a strong argument for changing the "good ole" convention 😃

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I switched the receiver types to the one @setrofim proposed. However, the linter complained that the receiver for FlagsMap was too big, so I reverted it to a pointer.

I did the same for Digests & IntegrityRegisters, which have the potential to get big. All others follow the existing convention.

Copy link
Contributor

Choose a reason for hiding this comment

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

I am still pondering whether it's better (or worse) to have an inconsistent situation (both pointer & value receiver for "read-only" methods) or one that allows unexpected modifications of the underlying object (i.e., pointer receiver in all cases).

Copy link
Contributor

Choose a reason for hiding this comment

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

My 2 cents:

  • The advantage of allowing pointer receivers for read-only is real, if
    negligible. You avoid copying large objects (FlagsMap -- as
    @thomas-fossati noted, Digests (and IntegrityRegisters also) is a slice,
    so we'd only be copying an additional two ints in those cases).
  • The advantages of forcing value receivers for read-only are speculative.
    Accidental modification of state would be caught through tests or code
    review. And it's unclear what the marginal use is of the convention in
    documenting a method's behaviour (it's read-only) beyond the method's name
    and just looking thorough its implementation.

We have 1 concrete but negligible points for pointer receivers vs two
speculative points for value receivers on read-only methods.

I don't think there is a clear winner on merit here.

The other thing to consider is consistency with the rest of the code base. I
believe, until now, we have stuck with the convention that read-only methods
have value receivers. Linter complaints can be suppressed per-line using
//nolint as needed.

But like I said above, personally I don't have a strong preference either way.

</bike shedding>

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sorry, @thomas-fossati . I missed your comment from yesterday.

Due to how Golang passes slices around, could we even have read-only methods for slices since the methods could modify them partially (they have access to the underlying array)?

Copy link
Contributor
@setrofim setrofim Feb 25, 2025

Choose a reason for hiding this comment

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

It would be read-only with respect to the slice (i.e. you would not be able to, e.g. append elements), but not with respect to its contents.

"read-only" also may not be, strictly speaking, correct here. You're still able to modify the receiver variable, even with a value receiver. However, since it is a copy, the modifications will not be visible outside the method.

Copy link
Contributor

Choose a reason for hiding this comment

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

The other thing to consider is consistency with the rest of the code base. I
believe, until now, we have stuck with the convention that read-only methods
have value receivers. Linter complaints can be suppressed per-line using
//nolint as needed.

👍

comid/digests.go Outdated
@@ -41,6 +43,54 @@ func (o Digests) Valid() error {
return nil
}

func (o *Digests) Equal(r *Digests) bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have a strong opinion on this. By using pointer receivers you avoid copying ponetially large values (here, Digests is already a slice, which is tiny, so we don't win much in this case); by using a value receiver you do not risk modifying state from a method that is not intended to do so, and clearly communicate that (lack of) intent to the reader.

I don't see the advantage of avoiding switching receiver type between methods, depending on what the method needs. Consitently using pointer receiver in methods that don't need them just because other methods modify state helpes neither readability nor usability.

However, I don't see this as a major issue. So if the others are ok with it, I'm fine with code going in as-is.

comid/digests.go Outdated
@@ -41,6 +43,54 @@ func (o Digests) Valid() error {
return nil
}

func (o *Digests) Equal(r *Digests) bool {
Copy link
Contributor
@thomas-fossati thomas-fossati Feb 24, 2025

Choose a reason for hiding this comment

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

I don't have a strong opinion on this. By using pointer receivers you avoid copying ponetially large values (here, Digests is already a slice, which is tiny, so we don't win much in this case); by using a value receiver you do not risk modifying state from a method that is not intended to do so, and clearly communicate that (lack of) intent to the reader.

I don't see the advantage of avoiding switching receiver type between methods, depending on what the method needs. Consitently using pointer receiver in methods that don't need them just because other methods modify state helpes neither readability nor usability.

However, I don't see this as a major issue. So if the others are ok with it, I'm fine with code going in as-is.

I don't see a strong argument for changing the "good ole" convention 😃

@jraman567
Copy link
Collaborator Author

@thomas-fossati @setrofim Thanks for your review! I addressed all your comments, except for the receiver types for FlagsMap, Digests and IntegrityRegisters; I explained the reason above. Thanks!

@jraman567 jraman567 self-assigned this Feb 25, 2025
- add a method to test if two digests are equal
- add a method to compare a digests object against reference, as outlined in
  the CoRIM spec

Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
Add equality and comparison methods for FlagsMap object

Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
- add a method to test if a RawValue type equals another one
- add a comparison method to check against a reference

Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
Add equality and comparison methods to test against reference

Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
- Add a method to test if an IntegrityRegisters object equals another
- Add a method to compare IntegrityRegisters object against a reference per CoRIM spec

Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
Copy link
Contributor
@thomas-fossati thomas-fossati 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 the changes (and the patience)!

@@ -41,6 +42,86 @@ func (o Digests) Valid() error {
return nil
}

// Equal confirms if the Digests object is the same as another
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Equal confirms if the Digests object is the same as another
// Equal confirms if the Digests object is equal

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sorry, @yogeshbdeshpande . I didn't see your comments. Let me submit another PR with these changes.

return false
}

ref := refDigests
Copy link
Contributor

Choose a reason for hiding this comment

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

Is ref needed ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I got a linter error if I used refDigests directly. So, I created a copy of it.

func (o RawValue) Equal(r RawValue) bool {
return reflect.DeepEqual(o, r)
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please add a header comment to this method and reference the comparison section!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK, will do

return ret == 0
}

// Compare helper function to compare two SVNs, object and reference
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Compare helper function to compare two SVNs, object and reference
// compare is a helper function to compare two SVNs, object and reference

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK, will change it

Copy link
Contributor
@yogeshbdeshpande yogeshbdeshpande left a comment

Choose a reason for hiding this comment

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

I have left few minor comments, but in general code looks very good to me!

Thanks for making this useful change!

@jraman567
Copy link
Collaborator Author

Thanks, @thomas-fossati & @setrofim for reviewing!

@jraman567 jraman567 merged commit 9221fd7 into veraison:main Feb 26, 2025
5 checks passed
@jraman567 jraman567 deleted the measurement-compare branch February 26, 2025 19:17
@jraman567 jraman567 restored the measurement-compare branch February 26, 2025 19:33
@jraman567
Copy link
Collaborator Author

Sorry, @yogeshbdeshpande . I didn't see your comment before submitting; I guess I opened the PR page when Thomas approved it, and I was in a meeting at that time. I merged it when the meeting got over, without refreshing the page.

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.

4 participants
0