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
MINIFICPP-710 - Fix errors in Property StringToInt conversion #472
Conversation
@@ -107,3 +107,28 @@ TEST_CASE("Test Trimmer Left", "[testTrims]") { | |||
REQUIRE(test.c_str()[1] == ' '); | |||
} | |||
|
|||
TEST_CASE("Test int conversion", "[testStringToInt]") { |
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.
Most of these would fail without 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.
Almost all of these are invalid and shouldn't be accepted, though (not the changes -- the actual entries like - 2 GB ). This was the point of https://issues.apache.org/jira/browse/MINIFICPP-707. I don't want to trounce on this PR, but the reason I created the ticket was that some of these SHOULD be prevented from being used at all in configs, fail early. Therefore it's not as simple as doing what is done here. There needs to be more thought put into this, this was the primary reason I created the ticket versus implementing.
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 think my last comment was entirely helpful so I went through and tried to parse out the parts that are appropriate and SHOULD be accepted. In retrospect I think I was being a bit terse jumping between tasks. Sorry if that comment didn't provide much insight into my thought process.
Hopefully I was more clear on what we can immediately accept and what I think needs more discussion. I'm totally okay if that discussion yields pushing some of the concerns down the road if it makes sense.
@@ -129,7 +129,7 @@ const C2Payload RESTProtocol::parseJsonResponse(const C2Payload &payload, const | |||
} catch (...) { | |||
} | |||
#endif | |||
return std::move(C2Payload(payload.getOperation(), state::UpdateState::READ_COMPLETE, true)); |
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.
These generated copy-elision warnings in clang, so removed.
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.
Good catch. I rarely build with clang these days...
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.
-1
REQUIRE(uint32_var == expected_value); | ||
|
||
int32_t int32_var = 0; | ||
REQUIRE(Property::StringToInt("3GB", int32_var) == false); // Doesn't fit |
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'll have to look at the changes more closely but we shouldn't be worrying about int32, we're almost exclusively uint64 and when we're not ( such as someone saying a type is int ), we should be okay up-converting. It's possible that someone sets a type, but I don't think we can say at this prospect if the type fits or doesn't fit
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.
One of the things that the big PR left as a todo was to write tests with conversions, so I will readily admit that there may be some cleanup.
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.
If we only aim for uint64, I think we should consider having this function only for uint_64t instead of the current template solution and provide possibility to safely cast for smaller types (like I did in AssignIfFits).
As this would introduce a breaking change, not sure when it can be done properly.
int32_t int32_var = 0; | ||
REQUIRE(Property::StringToInt("3GB", int32_var) == false); // Doesn't fit | ||
|
||
REQUIRE(Property::StringToInt("-1 G", int32_var) == true); |
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.
We should almost exclusively not allow signed Byte values in string to int. It should always be positive.
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.
Okay, thanks, will change.
REQUIRE(Property::StringToInt("-1 G", int32_var) == true); | ||
REQUIRE(int32_var == -1 * 1000 * 1000 * 1000); | ||
|
||
REQUIRE(Property::StringToInt("-1G", uint32_var) == false); // Negative to uint |
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.
This shouldn't be allowed. Whether this barrier exists here or elsewhere is what I think we should discuss. It's about the end to end correctness, so not sure if the appropriate action is to fix the signedness issues or correct the overall issue that 707 was hoping to look into. I fear that resolving any issues like signed values without looking into solving the whole picture takes us to an unknown state. Thoughts?
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.
Agreed, however as you see, it's not allowed, this conversion fails.
Although I think the current implementation is still more forgiving than it should be according to the whole picture, so let's try to explore that before going ahead with this.
|
||
//Signed | ||
template<typename T, typename std::enable_if<std::is_signed<T>::value, T>::type* = nullptr> | ||
static bool AssignIfFits(int64_t base, uint64_t mult, T& output) { |
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.
These I like and think we should use this.
auto ival = std::strtoll(cvalue, &pEnd, 0); | ||
|
||
T ival = 0; | ||
if(!StringToNum(cvalue, &pEnd, ival)) { |
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.
This is only applicable if we reach the null terminator and it is not a byte string
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.
Not sure I get the point, this does the same strto(u)ll call inside that was here before.
The only change is the return value, which comes from AssignIfFits.
libminifi/include/core/TypedValues.h
Outdated
//Unsigned | ||
template<typename T, typename std::enable_if<!std::is_signed<T>::value, T>::type* = nullptr> | ||
static bool StringToNum(const char* str_begin, char **str_end, T& output) { | ||
if(str_begin[0] == '-') { |
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.
can't assume that it's zero, and that it's non-empty
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.
Good shout, thanks!
This also means adding a tectcase to check with empty and invalid strings to make sure it handles errors gracefully.
} | ||
return false; | ||
} | ||
|
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.
Does this code compile in windows? I'm not sure that it will.
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.
Appveyor showed that it didn't. Do you have access to windows? I can probably help with that since I do.
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.
Yes, please! I can check Appveyor err messages (those made me find the fix), but that's not so efficient in longer term.
REQUIRE(Property::StringToInt("-2", uint64_var) == false); // Negative shouldn't be converted to uint | ||
|
||
uint32_t uint32_var = 0; | ||
REQUIRE(Property::StringToInt("3 GB", uint32_var) == true); // Test skipping of spaces, too |
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.
Some of these tests exist in yaml if I recall correctly ( but I didn't write them ), if you're interested.
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.
Surely yes
using org::apache::nifi::minifi::core::Property; | ||
|
||
uint64_t uint64_var = 0; | ||
REQUIRE(Property::StringToInt("-2", uint64_var) == false); // Negative shouldn't be converted to uint |
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 realize 710 kind of had a different focus, but your comment on the prior PR in which you mentioned this was to open 707 in hopes of opening a discussion for better dealing with this. For example, it may be the case where sometimes a negative value makes sense AND the conversion make sense. I will do some research, as I know these cases exist. In others, that is NOT true.
While the above test is totally valid from the perspective of 710, the hope was that we could discuss and more intelligently handle these cases, perhaps with the validators themselves. What is your perspective?
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.
Sure, let's focus on the long term solution.
Just scratched this as I spotted errors in the code, however as you correctly pointed out, my change doesn't include the integration approach.
template<typename T, typename std::enable_if<std::is_signed<T>::value, T>::type* = nullptr> | ||
static bool StringToNum(const char* str_begin, char **str_end, T& output) { | ||
int64_t val = std::strtoll(str_begin, str_end, 0); | ||
return AssignIfFits<T>(val, 1, output); |
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 have any multiplication at all if it is 1? same with the one, below.
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.
Just to reuse that code. The goal here was to make sure that the value just read fits target in case that's smaller than 64 bits.
8a1db8e
to
f97f943
Compare
Closing this because of inactivity |
Thank you for submitting a contribution to Apache NiFi - MiNiFi C++.
In order to streamline the review of the contribution we ask you
to ensure the following steps have been taken:
For all changes:
Is there a JIRA ticket associated with this PR? Is it referenced
in the commit message?
Does your PR title start with MINIFICPP-XXXX where XXXX is the JIRA number you are trying to resolve? Pay particular attention to the hyphen "-" character.
Has your PR been rebased against the latest commit within the target branch (typically master)?
Is your initial contribution a single, squashed commit?
For code changes:
For documentation related changes:
Note:
Please ensure that once the PR is submitted, you check travis-ci for build issues and submit an update to your PR as soon as possible.