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 CT::Option #4175

Merged
merged 1 commit into from
Jul 12, 2024
Merged

Add CT::Option #4175

merged 1 commit into from
Jul 12, 2024

Conversation

randombit
Copy link
Owner

GH #4172

@coveralls
Copy link

Coverage Status

coverage: 91.729% (-0.2%) from 91.932%
when pulling 8d376b0 on jack/ct-option
into 3657a72 on master.

Copy link
Collaborator

@reneme reneme left a comment

Choose a reason for hiding this comment

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

Nice idea!

Frankly, I would suggest to stay with the STL's nomenclature of things, though. I.e.

src/lib/utils/ct_utils.h Outdated Show resolved Hide resolved
src/lib/utils/ct_utils.h Outdated Show resolved Hide resolved
src/lib/utils/ct_utils.h Outdated Show resolved Hide resolved
src/lib/utils/ct_utils.h Outdated Show resolved Hide resolved
Comment on lines 212 to 217
/// Returns either the inner value or the alternative, in constant time
T unwrap_or(const T& other) const {
T r = other;
r.conditional_assign(m_is_some, m_value);
return r;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Probably a good idea to scope this to types that actually provide this method. And also make the copy of other visible via the interface (to optimize usage with rvalue refs for instance):

Suggested change
/// Returns either the inner value or the alternative, in constant time
T unwrap_or(const T& other) const {
T r = other;
r.conditional_assign(m_is_some, m_value);
return r;
}
/// Returns either the inner value or the alternative, in constant time
T unwrap_or(T other) const
requires concepts::ct_conditional_assignable<T>
{
other.conditional_assign(m_is_some, m_value);
return other;
}

... and the concept itself:

namespace concepts {

template <typename T>
concept ct_conditional_assignable = requires(T lhs, const T& rhs, Choice c) { lhs.conditional_assign(c, rhs); };

}  // namespace concepts

}

... if you wanted to, you could even provide a variable time overload if the concept isn't fulfilled. Though, I'd consider this fairly reckless, as it would potentially fool the user if their type doesn't model the concept (perhaps even subtly inadvertantly).

Copy link
Owner Author

Choose a reason for hiding this comment

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

I'd be quite against a variable time overload. That said it would be nice if we could handle this more generically, ie CT::Option<uint32_t> is entirely a sensible thing, and it's quite possible to implement unwrap_or in constant time as well, but requires additional help. I'll ponder this.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Nice way of expressing the conditional assignment btw.

I made a change such that value_or can handle either types which explicitly provided an appropriate conditional_assign, and for unsigned integral types which make use of CT::Mask to perform the selection.

@coveralls
Copy link

coveralls commented Jul 8, 2024

Coverage Status

coverage: 91.74% (+0.01%) from 91.726%
when pulling 07dbfb2 on jack/ct-option
into 7fad1d2 on master.

@randombit randombit force-pushed the jack/ct-option branch 3 times, most recently from 713755f to b46632e Compare July 8, 2024 19:02
@randombit randombit marked this pull request as ready for review July 8, 2024 19:02

namespace Botan_Tests {

class CT_Mask_Tests final : public Test {
Copy link
Owner Author

Choose a reason for hiding this comment

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

I moved the CT tests to a new file as test_utils.cpp is getting quite large. The Mask and Choice tests are unchanged

Copy link
Collaborator

@reneme reneme left a comment

Choose a reason for hiding this comment

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

Perhaps also constexpr the remaining member functions. I don't see why any of them wouldn't be constexpr.

src/lib/utils/ct_utils.h Outdated Show resolved Hide resolved
src/lib/utils/ct_utils.h Outdated Show resolved Hide resolved
Comment on lines 467 to 471
/// Either returns the value or throws an exception
T value() const {
BOTAN_STATE_CHECK(m_is_some.as_bool());
return m_value;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should that really return a copy of m_value? Maybe:

Suggested change
/// Either returns the value or throws an exception
T value() const {
BOTAN_STATE_CHECK(m_is_some.as_bool());
return m_value;
}
/// Either returns the value or throws an exception
const T& value() const {
BOTAN_STATE_CHECK(m_is_some.as_bool());
return m_value;
}

... for completeness, std::optional defines this both for lvalue and rvalue this. Not sure we really need that, though. It just optimizes cases where a temporary is passed on.

takes_an_Option_by_value(returns_an_option_by_value().value())

Copy link
Owner Author

Choose a reason for hiding this comment

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

I suppose I was thinking of Option as largely for holding types that are cheap to copy (integers, simple arrays, etc) where this wouldn't matter, but yeah no reason not to return a reference here, CT::Option<vector<uint8_t>> is entirely sensible and might indeed be something worth using in PK decryption or the like.

Copy link
Collaborator

Choose a reason for hiding this comment

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

When we foresee to use it with std::vector<uint8_t> it would actually make sense to implement the lvalue/rvalue overloads that std::optional<> implements. In fact, this just made me re-think the strong_type_unwrap() helper: https://github.com/randombit/botan/pull/4170/files#r1672372281

Copy link
Owner Author

Choose a reason for hiding this comment

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

Reading https://stackoverflow.com/questions/24824432/what-is-use-of-the-ref-qualifier-const I am unconvinced by const && and don't really see benefit. I'm going to merge this as is and if/when Option of non-trivally-copyable types comes up we can consider the best course.

src/lib/utils/ct_utils.h Outdated Show resolved Hide resolved
GH #4172

Co-authored-by: René Meusel <[email protected]>
@reneme
Copy link
Collaborator

reneme commented Jul 10, 2024

Perhaps its worth to reconsider this before merging.

@randombit randombit merged commit e280c77 into master Jul 12, 2024
39 checks passed
@randombit randombit deleted the jack/ct-option branch July 12, 2024 01:55
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.

None yet

3 participants