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

Add rawFractionalSeconds property to GeneralizedTime #53

Merged
merged 4 commits into from
Jun 19, 2024

Conversation

clintonpi
Copy link
Contributor

@clintonpi clintonpi commented May 23, 2024

Motivation:

There is a possible overflow when computing a Double value for fractionalSeconds from an ArraySlice of bytes. Hence, access to this original ArraySlice is necessary.

Modifications:

  • Add rawFractionalSeconds property to GeneralizedTime.
  • Add a new public init to GeneralizedTime that accepts rawFractionalSeconds instead of fractionalSeconds and generates the latter from the former.
  • Adjust the algorithm for computing fractionalSeconds, from a mathematical to a String computation. This is due to the precision mismatch between the mathematical computation and the native Double computation. This issue, which previously did not surface, now does, due to the newly included round-trip conversion from fractionalSeconds to rawFractionalSeconds.
  • Document that _fractionalSeconds is a cached value and _rawFractionalSeconds is the source of truth for the numerical fractional seconds. (This holds directly for the new GeneralizedTime init that takes rawFractionalSeconds. And it holds indirectly for the existing init that takes fractionalSeconds, as no information is lost in the conversion from _fractionalSeconds to _rawFractionalSeconds.)

Result:

  • The rawFractionalSeconds property now provides the original ArraySlice from which fractionalSeconds was computed.
  • GeneralizedTime can now compute fractionalSeconds from the provided rawFractionalSeconds.

@Lukasa
Copy link
Collaborator

Lukasa commented May 24, 2024

@swift-server-bot add to allowlist

@Lukasa Lukasa added the 🔼 needs-minor-version-bump For PRs that when merged cause a bump of the minor version, ie. 1.x.0 -> 1.(x+1).0 label May 24, 2024
}
}

/// The `ArraySlice` of bytes from which `fractionalSeconds` will be computed. (Preserved due to a possible overflow
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's remove the parenthetical here, it's not really necessary.

/// - minutes: The numerical minutes
/// - seconds: The numerical seconds
/// - rawFractionalSeconds: The `ArraySlice` of bytes from which `fractionalSeconds` will be computed.
/// (Preserved due to a possible overflow when computing a `Double` from this `ArraySlice`.)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Again, let's remove the parenthetical.

// Fractional seconds may not be negative and may not be 1 or more.
guard fractionalSeconds >= 0 && fractionalSeconds < 1 else {
throw ASN1Error.invalidASN1Object(
reason: "Invalid fractional seconds"
Copy link
Member

Choose a reason for hiding this comment

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

can we include fractionalSeconds in the reason?

Comment on lines 290 to 300
if lhs.fractionalSeconds < rhs.fractionalSeconds { return true } else if lhs.fractionalSeconds > rhs.fractionalSeconds { return false }

for (lhsByte, rhsByte) in zip(lhs.rawFractionalSeconds, rhs.rawFractionalSeconds) {
if lhsByte != rhsByte {
return lhsByte < rhsByte
}
}

// Since the above `zip` iteration stops at the length of the shorter `Sequence`, finally,
// compare the length of the two `Sequence`s.
return lhs.rawFractionalSeconds.count < rhs.rawFractionalSeconds.count;
Copy link
Member

Choose a reason for hiding this comment

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

Related to the discussion above about the source of truth. I think rawFractionalSeconds should be the source of truth and fractionalSeconds is just a cached/derived value from rawFractionalSeconds as it will always be more precise.

Therefore we might want to get rid of the lhs.fractionalSeconds < rhs.fractionalSeconds check. I'm not certain though as it might be a performance improvement. Is it guaranteed that there isn't a conflict between these two values? I guess so but I'm not sure. Either way, worth a comment that this is just an additional check to improve performance.

With that being said, how expensive is a double comparison compared to iterating over an array if the array is smallish?

Copy link
Collaborator

Choose a reason for hiding this comment

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

A double comparison is very cheap compared to the array iteration. As close to free as makes no odds for us here.

Copy link
Member

@dnadoba dnadoba left a comment

Choose a reason for hiding this comment

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

Great work!

Motivation:

There is a possible overflow when computing a `Double` value for `fractionalSeconds` from an `ArraySlice` of bytes. Hence, access to this original `ArraySlice` is necessary.

Modifications:

- Add `rawFractionalSeconds` property to `GeneralizedTime`.
- Add a new `internal init` to `GeneralizedTime` that accepts `rawFractionalSeconds` instead of `fractionalSeconds` and generates the later from the former.
- Adjust the algorithm for computing `fractionalSeconds`, from a mathematical to a `String` computation. This is due to the precision mismatch between the mathematical computation and the native `Double` computation. This issue, which previously did not surface, now does, due to the newly included round-trip conversion from `fractionalSeconds` to `rawFractionalSeconds`.

Result:

- The `rawFractionalSeconds` property now provides the original `ArraySlice` from which `fractionalSeconds` was computed.
- `GeneralizedTime` can now compute `fractionalSeconds` from the provided `rawFractionalSeconds`.
- Improve error and documentation.
- Change the new `GeneralizedTime` `init` from `internal` to `public`.
Copy link
Collaborator

@Lukasa Lukasa left a comment

Choose a reason for hiding this comment

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

Nice one, thanks!

@Lukasa Lukasa enabled auto-merge (squash) June 19, 2024 15:23
@Lukasa Lukasa merged commit 41bee14 into apple:main Jun 19, 2024
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔼 needs-minor-version-bump For PRs that when merged cause a bump of the minor version, ie. 1.x.0 -> 1.(x+1).0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants